Repository: lambdaclass/erlings
Branch: main
Commit: 3109c02dd6c3
Files: 198
Total size: 107.2 KB
Directory structure:
gitextract_kx806ov1/
├── .github/
│ └── workflows/
│ └── test.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── check-progress
├── concurrent/
│ ├── calculator/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── rebar.config
│ │ ├── solution/
│ │ │ ├── calculator.app.src
│ │ │ └── calculator.erl
│ │ ├── src/
│ │ │ ├── calculator.app.src
│ │ │ └── calculator.erl
│ │ └── test/
│ │ ├── calculator_SUITE.erl
│ │ └── calculator_test.erl
│ ├── parallel_map/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── rebar.config
│ │ ├── solution/
│ │ │ ├── parallel_map.app.src
│ │ │ └── parallel_map.erl
│ │ ├── src/
│ │ │ ├── parallel_map.app.src
│ │ │ └── parallel_map.erl
│ │ └── test/
│ │ ├── parallel_map_SUITE.erl
│ │ └── parallel_map_test.erl
│ ├── priority/
│ │ ├── .gitignore
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── rebar.config
│ │ ├── solution/
│ │ │ ├── priority.app.src
│ │ │ └── priority.erl
│ │ ├── src/
│ │ │ ├── priority.app.src
│ │ │ └── priority.erl
│ │ └── test/
│ │ ├── .keep
│ │ ├── priority_SUITE.erl
│ │ └── priority_test.erl
│ └── ring_benchmark/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── ring.erl
│ │ └── ring_benchmark.app.src
│ ├── src/
│ │ ├── ring.erl
│ │ └── ring_benchmark.app.src
│ └── test/
│ ├── ring_benchmark_SUITE.erl
│ └── ring_test.erl
├── distributed/
│ └── remote_fun/
│ ├── .gitignore
│ ├── Makefile
│ ├── README.md
│ ├── src/
│ │ ├── remote_fun.app.src
│ │ ├── remote_fun_client.erl
│ │ └── remote_fun_server.erl
│ └── test/
│ └── .keep
├── libraries/
│ └── shortly/
│ ├── .gitignore
│ ├── Makefile
│ ├── README.md
│ ├── rebar.config
│ ├── rebar3
│ ├── solution/
│ │ ├── config/
│ │ │ ├── node_1.config
│ │ │ └── node_2.config
│ │ ├── shortly.app.src
│ │ ├── shortly_app.erl
│ │ ├── shortly_db.erl
│ │ ├── shortly_link_handler.erl
│ │ ├── shortly_notification_algorithm.erl
│ │ ├── shortly_notification_ets.erl
│ │ ├── shortly_notification_pg2.erl
│ │ ├── shortly_shortener.erl
│ │ ├── shortly_sup.erl
│ │ ├── shortly_syn.erl
│ │ └── shortly_ws_handler.erl
│ ├── src/
│ │ ├── shortly.app.src
│ │ └── shortly_app.erl
│ └── test/
│ ├── shortly_shortener_SUITE.erl
│ └── shortly_shortener_test.erl
├── otp/
│ ├── pool/
│ │ ├── README.md
│ │ ├── rebar.config
│ │ ├── solution/
│ │ │ ├── poolie.app.src
│ │ │ ├── poolie_app.erl
│ │ │ ├── poolie_server.erl
│ │ │ ├── poolie_sup.erl
│ │ │ ├── poolie_worker.erl
│ │ │ └── poolie_worker_sup.erl
│ │ ├── src/
│ │ │ ├── poolie.app.src
│ │ │ ├── poolie_app.erl
│ │ │ ├── poolie_server.erl
│ │ │ ├── poolie_sup.erl
│ │ │ ├── poolie_worker.erl
│ │ │ └── poolie_worker_sup.erl
│ │ └── test/
│ │ ├── poolie_SUITE.erl
│ │ └── poolie_test.erl
│ └── shopping_cart/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── shopping_cart.app.src
│ │ └── shopping_cart.erl
│ ├── src/
│ │ ├── shopping_cart.app.src
│ │ └── shopping_cart.erl
│ └── test/
│ ├── shopping_cart_SUITE.erl
│ └── shopping_cart_test.erl
└── sequential/
├── bank_accounts/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── bank_account.app.src
│ │ └── bank_account.erl
│ ├── src/
│ │ ├── bank_account.app.src
│ │ └── bank_account.erl
│ └── test/
│ ├── bank_account_SUITE.erl
│ └── bank_account_test.erl
├── calculate_bmi/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── calculate_bmi.app.src
│ │ ├── calculate_bmi.erl
│ │ └── person_record.hrl
│ ├── src/
│ │ ├── calculate_bmi.app.src
│ │ ├── calculate_bmi.erl
│ │ └── person_record.hrl
│ └── test/
│ ├── calculate_bmi_SUITE.erl
│ └── calculate_bmi_test.erl
├── filter_fibonacci_numbers/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── filter_fibonacci_numbers.app.src
│ │ └── filter_fibonacci_numbers.erl
│ ├── src/
│ │ ├── filter_fibonacci_numbers.app.src
│ │ └── filter_fibonacci_numbers.erl
│ └── test/
│ ├── filter_fibonacci_numbers_SUITE.erl
│ └── filter_fibonacci_numbers_test.erl
├── filter_numbers/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── filter_numbers.app.src
│ │ └── filter_numbers.erl
│ ├── src/
│ │ ├── filter_numbers.app.src
│ │ └── filter_numbers.erl
│ └── test/
│ ├── filter_numbers_SUITE.erl
│ └── filter_numbers_test.erl
├── hello/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── hello.app.src
│ │ └── hello.erl
│ ├── src/
│ │ ├── hello.app.src
│ │ └── hello.erl
│ └── test/
│ ├── hello_SUITE.erl
│ └── hello_test.erl
├── hello_pattern/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── hello_pattern.app.src
│ │ └── hello_pattern.erl
│ ├── src/
│ │ ├── hello_pattern.app.src
│ │ └── hello_pattern.erl
│ └── test/
│ ├── hello_pattern_SUITE.erl
│ └── hello_pattern_test.erl
├── insert_element_at/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── insert_element_at.app.src
│ │ └── insert_element_at.erl
│ ├── src/
│ │ ├── insert_element_at.app.src
│ │ └── insert_element_at.erl
│ └── test/
│ ├── insert_element_at_SUITE.erl
│ └── insert_element_at_test.erl
├── installing/
│ ├── .gitignore
│ ├── Makefile
│ ├── README.md
│ ├── flake.nix
│ ├── src/
│ │ ├── installing.app.src
│ │ └── installing.erl
│ └── test/
│ ├── installing_SUITE.erl
│ └── installing_test.erl
├── lists/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── lists_exercises.app.src
│ │ └── lists_exercises.erl
│ ├── src/
│ │ ├── lists_exercises.app.src
│ │ └── lists_exercises.erl
│ └── test/
│ ├── lists_exercises_SUITE.erl
│ └── lists_exercises_test.erl
├── maps/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── maps_exercises.app.src
│ │ └── maps_exercises.erl
│ ├── src/
│ │ ├── maps_exercises.app.src
│ │ └── maps_exercises.erl
│ └── test/
│ ├── maps_exercises_SUITE.erl
│ └── maps_exercises_test.erl
└── regex/
├── .gitignore
├── README.md
├── rebar.config
├── solution/
│ ├── regex.app.src
│ └── regex.erl
├── src/
│ ├── regex.app.src
│ └── regex.erl
└── test/
├── regex_SUITE.erl
└── regex_test.erl
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/test.yml
================================================
on:
push:
branches: [main]
pull_request:
types: [opened, repoened, synchronize]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.0.0
- uses: cachix/install-nix-action@v27
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Setup erlang
run: cd sequential/installing && nix develop
- name: Re-use nix flake to check erlang tests
run: cd sequential/installing && nix develop --command bash -c "cd ../../ && make test PROFILE=ci"
================================================
FILE: .gitignore
================================================
.eunit
deps
*.o
*.beam
*.plt
erl_crash.dump
ebin/*.beam
rel/example_project
.concrete/DEV_MODE
.rebar
_build/
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 LambdaClass
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
DIRS = $(filter-out _build/, $(dir $(wildcard */)))
EXERCISES = $(patsubst %/, %, $(filter-out $(DIRS), $(dir $(wildcard */*/))))
CATEGORIES = $(subst /, , $(DIRS))
PROFILE ?= test
.PHONY: test $(CATEGORIES) $(EXERCISES) check-progress
check-progress:
./check-progress
test: $(CATEGORIES)
.SECONDEXPANSION:
$(CATEGORIES): $$(filter $$@%, $(EXERCISES))
$(EXERCISES):
cd $@ && rebar3 as $(PROFILE) ct
================================================
FILE: README.md
================================================

# Erlings: Small exercises to get you used to reading and writing Erlang code
source: http://www.erlang.org/
> 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.
One 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.
## Must Read
- Read [Learn you some Erlang for great good](http://learnyousomeerlang.com/)
- [Erlang and code style](https://medium.com/@jlouis666/erlang-and-code-style-b5936dceb5e4)
- Read our [guidelines](https://github.com/lambdaclass/guidelines)
## Exercises
The 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.
Start by clicking one of the exercises below or just run:
```
$ make
```
### A. Sequential Programming
1. [Installing](sequential/installing/)
1. [Filter numbers](sequential/filter_numbers/)
1. [Filter in](sequential/filter_numbers#filter-in)
1. [Filter out](sequential/filter_numbers#filter-out)
1. [Filter in values](sequential/filter_numbers#filter-in-values)
1. [Hello world](sequential/hello/)
1. [Hello pattern](sequential/hello_pattern/)
1. [Lists](sequential/lists/)
1. [Reverse](sequential/lists#reverse)
1. [Remove Consecutive](sequential/lists#remove-consecutive)
1. [Even Fibonacci Numbers](sequential/lists#even-fibonacci-numbers)
1. [Reduce](sequential/lists#reduce)
1. [Rotate Lists](sequential/lists#rotate-lists)
1. [Run-length Encoding of a List](sequential/lists#run-length-encoding-of-a-list)
1. [Any](sequential/lists#any)
1. [Anagram](sequential/lists#anagram)
1. [First Letter Last Letter](sequential/lists#first-letter-last-letter-game)
1. [Bank Accounts](sequential/bank_accounts/)
1. [Calculate BMI](sequential/calculate_bmi/)
1. [Insert element at position](sequential/insert_element_at/)
1. [Filter Fibonacci Numbers](sequential/filter_fibonacci_numbers/)
1. [Maps](sequential/maps/)
1. [Sum of Values](sequential/maps#sum-of-values)
1. [Min Value](sequential/maps#min-value)
1. [Sort keys](sequential/maps/#sort-keys)
1. [Return values](sequential/maps/#return-values)
1. [Mapping a Map](sequential/maps#mapping-a-map)
1. [Merge Map](sequential/maps#merge-map)
1. [List to Map](sequential/maps#list-to-map)
1. [Records to Maps](sequential/maps#records-to-maps)
1. [Maps to Records](sequential/maps#maps-to-records)
1. [Proplist to Map](sequential/maps#proplist-to-map)
1. [Implement a simple regex engine](sequential/regex/)
### B. Concurrent Programming
1. [Parallel Map](concurrent/parallel_map)
1. [Calculator](concurrent/calculator)
1. [Priority](concurrent/priority)
1. [Ring Benchmark](concurrent/ring_benchmark)
### C. OTP
1. [Shopping Cart](otp/shopping_cart/)
1. [Worker pool](/otp/pool)
### D. Distributed Programming
1. [Remote Function](distributed/remote_fun/)
### E. Libraries
1. [Shortly](libraries/shortly/)
## Down the Rabbit Hole
### Advanced Erlang
- [How to build stable systems](https://medium.com/@jlouis666/how-to-build-stable-systems-6fe9dcf32fc4)
- [Stacking Theory for Systems Design](https://medium.com@jlouis666/stacking-theory-for-systems-design-2450e6300689)
- [A Ramble Through Erlang IO Lists](http://prog21.dadgum.com/70.html)
- [Erlang String Handling](https://medium.com/@jlouis666/erlang-string-handling-7588daad8f05)
- [How Erlang does scheduling](http://jlouisramblings.blogspot.com.ar/2013/01/how-erlang-does-scheduling.html)
- [Red and Green callbacks](https://joearms.github.io/published/2013-04-02-Red-and-Green-Callbacks.html)
- [RTB: Where Erlang BLOOMs](https://ferd.ca/rtb-where-erlang-blooms.html)
- [On Erlang, State and Crashes](http://jlouisramblings.blogspot.com.ar/2010/11/on-erlang-state-and-crashes.html)
- [It's About the Guarantees](https://ferd.ca/it-s-about-the-guarantees.html)
- [Don’t Lose Your ETS Tables](http://steve.vinoski.net/blog/2011/03/23/dont-lose-your-ets-tables/)
- [The Road we didn't go down ](http://armstrongonsoftware.blogspot.com.ar/2008/05/road-we-didnt-go-down.html)
- [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)
- [Sequence and Order in Erlang](https://web.archive.org/web/20160419085030/http://notdennisbyrne.blogspot.com.ar/2008/04/sequence-and-order-in-erlang.html)
- [The Erlang shell](https://medium.com/@jlouis666/the-erlang-shell-ab8d8bec3972)
- [Queues Don't Fix Overload](https://ferd.ca/queues-don-t-fix-overload.html)
- [https://www.erlang-in-anger.com/](https://www.erlang-in-anger.com/)
- Whatever you can find in [spawned shelter](http://spawnedshelter.com/)
### Distributed systems and databases
- [Distributed Systems: for fun and profit](http://book.mixu.net/distsys/single-page.html)
- Designing Data-intensive applications book
- [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/)
- [Jepsen: On the perils of network partitions](https://aphyr.com/posts/281-jepsen-on-the-perils-of-network-partitions)
- MapReduce: Simplified Data Processing on Large Clusters
- Dynamo: Amazon’s Highly Available Key-value Store
- Bigtable: A Distributed Storage System for Structured Data
- Time, Clocks and Ordering of Events in a Distributed System
- Unreliable failure detectors and reliable distributed systems
### Riak and riak_core
- [Riak Core Tutorial](https://github.com/lambdaclass/riak_core_tutorial/)
### Elixir
- [Erlang/Elixir Syntax: A Crash Course](https://elixir-lang.org/crash-course.html)
- [Elixir: Introduction](https://elixir-lang.org/getting-started/introduction.html)
- [Phoenix: Overview](https://hexdocs.pm/phoenix/overview.html)
- [Phoenix: Up and Running](https://hexdocs.pm/phoenix/up_and_running.html#content)
- [GenState](https://hexdocs.pm/gen_stage/GenStage.html)
- [GenStage advanced](https://elixirschool.com/en/lessons/advanced/gen-stage/)
================================================
FILE: check-progress
================================================
#!/usr/bin/env escript
%% -*- erlang -*-
%%! -smp enable -sname factorial -mnesia debug verbose
main(_) ->
Folders = exercise_folders(),
run_tests(Folders, 1, length(Folders)).
run_tests([], _, _) ->
io:format("All tests completed."),
true;
run_tests([Folder | MoreFolders], CurrentCount, TotalCount) ->
case run_make_on_folder(Folder) of
true ->
io:format("~p.......ok~n", [Folder]),
run_tests(MoreFolders, CurrentCount + 1, TotalCount);
false ->
print_failure(Folder, CurrentCount, TotalCount)
end.
print_failure(Folder, CurrentCount, TotalCount) ->
io:format("Exercise `~s` failed.~n", [Folder]),
io:format("Review it, you may be closer than you think.~n",[]),
io:format("Progress: ~p/~p.~n", [CurrentCount-1, TotalCount]).
run_make_on_folder(Folder) ->
Output = cmd(Folder, "make"),
string:str(Output, "tests passed.") > 0.
cmd(Folder, Command) ->
Cmd = io_lib:format("~s ~s", [Command, Folder]),
CmdString = binary_to_list(iolist_to_binary(Cmd)),
os:cmd(CmdString).
exercise_folders() ->
lists:append(
[
in_folder("sequential", ["installing", "hello", "hello_pattern",
"lists", "bank_accounts", "calculate_bmi", "insert_element_at",
"filter_fibonacci_numbers", "maps", "regex"]),
in_folder("concurrent", ["calculator", "parallel_map", "priority", "ring_benchmark"]),
in_folder("otp", ["shopping_cart", "pool"])
%in_folder("distributed", ["remote_fun"])
%in_folder("libraries", ["shortly"])
]).
in_folder(Folder, SubFolders) ->
[Folder ++ "/" ++ SubFolder || SubFolder <- SubFolders].
================================================
FILE: concurrent/calculator/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
.lock
================================================
FILE: concurrent/calculator/README.md
================================================
# Calculator
## Reading Material
- [The Hitchhiker's Guide to Concurrency](http://learnyousomeerlang.com/the-hitchhikers-guide-to-concurrency)
- [More On Multiprocessing](http://learnyousomeerlang.com/more-on-multiprocessing)
## Exercise
You 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:
- `start_calculator/0`: This functions will spawn a `calculator:calculator_server/1`.
- `calculator_server/0`: It will receive through messages the operation to execute and its arguments, and send back the result.
- `turn_off/1`: Shuts down the calculator server.
This 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.
```erlang
1> Cal = calculator:start_calculator().
<0.67.0>
2> calculator:add(Cal, 1, 3).
4
3> calculator:subtract(Cal, 1, 3).
-2
4> calculator:multiply(Cal, 7, 3).
21
5> calculator:divide(Cal, 7, 3).
2.3333333333333335
6> calculator:divide(Cal, 0, 3).
0.0
7> calculator:divide(Cal, 2, 3).
0.6666666666666666
8> calculator:turn_off(Cal).
off
```
Check our proposed [solution](solution/calculator.erl).
================================================
FILE: concurrent/calculator/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: concurrent/calculator/solution/calculator.app.src
================================================
{application, calculator,
[{description, "calculator"},
{vsn, "0.1.0"},
{registered, []},
{mod, { calculator_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: concurrent/calculator/solution/calculator.erl
================================================
-module(calculator).
-export([start_calculator/0,
calculator_server/0,
add/3,
subtract/3,
multiply/3,
divide/3,
turn_off/1]).
start_calculator() ->
spawn(calculator, calculator_server, []).
send_operation(Pid, Operation, Numbers) ->
Pid ! {self(), Operation, Numbers},
receive
Result ->
Result
end.
add(Pid, X, Y) ->
send_operation(Pid, add, {X, Y}).
subtract(Pid, X, Y) ->
send_operation(Pid, subtract, {X, Y}).
multiply(Pid, X, Y) ->
send_operation(Pid, multiply, {X, Y}).
divide(Pid, X, Y) ->
send_operation(Pid, divide, {X, Y}).
calculator_server() ->
receive
{From, add, {X, Y}} ->
From ! X + Y;
{From, subtract, {X, Y}} ->
From ! X - Y;
{From, multiply, {X, Y}} ->
From ! X * Y;
{From, divide, {X, Y}} ->
From ! X / Y;
off ->
exit(shutdown)
end,
calculator_server().
turn_off(Pid) ->
Pid ! off.
================================================
FILE: concurrent/calculator/src/calculator.app.src
================================================
{application, calculator,
[{description, "calculator"},
{vsn, "0.1.0"},
{registered, []},
{mod, { calculator_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: concurrent/calculator/src/calculator.erl
================================================
-module(calculator).
-export([start_calculator/0,
calculator_server/0,
turn_off/1]).
start_calculator() ->
put_your_solution_here.
calculator_server() ->
put_your_solution_here.
turn_off(Pid) ->
put_your_solution_here.
================================================
FILE: concurrent/calculator/test/calculator_SUITE.erl
================================================
-module(calculator_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(calculator_test).
================================================
FILE: concurrent/calculator/test/calculator_test.erl
================================================
-module(calculator_test).
-include_lib("eunit/include/eunit.hrl").
calculator_add_test() ->
Cal = calculator:start_calculator(),
?assertEqual(2+3, calculator:add(Cal, 2, 3)).
calculator_subtract_test() ->
Cal = calculator:start_calculator(),
?assertEqual(2-3, calculator:subtract(Cal, 2, 3)).
calculator_multiply_test() ->
Cal = calculator:start_calculator(),
?assertEqual(7*9, calculator:multiply(Cal, 7, 9)).
calculator_divide_test() ->
Cal = calculator:start_calculator(),
?assertEqual(25/5, calculator:divide(Cal, 25, 5)).
================================================
FILE: concurrent/parallel_map/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
.lock
================================================
FILE: concurrent/parallel_map/README.md
================================================
# Parallel Map
## Reading Material
- [The Hitchhiker's Guide to Concurrency](http://learnyousomeerlang.com/the-hitchhikers-guide-to-concurrency)
- [More On Multiprocessing](http://learnyousomeerlang.com/more-on-multiprocessing)
## Exercise
You 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.
```erlang
1> Fun = fun(X) -> X * 10 end,
2> List = [1, 2, 3, 4, 5],
3> parallel_map:pmap(Fun, List)
[10,20,30,40,50]
```
Check our proposed [solution](solution/parallel_map.erl).
================================================
FILE: concurrent/parallel_map/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: concurrent/parallel_map/solution/parallel_map.app.src
================================================
{application, parallel_map,
[{description, "parallel_map"},
{vsn, "0.1.0"},
{registered, []},
{mod, { parallel_map_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: concurrent/parallel_map/solution/parallel_map.erl
================================================
-module(parallel_map).
-export([pmap/2,
apply/3]).
pmap(Fun, List) ->
Pids = lists:map(fun(Elem) ->
spawn(parallel_map, apply, [self(), Fun, Elem])
end,
List),
lists:map(fun(Pid) ->
receive
{Pid, Result} ->
Result
end
end,
Pids).
apply(From, Fun, Elem) ->
From ! {self(), Fun(Elem)}.
================================================
FILE: concurrent/parallel_map/src/parallel_map.app.src
================================================
{application, parallel_map,
[{description, "parallel_map"},
{vsn, "0.1.0"},
{registered, []},
{mod, { parallel_map_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: concurrent/parallel_map/src/parallel_map.erl
================================================
-module(parallel_map).
-export([pmap/2]).
pmap(Fun, List) ->
your_solution_here.
================================================
FILE: concurrent/parallel_map/test/parallel_map_SUITE.erl
================================================
-module(parallel_map_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(parallel_map_test).
================================================
FILE: concurrent/parallel_map/test/parallel_map_test.erl
================================================
-module(parallel_map_test).
-include_lib("eunit/include/eunit.hrl").
parallel_map_result_test() ->
Fun = fun(X) -> X * 10 end,
List = [1, 2, 3, 4, 5],
Result = lists:map(Fun, List),
?assertEqual(Result, parallel_map:pmap(Fun, List)).
================================================
FILE: concurrent/priority/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
.lock
================================================
FILE: concurrent/priority/Makefile
================================================
.PHONY: compile, run
default: run
compile:
erl -compile priority
run: compile
erl -noshell -s priority send_test -s init stop
================================================
FILE: concurrent/priority/README.md
================================================
# Priority
## Reading material
- [Learn You Some Erlang: More On Multiprocessing](http://learnyousomeerlang.com/more-on-multiprocessing)
- [Erlang Manual: processes](http://erlang.org/doc/reference_manual/processes.html)
- [Erlang Manual: receive expression](http://erlang.org/doc/reference_manual/expressions.html#id81776)
## Exercise
For this exercise we will process messages according to priorities from the erlang message box without using any data structure.
Messages 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).
You will write 3 functions for the `priority` module:
- `start/0`: This will create the process that will receive the messages (using `priority_loop/1`) and return its PID.
- `get_messages/1`: Given the PID will return all the messages stored in the receive loop state.
- `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.
Notes:
- Messages are of the form `{Priority, Message}`.
- `Priority` is either `vip` or `normal`.
- `priority_loop/1` may receive other things to perform certain actions needed.
You 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).
================================================
FILE: concurrent/priority/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: concurrent/priority/solution/priority.app.src
================================================
{application, priority,
[{description, "priority"},
{vsn, "0.1.0"},
{registered, []},
{mod, { priority_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: concurrent/priority/solution/priority.erl
================================================
-module(priority).
-export([start/0,
send_vip/2,
send_normal/2,
get_messages/1,
priority_loop/1]).
start() ->
spawn_link(priority, priority_loop, [[]]).
send_vip(Pid, Msg) ->
Pid ! {vip, Msg}.
send_normal(Pid, Msg) ->
Pid ! {normal, Msg}.
get_messages(Pid) ->
Pid ! {server, {self(), current_msgs}},
receive
Msgs ->
Msgs
end.
priority_loop(State) ->
receive
{vip, Msg} ->
priority_loop([{vip, Msg}] ++ State)
after 0 ->
receive
{server, {From, current_msgs}} ->
From ! lists:reverse(State);
{Priority, Msg} ->
priority_loop([{Priority, Msg}] ++ State)
end
end.
================================================
FILE: concurrent/priority/src/priority.app.src
================================================
{application, priority,
[{description, "priority"},
{vsn, "0.1.0"},
{registered, []},
{mod, { priority_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: concurrent/priority/src/priority.erl
================================================
-module(priority).
-export([start/0,
get_messages/1,
priority_loop/1]).
start() ->
your_solution_here.
get_messages(Pid) ->
your_solution_here.
priority_loop(State) ->
your_solution_here.
================================================
FILE: concurrent/priority/test/.keep
================================================
================================================
FILE: concurrent/priority/test/priority_SUITE.erl
================================================
-module(priority_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(priority_test).
================================================
FILE: concurrent/priority/test/priority_test.erl
================================================
-module(priority_test).
-include_lib("eunit/include/eunit.hrl").
sort_fun({vip, _}, {vip, _}) ->
true;
sort_fun({normal, _}, {normal, _}) ->
true;
sort_fun({vip, _}, {normal, _}) ->
true;
sort_fun({normal, _}, {vip, _}) ->
false.
priority_test() ->
Msgs = [{normal, 1},
{normal, 2},
{vip, 3},
{vip, 4},
{normal, 5},
{normal, 6},
{normal, 7},
{vip, 8},
{vip, 9}],
Msgs_ordered = lists:sort(fun sort_fun/2, Msgs),
Pid = priority:start(),
lists:foreach(fun(Msg) -> Pid ! Msg end, Msgs),
?assertEqual(Msgs_ordered, priority:get_messages(Pid)).
================================================
FILE: concurrent/ring_benchmark/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
================================================
FILE: concurrent/ring_benchmark/README.md
================================================
# Ring benchmark
## Reading Material
## Exercise
Write a function `ring:ring/2` which takes 2 arguments: M and N.
This function should create N process in a ring in such a way
that sending a message to the first process it get passed around
the ring M times so that a total of N * M messages get sent.
In case you need any guidance please check our
[proposed solution](solution/ring.erl).
================================================
FILE: concurrent/ring_benchmark/rebar.config
================================================
{erl_opts, [debug_info]}.
{deps, []}.
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: concurrent/ring_benchmark/solution/ring.erl
================================================
-module(ring).
-export([ring/2]).
node_loop(Parent) ->
receive
{msg, []} -> Parent ! done;
{msg, [FirstNode | OtherNodes]} ->
io:format("Node ~p forwarding to ~p~n", [self(), FirstNode]),
FirstNode ! {msg, OtherNodes},
node_loop(Parent)
end.
% N processes, M messages
ring(N, M) ->
[FirstNode | Nodes] = create_processes(N, M),
BeforeFistMessage = os:timestamp(),
FirstNode ! {msg, Nodes},
receive
done -> io:format("done received ~n")
end,
AfterLastMessage = os:timestamp(),
ElapsedTime = timer:now_diff(AfterLastMessage, BeforeFistMessage),
io:format("Processes: ~p, Messages ~p in ~pms~n", [N, M, ElapsedTime]).
create_processes(N, M) ->
Parent = self(),
Processes = [spawn_link(fun () -> node_loop(Parent) end)
|| _ <- lists:seq(1, N)],
lists:append(lists:duplicate(M, Processes)).
================================================
FILE: concurrent/ring_benchmark/solution/ring_benchmark.app.src
================================================
{application, ring_benchmark,
[{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, { ring_benchmark_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["Apache 2.0"]},
{links, []}
]}.
================================================
FILE: concurrent/ring_benchmark/src/ring.erl
================================================
-module(ring).
-export([ring/2]).
%% N processes, M messages
ring(_N, _M) ->
complete_here.
================================================
FILE: concurrent/ring_benchmark/src/ring_benchmark.app.src
================================================
{application, ring_benchmark,
[{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, { ring_benchmark_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["Apache 2.0"]},
{links, []}
]}.
================================================
FILE: concurrent/ring_benchmark/test/ring_benchmark_SUITE.erl
================================================
-module(ring_benchmark_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(ring).
================================================
FILE: concurrent/ring_benchmark/test/ring_test.erl
================================================
-module(ring_test).
-include_lib("eunit/include/eunit.hrl").
ring_test() ->
?assertMatch(#{msgs_sent := 4, procs_started := 2},
run_ring(2, 2)),
?assertMatch(#{msgs_sent := 12, procs_started := 4},
run_ring(4, 3)),
?assertMatch(#{msgs_sent := 380, procs_started := 19},
run_ring(19, 20)).
run_ring(M, N) ->
dbg_start_tracing(M, N),
ring:ring(M, N),
Result = dbg_wait_for_result(M,N),
dbg_stop_tracing(),
Result.
dbg_start_tracing(M, N) ->
dbg:stop_clear(),
dbg:tracer(process, {fun dbg_fun_handler/2, dbg_init_state(M, N)}),
dbg:p(new, [m,p]).
dbg_stop_tracing() -> dbg:stop_clear().
dbg_wait_for_result(M, N) ->
WaitTime = M * N * 1000,
receive
DbgState -> DbgState
after WaitTime ->
#{}
end.
dbg_init_state(M, N) ->
#{m => M,
n => N,
ret_pid => self(),
procs_started => 0,
msgs_sent => 0,
informed => false}.
dbg_fun_handler(Trace, State) ->
TracedState = dbg_capture_trace(Trace, State),
dbg_inform_if_necessary(TracedState).
dbg_capture_trace(Trace, State = #{procs_started := ProcsStarted,
msgs_sent := MsgsSent}) ->
case Trace of
{_, _, spawned, _, _} -> State#{procs_started := ProcsStarted + 1};
{_, _, send, _, _} -> State#{msgs_sent := MsgsSent + 1};
_ -> State
end.
dbg_inform_if_necessary(State = #{m := M, n := N, ret_pid := RetPid}) ->
MN = M * N,
case State of
#{msgs_sent := MN, informed := false} ->
RetPid ! State#{informed => true};
_ ->
State
end.
================================================
FILE: distributed/remote_fun/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
================================================
FILE: distributed/remote_fun/Makefile
================================================
.PHONY: server client
default:
@echo "usage make [client|server]"
client:
./rebar3 shell --sname 'client@localhost'
server:
./rebar3 shell --sname 'server@localhost'
================================================
FILE: distributed/remote_fun/README.md
================================================
# Remote Function Server
## Reading material
- [Learn you Some Erlang: Distribunomicon](http://learnyousomeerlang.com/distribunomicon)
- [My favorite erlang program](https://joearms.github.io/published/2013-11-21-My-favorite-erlang-program.html)
## Exercise
### Problem
Create an Erlang cluster with 2 nodes. One node is a server that just receives a function and become it.
The other node sends a function the first and execute something remotely.
### Solution
The server (`remote_fun_server.erl`) receives a message as normal, and get the function from there.
The client (`remote_fun_client.erl`) creates a function that is able to receive and loop, and also
to be killed, then it's send to the server.
#### Running the solution
Run two terminals with ``make server`` and ``make client``.
Then connect the two nodes:
~~~
(client@localhost)1> net_kernel:connect_node(server@localhost).
true
~~~
~~~
(server@localhost)1> net_kernel:connect_node(client@localhost).
true
~~~
After the connections are fine, you can test the functions with:
~~~
(server@localhost)2> remote_fun_server:function_server().
~~~
~~~
(client@localhost)2> remote_fun_client:function_client(server@localhost).
Ok!
ok
~~~
================================================
FILE: distributed/remote_fun/src/remote_fun.app.src
================================================
{application, remote_fun,
[{description, "Remote function"},
{vsn, "0.1.0"},
{registered, []},
{mod, { remote_fun_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: distributed/remote_fun/src/remote_fun_client.erl
================================================
-module(remote_fun_client).
-export([function_client/1]).
function_client(RemoteNode) ->
FunctionServerPid = {function_server, RemoteNode},
FunctionServerPid ! {be, fun multiply_loop/0},
FunctionServerPid ! {self(), {3, 9}},
receive
{product_result, _Result = 27} -> ok
end,
FunctionServerPid ! kill,
io:format("Ok!~n").
multiply_loop() ->
receive
{From, {Factor1, Factor2}} ->
Result = Factor1 * Factor2,
From ! {product_result, Result},
multiply_loop();
kill -> kill
end.
================================================
FILE: distributed/remote_fun/src/remote_fun_server.erl
================================================
-module(remote_fun_server).
-export([function_server/0]).
function_server() ->
register(function_server, self()),
receive
{be, Function} -> Function()
end.
================================================
FILE: distributed/remote_fun/test/.keep
================================================
================================================
FILE: libraries/shortly/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
================================================
FILE: libraries/shortly/Makefile
================================================
.PHONY: default server samples test
default:
@echo "usage [test|server]"
server:
./rebar3 shell --apps shortly
node_1:
./rebar3 shell --apps shortly --config src/config/node_1.config --sname node_1
node_2:
./rebar3 shell --apps shortly --config src/config/node_2.config --sname node_2
================================================
FILE: libraries/shortly/README.md
================================================
# Shortly: Link Shortener
## Reading material
- [Rebar3 build tool](https://github.com/erlang/rebar3)
- [Rebar3 hex](https://hex.pm/docs/rebar3_usage)
- [Cowboy http server](https://github.com/ninenines/cowboy)
- [Cowboy user-guide](https://ninenines.eu/docs/en/cowboy/2.2/guide/)
## Exercise
This exercise will be divided in multiple parts:
1. [Shortly application](#shortly-application)
1. [Create two nodes](#create-two-nodes)
1. [Mnesia](#mnesia)
1. [Syn](#syn)
### Shortly application
Create an ``OTP`` application using ``rebar3`` and [cowboy](https://github.com/ninenines/cowboy)
that is capable of receiving long links and returning shorts ones:
- Receive a ``HTTP POST`` at `http://localhost:8080/<LONG_URL>` returning a shortened link.
- Receive a ``HTTP GET`` at `http://localhost:8080/<SHORT_URL>` returning the original long link.
- Accept websocket connections at `http://localhost:8080/news` and notify every time a new link is shortened.
**BONUS:** Create similar endpoints (`GET` and `POST`), but using `cowboy_rest` handler.
### Create two nodes
Now 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:
- Receive cowboy's port as either a configuration variable or an environment variable.
- Set the node name when starting it.
### Mnesia
So 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:
- Create the Mnesia schema.
- Create the `shortly_urls` table.
- Add the Mnesia application as a dependency (no more manual starts).
- Change code to use Mnesia and abstract its usage to its own module `shortly_db`.
If 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.
### Syn
For 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:
- Add the Syn application as a dependency.
- Change code to use Syn and abstract it to a `shortly_presence` module.
Now 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.
## Solution
- ``src/bitly_short_link_handler.erl`` handles ``POST`` and ``GET`` requests.
- ``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.
To run the solution first star the server with ``make server`` and then ``make test`` for running simple requests.
================================================
FILE: libraries/shortly/rebar.config
================================================
{erl_opts, [debug_info]}.
{deps, [cowboy,
jsx,
gun,
syn]}.
{plugins,[rebar3_hex]}.
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: libraries/shortly/solution/config/node_1.config
================================================
[
{mnesia, [{dir, "/tmp/mnesia/node_8010"}]},
{shortly, [{port, 8010}]}
].
================================================
FILE: libraries/shortly/solution/config/node_2.config
================================================
[
{mnesia, [{dir, "/tmp/mnesia/node_8020"}]},
{shortly, [{port, 8020}]}
].
================================================
FILE: libraries/shortly/solution/shortly.app.src
================================================
{application, shortly,
[{description, "Link shortener exercise"},
{vsn, "0.1.0"},
{registered, []},
{mod, { shortly_app, []}},
{applications,
[kernel,
stdlib,
mnesia,
syn,
cowboy
]},
{env,[{port, 8080}]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: libraries/shortly/solution/shortly_app.erl
================================================
-module(shortly_app).
-behaviour(application).
-export([start/2,
stop/1,
install/1]).
-record(shortly_urls, {hash, url}).
start(_StartType, _StartArgs) ->
{ok, Port} = application:get_env(shortly, port),
Dispatch = cowboy_router:compile(
[
{'_', [
{"/news", shortly_ws_handler, []},
{"/:url", shortly_link_handler, []}
]}
]),
{ok, _} = cowboy:start_clear(http, [{port, Port}], #{
env => #{dispatch => Dispatch}
}),
shortly_sup:start_link().
stop(_State) ->
cowboy:stop_listener(http),
ok.
install(Nodes) ->
mnesia:stop(),
rpc:multicall(Nodes, application, stop, [mnesia]),
ok = mnesia:create_schema([node() | Nodes]),
mnesia:start(),
rpc:multicall(Nodes, application, start, [mnesia]),
mnesia:create_table(shortly_urls,
[{attributes, record_info(fields, shortly_urls)},
{type, set},
{ram_copies, Nodes}]),
mnesia:stop().
================================================
FILE: libraries/shortly/solution/shortly_db.erl
================================================
-module(shortly_db).
-export([store_url/1,
save_url/2]).
-record(shortly_urls, {hash, url}).
store_url(ShortUrl) ->
Result = mnesia:activity(transaction, fun() ->
mnesia:read(shortly_urls, ShortUrl)
end),
case Result of
[] ->
[];
[#shortly_urls{hash=ShortUrl, url=LongUrl}] ->
[{ShortUrl, LongUrl}]
end.
save_url(ShortUrl, LongUrl) ->
mnesia:activity(transaction, fun() ->
mnesia:write(#shortly_urls{hash=ShortUrl, url=LongUrl})
end).
================================================
FILE: libraries/shortly/solution/shortly_link_handler.erl
================================================
-module(shortly_link_handler).
-export([init/2]).
init(Req, State) ->
Url = get_request_url(Req),
ReqMethod = cowboy_req:method(Req),
{Status, ShortUrl} = handle_request(ReqMethod, Url),
Body = get_body_response(Status, ShortUrl),
Header = get_headers(Status, ShortUrl),
Resp = cowboy_req:reply(Status, Header, Body, Req),
{ok, Resp, State}.
handle_request(<<"POST">>, Url) ->
{CreationStatus, ShortUrl} = shortly_shortener:short(Url),
HttpStatus =
case CreationStatus of
old -> 200; %ok
new -> 201 %created
end,
{HttpStatus, ShortUrl};
handle_request(<<"GET">>, Url) ->
case shortly_shortener:get(Url) of
not_found -> {404, ""};
ShortUrl -> {302, ShortUrl}
end.
get_request_url(Req) ->
<<"/", Url/binary>> = cowboy_req:path(Req),
Url.
get_body_response(404, _) -> <<>>;
get_body_response(_, Url) -> jsx:encode(#{url => Url }).
get_headers(Status, _) when Status =/= 302 ->
common_headers();
get_headers(302, RedirectUrl) ->
CommonHeaders = common_headers(),
CommonHeaders#{<<"location">> => RedirectUrl }.
common_headers() ->
#{<<"content-type">> => <<"application/json">>}.
================================================
FILE: libraries/shortly/solution/shortly_notification_algorithm.erl
================================================
-module(shortly_notification_algorithm).
-export([behaviour_info/1]).
behaviour_info(callbacks) ->
[{init,0},
{subscribe, 1},
{unsubscribe, 1},
{notify, 1}].
================================================
FILE: libraries/shortly/solution/shortly_notification_ets.erl
================================================
-module(shortly_notification_ets).
-behaviour(shortly_notification_algorithm).
-export([init/0,
subscribe/1,
unsubscribe/1,
notify/1]).
-define(ETS_NAME, shortly_notification_ets_table).
init() ->
ets:new(?ETS_NAME, [set, public, named_table]).
subscribe(Pid) ->
ets:insert_new(?ETS_NAME, {Pid}).
unsubscribe(Pid) ->
ets:delete(?ETS_NAME, Pid).
notify(Msg)->
ets:foldl(
fun({Pid}, _) ->
Pid ! Msg
end,ignored, ?ETS_NAME).
================================================
FILE: libraries/shortly/solution/shortly_notification_pg2.erl
================================================
-module(shortly_notification_pg2).
-behaviour(shortly_notification_algorithm).
-export([init/0,
subscribe/1,
unsubscribe/1,
notify/1]).
-define (PG2_NAME, shortly_notification_pg2_name).
init() ->
pg2:create(?PG2_NAME).
subscribe(Pid) ->
pg2:join(?PG2_NAME, Pid).
unsubscribe(Pid) ->
pg2:leave(?PG2_NAME, Pid).
notify(Msg) ->
Subs = pg2:get_members(?PG2_NAME),
lists:foreach(
fun(Pid) ->
Pid ! Msg
end, Subs).
================================================
FILE: libraries/shortly/solution/shortly_shortener.erl
================================================
-module(shortly_shortener).
-export([init/0,
short/1,
get/1,
subscribe/1,
unsubscribe/1]).
init() ->
notify_init().
short(LongUrl) ->
ShortUrl = shortening_algorithm(LongUrl),
EntryType = store_url(LongUrl, ShortUrl),
notify_subscribers(EntryType, LongUrl, ShortUrl),
{EntryType, ShortUrl}.
get(ShortUrl) ->
search_long_url(ShortUrl).
subscribe(Pid) -> notify_subscribe(Pid).
unsubscribe(Pid) -> notify_unsubscribe(Pid).
%% Internal functions
shortening_algorithm(Url) ->
Hash = crypto:hash(md4, Url),
Base64 = base64:encode(Hash),
Sub = binary:part(Base64,1,5),
WithoutSlashes = re:replace(Sub, "/", "_", [global, {return, list}]),
list_to_binary(WithoutSlashes).
store_url(LongUrl, ShortUrl) ->
case shortly_db:store_url(ShortUrl) of
[] ->
shortly_db:save_url(ShortUrl, LongUrl),
new;
[{ShortUrl, LongUrl}] ->
old
end.
search_long_url(ShortUrl) ->
case shortly_db:store_url(ShortUrl) of
[] -> not_found;
[{ShortUrl, LongUrlEntry}] -> LongUrlEntry
end.
notify_technique_call(F,A) -> apply(shortly_syn,F,A).
notify_init() -> notify_technique_call(init,[]).
notify_subscribe(Pid) -> notify_technique_call(subscribe,[Pid]).
notify_unsubscribe(Pid) -> notify_technique_call(unsubscribe,[Pid]).
notify_notify(Msg) -> notify_technique_call(notify,[Msg]).
notify_subscribers(old,_,_) -> nothing;
notify_subscribers(new, LongUrl, ShortUrl) ->
notify_notify(#{long_url => LongUrl, short_url => ShortUrl}).
================================================
FILE: libraries/shortly/solution/shortly_sup.erl
================================================
-module(shortly_sup).
-behaviour(supervisor).
-export([start_link/0,
init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
shortly_shortener:init(),
{ok, { {one_for_all, 0, 1}, []} }.
================================================
FILE: libraries/shortly/solution/shortly_syn.erl
================================================
-module(shortly_syn).
-behaviour(shortly_notification_algorithm).
-export([init/0,
subscribe/1,
unsubscribe/1,
notify/1]).
-define(SYN_NAME, syn_ws_connections).
init() ->
syn:init().
subscribe(Pid) ->
syn:join(?SYN_NAME, Pid).
unsubscribe(Pid) ->
pg2:leave(?SYN_NAME, Pid).
notify(Msg) ->
syn:publish(?SYN_NAME, Msg).
================================================
FILE: libraries/shortly/solution/shortly_ws_handler.erl
================================================
-module(shortly_ws_handler).
-export([init/2,
websocket_init/1,
websocket_handle/2,
websocket_info/2,
terminate/3]).
init(Req, Opts) ->
{cowboy_websocket,Req,Opts}.
websocket_init(State) ->
shortly_shortener:subscribe(self()),
{ok, State}.
websocket_handle(_Msg, State) ->
{ok, State}.
websocket_info(#{long_url := LongUrl, short_url := ShortUrl}, State) ->
Response = get_ws_json_response(LongUrl, ShortUrl),
{reply, {text, Response}, State};
websocket_info(_Info, State) ->
{ok, State}.
terminate(_Msg, _Req, _State) ->
shortly_shortener:unsubscribe(self()),
ok.
get_ws_json_response(LongUrl, ShortUrl) ->
Data = #{ <<"long_url">> => LongUrl,
<<"short_url">> => ShortUrl},
jsx:encode(Data).
================================================
FILE: libraries/shortly/src/shortly.app.src
================================================
{application, shortly,
[{description, "Link shortener exercise"},
{vsn, "0.1.0"},
{registered, []},
{mod, { shortly_app, []}},
{applications,
[kernel,
stdlib,
mnesia,
syn,
cowboy
]},
{env,[{port, 8080}]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: libraries/shortly/src/shortly_app.erl
================================================
-module(shortly_app).
-behaviour(application).
-export([]).
================================================
FILE: libraries/shortly/test/shortly_shortener_SUITE.erl
================================================
-module(shortly_shortener_SUITE).
-include_lib("common_test/include/ct.hrl").
-compile(export_all).
-compile(nowarn_export_all).
-record(shortly_urls, {hash, url}).
all() ->
[test_notfound,
test_created,
test_ok,
test_redirect,
test_ws,
run_eunit].
init_per_suite(Config) ->
mnesia:start(),
mnesia:create_table(shortly_urls,
[{attributes, record_info(fields, shortly_urls)},
{type, set},
{ram_copies, [node()]}]),
application:ensure_all_started(shortly),
application:ensure_all_started(gun),
Config.
end_per_suite(Config) ->
mnesia:clear_table(shortly_urls),
application:stop(shortly),
application:stop(gun),
Config.
init_per_testcase(_,Config) ->
Config.
end_per_testcase(_, Config) ->
Config.
test_notfound(_) ->
NewUrl = url("http://not.com/existent"),
{404, _, #{}} = do_get_request(NewUrl).
test_created(_) ->
NewUrl = url("http://new.created.com/"),
{201, _, #{<<"url">> := _}} = do_post_request(NewUrl).
test_ok(_) ->
LongUrl = url("htts://random.org"),
{201, _, #{<<"url">> := ShortUrl}} = do_post_request(LongUrl),
{200, _, #{<<"url">> := ShortUrl}} = do_post_request(LongUrl).
test_redirect(_) ->
LongUrl = url("http://example.com/testing_redirect"),
{201, _, #{<<"url">> := ShortUrl}} = do_post_request(LongUrl),
{302, Headers, #{<<"url">> := LongUrl}} = do_get_request(ShortUrl),
RedirectionHeader = list_to_binary(proplists:get_value("location", Headers)),
LongUrl = RedirectionHeader.
test_ws(_) ->
WsConn = ws_connect("/news"),
LongUrl = url("http://random.com/long"),
{_, _, #{<<"url">> := ShortUrl}} = do_post_request(LongUrl),
JsonResponse = ws_get(WsConn),
Response = json_to_map(JsonResponse),
#{<<"long_url">> := LongUrl} = Response,
#{<<"short_url">> := ShortUrl} = Response,
ws_terminate(WsConn).
get_request_url(Url) ->
BinaryReqUrl = iolist_to_binary([<<"http://localhost:8080/">>, Url]),
UrlStr = binary_to_list(BinaryReqUrl),
UrlStr.
json_to_map(In) ->
InBinary = case is_list(In) of
true ->
list_to_binary(In);
_ ->
In
end,
case jsx:is_json(InBinary) of
true ->
jsx:decode(InBinary, [return_maps]);
_ ->
#{}
end.
do_post_request(Url) ->
ReqUrl = get_request_url(Url),
{ok, {{_, StatusCode, _}, Headers, Body}} =
httpc:request(post, {ReqUrl, [], [], []}, [], []),
{StatusCode, Headers, json_to_map(Body)}.
do_get_request(Url) ->
ReqUrl = get_request_url(Url),
{ok, {{_, StatusCode, _}, Headers, Body}} =
httpc:request(get, {ReqUrl, []},[{autoredirect,false}],[]),
{StatusCode, Headers, json_to_map(Body)}.
url(Url) ->
StringUrl = http_uri:encode(Url),
list_to_binary(StringUrl).
ws_connect(Path) ->
{ok, Pid} = gun:open("127.0.0.1", 8080, #{retry=>0}),
{ok, http} = gun:await_up(Pid),
Ref = monitor(process, Pid),
gun:ws_upgrade(Pid, Path, [], #{compress => true}),
receive
{gun_ws_upgrade, Pid, ok, _} ->
ok;
_ ->
error(failed)
end,
{Pid, Ref}.
ws_get({Pid,_}) ->
receive
{gun_ws, Pid, {text, Text}} ->
Text;
_ ->
error(failed)
after 5000 ->
error(timout)
end.
ws_terminate({Pid, Ref}) ->
demonitor(Ref),
gun:close(Pid).
run_eunit(_Config) ->
ok = eunit:test(shortly_shortener_test).
================================================
FILE: libraries/shortly/test/shortly_shortener_test.erl
================================================
-module(shortly_shortener_test).
-include_lib("eunit/include/eunit.hrl").
idempotence_test() ->
shortly_shortener:init(),
LongUrl = <<"long_url">>,
{_, ShortUrl} = shortly_shortener:short(LongUrl),
LongUrl = shortly_shortener:get(ShortUrl).
================================================
FILE: otp/pool/README.md
================================================
# Worker Pool
## Reading material
- [Learn You Some Erlang: Who Supervises The Supervisors?](https://learnyousomeerlang.com/supervisors)
- [Learn You Some Erlang: Building an Application With OTP](https://learnyousomeerlang.com/building-applications-with-otp)
- [Erlang OTP Design Principles: Supervisor Behaviour](http://erlang.org/doc/design_principles/sup_princ.html)
## Exercise
For this exercise create a pool of workers to compute a standard `{M, F, A}` or `{F, A}`.
You will write a `gen_server` that controls the pool of workers.
The supervision tree will look like this:

In `poolie_sup` and `poolie_worker_sup` you will define appropriate supervision strategies and child specs.
`poolie_server` implements the following api:
- `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.
- `run/2`: Same as `run/3`, but only takes a function and a list of args.
- `pool_info/0`: Displays the number of idle and busy workers in the pool.
Your task is to implement the `gen_server` callbacks in `poolie_server`, `poolie_worker_sup` and `poolie_worker` to handle the work requests.
### Example
```console
1> poolie_server:run(fun(X) -> X + 1 end, [5]).
Request is being processed
ok
Got results for {#Fun<erl_eval.6.128620087>,[5]}
Result: 6
2> poolie_server:run(lists, max, [[1,2,3,4,5]]).
Request is being processed
ok
Got results for {lists,max,[[1,2,3,4,5]]}
Result: 5
```
### Notes
- Think about what supervisor strategies you should use.
- Should you use `gen_server:call` or `gen_server:cast` to send work to your workers?
================================================
FILE: otp/pool/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
{shell, [
{apps, [poolie]}
]}.
================================================
FILE: otp/pool/solution/poolie.app.src
================================================
{application, poolie,
[{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, { poolie_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: otp/pool/solution/poolie_app.erl
================================================
%%%-------------------------------------------------------------------
%% @doc poolie public API
%% @end
%%%-------------------------------------------------------------------
-module(poolie_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
%%====================================================================
%% API
%%====================================================================
start(_StartType, _StartArgs) ->
poolie_sup:start_link().
%%--------------------------------------------------------------------
stop(_State) ->
ok.
%%====================================================================
%% Internal functions
%%====================================================================
================================================
FILE: otp/pool/solution/poolie_server.erl
================================================
-module(poolie_server).
-behaviour(gen_server).
-export([start_link/0, stop/0, run/3, run/2, pool_info/0]).
-export([init/1, handle_call/3, handle_cast/2]).
-record(state, {limit, idle}).
-define(N, 10). %% Number of workers
%% API
run(M, F, A) when is_list(A) ->
Msg = gen_server:call(?MODULE, {work, {M, F, A}}),
io:format(Msg).
run(F, A) when is_function(F), is_list(A) ->
Msg = gen_server:call(?MODULE, {work, {F, A}}),
io:format(Msg).
pool_info() ->
{PoolSize, Idle, Busy} = gen_server:call(?MODULE, info),
io:format("Pool has ~p workers.~nThere are ~p idle and ~p busy workers.~n", [PoolSize, Idle, Busy]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
stop() ->
gen_server:call({local, ?MODULE}, stop).
%% gen_server callbacks
init(_Args) ->
Workers = poolie_worker_sup:add_workers(?N),
{ok, #state{limit=?N, idle=Workers}}.
handle_call({work, MFA}, _From, S = #state{idle=[Worker | Rest]}) ->
Msg = "Request is being processed~n",
gen_server:cast(Worker, {work, MFA}),
{reply, Msg, S#state{idle=Rest}};
handle_call({work, _MFA}, _From, State) ->
Msg = "No idle workers at the moment, please try again later~n",
{reply, Msg, State};
handle_call(info, _From, S = #state{limit=Limit, idle=Idle}) ->
IdleWorkers = length(Idle),
{reply, {Limit, IdleWorkers, Limit - IdleWorkers}, S};
handle_call(stop, _From, State) ->
{stop, normal, stopped, State};
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast({result, {Worker, MFA, Result}}, S = #state{idle=Idle}) ->
io:format("Got results for ~p~nResult: ~p~n", [MFA, Result]),
{noreply, S#state{idle=[Worker | Idle]}};
handle_cast(_Msg, State) ->
{noreply, State}.
================================================
FILE: otp/pool/solution/poolie_sup.erl
================================================
%%%-------------------------------------------------------------------
%% @doc poolie top level supervisor.
%% @end
%%%-------------------------------------------------------------------
-module(poolie_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
%%====================================================================
%% API functions
%%====================================================================
start_link() ->
supervisor:start_link(?MODULE, []).
%%====================================================================
%% Supervisor callbacks
%%====================================================================
%% Child :: #{id => Id, start => {M, F, A}}
%% Optional keys are restart, shutdown, type, modules.
%% Before OTP 18 tuples must be used to specify a child. e.g.
%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
init([]) ->
WorkerSup = #{id => poolie_worker_sup,
start => {poolie_worker_sup, start_link, []},
restart => permanent, shutdown => 10000, type => supervisor, modules => [poolie_worker_sup]},
Server = #{id => poolie_server,
start => {poolie_server, start_link, []},
restart => permanent, shutdown => 10000, type => worker, modules => [poolie_server]},
{ok, {{one_for_all, 5, 150}, [WorkerSup, Server]}}.
%%====================================================================
%% Internal functions
%%====================================================================
================================================
FILE: otp/pool/solution/poolie_worker.erl
================================================
-module(poolie_worker).
-behaviour(gen_server).
%% API
-export([start/0]).
-export([init/1, handle_call/3, handle_cast/2]).
-define(SERVER, poolie_server).
%% API
start() ->
gen_server:start(?MODULE, [], []).
%% gen_server callbacks
init(_Args) ->
{ok, []}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast({work, MFA = {M, F, A}}, State) ->
Result = erlang:apply(M, F, A),
gen_server:cast(?SERVER, {result, {self(), MFA, Result}}),
{noreply, State};
handle_cast({work, FA = {F, A}}, State) ->
Result = erlang:apply(F, A),
gen_server:cast(?SERVER, {result, {self(), FA, Result}}),
{noreply, State};
handle_cast(_Msg, State) ->
{noreply, State}.
================================================
FILE: otp/pool/solution/poolie_worker_sup.erl
================================================
-module(poolie_worker_sup).
-behaviour(supervisor).
-export([start_link/0, add_workers/1, init/1]).
%% API
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
add_workers(N) when N >= 0 ->
add_workers(N, []).
add_workers(0, Workers) ->
Workers;
add_workers(N, Workers) ->
{ok, Pid} = supervisor:start_child(?MODULE, []),
add_workers(N-1, [Pid | Workers]).
%% supervisor callbacks
init(_Args) ->
{ok, {{simple_one_for_one, 5, 500}, [{poolie_worker, {poolie_worker, start, []},
permanent, 5000, worker, [poolie_worker]}]}}.
================================================
FILE: otp/pool/src/poolie.app.src
================================================
{application, poolie,
[{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, { poolie_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: otp/pool/src/poolie_app.erl
================================================
%%%-------------------------------------------------------------------
%% @doc poolie public API
%% @end
%%%-------------------------------------------------------------------
-module(poolie_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
%%====================================================================
%% API
%%====================================================================
start(_StartType, _StartArgs) ->
poolie_sup:start_link().
%%--------------------------------------------------------------------
stop(_State) ->
ok.
%%====================================================================
%% Internal functions
%%====================================================================
================================================
FILE: otp/pool/src/poolie_server.erl
================================================
-module(poolie_server).
-behaviour(gen_server).
-export([start_link/0, stop/0, run/3, run/2, pool_info/0]).
-export([init/1, handle_call/3, handle_cast/2]).
-record(state, {limit, idle}).
-define(N, 10). %% Number of workers
%% API
run(M, F, A) when is_list(A) ->
Msg = gen_server:call(?MODULE, {work, {M, F, A}}),
io:format(Msg).
run(F, A) when is_function(F), is_list(A) ->
Msg = gen_server:call(?MODULE, {work, {F, A}}),
io:format(Msg).
pool_info() ->
{PoolSize, Idle, Busy} = gen_server:call(?MODULE, info),
io:format("Pool has ~p workers.~nThere are ~p idle and ~p busy workers.~n", [PoolSize, Idle, Busy]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
stop() ->
gen_server:call({local, ?MODULE}, stop).
%% gen_server callbacks
init(_Args) ->
Workers = poolie_worker_sup:add_workers(?N),
{ok, #state{limit=?N, idle=Workers}}.
handle____({result, {Worker, MFA, Result}}, State) ->
put_your_solution_here.
handle_call({work, MFA}, _From, State) ->
put_your_solution_here;
handle_call(info, _From, S = #state{limit=Limit, idle=Idle}) ->
IdleWorkers = length(Idle),
{reply, {Limit, IdleWorkers, Limit - IdleWorkers}, S};
handle_call(stop, _From, State) ->
{stop, normal, stopped, State};
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
================================================
FILE: otp/pool/src/poolie_sup.erl
================================================
%%%-------------------------------------------------------------------
%% @doc poolie top level supervisor.
%% @end
%%%-------------------------------------------------------------------
-module(poolie_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
%%====================================================================
%% API functions
%%====================================================================
start_link() ->
supervisor:start_link(?MODULE, []).
%%====================================================================
%% Supervisor callbacks
%%====================================================================
%% Child :: #{id => Id, start => {M, F, A}}
%% Optional keys are restart, shutdown, type, modules.
%% Before OTP 18 tuples must be used to specify a child. e.g.
%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
init([]) ->
WorkerSup = worker_supervisor_spec,
Server = pool_server_spec,
{ok, {{supervisor_strategy}, [WorkerSup, Server]}}.
%%====================================================================
%% Internal functions
%%====================================================================
================================================
FILE: otp/pool/src/poolie_worker.erl
================================================
-module(poolie_worker).
-behaviour(gen_server).
%% API
-export([start/0]).
-export([init/1, handle_call/3, handle_cast/2]).
-define(SERVER, poolie_server).
%% API
start() ->
gen_server:start(?MODULE, [], []).
%% gen_server callbacks
init(_Args) ->
{ok, []}.
handle____({work, MFA = {M, F, A}}, State) ->
put_your_solution_here;
handle____({work, FA = {F, A}}, State) ->
put_your_solution_here.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
================================================
FILE: otp/pool/src/poolie_worker_sup.erl
================================================
-module(poolie_worker_sup).
-behaviour(supervisor).
-export([start_link/0, add_workers/1, init/1]).
%% API
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
add_workers(N) when N >= 0 ->
put_your_solution_here.
%% supervisor callbacks
init(_Args) ->
Worker = worker_spec,
{ok, {{worker_supervisor_strategy}, [Worker]}}.
================================================
FILE: otp/pool/test/poolie_SUITE.erl
================================================
-module(poolie_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
{ok, _Pid} = application:ensure_all_started(poolie),
ok = eunit:test(poolie_test).
================================================
FILE: otp/pool/test/poolie_test.erl
================================================
-module(poolie_test).
-include_lib("eunit/include/eunit.hrl").
poolie_worker_sup_test() ->
WorkerCount = worker_count(poolie_worker_sup),
%% Add 5 workers
WorkerList = poolie_worker_sup:add_workers(5),
?assertEqual(length(WorkerList), 5),
NewWorkerCount = worker_count(poolie_worker_sup),
?assertEqual(NewWorkerCount, WorkerCount + 5).
%% Helper functions
worker_count(Sup) ->
WorkerSup = supervisor:count_children(Sup),
{workers, WorkerCount} = proplists:lookup(workers, WorkerSup),
WorkerCount.
================================================
FILE: otp/shopping_cart/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
================================================
FILE: otp/shopping_cart/README.md
================================================
# Shopping Cart
## Reading Material
- [Learn You Some Erlang: What is OTP?](http://learnyousomeerlang.com/what-is-otp)
- [Learn You Some Erlang: Clients and Servers](http://learnyousomeerlang.com/clients-and-servers)
## Exercise
In 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.
So, using a [gen_server](http://erlang.org/doc/man/gen_server.html), write the following functions:
* `shopping_cart:start_link/0`: Start an empty shopping cart.
* `shopping_cart:put_item/2`: Takes a PID and an item (a tuple) and saves it into the shopping cart.
* `shopping_cart:cost_so_far/1`: Calculates the cost of all the items inside the shopping cart.
* `finish/1`: Print the total price and finishes the shopping cart.
* All the functions required by the behaviour.
Example:
``` erlang
{ok, Pid} = shopping_cart:start_link().
shopping_cart:put_item(Pid, {orange, 2}).
%% [{orange, 2}]
shopping_cart:put_item(Pid, {apple, 1}).
%% [{apple, 1}, {orange, 2}]
shopping_cart:cost_so_far(Pid).
%% 3
```
[solution](src/solution/shopping_cart.erl)
================================================
FILE: otp/shopping_cart/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: otp/shopping_cart/solution/shopping_cart.app.src
================================================
{application, shopping_cart,
[{description, "shopping_part"},
{vsn, "0.1.0"},
{registered, []},
{mod, { shopping_cart_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: otp/shopping_cart/solution/shopping_cart.erl
================================================
-module(shopping_cart).
-behaviour(gen_server).
-export([start_link/0,
put_item/2,
finish/1,
init/1,
handle_call/3,
terminate/2,
cost_so_far/1,
handle_cast/2
]).
%% Client functions
start_link() ->
gen_server:start_link(?MODULE, [], []).
put_item(Pid, Item) ->
gen_server:call(Pid, {item, Item}).
cost_so_far(Pid) ->
gen_server:call(Pid, cost).
finish(Pid) ->
gen_server:call(Pid, terminate).
%% Server functions
init([]) ->
{ok, []}.
handle_call({item, Item}, _From, Cart) ->
{reply, [Item|Cart], [Item|Cart]};
handle_call(terminate, _From, Cart) ->
{stop, normal, ok, Cart};
handle_call(cost, _From, Cart) ->
Total_Price = total_price(Cart),
{reply, Total_Price, Cart}.
handle_cast(_, _) ->
{noreply, ok}.
terminate(normal, Cart) ->
io:format("The total price was: ~p. ~n", [total_price(Cart)]),
ok.
%% Private functions
total_price(Cart) ->
Prices = lists:map(fun({_, Price}) -> Price end, Cart),
lists:sum(Prices).
================================================
FILE: otp/shopping_cart/src/shopping_cart.app.src
================================================
{application, shopping_cart,
[{description, "shopping_part"},
{vsn, "0.1.0"},
{registered, []},
{mod, { shopping_cart_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: otp/shopping_cart/src/shopping_cart.erl
================================================
-module(shopping_cart).
-behaviour(gen_server).
-export([start_link/0,
put_item/2,
finish/1,
cost_so_far/1,
]).
start_link() ->
put_your_solution_here.
put_item(Pid, Item) ->
put_your_solution_here.
cost_so_far(Pid) ->
put_your_solution_here.
finish(Pid) ->
put_your_solution_here.
================================================
FILE: otp/shopping_cart/test/shopping_cart_SUITE.erl
================================================
-module(shopping_cart_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(shopping_cart_test).
================================================
FILE: otp/shopping_cart/test/shopping_cart_test.erl
================================================
-module(shopping_cart_test).
-include_lib("eunit/include/eunit.hrl").
shopping_cart_add_item_test() ->
{ok, Pid} = shopping_cart:start_link(),
Orange = {orange, 3}, % tuple with name and price
Ans = shopping_cart:put_item(Pid, Orange),
?assertEqual(Ans, [{orange, 3}]).
shopping_cart_calculate_cost_test() ->
{ok, Pid} = shopping_cart:start_link(),
Apple = {apple, 2},
shopping_cart:put_item(Pid, Apple),
Cost = shopping_cart:cost_so_far(Pid),
?assertEqual(Cost, 2).
================================================
FILE: sequential/bank_accounts/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
.lock
================================================
FILE: sequential/bank_accounts/README.md
================================================
# Bank Accounts
## Reading Material
- [Learn You Some Erlang: Errors and Exceptions](http://learnyousomeerlang.com/errors-and-exceptions)
## Exercise
Create 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.
#### Operations
There are two kind of operations: `withdraw` and `deposit`.
#### Examples
``` erlang
1> bank_account:process_operation([{1, 100}, {2, 45}], {1, withdraw, 25}).
%% {1, 75}
2> process_operation([{1, 100}, {2, 45}], {2, deposit, 15}).
%% {2, 60}
```
#### Error Handling
* 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}`.
* If you try to withdraw from an account with insufficient funds, return a tuple `{error, insufficient_funds}`.
#### Examples
``` erlang
1> bank_account:process_operation([{1, 100}, {2, 45}], {7, deposit, 55}).
%% {error, account_not_found}
2> bank_account:process_operation([{1, 100}, {2, 45}], {2, withdraw, 100}).
%% {error, insufficient_funds}
```
Run tests with `$> make`.
As 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).
================================================
FILE: sequential/bank_accounts/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: sequential/bank_accounts/solution/bank_account.app.src
================================================
{application, bank_account,
[{description, "bank_account"},
{vsn, "0.1.0"},
{registered, []},
{mod, { bank_account_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/bank_accounts/solution/bank_account.erl
================================================
-module(bank_account).
-export([process_operation/2]).
operate({AccountNumber, Money}, withdraw, Amount) ->
case (Money - Amount) < 0 of
true -> {error, insufficient_funds};
false -> {AccountNumber, Money - Amount}
end;
operate({AccountNumber, Money}, deposit, Amount) when Amount >= 0 ->
{AccountNumber, Money + Amount}.
process_operation(Bank, {AccountNumber, Operation_Type, Amount}) ->
case lists:keyfind(AccountNumber, 1, Bank) of
false -> {error, account_not_found};
Account -> operate(Account, Operation_Type, Amount)
end.
================================================
FILE: sequential/bank_accounts/src/bank_account.app.src
================================================
{application, bank_account,
[{description, "bank_account"},
{vsn, "0.1.0"},
{registered, []},
{mod, { bank_account_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/bank_accounts/src/bank_account.erl
================================================
-module(bank_account).
-export([process_operation/2]).
process_operation(Bank, Operation) ->
put_your_solution_here.
================================================
FILE: sequential/bank_accounts/test/bank_account_SUITE.erl
================================================
-module(bank_account_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(bank_account_test).
================================================
FILE: sequential/bank_accounts/test/bank_account_test.erl
================================================
-module(bank_account_test).
-include_lib("eunit/include/eunit.hrl").
process_operation_withdraw_ok_test() ->
Bank = [{213, 150}, {214, 0}],
Op = {213, withdraw, 50},
Res = {213, 100},
?assertEqual(Res, bank_account:process_operation(Bank, Op)).
process_operation_not_found_test() ->
Bank = [{214, 0}],
Op = {213, deposit, 20},
Res = {error, account_not_found},
?assertEqual(Res, bank_account:process_operation(Bank, Op)).
process_operation_insufficient_funds_test() ->
Bank = [{213, 150}, {214, 0}],
Op = {213, withdraw, 200},
Res = {error, insufficient_funds},
?assertEqual(Res, bank_account:process_operation(Bank, Op)).
================================================
FILE: sequential/calculate_bmi/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
.lock
================================================
FILE: sequential/calculate_bmi/README.md
================================================
# Calculate BMI
## Reading Material
- [Learn You Some Erlang](http://learnyousomeerlang.com/a-short-visit-to-common-data-structures)
## Exercise
Write 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).
Then, write `calculate_bmi:classify/1` to classify a person according to her BMI:
* `underweight`: when the BMI is less than 18.5.
* `normal`: when the BMI is greater than 18.5 and less than 25.
* `overweight`: when the BMI is between 25 and 30.
* `obese`: when the BMI is greater than 30.
#### Examples
``` erlang
1> calculate_bmi:bmi(#person{name = "Maria", weight = 60, height = 1.6}).
%% 23.437499999999996
2> calculate_bmi:classify(#person{name = "Maria", weight = 60, height = 1.6}).
%% normal
```
Run tests with ``make``.
As a hint, the file you should be editing is `src/calculate_bmi.erl`. But in any
case if the things get difficult you can check our [proposed solution](solution/calculate_bmi.erl).
================================================
FILE: sequential/calculate_bmi/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: sequential/calculate_bmi/solution/calculate_bmi.app.src
================================================
{application, calculate_bmi,
[{description, "calculate_bmi"},
{vsn, "0.1.0"},
{registered, []},
{mod, { calculate_bmi_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/calculate_bmi/solution/calculate_bmi.erl
================================================
-module(calculate_bmi).
-export([bmi/1, classify/1]).
-include("../src/person_record.hrl").
bmi(P) when is_record(P, person) ->
P#person.weight / math:pow(P#person.height, 2).
classify(P) when is_record(P, person) ->
classify(bmi(P));
classify(BMI) when BMI > 25 andalso BMI < 30 ->
overweight;
classify(BMI) when BMI < 18.5 ->
underweight;
classify(BMI) when BMI > 30 ->
obese;
classify(BMI) when BMI > 18.5 andalso BMI < 25 ->
normal.
================================================
FILE: sequential/calculate_bmi/solution/person_record.hrl
================================================
% person record declaration:
% name
% weight specified in kg.
% height specified in metres.
-record(person, {name, weight, height}).
================================================
FILE: sequential/calculate_bmi/src/calculate_bmi.app.src
================================================
{application, calculate_bmi,
[{description, "calculate_bmi"},
{vsn, "0.1.0"},
{registered, []},
{mod, { calculate_bmi_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/calculate_bmi/src/calculate_bmi.erl
================================================
-module(calculate_bmi).
-export([bmi/1, classify/1]).
-include("../src/person_record.hrl").
bmi(Person) ->
put_your_solution_here.
classify(Person) ->
put_your_solution_here.
================================================
FILE: sequential/calculate_bmi/src/person_record.hrl
================================================
% person record declaration:
% name
% weight specified in kg.
% height specified in metres.
-record(person, {name, weight, height}).
================================================
FILE: sequential/calculate_bmi/test/calculate_bmi_SUITE.erl
================================================
-module(calculate_bmi_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(calculate_bmi_test).
================================================
FILE: sequential/calculate_bmi/test/calculate_bmi_test.erl
================================================
-module(calculate_bmi_test).
-include_lib("eunit/include/eunit.hrl").
-include("../src/person_record.hrl").
calculate_bmi_bmi_calculation_test() ->
Person = #person{ name = "Alicia", weight = 62, height = 1.63},
?assertEqual(23.335466144755166, calculate_bmi:bmi(Person)).
calculate_bmi_classify_test() ->
Person1 = #person{ name = "Alicia", weight = 62, height = 1.63},
Person2 = #person{ name = "Peter", weight = 104, height = 1.8},
Person3 = #person{ name = "Ana", weight = 52, height = 1.7},
Person4 = #person{ name = "Charles", weight = 84, height = 1.75},
?assertEqual(normal, calculate_bmi:classify(Person1)),
?assertEqual(obese, calculate_bmi:classify(Person2)),
?assertEqual(underweight, calculate_bmi:classify(Person3)),
?assertEqual(overweight, calculate_bmi:classify(Person4)).
================================================
FILE: sequential/filter_fibonacci_numbers/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
.lock
================================================
FILE: sequential/filter_fibonacci_numbers/README.md
================================================
# Filter Fibonacci Numbers
## Reading material
- [Fibonnaci number](https://en.wikipedia.org/wiki/Fibonacci_number)
## Exercise
Create a function `filter_fibonacci_numbers:filter/1` that takes a list and filter every Fibonnaci number using **list comprehensions**.
Tip: The nth Fibonnaci number, *Fn*, can be calculated by the sum of the two preceding ones.
Using F0 = 1 and F1 = 1 as initial values we have:
1, 1, 2, 3, 5, 8, 13, 21, 54, ...
Example:
``` erlang
1> filter_fibonacci_numbers:filter([1, 2, 3, 4, 5, 7, 8]).
%% [1, 2, 3, 5, 8]
```
Write your answer in `src/filter_fibonacci_numbers.erl`. You can check your answer by doing `$> make` inside this directory.
If things gets difficult you can check our [proposed solution](solution/filter_fibonacci_numbers.erl).
================================================
FILE: sequential/filter_fibonacci_numbers/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: sequential/filter_fibonacci_numbers/solution/filter_fibonacci_numbers.app.src
================================================
{application, filter_fibonacci_numbers,
[{description, "filter_fibonacci_numbers"},
{vsn, "0.1.0"},
{registered, []},
{mod, { filter_fibonacci_numbers_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/filter_fibonacci_numbers/solution/filter_fibonacci_numbers.erl
================================================
-module(filter_fibonacci_numbers).
-export([filter/1]).
fibonacci(1) ->
1;
fibonacci(2) ->
2;
fibonacci(N) when N > 2 ->
fibonacci(N-1) + fibonacci(N-2).
is_fibonacci_number(X) ->
is_fibonacci_number(X, []).
is_fibonacci_number(X, []) ->
F1 = fibonacci(1),
case X =:= F1 of
true ->
true;
false ->
is_fibonacci_number(X, [fibonacci(1)])
end;
is_fibonacci_number(X, Fibs) ->
Fn = fibonacci(length(Fibs)),
case X =:= Fn of
true ->
true;
false when X > Fn ->
is_fibonacci_number(X, [Fn|Fibs]);
_ -> false
end.
filter(L) ->
[X || X <- L, is_fibonacci_number(X)].
================================================
FILE: sequential/filter_fibonacci_numbers/src/filter_fibonacci_numbers.app.src
================================================
{application, filter_fibonacci_numbers,
[{description, "filter_fibonacci_numbers"},
{vsn, "0.1.0"},
{registered, []},
{mod, { filter_fibonacci_numbers_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/filter_fibonacci_numbers/src/filter_fibonacci_numbers.erl
================================================
-module(filter_fibonacci_numbers).
-export([filter/1]).
filter(List) ->
put_your_solution_here.
================================================
FILE: sequential/filter_fibonacci_numbers/test/filter_fibonacci_numbers_SUITE.erl
================================================
-module(filter_fibonacci_numbers_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(filter_fibonacci_numbers_test).
================================================
FILE: sequential/filter_fibonacci_numbers/test/filter_fibonacci_numbers_test.erl
================================================
-module(filter_fibonacci_numbers_test).
-include_lib("eunit/include/eunit.hrl").
filter_fibonacci_numbers_test() ->
List = [1, 2, 3, 4, 5, 7, 8, 9, 10],
?assertEqual([1, 2, 3, 5, 8], filter_fibonacci_numbers:filter(List)).
================================================
FILE: sequential/filter_numbers/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
.lock
================================================
FILE: sequential/filter_numbers/README.md
================================================
# Filter Numbers
## Reading material
- [Learn You Some Erlang: Starting Out (for real)](http://learnyousomeerlang.com/starting-out-for-real)
- How to make a list of numbers: [lists:seq/2](http://erlang.org/doc/man/lists.html#seq-2)
## Exercises
### Filter in
Create 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.
```erlang
1> filter_numbers:filter_in(1,100, 7).
[7,14,21,28,35,42,49,56,63,70,77,84,91,98]
```
### Filter out
Create 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.
```erlang
1> filter_numbers:filter_out(1,10, 7).
[1,2,3,4,5,6,8,9,10]
```
### Filter in values
Create 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.
```erlang
1> filter_numbers:filter_in_values(1,100,{25,38}).
[25,26,27,28,29,30,31,32,33,34,35,36,37,38]
```
## Testing your solution
To 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.
## Our solution
You can check our [proposed solution](solution/filter_numbers.erl).
================================================
FILE: sequential/filter_numbers/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: sequential/filter_numbers/solution/filter_numbers.app.src
================================================
{application, filter_numbers,
[{description, "filter_numbers"},
{vsn, "0.1.0"},
{registered, []},
{mod, { filter_numbers_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/filter_numbers/solution/filter_numbers.erl
================================================
-module(filter_numbers).
-export([filter_in/3,
filter_out/3,
filter_in_values/3]).
filter_in(From, To, N) ->
[X || X <- lists:seq(From, To), X rem N =:= 0].
filter_out(From, To, N) ->
[X || X <- lists:seq(From, To), X rem N =/= 0].
filter_in_values(From, To, {Min, Max}) ->
[X || X <- lists:seq(From, To), X >= Min, X =< Max].
================================================
FILE: sequential/filter_numbers/src/filter_numbers.app.src
================================================
{application, filter_numbers,
[{description, "filter_numbers"},
{vsn, "0.1.0"},
{registered, []},
{mod, { filter_numbers_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/filter_numbers/src/filter_numbers.erl
================================================
-module(filter_numbers).
-export([filter_in/3,
filter_out/3,
filter_in_values/3]).
filter_in(From, To, N) ->
put_your_solution_here.
filter_out(From, To, N) ->
put_your_solution_here.
filter_in_values(From, To, {Min, Max}) ->
put_your_solution_here.
================================================
FILE: sequential/filter_numbers/test/filter_numbers_SUITE.erl
================================================
-module(filter_numbers_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(filter_numbers_test).
================================================
FILE: sequential/filter_numbers/test/filter_numbers_test.erl
================================================
-module(filter_numbers_test).
-include_lib("eunit/include/eunit.hrl").
filter_numbers_in_test() ->
List = [7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98],
?assertEqual(List, filter_numbers:filter_in(1, 100, 7)).
filter_numbers_out_test() ->
List = [1, 2, 3, 4, 5, 6, 8, 9, 10],
?assertEqual(List, filter_numbers:filter_out(1, 10, 7)).
filter_numbers_in_values_test() ->
List = [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38],
?assertEqual(List, filter_numbers:filter_in_values(1, 100, {25, 38})).
================================================
FILE: sequential/hello/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
================================================
FILE: sequential/hello/README.md
================================================
# Hello world
## Reading Material
- [Learn You Some Erlang: Modules](http://learnyousomeerlang.com/modules)
## Exercise
This is the most iconic exercise in coding history, we should include
this one. Your only task is to create a function `hello:hello/0` that
returns the binary ``<<"hello world">>`` (don't worry about understanding binaries, yet). To test that is working correctly check it running `make`.
As a hint, the file you should be editing is `src/hello.erl`. But in any
case if the things get difficult you can check our
[proposal solution](solution/hello.erl).
================================================
FILE: sequential/hello/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: sequential/hello/solution/hello.app.src
================================================
{application, hello,
[{description, "hello"},
{vsn, "0.1.0"},
{registered, []},
{mod, { hello_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/hello/solution/hello.erl
================================================
-module(hello).
-export([hello/0]).
hello() ->
<<"hello world">>.
================================================
FILE: sequential/hello/src/hello.app.src
================================================
{application, hello,
[{description, "hello"},
{vsn, "0.1.0"},
{registered, []},
{mod, { hello_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/hello/src/hello.erl
================================================
-module(write_your_module_here).
-export([function_you_want_to_export/0]).
hello() ->
return_your_binary.
================================================
FILE: sequential/hello/test/hello_SUITE.erl
================================================
-module(hello_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(hello_test).
================================================
FILE: sequential/hello/test/hello_test.erl
================================================
-module(hello_test).
-include_lib("eunit/include/eunit.hrl").
hello_world_test() ->
<<"hello world">> = hello:hello().
================================================
FILE: sequential/hello_pattern/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
================================================
FILE: sequential/hello_pattern/README.md
================================================
# Hello Pattern
## Reading Material
- [Learn You Some Erlang: Syntax in functions](http://learnyousomeerlang.com/syntax-in-functions)
## Exercise
In this exercise we will introduce you to pattern matching and guards. Write a function `hello_pattern:hello/1` which takes a tuple:
- `{morning, Name}`, ignores the name and returns `morning`.
- `{evening, Name}`, returns a tuple `{good, evening, Name}`.
- `{night, Name}`, ignores the name and return `night`.
- `{math_class, Number, Name}`. If the number is lower than zero, return
`none`, in any other case return `{math_class, Name}`.
Resolve this exercise without using `if`, `case`. You should use pattern matching and guard only.
Check 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).
================================================
FILE: sequential/hello_pattern/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: sequential/hello_pattern/solution/hello_pattern.app.src
================================================
{application, hello_pattern,
[{description, "hello_pattern"},
{vsn, "0.1.0"},
{registered, []},
{mod, { hello_pattern_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/hello_pattern/solution/hello_pattern.erl
================================================
-module(hello_pattern).
-export([hello/1]).
hello({morning, _Name}) ->
morning;
hello({evening, Name}) ->
{good, evening, Name};
hello({night, _Name}) ->
night;
hello({math_class, Number, _Name}) when Number < 0 ->
none;
hello({math_class, _Number, Name}) ->
{math_class, Name}.
================================================
FILE: sequential/hello_pattern/src/hello_pattern.app.src
================================================
{application, hello_pattern,
[{description, "hello_pattern"},
{vsn, "0.1.0"},
{registered, []},
{mod, { hello_pattern_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/hello_pattern/src/hello_pattern.erl
================================================
-module(hello_pattern).
-export([hello/1]).
hello({tuple_element_1, tuple_element_2}) ->
resolve.
================================================
FILE: sequential/hello_pattern/test/hello_pattern_SUITE.erl
================================================
-module(hello_pattern_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(hello_pattern_test).
================================================
FILE: sequential/hello_pattern/test/hello_pattern_test.erl
================================================
-module(hello_pattern_test).
-include_lib("eunit/include/eunit.hrl").
hello_day_test() ->
morning =
hello_pattern:hello({morning, "Jimmy"}),
{good, evening, "Rick"} =
hello_pattern:hello({evening, "Rick"}),
night =
hello_pattern:hello({night, "Morty"}).
hello_math_class_test() ->
none =
hello_pattern:hello({math_class, -100, "K-Colored"}),
{math_class, "Analysis II"} =
hello_pattern:hello({math_class, 10, "Analysis II"}).
================================================
FILE: sequential/insert_element_at/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
================================================
FILE: sequential/insert_element_at/README.md
================================================
# Insert element at position
## Reading Material
- [Learn You Some Erlang: A Short Visit to Common Data Structures](http://learnyousomeerlang.com/a-short-visit-to-common-data-structures)
- [Learn You Some Erlang: Maps](http://learnyousomeerlang.com/maps#what-maps-shall-be)
## Exercise
Since 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
element with another one, but at the same time remember the old one.
For example if we have `[1,2,3]` and we replace the second element with
`"hi"`, it should be `[1, #{current => "hi", old => 2}]`. And if we
call that function with `[1, #{current => 10, old => 0}, 3]` replacing the
second element with "hi" the result should be `[1, #{current => "hi", old => 10}, 3]`.
The function should be `insert_element_at:insert/3`. A sample call of the
previous example could be `insert([1,2,3], 2, "hi")`. If you are stuck or
want to compare check [our solution](solution/insert_element_at.erl).
================================================
FILE: sequential/insert_element_at/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: sequential/insert_element_at/solution/insert_element_at.app.src
================================================
{application, insert_element_at,
[{description, "insert_element_at"},
{vsn, "0.1.0"},
{registered, []},
{mod, { insert_element_at_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/insert_element_at/solution/insert_element_at.erl
================================================
-module(insert_element_at).
-export([insert/3]).
insert([H|T], Pos, Element) ->
insert(Pos, Element, H, [], T).
insert(1, NewElement, CurrentElement, Pre, Post) ->
Pre ++ [replace(CurrentElement, NewElement)] ++ Post;
insert(Pos, NewElement, CurrentElement, Pre, [PostH|PostT]) ->
insert(Pos - 1, NewElement, PostH, Pre ++ [CurrentElement], PostT).
replace(#{current := Old, old := _}, NewElement) ->
#{current => NewElement, old => Old};
replace(Element, NewElement) ->
#{current => NewElement, old => Element}.
================================================
FILE: sequential/insert_element_at/src/insert_element_at.app.src
================================================
{application, insert_element_at,
[{description, "insert_element_at"},
{vsn, "0.1.0"},
{registered, []},
{mod, { insert_element_at_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/insert_element_at/src/insert_element_at.erl
================================================
-module(insert_element_at).
-export([insert/3]).
insert(_List, _Pos, _Element) ->
believe_in_yourself.
================================================
FILE: sequential/insert_element_at/test/insert_element_at_SUITE.erl
================================================
-module(insert_element_at_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(insert_element_at_test).
================================================
FILE: sequential/insert_element_at/test/insert_element_at_test.erl
================================================
-module(insert_element_at_test).
-include_lib("eunit/include/eunit.hrl").
insertion_test() ->
[1, #{current := "hi", old := 2}, 3] =
insert_element_at:insert([1, 2, 3], 2, "hi"),
[1, #{current := "hi", old := 10}, 3] =
insert_element_at:insert([1, #{current => 10, old => 2}, 3], 2, "hi").
================================================
FILE: sequential/installing/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
================================================
FILE: sequential/installing/Makefile
================================================
.PHONY: test
test:
rebar3 ct
================================================
FILE: sequential/installing/README.md
================================================
# Installing
## Reading Material
- [Learn You Some Erlang: Introduction](http://learnyousomeerlang.com/introduction)
## Requirements
In this exercise we will setup our environment before we start our
real coding. We will need some software:
- Erlang 27.
- Make (probably already installed on your system).
- Rebar.
We suggest 2 ways to install Erlang + Rebar: asdf or nix.
### ASDF
First, we need to install ASDF:
```sh
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.14.1
```
Then we'll have to add this to your bash or zsh config file:
```sh
. "$HOME/.asdf/asdf.sh"
```
Or, if you're using fish:
```fish
source ~/.asdf/asdf.fish
```
Be sure to check asdf's website, since there are some extra goodies to config
like shell completions.
```sh
asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git
```
And finally, install erlang 27.0.1:
```sh
asdf install erlang 27.0.1 && asdf global erlang 27.0.1
```
The global command is just to tell your environment to always use erlang 27, you
can use `asdf local` instead to have distinct versions base on which folder you're currently in.
### Nix
Clone the repo:
```sh
git clone https://github.com/lambdaclass/erlings.git
```
cd into this folder and eval the nix flake:
```sh
cd erlings/sequential/install && nix develop
```
This will drop you into a bare-bones bash shell with erlang and rebar installed,
which you can use for the exercises.
## Checking environment
To check your environment do the following:
~~~
$> git clone https://github.com/lambdaclass/erlings.git
$> cd ~/erlings/sequential/installing
$> make
~~~
You should get the following output:
~~~
rebar3 ct
===> Verifying dependencies...
===> Analyzing applications...
===> Compiling installing
===> Running Common Test suites...
%%% installing_SUITE: .
All 1 tests passed.
~~~
================================================
FILE: sequential/installing/flake.nix
================================================
{
description = "A flake to download Erlang 27 and Rebar3 from Nixpkgs 24.05";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/24.05";
flake-utils.url = "github:numtide/flake-utils/main";
};
outputs = { self, nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.default = pkgs.mkShell {
buildInputs = [
pkgs.erlang_27
pkgs.rebar3
];
};
});
}
================================================
FILE: sequential/installing/src/installing.app.src
================================================
{application, installing,
[{description, "installing"},
{vsn, "0.1.0"},
{registered, []},
{mod, { installing_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/installing/src/installing.erl
================================================
-module(installing).
-export([installed/0]).
installed() ->
'i can not wait for more code'.
================================================
FILE: sequential/installing/test/installing_SUITE.erl
================================================
-module(installing_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(installing_test).
================================================
FILE: sequential/installing/test/installing_test.erl
================================================
-module(installing_test).
-include_lib("eunit/include/eunit.hrl").
installing_test() ->
?assertEqual('i can not wait for more code', installing:installed()).
================================================
FILE: sequential/lists/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
.lock
================================================
FILE: sequential/lists/README.md
================================================
# Lists
## Reading Material
Recursion 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.
- [Recursion theory](https://en.wikipedia.org/wiki/Recursion_(computer_science))
- [Learn You Some Erlang: Recursion](http://learnyousomeerlang.com/recursion)
- [Learn You Some Erlang: Higher Order Functions](http://learnyousomeerlang.com/higher-order-functions)
## Exercises
### Reverse
This 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.
Example:
``` erlang
1> lists_exercises:reverse([1, 2, 3, 4]).
%% [4, 3, 2, 1]
```
[solution](solution/lists_exercises.erl#L14-L17)
### Remove consecutive
Create a function `lists_exercises:rmconsecutive/1` that takes a list and returns another one but without any consecutive repetitions.
Example:
``` erlang
1> lists_exercises:rmconsecutive([1,1,1,2,3,4,1,1,1,3]).
%% [1, 2, 3, 4, 1, 3]
```
[solution](solution/lists_exercises.erl#L21-L31)
### Even Fibonacci Numbers
Each 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:
` 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...`
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
[solution](solution/lists_exercises.erl#L35-L60)
### Reduce
Implement `lists_exercises:foldl/3` and `lists_exercises:foldl/2` using recursion (see [Erlang foldl reference](http://erlang.org/doc/man/lists.html#foldl-3)).
Example:
``` erlang
1> lists_exercises:foldl(fun(X, Y) -> X + Y end, 0, [1, 2, 3, 4]).
%% 10
2> lists_exercises:foldl(fun(X, Y) -> X * Y end, [1, 2, 3]).
%% 6
3> lists_exercises:foldl(func(X, Y) -> X andalso Y, [true, false, true]).
%% false
```
[solution](solution/lists_exercises.erl#L65-L73)
### Rotate Lists
Create a function `lists_exercises:rotate/2` that rotates the contents of a list `n` positions. It should take 2 arguments:
- A list
- A tuple of `{left, N}` or `{right, N}` that indicates the direction and the size of the displacement expected.
Example:
``` erlang
1> lists_exercises:rotate([1, 2, 3, 4, 5], {right, 2}).
%% [4,5,1,2,3]
```
[solution](solution/lists_exercises.erl#L77-L86)
### Run-length encoding of a list
Implement 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.
Example:
``` erlang
1> lists_exercises:run_length_encode([a,a,a,a,b,c,c,a,a,d,e,e,e,e]).
%%[{4,a},{1,b},{2,c},{2,a},{1,d},{4,e}]
```
[solution](solution/lists_exercises.erl#L90-L103)
### Any
Write 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.
Note: Implement it without using `lists:list_any`.
Example:
``` erlang
1> lists_exercises:list_any(fun(X) -> X > 3 end, [4, 2, 0]).
%% true
2> lists_exercises:list_any(fun(X) -> X == 0 end, [1, 2, 3]).
%% false
```
[solution](solution/lists_exercises.erl#L107-L108)
### Anagram
Write 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"
Example:
```erlang
1>lists_exercises:anagram(["god","cat","dog"], "dog").
%%["god"]
```
[solution](solution/lists:exercises.erl#L111-L131)
### First letter last letter game
There 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.
Write 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.
Example:
```erlang
1> lists_exercises:last_letter(["turnIp","Potato","peas","OniOn","yam","letuCe","broccoli",
"asparaguS","Artichoke"]).
%%["turnIp","Potato","OniOn"]
2> lists_exercises:last_letter([])
%% []
```
[solution](solution/lists:exercises.erl#L117-L141)
================================================
FILE: sequential/lists/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: sequential/lists/solution/lists_exercises.app.src
================================================
{application, lists_exercises,
[{description, "lists_exercises"},
{vsn, "0.1.0"},
{registered, []},
{mod, { lists_exercises_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/lists/solution/lists_exercises.erl
================================================
-module(lists_exercises).
-export([reverse/1,
rmconsecutive/1,
even_fib_numbers/0,
foldl/2,
foldl/3,
rotate/2,
run_length_encode/1,
list_any/2,
anagram/2,
last_letter/1]).
% Reverse
reverse(List) ->
reverse(List, []).
reverse([], Acc) ->
Acc;
reverse([H|T], Acc) ->
reverse(T, [H|Acc]).
% Remove Consecutive
rmconsecutive([]) ->
[];
rmconsecutive([H|T]) ->
rmconsecutive(H, T).
rmconsecutive(E, []) ->
[E];
rmconsecutive(E, [E|T]) ->
rmconsecutive(E,T);
rmconsecutive(E, [H|T]) ->
[E| rmconsecutive(H,T)].
% Even Fibonacci Numbers
fib(1) ->
1;
fib(2) ->
2;
fib(N) when N > 2 ->
fib(N-1) + fib(N-2).
% list of every fibonacci number less than N.
fibs_less_than(N) when N < 1->
[];
fibs_less_than(N) ->
fibs_less_than(N, 1, []).
fibs_less_than(N, M, AccList) ->
FibM = fib(M),
case FibM < N of
true ->
fibs_less_than(N, M + 1, [ FibM | AccList]);
false ->
AccList
end.
even_fib_numbers() ->
Less_than_4mill = fibs_less_than(4000000),
Even_fibs = lists:filter(fun(X) -> X rem 2 == 0 end, Less_than_4mill),
lists:sum(Even_fibs).
% Reduce
% with accumulator
foldl(_, Acc, []) ->
Acc;
foldl(Fun, Acc, [A | T]) ->
NewAcc = Fun(A,Acc),
foldl(Fun,NewAcc,T).
% w/o accumulator
foldl(_, List) when length(List) < 2 ->
undefined;
foldl(Fun, [A, B | T]) ->
foldl(Fun, A, [B | T]).
% Rotate Lists
rotate([], _) ->
[];
rotate(L, {Dir, N}) ->
case Dir of
left ->
{Right, Left} = lists:split(N, L);
right ->
{Right, Left} = lists:split(length(L) - N, L)
end,
lists:append(Left, Right).
% Run-length encoding of a list
run_length_encode([], Acc) -> reverse(Acc);
run_length_encode([H|T], [{Count, H}|AccT]) ->
run_length_encode(T, [{Count + 1, H}|AccT]);
run_length_encode([H|T], Acc) ->
run_length_encode(T, [{1, H}] ++ Acc).
run_length_encode(L) -> run_length_encode(L, []).
% Any
list_any(F, List) ->
lists:foldl(fun(X, Y) -> F(X) or Y end, false, List).
%Anagram
anagram(List, S) ->
LowerS = string:lowercase(S),
SortedS = lists:sort(LowerS),
lists:filter(fun (X) ->
LowerH = string:lowercase(X),
LowerH =/= LowerS andalso lists:sort(LowerH) =:= SortedS
end, List).
%Last Letter game
last_letter([]) -> [];
last_letter([H|T]) -> last_letter(T, [H]).
last_letter([], Acc) ->
lists:reverse(Acc);
last_letter(List, [H|T]) ->
Letter = lists:nthtail(length(H)-1,H),
Word = find_word(Letter, List),
case Word of
[] ->
last_letter([], [H|T]);
_ ->
NewWord = [Word, H|T],
NewList = lists:delete(Word, List),
last_letter(NewList, NewWord)
end.
find_word(_L,[]) -> [];
find_word(L,[H|T]) ->
Letter = string:lowercase(L),
[A|_Rest] = string:lowercase(H),
case [A] == Letter of
true ->
H;
false ->
find_word(L,T)
end.
================================================
FILE: sequential/lists/src/lists_exercises.app.src
================================================
{application, lists_exercises,
[{description, "lists_exercises"},
{vsn, "0.1.0"},
{registered, []},
{mod, { lists_exercises_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/lists/src/lists_exercises.erl
================================================
-module(lists_exercises).
-export([reverse/1,
rmconsecutive/1,
even_fib_numbers/0,
foldl/3,
foldl/2,
rotate/2,
run_length_encode/1,
list_any/1,
anagram/2,
last_letter/1]).
reverse(List) ->
put_your_solution_here.
rmconsecutive(List) ->
put_your_solution_here.
even_fib_numbers() ->
put_your_solution_here.
foldl(Fun, Acc, List) ->
put_your_solution_here.
foldl(Fun, List) ->
put_your_solution_here.
rotate(List, Tuple) ->
put_your_solution_here.
run_length_encode(List) ->
put_your_solution_here.
list_any(N) ->
put_your_solution_here.
anagram(List, S) ->
put_your_solution_here.
last_letter(List) ->
put_your_solution_here.
================================================
FILE: sequential/lists/test/lists_exercises_SUITE.erl
================================================
-module(lists_exercises_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(lists_exercises_test).
================================================
FILE: sequential/lists/test/lists_exercises_test.erl
================================================
-module(lists_exercises_test).
-include_lib("eunit/include/eunit.hrl").
all_the_same_test() ->
?assertEqual([1, 1, 1, 1], lists_exercises:reverse([1, 1, 1, 1])).
all_different_test() ->
?assertEqual([5, 4, 3, 2, 1], lists_exercises:reverse([1, 2, 3, 4, 5])).
rmconsecutive_test() ->
List = [1, 1, 1, 2, 3, 4, 1, 1, 1, 3],
?assertEqual([1,2,3,4,1,3], lists_exercises:rmconsecutive(List)).
even_fib_numbers_test() ->
?assertEqual(4613732, lists_exercises:even_fib_numbers()).
foldl_multiply_test() ->
Multiply = fun (A, B) -> A * B end,
?assertEqual(300, lists_exercises:foldl(Multiply, 10, [2, 5, 3])).
foldl_add_without_acc_test() ->
Add = fun (A, B) -> A + B end,
?assertEqual(100, lists_exercises:foldl(Add, [20, 20, 10, 50])).
rotate_list_right_test() ->
Res = [4, 5, 1, 2, 3],
?assertEqual(Res, lists_exercises:rotate([1, 2, 3, 4, 5], {right, 2})).
rotate_list_left_test() ->
Res = [3, 4, 5, 1, 2],
?assertEqual(Res, lists_exercises:rotate([1, 2, 3, 4, 5], {left, 2})).
run_length_encoding_test() ->
List = [a, a, a, a, b, c, c, a, a, d, e, e, e, e],
Res = [{4, a}, {1, b}, {2, c}, {2, a}, {1, d}, {4, e}],
?assertEqual(Res, lists_exercises:run_length_encode(List)).
any_is_even_test() ->
List = [2, 3, 4, 1, 5, 6, 9],
?assert(lists_exercises:list_any(fun(X) -> X rem 2 == 0 end, List)).
any_empty_test() ->
?assertNot(lists_exercises:list_any(fun(X) -> X == 20 end, [])).
anagram_test() ->
List = ["Panel", "plane", "Penal", "PlenA", "Nepal", "ArgentinA", "Laos"],
String = "Nepal",
Res = ["Panel", "plane", "Penal", "PlenA"],
?assertEqual(Res, lists_exercises:anagram(List,String)).
last_letter_test()->
List = ["Afghanistan", "Albania", "Algeria", "Andorra", "Nigeria", "Norway",
"Yemen", "Nepal", "Morocco", "Oman", "Portugal", "Spain"],
Res = ["Afghanistan", "Nigeria", "Albania", "Algeria", "Andorra"],
?assertEqual(Res, lists_exercises:last_letter(List)).
================================================
FILE: sequential/maps/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
.lock
================================================
FILE: sequential/maps/README.md
================================================
# Maps
## Reading Material
- [Learn You Some Erlang: Maps](http://learnyousomeerlang.com/maps)
## Exercises
### Sum of Values
Write a function `maps_exercises:sum_of_values/1` that takes a map a returns a sum of all values in the map.
Note: the function maps:fold/3 can be useful.
Example:
``` erlang
1> maps_exercises:sum_of_values(#{a => 1, b =>3, c =>4}).
%%8
2> maps_exercises:sum_of_values(#{one => 1, three=> 3, seven => 7}).
%% 11
```
### Min Value
Write a function `maps_exercises:min_value(Map)` that returns the minimum value of that Map.
Example:
```erlang
1>maps_exercises:min_value(#{a => 1, b =>3, c =>4}).
%%1
2>maps_exercises:min_value(#{five => 5, three => 3, seven => 7, ten =>10}).
%%3
```
### Sort keys
Write 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.
Note: Remember you can use maps:keys/1 that returns a list with all the keys in a map.
Example:
``` erlang
1>maps_exercises:sort_by_keys(#{1=> one, 2 => two, 5 => five, 10 => ten, 3 => three, 15 => fifteen}).
%% #{1 => one,2 => two,3 => three,5 => five,10 => ten, 15 => fifteen}
2> maps:exercises:sort_by_keys(#{"one" => 1,"two" => 2, "three" => 3, "five" => 5, "four"=>4}).
#{"five" => 5,"four" => 4,"one" => 1,"three" => 3,"two" => 2}
```
### Return values
Write a function `maps_exercises:return_values/1` that takes a map a returns a list with all the values in that map
Example:
``` erlang
1> maps_exercises:return_values(#{"one" => 1,"two" => 2, "three" =>3 , "five" => 5, "four" => 4}).
%% [1,2,3,5,4]
2> maps_exercises:return_values(#{1 => one, 2 => two, 5 => five, 10 => ten, 3 => three, 15 => fifteen})
%%[one,two,three,five,ten,fifteen]
```
### Merge Map
Write a function `maps_exercises:merge/2` that merges 2 maps, if they have a key in common, keep the value from the second map.
Note: the function `maps:fold/3` can be useful.
Example:
``` erlang
1> maps_exercises:merge(#{}, #{a => 1, b => 2}).
%% #{a => 1, b => 2}
2> maps_exercises:merge(#{a => 1, b => 2}, #{a => 5, c => 3}).
%% #{a => 5, b => 2, c => 3}
```
[solution](src/solution/maps_exercises.erl#L6-L7).
### Mapping a Map
Write a function `maps_exercises:map/2` for mapping a function over the values of a Map without using `maps:map/2`.
Example:
``` erlang
1> maps_exercises:map(fun(X) -> X + 1 end, #{a => 4, b => 2}).
%% #{a => 5, b => 3}
2> maps_exercises:map(#{}).
%% #{}
```
[solution](src/solution/maps_exercises.erl#L10-L13).
### List to Map
Create a function `maps_exercises:to_map/1` that converts a list to a [Map](http://learnyousomeerlang.com/maps) without using `maps:from_list`.
Example:
``` erlang
1> maps_exercises:to_map([2, 1, 6, 4]).
%% #{1 => 2, 2 => 1, 3 => 6, 4 => 4}
2> maps_exercises:to_map([]).
%% #{}
```
[solution](src/solution/maps_exercises.erl#L16-L24).
### Records to Maps
Create 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`.
Example:
```erlang
1> maps_exercises:records_to_maps([]).
%% []
2> maps_exercises:records_to_maps([#person{name="Pepe", age=28}, #person{name="Luis", age=77}]).
%% [#{age => 28,name => "Pepe"},#{age => 77,name => "Luis"}]
```
[solution](src/solution/maps_exercises.erl#L27-L30).
### Maps to Records
Create 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.
Example:
```erlang
1> maps_exercises:maps_to_records([]).
%% []
2> maps_exercises:maps_to_records([#{age => 28,name => "Pepe"},#{age => 77,name => "Luis"}]).
%% [#person{name = "Luis",age = 77}, #person{name = "Pepe",age = 28}]
```
[solution](src/solution/maps_exercises.erl#L33-L38).
### Proplist to Map
**DISCLAIMER** Erlang provides functions for the following task: `maps:from_list` and `maps:to_list`, but we want you to implement it by hand.
Write 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.
Example:
```erlang
1> maps_exercises:proplist_to_map([]).
%% #{}
2> maps_exercises:proplist_to_map([{firstname, "Pedro"}, {lastname, "Sanches"}, {age, 11}]).
%% #{age => 11,firstname => "Pedro",lastname => "Sanches"}
```
[solution](src/solution/maps_exercises.erl#L41-L44).
================================================
FILE: sequential/maps/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: sequential/maps/solution/maps_exercises.app.src
================================================
{application, maps_exercises,
[{description, "maps_exercises"},
{vsn, "0.1.0"},
{registered, []},
{mod, { maps_exercises_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/maps/solution/maps_exercises.erl
================================================
-module(maps_exercises).
-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]).
-record(person, {name, age}).
%% Return the sum of all values in a list
sum_of_values(Map) ->
maps:fold(fun(_K,V,AccIn) -> AccIn + V end, 0, Map).
%% Min value
min_value(Map) ->
Keys = maps:keys(Map),
min_value(Keys, Map, []).
min_value([], _Map, Acc)-> lists:min(Acc);
min_value([H|T], Map, Acc) ->
Element = maps:find(H, Map),
{_Atom, Value} = Element,
min_value(T, Map, [Value|Acc]).
%% Order Map
sort_by_keys(Map)->
List = maps:keys(Map),
OrderedL = lists:sort(List),
build_ordered_map(OrderedL, Map, []).
build_ordered_map([], _Map, Acc)->
NewMap = lists:reverse(Acc),
maps:from_list(NewMap);
build_ordered_map([H|T], Map, Acc) ->
Value = maps:get(H, Map),
Tuple = {H, Value},
build_ordered_map(T, Map, [Tuple|Acc]).
%% Values to list
return_values(Map) ->
List = maps:keys(Map),
get_values(List, Map, []).
get_values([], _Map, Acc) ->
lists:reverse(Acc);
get_values([H|T], Map, Acc)->
Value = maps:get(H, Map),
get_values(T, Map, [Value|Acc]).
%% Merge map
merge(Map1, Map2) ->
maps:fold(fun maps:put/3, Map1, Map2).
%% Mapping a map
map(F, Map) ->
List_Of_Tuples = maps:to_list(Map),
Mapped_List = lists:keymap(F, 2, List_Of_Tuples),
maps:from_list(Mapped_List).
%% List to map
to_map([]) ->
#{};
to_map([H|T]) ->
to_map(2, #{1 => H}, T).
to_map(_, Map, []) ->
Map;
to_map(N, Map, [H|T]) ->
to_map(N+1, maps:put(N, H, Map), T).
%% Records to maps
records_to_maps(Records) ->
lists:map(fun(#person{name = Name, age = Age}) ->
#{name => Name, age => Age} end,
Records).
%% Maps to records
maps_to_records(Maps) ->
maps_to_records([], Maps).
maps_to_records(Acc, []) ->
Acc;
maps_to_records(Acc, [#{name := Name, age := Age} | Tail]) ->
maps_to_records([#person{name = Name, age = Age} | Acc], Tail).
%% Proplist to map
proplist_to_map(Proplist) ->
lists:foldl(fun({K, V}, Map) ->
Map#{K => V} end,
#{}, Proplist).
================================================
FILE: sequential/maps/src/maps_exercises.app.src
================================================
{application, maps_exercises,
[{description, "maps_exercises"},
{vsn, "0.1.0"},
{registered, []},
{mod, { maps_exercises_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{maintainers, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/maps/src/maps_exercises.erl
================================================
-module(maps_exercises).
-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]).
sum_of_values(Map) ->
put_your_solution_here.
min_value(Map)->
put_your_solution_here.
sort_by_keys(Map)->
put_your_solution_here.
return_values(Map)->
put_your_solution_here.
merge(M1, M2) ->
put_your_solution_here.
map(Function, Map) ->
put_your_solution_here.
to_map(List) ->
put_your_solution_here.
records_to_maps(Records) ->
put_your_solution_here.
maps_to_records(Maps) ->
put_your_solution_here.
proplist_to_map(Proplist) ->
put_your_solution_here.
================================================
FILE: sequential/maps/test/maps_exercises_SUITE.erl
================================================
-module(maps_exercises_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(maps_exercises_test).
================================================
FILE: sequential/maps/test/maps_exercises_test.erl
================================================
-module(maps_exercises_test).
-record(person, {name, age}).
-include_lib("eunit/include/eunit.hrl").
sum_of_values_test()->
Map = #{a=>1, b=> 5, c => 6, d => 5},
?assertEqual(17, maps_exercises:sum_of_values(Map)).
min_value_test()->
Map = #{a=> 5, b=> 3, c=> 4, d=>2},
?assertEqual(2, maps_exercises:min_value(Map)).
sort_by_keys_test()->
Map = #{1 => a, 5=> c, 3=> d, 7=> f},
OrderedM = #{1=>a,3=>d,5=>c,7=>f},
?assertEqual(OrderedM, maps_exercises:sort_by_keys(Map)).
return_values_test()->
Map = #{a=>1, b=>5, c=>3},
List = [1,5,3],
?assertEqual(List, maps_exercises:return_values(Map)).
merge_empty_test() ->
Map = #{a => 1, b => 2, c => 3},
?assertEqual(Map, maps_exercises:merge(#{}, Map)),
?assertEqual(Map, maps_exercises:merge(Map, #{})).
merge_update_test() ->
Map1 = #{a => 1, b => 2, c => 3},
Map2 = #{b => 6, d => 4},
Map3 = #{a => 1, b => 6, c => 3, d => 4},
?assertEqual(Map3, maps_exercises:merge(Map1, Map2)).
map_empty_map_test() ->
F = fun(X) -> X + 1 end,
?assertEqual(#{}, maps_exercises:map(F, #{})).
map_add_1_test() ->
F = fun(X) -> X + 1 end,
Map = #{a => 1, b => 2, c => 3},
Res = #{a => 2, b => 3, c => 4},
?assertEqual(Res, maps_exercises:map(F, Map)).
to_map_test() ->
List = [2, 23, a],
Map = #{1 => 2, 2 => 23, 3 => a},
?assertEqual(Map, maps_exercises:to_map(List)).
to_map_empty_test() ->
?assertEqual(#{}, maps_exercises:to_map([])).
records_to_maps_test() ->
Records = [#person{name="Pepe", age=28}, #person{name="Luis", age=77}],
Maps = [#{age => 28,name => "Pepe"}, #{age => 77,name => "Luis"}],
?assertEqual(Maps, maps_exercises:records_to_maps(Records)).
records_to_maps_empty_test() ->
?assertEqual([], maps_exercises:records_to_maps([])).
maps_to_records_test() ->
Records = [#person{name="Pepe", age=28}, #person{name="Luis", age=77}],
Maps = [#{age => 77,name => "Luis"}, #{age => 28,name => "Pepe"}],
?assertEqual(Records, maps_exercises:maps_to_records(Maps)).
maps_to_records_empty_test() ->
?assertEqual([], maps_exercises:maps_to_records([])).
proplist_to_map_test() ->
Proplist = [{firstname, "Pedro"}, {lastname, "Sanches"}, {age, 11}],
Map = #{age => 11,firstname => "Pedro",lastname => "Sanches"},
?assertEqual(Map, maps_exercises:proplist_to_map(Proplist)).
proplist_to_map_empty_test() ->
?assertEqual(#{}, maps_exercises:proplist_to_map([])).
================================================
FILE: sequential/regex/.gitignore
================================================
.rebar3
_*
.eunit
*.o
*.beam
*.plt
*.swp
*.swo
.erlang.cookie
ebin
log
erl_crash.dump
.rebar
logs
_build
.idea
*.iml
rebar3.crashdump
*~
================================================
FILE: sequential/regex/README.md
================================================
regex
=====
Create a function `match/2` that will receive a string and a regex (also a string), and return `true` if it matches or `false` otherwise.
You won't be using all of Regex, just a small subset of it as follows:
| Syntax | Meaning | Example | Matches |
| ------------- |:-------------------------------------------:| :------: | :------------: |
| a | match the specified character | k | k |
| . | matches any character (except newline) | . | a,b,c,d... |
| ? | matches 0 or 1 of the previous character | aq? | a, aq |
| * | matches 0 or more of the previous character | b* | "", b, bb, bbb |
| ^ | matches the start of a string | ^ca | ca |
| $ | matches the end of a string | eb$ | eb |
### Considerations
* The given regex won't be invalid (Eg: `"^*"`, `"*?"`)
* I can ignore the existance of the newline character since I expect simple strings as inputs. Thus the character `.` matches anything
* An empty regex matches nothing so `match(SomeString, EmptyRegex)` will return false
* `match/2` can return true as soon it matches something since I don't care about where or what.
================================================
FILE: sequential/regex/rebar.config
================================================
{profiles, [
{ci, [{src_dirs, ["solution"]}]}
]}.
================================================
FILE: sequential/regex/solution/regex.app.src
================================================
{application, regex,
[{description, "simple regex engine"},
{vsn, "0.1.0"},
{registered, []},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/regex/solution/regex.erl
================================================
-module(regex).
-export([match/2]).
match([], _Regex) ->
false;
match(_String, []) ->
false;
match(String, [$^ | Regex]) ->
matching(String, Regex);
match(String, Regex) ->
case matching(String, Regex) of
true -> true;
false -> match(tl(String), Regex)
end.
matching(_String, []) ->
true;
matching([], [$$]) ->
true;
matching([], [_Char, $? | Regex]) ->
matching([], Regex);
matching([], [_Char, $* | Regex]) ->
matching([], Regex);
matching([], _Regex) ->
false;
matching(_String, [$$]) ->
false;
matching(String, [$., $* | RemRegex] = Regex) ->
case matching(String, RemRegex) of
true -> true;
false -> matching(tl(String), Regex)
end;
matching([Char | String], [Char, $? | Regex]) ->
matching(String, Regex);
matching(String, [_OtherChar, $? | Regex]) ->
matching(String, Regex);
matching([Char | String],
[Char, $* | _RemRegex] = Regex) ->
matching(String, Regex);
matching(String, [_OtherChar, $* | Regex]) ->
matching(String, Regex);
matching([_Char | String], [$. | Regex]) ->
matching(String, Regex);
matching([Char | String], [Char | Regex]) ->
matching(String, Regex);
matching([_Char | _String], [_OtherChar | _Regex]) ->
false.
================================================
FILE: sequential/regex/src/regex.app.src
================================================
{application, regex,
[{description, "simple regex engine"},
{vsn, "0.1.0"},
{registered, []},
{applications,
[kernel,
stdlib
]},
{env,[]},
{modules, []},
{licenses, ["MIT"]},
{links, []}
]}.
================================================
FILE: sequential/regex/src/regex.erl
================================================
-module(regex).
-export([match/2]).
match(_String, _Regex) ->
put_your_solution_here.
================================================
FILE: sequential/regex/test/regex_SUITE.erl
================================================
-module(regex_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([run_eunit/1]).
all() ->
[run_eunit].
run_eunit(_Config) ->
ok = eunit:test(regex_test).
================================================
FILE: sequential/regex/test/regex_test.erl
================================================
-module(regex_test).
-include_lib("eunit/include/eunit.hrl").
matches_characters_test() ->
?assert(regex:match("a", "a")).
matches_numbers_test() ->
?assert(regex:match("49", "49")).
matches_question_mark_test() ->
?assert(regex:match("Hi", "h?")).
matches_multiple_chars_test() ->
?assert(regex:match("aaaaaaaaa", "a*")).
matches_empty_string_test() ->
?assert(regex:match(" ", "a*")).
matches_any_character_test() ->
?assert(regex:match("asdf as 42", ".")).
gitextract_kx806ov1/
├── .github/
│ └── workflows/
│ └── test.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── check-progress
├── concurrent/
│ ├── calculator/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── rebar.config
│ │ ├── solution/
│ │ │ ├── calculator.app.src
│ │ │ └── calculator.erl
│ │ ├── src/
│ │ │ ├── calculator.app.src
│ │ │ └── calculator.erl
│ │ └── test/
│ │ ├── calculator_SUITE.erl
│ │ └── calculator_test.erl
│ ├── parallel_map/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── rebar.config
│ │ ├── solution/
│ │ │ ├── parallel_map.app.src
│ │ │ └── parallel_map.erl
│ │ ├── src/
│ │ │ ├── parallel_map.app.src
│ │ │ └── parallel_map.erl
│ │ └── test/
│ │ ├── parallel_map_SUITE.erl
│ │ └── parallel_map_test.erl
│ ├── priority/
│ │ ├── .gitignore
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── rebar.config
│ │ ├── solution/
│ │ │ ├── priority.app.src
│ │ │ └── priority.erl
│ │ ├── src/
│ │ │ ├── priority.app.src
│ │ │ └── priority.erl
│ │ └── test/
│ │ ├── .keep
│ │ ├── priority_SUITE.erl
│ │ └── priority_test.erl
│ └── ring_benchmark/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── ring.erl
│ │ └── ring_benchmark.app.src
│ ├── src/
│ │ ├── ring.erl
│ │ └── ring_benchmark.app.src
│ └── test/
│ ├── ring_benchmark_SUITE.erl
│ └── ring_test.erl
├── distributed/
│ └── remote_fun/
│ ├── .gitignore
│ ├── Makefile
│ ├── README.md
│ ├── src/
│ │ ├── remote_fun.app.src
│ │ ├── remote_fun_client.erl
│ │ └── remote_fun_server.erl
│ └── test/
│ └── .keep
├── libraries/
│ └── shortly/
│ ├── .gitignore
│ ├── Makefile
│ ├── README.md
│ ├── rebar.config
│ ├── rebar3
│ ├── solution/
│ │ ├── config/
│ │ │ ├── node_1.config
│ │ │ └── node_2.config
│ │ ├── shortly.app.src
│ │ ├── shortly_app.erl
│ │ ├── shortly_db.erl
│ │ ├── shortly_link_handler.erl
│ │ ├── shortly_notification_algorithm.erl
│ │ ├── shortly_notification_ets.erl
│ │ ├── shortly_notification_pg2.erl
│ │ ├── shortly_shortener.erl
│ │ ├── shortly_sup.erl
│ │ ├── shortly_syn.erl
│ │ └── shortly_ws_handler.erl
│ ├── src/
│ │ ├── shortly.app.src
│ │ └── shortly_app.erl
│ └── test/
│ ├── shortly_shortener_SUITE.erl
│ └── shortly_shortener_test.erl
├── otp/
│ ├── pool/
│ │ ├── README.md
│ │ ├── rebar.config
│ │ ├── solution/
│ │ │ ├── poolie.app.src
│ │ │ ├── poolie_app.erl
│ │ │ ├── poolie_server.erl
│ │ │ ├── poolie_sup.erl
│ │ │ ├── poolie_worker.erl
│ │ │ └── poolie_worker_sup.erl
│ │ ├── src/
│ │ │ ├── poolie.app.src
│ │ │ ├── poolie_app.erl
│ │ │ ├── poolie_server.erl
│ │ │ ├── poolie_sup.erl
│ │ │ ├── poolie_worker.erl
│ │ │ └── poolie_worker_sup.erl
│ │ └── test/
│ │ ├── poolie_SUITE.erl
│ │ └── poolie_test.erl
│ └── shopping_cart/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── shopping_cart.app.src
│ │ └── shopping_cart.erl
│ ├── src/
│ │ ├── shopping_cart.app.src
│ │ └── shopping_cart.erl
│ └── test/
│ ├── shopping_cart_SUITE.erl
│ └── shopping_cart_test.erl
└── sequential/
├── bank_accounts/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── bank_account.app.src
│ │ └── bank_account.erl
│ ├── src/
│ │ ├── bank_account.app.src
│ │ └── bank_account.erl
│ └── test/
│ ├── bank_account_SUITE.erl
│ └── bank_account_test.erl
├── calculate_bmi/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── calculate_bmi.app.src
│ │ ├── calculate_bmi.erl
│ │ └── person_record.hrl
│ ├── src/
│ │ ├── calculate_bmi.app.src
│ │ ├── calculate_bmi.erl
│ │ └── person_record.hrl
│ └── test/
│ ├── calculate_bmi_SUITE.erl
│ └── calculate_bmi_test.erl
├── filter_fibonacci_numbers/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── filter_fibonacci_numbers.app.src
│ │ └── filter_fibonacci_numbers.erl
│ ├── src/
│ │ ├── filter_fibonacci_numbers.app.src
│ │ └── filter_fibonacci_numbers.erl
│ └── test/
│ ├── filter_fibonacci_numbers_SUITE.erl
│ └── filter_fibonacci_numbers_test.erl
├── filter_numbers/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── filter_numbers.app.src
│ │ └── filter_numbers.erl
│ ├── src/
│ │ ├── filter_numbers.app.src
│ │ └── filter_numbers.erl
│ └── test/
│ ├── filter_numbers_SUITE.erl
│ └── filter_numbers_test.erl
├── hello/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── hello.app.src
│ │ └── hello.erl
│ ├── src/
│ │ ├── hello.app.src
│ │ └── hello.erl
│ └── test/
│ ├── hello_SUITE.erl
│ └── hello_test.erl
├── hello_pattern/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── hello_pattern.app.src
│ │ └── hello_pattern.erl
│ ├── src/
│ │ ├── hello_pattern.app.src
│ │ └── hello_pattern.erl
│ └── test/
│ ├── hello_pattern_SUITE.erl
│ └── hello_pattern_test.erl
├── insert_element_at/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── insert_element_at.app.src
│ │ └── insert_element_at.erl
│ ├── src/
│ │ ├── insert_element_at.app.src
│ │ └── insert_element_at.erl
│ └── test/
│ ├── insert_element_at_SUITE.erl
│ └── insert_element_at_test.erl
├── installing/
│ ├── .gitignore
│ ├── Makefile
│ ├── README.md
│ ├── flake.nix
│ ├── src/
│ │ ├── installing.app.src
│ │ └── installing.erl
│ └── test/
│ ├── installing_SUITE.erl
│ └── installing_test.erl
├── lists/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── lists_exercises.app.src
│ │ └── lists_exercises.erl
│ ├── src/
│ │ ├── lists_exercises.app.src
│ │ └── lists_exercises.erl
│ └── test/
│ ├── lists_exercises_SUITE.erl
│ └── lists_exercises_test.erl
├── maps/
│ ├── .gitignore
│ ├── README.md
│ ├── rebar.config
│ ├── solution/
│ │ ├── maps_exercises.app.src
│ │ └── maps_exercises.erl
│ ├── src/
│ │ ├── maps_exercises.app.src
│ │ └── maps_exercises.erl
│ └── test/
│ ├── maps_exercises_SUITE.erl
│ └── maps_exercises_test.erl
└── regex/
├── .gitignore
├── README.md
├── rebar.config
├── solution/
│ ├── regex.app.src
│ └── regex.erl
├── src/
│ ├── regex.app.src
│ └── regex.erl
└── test/
├── regex_SUITE.erl
└── regex_test.erl
Condensed preview — 198 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (129K chars).
[
{
"path": ".github/workflows/test.yml",
"chars": 531,
"preview": "on:\n push:\n branches: [main]\n pull_request:\n types: [opened, repoened, synchronize]\njobs:\n test:\n runs-on: u"
},
{
"path": ".gitignore",
"chars": 110,
"preview": ".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",
"chars": 1068,
"preview": "MIT License\n\nCopyright (c) 2018 LambdaClass\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
},
{
"path": "Makefile",
"chars": 408,
"preview": "DIRS = $(filter-out _build/, $(dir $(wildcard */)))\n\nEXERCISES = $(patsubst %/, %, $(filter-out $(DIRS), $(dir $(wildcar"
},
{
"path": "README.md",
"chars": 6554,
"preview": "\n\n\n# Erlings: Small exercise"
},
{
"path": "check-progress",
"chars": 1647,
"preview": "#!/usr/bin/env escript\n%% -*- erlang -*-\n%%! -smp enable -sname factorial -mnesia debug verbose\n\nmain(_) ->\n Folders = "
},
{
"path": "concurrent/calculator/.gitignore",
"chars": 140,
"preview": ".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\nreb"
},
{
"path": "concurrent/calculator/README.md",
"chars": 1694,
"preview": "# Calculator\n\n## Reading Material\n\n- [The Hitchhiker's Guide to Concurrency](http://learnyousomeerlang.com/the-hitchhike"
},
{
"path": "concurrent/calculator/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "concurrent/calculator/solution/calculator.app.src",
"chars": 267,
"preview": "{application, calculator,\n [{description, \"calculator\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { calculator_app,"
},
{
"path": "concurrent/calculator/solution/calculator.erl",
"chars": 947,
"preview": "-module(calculator).\n-export([start_calculator/0,\n calculator_server/0,\n add/3,\n subtract/3,\n "
},
{
"path": "concurrent/calculator/src/calculator.app.src",
"chars": 267,
"preview": "{application, calculator,\n [{description, \"calculator\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { calculator_app,"
},
{
"path": "concurrent/calculator/src/calculator.erl",
"chars": 246,
"preview": "-module(calculator).\n-export([start_calculator/0,\n calculator_server/0,\n turn_off/1]).\n\nstart_calculator"
},
{
"path": "concurrent/calculator/test/calculator_SUITE.erl",
"chars": 199,
"preview": "-module(calculator_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall("
},
{
"path": "concurrent/calculator/test/calculator_test.erl",
"chars": 548,
"preview": "-module(calculator_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\ncalculator_add_test() ->\n Cal = calculator:start_c"
},
{
"path": "concurrent/parallel_map/.gitignore",
"chars": 140,
"preview": ".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\nreb"
},
{
"path": "concurrent/parallel_map/README.md",
"chars": 686,
"preview": "# Parallel Map\n\n## Reading Material\n\n- [The Hitchhiker's Guide to Concurrency](http://learnyousomeerlang.com/the-hitchhi"
},
{
"path": "concurrent/parallel_map/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "concurrent/parallel_map/solution/parallel_map.app.src",
"chars": 273,
"preview": "{application, parallel_map,\n [{description, \"parallel_map\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { parallel_ma"
},
{
"path": "concurrent/parallel_map/solution/parallel_map.erl",
"chars": 353,
"preview": "-module(parallel_map).\n-export([pmap/2,\n apply/3]).\n\npmap(Fun, List) ->\n Pids = lists:map(fun(Elem) ->\n sp"
},
{
"path": "concurrent/parallel_map/src/parallel_map.app.src",
"chars": 273,
"preview": "{application, parallel_map,\n [{description, \"parallel_map\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { parallel_ma"
},
{
"path": "concurrent/parallel_map/src/parallel_map.erl",
"chars": 84,
"preview": "-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",
"chars": 203,
"preview": "-module(parallel_map_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nal"
},
{
"path": "concurrent/parallel_map/test/parallel_map_test.erl",
"chars": 244,
"preview": "-module(parallel_map_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nparallel_map_result_test() ->\n Fun = fun(X) -> X"
},
{
"path": "concurrent/priority/.gitignore",
"chars": 140,
"preview": ".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\nreb"
},
{
"path": "concurrent/priority/Makefile",
"chars": 131,
"preview": ".PHONY: compile, run\n\ndefault: run\n\ncompile:\n\terl -compile priority\n\nrun: compile\n\terl -noshell -s priority send_test -s"
},
{
"path": "concurrent/priority/README.md",
"chars": 1655,
"preview": "# Priority\n\n## Reading material\n\n- [Learn You Some Erlang: More On Multiprocessing](http://learnyousomeerlang.com/more-o"
},
{
"path": "concurrent/priority/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "concurrent/priority/solution/priority.app.src",
"chars": 261,
"preview": "{application, priority,\n [{description, \"priority\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { priority_app, []}},"
},
{
"path": "concurrent/priority/solution/priority.erl",
"chars": 677,
"preview": "-module(priority).\n\n-export([start/0,\n send_vip/2,\n send_normal/2,\n get_messages/1,\n pri"
},
{
"path": "concurrent/priority/src/priority.app.src",
"chars": 261,
"preview": "{application, priority,\n [{description, \"priority\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { priority_app, []}},"
},
{
"path": "concurrent/priority/src/priority.erl",
"chars": 216,
"preview": "-module(priority).\n\n-export([start/0,\n get_messages/1,\n priority_loop/1]).\n\nstart() ->\n your_solution_h"
},
{
"path": "concurrent/priority/test/.keep",
"chars": 0,
"preview": ""
},
{
"path": "concurrent/priority/test/priority_SUITE.erl",
"chars": 195,
"preview": "-module(priority_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() "
},
{
"path": "concurrent/priority/test/priority_test.erl",
"chars": 642,
"preview": "-module(priority_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\n\nsort_fun({vip, _}, {vip, _}) ->\n true;\nsort_fun({no"
},
{
"path": "concurrent/ring_benchmark/.gitignore",
"chars": 134,
"preview": ".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\nreb"
},
{
"path": "concurrent/ring_benchmark/README.md",
"chars": 392,
"preview": "# Ring benchmark\n\n## Reading Material\n\n## Exercise\nWrite a function `ring:ring/2` which takes 2 arguments: M and N.\nThis"
},
{
"path": "concurrent/ring_benchmark/rebar.config",
"chars": 92,
"preview": "{erl_opts, [debug_info]}.\n{deps, []}.\n{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "concurrent/ring_benchmark/solution/ring.erl",
"chars": 860,
"preview": "-module(ring).\n-export([ring/2]).\n\nnode_loop(Parent) ->\n receive\n {msg, []} -> Parent ! done;\n {msg, [FirstNode |"
},
{
"path": "concurrent/ring_benchmark/solution/ring_benchmark.app.src",
"chars": 290,
"preview": "{application, ring_benchmark,\n [{description, \"An OTP application\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { rin"
},
{
"path": "concurrent/ring_benchmark/src/ring.erl",
"chars": 95,
"preview": "-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",
"chars": 290,
"preview": "{application, ring_benchmark,\n [{description, \"An OTP application\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { rin"
},
{
"path": "concurrent/ring_benchmark/test/ring_benchmark_SUITE.erl",
"chars": 192,
"preview": "-module(ring_benchmark_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\n"
},
{
"path": "concurrent/ring_benchmark/test/ring_test.erl",
"chars": 1566,
"preview": "-module(ring_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nring_test() ->\n ?assertMatch(#{msgs_sent := 4, procs_sta"
},
{
"path": "distributed/remote_fun/.gitignore",
"chars": 134,
"preview": ".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\nreb"
},
{
"path": "distributed/remote_fun/Makefile",
"chars": 172,
"preview": ".PHONY: server client\n\ndefault:\n\t@echo \"usage make [client|server]\"\n\nclient:\n\t./rebar3 shell --sname 'client@localhost'\n"
},
{
"path": "distributed/remote_fun/README.md",
"chars": 1206,
"preview": "# Remote Function Server\n\n## Reading material\n\n- [Learn you Some Erlang: Distribunomicon](http://learnyousomeerlang.com/"
},
{
"path": "distributed/remote_fun/src/remote_fun.app.src",
"chars": 272,
"preview": "{application, remote_fun,\n [{description, \"Remote function\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { remote_fun"
},
{
"path": "distributed/remote_fun/src/remote_fun_client.erl",
"chars": 573,
"preview": "-module(remote_fun_client).\n\n-export([function_client/1]).\n\nfunction_client(RemoteNode) ->\n FunctionServerPid = {func"
},
{
"path": "distributed/remote_fun/src/remote_fun_server.erl",
"chars": 178,
"preview": "-module(remote_fun_server).\n\n-export([function_server/0]).\n\nfunction_server() ->\n register(function_server, self()),\n"
},
{
"path": "distributed/remote_fun/test/.keep",
"chars": 0,
"preview": ""
},
{
"path": "libraries/shortly/.gitignore",
"chars": 134,
"preview": ".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\nreb"
},
{
"path": "libraries/shortly/Makefile",
"chars": 293,
"preview": ".PHONY: default server samples test\n\ndefault:\n\t@echo \"usage [test|server]\"\n\nserver:\n\t./rebar3 shell --apps shortly\n\nnode"
},
{
"path": "libraries/shortly/README.md",
"chars": 3052,
"preview": "# Shortly: Link Shortener\n\n## Reading material\n\n- [Rebar3 build tool](https://github.com/erlang/rebar3)\n- [Rebar3 hex](h"
},
{
"path": "libraries/shortly/rebar.config",
"chars": 162,
"preview": "{erl_opts, [debug_info]}.\n{deps, [cowboy,\n jsx,\n gun,\n syn]}.\n{plugins,[rebar3_hex]}.\n\n{profiles, ["
},
{
"path": "libraries/shortly/solution/config/node_1.config",
"chars": 79,
"preview": "[\n {mnesia, [{dir, \"/tmp/mnesia/node_8010\"}]},\n {shortly, [{port, 8010}]}\n].\n"
},
{
"path": "libraries/shortly/solution/config/node_2.config",
"chars": 79,
"preview": "[\n {mnesia, [{dir, \"/tmp/mnesia/node_8020\"}]},\n {shortly, [{port, 8020}]}\n].\n"
},
{
"path": "libraries/shortly/solution/shortly.app.src",
"chars": 318,
"preview": "{application, shortly,\n [{description, \"Link shortener exercise\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { short"
},
{
"path": "libraries/shortly/solution/shortly_app.erl",
"chars": 1046,
"preview": "-module(shortly_app).\n\n-behaviour(application).\n\n-export([start/2,\n stop/1,\n install/1]).\n\n-record(short"
},
{
"path": "libraries/shortly/solution/shortly_db.erl",
"chars": 496,
"preview": "-module(shortly_db).\n\n-export([store_url/1,\n save_url/2]).\n\n-record(shortly_urls, {hash, url}).\n\nstore_url(Short"
},
{
"path": "libraries/shortly/solution/shortly_link_handler.erl",
"chars": 1220,
"preview": "-module(shortly_link_handler).\n\n-export([init/2]).\n\ninit(Req, State) ->\n Url = get_request_url(Req),\n ReqMethod = "
},
{
"path": "libraries/shortly/solution/shortly_notification_algorithm.erl",
"chars": 179,
"preview": "-module(shortly_notification_algorithm).\n\n-export([behaviour_info/1]).\n\nbehaviour_info(callbacks) ->\n [{init,0},\n "
},
{
"path": "libraries/shortly/solution/shortly_notification_ets.erl",
"chars": 498,
"preview": "-module(shortly_notification_ets).\n\n-behaviour(shortly_notification_algorithm).\n\n-export([init/0,\n subscribe/1,\n"
},
{
"path": "libraries/shortly/solution/shortly_notification_pg2.erl",
"chars": 487,
"preview": "-module(shortly_notification_pg2).\n\n-behaviour(shortly_notification_algorithm).\n\n-export([init/0,\n subscribe/1,\n"
},
{
"path": "libraries/shortly/solution/shortly_shortener.erl",
"chars": 1579,
"preview": "-module(shortly_shortener).\n\n-export([init/0,\n short/1,\n get/1,\n subscribe/1,\n unsubscri"
},
{
"path": "libraries/shortly/solution/shortly_sup.erl",
"chars": 247,
"preview": "-module(shortly_sup).\n\n-behaviour(supervisor).\n\n-export([start_link/0,\n init/1]).\n\nstart_link() ->\n superviso"
},
{
"path": "libraries/shortly/solution/shortly_syn.erl",
"chars": 369,
"preview": "-module(shortly_syn).\n\n-behaviour(shortly_notification_algorithm).\n\n-export([init/0,\n subscribe/1,\n unsu"
},
{
"path": "libraries/shortly/solution/shortly_ws_handler.erl",
"chars": 796,
"preview": "-module(shortly_ws_handler).\n\n-export([init/2,\n websocket_init/1,\n websocket_handle/2,\n websocke"
},
{
"path": "libraries/shortly/src/shortly.app.src",
"chars": 318,
"preview": "{application, shortly,\n [{description, \"Link shortener exercise\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { short"
},
{
"path": "libraries/shortly/src/shortly_app.erl",
"chars": 62,
"preview": "-module(shortly_app).\n\n-behaviour(application).\n\n-export([]).\n"
},
{
"path": "libraries/shortly/test/shortly_shortener_SUITE.erl",
"chars": 3264,
"preview": "-module(shortly_shortener_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-compile(export_all).\n-compile(nowarn_ex"
},
{
"path": "libraries/shortly/test/shortly_shortener_test.erl",
"chars": 259,
"preview": "-module(shortly_shortener_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nidempotence_test() ->\n shortly_shortener:"
},
{
"path": "otp/pool/README.md",
"chars": 1694,
"preview": "# Worker Pool\n\n## Reading material\n\n- [Learn You Some Erlang: Who Supervises The Supervisors?](https://learnyousomeerlan"
},
{
"path": "otp/pool/rebar.config",
"chars": 90,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n\n{shell, [\n {apps, [poolie]}\n]}.\n"
},
{
"path": "otp/pool/solution/poolie.app.src",
"chars": 267,
"preview": "{application, poolie,\n [{description, \"An OTP application\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { poolie_app,"
},
{
"path": "otp/pool/solution/poolie_app.erl",
"chars": 750,
"preview": "%%%-------------------------------------------------------------------\n%% @doc poolie public API\n%% @end\n%%%------------"
},
{
"path": "otp/pool/solution/poolie_server.erl",
"chars": 1726,
"preview": "-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([i"
},
{
"path": "otp/pool/solution/poolie_sup.erl",
"chars": 1507,
"preview": "%%%-------------------------------------------------------------------\n%% @doc poolie top level supervisor.\n%% @end\n%%%-"
},
{
"path": "otp/pool/solution/poolie_worker.erl",
"chars": 698,
"preview": "-module(poolie_worker).\n\n-behaviour(gen_server).\n\n%% API\n-export([start/0]).\n-export([init/1, handle_call/3, handle_cast"
},
{
"path": "otp/pool/solution/poolie_worker_sup.erl",
"chars": 563,
"preview": "-module(poolie_worker_sup).\n\n-behaviour(supervisor).\n\n-export([start_link/0, add_workers/1, init/1]).\n\n%% API\n\nstart_lin"
},
{
"path": "otp/pool/src/poolie.app.src",
"chars": 267,
"preview": "{application, poolie,\n [{description, \"An OTP application\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { poolie_app,"
},
{
"path": "otp/pool/src/poolie_app.erl",
"chars": 750,
"preview": "%%%-------------------------------------------------------------------\n%% @doc poolie public API\n%% @end\n%%%------------"
},
{
"path": "otp/pool/src/poolie_server.erl",
"chars": 1376,
"preview": "-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([i"
},
{
"path": "otp/pool/src/poolie_sup.erl",
"chars": 1213,
"preview": "%%%-------------------------------------------------------------------\n%% @doc poolie top level supervisor.\n%% @end\n%%%-"
},
{
"path": "otp/pool/src/poolie_worker.erl",
"chars": 521,
"preview": "-module(poolie_worker).\n\n-behaviour(gen_server).\n\n%% API\n-export([start/0]).\n-export([init/1, handle_call/3, handle_cast"
},
{
"path": "otp/pool/src/poolie_worker_sup.erl",
"chars": 355,
"preview": "-module(poolie_worker_sup).\n\n-behaviour(supervisor).\n\n-export([start_link/0, add_workers/1, init/1]).\n\n%% API\n\nstart_lin"
},
{
"path": "otp/pool/test/poolie_SUITE.erl",
"chars": 246,
"preview": "-module(poolie_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall() ->"
},
{
"path": "otp/pool/test/poolie_test.erl",
"chars": 539,
"preview": "-module(poolie_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\npoolie_worker_sup_test() ->\n WorkerCount = worker_co"
},
{
"path": "otp/shopping_cart/.gitignore",
"chars": 134,
"preview": ".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\nreb"
},
{
"path": "otp/shopping_cart/README.md",
"chars": 1162,
"preview": "# Shopping Cart\n\n## Reading Material\n\n- [Learn You Some Erlang: What is OTP?](http://learnyousomeerlang.com/what-is-otp)"
},
{
"path": "otp/shopping_cart/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "otp/shopping_cart/solution/shopping_cart.app.src",
"chars": 276,
"preview": "{application, shopping_cart,\n [{description, \"shopping_part\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { shopping_"
},
{
"path": "otp/shopping_cart/solution/shopping_cart.erl",
"chars": 1053,
"preview": "-module(shopping_cart).\n\n-behaviour(gen_server).\n\n-export([start_link/0,\n put_item/2,\n finish/1,\n "
},
{
"path": "otp/shopping_cart/src/shopping_cart.app.src",
"chars": 276,
"preview": "{application, shopping_cart,\n [{description, \"shopping_part\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { shopping_"
},
{
"path": "otp/shopping_cart/src/shopping_cart.erl",
"chars": 340,
"preview": "-module(shopping_cart).\n\n-behaviour(gen_server).\n\n-export([start_link/0,\n put_item/2,\n finish/1,\n "
},
{
"path": "otp/shopping_cart/test/shopping_cart_SUITE.erl",
"chars": 205,
"preview": "-module(shopping_cart_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\na"
},
{
"path": "otp/shopping_cart/test/shopping_cart_test.erl",
"chars": 506,
"preview": "-module(shopping_cart_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nshopping_cart_add_item_test() ->\n {ok, Pid} ="
},
{
"path": "sequential/bank_accounts/.gitignore",
"chars": 140,
"preview": ".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\nreb"
},
{
"path": "sequential/bank_accounts/README.md",
"chars": 1416,
"preview": "# Bank Accounts\n\n## Reading Material\n\n- [Learn You Some Erlang: Errors and Exceptions](http://learnyousomeerlang.com/err"
},
{
"path": "sequential/bank_accounts/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "sequential/bank_accounts/solution/bank_account.app.src",
"chars": 273,
"preview": "{application, bank_account,\n [{description, \"bank_account\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { bank_accoun"
},
{
"path": "sequential/bank_accounts/solution/bank_account.erl",
"chars": 562,
"preview": "-module(bank_account).\n\n-export([process_operation/2]).\n\noperate({AccountNumber, Money}, withdraw, Amount) ->\n case (Mo"
},
{
"path": "sequential/bank_accounts/src/bank_account.app.src",
"chars": 273,
"preview": "{application, bank_account,\n [{description, \"bank_account\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { bank_accoun"
},
{
"path": "sequential/bank_accounts/src/bank_account.erl",
"chars": 121,
"preview": "-module(bank_account).\n\n-export([process_operation/2]).\n\nprocess_operation(Bank, Operation) ->\n put_your_solution_here."
},
{
"path": "sequential/bank_accounts/test/bank_account_SUITE.erl",
"chars": 203,
"preview": "-module(bank_account_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nal"
},
{
"path": "sequential/bank_accounts/test/bank_account_test.erl",
"chars": 651,
"preview": "-module(bank_account_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nprocess_operation_withdraw_ok_test() ->\n Bank = "
},
{
"path": "sequential/calculate_bmi/.gitignore",
"chars": 140,
"preview": ".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\nreb"
},
{
"path": "sequential/calculate_bmi/README.md",
"chars": 1077,
"preview": "# Calculate BMI\n\n## Reading Material\n\n- [Learn You Some Erlang](http://learnyousomeerlang.com/a-short-visit-to-common-da"
},
{
"path": "sequential/calculate_bmi/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "sequential/calculate_bmi/solution/calculate_bmi.app.src",
"chars": 276,
"preview": "{application, calculate_bmi,\n [{description, \"calculate_bmi\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { calculate"
},
{
"path": "sequential/calculate_bmi/solution/calculate_bmi.erl",
"chars": 453,
"preview": "-module(calculate_bmi).\n\n-export([bmi/1, classify/1]).\n\n-include(\"../src/person_record.hrl\").\n\nbmi(P) when is_record(P, "
},
{
"path": "sequential/calculate_bmi/solution/person_record.hrl",
"chars": 133,
"preview": "% person record declaration:\n% name\n% weight specified in kg.\n% height specified in metres.\n-record(person, {name, weigh"
},
{
"path": "sequential/calculate_bmi/src/calculate_bmi.app.src",
"chars": 276,
"preview": "{application, calculate_bmi,\n [{description, \"calculate_bmi\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { calculate"
},
{
"path": "sequential/calculate_bmi/src/calculate_bmi.erl",
"chars": 183,
"preview": "-module(calculate_bmi).\n\n-export([bmi/1, classify/1]).\n\n-include(\"../src/person_record.hrl\").\n\nbmi(Person) ->\n put_your"
},
{
"path": "sequential/calculate_bmi/src/person_record.hrl",
"chars": 133,
"preview": "% person record declaration:\n% name\n% weight specified in kg.\n% height specified in metres.\n-record(person, {name, weigh"
},
{
"path": "sequential/calculate_bmi/test/calculate_bmi_SUITE.erl",
"chars": 205,
"preview": "-module(calculate_bmi_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\na"
},
{
"path": "sequential/calculate_bmi/test/calculate_bmi_test.erl",
"chars": 814,
"preview": "-module(calculate_bmi_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\n-include(\"../src/person_record.hrl\").\n\ncalculate"
},
{
"path": "sequential/filter_fibonacci_numbers/.gitignore",
"chars": 140,
"preview": ".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\nreb"
},
{
"path": "sequential/filter_fibonacci_numbers/README.md",
"chars": 782,
"preview": "# Filter Fibonacci Numbers\n\n## Reading material\n\n- [Fibonnaci number](https://en.wikipedia.org/wiki/Fibonacci_number)\n\n#"
},
{
"path": "sequential/filter_fibonacci_numbers/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "sequential/filter_fibonacci_numbers/solution/filter_fibonacci_numbers.app.src",
"chars": 309,
"preview": "{application, filter_fibonacci_numbers,\n [{description, \"filter_fibonacci_numbers\"},\n {vsn, \"0.1.0\"},\n {registered, []"
},
{
"path": "sequential/filter_fibonacci_numbers/solution/filter_fibonacci_numbers.erl",
"chars": 636,
"preview": "-module(filter_fibonacci_numbers).\n\n-export([filter/1]).\n\nfibonacci(1) ->\n 1;\nfibonacci(2) ->\n 2;\nfibonacci(N) when N "
},
{
"path": "sequential/filter_fibonacci_numbers/src/filter_fibonacci_numbers.app.src",
"chars": 309,
"preview": "{application, filter_fibonacci_numbers,\n [{description, \"filter_fibonacci_numbers\"},\n {vsn, \"0.1.0\"},\n {registered, []"
},
{
"path": "sequential/filter_fibonacci_numbers/src/filter_fibonacci_numbers.erl",
"chars": 100,
"preview": "-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",
"chars": 227,
"preview": "-module(filter_fibonacci_numbers_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eu"
},
{
"path": "sequential/filter_fibonacci_numbers/test/filter_fibonacci_numbers_test.erl",
"chars": 229,
"preview": "-module(filter_fibonacci_numbers_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nfilter_fibonacci_numbers_test() ->\n "
},
{
"path": "sequential/filter_numbers/.gitignore",
"chars": 140,
"preview": ".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\nreb"
},
{
"path": "sequential/filter_numbers/README.md",
"chars": 1591,
"preview": "# Filter Numbers\n\n## Reading material\n\n- [Learn You Some Erlang: Starting Out (for real)](http://learnyousomeerlang.com/"
},
{
"path": "sequential/filter_numbers/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "sequential/filter_numbers/solution/filter_numbers.app.src",
"chars": 279,
"preview": "{application, filter_numbers,\n [{description, \"filter_numbers\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { filter_"
},
{
"path": "sequential/filter_numbers/solution/filter_numbers.erl",
"chars": 355,
"preview": "-module(filter_numbers).\n\n-export([filter_in/3,\n filter_out/3,\n filter_in_values/3]).\n\nfilter_in(From, T"
},
{
"path": "sequential/filter_numbers/src/filter_numbers.app.src",
"chars": 279,
"preview": "{application, filter_numbers,\n [{description, \"filter_numbers\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { filter_"
},
{
"path": "sequential/filter_numbers/src/filter_numbers.erl",
"chars": 278,
"preview": "-module(filter_numbers).\n\n-export([filter_in/3,\n filter_out/3,\n filter_in_values/3]).\n\nfilter_in(From, T"
},
{
"path": "sequential/filter_numbers/test/filter_numbers_SUITE.erl",
"chars": 207,
"preview": "-module(filter_numbers_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\n"
},
{
"path": "sequential/filter_numbers/test/filter_numbers_test.erl",
"chars": 530,
"preview": "-module(filter_numbers_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nfilter_numbers_in_test() ->\n List = [7, 14, 21"
},
{
"path": "sequential/hello/.gitignore",
"chars": 134,
"preview": ".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\nreb"
},
{
"path": "sequential/hello/README.md",
"chars": 579,
"preview": "# Hello world\n\n## Reading Material\n\n- [Learn You Some Erlang: Modules](http://learnyousomeerlang.com/modules)\n\n## Exerci"
},
{
"path": "sequential/hello/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "sequential/hello/solution/hello.app.src",
"chars": 252,
"preview": "{application, hello,\n [{description, \"hello\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { hello_app, []}},\n {appli"
},
{
"path": "sequential/hello/solution/hello.erl",
"chars": 70,
"preview": "-module(hello).\n\n-export([hello/0]).\n\nhello() ->\n <<\"hello world\">>.\n"
},
{
"path": "sequential/hello/src/hello.app.src",
"chars": 252,
"preview": "{application, hello,\n [{description, \"hello\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { hello_app, []}},\n {appli"
},
{
"path": "sequential/hello/src/hello.erl",
"chars": 110,
"preview": "-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",
"chars": 189,
"preview": "-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"
},
{
"path": "sequential/hello/test/hello_test.erl",
"chars": 123,
"preview": "-module(hello_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nhello_world_test() ->\n <<\"hello world\">> = hello:hello("
},
{
"path": "sequential/hello_pattern/.gitignore",
"chars": 134,
"preview": ".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\nreb"
},
{
"path": "sequential/hello_pattern/README.md",
"chars": 856,
"preview": "# Hello Pattern\n\n## Reading Material\n\n- [Learn You Some Erlang: Syntax in functions](http://learnyousomeerlang.com/synta"
},
{
"path": "sequential/hello_pattern/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "sequential/hello_pattern/solution/hello_pattern.app.src",
"chars": 275,
"preview": "{application, hello_pattern,\n [{description, \"hello_pattern\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { hello_pat"
},
{
"path": "sequential/hello_pattern/solution/hello_pattern.erl",
"chars": 296,
"preview": "-module(hello_pattern).\n\n-export([hello/1]).\n\nhello({morning, _Name}) ->\n morning;\nhello({evening, Name}) ->\n {good, "
},
{
"path": "sequential/hello_pattern/src/hello_pattern.app.src",
"chars": 275,
"preview": "{application, hello_pattern,\n [{description, \"hello_pattern\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { hello_pat"
},
{
"path": "sequential/hello_pattern/src/hello_pattern.erl",
"chars": 102,
"preview": "-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",
"chars": 205,
"preview": "-module(hello_pattern_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\na"
},
{
"path": "sequential/hello_pattern/test/hello_pattern_test.erl",
"chars": 459,
"preview": "-module(hello_pattern_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nhello_day_test() ->\n morning =\n hello_patter"
},
{
"path": "sequential/insert_element_at/.gitignore",
"chars": 134,
"preview": ".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\nreb"
},
{
"path": "sequential/insert_element_at/README.md",
"chars": 1011,
"preview": "# Insert element at position\n\n## Reading Material\n\n- [Learn You Some Erlang: A Short Visit to Common Data Structures](ht"
},
{
"path": "sequential/insert_element_at/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "sequential/insert_element_at/solution/insert_element_at.app.src",
"chars": 288,
"preview": "{application, insert_element_at,\n [{description, \"insert_element_at\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { i"
},
{
"path": "sequential/insert_element_at/solution/insert_element_at.erl",
"chars": 527,
"preview": "-module(insert_element_at).\n\n-export([insert/3]).\n\ninsert([H|T], Pos, Element) ->\n insert(Pos, Element, H, [], T).\n\nins"
},
{
"path": "sequential/insert_element_at/src/insert_element_at.app.src",
"chars": 288,
"preview": "{application, insert_element_at,\n [{description, \"insert_element_at\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { i"
},
{
"path": "sequential/insert_element_at/src/insert_element_at.erl",
"chars": 107,
"preview": "-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",
"chars": 213,
"preview": "-module(insert_element_at_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1])"
},
{
"path": "sequential/insert_element_at/test/insert_element_at_test.erl",
"chars": 304,
"preview": "-module(insert_element_at_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\ninsertion_test() ->\n [1, #{current := \"hi\","
},
{
"path": "sequential/installing/.gitignore",
"chars": 134,
"preview": ".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\nreb"
},
{
"path": "sequential/installing/Makefile",
"chars": 30,
"preview": ".PHONY: test\ntest:\n\trebar3 ct\n"
},
{
"path": "sequential/installing/README.md",
"chars": 1835,
"preview": "# Installing\n\n## Reading Material\n\n- [Learn You Some Erlang: Introduction](http://learnyousomeerlang.com/introduction)\n\n"
},
{
"path": "sequential/installing/flake.nix",
"chars": 539,
"preview": "{\n description = \"A flake to download Erlang 27 and Rebar3 from Nixpkgs 24.05\";\n\n inputs = {\n nixpkgs.url = \"github"
},
{
"path": "sequential/installing/src/installing.app.src",
"chars": 267,
"preview": "{application, installing,\n [{description, \"installing\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { installing_app,"
},
{
"path": "sequential/installing/src/installing.erl",
"chars": 96,
"preview": "-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",
"chars": 199,
"preview": "-module(installing_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\nall("
},
{
"path": "sequential/installing/test/installing_test.erl",
"chars": 162,
"preview": "-module(installing_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\ninstalling_test() ->\n ?assertEqual('i can not wait"
},
{
"path": "sequential/lists/.gitignore",
"chars": 140,
"preview": ".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\nreb"
},
{
"path": "sequential/lists/README.md",
"chars": 4507,
"preview": "# Lists\n\n## Reading Material\n\nRecursion and high order functions can be tricky if you don't have a functional programmin"
},
{
"path": "sequential/lists/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "sequential/lists/solution/lists_exercises.app.src",
"chars": 282,
"preview": "{application, lists_exercises,\n [{description, \"lists_exercises\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { lists"
},
{
"path": "sequential/lists/solution/lists_exercises.erl",
"chars": 3010,
"preview": "-module(lists_exercises).\n\n-export([reverse/1,\n rmconsecutive/1,\n even_fib_numbers/0,\n foldl/2,\n"
},
{
"path": "sequential/lists/src/lists_exercises.app.src",
"chars": 282,
"preview": "{application, lists_exercises,\n [{description, \"lists_exercises\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { lists"
},
{
"path": "sequential/lists/src/lists_exercises.erl",
"chars": 738,
"preview": "-module(lists_exercises).\n\n-export([reverse/1,\n rmconsecutive/1,\n even_fib_numbers/0,\n foldl/3,\n"
},
{
"path": "sequential/lists/test/lists_exercises_SUITE.erl",
"chars": 209,
"preview": "-module(lists_exercises_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n"
},
{
"path": "sequential/lists/test/lists_exercises_test.erl",
"chars": 1953,
"preview": "-module(lists_exercises_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nall_the_same_test() ->\n ?assertEqual([1, 1, 1"
},
{
"path": "sequential/maps/.gitignore",
"chars": 140,
"preview": ".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\nreb"
},
{
"path": "sequential/maps/README.md",
"chars": 4628,
"preview": "# Maps\n\n## Reading Material\n\n- [Learn You Some Erlang: Maps](http://learnyousomeerlang.com/maps)\n\n## Exercises\n\n### Sum "
},
{
"path": "sequential/maps/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "sequential/maps/solution/maps_exercises.app.src",
"chars": 279,
"preview": "{application, maps_exercises,\n [{description, \"maps_exercises\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { maps_ex"
},
{
"path": "sequential/maps/solution/maps_exercises.erl",
"chars": 2172,
"preview": "-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, r"
},
{
"path": "sequential/maps/src/maps_exercises.app.src",
"chars": 279,
"preview": "{application, maps_exercises,\n [{description, \"maps_exercises\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {mod, { maps_ex"
},
{
"path": "sequential/maps/src/maps_exercises.erl",
"chars": 665,
"preview": "-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, r"
},
{
"path": "sequential/maps/test/maps_exercises_SUITE.erl",
"chars": 207,
"preview": "-module(maps_exercises_SUITE).\n\n-include_lib(\"common_test/include/ct.hrl\").\n\n-export([all/0]).\n-export([run_eunit/1]).\n\n"
},
{
"path": "sequential/maps/test/maps_exercises_test.erl",
"chars": 2393,
"preview": "-module(maps_exercises_test).\n-record(person, {name, age}).\n-include_lib(\"eunit/include/eunit.hrl\").\n\nsum_of_values_test"
},
{
"path": "sequential/regex/.gitignore",
"chars": 137,
"preview": ".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\nreb"
},
{
"path": "sequential/regex/README.md",
"chars": 1359,
"preview": "regex\n=====\n\nCreate a function `match/2` that will receive a string and a regex (also a string), and return `true` if it"
},
{
"path": "sequential/regex/rebar.config",
"chars": 54,
"preview": "{profiles, [\n {ci, [{src_dirs, [\"solution\"]}]}\n]}.\n"
},
{
"path": "sequential/regex/solution/regex.app.src",
"chars": 218,
"preview": "{application, regex,\n [{description, \"simple regex engine\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {applications,\n ["
},
{
"path": "sequential/regex/solution/regex.erl",
"chars": 1255,
"preview": "-module(regex).\n-export([match/2]).\n\nmatch([], _Regex) -> \n false;\nmatch(_String, []) -> \n false;\nmatch(String, [$"
},
{
"path": "sequential/regex/src/regex.app.src",
"chars": 218,
"preview": "{application, regex,\n [{description, \"simple regex engine\"},\n {vsn, \"0.1.0\"},\n {registered, []},\n {applications,\n ["
},
{
"path": "sequential/regex/src/regex.erl",
"chars": 92,
"preview": "-module(regex).\n\n-export([match/2]).\n\nmatch(_String, _Regex) ->\n put_your_solution_here.\n"
},
{
"path": "sequential/regex/test/regex_SUITE.erl",
"chars": 189,
"preview": "-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"
},
{
"path": "sequential/regex/test/regex_test.erl",
"chars": 492,
"preview": "-module(regex_test).\n-include_lib(\"eunit/include/eunit.hrl\").\n\nmatches_characters_test() -> \n ?assert(regex:match(\"a\""
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the lambdaclass/erlings GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 198 files (107.2 KB), approximately 38.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.