commit 2c9de8cefdade730af1da8dcdf36f3e40df62e8e Author: Dominic Grimm Date: Sat Nov 20 13:14:36 2021 +0100 init diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..163eb75 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*.cr] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true diff --git a/.github/workflows/crystal.yml b/.github/workflows/crystal.yml new file mode 100644 index 0000000..ff8902c --- /dev/null +++ b/.github/workflows/crystal.yml @@ -0,0 +1,19 @@ +name: Crystal CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + container: + image: crystallang/crystal + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: shards install + - name: Run tests + run: crystal spec -v diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0bbd4a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/docs/ +/lib/ +/bin/ +/.shards/ +*.dwarf + +# Libraries don't need dependency lock +# Dependencies will be locked in applications that use them +/shard.lock diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..37977e4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 Dominic Grimm + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e71309d --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# lifo + +[![GitHub release](https://img.shields.io/github/release/grimmigerFuchs/lifo.svg)](https://github.com/grimmigerFuchs/lifo/releases) + +A library for working with LIFO like data structures (stacks, queues). + +## Installation + +1. Add the dependency to your `shard.yml`: + + ```yaml + dependencies: + stack: + github: grimmigerFuchs/lifo + ``` + +2. Run `shards install` + +## Usage + +```crystal +require "lifo" + +stack = Lifo::Stack(String).new(3) +stack.push("Foo") +stack.push("Bar") +stack.push("Baz") + +p! stack.pop +# stack.pop # => "Baz" +p! stack.pop +# stack.pop # => "Bar" +p! stack.peek +# stack.peek # => "Foo" + +queue = Lifo::Queue(String).new(3) +queue.enqueue("John Doe") +queue.enqueue("Jane Doe") + +p! queue.dequeue +# queue.dequeue # => "John Doe" +p! queue.peek +# queue.peek # => "Jane Doe" +``` + +## Contributing + +1. Fork it () +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create a new Pull Request + +## Contributors + +- [Dominic Grimm](https://github.com/grimmigerFuchs) - creator and maintainer diff --git a/shard.yml b/shard.yml new file mode 100644 index 0000000..7eeb3e9 --- /dev/null +++ b/shard.yml @@ -0,0 +1,10 @@ +name: lifo +description: A library for working with LIFO like data structures (stacks, queues). +version: 0.1.0 + +authors: + - Dominic Grimm + +crystal: 1.2.2 + +license: MIT diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr new file mode 100644 index 0000000..202da46 --- /dev/null +++ b/spec/spec_helper.cr @@ -0,0 +1,2 @@ +require "spec" +require "../src/lifo" diff --git a/spec/stack/queue.cr b/spec/stack/queue.cr new file mode 100644 index 0000000..c8c5248 --- /dev/null +++ b/spec/stack/queue.cr @@ -0,0 +1,104 @@ +describe Lifo::Queue do + describe "#initialize" do + it "sets capacity to -1 if none is given" do + queue = Lifo::Queue(Int32).new + queue.capacity.should eq -1 + end + end + + describe "#enqueue" do + it "enqueues to the queue" do + queue = Lifo::Queue(Int32).new + queue.enqueue(1) + queue.peek.should eq(1) + queue.enqueue(3) + queue.peek.should eq(1) + queue.enqueue(5) + queue.peek.should eq(1) + end + + it "returns queue size" do + queue = Lifo::Queue(Int32).new + queue.enqueue(1) + queue.enqueue(3) + queue.size.should eq(2) + end + + it "raises OverflowError if queue is full" do + queue = Lifo::Queue(Int32).new(0) + + expect_raises(Lifo::OverflowError) do + queue.enqueue(1) + end + end + + it "doesn't raise an error when queue is infinite" do + queue = Lifo::Queue(Int32).new + queue.enqueue(1) + end + end + + describe "#enqueue?" do + it "enqueues to the queue" do + queue = Lifo::Queue(Int32).new + queue.enqueue?(1) + queue.peek.should eq(1) + queue.enqueue?(3) + queue.peek.should eq(1) + queue.enqueue?(5) + queue.peek.should eq(1) + end + + it "returns queue size" do + queue = Lifo::Queue(Int32).new + queue.enqueue?(1) + queue.enqueue?(3) + queue.size.should eq(2) + end + + it "returns nil if error raised" do + queue = Lifo::Queue(Int32).new(0) + queue.enqueue?(1).should be_nil + end + end + + describe "#dequeue" do + it "dequeues from the queue" do + queue = Lifo::Queue(Int32).new + queue.enqueue(1) + queue.enqueue(3) + queue.peek.should eq(1) + queue.dequeue + queue.peek.should eq(3) + queue.dequeue + end + + it "returns dequeued element" do + queue = Lifo::Queue(Int32).new + queue.enqueue(1) + queue.enqueue(3) + queue.peek.should eq(1) + queue.dequeue.should eq(1) + queue.dequeue.should eq(3) + end + + it "raises UnderflowError if queue is empty" do + queue = Lifo::Queue(Int32).new(0) + expect_raises(Lifo::UnderflowError) do + queue.dequeue + end + end + end + + describe "#dequeue?" do + it "dequeues from the queue" do + queue = Lifo::Queue(Int32).new + queue.enqueue(1) + queue.enqueue(3) + queue.peek.should eq(1) + queue.dequeue? + queue.peek.should eq(3) + queue.dequeue? + end + end +end diff --git a/spec/stack/stack.cr b/spec/stack/stack.cr new file mode 100644 index 0000000..6725e0a --- /dev/null +++ b/spec/stack/stack.cr @@ -0,0 +1,165 @@ +describe Lifo::Stack do + describe "#initialize" do + it "sets capacity to -1 if none is given" do + stack = Lifo::Stack(Int32).new + stack.capacity.should eq -1 + end + end + + describe "#push" do + it "pushes to the stack" do + stack = Lifo::Stack(Int32).new + stack.push(1) + stack.peek.should eq(1) + stack.push(3) + stack.peek.should eq(3) + end + + it "returns stack size" do + stack = Lifo::Stack(Int32).new + stack.push(1) + stack.push(3) + stack.size.should eq(2) + end + + it "raises OverflowError if stack is full" do + stack = Lifo::Stack(Int32).new(0) + + expect_raises(Lifo::OverflowError) do + stack.push(1) + end + end + + it "doesn't raise an error when stack is infinite" do + stack = Lifo::Stack(Int32).new + stack.push(1) + end + end + + describe "#push?" do + it "pushes to the stack" do + stack = Lifo::Stack(Int32).new + stack.push?(1) + stack.peek.should eq(1) + stack.push?(3) + stack.peek.should eq(3) + end + + it "returns stack size" do + stack = Lifo::Stack(Int32).new + stack.push?(1) + stack.push?(3) + stack.size.should eq(2) + end + + it "returns nil if error raised" do + stack = Lifo::Stack(Int32).new(0) + stack.push?(1).should be_nil + end + end + + describe "#pop" do + it "pops from the stack" do + stack = Lifo::Stack(Int32).new + stack.push(1) + stack.push(3) + stack.peek.should eq(3) + stack.pop + stack.peek.should eq(1) + stack.pop + end + + it "returns popped element" do + stack = Lifo::Stack(Int32).new + stack.push(1) + stack.push(3) + stack.pop.should eq(3) + stack.pop.should eq(1) + end + + it "raises UnderflowError if stack is empty" do + stack = Lifo::Stack(Int32).new + expect_raises(Lifo::UnderflowError) do + stack.pop + end + end + end + + describe "#pop?" do + it "pops from the stack" do + stack = Lifo::Stack(Int32).new + stack.push(1) + stack.push(3) + stack.peek.should eq(3) + stack.pop? + stack.peek.should eq(1) + stack.pop? + end + + it "returns popped element" do + stack = Lifo::Stack(Int32).new + stack.push(1) + stack.push(3) + stack.peek.should eq(3) + stack.pop?.should eq(3) + stack.peek.should eq(1) + stack.pop?.should eq(1) + end + + it "returns nil if stack is empty" do + stack = Lifo::Stack(Int32).new + stack.pop?.should be_nil + end + end + + describe "#peek" do + it "returns top element" do + stack = Lifo::Stack(Int32).new + stack.push(2) + stack.peek.should eq(2) + end + + it "raises UnderflowError if stack is empty" do + stack = Lifo::Stack(Int32).new + expect_raises(Lifo::UnderflowError) do + stack.peek + end + end + end + + describe "#peek?" do + it "returns top element" do + stack = Lifo::Stack(Int32).new + stack.push(1) + stack.push(3) + stack.peek?.should eq(3) + end + + it "returns nil if stack is empty" do + stack = Lifo::Stack(Int32).new + stack.peek?.should be_nil + end + end + + describe "#size" do + it "returns stack size" do + stack = Lifo::Stack(Int32).new + stack.push(1) + stack.push(3) + stack.size.should eq(2) + end + end + + describe "#empty?" do + it "returns true if stack is empty" do + stack = Lifo::Stack(Int32).new + stack.empty?.should be_true + end + + it "returns false if stack is not empty" do + stack = Lifo::Stack(Int32).new + stack.push(1) + stack.empty?.should be_false + end + end +end diff --git a/spec/stack_spec.cr b/spec/stack_spec.cr new file mode 100644 index 0000000..82990b2 --- /dev/null +++ b/spec/stack_spec.cr @@ -0,0 +1,4 @@ +require "./spec_helper" + +require "./stack/stack" +require "./stack/queue" diff --git a/src/lifo.cr b/src/lifo.cr new file mode 100644 index 0000000..56f142b --- /dev/null +++ b/src/lifo.cr @@ -0,0 +1,5 @@ +require "./lifo/version" +require "./lifo/exception" +require "./lifo/lifolike" +require "./lifo/stack" +require "./lifo/queue" diff --git a/src/lifo/exception.cr b/src/lifo/exception.cr new file mode 100644 index 0000000..3cb4ffc --- /dev/null +++ b/src/lifo/exception.cr @@ -0,0 +1,7 @@ +module Lifo + class OverflowError < Exception + end + + class UnderflowError < Exception + end +end diff --git a/src/lifo/lifolike.cr b/src/lifo/lifolike.cr new file mode 100644 index 0000000..e843d34 --- /dev/null +++ b/src/lifo/lifolike.cr @@ -0,0 +1,20 @@ +module Lifo + abstract class LifoLike(T) + protected property data = [] of T + getter capacity + + def initialize(@capacity = -1) + if @capacity < -1 + raise ArgumentError.new("capacity must be >= -1") + end + end + + def size : Int32 + @data.size + end + + def empty? : Bool + size == 0 + end + end +end diff --git a/src/lifo/queue.cr b/src/lifo/queue.cr new file mode 100644 index 0000000..3a1ec66 --- /dev/null +++ b/src/lifo/queue.cr @@ -0,0 +1,60 @@ +module Lifo + class Queue(T) < LifoLike(T) + def initialize(capacity = -1) + super(capacity) + end + + def enqueue(e : T) : Int32 + if @capacity == -1 || size < @capacity + @data << e + + size + else + raise OverflowError.new + end + end + + def enqueue?(e : T) : Int32? + begin + enqueue(e) + rescue OverflowError + nil + end + end + + def dequeue : T + begin + res = @data.first + @data.shift + rescue Enumerable::EmptyError + raise UnderflowError.new + end + + res + end + + def dequeue? : T? + begin + dequeue + rescue UnderflowError + nil + end + end + + def peek : T + begin + @data.first + rescue IndexError + raise UnderflowError.new + end + end + + def peek? : T? + begin + peek + rescue UnderflowError + nil + end + end + end +end diff --git a/src/lifo/stack.cr b/src/lifo/stack.cr new file mode 100644 index 0000000..8c2a4e0 --- /dev/null +++ b/src/lifo/stack.cr @@ -0,0 +1,60 @@ +module Lifo + class Stack(T) < LifoLike(T) + def initialize(capacity = -1) + super(capacity) + end + + def push(e : T) : Int32 + if @capacity == -1 || size < @capacity + @data << e + + size + else + raise OverflowError.new + end + end + + def push?(e : T) : Int32? + begin + push(e) + rescue OverflowError + nil + end + end + + def pop : T + begin + res = @data.last + @data.pop + rescue IndexError + raise UnderflowError.new + end + + res + end + + def pop? : T? + begin + pop + rescue UnderflowError + nil + end + end + + def peek : T + begin + @data.last + rescue IndexError + raise UnderflowError.new + end + end + + def peek? : T? + begin + peek + rescue UnderflowError + nil + end + end + end +end diff --git a/src/lifo/version.cr b/src/lifo/version.cr new file mode 100644 index 0000000..75962f7 --- /dev/null +++ b/src/lifo/version.cr @@ -0,0 +1,3 @@ +module Lifo + VERSION = {{ `shards version`.stringify.chomp }} +end