# `Monty.Cmp`
[🔗](https://github.com/d-led/ex.tbp.monty/blob/v0.1.0/packages/ex_tbp_monty_core/lib/monty/cmp.ex#L1)

The cortical messaging protocol: one payload for observed states,
hypothesized states and goals.

Mirrors `tbp/monty/cmp.py`. Everything that crosses a module boundary in
Monty is a `Monty.Cmp.Message` (or a `Monty.Cmp.Goal`), which is what makes
arbitrary module configurations — `sm_to_lm_matrix`, `lm_to_lm_matrix`,
`lm_to_lm_vote_matrix` — expressible as wiring rather than code.

Two representations live here:

  * the structs (`Message`, `Goal`, `AttentionRegion`), used in the runtime;
  * the wire form, plain maps with string keys, used for persistence, the
    step inspector in the UI, and the simulator sidecar.

The wire form is deliberately close to Monty's own `ActionJSONEncoder` /
message dictionaries so a Python fixture can be compared field by field.

# `message`

```elixir
@type message() ::
  Monty.Cmp.Message.t() | Monty.Cmp.Goal.t() | Monty.Cmp.AttentionRegion.t()
```

Anything that can travel on the CMP.

# `decode`

```elixir
@spec decode(map()) :: {:ok, message()} | {:error, term()}
```

Reads a payload produced by `encode/1` back into a struct.

Validation is the same as construction: a payload that would not have been
accepted by `Message.new/1` is rejected here too.

# `encode`

```elixir
@spec encode(message()) :: map()
```

The wire form of a CMP payload.

Pose vectors keep their type: a `SurfacePoseVectors` and an
`OrientationMatrix` are both 3×3, and confusing them rotates a reference
frame by 90 degrees without any number looking wrong.

# `location_mean`

```elixir
@spec location_mean([Monty.Cmp.Message.t() | Monty.Cmp.Goal.t()]) :: [float()] | nil
```

Mean of the messages' locations, or `nil` when no message has a location.

Mirrors `cmp.location_mean`: a missing location is not zero, and the mean of
an empty list is not a point.

---

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