[
  {
    "path": ".github/workflows/test.yml",
    "content": "on:\n  push:\n    branches: [main]\n  pull_request:\n    types: [opened, repoened, synchronize]\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2.0.0\n      - uses: cachix/install-nix-action@v27\n        with:\n          nix_path: nixpkgs=channel:nixos-unstable\n      - name: Setup erlang\n        run: cd sequential/installing && nix develop\n      - name: Re-use nix flake to check erlang tests\n        run: cd sequential/installing && nix develop --command bash -c \"cd ../../ && make test PROFILE=ci\"\n"
  },
  {
    "path": ".gitignore",
    "content": ".eunit\ndeps\n*.o\n*.beam\n*.plt\nerl_crash.dump\nebin/*.beam\nrel/example_project\n.concrete/DEV_MODE\n.rebar\n_build/\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 LambdaClass\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "DIRS = $(filter-out _build/, $(dir $(wildcard */)))\n\nEXERCISES = $(patsubst %/, %, $(filter-out $(DIRS), $(dir $(wildcard */*/))))\nCATEGORIES = $(subst /, , $(DIRS))\n\nPROFILE ?= test\n\n.PHONY: test $(CATEGORIES) $(EXERCISES) check-progress\n\ncheck-progress:\n\t./check-progress\n\ntest: $(CATEGORIES)\n\n.SECONDEXPANSION:\n$(CATEGORIES): $$(filter $$@%, $(EXERCISES))\n\n$(EXERCISES):\n\tcd $@ && rebar3 as $(PROFILE) ct\n"
  },
  {
    "path": "README.md",
    "content": "![Build Status](https://github.com/lambdaclass/erlings/actions/workflows/test.yml/badge.svg)\n\n\n# Erlings: Small exercises to get you used to reading and writing Erlang code\n\nsource: http://www.erlang.org/\n\n> Erlang is a programming language used to build massively scalable soft real-time systems with requirements on high availability. Some of its uses are in telecoms, banking, e-commerce, computer telephony and instant messaging. Erlang's runtime system has built-in support for concurrency, distribution and fault tolerance.\n\nOne of the most important parts of any learning process it the resolution of practical problems by one self, but is hard when you don't have anything to guide you or something to compare with.\n\n## Must Read\n- Read [Learn you some Erlang for great good](http://learnyousomeerlang.com/)\n- [Erlang and code style](https://medium.com/@jlouis666/erlang-and-code-style-b5936dceb5e4)\n- Read our [guidelines](https://github.com/lambdaclass/guidelines)\n\n## Exercises\nThe exercises are divided in 5 sections. They are meant to be done as you read [Learn you some Erlang for great good](http://learnyousomeerlang.com/), but feel free to do them as you wish.\n\nStart by clicking one of the exercises below or just run:\n\n```\n$ make\n```\n\n### A. Sequential Programming\n\n1. [Installing](sequential/installing/)\n1. [Filter numbers](sequential/filter_numbers/)\n   1. [Filter in](sequential/filter_numbers#filter-in)\n   1. [Filter out](sequential/filter_numbers#filter-out)\n   1. [Filter in values](sequential/filter_numbers#filter-in-values)\n1. [Hello world](sequential/hello/)\n1. [Hello pattern](sequential/hello_pattern/)\n1. [Lists](sequential/lists/)\n   1. [Reverse](sequential/lists#reverse)\n   1. [Remove Consecutive](sequential/lists#remove-consecutive)\n   1. [Even Fibonacci Numbers](sequential/lists#even-fibonacci-numbers)\n   1. [Reduce](sequential/lists#reduce)\n   1. [Rotate Lists](sequential/lists#rotate-lists)\n   1. [Run-length Encoding of a List](sequential/lists#run-length-encoding-of-a-list)\n   1. [Any](sequential/lists#any)\n   1. [Anagram](sequential/lists#anagram)\n   1. [First Letter Last Letter](sequential/lists#first-letter-last-letter-game)\n\n\n1. [Bank Accounts](sequential/bank_accounts/)\n1. [Calculate BMI](sequential/calculate_bmi/)\n1. [Insert element at position](sequential/insert_element_at/)\n1. [Filter Fibonacci Numbers](sequential/filter_fibonacci_numbers/)\n1. [Maps](sequential/maps/)\n   1. [Sum of Values](sequential/maps#sum-of-values)\n   1. [Min Value](sequential/maps#min-value)\n   1. [Sort keys](sequential/maps/#sort-keys)\n   1. [Return values](sequential/maps/#return-values)\n   1. [Mapping a Map](sequential/maps#mapping-a-map)\n   1. [Merge Map](sequential/maps#merge-map)\n   1. [List to Map](sequential/maps#list-to-map)\n   1. [Records to Maps](sequential/maps#records-to-maps)\n   1. [Maps to Records](sequential/maps#maps-to-records)\n   1. [Proplist to Map](sequential/maps#proplist-to-map)\n1. [Implement a simple regex engine](sequential/regex/)\n\n### B. Concurrent Programming\n\n1. [Parallel Map](concurrent/parallel_map)\n1. [Calculator](concurrent/calculator)\n1. [Priority](concurrent/priority)\n1. [Ring Benchmark](concurrent/ring_benchmark)\n\n### C. OTP\n\n1. [Shopping Cart](otp/shopping_cart/)\n1. [Worker pool](/otp/pool)\n\n### D. Distributed Programming\n\n1. [Remote Function](distributed/remote_fun/)\n\n### E. Libraries\n\n1. [Shortly](libraries/shortly/)\n\n## Down the Rabbit Hole\n\n### Advanced Erlang\n- [How to build stable systems](https://medium.com/@jlouis666/how-to-build-stable-systems-6fe9dcf32fc4)\n- [Stacking Theory for Systems Design](https://medium.com@jlouis666/stacking-theory-for-systems-design-2450e6300689)\n- [A Ramble Through Erlang IO Lists](http://prog21.dadgum.com/70.html)\n- [Erlang String Handling](https://medium.com/@jlouis666/erlang-string-handling-7588daad8f05)\n- [How Erlang does scheduling](http://jlouisramblings.blogspot.com.ar/2013/01/how-erlang-does-scheduling.html)\n- [Red and Green callbacks](https://joearms.github.io/published/2013-04-02-Red-and-Green-Callbacks.html)\n- [RTB: Where Erlang BLOOMs](https://ferd.ca/rtb-where-erlang-blooms.html)\n- [On Erlang, State and Crashes](http://jlouisramblings.blogspot.com.ar/2010/11/on-erlang-state-and-crashes.html)\n- [It's About the Guarantees](https://ferd.ca/it-s-about-the-guarantees.html)\n- [Don’t Lose Your ETS Tables](http://steve.vinoski.net/blog/2011/03/23/dont-lose-your-ets-tables/)\n- [The Road we didn't go down ](http://armstrongonsoftware.blogspot.com.ar/2008/05/road-we-didnt-go-down.html)\n- [Erlang Garbage Collection Details and Why It Matters](https://hamidreza-s.github.io/erlang%20garbage%20collection%20memory%20layout%20soft%20realtime/2015/08/24/erlang-garbage-collection-details-and-why-it-matters.html)\n- [Sequence and Order in Erlang](https://web.archive.org/web/20160419085030/http://notdennisbyrne.blogspot.com.ar/2008/04/sequence-and-order-in-erlang.html)\n- [The Erlang shell](https://medium.com/@jlouis666/the-erlang-shell-ab8d8bec3972)\n- [Queues Don't Fix Overload](https://ferd.ca/queues-don-t-fix-overload.html)\n- [https://www.erlang-in-anger.com/](https://www.erlang-in-anger.com/)\n- Whatever you can find in [spawned shelter](http://spawnedshelter.com/)\n\n### Distributed systems and databases\n- [Distributed Systems: for fun and profit](http://book.mixu.net/distsys/single-page.html)\n- Designing Data-intensive applications book\n- [From the Ground Up: Reasoning About Distributed Systems in the Real World](https://bravenewgeek.com/from-the-ground-up-reasoning-about-distributed-systems-in-the-real-world/)\n- [Jepsen: On the perils of network partitions](https://aphyr.com/posts/281-jepsen-on-the-perils-of-network-partitions)\n- MapReduce: Simplified Data Processing on Large Clusters\n- Dynamo: Amazon’s Highly Available Key-value Store\n- Bigtable: A Distributed Storage System for Structured Data\n- Time, Clocks and Ordering of Events in a Distributed System\n- Unreliable failure detectors and reliable distributed systems\n\n### Riak and riak_core\n- [Riak Core Tutorial](https://github.com/lambdaclass/riak_core_tutorial/)\n\n### Elixir\n- [Erlang/Elixir Syntax: A Crash Course](https://elixir-lang.org/crash-course.html)\n- [Elixir: Introduction](https://elixir-lang.org/getting-started/introduction.html)\n- [Phoenix: Overview](https://hexdocs.pm/phoenix/overview.html)\n- [Phoenix: Up and Running](https://hexdocs.pm/phoenix/up_and_running.html#content)\n- [GenState](https://hexdocs.pm/gen_stage/GenStage.html)\n- [GenStage advanced](https://elixirschool.com/en/lessons/advanced/gen-stage/)\n"
  },
  {
    "path": "check-progress",
    "content": "#!/usr/bin/env escript\n%% -*- erlang -*-\n%%! -smp enable -sname factorial -mnesia debug verbose\n\nmain(_) ->\n  Folders = exercise_folders(),\n  run_tests(Folders, 1, length(Folders)).\n\nrun_tests([], _, _) ->\n  io:format(\"All tests completed.\"),\n  true;\nrun_tests([Folder | MoreFolders], CurrentCount, TotalCount) ->\n  case run_make_on_folder(Folder) of\n    true ->\n      io:format(\"~p.......ok~n\", [Folder]),\n      run_tests(MoreFolders, CurrentCount + 1, TotalCount);\n    false ->\n      print_failure(Folder, CurrentCount, TotalCount)\n  end.\n\nprint_failure(Folder, CurrentCount, TotalCount) ->\n  io:format(\"Exercise `~s` failed.~n\", [Folder]),\n  io:format(\"Review it, you may be closer than you think.~n\",[]),\n  io:format(\"Progress: ~p/~p.~n\", [CurrentCount-1, TotalCount]).\n\nrun_make_on_folder(Folder) ->\n  Output = cmd(Folder, \"make\"),\n  string:str(Output, \"tests passed.\") > 0.\n\ncmd(Folder, Command) ->\n  Cmd = io_lib:format(\"~s ~s\", [Command, Folder]),\n  CmdString = binary_to_list(iolist_to_binary(Cmd)),\n  os:cmd(CmdString).\n\nexercise_folders() ->\n  lists:append(\n    [\n     in_folder(\"sequential\", [\"installing\", \"hello\", \"hello_pattern\",\n                              \"lists\", \"bank_accounts\", \"calculate_bmi\", \"insert_element_at\",\n                              \"filter_fibonacci_numbers\", \"maps\", \"regex\"]),\n     in_folder(\"concurrent\", [\"calculator\", \"parallel_map\", \"priority\", \"ring_benchmark\"]),\n     in_folder(\"otp\", [\"shopping_cart\", \"pool\"])\n     %in_folder(\"distributed\", [\"remote_fun\"])\n     %in_folder(\"libraries\", [\"shortly\"])\n    ]).\n\nin_folder(Folder, SubFolders) ->\n  [Folder ++ \"/\" ++ SubFolder || SubFolder <- SubFolders].\n"
  },
  {
    "path": "concurrent/calculator/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n.lock\n"
  },
  {
    "path": "concurrent/calculator/README.md",
    "content": "# Calculator\n\n## Reading Material\n\n- [The Hitchhiker's Guide to Concurrency](http://learnyousomeerlang.com/the-hitchhikers-guide-to-concurrency)\n- [More On Multiprocessing](http://learnyousomeerlang.com/more-on-multiprocessing)\n\n## Exercise\n\nYou will write a simple calculator (sum, substraction, multiplication, and division) using processes and passing messages. For this you will write a module `calculator` and create the following functions:\n\n- `start_calculator/0`: This functions will spawn a `calculator:calculator_server/1`.\n- `calculator_server/0`: It will receive through messages the operation to execute and its arguments, and send back the result.\n- `turn_off/1`: Shuts down the calculator server.\n\nThis is the bare minimum you need to make a working example, but we won't stop there so will use what you learned in [\"We Love Messages, but we keep them secret\"](http://learnyousomeerlang.com/more-on-multiprocessing#secret-messages) to abstract everything to simple functions the user can use without knowing about underlying protocol. For this create the functions: `add/3, subtract/3, multiply/3, divide/3`. This functions will receive as input PID and a tuple contaning the two numbers to operate on, send the message to the calculator server, and return the result.\n\n```erlang\n1> Cal = calculator:start_calculator().\n<0.67.0>\n\n2> calculator:add(Cal, 1, 3).\n4\n\n3> calculator:subtract(Cal, 1, 3).\n-2\n\n4> calculator:multiply(Cal, 7, 3).\n21\n\n5> calculator:divide(Cal, 7, 3).\n2.3333333333333335\n\n6> calculator:divide(Cal, 0, 3).\n0.0\n\n7> calculator:divide(Cal, 2, 3).\n0.6666666666666666\n\n8> calculator:turn_off(Cal).\noff\n```\n\nCheck our proposed [solution](solution/calculator.erl).\n"
  },
  {
    "path": "concurrent/calculator/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "concurrent/calculator/solution/calculator.app.src",
    "content": "{application, calculator,\n [{description, \"calculator\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { calculator_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "concurrent/calculator/solution/calculator.erl",
    "content": "-module(calculator).\n-export([start_calculator/0,\n         calculator_server/0,\n         add/3,\n         subtract/3,\n         multiply/3,\n         divide/3,\n         turn_off/1]).\n\nstart_calculator() ->\n  spawn(calculator, calculator_server, []).\n\nsend_operation(Pid, Operation, Numbers) ->\n  Pid ! {self(), Operation, Numbers},\n  receive\n    Result ->\n      Result\n  end.\n\nadd(Pid, X, Y) ->\n  send_operation(Pid, add, {X, Y}).\n\nsubtract(Pid, X, Y) ->\n  send_operation(Pid, subtract, {X, Y}).\n\nmultiply(Pid, X, Y) ->\n  send_operation(Pid, multiply, {X, Y}).\n\ndivide(Pid, X, Y) ->\n  send_operation(Pid, divide, {X, Y}).\n\ncalculator_server() ->\n  receive\n    {From, add, {X, Y}} ->\n      From ! X + Y;\n    {From, subtract, {X, Y}} ->\n      From ! X - Y;\n    {From, multiply, {X, Y}} ->\n      From ! X * Y;\n    {From, divide, {X, Y}} ->\n      From ! X / Y;\n    off ->\n      exit(shutdown)\n  end,\n  calculator_server().\n\nturn_off(Pid) ->\n  Pid ! off.\n"
  },
  {
    "path": "concurrent/calculator/src/calculator.app.src",
    "content": "{application, calculator,\n [{description, \"calculator\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { calculator_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "concurrent/calculator/src/calculator.erl",
    "content": "-module(calculator).\n-export([start_calculator/0,\n         calculator_server/0,\n         turn_off/1]).\n\nstart_calculator() ->\n  put_your_solution_here.\n\ncalculator_server() ->\n  put_your_solution_here.\n\nturn_off(Pid) ->\n  put_your_solution_here.\n"
  },
  {
    "path": "concurrent/calculator/test/calculator_SUITE.erl",
    "content": "-module(calculator_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(calculator_test).\n"
  },
  {
    "path": "concurrent/calculator/test/calculator_test.erl",
    "content": "-module(calculator_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\ncalculator_add_test() ->\n  Cal = calculator:start_calculator(),\n  ?assertEqual(2+3, calculator:add(Cal, 2, 3)).\n\ncalculator_subtract_test() ->\n  Cal = calculator:start_calculator(),\n  ?assertEqual(2-3, calculator:subtract(Cal, 2, 3)).\n\ncalculator_multiply_test() ->\n  Cal = calculator:start_calculator(),\n  ?assertEqual(7*9, calculator:multiply(Cal, 7, 9)).\n\ncalculator_divide_test() ->\n  Cal = calculator:start_calculator(),\n  ?assertEqual(25/5, calculator:divide(Cal, 25, 5)).\n"
  },
  {
    "path": "concurrent/parallel_map/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n.lock\n"
  },
  {
    "path": "concurrent/parallel_map/README.md",
    "content": "# Parallel Map\n\n## Reading Material\n\n- [The Hitchhiker's Guide to Concurrency](http://learnyousomeerlang.com/the-hitchhikers-guide-to-concurrency)\n- [More On Multiprocessing](http://learnyousomeerlang.com/more-on-multiprocessing)\n\n## Exercise\n\nYou will write `parallel_map:pmap/2` a parallel implementation of [lists:map](http://erlang.org/doc/man/lists.html#map-2). Every element in the input list must me processed by a separate process, this means that a list of 100 elements will spawn 100 processes.\n\n```erlang\n1> Fun = fun(X) -> X * 10 end,\n2> List = [1, 2, 3, 4, 5],\n3> parallel_map:pmap(Fun, List)\n[10,20,30,40,50]\n```\n\nCheck our proposed [solution](solution/parallel_map.erl).\n"
  },
  {
    "path": "concurrent/parallel_map/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "concurrent/parallel_map/solution/parallel_map.app.src",
    "content": "{application, parallel_map,\n [{description, \"parallel_map\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { parallel_map_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "concurrent/parallel_map/solution/parallel_map.erl",
    "content": "-module(parallel_map).\n-export([pmap/2,\n         apply/3]).\n\npmap(Fun, List) ->\n  Pids = lists:map(fun(Elem) ->\n      spawn(parallel_map, apply, [self(), Fun, Elem])\n    end,\n    List),\n  lists:map(fun(Pid) ->\n      receive\n        {Pid, Result} ->\n          Result\n      end\n    end,\n    Pids).\n\napply(From, Fun, Elem) ->\n  From ! {self(), Fun(Elem)}.\n"
  },
  {
    "path": "concurrent/parallel_map/src/parallel_map.app.src",
    "content": "{application, parallel_map,\n [{description, \"parallel_map\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { parallel_map_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "concurrent/parallel_map/src/parallel_map.erl",
    "content": "-module(parallel_map).\n-export([pmap/2]).\n\npmap(Fun, List) ->\n  your_solution_here.\n"
  },
  {
    "path": "concurrent/parallel_map/test/parallel_map_SUITE.erl",
    "content": "-module(parallel_map_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(parallel_map_test).\n"
  },
  {
    "path": "concurrent/parallel_map/test/parallel_map_test.erl",
    "content": "-module(parallel_map_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nparallel_map_result_test() ->\n  Fun = fun(X) -> X * 10 end,\n  List = [1, 2, 3, 4, 5],\n  Result = lists:map(Fun, List),\n  ?assertEqual(Result, parallel_map:pmap(Fun, List)).\n"
  },
  {
    "path": "concurrent/priority/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n.lock\n"
  },
  {
    "path": "concurrent/priority/Makefile",
    "content": ".PHONY: compile, run\n\ndefault: run\n\ncompile:\n\terl -compile priority\n\nrun: compile\n\terl -noshell -s priority send_test -s init stop\n"
  },
  {
    "path": "concurrent/priority/README.md",
    "content": "# Priority\n\n## Reading material\n\n- [Learn You Some Erlang: More On Multiprocessing](http://learnyousomeerlang.com/more-on-multiprocessing)\n- [Erlang Manual: processes](http://erlang.org/doc/reference_manual/processes.html)\n- [Erlang Manual: receive expression](http://erlang.org/doc/reference_manual/expressions.html#id81776)\n\n## Exercise\n\nFor this exercise we will process messages according to priorities from the erlang message box without using any data structure.  \nMessages with priority `vip` should be processed _as soon as_ they enter the `priority_loop`. Carefully consider how messages are processed by a `receive` block, and how you can _combine_ more than one block to match clauses in a certain order. Hints on how to solve this exercise found [here](http://learnyousomeerlang.com/more-on-multiprocessing).\n\nYou will write 3 functions for the `priority` module:\n- `start/0`: This will create the process that will receive the messages (using `priority_loop/1`) and return its PID.\n- `get_messages/1`: Given the PID will return all the messages stored in the receive loop state.\n- `priority_loop/1`: This function will hold a list of messages received and read incoming messages. Remeber, messages need to be read according to their priority.\n\nNotes:\n- Messages are of the form `{Priority, Message}`.\n- `Priority` is either `vip` or `normal`.\n- `priority_loop/1` may receive other things to perform certain actions needed.\n\nYou can check your answer by doing `make concurrent/priority` from the root directory of your [erlings](https://github.com/lambdaclass/erlings) folder. If you wish you can also check our proposed [solution](solution).\n"
  },
  {
    "path": "concurrent/priority/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "concurrent/priority/solution/priority.app.src",
    "content": "{application, priority,\n [{description, \"priority\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { priority_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "concurrent/priority/solution/priority.erl",
    "content": "-module(priority).\n\n-export([start/0,\n         send_vip/2,\n         send_normal/2,\n         get_messages/1,\n         priority_loop/1]).\n\nstart() ->\n  spawn_link(priority, priority_loop, [[]]).\n\nsend_vip(Pid, Msg) ->\n  Pid ! {vip, Msg}.\n\nsend_normal(Pid, Msg) ->\n  Pid ! {normal, Msg}.\n\nget_messages(Pid) ->\n  Pid ! {server, {self(), current_msgs}},\n  receive\n    Msgs ->\n      Msgs\n  end.\n\npriority_loop(State) ->\n  receive\n    {vip, Msg} ->\n      priority_loop([{vip, Msg}] ++ State)\n  after 0 ->\n    receive\n      {server, {From, current_msgs}} ->\n        From ! lists:reverse(State);\n      {Priority, Msg} ->\n        priority_loop([{Priority, Msg}] ++ State)\n    end\n  end.\n"
  },
  {
    "path": "concurrent/priority/src/priority.app.src",
    "content": "{application, priority,\n [{description, \"priority\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { priority_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "concurrent/priority/src/priority.erl",
    "content": "-module(priority).\n\n-export([start/0,\n         get_messages/1,\n         priority_loop/1]).\n\nstart() ->\n  your_solution_here.\n\nget_messages(Pid) ->\n  your_solution_here.\n\npriority_loop(State) ->\n  your_solution_here.\n"
  },
  {
    "path": "concurrent/priority/test/.keep",
    "content": ""
  },
  {
    "path": "concurrent/priority/test/priority_SUITE.erl",
    "content": "-module(priority_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(priority_test).\n"
  },
  {
    "path": "concurrent/priority/test/priority_test.erl",
    "content": "-module(priority_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\n\nsort_fun({vip, _}, {vip, _}) ->\n  true;\nsort_fun({normal, _}, {normal, _}) ->\n  true;\nsort_fun({vip, _}, {normal, _}) ->\n  true;\nsort_fun({normal, _}, {vip, _}) ->\n  false.\n\npriority_test() ->\n  Msgs = [{normal, 1},\n          {normal, 2},\n          {vip, 3},\n          {vip, 4},\n          {normal, 5},\n          {normal, 6},\n          {normal, 7},\n          {vip, 8},\n          {vip, 9}],\n  Msgs_ordered = lists:sort(fun sort_fun/2, Msgs),\n  Pid = priority:start(),\n  lists:foreach(fun(Msg) -> Pid ! Msg end, Msgs),\n  ?assertEqual(Msgs_ordered, priority:get_messages(Pid)).\n"
  },
  {
    "path": "concurrent/ring_benchmark/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n"
  },
  {
    "path": "concurrent/ring_benchmark/README.md",
    "content": "# Ring benchmark\n\n## Reading Material\n\n## Exercise\nWrite a function `ring:ring/2` which takes 2 arguments: M and N.\nThis function should create N process in a ring in such a way\nthat sending a message to the first process it get passed around\nthe ring M times so that a total of N * M messages get sent.\n\nIn case you need any guidance please check our\n[proposed solution](solution/ring.erl).\n"
  },
  {
    "path": "concurrent/ring_benchmark/rebar.config",
    "content": "{erl_opts, [debug_info]}.\n{deps, []}.\n{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "concurrent/ring_benchmark/solution/ring.erl",
    "content": "-module(ring).\n-export([ring/2]).\n\nnode_loop(Parent) ->\n  receive\n    {msg, []} -> Parent ! done;\n    {msg, [FirstNode | OtherNodes]} ->\n      io:format(\"Node ~p forwarding to ~p~n\", [self(), FirstNode]),\n      FirstNode ! {msg, OtherNodes},\n      node_loop(Parent)\n  end.\n\n% N processes, M messages\nring(N, M) ->\n  [FirstNode | Nodes] = create_processes(N, M),\n  BeforeFistMessage = os:timestamp(),\n  FirstNode ! {msg, Nodes},\n  receive\n    done -> io:format(\"done received ~n\")\n  end,\n  AfterLastMessage = os:timestamp(),\n  ElapsedTime = timer:now_diff(AfterLastMessage, BeforeFistMessage),\n  io:format(\"Processes: ~p, Messages ~p in ~pms~n\", [N, M, ElapsedTime]).\n\ncreate_processes(N, M) ->\n  Parent = self(),\n  Processes = [spawn_link(fun () -> node_loop(Parent) end)\n               || _ <- lists:seq(1, N)],\n  lists:append(lists:duplicate(M, Processes)).\n"
  },
  {
    "path": "concurrent/ring_benchmark/solution/ring_benchmark.app.src",
    "content": "{application, ring_benchmark,\n [{description, \"An OTP application\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { ring_benchmark_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"Apache 2.0\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "concurrent/ring_benchmark/src/ring.erl",
    "content": "-module(ring).\n-export([ring/2]).\n\n%% N processes, M messages\nring(_N, _M) ->\n  complete_here.\n"
  },
  {
    "path": "concurrent/ring_benchmark/src/ring_benchmark.app.src",
    "content": "{application, ring_benchmark,\n [{description, \"An OTP application\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { ring_benchmark_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"Apache 2.0\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "concurrent/ring_benchmark/test/ring_benchmark_SUITE.erl",
    "content": "-module(ring_benchmark_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(ring).\n"
  },
  {
    "path": "concurrent/ring_benchmark/test/ring_test.erl",
    "content": "-module(ring_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nring_test() ->\n  ?assertMatch(#{msgs_sent := 4, procs_started := 2},\n               run_ring(2, 2)),\n  ?assertMatch(#{msgs_sent := 12, procs_started := 4},\n               run_ring(4, 3)),\n  ?assertMatch(#{msgs_sent := 380, procs_started := 19},\n               run_ring(19, 20)).\n\nrun_ring(M, N) ->\n  dbg_start_tracing(M, N),\n  ring:ring(M, N),\n  Result = dbg_wait_for_result(M,N),\n  dbg_stop_tracing(),\n  Result.\n\ndbg_start_tracing(M, N) ->\n  dbg:stop_clear(),\n  dbg:tracer(process, {fun dbg_fun_handler/2, dbg_init_state(M, N)}),\n  dbg:p(new, [m,p]).\n\ndbg_stop_tracing() -> dbg:stop_clear().\n\ndbg_wait_for_result(M, N) ->\n  WaitTime = M * N * 1000,\n  receive\n    DbgState -> DbgState\n  after WaitTime ->\n      #{}\n  end.\n\ndbg_init_state(M, N) ->\n  #{m => M,\n    n => N,\n    ret_pid => self(),\n    procs_started => 0,\n    msgs_sent => 0,\n    informed => false}.\n\ndbg_fun_handler(Trace, State) ->\n  TracedState = dbg_capture_trace(Trace, State),\n  dbg_inform_if_necessary(TracedState).\n\ndbg_capture_trace(Trace, State = #{procs_started := ProcsStarted,\n                                   msgs_sent := MsgsSent}) ->\n  case Trace of\n    {_, _, spawned, _, _} -> State#{procs_started := ProcsStarted + 1};\n    {_, _, send, _, _} -> State#{msgs_sent := MsgsSent + 1};\n    _ -> State\n  end.\n\ndbg_inform_if_necessary(State = #{m := M, n := N, ret_pid := RetPid}) ->\n  MN = M * N,\n  case State of\n    #{msgs_sent := MN, informed := false} ->\n      RetPid ! State#{informed => true};\n    _ ->\n      State\n  end.\n"
  },
  {
    "path": "distributed/remote_fun/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n"
  },
  {
    "path": "distributed/remote_fun/Makefile",
    "content": ".PHONY: server client\n\ndefault:\n\t@echo \"usage make [client|server]\"\n\nclient:\n\t./rebar3 shell --sname 'client@localhost'\n\nserver:\n\t./rebar3 shell --sname 'server@localhost'\n"
  },
  {
    "path": "distributed/remote_fun/README.md",
    "content": "# Remote Function Server\n\n## Reading material\n\n- [Learn you Some Erlang: Distribunomicon](http://learnyousomeerlang.com/distribunomicon)\n- [My favorite erlang program](https://joearms.github.io/published/2013-11-21-My-favorite-erlang-program.html)\n\n## Exercise\n\n### Problem\nCreate an Erlang cluster with 2 nodes. One node is a server that just receives a function and become it.\nThe other node sends a function the first and execute something remotely.\n\n### Solution\nThe server (`remote_fun_server.erl`) receives a message as normal, and get the function from there.\n\nThe client (`remote_fun_client.erl`) creates a function that is able to receive and loop, and also\nto be killed, then it's send to the server.\n\n#### Running the solution\n\nRun two terminals with ``make server`` and ``make client``.\n\nThen connect the two nodes:\n~~~\n(client@localhost)1> net_kernel:connect_node(server@localhost).\ntrue\n~~~\n\n~~~\n(server@localhost)1> net_kernel:connect_node(client@localhost).\ntrue\n~~~\n\nAfter the connections are fine, you can test the functions with:\n\n~~~\n(server@localhost)2> remote_fun_server:function_server().\n~~~\n\n~~~\n(client@localhost)2> remote_fun_client:function_client(server@localhost).\nOk!\nok\n~~~\n"
  },
  {
    "path": "distributed/remote_fun/src/remote_fun.app.src",
    "content": "{application, remote_fun,\n [{description, \"Remote function\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { remote_fun_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "distributed/remote_fun/src/remote_fun_client.erl",
    "content": "-module(remote_fun_client).\n\n-export([function_client/1]).\n\nfunction_client(RemoteNode) ->\n    FunctionServerPid = {function_server, RemoteNode},\n    FunctionServerPid ! {be, fun multiply_loop/0},\n    FunctionServerPid ! {self(), {3, 9}},\n    receive\n        {product_result, _Result = 27} -> ok\n    end,\n    FunctionServerPid ! kill,\n    io:format(\"Ok!~n\").\n\nmultiply_loop() ->\n    receive\n        {From, {Factor1, Factor2}} ->\n            Result = Factor1 * Factor2,\n            From ! {product_result, Result},\n            multiply_loop();\n        kill -> kill\n    end.\n"
  },
  {
    "path": "distributed/remote_fun/src/remote_fun_server.erl",
    "content": "-module(remote_fun_server).\n\n-export([function_server/0]).\n\nfunction_server() ->\n    register(function_server, self()),\n    receive\n        {be, Function} -> Function()\n    end.\n"
  },
  {
    "path": "distributed/remote_fun/test/.keep",
    "content": ""
  },
  {
    "path": "libraries/shortly/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n"
  },
  {
    "path": "libraries/shortly/Makefile",
    "content": ".PHONY: default server samples test\n\ndefault:\n\t@echo \"usage [test|server]\"\n\nserver:\n\t./rebar3 shell --apps shortly\n\nnode_1:\n\t./rebar3 shell --apps shortly --config src/config/node_1.config --sname node_1\n\nnode_2:\n\t./rebar3 shell --apps shortly --config src/config/node_2.config --sname node_2\n"
  },
  {
    "path": "libraries/shortly/README.md",
    "content": "# Shortly: Link Shortener\n\n## Reading material\n\n- [Rebar3 build tool](https://github.com/erlang/rebar3)\n- [Rebar3 hex](https://hex.pm/docs/rebar3_usage)\n- [Cowboy http server](https://github.com/ninenines/cowboy)\n- [Cowboy user-guide](https://ninenines.eu/docs/en/cowboy/2.2/guide/)\n\n## Exercise\n\nThis exercise will be divided in multiple parts:\n\n1. [Shortly application](#shortly-application)\n1. [Create two nodes](#create-two-nodes)\n1. [Mnesia](#mnesia)\n1. [Syn](#syn)\n\n### Shortly application\n\nCreate an ``OTP`` application using ``rebar3`` and [cowboy](https://github.com/ninenines/cowboy)\nthat is capable of receiving long links and returning shorts ones:\n\n- Receive a ``HTTP POST`` at `http://localhost:8080/<LONG_URL>` returning a shortened link.\n- Receive a ``HTTP GET`` at `http://localhost:8080/<SHORT_URL>` returning the original long link.\n- Accept websocket connections at `http://localhost:8080/news` and notify every time a new link is shortened.\n\n**BONUS:** Create similar endpoints (`GET` and `POST`), but using `cowboy_rest` handler.\n\n### Create two nodes\n\nNow that you have a working application for a single node we need to make it distributed. For this you will need to do the following:\n\n- Receive cowboy's port as either a configuration variable or an environment variable.\n- Set the node name when starting it.\n\n### Mnesia\n\nSo we can have two nodes of our application running, but what is the point if they don't actually comunicate. To fix this we will make use of [Mnesia](http://erlang.org/doc/man/mnesia.html), which will let us have a distributed database across our nodes. To achieve this do the following:\n\n- Create the Mnesia schema.\n- Create the `shortly_urls` table.\n- Add the Mnesia application as a dependency (no more manual starts).\n- Change code to use Mnesia and abstract its usage to its own module `shortly_db`.\n\nIf everything went smoothly you should be able to create a short URL in one node, ask the other node for the long URL, and get your answer.\n\n### Syn\n\nFor the final touch we need to let our users connect (websocket) to any node and still receive the notifications of link creations in all other nodes. For this task we will use [Syn](https://github.com/ostinelli/syn) and do the following:\n\n- Add the Syn application as a dependency.\n- Change code to use Syn and abstract it to a `shortly_presence` module.\n\nNow no matter to what node your users are connected whenever a URL is shortened by their node or any other they should receive the notification.\n\n## Solution\n\n- ``src/bitly_short_link_handler.erl`` handles ``POST`` and ``GET`` requests.\n- ``src/bitly_shortener`` is a ``gen_server`` that uses ``ets`` for storing the links in memory. Being a separated process from the normal ``cowboy`` request handler is very important because the ``ets`` data is lost if the process which created it dies, and the ``cowboy`` handlers processes are created and destroyed in every request.\n\nTo run the solution first star the server with ``make server`` and then ``make test`` for running simple requests.\n"
  },
  {
    "path": "libraries/shortly/rebar.config",
    "content": "{erl_opts, [debug_info]}.\n{deps, [cowboy,\n        jsx,\n        gun,\n        syn]}.\n{plugins,[rebar3_hex]}.\n\n{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "libraries/shortly/solution/config/node_1.config",
    "content": "[\n  {mnesia, [{dir, \"/tmp/mnesia/node_8010\"}]},\n  {shortly, [{port, 8010}]}\n].\n"
  },
  {
    "path": "libraries/shortly/solution/config/node_2.config",
    "content": "[\n  {mnesia, [{dir, \"/tmp/mnesia/node_8020\"}]},\n  {shortly, [{port, 8020}]}\n].\n"
  },
  {
    "path": "libraries/shortly/solution/shortly.app.src",
    "content": "{application, shortly,\n [{description, \"Link shortener exercise\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { shortly_app, []}},\n  {applications,\n   [kernel,\n    stdlib,\n    mnesia,\n    syn,\n    cowboy\n   ]},\n  {env,[{port, 8080}]},\n  {modules, []},\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "libraries/shortly/solution/shortly_app.erl",
    "content": "-module(shortly_app).\n\n-behaviour(application).\n\n-export([start/2,\n         stop/1,\n         install/1]).\n\n-record(shortly_urls, {hash, url}).\n\nstart(_StartType, _StartArgs) ->\n    {ok, Port} = application:get_env(shortly, port),\n    Dispatch = cowboy_router:compile(\n                 [\n                  {'_', [\n                         {\"/news\", shortly_ws_handler, []},\n                         {\"/:url\", shortly_link_handler, []}\n                        ]}\n                 ]),\n    {ok, _} = cowboy:start_clear(http, [{port, Port}], #{\n\t\tenv => #{dispatch => Dispatch}\n\t}),\n    shortly_sup:start_link().\n\nstop(_State) ->\n    cowboy:stop_listener(http),\n    ok.\n\ninstall(Nodes) ->\n    mnesia:stop(),\n    rpc:multicall(Nodes, application, stop, [mnesia]),\n    ok = mnesia:create_schema([node() | Nodes]),\n    mnesia:start(),\n    rpc:multicall(Nodes, application, start, [mnesia]),\n    mnesia:create_table(shortly_urls,\n      [{attributes, record_info(fields, shortly_urls)},\n       {type, set},\n       {ram_copies, Nodes}]),\n    mnesia:stop().\n"
  },
  {
    "path": "libraries/shortly/solution/shortly_db.erl",
    "content": "-module(shortly_db).\n\n-export([store_url/1,\n         save_url/2]).\n\n-record(shortly_urls, {hash, url}).\n\nstore_url(ShortUrl) ->\n  Result = mnesia:activity(transaction, fun() ->\n      mnesia:read(shortly_urls, ShortUrl)\n    end),\n  case Result of\n    [] ->\n      [];\n    [#shortly_urls{hash=ShortUrl, url=LongUrl}] ->\n      [{ShortUrl, LongUrl}]\n  end.\n\nsave_url(ShortUrl, LongUrl) ->\n  mnesia:activity(transaction, fun() ->\n      mnesia:write(#shortly_urls{hash=ShortUrl, url=LongUrl})\n    end).\n"
  },
  {
    "path": "libraries/shortly/solution/shortly_link_handler.erl",
    "content": "-module(shortly_link_handler).\n\n-export([init/2]).\n\ninit(Req, State) ->\n    Url = get_request_url(Req),\n    ReqMethod = cowboy_req:method(Req),\n    {Status, ShortUrl} = handle_request(ReqMethod, Url),\n    Body = get_body_response(Status, ShortUrl),\n    Header = get_headers(Status, ShortUrl),\n    Resp = cowboy_req:reply(Status, Header, Body, Req),\n    {ok, Resp, State}.\n\nhandle_request(<<\"POST\">>, Url) -> \n    {CreationStatus, ShortUrl} = shortly_shortener:short(Url),\n    HttpStatus =\n        case CreationStatus of\n            old -> 200; %ok\n            new -> 201  %created\n        end,\n    {HttpStatus, ShortUrl};\n\nhandle_request(<<\"GET\">>, Url) ->\n    case shortly_shortener:get(Url) of\n        not_found -> {404, \"\"};\n        ShortUrl  -> {302, ShortUrl}\n    end.\n\nget_request_url(Req) ->\n    <<\"/\", Url/binary>> = cowboy_req:path(Req),\n    Url.\n\nget_body_response(404, _)   -> <<>>;\nget_body_response(_,   Url) -> jsx:encode(#{url => Url }).\n\nget_headers(Status, _) when Status =/= 302 -> \n    common_headers();\nget_headers(302, RedirectUrl) ->\n    CommonHeaders = common_headers(),\n    CommonHeaders#{<<\"location\">> => RedirectUrl }.\n\ncommon_headers() ->\n    #{<<\"content-type\">> => <<\"application/json\">>}.\n"
  },
  {
    "path": "libraries/shortly/solution/shortly_notification_algorithm.erl",
    "content": "-module(shortly_notification_algorithm).\n\n-export([behaviour_info/1]).\n\nbehaviour_info(callbacks) ->\n    [{init,0},\n     {subscribe, 1},\n     {unsubscribe, 1},\n     {notify, 1}].\n"
  },
  {
    "path": "libraries/shortly/solution/shortly_notification_ets.erl",
    "content": "-module(shortly_notification_ets).\n\n-behaviour(shortly_notification_algorithm).\n\n-export([init/0,\n         subscribe/1,\n         unsubscribe/1,\n         notify/1]).\n\n-define(ETS_NAME, shortly_notification_ets_table).\n\ninit() ->\n    ets:new(?ETS_NAME, [set, public, named_table]).\n\nsubscribe(Pid) ->\n    ets:insert_new(?ETS_NAME, {Pid}).\n\nunsubscribe(Pid) ->\n    ets:delete(?ETS_NAME, Pid).\n\nnotify(Msg)->\n    ets:foldl(\n      fun({Pid}, _) ->\n              Pid ! Msg\n      end,ignored, ?ETS_NAME).\n"
  },
  {
    "path": "libraries/shortly/solution/shortly_notification_pg2.erl",
    "content": "-module(shortly_notification_pg2).\n\n-behaviour(shortly_notification_algorithm).\n\n-export([init/0,\n         subscribe/1,\n         unsubscribe/1,\n         notify/1]).\n\n-define (PG2_NAME, shortly_notification_pg2_name).\n\ninit() ->\n    pg2:create(?PG2_NAME).\n\nsubscribe(Pid) ->\n    pg2:join(?PG2_NAME, Pid).\n\nunsubscribe(Pid) ->\n    pg2:leave(?PG2_NAME, Pid).\n\nnotify(Msg) ->\n    Subs = pg2:get_members(?PG2_NAME),\n    lists:foreach(\n     fun(Pid) ->\n             Pid ! Msg\n     end, Subs).\n"
  },
  {
    "path": "libraries/shortly/solution/shortly_shortener.erl",
    "content": "-module(shortly_shortener).\n\n-export([init/0,\n         short/1,\n         get/1,\n         subscribe/1,\n         unsubscribe/1]).\n\ninit() ->\n    notify_init().\n\nshort(LongUrl) ->\n    ShortUrl = shortening_algorithm(LongUrl),\n    EntryType = store_url(LongUrl, ShortUrl),\n    notify_subscribers(EntryType, LongUrl, ShortUrl),\n    {EntryType, ShortUrl}.\n\nget(ShortUrl) ->\n     search_long_url(ShortUrl).\n\nsubscribe(Pid) -> notify_subscribe(Pid).\n\nunsubscribe(Pid) -> notify_unsubscribe(Pid).\n\n%% Internal functions\n\nshortening_algorithm(Url) ->\n    Hash = crypto:hash(md4, Url),\n    Base64 = base64:encode(Hash),\n    Sub = binary:part(Base64,1,5),\n    WithoutSlashes = re:replace(Sub, \"/\", \"_\", [global, {return, list}]),\n    list_to_binary(WithoutSlashes).\n\nstore_url(LongUrl, ShortUrl) ->\n    case shortly_db:store_url(ShortUrl) of\n        [] ->\n            shortly_db:save_url(ShortUrl, LongUrl),\n            new;\n        [{ShortUrl, LongUrl}] ->\n            old\n    end.\n\nsearch_long_url(ShortUrl) ->\n    case shortly_db:store_url(ShortUrl) of\n        [] -> not_found;\n        [{ShortUrl, LongUrlEntry}] -> LongUrlEntry\n    end.\n\nnotify_technique_call(F,A) -> apply(shortly_syn,F,A).\nnotify_init() -> notify_technique_call(init,[]).\nnotify_subscribe(Pid) -> notify_technique_call(subscribe,[Pid]).\nnotify_unsubscribe(Pid) -> notify_technique_call(unsubscribe,[Pid]).\nnotify_notify(Msg) -> notify_technique_call(notify,[Msg]).\n\nnotify_subscribers(old,_,_) -> nothing;\nnotify_subscribers(new, LongUrl, ShortUrl) ->\n    notify_notify(#{long_url => LongUrl, short_url => ShortUrl}).\n"
  },
  {
    "path": "libraries/shortly/solution/shortly_sup.erl",
    "content": "-module(shortly_sup).\n\n-behaviour(supervisor).\n\n-export([start_link/0,\n         init/1]).\n\nstart_link() ->\n    supervisor:start_link({local, ?MODULE}, ?MODULE, []).\n\ninit([]) ->\n    shortly_shortener:init(),\n    {ok, { {one_for_all, 0, 1}, []} }.\n"
  },
  {
    "path": "libraries/shortly/solution/shortly_syn.erl",
    "content": "-module(shortly_syn).\n\n-behaviour(shortly_notification_algorithm).\n\n-export([init/0,\n         subscribe/1,\n         unsubscribe/1,\n         notify/1]).\n\n-define(SYN_NAME, syn_ws_connections).\n\ninit() ->\n    syn:init().\n\nsubscribe(Pid) ->\n    syn:join(?SYN_NAME, Pid).\n\nunsubscribe(Pid) ->\n    pg2:leave(?SYN_NAME, Pid).\n\nnotify(Msg) ->\n    syn:publish(?SYN_NAME, Msg).\n"
  },
  {
    "path": "libraries/shortly/solution/shortly_ws_handler.erl",
    "content": "-module(shortly_ws_handler).\n\n-export([init/2,\n         websocket_init/1,\n         websocket_handle/2,\n         websocket_info/2,\n         terminate/3]).\n\ninit(Req, Opts) ->\n    {cowboy_websocket,Req,Opts}.\n\nwebsocket_init(State) ->\n    shortly_shortener:subscribe(self()),\n    {ok, State}.\n\nwebsocket_handle(_Msg, State) ->\n    {ok, State}.\n\nwebsocket_info(#{long_url := LongUrl, short_url := ShortUrl}, State) ->\n    Response = get_ws_json_response(LongUrl, ShortUrl),\n    {reply, {text, Response}, State};\n\nwebsocket_info(_Info, State) ->\n    {ok, State}.\n\nterminate(_Msg, _Req, _State) ->\n    shortly_shortener:unsubscribe(self()),\n    ok.\n\nget_ws_json_response(LongUrl, ShortUrl) ->\n    Data = #{ <<\"long_url\">>  => LongUrl,\n              <<\"short_url\">> => ShortUrl},\n    jsx:encode(Data).\n"
  },
  {
    "path": "libraries/shortly/src/shortly.app.src",
    "content": "{application, shortly,\n [{description, \"Link shortener exercise\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { shortly_app, []}},\n  {applications,\n   [kernel,\n    stdlib,\n    mnesia,\n    syn,\n    cowboy\n   ]},\n  {env,[{port, 8080}]},\n  {modules, []},\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "libraries/shortly/src/shortly_app.erl",
    "content": "-module(shortly_app).\n\n-behaviour(application).\n\n-export([]).\n"
  },
  {
    "path": "libraries/shortly/test/shortly_shortener_SUITE.erl",
    "content": "-module(shortly_shortener_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-compile(export_all).\n-compile(nowarn_export_all).\n\n-record(shortly_urls, {hash, url}).\n\nall() ->\n  [test_notfound,\n   test_created,\n   test_ok,\n   test_redirect,\n   test_ws,\n   run_eunit].\n\ninit_per_suite(Config) ->\n  mnesia:start(),\n  mnesia:create_table(shortly_urls,\n    [{attributes, record_info(fields, shortly_urls)},\n     {type, set},\n     {ram_copies, [node()]}]),\n  application:ensure_all_started(shortly),\n  application:ensure_all_started(gun),\n  Config.\n\nend_per_suite(Config) ->\n  mnesia:clear_table(shortly_urls),\n  application:stop(shortly),\n  application:stop(gun),\n  Config.\n\ninit_per_testcase(_,Config) ->\n  Config.\n\nend_per_testcase(_, Config) ->\n  Config.\n\ntest_notfound(_) ->\n  NewUrl = url(\"http://not.com/existent\"),\n  {404, _,  #{}} = do_get_request(NewUrl).\n\ntest_created(_) ->\n  NewUrl = url(\"http://new.created.com/\"),\n  {201, _, #{<<\"url\">> := _}} = do_post_request(NewUrl).\n\ntest_ok(_) ->\n  LongUrl = url(\"htts://random.org\"),\n  {201, _, #{<<\"url\">> := ShortUrl}} = do_post_request(LongUrl),\n  {200, _, #{<<\"url\">> := ShortUrl}} = do_post_request(LongUrl).\n\ntest_redirect(_) ->\n  LongUrl = url(\"http://example.com/testing_redirect\"),\n  {201, _, #{<<\"url\">> := ShortUrl}} = do_post_request(LongUrl),\n  {302, Headers, #{<<\"url\">> := LongUrl}} = do_get_request(ShortUrl),\n  RedirectionHeader = list_to_binary(proplists:get_value(\"location\", Headers)),\n  LongUrl = RedirectionHeader.\n\ntest_ws(_) ->\n  WsConn = ws_connect(\"/news\"),\n  LongUrl = url(\"http://random.com/long\"),\n  {_, _, #{<<\"url\">> := ShortUrl}} = do_post_request(LongUrl),\n  JsonResponse = ws_get(WsConn),\n  Response = json_to_map(JsonResponse),\n  #{<<\"long_url\">> := LongUrl} = Response,\n  #{<<\"short_url\">> := ShortUrl} = Response,\n  ws_terminate(WsConn).\n\nget_request_url(Url) ->\n  BinaryReqUrl = iolist_to_binary([<<\"http://localhost:8080/\">>, Url]),\n  UrlStr = binary_to_list(BinaryReqUrl),\n  UrlStr.\n\njson_to_map(In) ->\n  InBinary = case is_list(In) of\n    true ->\n      list_to_binary(In);\n    _ ->\n      In\n  end,\n  case jsx:is_json(InBinary) of\n    true ->\n      jsx:decode(InBinary, [return_maps]);\n    _ ->\n      #{}\n  end.\n\ndo_post_request(Url) ->\n  ReqUrl = get_request_url(Url),\n  {ok, {{_, StatusCode, _}, Headers, Body}} =\n    httpc:request(post, {ReqUrl, [], [], []}, [], []),\n  {StatusCode, Headers, json_to_map(Body)}.\n\ndo_get_request(Url) ->\n  ReqUrl = get_request_url(Url),\n  {ok, {{_, StatusCode, _}, Headers, Body}} =\n    httpc:request(get, {ReqUrl, []},[{autoredirect,false}],[]),\n  {StatusCode, Headers, json_to_map(Body)}.\n\nurl(Url) ->\n  StringUrl = http_uri:encode(Url),\n  list_to_binary(StringUrl).\n\nws_connect(Path) ->\n\t{ok, Pid} = gun:open(\"127.0.0.1\", 8080, #{retry=>0}),\n\t{ok, http} = gun:await_up(Pid),\n\tRef = monitor(process, Pid),\n\tgun:ws_upgrade(Pid, Path, [], #{compress => true}),\n\treceive\n\t\t{gun_ws_upgrade, Pid, ok, _} ->\n\t\t\tok;\n\t\t_ ->\n      error(failed)\n\tend,\n\t{Pid, Ref}.\n\nws_get({Pid,_}) ->\n  receive\n    {gun_ws, Pid, {text, Text}} ->\n      Text;\n    _ ->\n        error(failed)\n  after 5000 ->\n    error(timout)\n  end.\n\nws_terminate({Pid, Ref}) ->\n  demonitor(Ref),\n  gun:close(Pid).\n\nrun_eunit(_Config) ->\n  ok = eunit:test(shortly_shortener_test).\n"
  },
  {
    "path": "libraries/shortly/test/shortly_shortener_test.erl",
    "content": "-module(shortly_shortener_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nidempotence_test() ->\n    shortly_shortener:init(),\n    LongUrl = <<\"long_url\">>,\n    {_, ShortUrl} = shortly_shortener:short(LongUrl),\n    LongUrl = shortly_shortener:get(ShortUrl).\n"
  },
  {
    "path": "otp/pool/README.md",
    "content": "# Worker Pool\n\n## Reading material\n\n- [Learn You Some Erlang: Who Supervises The Supervisors?](https://learnyousomeerlang.com/supervisors)\n- [Learn You Some Erlang: Building an Application With OTP](https://learnyousomeerlang.com/building-applications-with-otp)\n- [Erlang OTP Design Principles: Supervisor Behaviour](http://erlang.org/doc/design_principles/sup_princ.html)\n\n## Exercise\n\nFor this exercise create a pool of workers to compute a standard `{M, F, A}` or `{F, A}`. \n\nYou will write a `gen_server` that controls the pool of workers.  \nThe supervision tree will look like this:  \n  \n![Supervision tree](suptree.png)\n\nIn `poolie_sup` and `poolie_worker_sup` you will define appropriate supervision strategies and child specs.\n\n`poolie_server` implements the following api:  \n- `run/3`: Takes a module, a function and a list of args and dispatches the computation to an idle worker. If all workers are busy, asks user to try again later.\n￼- `run/2`: Same as `run/3`, but only takes a function and a list of args.\n- `pool_info/0`: Displays the number of idle and busy workers in the pool.\n\nYour task is to implement the `gen_server` callbacks in `poolie_server`, `poolie_worker_sup` and `poolie_worker` to handle the work requests.\n\n\n### Example\n```console\n1> poolie_server:run(fun(X) -> X + 1 end, [5]).\nRequest is being processed\nok\n\nGot results for {#Fun<erl_eval.6.128620087>,[5]}\nResult: 6\n\n2> poolie_server:run(lists, max, [[1,2,3,4,5]]).\nRequest is being processed\nok\n\nGot results for {lists,max,[[1,2,3,4,5]]}\nResult: 5\n```\n\n\n### Notes\n- Think about what supervisor strategies you should use.\n- Should you use `gen_server:call` or `gen_server:cast` to send work to your workers?\n"
  },
  {
    "path": "otp/pool/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n\n{shell, [\n    {apps, [poolie]}\n]}.\n"
  },
  {
    "path": "otp/pool/solution/poolie.app.src",
    "content": "{application, poolie,\n [{description, \"An OTP application\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { poolie_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "otp/pool/solution/poolie_app.erl",
    "content": "%%%-------------------------------------------------------------------\n%% @doc poolie public API\n%% @end\n%%%-------------------------------------------------------------------\n\n-module(poolie_app).\n\n-behaviour(application).\n\n%% Application callbacks\n-export([start/2, stop/1]).\n\n%%====================================================================\n%% API\n%%====================================================================\n\nstart(_StartType, _StartArgs) ->\n  poolie_sup:start_link().\n\n%%--------------------------------------------------------------------\nstop(_State) ->\n    ok.\n\n%%====================================================================\n%% Internal functions\n%%====================================================================\n"
  },
  {
    "path": "otp/pool/solution/poolie_server.erl",
    "content": "-module(poolie_server).\n\n-behaviour(gen_server).\n\n-export([start_link/0, stop/0, run/3, run/2, pool_info/0]).\n-export([init/1, handle_call/3, handle_cast/2]).\n\n-record(state, {limit, idle}).\n-define(N, 10). %% Number of workers\n\n%% API\n\nrun(M, F, A) when is_list(A) ->\n  Msg = gen_server:call(?MODULE, {work, {M, F, A}}),\n  io:format(Msg).\n\nrun(F, A) when is_function(F), is_list(A) ->\n  Msg = gen_server:call(?MODULE, {work, {F, A}}),\n  io:format(Msg).\n\npool_info() ->\n  {PoolSize, Idle, Busy} = gen_server:call(?MODULE, info),\n  io:format(\"Pool has ~p workers.~nThere are ~p idle and ~p busy workers.~n\", [PoolSize, Idle, Busy]).\n\nstart_link() ->\n  gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).\n\nstop() ->\n  gen_server:call({local, ?MODULE}, stop).\n\n%% gen_server callbacks\n\ninit(_Args) ->\n  Workers = poolie_worker_sup:add_workers(?N),\n  {ok, #state{limit=?N, idle=Workers}}.\n\nhandle_call({work, MFA}, _From, S = #state{idle=[Worker | Rest]}) -> \n  Msg = \"Request is being processed~n\",\n  gen_server:cast(Worker, {work, MFA}),\n  {reply, Msg, S#state{idle=Rest}};\n\nhandle_call({work, _MFA}, _From, State) -> \n  Msg = \"No idle workers at the moment, please try again later~n\",\n  {reply, Msg, State};\n\nhandle_call(info, _From, S = #state{limit=Limit, idle=Idle}) ->\n  IdleWorkers = length(Idle),\n  {reply, {Limit, IdleWorkers, Limit - IdleWorkers}, S};\n\nhandle_call(stop, _From, State) ->\n  {stop, normal, stopped, State};\n\nhandle_call(_Request, _From, State) ->\n  {reply, ok, State}.\n\nhandle_cast({result, {Worker, MFA, Result}}, S = #state{idle=Idle}) ->\n  io:format(\"Got results for ~p~nResult: ~p~n\", [MFA, Result]),\n  {noreply, S#state{idle=[Worker | Idle]}};\n\nhandle_cast(_Msg, State) ->\n  {noreply, State}.\n"
  },
  {
    "path": "otp/pool/solution/poolie_sup.erl",
    "content": "%%%-------------------------------------------------------------------\n%% @doc poolie top level supervisor.\n%% @end\n%%%-------------------------------------------------------------------\n\n-module(poolie_sup).\n\n-behaviour(supervisor).\n\n%% API\n-export([start_link/0]).\n\n%% Supervisor callbacks\n-export([init/1]).\n\n%%====================================================================\n%% API functions\n%%====================================================================\n\nstart_link() ->\n    supervisor:start_link(?MODULE, []).\n\n%%====================================================================\n%% Supervisor callbacks\n%%====================================================================\n\n%% Child :: #{id => Id, start => {M, F, A}}\n%% Optional keys are restart, shutdown, type, modules.\n%% Before OTP 18 tuples must be used to specify a child. e.g.\n%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}\ninit([]) ->\n  WorkerSup = #{id => poolie_worker_sup,\n    start => {poolie_worker_sup, start_link, []}, \n    restart => permanent, shutdown => 10000, type => supervisor, modules => [poolie_worker_sup]},\n  Server = #{id => poolie_server,\n    start => {poolie_server, start_link, []}, \n    restart => permanent, shutdown => 10000, type => worker, modules => [poolie_server]},\n  {ok, {{one_for_all, 5, 150}, [WorkerSup, Server]}}.\n\n%%====================================================================\n%% Internal functions\n%%====================================================================\n\n"
  },
  {
    "path": "otp/pool/solution/poolie_worker.erl",
    "content": "-module(poolie_worker).\n\n-behaviour(gen_server).\n\n%% API\n-export([start/0]).\n-export([init/1, handle_call/3, handle_cast/2]).\n\n-define(SERVER, poolie_server).\n\n%% API\n\nstart() ->\n  gen_server:start(?MODULE, [], []).\n\n%% gen_server callbacks\n\ninit(_Args) ->\n  {ok, []}.\n\nhandle_call(_Request, _From, State) ->\n  {reply, ok, State}.\n\nhandle_cast({work, MFA = {M, F, A}}, State) ->\n  Result = erlang:apply(M, F, A),\n  gen_server:cast(?SERVER, {result, {self(), MFA, Result}}),\n  {noreply, State};\n\nhandle_cast({work, FA = {F, A}}, State) ->\n  Result = erlang:apply(F, A),\n  gen_server:cast(?SERVER, {result, {self(), FA, Result}}),\n  {noreply, State};\n\nhandle_cast(_Msg, State) ->\n  {noreply, State}.\n"
  },
  {
    "path": "otp/pool/solution/poolie_worker_sup.erl",
    "content": "-module(poolie_worker_sup).\n\n-behaviour(supervisor).\n\n-export([start_link/0, add_workers/1, init/1]).\n\n%% API\n\nstart_link() ->\n  supervisor:start_link({local, ?MODULE}, ?MODULE, []).\n\nadd_workers(N) when N >= 0 ->\n  add_workers(N, []).\n\nadd_workers(0, Workers) ->\n  Workers;\nadd_workers(N, Workers) ->\n  {ok, Pid} = supervisor:start_child(?MODULE, []),\n  add_workers(N-1, [Pid | Workers]).\n\n%% supervisor callbacks\n\ninit(_Args) ->\n  {ok, {{simple_one_for_one, 5, 500}, [{poolie_worker, {poolie_worker, start, []}, \n  permanent, 5000, worker, [poolie_worker]}]}}.\n"
  },
  {
    "path": "otp/pool/src/poolie.app.src",
    "content": "{application, poolie,\n [{description, \"An OTP application\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { poolie_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "otp/pool/src/poolie_app.erl",
    "content": "%%%-------------------------------------------------------------------\n%% @doc poolie public API\n%% @end\n%%%-------------------------------------------------------------------\n\n-module(poolie_app).\n\n-behaviour(application).\n\n%% Application callbacks\n-export([start/2, stop/1]).\n\n%%====================================================================\n%% API\n%%====================================================================\n\nstart(_StartType, _StartArgs) ->\n  poolie_sup:start_link().\n\n%%--------------------------------------------------------------------\nstop(_State) ->\n    ok.\n\n%%====================================================================\n%% Internal functions\n%%====================================================================\n"
  },
  {
    "path": "otp/pool/src/poolie_server.erl",
    "content": "-module(poolie_server).\n\n-behaviour(gen_server).\n\n-export([start_link/0, stop/0, run/3, run/2, pool_info/0]).\n-export([init/1, handle_call/3, handle_cast/2]).\n\n-record(state, {limit, idle}).\n\n-define(N, 10). %% Number of workers\n\n%% API\n\nrun(M, F, A) when is_list(A) ->\n  Msg = gen_server:call(?MODULE, {work, {M, F, A}}),\n  io:format(Msg).\n\nrun(F, A) when is_function(F), is_list(A) ->\n  Msg = gen_server:call(?MODULE, {work, {F, A}}),\n  io:format(Msg).\n\npool_info() ->\n  {PoolSize, Idle, Busy} = gen_server:call(?MODULE, info),\n  io:format(\"Pool has ~p workers.~nThere are ~p idle and ~p busy workers.~n\", [PoolSize, Idle, Busy]).\n\nstart_link() ->\n  gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).\n\nstop() ->\n  gen_server:call({local, ?MODULE}, stop).\n\n%% gen_server callbacks\n\ninit(_Args) ->\n  Workers = poolie_worker_sup:add_workers(?N),\n  {ok, #state{limit=?N, idle=Workers}}.\n\nhandle____({result, {Worker, MFA, Result}}, State) ->\n  put_your_solution_here.\n\nhandle_call({work, MFA}, _From, State) -> \n  put_your_solution_here;\n\nhandle_call(info, _From, S = #state{limit=Limit, idle=Idle}) ->\n  IdleWorkers = length(Idle),\n  {reply, {Limit, IdleWorkers, Limit - IdleWorkers}, S};\n\nhandle_call(stop, _From, State) ->\n  {stop, normal, stopped, State};\n\nhandle_call(_Request, _From, State) ->\n  {reply, ok, State}.\n\nhandle_cast(_Msg, State) ->\n  {noreply, State}.\n"
  },
  {
    "path": "otp/pool/src/poolie_sup.erl",
    "content": "%%%-------------------------------------------------------------------\n%% @doc poolie top level supervisor.\n%% @end\n%%%-------------------------------------------------------------------\n\n-module(poolie_sup).\n\n-behaviour(supervisor).\n\n%% API\n-export([start_link/0]).\n\n%% Supervisor callbacks\n-export([init/1]).\n\n%%====================================================================\n%% API functions\n%%====================================================================\n\nstart_link() ->\n    supervisor:start_link(?MODULE, []).\n\n%%====================================================================\n%% Supervisor callbacks\n%%====================================================================\n\n%% Child :: #{id => Id, start => {M, F, A}}\n%% Optional keys are restart, shutdown, type, modules.\n%% Before OTP 18 tuples must be used to specify a child. e.g.\n%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}\ninit([]) ->\n  WorkerSup = worker_supervisor_spec,\n  Server = pool_server_spec,\n  {ok, {{supervisor_strategy}, [WorkerSup, Server]}}.\n\n%%====================================================================\n%% Internal functions\n%%====================================================================\n\n"
  },
  {
    "path": "otp/pool/src/poolie_worker.erl",
    "content": "-module(poolie_worker).\n\n-behaviour(gen_server).\n\n%% API\n-export([start/0]).\n-export([init/1, handle_call/3, handle_cast/2]).\n\n-define(SERVER, poolie_server).\n\n%% API\n\nstart() ->\n  gen_server:start(?MODULE, [], []).\n\n%% gen_server callbacks\n\ninit(_Args) ->\n  {ok, []}.\n\nhandle____({work, MFA = {M, F, A}}, State) ->\n  put_your_solution_here;\n\nhandle____({work, FA = {F, A}}, State) ->\n  put_your_solution_here.\n\nhandle_cast(_Msg, State) ->\n  {noreply, State}.\nhandle_call(_Request, _From, State) ->\n  {reply, ok, State}.\n"
  },
  {
    "path": "otp/pool/src/poolie_worker_sup.erl",
    "content": "-module(poolie_worker_sup).\n\n-behaviour(supervisor).\n\n-export([start_link/0, add_workers/1, init/1]).\n\n%% API\n\nstart_link() ->\n  supervisor:start_link({local, ?MODULE}, ?MODULE, []).\n\nadd_workers(N) when N >= 0 ->\n  put_your_solution_here.\n\n%% supervisor callbacks\n\ninit(_Args) ->\n  Worker = worker_spec,\n  {ok, {{worker_supervisor_strategy}, [Worker]}}.\n"
  },
  {
    "path": "otp/pool/test/poolie_SUITE.erl",
    "content": "-module(poolie_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  {ok, _Pid} = application:ensure_all_started(poolie),\n  ok = eunit:test(poolie_test).\n"
  },
  {
    "path": "otp/pool/test/poolie_test.erl",
    "content": "-module(poolie_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\npoolie_worker_sup_test() ->\n    WorkerCount = worker_count(poolie_worker_sup),\n\n    %% Add 5 workers\n    WorkerList = poolie_worker_sup:add_workers(5),\n    ?assertEqual(length(WorkerList), 5),\n\n    NewWorkerCount = worker_count(poolie_worker_sup),\n    ?assertEqual(NewWorkerCount, WorkerCount + 5).\n\n%% Helper functions\n\nworker_count(Sup) ->\n    WorkerSup = supervisor:count_children(Sup),\n    {workers, WorkerCount} = proplists:lookup(workers, WorkerSup),\n    WorkerCount.\n"
  },
  {
    "path": "otp/shopping_cart/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n"
  },
  {
    "path": "otp/shopping_cart/README.md",
    "content": "# Shopping Cart\n\n## Reading Material\n\n- [Learn You Some Erlang: What is OTP?](http://learnyousomeerlang.com/what-is-otp)\n- [Learn You Some Erlang: Clients and Servers](http://learnyousomeerlang.com/clients-and-servers)\n\n## Exercise\n\nIn this exercise we are going to model a shopping cart. You can put items inside it (an item is just a tuple `{ItemName, Price}`) and ask it for the total cost of all the items.\n\nSo, using a [gen_server](http://erlang.org/doc/man/gen_server.html), write the following functions:\n\n* `shopping_cart:start_link/0`: Start an empty shopping cart.\n* `shopping_cart:put_item/2`: Takes a PID and an item (a tuple) and saves it into the shopping cart.\n* `shopping_cart:cost_so_far/1`: Calculates the cost of all the items inside the shopping cart.\n* `finish/1`: Print the total price and finishes the shopping cart.\n\n* All the functions required by the behaviour.\n\nExample:\n``` erlang\n{ok, Pid} = shopping_cart:start_link().\nshopping_cart:put_item(Pid, {orange, 2}).\n%% [{orange, 2}]\nshopping_cart:put_item(Pid, {apple, 1}).\n%% [{apple, 1}, {orange, 2}]\nshopping_cart:cost_so_far(Pid).\n%% 3\n```\n[solution](src/solution/shopping_cart.erl)\n"
  },
  {
    "path": "otp/shopping_cart/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "otp/shopping_cart/solution/shopping_cart.app.src",
    "content": "{application, shopping_cart,\n [{description, \"shopping_part\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { shopping_cart_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "otp/shopping_cart/solution/shopping_cart.erl",
    "content": "-module(shopping_cart).\n\n-behaviour(gen_server).\n\n-export([start_link/0,\n         put_item/2,\n         finish/1,\n         init/1,\n         handle_call/3,\n         terminate/2,\n         cost_so_far/1,\n\t handle_cast/2\n        ]).\n\n%% Client functions\nstart_link() ->\n     gen_server:start_link(?MODULE, [], []).\n\nput_item(Pid, Item) ->\n    gen_server:call(Pid, {item, Item}).\n\ncost_so_far(Pid) ->\n    gen_server:call(Pid, cost).\n\nfinish(Pid) ->\n    gen_server:call(Pid, terminate).\n\n%% Server functions\ninit([]) ->\n    {ok, []}.\n\nhandle_call({item, Item}, _From, Cart) ->\n    {reply, [Item|Cart], [Item|Cart]};\n\nhandle_call(terminate, _From, Cart) ->\n    {stop, normal, ok, Cart};\n\nhandle_call(cost, _From, Cart) ->\n    Total_Price = total_price(Cart),\n    {reply, Total_Price, Cart}.\n\nhandle_cast(_, _) ->\n    {noreply, ok}.\n\nterminate(normal, Cart) ->\n    io:format(\"The total price was: ~p. ~n\", [total_price(Cart)]),\n    ok.\n\n%% Private functions\ntotal_price(Cart) ->\n    Prices = lists:map(fun({_, Price}) -> Price end, Cart),\n    lists:sum(Prices).\n"
  },
  {
    "path": "otp/shopping_cart/src/shopping_cart.app.src",
    "content": "{application, shopping_cart,\n [{description, \"shopping_part\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { shopping_cart_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "otp/shopping_cart/src/shopping_cart.erl",
    "content": "-module(shopping_cart).\n\n-behaviour(gen_server).\n\n-export([start_link/0,\n         put_item/2,\n         finish/1,\n         cost_so_far/1,\n        ]).\n\nstart_link() ->\n     put_your_solution_here.\n\nput_item(Pid, Item) ->\n    put_your_solution_here.\n\ncost_so_far(Pid) ->\n    put_your_solution_here.\n\nfinish(Pid) ->\n    put_your_solution_here.\n"
  },
  {
    "path": "otp/shopping_cart/test/shopping_cart_SUITE.erl",
    "content": "-module(shopping_cart_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(shopping_cart_test).\n"
  },
  {
    "path": "otp/shopping_cart/test/shopping_cart_test.erl",
    "content": "-module(shopping_cart_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nshopping_cart_add_item_test() ->\n    {ok, Pid} = shopping_cart:start_link(),\n    Orange = {orange, 3}, % tuple with name and price\n    Ans = shopping_cart:put_item(Pid, Orange),\n    ?assertEqual(Ans, [{orange, 3}]).\n\nshopping_cart_calculate_cost_test() ->\n    {ok, Pid} = shopping_cart:start_link(),\n    Apple = {apple, 2},\n    shopping_cart:put_item(Pid, Apple),\n    Cost = shopping_cart:cost_so_far(Pid),\n    ?assertEqual(Cost, 2).\n"
  },
  {
    "path": "sequential/bank_accounts/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n.lock\n"
  },
  {
    "path": "sequential/bank_accounts/README.md",
    "content": "# Bank Accounts\n\n## Reading Material\n\n- [Learn You Some Erlang: Errors and Exceptions](http://learnyousomeerlang.com/errors-and-exceptions)\n\n## Exercise\n\nCreate a function `bank_account:process_operation/2` that takes a bank (a list of accounts: `{AccountNumber, AmountOfMoney}`) and an operation (a tuple: `{AccountNumber, Operation_Type, Amount}`) and process it, returning the resulting account.\n\n#### Operations\nThere are two kind of operations: `withdraw` and `deposit`.\n\n#### Examples\n\n``` erlang\n1> bank_account:process_operation([{1, 100}, {2, 45}], {1, withdraw, 25}).\n%% {1, 75}\n\n2> process_operation([{1, 100}, {2, 45}], {2, deposit, 15}).\n%% {2, 60}\n```\n\n#### Error Handling\n* If the account specified in the operation doesn't exist (i.e. there is no tuple in the list with that account number), return a tuple `{error, account_not_found}`.\n\n* If you try to withdraw from an account with insufficient funds, return a tuple `{error, insufficient_funds}`.\n\n#### Examples\n\n``` erlang\n1> bank_account:process_operation([{1, 100}, {2, 45}], {7, deposit, 55}).\n%% {error, account_not_found}\n\n2> bank_account:process_operation([{1, 100}, {2, 45}], {2, withdraw, 100}).\n%% {error, insufficient_funds}\n```\n\nRun tests with `$> make`.\n\nAs a hint, the file you should be editing is `src/bank_account.erl`. But in any case if the things get difficult you can check our [proposed solution](solution/bank_account.erl).\n"
  },
  {
    "path": "sequential/bank_accounts/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "sequential/bank_accounts/solution/bank_account.app.src",
    "content": "{application, bank_account,\n [{description, \"bank_account\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { bank_account_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/bank_accounts/solution/bank_account.erl",
    "content": "-module(bank_account).\n\n-export([process_operation/2]).\n\noperate({AccountNumber, Money}, withdraw, Amount) ->\n  case (Money - Amount) < 0 of\n    true  -> {error, insufficient_funds};\n    false -> {AccountNumber, Money - Amount}\n  end;\noperate({AccountNumber, Money}, deposit, Amount) when Amount >= 0 ->\n  {AccountNumber, Money + Amount}.\n\nprocess_operation(Bank, {AccountNumber, Operation_Type, Amount}) ->\n  case lists:keyfind(AccountNumber, 1, Bank) of\n    false   -> {error, account_not_found};\n    Account -> operate(Account, Operation_Type, Amount)\n  end.\n"
  },
  {
    "path": "sequential/bank_accounts/src/bank_account.app.src",
    "content": "{application, bank_account,\n [{description, \"bank_account\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { bank_account_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/bank_accounts/src/bank_account.erl",
    "content": "-module(bank_account).\n\n-export([process_operation/2]).\n\nprocess_operation(Bank, Operation) ->\n  put_your_solution_here.\n"
  },
  {
    "path": "sequential/bank_accounts/test/bank_account_SUITE.erl",
    "content": "-module(bank_account_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(bank_account_test).\n"
  },
  {
    "path": "sequential/bank_accounts/test/bank_account_test.erl",
    "content": "-module(bank_account_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nprocess_operation_withdraw_ok_test() ->\n  Bank = [{213, 150}, {214, 0}],\n  Op = {213, withdraw, 50},\n  Res = {213, 100},\n  ?assertEqual(Res, bank_account:process_operation(Bank, Op)).\n\nprocess_operation_not_found_test() ->\n  Bank = [{214, 0}],\n  Op = {213, deposit, 20},\n  Res = {error, account_not_found},\n  ?assertEqual(Res, bank_account:process_operation(Bank, Op)).\n\nprocess_operation_insufficient_funds_test() ->\n  Bank = [{213, 150}, {214, 0}],\n  Op = {213, withdraw, 200},\n  Res = {error, insufficient_funds},\n  ?assertEqual(Res, bank_account:process_operation(Bank, Op)).\n"
  },
  {
    "path": "sequential/calculate_bmi/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n.lock\n"
  },
  {
    "path": "sequential/calculate_bmi/README.md",
    "content": "# Calculate BMI\n\n## Reading Material\n\n- [Learn You Some Erlang](http://learnyousomeerlang.com/a-short-visit-to-common-data-structures)\n\n## Exercise\n\nWrite a function `calculate_bmi:bmi/1` that takes a person (the record defined in `src/person_record.hrl`) as argument and calculate her [body mass index (BMI)](https://en.wikipedia.org/wiki/Body_mass_index).\n\nThen, write `calculate_bmi:classify/1` to classify a person according to her BMI:\n\n* `underweight`: when the BMI is less than 18.5.\n\n* `normal`: when the BMI is greater than 18.5 and less than 25.\n\n* `overweight`: when the BMI is between 25 and 30.\n\n* `obese`: when the BMI is greater than 30.\n\n#### Examples\n``` erlang\n1> calculate_bmi:bmi(#person{name = \"Maria\", weight = 60, height = 1.6}).\n%% 23.437499999999996\n\n2> calculate_bmi:classify(#person{name = \"Maria\", weight = 60, height = 1.6}).\n%% normal\n```\n\nRun tests with ``make``.\n\nAs a hint, the file you should be editing is `src/calculate_bmi.erl`. But in any\ncase if the things get difficult you can check our [proposed solution](solution/calculate_bmi.erl).\n"
  },
  {
    "path": "sequential/calculate_bmi/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "sequential/calculate_bmi/solution/calculate_bmi.app.src",
    "content": "{application, calculate_bmi,\n [{description, \"calculate_bmi\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { calculate_bmi_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/calculate_bmi/solution/calculate_bmi.erl",
    "content": "-module(calculate_bmi).\n\n-export([bmi/1, classify/1]).\n\n-include(\"../src/person_record.hrl\").\n\nbmi(P) when is_record(P, person) ->\n  P#person.weight / math:pow(P#person.height, 2).\n\nclassify(P) when is_record(P, person) ->\n  classify(bmi(P));\nclassify(BMI) when BMI > 25 andalso BMI < 30 ->\n  overweight;\nclassify(BMI) when BMI < 18.5 ->\n  underweight;\nclassify(BMI) when BMI > 30 ->\n  obese;\nclassify(BMI) when BMI > 18.5 andalso BMI < 25 ->\n  normal.\n"
  },
  {
    "path": "sequential/calculate_bmi/solution/person_record.hrl",
    "content": "% person record declaration:\n% name\n% weight specified in kg.\n% height specified in metres.\n-record(person, {name, weight, height}).\n"
  },
  {
    "path": "sequential/calculate_bmi/src/calculate_bmi.app.src",
    "content": "{application, calculate_bmi,\n [{description, \"calculate_bmi\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { calculate_bmi_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/calculate_bmi/src/calculate_bmi.erl",
    "content": "-module(calculate_bmi).\n\n-export([bmi/1, classify/1]).\n\n-include(\"../src/person_record.hrl\").\n\nbmi(Person) ->\n  put_your_solution_here.\n\nclassify(Person) ->\n  put_your_solution_here.\n"
  },
  {
    "path": "sequential/calculate_bmi/src/person_record.hrl",
    "content": "% person record declaration:\n% name\n% weight specified in kg.\n% height specified in metres.\n-record(person, {name, weight, height}).\n"
  },
  {
    "path": "sequential/calculate_bmi/test/calculate_bmi_SUITE.erl",
    "content": "-module(calculate_bmi_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(calculate_bmi_test).\n"
  },
  {
    "path": "sequential/calculate_bmi/test/calculate_bmi_test.erl",
    "content": "-module(calculate_bmi_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\n-include(\"../src/person_record.hrl\").\n\ncalculate_bmi_bmi_calculation_test() ->\n  Person = #person{ name = \"Alicia\", weight = 62, height = 1.63},\n  ?assertEqual(23.335466144755166, calculate_bmi:bmi(Person)).\n\ncalculate_bmi_classify_test() ->\n  Person1 = #person{ name = \"Alicia\", weight = 62, height = 1.63},\n  Person2 = #person{ name = \"Peter\", weight = 104, height = 1.8},\n  Person3 = #person{ name = \"Ana\", weight = 52, height = 1.7},\n  Person4 = #person{ name = \"Charles\", weight = 84, height = 1.75},\n  ?assertEqual(normal, calculate_bmi:classify(Person1)),\n  ?assertEqual(obese, calculate_bmi:classify(Person2)),\n  ?assertEqual(underweight, calculate_bmi:classify(Person3)),\n  ?assertEqual(overweight, calculate_bmi:classify(Person4)).\n"
  },
  {
    "path": "sequential/filter_fibonacci_numbers/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n.lock\n"
  },
  {
    "path": "sequential/filter_fibonacci_numbers/README.md",
    "content": "# Filter Fibonacci Numbers\n\n## Reading material\n\n- [Fibonnaci number](https://en.wikipedia.org/wiki/Fibonacci_number)\n\n## Exercise\n\nCreate a function `filter_fibonacci_numbers:filter/1` that takes a list and filter every Fibonnaci number using **list comprehensions**.\n\nTip: The nth Fibonnaci number, *Fn*, can be calculated by the sum of the two preceding ones.\nUsing F0 = 1 and F1 = 1 as initial values we have:\n\n1, 1, 2, 3, 5, 8, 13, 21, 54, ...\n\nExample:\n``` erlang\n1> filter_fibonacci_numbers:filter([1, 2, 3, 4, 5, 7, 8]).\n%% [1, 2, 3, 5, 8]\n```\n\nWrite your answer in `src/filter_fibonacci_numbers.erl`. You can check your answer by doing `$> make` inside this directory.\n\nIf things gets difficult you can check our [proposed solution](solution/filter_fibonacci_numbers.erl).\n"
  },
  {
    "path": "sequential/filter_fibonacci_numbers/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "sequential/filter_fibonacci_numbers/solution/filter_fibonacci_numbers.app.src",
    "content": "{application, filter_fibonacci_numbers,\n [{description, \"filter_fibonacci_numbers\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { filter_fibonacci_numbers_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/filter_fibonacci_numbers/solution/filter_fibonacci_numbers.erl",
    "content": "-module(filter_fibonacci_numbers).\n\n-export([filter/1]).\n\nfibonacci(1) ->\n  1;\nfibonacci(2) ->\n  2;\nfibonacci(N) when N > 2 ->\n  fibonacci(N-1) + fibonacci(N-2).\n\nis_fibonacci_number(X) ->\n  is_fibonacci_number(X, []).\nis_fibonacci_number(X, []) ->\n  F1 = fibonacci(1),\n  case X =:= F1 of\n    true ->\n        true;\n    false ->\n        is_fibonacci_number(X, [fibonacci(1)])\n  end;\nis_fibonacci_number(X, Fibs) ->\n  Fn = fibonacci(length(Fibs)),\n  case X =:= Fn of\n    true ->\n        true;\n    false when X > Fn ->\n        is_fibonacci_number(X, [Fn|Fibs]);\n    _ -> false\n  end.\n\nfilter(L) ->\n  [X || X <- L, is_fibonacci_number(X)].\n"
  },
  {
    "path": "sequential/filter_fibonacci_numbers/src/filter_fibonacci_numbers.app.src",
    "content": "{application, filter_fibonacci_numbers,\n [{description, \"filter_fibonacci_numbers\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { filter_fibonacci_numbers_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/filter_fibonacci_numbers/src/filter_fibonacci_numbers.erl",
    "content": "-module(filter_fibonacci_numbers).\n\n-export([filter/1]).\n\nfilter(List) ->\n  put_your_solution_here.\n"
  },
  {
    "path": "sequential/filter_fibonacci_numbers/test/filter_fibonacci_numbers_SUITE.erl",
    "content": "-module(filter_fibonacci_numbers_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(filter_fibonacci_numbers_test).\n"
  },
  {
    "path": "sequential/filter_fibonacci_numbers/test/filter_fibonacci_numbers_test.erl",
    "content": "-module(filter_fibonacci_numbers_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nfilter_fibonacci_numbers_test() ->\n  List = [1, 2, 3, 4, 5, 7, 8, 9, 10],\n  ?assertEqual([1, 2, 3, 5, 8], filter_fibonacci_numbers:filter(List)).\n"
  },
  {
    "path": "sequential/filter_numbers/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n.lock\n"
  },
  {
    "path": "sequential/filter_numbers/README.md",
    "content": "# Filter Numbers\n\n## Reading material\n\n- [Learn You Some Erlang: Starting Out (for real)](http://learnyousomeerlang.com/starting-out-for-real)\n- How to make a list of numbers: [lists:seq/2](http://erlang.org/doc/man/lists.html#seq-2)\n\n## Exercises\n\n### Filter in\n\nCreate a function `filter_numbers:filter_in/3` that takes three integers (`From`, `To`, and `N`) and returns a list of all the numbers between `From` and `To` that are multiples of `N`. You **must** use list comprehension.\n\n```erlang\n1> filter_numbers:filter_in(1,100, 7).\n[7,14,21,28,35,42,49,56,63,70,77,84,91,98]\n```\n\n### Filter out\n\nCreate a function `filter_numbers:filter_out/3` that takes three integers (`From`, `To`, and `N`) and returns a list of all the numbers between `From` and `To` that are **not** multiples of `N`. You **must** use list comprehension.\n\n```erlang\n1> filter_numbers:filter_out(1,10, 7).\n[1,2,3,4,5,6,8,9,10]\n```\n\n### Filter in values\n\nCreate a function `filter_numbers:filter_in_values/3` that takes two integers and a tuple of two integers(`From`, `To`, and `{Min, Max}`) and returns a list of all the numbers between `From` and `To` that are between the values of `Min` and `Max`. You **must** use list comprehension.\n\n```erlang\n1> filter_numbers:filter_in_values(1,100,{25,38}).\n[25,26,27,28,29,30,31,32,33,34,35,36,37,38]\n```\n\n## Testing your solution\n\nTo test your solution go to the root of the erlings folder and do `make sequential/filter_numbers`. This will run the tests for this specific exercise.\n\n## Our solution\n\nYou can check our [proposed solution](solution/filter_numbers.erl).\n"
  },
  {
    "path": "sequential/filter_numbers/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "sequential/filter_numbers/solution/filter_numbers.app.src",
    "content": "{application, filter_numbers,\n [{description, \"filter_numbers\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { filter_numbers_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/filter_numbers/solution/filter_numbers.erl",
    "content": "-module(filter_numbers).\n\n-export([filter_in/3,\n         filter_out/3,\n         filter_in_values/3]).\n\nfilter_in(From, To, N) ->\n  [X || X <- lists:seq(From, To), X rem N =:= 0].\n\nfilter_out(From, To, N) ->\n  [X || X <- lists:seq(From, To), X rem N =/= 0].\n\nfilter_in_values(From, To, {Min, Max}) ->\n  [X || X <- lists:seq(From, To), X >= Min, X =< Max].\n"
  },
  {
    "path": "sequential/filter_numbers/src/filter_numbers.app.src",
    "content": "{application, filter_numbers,\n [{description, \"filter_numbers\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { filter_numbers_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/filter_numbers/src/filter_numbers.erl",
    "content": "-module(filter_numbers).\n\n-export([filter_in/3,\n         filter_out/3,\n         filter_in_values/3]).\n\nfilter_in(From, To, N) ->\n  put_your_solution_here.\n\nfilter_out(From, To, N) ->\n  put_your_solution_here.\n\nfilter_in_values(From, To, {Min, Max}) ->\n  put_your_solution_here.\n"
  },
  {
    "path": "sequential/filter_numbers/test/filter_numbers_SUITE.erl",
    "content": "-module(filter_numbers_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(filter_numbers_test).\n"
  },
  {
    "path": "sequential/filter_numbers/test/filter_numbers_test.erl",
    "content": "-module(filter_numbers_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nfilter_numbers_in_test() ->\n  List = [7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98],\n  ?assertEqual(List, filter_numbers:filter_in(1, 100, 7)).\n\nfilter_numbers_out_test() ->\n  List = [1, 2, 3, 4, 5, 6, 8, 9, 10],\n  ?assertEqual(List, filter_numbers:filter_out(1, 10, 7)).\n\nfilter_numbers_in_values_test() ->\n  List = [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38],\n  ?assertEqual(List, filter_numbers:filter_in_values(1, 100, {25, 38})).\n"
  },
  {
    "path": "sequential/hello/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n"
  },
  {
    "path": "sequential/hello/README.md",
    "content": "# Hello world\n\n## Reading Material\n\n- [Learn You Some Erlang: Modules](http://learnyousomeerlang.com/modules)\n\n## Exercise\n\nThis is the most iconic exercise in coding history, we should include\nthis one. Your only task is to create a function `hello:hello/0` that\nreturns the binary ``<<\"hello world\">>`` (don't worry about understanding binaries, yet). To test that is working correctly check it running `make`.\n\nAs a hint, the file you should be editing is `src/hello.erl`. But in any\ncase if the things get difficult you can check our\n[proposal solution](solution/hello.erl).\n"
  },
  {
    "path": "sequential/hello/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "sequential/hello/solution/hello.app.src",
    "content": "{application, hello,\n [{description, \"hello\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { hello_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/hello/solution/hello.erl",
    "content": "-module(hello).\n\n-export([hello/0]).\n\nhello() ->\n  <<\"hello world\">>.\n"
  },
  {
    "path": "sequential/hello/src/hello.app.src",
    "content": "{application, hello,\n [{description, \"hello\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { hello_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/hello/src/hello.erl",
    "content": "-module(write_your_module_here).\n\n-export([function_you_want_to_export/0]).\n\nhello() ->\n  return_your_binary.\n"
  },
  {
    "path": "sequential/hello/test/hello_SUITE.erl",
    "content": "-module(hello_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(hello_test).\n"
  },
  {
    "path": "sequential/hello/test/hello_test.erl",
    "content": "-module(hello_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nhello_world_test() ->\n  <<\"hello world\">> = hello:hello().\n"
  },
  {
    "path": "sequential/hello_pattern/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n"
  },
  {
    "path": "sequential/hello_pattern/README.md",
    "content": "# Hello Pattern\n\n## Reading Material\n\n- [Learn You Some Erlang: Syntax in functions](http://learnyousomeerlang.com/syntax-in-functions)\n\n## Exercise\n\nIn this exercise we will introduce you to pattern matching and guards. Write a function `hello_pattern:hello/1` which takes a tuple:\n\n- `{morning, Name}`, ignores the name and returns `morning`.\n- `{evening, Name}`, returns a tuple `{good, evening, Name}`.\n- `{night, Name}`, ignores the name and return `night`.\n- `{math_class, Number, Name}`. If the number is lower than zero, return\n  `none`, in any other case return `{math_class, Name}`.\n\nResolve this exercise without using `if`, `case`. You should use pattern matching and guard only.\n\nCheck if your solution is working running `make`. And if your find your self in trouble you can always check our [suggested solution](solution/hello_pattern.erl).\n"
  },
  {
    "path": "sequential/hello_pattern/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "sequential/hello_pattern/solution/hello_pattern.app.src",
    "content": "{application, hello_pattern,\n [{description, \"hello_pattern\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { hello_pattern_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/hello_pattern/solution/hello_pattern.erl",
    "content": "-module(hello_pattern).\n\n-export([hello/1]).\n\nhello({morning, _Name}) ->\n  morning;\nhello({evening,  Name}) ->\n  {good, evening, Name};\nhello({night,   _Name}) ->\n  night;\nhello({math_class,  Number, _Name}) when Number < 0 ->\n  none;\nhello({math_class, _Number,  Name}) ->\n  {math_class, Name}.\n"
  },
  {
    "path": "sequential/hello_pattern/src/hello_pattern.app.src",
    "content": "{application, hello_pattern,\n [{description, \"hello_pattern\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { hello_pattern_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/hello_pattern/src/hello_pattern.erl",
    "content": "-module(hello_pattern).\n\n-export([hello/1]).\n\nhello({tuple_element_1, tuple_element_2}) ->\n  resolve.\n"
  },
  {
    "path": "sequential/hello_pattern/test/hello_pattern_SUITE.erl",
    "content": "-module(hello_pattern_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(hello_pattern_test).\n"
  },
  {
    "path": "sequential/hello_pattern/test/hello_pattern_test.erl",
    "content": "-module(hello_pattern_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nhello_day_test() ->\n  morning =\n    hello_pattern:hello({morning, \"Jimmy\"}),\n  {good, evening, \"Rick\"} =\n    hello_pattern:hello({evening, \"Rick\"}),\n  night =\n    hello_pattern:hello({night, \"Morty\"}).\n\nhello_math_class_test() ->\n  none =\n    hello_pattern:hello({math_class, -100, \"K-Colored\"}),\n  {math_class, \"Analysis II\"} =\n    hello_pattern:hello({math_class, 10, \"Analysis II\"}).\n"
  },
  {
    "path": "sequential/insert_element_at/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n"
  },
  {
    "path": "sequential/insert_element_at/README.md",
    "content": "# Insert element at position\n\n## Reading Material\n\n- [Learn You Some Erlang: A Short Visit to Common Data Structures](http://learnyousomeerlang.com/a-short-visit-to-common-data-structures)\n- [Learn You Some Erlang: Maps](http://learnyousomeerlang.com/maps#what-maps-shall-be)\n\n## Exercise\n\nSince we already know recursion we can play a little bit more, now consider we have a list of element, and we want to replace that\nelement with another one, but at the same time remember the old one.\nFor example if we have `[1,2,3]` and we replace the second element with\n`\"hi\"`, it should be `[1, #{current => \"hi\", old => 2}]`. And if we\ncall that function with `[1, #{current => 10, old => 0}, 3]` replacing the\nsecond element with \"hi\" the result should be `[1, #{current => \"hi\", old => 10}, 3]`.\n\nThe function should be `insert_element_at:insert/3`. A sample call of the\nprevious example could be `insert([1,2,3], 2, \"hi\")`. If you are stuck or\nwant to compare check [our solution](solution/insert_element_at.erl).\n"
  },
  {
    "path": "sequential/insert_element_at/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "sequential/insert_element_at/solution/insert_element_at.app.src",
    "content": "{application, insert_element_at,\n [{description, \"insert_element_at\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { insert_element_at_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/insert_element_at/solution/insert_element_at.erl",
    "content": "-module(insert_element_at).\n\n-export([insert/3]).\n\ninsert([H|T], Pos, Element) ->\n  insert(Pos, Element, H, [], T).\n\ninsert(1, NewElement, CurrentElement, Pre, Post) ->\n  Pre ++ [replace(CurrentElement, NewElement)] ++ Post;\ninsert(Pos, NewElement, CurrentElement, Pre, [PostH|PostT]) ->\n  insert(Pos - 1, NewElement, PostH, Pre ++ [CurrentElement], PostT).\n\nreplace(#{current := Old, old := _}, NewElement) ->\n  #{current => NewElement, old => Old};\nreplace(Element, NewElement) ->\n  #{current => NewElement, old => Element}.\n"
  },
  {
    "path": "sequential/insert_element_at/src/insert_element_at.app.src",
    "content": "{application, insert_element_at,\n [{description, \"insert_element_at\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { insert_element_at_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/insert_element_at/src/insert_element_at.erl",
    "content": "-module(insert_element_at).\n\n-export([insert/3]).\n\ninsert(_List, _Pos, _Element) ->\n  believe_in_yourself.\n"
  },
  {
    "path": "sequential/insert_element_at/test/insert_element_at_SUITE.erl",
    "content": "-module(insert_element_at_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(insert_element_at_test).\n"
  },
  {
    "path": "sequential/insert_element_at/test/insert_element_at_test.erl",
    "content": "-module(insert_element_at_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\ninsertion_test() ->\n  [1, #{current := \"hi\", old := 2}, 3] =\n    insert_element_at:insert([1, 2, 3], 2, \"hi\"),\n  [1, #{current := \"hi\", old := 10}, 3] =\n    insert_element_at:insert([1, #{current => 10, old => 2}, 3], 2, \"hi\").\n"
  },
  {
    "path": "sequential/installing/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n"
  },
  {
    "path": "sequential/installing/Makefile",
    "content": ".PHONY: test\ntest:\n\trebar3 ct\n"
  },
  {
    "path": "sequential/installing/README.md",
    "content": "# Installing\n\n## Reading Material\n\n- [Learn You Some Erlang: Introduction](http://learnyousomeerlang.com/introduction)\n\n## Requirements\n\nIn this exercise we will setup our environment before we start our\nreal coding. We will need some software:\n\n- Erlang 27.\n- Make (probably already installed on your system).\n- Rebar.\n\nWe suggest 2 ways to install Erlang + Rebar: asdf or nix.\n\n### ASDF\n\nFirst, we need to install ASDF:\n```sh\ngit clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.14.1\n```\nThen we'll have to add this to your bash or zsh config file:\n```sh\n. \"$HOME/.asdf/asdf.sh\"\n```\nOr, if you're using fish:\n```fish\nsource ~/.asdf/asdf.fish\n```\n\nBe sure to check asdf's website, since there are some extra goodies to config\nlike shell completions.\n\n```sh\nasdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git\n```\n\nAnd finally, install erlang 27.0.1:\n```sh\nasdf install erlang 27.0.1 && asdf global erlang 27.0.1\n```\n\nThe global command is just to tell your environment to always use erlang 27, you\ncan use `asdf local` instead to have distinct versions base on which folder you're currently in.\n\n### Nix\n\nClone the repo: \n```sh\ngit clone https://github.com/lambdaclass/erlings.git\n```\ncd into this folder and eval the nix flake:\n```sh\ncd erlings/sequential/install && nix develop\n```\nThis will drop you into a bare-bones bash shell with erlang and rebar installed,\nwhich you can use for the exercises.\n\n## Checking environment\n\nTo check your environment do the following:\n\n~~~\n$> git clone https://github.com/lambdaclass/erlings.git\n$> cd ~/erlings/sequential/installing\n$> make\n~~~\n\nYou should get the following output:\n\n~~~\nrebar3 ct\n===> Verifying dependencies...\n===> Analyzing applications...\n===> Compiling installing\n===> Running Common Test suites...\n%%% installing_SUITE: .\nAll 1 tests passed.\n~~~\n"
  },
  {
    "path": "sequential/installing/flake.nix",
    "content": "{\n  description = \"A flake to download Erlang 27 and Rebar3 from Nixpkgs 24.05\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/24.05\";\n    flake-utils.url = \"github:numtide/flake-utils/main\";\n  };\n\n  outputs = { self, nixpkgs, flake-utils, ... }:\n    flake-utils.lib.eachDefaultSystem (system:\n      let\n        pkgs = nixpkgs.legacyPackages.${system};\n      in {\n        packages.default = pkgs.mkShell {\n          buildInputs = [\n            pkgs.erlang_27    \n            pkgs.rebar3       \n          ];\n        };\n      });\n}\n\n"
  },
  {
    "path": "sequential/installing/src/installing.app.src",
    "content": "{application, installing,\n [{description, \"installing\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { installing_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/installing/src/installing.erl",
    "content": "-module(installing).\n\n-export([installed/0]).\n\ninstalled() ->\n  'i can not wait for more code'.\n"
  },
  {
    "path": "sequential/installing/test/installing_SUITE.erl",
    "content": "-module(installing_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(installing_test).\n"
  },
  {
    "path": "sequential/installing/test/installing_test.erl",
    "content": "-module(installing_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\ninstalling_test() ->\n  ?assertEqual('i can not wait for more code', installing:installed()).\n"
  },
  {
    "path": "sequential/lists/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n.lock\n"
  },
  {
    "path": "sequential/lists/README.md",
    "content": "# Lists\n\n## Reading Material\n\nRecursion and high order functions can be tricky if you don't have a functional programming background, for that reason take your time to read and really understand the concepts.\n\n- [Recursion theory](https://en.wikipedia.org/wiki/Recursion_(computer_science))\n- [Learn You Some Erlang: Recursion](http://learnyousomeerlang.com/recursion)\n- [Learn You Some Erlang: Higher Order Functions](http://learnyousomeerlang.com/higher-order-functions)\n\n## Exercises\n\n### Reverse\nThis exercise consists in creating the function `lists_exercises:reverse/1` which should be [Tail Recursive](https://stackoverflow.com/questions/33923/what-is-tail-recursion), and take a list as argument and return another list with every element in the opposed position.\n\nExample:\n``` erlang\n1> lists_exercises:reverse([1, 2, 3, 4]).\n%% [4, 3, 2, 1]\n```\n[solution](solution/lists_exercises.erl#L14-L17)\n\n### Remove consecutive\nCreate a function `lists_exercises:rmconsecutive/1` that takes a list and returns another one but without any consecutive repetitions.\n\nExample:\n``` erlang\n1> lists_exercises:rmconsecutive([1,1,1,2,3,4,1,1,1,3]).\n%% [1, 2, 3, 4, 1, 3]\n```\n[solution](solution/lists_exercises.erl#L21-L31)\n\n### Even Fibonacci Numbers\nEach new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:\n\n` 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...`\n\nBy considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.\n\n[solution](solution/lists_exercises.erl#L35-L60)\n\n### Reduce\nImplement `lists_exercises:foldl/3` and `lists_exercises:foldl/2` using recursion (see [Erlang foldl reference](http://erlang.org/doc/man/lists.html#foldl-3)).\n\nExample:\n``` erlang\n1> lists_exercises:foldl(fun(X, Y) -> X + Y end, 0, [1, 2, 3, 4]).\n%% 10\n\n2> lists_exercises:foldl(fun(X, Y) -> X * Y end, [1, 2, 3]).\n%% 6\n\n3> lists_exercises:foldl(func(X, Y) -> X andalso Y, [true, false, true]).\n%% false\n```\n[solution](solution/lists_exercises.erl#L65-L73)\n\n### Rotate Lists\nCreate a function `lists_exercises:rotate/2` that rotates the contents of a list `n` positions. It should take 2 arguments:\n\n- A list\n- A tuple of `{left, N}` or `{right, N}` that indicates the direction and the size of the displacement expected.\n\nExample:\n``` erlang\n1> lists_exercises:rotate([1, 2, 3, 4, 5], {right, 2}).\n%% [4,5,1,2,3]\n```\n[solution](solution/lists_exercises.erl#L77-L86)\n\n### Run-length encoding of a list\nImplement the so-called run-length encoding data compression method. Consecutive duplicates of elements are encoded as terms {N,E} where N is the number of duplicates of the element E.\n\nExample:\n\n``` erlang\n1> lists_exercises:run_length_encode([a,a,a,a,b,c,c,a,a,d,e,e,e,e]).\n%%[{4,a},{1,b},{2,c},{2,a},{1,d},{4,e}]\n```\n[solution](solution/lists_exercises.erl#L90-L103)\n\n### Any\nWrite a function `lists_exercises:list_any/2` that takes a predicate (a function that returns a boolean value) and a list and returns true if any element of the list satisfies the predicate and false otherwise.\nNote: Implement it without using `lists:list_any`.\n\nExample:\n``` erlang\n1> lists_exercises:list_any(fun(X) -> X > 3 end, [4, 2, 0]).\n%% true\n\n2> lists_exercises:list_any(fun(X) -> X == 0 end, [1, 2, 3]).\n%% false\n```\n[solution](solution/lists_exercises.erl#L107-L108)\n\n### Anagram\nWrite a function `list_exercises:anagram/2` that takes a list of strings, a string (\"MyWord\"), and returns all elements of the list that are an anagram of \"MyWord\"\n\nExample:\n```erlang\n1>lists_exercises:anagram([\"god\",\"cat\",\"dog\"], \"dog\").\n%%[\"god\"]\n\n```\n\n[solution](solution/lists:exercises.erl#L111-L131)\n\n\n### First letter last letter game\n\nThere is a game called first letter, last letter. The object of this game is for one player to say a word apple, and for the other player to say a word that begins with the last letter of the previous word, i.e. elephant.\n\nWrite a function `lists:exercises:last_letter/1` that takes a list of strings and return a list where the subsequent name starts with the final letter of the previous name. Take the first element of the list as the first word of the sequence, that names cannot be repeated.\n\n\nExample:\n```erlang\n1> lists_exercises:last_letter([\"turnIp\",\"Potato\",\"peas\",\"OniOn\",\"yam\",\"letuCe\",\"broccoli\",\n \"asparaguS\",\"Artichoke\"]).\n %%[\"turnIp\",\"Potato\",\"OniOn\"]\n\n2> lists_exercises:last_letter([])\n%% []\n\n```\n\n[solution](solution/lists:exercises.erl#L117-L141)\n"
  },
  {
    "path": "sequential/lists/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "sequential/lists/solution/lists_exercises.app.src",
    "content": "{application, lists_exercises,\n [{description, \"lists_exercises\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { lists_exercises_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/lists/solution/lists_exercises.erl",
    "content": "-module(lists_exercises).\n\n-export([reverse/1,\n         rmconsecutive/1,\n         even_fib_numbers/0,\n         foldl/2,\n         foldl/3,\n         rotate/2,\n         run_length_encode/1,\n         list_any/2,\n         anagram/2,\n         last_letter/1]).\n\n\n\n% Reverse\nreverse(List) ->\n  reverse(List, []).\n\nreverse([], Acc) ->\n  Acc;\nreverse([H|T], Acc) ->\n  reverse(T, [H|Acc]).\n\n% Remove Consecutive\nrmconsecutive([]) ->\n  [];\nrmconsecutive([H|T]) ->\n  rmconsecutive(H, T).\n\nrmconsecutive(E, []) ->\n  [E];\nrmconsecutive(E, [E|T]) ->\n  rmconsecutive(E,T);\nrmconsecutive(E, [H|T]) ->\n  [E| rmconsecutive(H,T)].\n\n% Even Fibonacci Numbers\nfib(1) ->\n  1;\nfib(2) ->\n  2;\nfib(N) when N > 2 ->\n  fib(N-1) + fib(N-2).\n\n% list of every fibonacci number less than N.\nfibs_less_than(N) when N < 1->\n  [];\nfibs_less_than(N) ->\n  fibs_less_than(N, 1, []).\n\nfibs_less_than(N, M, AccList) ->\n  FibM = fib(M),\n  case FibM < N of\n    true ->\n      fibs_less_than(N, M + 1, [ FibM | AccList]);\n    false ->\n      AccList\n  end.\n\neven_fib_numbers() ->\n  Less_than_4mill = fibs_less_than(4000000),\n  Even_fibs = lists:filter(fun(X) -> X rem 2 == 0 end, Less_than_4mill),\n  lists:sum(Even_fibs).\n\n% Reduce\n% with accumulator\nfoldl(_, Acc, []) ->\n  Acc;\nfoldl(Fun, Acc, [A | T]) ->\n  NewAcc = Fun(A,Acc),\n  foldl(Fun,NewAcc,T).\n\n% w/o accumulator\nfoldl(_, List) when length(List) < 2 ->\n  undefined;\nfoldl(Fun, [A, B | T]) ->\n  foldl(Fun, A, [B | T]).\n\n% Rotate Lists\nrotate([], _) ->\n  [];\nrotate(L, {Dir, N}) ->\n  case Dir of\n  left ->\n      {Right, Left} = lists:split(N, L);\n  right ->\n      {Right, Left} = lists:split(length(L) - N, L)\n  end,\n  lists:append(Left, Right).\n\n% Run-length encoding of a list\nrun_length_encode([], Acc) -> reverse(Acc);\nrun_length_encode([H|T], [{Count, H}|AccT]) ->\n  run_length_encode(T, [{Count + 1, H}|AccT]);\nrun_length_encode([H|T], Acc) -> \n  run_length_encode(T, [{1, H}] ++ Acc).\n\nrun_length_encode(L) -> run_length_encode(L, []).\n\n% Any\nlist_any(F, List) ->\n  lists:foldl(fun(X, Y) -> F(X) or Y end, false, List).\n\n%Anagram\nanagram(List, S) -> \n    LowerS = string:lowercase(S),\n    SortedS = lists:sort(LowerS),\n    lists:filter(fun (X) -> \n                    LowerH = string:lowercase(X),\n                    LowerH =/= LowerS andalso lists:sort(LowerH) =:= SortedS\n                end, List).\n\n%Last Letter game\nlast_letter([]) -> [];\nlast_letter([H|T]) -> last_letter(T, [H]).\n\nlast_letter([], Acc) -> \n    lists:reverse(Acc);\nlast_letter(List, [H|T]) ->\n    Letter = lists:nthtail(length(H)-1,H),\n    Word = find_word(Letter, List),\n    case Word of\n        [] -> \n            last_letter([], [H|T]);\n        _ -> \n            NewWord = [Word, H|T],\n            NewList = lists:delete(Word, List),\n            last_letter(NewList, NewWord)    \n    end.\n\nfind_word(_L,[]) -> [];\nfind_word(L,[H|T]) ->\n    Letter = string:lowercase(L),\n    [A|_Rest] = string:lowercase(H),\n    case [A] == Letter of\n        true ->\n            H;\n        false ->\n            find_word(L,T)\n    end.\n"
  },
  {
    "path": "sequential/lists/src/lists_exercises.app.src",
    "content": "{application, lists_exercises,\n [{description, \"lists_exercises\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { lists_exercises_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/lists/src/lists_exercises.erl",
    "content": "-module(lists_exercises).\n\n-export([reverse/1,\n         rmconsecutive/1,\n         even_fib_numbers/0,\n         foldl/3,\n         foldl/2,\n         rotate/2,\n         run_length_encode/1,\n         list_any/1,\n         anagram/2,\n         last_letter/1]).\n\nreverse(List) ->\n  put_your_solution_here.\n\nrmconsecutive(List) ->\n  put_your_solution_here.\n\neven_fib_numbers() ->\n  put_your_solution_here.\n\nfoldl(Fun, Acc, List) ->\n  put_your_solution_here.\n\nfoldl(Fun, List) ->\n  put_your_solution_here.\n\nrotate(List, Tuple) ->\n  put_your_solution_here.\n\nrun_length_encode(List) ->\n  put_your_solution_here.\n\nlist_any(N) ->\n  put_your_solution_here.\n\nanagram(List, S) -> \n  put_your_solution_here.\n\nlast_letter(List) ->\n  put_your_solution_here.\n"
  },
  {
    "path": "sequential/lists/test/lists_exercises_SUITE.erl",
    "content": "-module(lists_exercises_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(lists_exercises_test).\n"
  },
  {
    "path": "sequential/lists/test/lists_exercises_test.erl",
    "content": "-module(lists_exercises_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nall_the_same_test() ->\n  ?assertEqual([1, 1, 1, 1], lists_exercises:reverse([1, 1, 1, 1])).\n\nall_different_test() ->\n ?assertEqual([5, 4, 3, 2, 1], lists_exercises:reverse([1, 2, 3, 4, 5])).\n\nrmconsecutive_test() ->\n  List = [1, 1, 1, 2, 3, 4, 1, 1, 1, 3],\n  ?assertEqual([1,2,3,4,1,3], lists_exercises:rmconsecutive(List)).\n\neven_fib_numbers_test() ->\n  ?assertEqual(4613732, lists_exercises:even_fib_numbers()).\n\nfoldl_multiply_test() ->\n  Multiply = fun (A, B) -> A * B end,\n  ?assertEqual(300, lists_exercises:foldl(Multiply, 10, [2, 5, 3])).\n\nfoldl_add_without_acc_test() ->\n  Add = fun (A, B) -> A + B end,\n  ?assertEqual(100, lists_exercises:foldl(Add, [20, 20, 10, 50])).\n\nrotate_list_right_test() ->\n  Res = [4, 5, 1, 2, 3],\n  ?assertEqual(Res, lists_exercises:rotate([1, 2, 3, 4, 5], {right, 2})).\n\nrotate_list_left_test() ->\n  Res = [3, 4, 5, 1, 2],\n  ?assertEqual(Res, lists_exercises:rotate([1, 2, 3, 4, 5], {left, 2})).\n\nrun_length_encoding_test() ->\n  List = [a, a, a, a, b, c, c, a, a, d, e, e, e, e],\n  Res = [{4, a}, {1, b}, {2, c}, {2, a}, {1, d}, {4, e}],\n  ?assertEqual(Res, lists_exercises:run_length_encode(List)).\n\nany_is_even_test() ->\n  List = [2, 3, 4, 1, 5, 6, 9],\n  ?assert(lists_exercises:list_any(fun(X) -> X rem 2 == 0 end, List)).\n\nany_empty_test() ->\n    ?assertNot(lists_exercises:list_any(fun(X) -> X == 20 end, [])).\n\nanagram_test() ->\n    List = [\"Panel\", \"plane\", \"Penal\", \"PlenA\", \"Nepal\", \"ArgentinA\", \"Laos\"],\n    String = \"Nepal\",\n    Res = [\"Panel\", \"plane\", \"Penal\", \"PlenA\"],\n    ?assertEqual(Res, lists_exercises:anagram(List,String)).\n\nlast_letter_test()->\n  List = [\"Afghanistan\", \"Albania\", \"Algeria\", \"Andorra\", \"Nigeria\", \"Norway\", \n          \"Yemen\", \"Nepal\", \"Morocco\", \"Oman\", \"Portugal\", \"Spain\"],\n  Res = [\"Afghanistan\", \"Nigeria\", \"Albania\", \"Algeria\", \"Andorra\"],\n  ?assertEqual(Res, lists_exercises:last_letter(List)).\n"
  },
  {
    "path": "sequential/maps/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n.lock\n"
  },
  {
    "path": "sequential/maps/README.md",
    "content": "# Maps\n\n## Reading Material\n\n- [Learn You Some Erlang: Maps](http://learnyousomeerlang.com/maps)\n\n## Exercises\n\n### Sum of Values\n\nWrite a function `maps_exercises:sum_of_values/1` that takes a map a returns a sum of all values in the map.\n\nNote: the function maps:fold/3 can be useful.\n\nExample:\n``` erlang\n1> maps_exercises:sum_of_values(#{a => 1, b =>3, c =>4}).\n%%8\n\n2> maps_exercises:sum_of_values(#{one => 1, three=> 3, seven => 7}).\n%% 11\n```\n\n### Min Value\n\nWrite a function `maps_exercises:min_value(Map)` that returns the minimum value of that Map.\n\nExample:\n```erlang\n1>maps_exercises:min_value(#{a => 1, b =>3, c =>4}).\n%%1\n\n2>maps_exercises:min_value(#{five => 5, three => 3, seven => 7, ten =>10}).\n%%3\n```\n\n### Sort keys\n\nWrite a function `maps_exercises:sort_by_keys/1` that takes a map and returns a new map with the keys ordered from min to max.\n\nNote: Remember you can use maps:keys/1 that returns a list with all the keys in a map. \n\nExample:\n``` erlang\n1>maps_exercises:sort_by_keys(#{1=> one, 2 => two, 5 => five, 10 => ten, 3 => three, 15 => fifteen}).\n%% #{1 => one,2 => two,3 => three,5 => five,10 => ten, 15 => fifteen}\n\n2> maps:exercises:sort_by_keys(#{\"one\" => 1,\"two\" => 2, \"three\" => 3, \"five\" => 5, \"four\"=>4}).\n#{\"five\" => 5,\"four\" => 4,\"one\" => 1,\"three\" => 3,\"two\" => 2}\n\n```\n\n### Return values\n\nWrite a function `maps_exercises:return_values/1` that takes a map a returns a list with all the values in that map\n\nExample:\n``` erlang\n1> maps_exercises:return_values(#{\"one\" => 1,\"two\" => 2, \"three\" =>3 , \"five\" => 5, \"four\" => 4}).\n%% [1,2,3,5,4]\n\n2> maps_exercises:return_values(#{1 => one, 2 => two, 5 => five, 10 => ten, 3 => three, 15 => fifteen})\n%%[one,two,three,five,ten,fifteen]\n\n```\n\n### Merge Map\n\nWrite a function `maps_exercises:merge/2` that merges 2 maps, if they have a key in common, keep the value from the second map.\n\nNote: the function `maps:fold/3` can be useful.\n\nExample:\n``` erlang\n1> maps_exercises:merge(#{}, #{a => 1, b => 2}).\n%% #{a => 1, b => 2}\n\n2> maps_exercises:merge(#{a => 1, b => 2}, #{a => 5, c => 3}).\n%% #{a => 5, b => 2, c => 3}\n```\n[solution](src/solution/maps_exercises.erl#L6-L7).\n\n### Mapping a Map\n\nWrite a function `maps_exercises:map/2` for mapping a function over the values of a Map without using `maps:map/2`.\n\nExample:\n``` erlang\n1> maps_exercises:map(fun(X) -> X + 1 end, #{a => 4, b => 2}).\n%% #{a => 5, b => 3}\n\n2> maps_exercises:map(#{}).\n%% #{}\n```\n[solution](src/solution/maps_exercises.erl#L10-L13).\n\n### List to Map\n\nCreate a function `maps_exercises:to_map/1` that converts a list to a [Map](http://learnyousomeerlang.com/maps) without using `maps:from_list`.\n\nExample:\n``` erlang\n1> maps_exercises:to_map([2, 1, 6, 4]).\n%% #{1 => 2, 2 => 1, 3 => 6, 4 => 4}\n\n2> maps_exercises:to_map([]).\n%% #{}\n```\n[solution](src/solution/maps_exercises.erl#L16-L24).\n\n### Records to Maps\n\nCreate a record named `person` that has the attributes `name` and `age`. Then write a function `maps_exercises:records_to_maps(Records)` that converts a list of records (in this case people) into a list of maps with the attributes. For this you should use `lists:map`.\n\nExample:\n```erlang\n1> maps_exercises:records_to_maps([]).\n%% []\n\n2> maps_exercises:records_to_maps([#person{name=\"Pepe\", age=28}, #person{name=\"Luis\", age=77}]).\n%% [#{age => 28,name => \"Pepe\"},#{age => 77,name => \"Luis\"}]\n```\n\n[solution](src/solution/maps_exercises.erl#L27-L30).\n\n### Maps to Records\n\nCreate a record named `person` that has the attributes `name` and `age`. Then write a function `maps_exercises:maps_to_records(Records)` that converts a list of maps into a list of records (in this case people). For this you should only use recursion.\n\nExample:\n```erlang\n1> maps_exercises:maps_to_records([]).\n%% []\n\n2> maps_exercises:maps_to_records([#{age => 28,name => \"Pepe\"},#{age => 77,name => \"Luis\"}]).\n%% [#person{name = \"Luis\",age = 77}, #person{name = \"Pepe\",age = 28}]\n```\n\n[solution](src/solution/maps_exercises.erl#L33-L38).\n\n### Proplist to Map\n\n**DISCLAIMER** Erlang provides functions for the following task: `maps:from_list` and `maps:to_list`, but we want you to implement it by hand.\n\nWrite a recursive function `proplist_to_map/1` that takes a proplist (a list of tuples) and builds a map from it. Use the first component of each tuple as the key and the second component as the value.\n\n\nExample:\n```erlang\n1> maps_exercises:proplist_to_map([]).\n%% #{}\n\n2> maps_exercises:proplist_to_map([{firstname, \"Pedro\"}, {lastname, \"Sanches\"}, {age, 11}]).\n%% #{age => 11,firstname => \"Pedro\",lastname => \"Sanches\"}\n```\n\n[solution](src/solution/maps_exercises.erl#L41-L44).\n"
  },
  {
    "path": "sequential/maps/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "sequential/maps/solution/maps_exercises.app.src",
    "content": "{application, maps_exercises,\n [{description, \"maps_exercises\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { maps_exercises_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/maps/solution/maps_exercises.erl",
    "content": "-module(maps_exercises).\n-export([sum_of_values/1,return_values/1,sort_by_keys/1,min_value/1,merge/2, map/2, to_map/1, records_to_maps/1, maps_to_records/1, proplist_to_map/1]).\n-record(person, {name, age}).\n\n%% Return the sum of all values in a list\nsum_of_values(Map) ->\n    maps:fold(fun(_K,V,AccIn) -> AccIn + V end, 0, Map).\n\n%% Min value\nmin_value(Map) ->\n    Keys = maps:keys(Map),\n    min_value(Keys, Map, []).\n\nmin_value([], _Map, Acc)-> lists:min(Acc);\nmin_value([H|T], Map, Acc) ->\n    Element = maps:find(H, Map),\n    {_Atom, Value} = Element,\n    min_value(T, Map, [Value|Acc]).\n\n%% Order Map\nsort_by_keys(Map)->\n    List = maps:keys(Map),\n    OrderedL = lists:sort(List),\n    build_ordered_map(OrderedL, Map, []).\n\nbuild_ordered_map([], _Map, Acc)-> \n    NewMap = lists:reverse(Acc),\n    maps:from_list(NewMap);\n\nbuild_ordered_map([H|T], Map, Acc) ->\n    Value = maps:get(H, Map),\n    Tuple = {H, Value},\n    build_ordered_map(T, Map, [Tuple|Acc]).\n\n%% Values to list\nreturn_values(Map) ->\n    List = maps:keys(Map),\n    get_values(List, Map, []).\n\nget_values([], _Map, Acc) ->\n    lists:reverse(Acc);\nget_values([H|T], Map, Acc)->\n    Value = maps:get(H, Map),\n    get_values(T, Map, [Value|Acc]).\n\n%% Merge map\nmerge(Map1, Map2) ->\n  maps:fold(fun maps:put/3, Map1, Map2).\n\n%% Mapping a map\nmap(F, Map)  ->\n  List_Of_Tuples = maps:to_list(Map),\n  Mapped_List = lists:keymap(F, 2, List_Of_Tuples),\n  maps:from_list(Mapped_List).\n\n%% List to map\nto_map([]) ->\n  #{};\nto_map([H|T]) ->\n  to_map(2, #{1 => H}, T).\n\nto_map(_, Map, []) ->\n  Map;\nto_map(N, Map, [H|T]) ->\n  to_map(N+1, maps:put(N, H, Map), T).\n\n%% Records to maps\nrecords_to_maps(Records) ->\n  lists:map(fun(#person{name = Name, age = Age}) ->\n              #{name => Name, age => Age} end,\n            Records).\n\n%% Maps to records\nmaps_to_records(Maps) ->\n  maps_to_records([], Maps).\nmaps_to_records(Acc, []) ->\n  Acc;\nmaps_to_records(Acc, [#{name := Name, age := Age} | Tail]) ->\n  maps_to_records([#person{name = Name, age = Age} | Acc], Tail).\n\n%% Proplist to map\nproplist_to_map(Proplist) ->\n  lists:foldl(fun({K, V}, Map) ->\n                Map#{K => V} end,\n              #{}, Proplist).\n"
  },
  {
    "path": "sequential/maps/src/maps_exercises.app.src",
    "content": "{application, maps_exercises,\n [{description, \"maps_exercises\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {mod, { maps_exercises_app, []}},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {maintainers, []},\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/maps/src/maps_exercises.erl",
    "content": "-module(maps_exercises).\n-export([sum_of_values/1,return_values/1,sort_by_keys/1,min_value/1,merge/2, map/2, to_map/1, records_to_maps/1, maps_to_records/1, proplist_to_map/1]).\n\nsum_of_values(Map) ->\n  put_your_solution_here.\n\nmin_value(Map)->\n  put_your_solution_here.\n\nsort_by_keys(Map)->\n  put_your_solution_here.\n\nreturn_values(Map)->\n  put_your_solution_here.\n\nmerge(M1, M2) ->\n  put_your_solution_here.\n\nmap(Function, Map) ->\n  put_your_solution_here.\n\nto_map(List) ->\n  put_your_solution_here.\n\nrecords_to_maps(Records) ->\n  put_your_solution_here.\n\nmaps_to_records(Maps) ->\n  put_your_solution_here.\n\nproplist_to_map(Proplist) ->\n  put_your_solution_here.\n"
  },
  {
    "path": "sequential/maps/test/maps_exercises_SUITE.erl",
    "content": "-module(maps_exercises_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(maps_exercises_test).\n"
  },
  {
    "path": "sequential/maps/test/maps_exercises_test.erl",
    "content": "-module(maps_exercises_test).\n-record(person, {name, age}).\n-include_lib(\"eunit/include/eunit.hrl\").\n\nsum_of_values_test()->\n  Map = #{a=>1, b=> 5, c => 6, d => 5},\n  ?assertEqual(17, maps_exercises:sum_of_values(Map)).\n\nmin_value_test()->\n  Map = #{a=> 5, b=> 3, c=> 4, d=>2},\n  ?assertEqual(2, maps_exercises:min_value(Map)).\n\nsort_by_keys_test()->\n  Map = #{1 => a, 5=> c, 3=> d, 7=> f},\n  OrderedM = #{1=>a,3=>d,5=>c,7=>f},\n  ?assertEqual(OrderedM, maps_exercises:sort_by_keys(Map)).\n\nreturn_values_test()->\n  Map = #{a=>1, b=>5, c=>3},\n  List = [1,5,3],\n  ?assertEqual(List, maps_exercises:return_values(Map)).\n\nmerge_empty_test() ->\n  Map = #{a => 1, b => 2, c => 3},\n  ?assertEqual(Map, maps_exercises:merge(#{}, Map)),\n  ?assertEqual(Map, maps_exercises:merge(Map, #{})).\n\nmerge_update_test() ->\n  Map1 = #{a => 1, b => 2, c => 3},\n  Map2 = #{b => 6, d => 4},\n  Map3 = #{a => 1, b => 6, c => 3, d => 4},\n  ?assertEqual(Map3, maps_exercises:merge(Map1, Map2)).\n\nmap_empty_map_test() ->\n  F = fun(X) -> X + 1 end,\n  ?assertEqual(#{}, maps_exercises:map(F, #{})).\n\nmap_add_1_test() ->\n  F = fun(X) -> X + 1 end,\n  Map = #{a => 1, b => 2, c => 3},\n  Res = #{a => 2, b => 3, c => 4},\n  ?assertEqual(Res, maps_exercises:map(F, Map)).\n\nto_map_test() ->\n  List = [2, 23, a],\n  Map = #{1 => 2, 2 => 23, 3 => a},\n  ?assertEqual(Map, maps_exercises:to_map(List)).\n\nto_map_empty_test() ->\n  ?assertEqual(#{}, maps_exercises:to_map([])).\n\nrecords_to_maps_test() ->\n  Records = [#person{name=\"Pepe\", age=28}, #person{name=\"Luis\", age=77}],\n  Maps = [#{age => 28,name => \"Pepe\"}, #{age => 77,name => \"Luis\"}],\n  ?assertEqual(Maps, maps_exercises:records_to_maps(Records)).\n\nrecords_to_maps_empty_test() ->\n  ?assertEqual([], maps_exercises:records_to_maps([])).\n\nmaps_to_records_test() ->\n  Records = [#person{name=\"Pepe\", age=28}, #person{name=\"Luis\", age=77}],\n  Maps = [#{age => 77,name => \"Luis\"}, #{age => 28,name => \"Pepe\"}],\n  ?assertEqual(Records, maps_exercises:maps_to_records(Maps)).\n\nmaps_to_records_empty_test() ->\n  ?assertEqual([], maps_exercises:maps_to_records([])).\n\nproplist_to_map_test() ->\n  Proplist = [{firstname, \"Pedro\"}, {lastname, \"Sanches\"}, {age, 11}],\n  Map = #{age => 11,firstname => \"Pedro\",lastname => \"Sanches\"},\n  ?assertEqual(Map, maps_exercises:proplist_to_map(Proplist)).\n\nproplist_to_map_empty_test() ->\n  ?assertEqual(#{}, maps_exercises:proplist_to_map([])).\n"
  },
  {
    "path": "sequential/regex/.gitignore",
    "content": ".rebar3\n_*\n.eunit\n*.o\n*.beam\n*.plt\n*.swp\n*.swo\n.erlang.cookie\nebin\nlog\nerl_crash.dump\n.rebar\nlogs\n_build\n.idea\n*.iml\nrebar3.crashdump\n*~\n"
  },
  {
    "path": "sequential/regex/README.md",
    "content": "regex\n=====\n\nCreate a function `match/2` that will receive a string and a regex (also a string), and return `true` if it matches or `false` otherwise. \n\nYou won't be using all of Regex, just a small subset of it as follows:\n\n\n| Syntax        | Meaning                                     | Example  | Matches        | \n| ------------- |:-------------------------------------------:| :------: | :------------: |\n| a             | match the specified character               |    k     |       k        |\n| .             | matches any character (except newline)      |    .     | a,b,c,d...     |\n| ?             | matches 0 or 1 of the previous character    |    aq?   |   a, aq        |\n| *             | matches 0 or more of the previous character |    b*    | \"\", b, bb, bbb |\n| ^             | matches the start of a string               |    ^ca   |      ca        |\n| $             | matches the end of a string                 |    eb$   |      eb        |\n\n\n### Considerations\n\n* The given regex won't be invalid (Eg: `\"^*\"`, `\"*?\"`)\n* I can ignore the existance of the newline character since I expect simple strings as inputs. Thus the character `.` matches anything\n* An empty regex matches nothing so `match(SomeString, EmptyRegex)` will return false\n* `match/2` can return true as soon it matches something since I don't care about where or what.\n"
  },
  {
    "path": "sequential/regex/rebar.config",
    "content": "{profiles, [\n    {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
  },
  {
    "path": "sequential/regex/solution/regex.app.src",
    "content": "{application, regex,\n [{description, \"simple regex engine\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/regex/solution/regex.erl",
    "content": "-module(regex).\n-export([match/2]).\n\nmatch([], _Regex) -> \n    false;\nmatch(_String, []) -> \n    false;\nmatch(String, [$^ | Regex]) -> \n    matching(String, Regex);\nmatch(String, Regex) ->\n    case matching(String, Regex) of\n      true -> true;\n      false -> match(tl(String), Regex)\n    end.\n\nmatching(_String, []) -> \n    true;\nmatching([], [$$]) -> \n    true;\nmatching([], [_Char, $? | Regex]) ->\n    matching([], Regex);\nmatching([], [_Char, $* | Regex]) ->\n    matching([], Regex);\nmatching([], _Regex) -> \n    false;\nmatching(_String, [$$]) -> \n    false;\nmatching(String, [$., $* | RemRegex] = Regex) ->\n    case matching(String, RemRegex) of\n      true -> true;\n      false -> matching(tl(String), Regex)\n    end;\nmatching([Char | String], [Char, $? | Regex]) ->\n    matching(String, Regex);\nmatching(String, [_OtherChar, $? | Regex]) ->\n    matching(String, Regex);\nmatching([Char | String],\n\t [Char, $* | _RemRegex] = Regex) ->\n    matching(String, Regex);\nmatching(String, [_OtherChar, $* | Regex]) ->\n    matching(String, Regex);\nmatching([_Char | String], [$. | Regex]) ->\n    matching(String, Regex);\nmatching([Char | String], [Char | Regex]) ->\n    matching(String, Regex);\nmatching([_Char | _String], [_OtherChar | _Regex]) ->\n    false.\n"
  },
  {
    "path": "sequential/regex/src/regex.app.src",
    "content": "{application, regex,\n [{description, \"simple regex engine\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {applications,\n   [kernel,\n    stdlib\n   ]},\n  {env,[]},\n  {modules, []},\n\n  {licenses, [\"MIT\"]},\n  {links, []}\n ]}.\n"
  },
  {
    "path": "sequential/regex/src/regex.erl",
    "content": "-module(regex).\n\n-export([match/2]).\n\nmatch(_String, _Regex) ->\n    put_your_solution_here.\n"
  },
  {
    "path": "sequential/regex/test/regex_SUITE.erl",
    "content": "-module(regex_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->\n  [run_eunit].\n\nrun_eunit(_Config) ->\n  ok = eunit:test(regex_test).\n"
  },
  {
    "path": "sequential/regex/test/regex_test.erl",
    "content": "-module(regex_test).\n-include_lib(\"eunit/include/eunit.hrl\").\n\nmatches_characters_test() -> \n    ?assert(regex:match(\"a\", \"a\")).\n\nmatches_numbers_test() -> \n    ?assert(regex:match(\"49\", \"49\")).\n\nmatches_question_mark_test() ->\n    ?assert(regex:match(\"Hi\", \"h?\")).\n\nmatches_multiple_chars_test() ->\n    ?assert(regex:match(\"aaaaaaaaa\", \"a*\")).\n\nmatches_empty_string_test() ->\n    ?assert(regex:match(\" \", \"a*\")).\n\nmatches_any_character_test() ->\n    ?assert(regex:match(\"asdf as 42\", \".\")).\n"
  }
]