# `Tempo.Network`
[🔗](https://github.com/elixir-tempo/tempo_network/blob/v1.0.0/lib/tempo/network.ex#L1)

A chronological network — a set of time-periods together with the
sequences and relations that constrain them.

This is the top-level model of the ChronoLog scheme (Levy et al.
2020). A network is built incrementally and then handed to
`Tempo.Network.Solver` to check consistency or to propagate the
constraints to every period's narrowest bounds.

A network holds:

* `periods` — a map of `id => t:Tempo.Network.TimePeriod.t/0`;

* `sequences` — ordered lists of period ids with no gaps between
  consecutive members (`end(pᵢ) = start(pᵢ₊₁)`);

* `relations` — `t:Tempo.Network.Relation.t/0` constraints between
  pairs of periods;

* `errors` — what the builders could not add, in order.

The primary public API is `new/0`, `add_period/2,3`, `add_sequence/2`,
and `add_relation/4,5`. The builders never raise: an option they do
not take, or a value they cannot read, is recorded in `errors`, and
every `Tempo.Network.Solver` function returns the first of them as
`{:error, reason}` (its predicates raise it).

# `t`

```elixir
@type t() :: %Tempo.Network{
  errors: [Exception.t()],
  periods: %{optional(term()) =&gt; Tempo.Network.TimePeriod.t()},
  relations: [Tempo.Network.Relation.t()],
  sequences: [[term()]]
}
```

# `add_period`

```elixir
@spec add_period(t(), Tempo.Network.TimePeriod.t()) :: t()
```

Add a time-period to the network.

### Arguments

* `network` is the network to extend.

* `period` is a `t:Tempo.Network.TimePeriod.t/0`.

### Returns

* the network with `period` added (replacing any period of the same
  id), or with an error recorded when `period` is not a time-period.

### Examples

    iex> {:ok, period} = Tempo.Network.TimePeriod.new(:k1, name: "King 1")
    iex> network =
    ...>   Tempo.Network.new()
    ...>   |> Tempo.Network.add_period(period)
    iex> Map.keys(network.periods)
    [:k1]

# `add_period`

```elixir
@spec add_period(t(), term(), keyword()) :: t()
```

Build a time-period from `id` and `options` and add it to the network.

A convenience wrapping `Tempo.Network.TimePeriod.new/2` and
`add_period/2`.

### Arguments

* `network` is the network to extend.

* `id` is any term uniquely identifying the period.

* `options` is a keyword list of options.

### Options

* The options of `Tempo.Network.TimePeriod.new/2`: `:name`, `:from`,
  `:to`, `:duration` and `:metadata`.

### Returns

* the network with the new period added, or with the error
  `Tempo.Network.TimePeriod.new/2` returns recorded.

### Examples

    iex> network =
    ...>   Tempo.Network.new()
    ...>   |> Tempo.Network.add_period(:k1, from: {:not_before, ~o"1200Y"})
    iex> network.periods[:k1].earliest_start
    ~o"1200Y"

# `add_relation`

```elixir
@spec add_relation(
  t(),
  Tempo.Network.Relation.relation_type(),
  term(),
  term(),
  keyword()
) :: t()
```

Add a chronological relation between two periods.

### Arguments

* `network` is the network to extend.

* `type` is a relation type (see `Tempo.Network.Relation`).

* `from` and `to` are the ids of the related periods, read as
  "`from` *type* `to`".

* `options` is a keyword list of options.

### Options

* `:metadata` is an arbitrary map carried with the relation.

### Returns

* the network with the relation added, or with an error recorded for
  a type `Tempo.Network.Relation` does not define or an option it
  does not take.

### Examples

    iex> network =
    ...>   Tempo.Network.new()
    ...>   |> Tempo.Network.add_relation(:included_in, :s1, :k1)
    iex> [relation] = network.relations
    iex> {relation.type, relation.from, relation.to}
    {:included_in, :s1, :k1}

# `add_sequence`

```elixir
@spec add_sequence(t(), [term()]) :: t()
```

Add a gap-free sequence of periods.

The periods, given by id in chronological order, are consecutive:
each one ends exactly where the next begins. Normalisation expands a
sequence into `immediately_precedes` constraints between adjacent
members.

### Arguments

* `network` is the network to extend.

* `period_ids` is an ordered list of period ids.

### Returns

* the network with the sequence recorded, or with an error recorded
  when `period_ids` is not a list.

### Examples

    iex> network =
    ...>   Tempo.Network.new()
    ...>   |> Tempo.Network.add_sequence([:k1, :k2, :k3])
    iex> network.sequences
    [[:k1, :k2, :k3]]

# `new`

An empty network.

### Returns

* an empty `t:Tempo.Network.t/0`.

### Examples

    iex> network = Tempo.Network.new()
    iex> {map_size(network.periods), network.sequences, network.relations}
    {0, [], []}

# `period_ids`

```elixir
@spec period_ids(t()) :: [term()]
```

The list of all period ids referenced by the network — those with a
registered period plus any named only by a sequence or relation.

### Arguments

* `network` is a `t:Tempo.Network.t/0`.

### Returns

* a sorted-by-insertion list of unique period ids.

### Examples

    iex> network =
    ...>   Tempo.Network.new()
    ...>   |> Tempo.Network.add_period(:k1, [])
    ...>   |> Tempo.Network.add_relation(:before, :k1, :k2)
    iex> Enum.sort(Tempo.Network.period_ids(network))
    [:k1, :k2]

---

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