diff --git a/Makefile b/Makefile index 4d18490..3a748a2 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,8 @@ SRC_MODULES = \ kruskal \ heap \ union_find \ - demo + demo \ + doc TARGETS = \ src_target @@ -36,6 +37,9 @@ src_target: $(SRC_MODULES:%=$(EBIN)/%.beam) $(EBIN)/%.beam: %.erl $(ERLC) $(ERLC_FLAGS) -o $(EBIN) $< +edoc: + @(./makedoc.rb) + dialyze: $(TARGETS) dialyzer -n -Wunmatched_returns $(EBIN)/*.beam diff --git a/doc/overview.edoc b/doc/overview.edoc new file mode 100644 index 0000000..8484b3d --- /dev/null +++ b/doc/overview.edoc @@ -0,0 +1,20 @@ +@author Aggelos Giantsios +@copyright 2013 Aggelos Giantsios +@title Welcome to erlang-algorithms! +@doc The goal of this project is to implement some useful algorithms and data structures in Erlang so as to help anyone who may need them. + +== Currently Implemented Data Structures == + + +== Currently Implemented Algorithms == + + diff --git a/makedoc.rb b/makedoc.rb new file mode 100755 index 0000000..a1bd669 --- /dev/null +++ b/makedoc.rb @@ -0,0 +1,5 @@ +#! /usr/bin/env ruby + +puts `erl -noinput -pa ebin/ -eval "doc:make_doc()" -s init stop` + + diff --git a/src/doc.erl b/src/doc.erl new file mode 100644 index 0000000..ed0edfa --- /dev/null +++ b/src/doc.erl @@ -0,0 +1,9 @@ +-module(doc). + +-compile(export_all). + +-spec make_doc() -> 'ok'. +make_doc() -> + Mods = ["graph.erl", "heap.erl"], + Fs = lists:map(fun(M) -> filename:absname("src/" ++ M) end, Mods), + edoc:files(Fs, [{dir, "doc"}]). diff --git a/src/graph.erl b/src/graph.erl index 82abfbb..73d83b8 100644 --- a/src/graph.erl +++ b/src/graph.erl @@ -1,43 +1,80 @@ %% +%% %CopyrightBegin% +%% %% Copyright © 2013 Aggelos Giantsios %% - %% 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. - %% -%% Directed / Undirected Graphs +%% %CopyrightEnd% %% -%% This module implements directed and undirected graphs that are either weighted or unweighted. -%% It is basically syntactic sugar for the digraph module with added support for undirected graphs. +%% @copyright 2013 Aggelos Giantsios +%% @author Aggelos Giantsios + +%% ============================================================================ +%% @doc Directed / Undirected Graphs +%% +%%

This module implements directed and undirected graphs that are either +%% weighted or unweighted.

+%% +%%

It is basically syntactic sugar for the digraph module with added +%% support for undirected graphs.

+%% +%%

How to use

+%%

In order to create a graph you must load it from a file. +%% The file that contains the graph must have the following format.

+%% +%% +%% +%%

For examples you can check the files in the test_data directory.

+%% -module(graph). -%% External Exports -export([new_graph/1, del_graph/1, vertices/1, edges/1, edge_weight/2, edges_with_weights/1, out_neighbours/2, num_of_vertices/1, num_of_edges/1, pprint/1]). -%% Exported Types -export_type([graph/0, vertex/0, edge/0]). -%% Types Declarations +%% +%% @type graph(). A directed or undirected graph. +%%

It is wrapper for a digraph with the extra information on its type.

+%% -record(graph, {type :: graphtype(), graph :: digraph()}). --type graph() :: #graph{}. --type vertex() :: non_neg_integer() | atom(). +-opaque graph() :: #graph{}. +-type vertex() :: non_neg_integer(). -type edge() :: {vertex(), vertex()}. -type graphtype() :: 'directed' | 'undirected'. -type weighttype() :: 'unweighted' | 'd' | 'f'. @@ -46,7 +83,7 @@ %% Exported Functions %% ========================================================== -%% Create a new graph from a file +%% @doc Create a new graph from a file -spec new_graph(file:name()) -> graph(). new_graph(File) -> @@ -62,26 +99,26 @@ new_graph(File) -> 'ok' = init_edges(G, M, IO, T, W), #graph{type=T, graph=G}. -%% Delete a graph +%% @doc Delete a graph -spec del_graph(graph()) -> 'true'. del_graph(G) -> digraph:delete(G#graph.graph). -%% Get the vertices of a graph +%% @doc Return a list of the vertices of a graph -spec vertices(graph()) -> [vertex()]. vertices(G) -> digraph:vertices(G#graph.graph). -%% Return the number of vertices in a graph +%% @doc Return the number of vertices in a graph -spec num_of_vertices(graph()) -> non_neg_integer(). num_of_vertices(G) -> Vs = vertices(G), length(Vs). -%% Get the edges of a graph +%% @doc Return a list of the edges of a graph -spec edges(graph()) -> [edge()]. edges(G) -> @@ -93,34 +130,34 @@ edges(G) -> remove_duplicate_edges(Es, []) end. -%% Return the number of edges in a graph +%% @doc Return the number of edges in a graph -spec num_of_edges(graph()) -> non_neg_integer(). num_of_edges(G) -> Es = edges(G), length(Es). -%% Get the weight of an edge +%% @doc Return the weight of an edge -spec edge_weight(graph(), edge()) -> term(). edge_weight(G, E) -> {E, _V1, _V2, W} = digraph:edge(G#graph.graph, E), W. -%% Get the edges of a graph along with their weights +%% @doc Return a list of the edges of a graph along with their weights -spec edges_with_weights(graph()) -> [{edge(), term()}]. edges_with_weights(G) -> Es = edges(G), lists:map(fun(E) -> {E, edge_weight(G, E)} end, Es). -%% Get the out neighbours of a vertex +%% @doc Return a list of the out neighbours of a vertex -spec out_neighbours(graph(), vertex()) -> [vertex()]. out_neighbours(G, V) -> digraph:out_neighbours(G#graph.graph, V). -%% Pretty print a graph +%% @doc Pretty print a graph -spec pprint(graph()) -> 'ok'. pprint(G) -> diff --git a/src/heap.erl b/src/heap.erl index 28b4a44..cdc16b4 100644 --- a/src/heap.erl +++ b/src/heap.erl @@ -1,48 +1,63 @@ %% +%% %CopyrightBegin% +%% %% Copyright © 2013 Aggelos Giantsios %% - %% 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. - %% -%% Min-Heap, Max-Heap, Priority Queue +%% %CopyrightEnd% %% +%% @copyright 2013 Aggelos Giantsios +%% @author Aggelos Giantsios + +%% ============================================================================ +%% @doc Min-Heap, Max-Heap for Priority Queues +%% +%% %% This module implements min-heaps and max-heaps for use in priority queues. -%% Each value in the heap is assosiated with a reference so that the user can change its priority in O(log n). +%% Each value in the heap is assosiated with a reference so that +%% the user can change its priority in O(log n). %% %% The implementation is based on ETS tables for the O(1) lookup time. %% It supports all the basic heap operations: -%% * min/1, max/1 in O(1) -%% * take_min/1, take_max/1 in O(log n) -%% * insert/2 in O(log n) -%% * update/3 in O(log n) -%% * from_list/2 in Θ(n) - +%% +%% +%%

In order to achieve the above complexities the heap needs to store +%% an extra tuple {Key, Reference} for every +%% Key stored. In addition, the size of the heap is +%% stored as a tuple {size, Size}.

+%% -module(heap). -%% External Exports -export([new/1, heap_size/1, is_empty/1, max/1, min/1, insert/2, delete/1, take_min/1, take_max/1, update/3, from_list/2, to_list/1]). -%% Exported Types --export_type([mode/0, heap/0]). +-export_type([heap/0]). -%% Types Declarations +%% +%% @type heap(). Min / Max Heap. +%% -record(heap, { mode :: mode(), htab :: ets:tab() @@ -55,7 +70,7 @@ %% External Exports %% ======================================================================= -%% Get a list of the terms in a heap() +%% @doc Returns a list of the terms in a heap. -spec to_list(heap()) -> [term()]. to_list(H) -> @@ -69,9 +84,9 @@ to_list(H) -> LL = lists:filter(M, L), lists:map(fun({_, {X, _}}) -> X end, LL). -%% ----------------------------------------------------------------------- -%% Creates an empty heap -%% ----------------------------------------------------------------------- +%% @doc Creates an empty heap. +%%

If M is max then it will be a max heap, +%% else if M is min it will be a min heap.

-spec new(mode()) -> heap(). new(M) when M =:= 'max'; M=:= 'min' -> @@ -81,35 +96,27 @@ new(M) when M =:= 'max'; M=:= 'min' -> new(_Mode) -> erlang:error('badarg'). -%% ----------------------------------------------------------------------- -%% Deletes a heap -%% ----------------------------------------------------------------------- +%% @doc Deletes a heap. -spec delete(heap()) -> 'true'. delete(H) -> ets:delete(H#heap.htab). -%% ----------------------------------------------------------------------- -%% Returns the number of elements the Heap contains -%% ----------------------------------------------------------------------- +%% @doc Returns the number of elements contained in a heap. -spec heap_size(heap()) -> non_neg_integer(). heap_size(H) -> [{'size', Len}] = ets:lookup(H#heap.htab, 'size'), Len. -%% ----------------------------------------------------------------------- -%% Checks whether Heap is an empty heap or not -%% ----------------------------------------------------------------------- +%% @doc Checks whether a heap is empty or not. -spec is_empty(heap()) -> boolean(). is_empty(H) -> heap_size(H) =:= 0. -%% ----------------------------------------------------------------------- -%% Returns the element of the heap with the maximum priority. -%% If Heap is a minimum priority heap, it returns {error, min_heap} -%% ----------------------------------------------------------------------- +%% @doc Returns the element of a max heap with the maximum priority. +%%

If it is a min heap, it returns {error, min_heap}.

-spec max(heap()) -> term() | {'error', 'min_heap' | 'empty_heap'}. max(H) when H#heap.mode =:= 'max' -> @@ -120,10 +127,8 @@ max(H) when H#heap.mode =:= 'max' -> max(_H) -> {'error', 'min_heap'}. -%% ----------------------------------------------------------------------- -%% Returns the element of the heap with the minimum priority. -%% If Heap is a maximum priority heap, it returns {error, max_heap} -%% ----------------------------------------------------------------------- +%% @doc Returns the element of a min heap with the minimum priority. +%%

If it is a max heap, it returns {error, max_heap}.

-spec min(heap()) -> term() | {'error', 'max_heap' | 'empty_heap'}. min(H) when H#heap.mode =:= 'min' -> @@ -134,10 +139,9 @@ min(H) when H#heap.mode =:= 'min' -> min(_H) -> {'error', 'max_heap'}. -%% ----------------------------------------------------------------------- -%% Add a new element to the Heap and returns a tuple with the element -%% and a reference so that one can change its priority -%% ----------------------------------------------------------------------- +%% @doc Add a new element to a heap. +%%

It returns a tuple with the element added and a reference +%% so that one can change its priority.

-spec insert(heap(), term()) -> refterm(). insert(H, X) -> @@ -152,9 +156,7 @@ insert(H, X) -> insert_loop(H, I, P), {X, Ref}. -%% ----------------------------------------------------------------------- -%% Removes and returns the max -%% ----------------------------------------------------------------------- +%% @doc Removes and returns the maximum priority element of a max heap. -spec take_max(heap()) -> term() | {'error', 'min_heap' | 'empty_heap'}. take_max(H) when H#heap.mode =:= 'max' -> @@ -162,20 +164,16 @@ take_max(H) when H#heap.mode =:= 'max' -> take_max(_H) -> {'error', 'min_heap'}. -%% ----------------------------------------------------------------------- -%% Removes and returns the min -%% ----------------------------------------------------------------------- +%% @doc Removes and returns the minimum priority element of a min heap. -spec take_min(heap()) -> term() | {'error', 'max_heap' | 'empty_heap'}. - + take_min(H) when H#heap.mode =:= 'min' -> pop(H); take_min(_H) -> {'error', 'max_heap'}. -%% ----------------------------------------------------------------------- %% Deletes and returns the element at the top of the heap %% and re-arranges the rest of the heap -%% ----------------------------------------------------------------------- -spec pop(heap()) -> term(). pop(H) -> @@ -199,10 +197,9 @@ pop(H) -> Head end. -%% ----------------------------------------------------------------------- -%% Changes the priority of the element referenced with Ref to Value -%% and then re-arranges the heap -%% ----------------------------------------------------------------------- +%% @doc Change the priority of an element. +%%

It changes the priority of the element referenced with +%% Ref to Value and then re-arranges the heap.

-spec update(heap(), reference(), term()) -> 'true'. update(H, Ref, X) -> @@ -220,9 +217,10 @@ update(H, Ref, X) -> 'true' end. -%% ----------------------------------------------------------------------- -%% Create a heap from a list of terms -%% ----------------------------------------------------------------------- +%% @doc Create a heap from a list of terms. +%%

It returns the heap and a list of tuples {Key, Ref} +%% where Key is the term that was added and Ref +%% is its reference (used to change its priority).

-spec from_list(mode(), [term()]) -> {heap(), [refterm()]}. from_list(M, L) when is_list(L), is_atom(M) ->