# `Yog.Builder.Labeled`
[🔗](https://github.com/code-shoily/yog_ex/blob/v0.98.5/lib/yog/builder/labeled.ex#L1)

Build graphs using arbitrary labels instead of integer IDs.

This module provides a convenient way to build graphs when your nodes are
naturally identified by strings, atoms, or other types, rather than integers.

## Example Usage

    # Build a graph with string labels
    builder = Yog.Builder.Labeled.directed()
      |> Yog.Builder.Labeled.add_edge("home", "work", 10)
      |> Yog.Builder.Labeled.add_edge("work", "gym", 5)

    # Convert to a Graph to use with algorithms
    graph = Yog.Builder.Labeled.to_graph(builder)

    # Get the node ID for a label
    {:ok, home_id} = Yog.Builder.Labeled.get_id(builder, "home")

    # Use with pathfinding
    case Yog.Pathfinding.Dijkstra.shortest_path(
      in: graph,
      from: home_id,
      to: gym_id,
      zero: 0,
      add: &Kernel.+/2,
      compare: &Integer.compare/2
    ) do
      {:ok, path} -> path
      _ -> :no_path
    end

## Batch Construction

For building from existing data, use the `from_list` functions:

    edges = [{"A", "B", 5}, {"B", "C", 3}, {"A", "C", 10}]
    builder = Yog.Builder.Labeled.from_list(:directed, edges)
    graph = Yog.Builder.Labeled.to_graph(builder)

# `label`

```elixir
@type label() :: term()
```

Any type can be used as a label

# `t`

```elixir
@type t() :: %Yog.Builder.Labeled{
  graph: Yog.graph(),
  kind: Yog.Model.graph_type(),
  label_to_id: %{required(label()) =&gt; Yog.node_id()},
  next_id: integer()
}
```

Labeled builder struct

# `add_edge`

```elixir
@spec add_edge(t(), label(), label(), term()) :: t()
```

Adds an edge between two labeled nodes with a weight.

If either node doesn't exist, it will be created automatically.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_edge("A", "B", 10)
    iex> {:ok, successors} = Yog.Builder.Labeled.successors(builder, "A")
    iex> successors
    [{"B", 10}]

# `add_node`

```elixir
@spec add_node(t(), label()) :: t()
```

Adds a node with the given label.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_node("Node A")
    iex> Yog.Builder.Labeled.all_labels(builder)
    ["Node A"]

# `add_simple_edge`

```elixir
@spec add_simple_edge(t(), label(), label()) :: t()
```

Adds a simple edge with weight 1 between two labeled nodes.

Unlike `add_unweighted_edge/3` which stores weight as nil, this stores weight as 1.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_simple_edge("A", "B")
    iex> is_struct(builder, Yog.Builder.Labeled)
    true

# `add_unweighted_edge`

```elixir
@spec add_unweighted_edge(t(), label(), label()) :: t()
```

Adds an unweighted edge between two labeled nodes.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_unweighted_edge("A", "B")
    iex> {:ok, [{"B", nil}]} = Yog.Builder.Labeled.successors(builder, "A")

# `all_labels`

```elixir
@spec all_labels(t()) :: [label()]
```

Returns all labels that have been added to the builder.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_node("A")
    ...> |> Yog.Builder.Labeled.add_node("B")
    iex> Yog.Builder.Labeled.all_labels(builder)
    ["A", "B"]

# `directed`

```elixir
@spec directed() :: t()
```

Creates a new labeled directed graph builder.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    iex> is_struct(builder, Yog.Builder.Labeled)
    true

# `edge_count`

```elixir
@spec edge_count(t()) :: non_neg_integer()
```

Returns the number of edges in the graph.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_edge("A", "B", 10)
    ...> |> Yog.Builder.Labeled.add_edge("B", "C", 5)
    iex> Yog.Builder.Labeled.edge_count(builder)
    2

# `ensure_node`

```elixir
@spec ensure_node(t(), label()) :: {t(), Yog.node_id()}
```

Gets or creates a node for the given label.

If a node with this label already exists, returns its existing ID.
If it doesn't exist, creates a new node and returns the new ID.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    iex> {_builder, id} = Yog.Builder.Labeled.ensure_node(builder, "A")
    iex> is_integer(id)
    true

# `from_list`

```elixir
@spec from_list(Yog.Model.graph_type(), [{label(), label(), term()}]) :: t()
```

Creates a builder from a list of labeled edges.

## Examples

    iex> edges = [{"A", "B", 5}, {"B", "C", 3}]
    iex> builder = Yog.Builder.Labeled.from_list(:directed, edges)
    iex> {:ok, [{"B", 5}]} = Yog.Builder.Labeled.successors(builder, "A")

# `from_unweighted_list`

```elixir
@spec from_unweighted_list(Yog.Model.graph_type(), [{label(), label()}]) :: t()
```

Creates a builder from a list of unweighted labeled edges.

## Examples

    iex> edges = [{"A", "B"}, {"B", "C"}]
    iex> builder = Yog.Builder.Labeled.from_unweighted_list(:directed, edges)
    iex> {:ok, [{"B", nil}]} = Yog.Builder.Labeled.successors(builder, "A")

# `get_id`

```elixir
@spec get_id(t(), label()) :: {:ok, Yog.node_id()} | {:error, nil}
```

Looks up the internal node ID for a given label.

Returns `{:ok, id}` if the label exists, `{:error, nil}` otherwise.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_node("A")
    iex> Yog.Builder.Labeled.get_id(builder, "A")
    {:ok, 0}

    iex> builder = Yog.Builder.Labeled.directed()
    iex> Yog.Builder.Labeled.get_id(builder, "NonExistent")
    {:error, nil}

# `get_label`

```elixir
@spec get_label(t(), Yog.node_id()) :: {:ok, label()} | {:error, nil}
```

Looks up the label for a given internal node ID.

Returns `{:ok, label}` if the ID exists, `{:error, nil}` otherwise.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_node("A")
    iex> {:ok, id} = Yog.Builder.Labeled.get_id(builder, "A")
    iex> Yog.Builder.Labeled.get_label(builder, id)
    {:ok, "A"}

    iex> builder = Yog.Builder.Labeled.directed()
    iex> Yog.Builder.Labeled.get_label(builder, 999)
    {:error, nil}

# `has_edge?`

```elixir
@spec has_edge?(t(), label(), label()) :: boolean()
```

Checks if an edge exists between two labeled nodes.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_edge("A", "B", 10)
    iex> Yog.Builder.Labeled.has_edge?(builder, "A", "B")
    true
    iex> Yog.Builder.Labeled.has_edge?(builder, "B", "A")
    false

# `has_label?`

```elixir
@spec has_label?(t(), label()) :: boolean()
```

Checks if a label has been registered in the builder.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_node("A")
    iex> Yog.Builder.Labeled.has_label?(builder, "A")
    true
    iex> Yog.Builder.Labeled.has_label?(builder, "B")
    false

# `new`

```elixir
@spec new(Yog.Model.graph_type()) :: t()
```

Creates a new labeled graph builder of the specified type.

## Examples

    iex> builder = Yog.Builder.Labeled.new(:directed)
    iex> is_struct(builder, Yog.Builder.Labeled)
    true

# `next_id`

```elixir
@spec next_id(t()) :: Yog.node_id()
```

Gets the next available node ID.

This is the ID that would be assigned to the next new node.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    iex> Yog.Builder.Labeled.next_id(builder)
    0
    iex> builder = Yog.Builder.Labeled.add_node(builder, "A")
    iex> Yog.Builder.Labeled.next_id(builder)
    1

# `node_count`

```elixir
@spec node_count(t()) :: non_neg_integer()
```

Returns the number of registered nodes.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_node("A")
    ...> |> Yog.Builder.Labeled.add_node("B")
    iex> Yog.Builder.Labeled.node_count(builder)
    2

# `predecessors`

```elixir
@spec predecessors(t(), label()) :: {:ok, [{label(), term()}]} | {:error, nil}
```

Gets the predecessors of a node by its label.

Returns `{:ok, edges}` where edges is a list of `{label, weight}` tuples,
or `{:error, nil}` if the label doesn't exist.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_edge("A", "B", 5)
    iex> Yog.Builder.Labeled.predecessors(builder, "B")
    {:ok, [{"A", 5}]}

# `successors`

```elixir
@spec successors(t(), label()) :: {:ok, [{label(), term()}]} | {:error, nil}
```

Gets the successors of a node by its label.

Returns `{:ok, edges}` where edges is a list of `{label, weight}` tuples,
or `{:error, nil}` if the label doesn't exist.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_edge("A", "B", 10)
    iex> Yog.Builder.Labeled.successors(builder, "A")
    {:ok, [{"B", 10}]}

# `to_graph`

```elixir
@spec to_graph(t()) :: Yog.graph()
```

Converts the builder to a standard Graph.

The resulting graph can be used with all Yog algorithms.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_edge("A", "B", 5)
    iex> graph = Yog.Builder.Labeled.to_graph(builder)
    iex> Yog.graph?(graph)
    true

# `to_registry`

```elixir
@spec to_registry(t()) :: %{required(label()) =&gt; Yog.node_id()}
```

Gets the label-to-ID registry as a map.

Returns a map where keys are labels and values are node IDs.

## Examples

    iex> builder = Yog.Builder.Labeled.directed()
    ...> |> Yog.Builder.Labeled.add_node("A")
    iex> registry = Yog.Builder.Labeled.to_registry(builder)
    iex> Map.get(registry, "A")
    0

# `undirected`

```elixir
@spec undirected() :: t()
```

Creates a new labeled undirected graph builder.

## Examples

    iex> builder = Yog.Builder.Labeled.undirected()
    iex> is_struct(builder, Yog.Builder.Labeled)
    true

---

*Consult [api-reference.md](api-reference.md) for complete listing*
