README.md

# aonyx_graph

[![Package Version](https://img.shields.io/hexpm/v/aonyx_graph)](https://hex.pm/packages/aonyx_graph)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/aonyx_graph/)

A functional graph library for Gleam.

```sh
gleam add aonyx_graph
```

## Features

- Add, remove, and update nodes and edges
- List neighbors of a node, including incoming and outgoing edges
- Find paths between nodes

## Example

```gleam
import aonyx/graph
import aonyx/graph/dijkstra
import aonyx/graph/edge
import aonyx/graph/node
import gleam/list
import gleam/set
import gleeunit/should

pub fn main() {
  creating_and_modifying_a_graph()
  |> path_finding()
}

fn creating_and_modifying_a_graph() {
  let g = graph.new()

  let g =
    [
      node.new("A"),
      node.new("B"),
      node.new("C"),
      node.new("D") |> node.with_value("Some value"),
      // you can also add nodes with incoming or outgoing edges.
      // the edges will be created automatically as well as the target nodes.
      node.new("E")
        |> node.with_incoming(["B"])
        |> node.with_outgoing(["F"]),
    ]
    |> list.fold(g, graph.insert_node)

  g |> graph.get_nodes() |> list.length() |> should.equal(6)

  let g =
    [
      edge.new("A", "B"),
      edge.new("B", "C"),
      edge.new("C", "A") |> edge.with_label("Some label"),
      edge.new("A", "D") |> edge.with_weight(0.5),
      edge.new("D", "F"),
      // you can also add edges to non-existing nodes.
      // the nodes will be created automatically.
      edge.new("G", "H"),
    ]
    |> list.fold(g, graph.insert_edge)

  g |> graph.get_edges() |> list.length() |> should.equal(8)
  g |> graph.get_nodes() |> list.length() |> should.equal(8)

  // the graph now looks like this:
  // ┌── C
  // ▼   ▲
  // A ► B ► E
  // ▼       ▼
  // D ────► F
  // 
  // G ► H   

  g
  |> graph.get_node("A")
  |> should.be_ok()
  |> node.get_neighbors_out()
  |> set.to_list()
  |> should.equal(["B", "D"])

  g
  |> graph.get_node("B")
  |> should.be_ok()
  |> node.get_neighbors_in()
  |> set.to_list()
  |> should.equal(["A"])

  g
  |> graph.get_node("B")
  |> should.be_ok()
  |> node.get_neighbors()
  |> set.to_list()
  |> should.equal(["A", "C", "E"])

  g
}

fn path_finding(g: graph.Graph(String, _, _)) {
  g
  |> dijkstra.find_path("A", "F")
  |> should.be_some()
  |> should.equal(["A", "D", "F"])

  g
  |> dijkstra.find_path("B", "F")
  |> should.be_some()
  |> should.equal(["B", "E", "F"])

  let g = g |> graph.remove_edge(edge.new("A", "D"))

  g
  |> dijkstra.find_path("A", "F")
  |> should.be_some()
  |> should.equal(["A", "B", "E", "F"])

  let g = g |> graph.remove_node(node.new("E"))

  g
  |> dijkstra.find_path("A", "F")
  |> should.be_none()

  let g =
    g
    |> graph.get_node("D")
    |> should.be_ok()
    |> node.with_incoming(["A"])
    |> node.with_outgoing(["G"])
    |> graph.insert_node(g, _)

  g
  |> dijkstra.find_path("A", "H")
  |> should.be_some()
  |> should.equal(["A", "D", "G", "H"])
}
```

## Development

```sh
gleam test # Run the tests
gleam run -m aonyx_graph_examples # Run the examples
```

## Pre-commit Hooks

This repository includes a pre-commit hook that automatically updates the code examples in this README with the latest content from the `test/aonyx_graph_examples.gleam` file.

To set up the hook in your local repository:

```sh
git config core.hooksPath .githooks
```

After setting this up, whenever you make a commit, the hook will automatically update the README.md with the latest code examples and include it in your commit.