From 3a6d379c245248768c60e9ffdcd1f6a2922ec409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Fernandes?= Date: Wed, 4 Sep 2024 13:36:42 -0300 Subject: [PATCH] [gleam] complete circular-buffer --- gleam/circular-buffer/.gitignore | 4 + gleam/circular-buffer/HELP.md | 32 +++++ gleam/circular-buffer/README.md | 73 +++++++++++ gleam/circular-buffer/gleam.toml | 12 ++ gleam/circular-buffer/manifest.toml | 28 ++++ .../circular-buffer/src/circular_buffer.gleam | 35 +++++ .../test/circular_buffer_test.gleam | 120 ++++++++++++++++++ 7 files changed, 304 insertions(+) create mode 100644 gleam/circular-buffer/.gitignore create mode 100644 gleam/circular-buffer/HELP.md create mode 100644 gleam/circular-buffer/README.md create mode 100644 gleam/circular-buffer/gleam.toml create mode 100644 gleam/circular-buffer/manifest.toml create mode 100644 gleam/circular-buffer/src/circular_buffer.gleam create mode 100644 gleam/circular-buffer/test/circular_buffer_test.gleam diff --git a/gleam/circular-buffer/.gitignore b/gleam/circular-buffer/.gitignore new file mode 100644 index 0000000..170cca9 --- /dev/null +++ b/gleam/circular-buffer/.gitignore @@ -0,0 +1,4 @@ +*.beam +*.ez +build +erl_crash.dump diff --git a/gleam/circular-buffer/HELP.md b/gleam/circular-buffer/HELP.md new file mode 100644 index 0000000..1292fcb --- /dev/null +++ b/gleam/circular-buffer/HELP.md @@ -0,0 +1,32 @@ +# Help + +## Running the tests + +To run the tests, run the command `gleam test` from within the exercise directory. + +## Submitting your solution + +You can submit your solution using the `exercism submit src/circular_buffer.gleam` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Gleam track's documentation](https://exercism.org/docs/tracks/gleam) +- The [Gleam track's programming category on the forum](https://forum.exercism.org/c/programming/gleam) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +To get help if you're having trouble, you can use one of the following resources: + +- [gleam.run](https://gleam.run/documentation/) is the gleam official documentation. +- [Discord](https://discord.gg/Fm8Pwmy) is the discord channel. +- [StackOverflow](https://stackoverflow.com/questions/tagged/gleam) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions. \ No newline at end of file diff --git a/gleam/circular-buffer/README.md b/gleam/circular-buffer/README.md new file mode 100644 index 0000000..609e0c0 --- /dev/null +++ b/gleam/circular-buffer/README.md @@ -0,0 +1,73 @@ +# Circular Buffer + +Welcome to Circular Buffer on Exercism's Gleam Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +A circular buffer, cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. + +A circular buffer first starts empty and of some predefined length. +For example, this is a 7-element buffer: + +```text +[ ][ ][ ][ ][ ][ ][ ] +``` + +Assume that a 1 is written into the middle of the buffer (exact starting location does not matter in a circular buffer): + +```text +[ ][ ][ ][1][ ][ ][ ] +``` + +Then assume that two more elements are added — 2 & 3 — which get appended after the 1: + +```text +[ ][ ][ ][1][2][3][ ] +``` + +If two elements are then removed from the buffer, the oldest values inside the buffer are removed. +The two elements removed, in this case, are 1 & 2, leaving the buffer with just a 3: + +```text +[ ][ ][ ][ ][ ][3][ ] +``` + +If the buffer has 7 elements then it is completely full: + +```text +[5][6][7][8][9][3][4] +``` + +When the buffer is full an error will be raised, alerting the client that further writes are blocked until a slot becomes free. + +When the buffer is full, the client can opt to overwrite the oldest data with a forced write. +In this case, two more elements — A & B — are added and they overwrite the 3 & 4: + +```text +[5][6][7][8][9][A][B] +``` + +3 & 4 have been replaced by A & B making 5 now the oldest data in the buffer. +Finally, if two elements are removed then what would be returned is 5 & 6 yielding the buffer: + +```text +[ ][ ][7][8][9][A][B] +``` + +Because there is space available, if the client again uses overwrite to store C & D then the space where 5 & 6 were stored previously will be used not the location of 7 & 8. +7 is still the oldest element and the buffer is once again full. + +```text +[C][D][7][8][9][A][B] +``` + +## Source + +### Created by + +- @lpil + +### Based on + +Wikipedia - https://en.wikipedia.org/wiki/Circular_buffer \ No newline at end of file diff --git a/gleam/circular-buffer/gleam.toml b/gleam/circular-buffer/gleam.toml new file mode 100644 index 0000000..dfafa2c --- /dev/null +++ b/gleam/circular-buffer/gleam.toml @@ -0,0 +1,12 @@ +name = "circular_buffer" +version = "0.1.0" + +[dependencies] +gleam_bitwise = "~> 1.2" +gleam_otp = "~> 0.7 or ~> 1.0" +gleam_stdlib = "~> 0.32 or ~> 1.0" +simplifile = "~> 1.0" +gleam_erlang = ">= 0.25.0 and < 1.0.0" + +[dev-dependencies] +exercism_test_runner = "~> 1.4" diff --git a/gleam/circular-buffer/manifest.toml b/gleam/circular-buffer/manifest.toml new file mode 100644 index 0000000..3d13c8c --- /dev/null +++ b/gleam/circular-buffer/manifest.toml @@ -0,0 +1,28 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, + { name = "exercism_test_runner", version = "1.8.0", build_tools = ["gleam"], requirements = ["argv", "gap", "glance", "gleam_community_ansi", "gleam_erlang", "gleam_json", "gleam_stdlib", "simplifile"], otp_app = "exercism_test_runner", source = "hex", outer_checksum = "B944D89A9D049897DF28C63D595D89CB54D8C407D06EFFCE4CDA8C3EC1C9F51E" }, + { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, + { name = "gap", version = "1.1.3", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib"], otp_app = "gap", source = "hex", outer_checksum = "6EF5E3B523FDFBC317E9EA28D5163EE04744A97C007106F90207569789612291" }, + { name = "glance", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "8F3314D27773B7C3B9FB58D8C02C634290422CE531988C0394FA0DF8676B964D" }, + { name = "gleam_bitwise", version = "1.3.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_bitwise", source = "hex", outer_checksum = "B36E1D3188D7F594C7FD4F43D0D2CE17561DE896202017548578B16FE1FE9EFC" }, + { name = "gleam_community_ansi", version = "1.4.1", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "4CD513FC62523053E62ED7BAC2F36136EC17D6A8942728250A9A00A15E340E4B" }, + { name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" }, + { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, + { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, + { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, + { name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" }, + { name = "glexer", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glexer", source = "hex", outer_checksum = "BD477AD657C2B637FEF75F2405FAEFFA533F277A74EF1A5E17B55B1178C228FB" }, + { name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" }, + { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, +] + +[requirements] +exercism_test_runner = { version = "~> 1.4" } +gleam_bitwise = { version = "~> 1.2" } +gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } +gleam_otp = { version = "~> 0.7 or ~> 1.0" } +gleam_stdlib = { version = "~> 0.32 or ~> 1.0" } +simplifile = { version = "~> 1.0" } diff --git a/gleam/circular-buffer/src/circular_buffer.gleam b/gleam/circular-buffer/src/circular_buffer.gleam new file mode 100644 index 0000000..24fe417 --- /dev/null +++ b/gleam/circular-buffer/src/circular_buffer.gleam @@ -0,0 +1,35 @@ +import gleam/list +import gleam/result + +pub opaque type CircularBuffer(t) { + CircularBuffer(cap: Int, items: List(t)) +} + +pub fn new(capacity: Int) -> CircularBuffer(t) { + CircularBuffer(capacity, []) +} + +pub fn read(buf: CircularBuffer(t)) -> Result(#(t, CircularBuffer(t)), Nil) { + case buf.items { + [first, ..rest] -> Ok(#(first, CircularBuffer(buf.cap, rest))) + _ -> Error(Nil) + } +} + +pub fn write(buf: CircularBuffer(t), item: t) -> Result(CircularBuffer(t), Nil) { + case list.length(buf.items) >= buf.cap { + True -> Error(Nil) + False -> Ok(CircularBuffer(buf.cap, list.append(buf.items, [item]))) + } +} + +pub fn overwrite(buf: CircularBuffer(t), item: t) -> CircularBuffer(t) { + use <- result.lazy_unwrap(write(buf, item)) + + let items = list.drop(buf.items, 1) |> list.append([item]) + CircularBuffer(buf.cap, items) +} + +pub fn clear(buf: CircularBuffer(t)) -> CircularBuffer(t) { + CircularBuffer(buf.cap, []) +} diff --git a/gleam/circular-buffer/test/circular_buffer_test.gleam b/gleam/circular-buffer/test/circular_buffer_test.gleam new file mode 100644 index 0000000..de847b3 --- /dev/null +++ b/gleam/circular-buffer/test/circular_buffer_test.gleam @@ -0,0 +1,120 @@ +import circular_buffer +import exercism/test_runner + +pub fn main() { + test_runner.main() +} + +pub fn reading_an_empty_buffer_should_fail_test() { + let buffer = circular_buffer.new(1) + let assert Error(Nil) = circular_buffer.read(buffer) +} + +pub fn can_read_an_item_just_written_test() { + let buffer = circular_buffer.new(1) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let assert Ok(#(1, _)) = circular_buffer.read(buffer) +} + +pub fn each_item_may_only_be_read_once_test() { + let buffer = circular_buffer.new(1) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let assert Ok(#(1, buffer)) = circular_buffer.read(buffer) + let assert Error(Nil) = circular_buffer.read(buffer) +} + +pub fn items_are_read_in_the_order_they_are_written_test() { + let buffer = circular_buffer.new(2) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let assert Ok(buffer) = circular_buffer.write(buffer, 2) + let assert Ok(#(1, buffer)) = circular_buffer.read(buffer) + let assert Ok(#(2, _)) = circular_buffer.read(buffer) +} + +pub fn full_buffer_cant_be_written_to_test() { + let buffer = circular_buffer.new(1) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let assert Error(Nil) = circular_buffer.write(buffer, 2) +} + +pub fn a_read_frees_up_capacity_for_another_write_test() { + let buffer = circular_buffer.new(1) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let assert Ok(#(1, buffer)) = circular_buffer.read(buffer) + let assert Ok(buffer) = circular_buffer.write(buffer, 2) + let assert Ok(#(2, _)) = circular_buffer.read(buffer) +} + +pub fn read_position_is_maintained_even_across_multiple_writes_test() { + let buffer = circular_buffer.new(3) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let assert Ok(buffer) = circular_buffer.write(buffer, 2) + let assert Ok(#(1, buffer)) = circular_buffer.read(buffer) + let assert Ok(buffer) = circular_buffer.write(buffer, 3) + let assert Ok(#(2, buffer)) = circular_buffer.read(buffer) + let assert Ok(#(3, _)) = circular_buffer.read(buffer) +} + +pub fn items_cleared_out_of_buffer_cant_be_read_test() { + let buffer = circular_buffer.new(1) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let buffer = circular_buffer.clear(buffer) + let assert Error(Nil) = circular_buffer.read(buffer) +} + +pub fn clear_frees_up_capacity_for_another_write_test() { + let buffer = circular_buffer.new(1) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let buffer = circular_buffer.clear(buffer) + let assert Ok(buffer) = circular_buffer.write(buffer, 2) + let assert Ok(#(2, _)) = circular_buffer.read(buffer) +} + +pub fn clear_does_nothing_on_empty_buffer_test() { + let buffer = circular_buffer.new(1) + let buffer = circular_buffer.clear(buffer) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let assert Ok(#(1, _)) = circular_buffer.read(buffer) +} + +pub fn overwrite_acts_like_write_on_non_full_buffer_test() { + let buffer = circular_buffer.new(2) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let buffer = circular_buffer.overwrite(buffer, 2) + let assert Ok(#(1, buffer)) = circular_buffer.read(buffer) + let assert Ok(#(2, _)) = circular_buffer.read(buffer) +} + +pub fn overwrite_replaces_the_oldest_item_on_full_buffer_test() { + let buffer = circular_buffer.new(2) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let assert Ok(buffer) = circular_buffer.write(buffer, 2) + let buffer = circular_buffer.overwrite(buffer, 3) + let assert Ok(#(2, buffer)) = circular_buffer.read(buffer) + let assert Ok(#(3, _)) = circular_buffer.read(buffer) +} + +pub fn overwrite_replaces_the_oldest_item_remaining_in_buffer_following_a_read_test() { + let buffer = circular_buffer.new(3) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let assert Ok(buffer) = circular_buffer.write(buffer, 2) + let assert Ok(buffer) = circular_buffer.write(buffer, 3) + let assert Ok(#(1, buffer)) = circular_buffer.read(buffer) + let assert Ok(buffer) = circular_buffer.write(buffer, 4) + let buffer = circular_buffer.overwrite(buffer, 5) + let assert Ok(#(3, buffer)) = circular_buffer.read(buffer) + let assert Ok(#(4, buffer)) = circular_buffer.read(buffer) + let assert Ok(#(5, _)) = circular_buffer.read(buffer) +} + +pub fn initial_clear_does_not_affect_wrapping_around_test() { + let buffer = circular_buffer.new(2) + let buffer = circular_buffer.clear(buffer) + let assert Ok(buffer) = circular_buffer.write(buffer, 1) + let assert Ok(buffer) = circular_buffer.write(buffer, 2) + let buffer = circular_buffer.overwrite(buffer, 3) + let buffer = circular_buffer.overwrite(buffer, 4) + let assert Ok(#(3, buffer)) = circular_buffer.read(buffer) + let assert Ok(#(4, buffer)) = circular_buffer.read(buffer) + let assert Error(_) = circular_buffer.read(buffer) +}