# `ExVrp.VehicleType`
[🔗](https://github.com/sephianl/ex_vrp/blob/v0.8.0/lib/ex_vrp/vehicle_type.ex#L1)

Represents a vehicle type in a VRP.

Vehicle types define the characteristics of vehicles in the fleet,
including capacity, costs, time windows, and depot assignments.

# `t`

```elixir
@type t() :: %ExVrp.VehicleType{
  capacity: [non_neg_integer()],
  end_depot: non_neg_integer(),
  fixed_cost: non_neg_integer(),
  forbidden_windows: [{non_neg_integer(), non_neg_integer()}],
  initial_load: [non_neg_integer()],
  max_distance: non_neg_integer() | :infinity,
  max_duration: non_neg_integer() | :infinity,
  max_reloads: non_neg_integer() | :infinity,
  name: String.t(),
  num_available: pos_integer(),
  overtime_start: non_neg_integer() | :infinity,
  profile: non_neg_integer(),
  reload_depots: [non_neg_integer()],
  shift_duration: non_neg_integer() | :infinity,
  start_depot: non_neg_integer(),
  start_late: non_neg_integer(),
  tw_early: non_neg_integer(),
  tw_late: non_neg_integer() | :infinity,
  unit_distance_cost: non_neg_integer(),
  unit_duration_cost: non_neg_integer(),
  unit_overtime_cost: non_neg_integer()
}
```

# `new`

```elixir
@spec new(keyword()) :: t()
```

Creates a new vehicle type.

## Required Options

- `:num_available` - Number of vehicles of this type available
- `:capacity` - List of capacity values per dimension

## Optional Options

- `:time_windows` - List of `{start, end}` tuples representing operating windows
  (default: `[{0, :infinity}]`). Overlapping/adjacent windows are merged automatically.
  Example: `[{0, 500}, {600, 1000}]` becomes `tw_early: 0, tw_late: 1000,
  forbidden_windows: [{500, 600}]`.
- `:start_depot` - Index of starting depot (default: `0`)
- `:end_depot` - Index of ending depot (default: `0`)
- `:fixed_cost` - Fixed cost for using this vehicle (default: `0`)
- `:shift_duration` - Maximum shift duration (default: `:infinity`)
- `:max_distance` - Maximum distance allowed (default: `:infinity`)
- `:unit_distance_cost` - Cost per unit distance (default: `1`)
- `:unit_duration_cost` - Cost per unit time (default: `0`)
- `:profile` - Index of distance/duration matrix to use (default: `0`)
- `:start_late` - Latest allowed start time (default: `0`)
- `:max_duration` - Hard maximum route duration, independent of `:shift_duration`
  but defaulting to it, i.e. a route may not run longer than its nominal shift
  unless you say otherwise. Measures **elapsed** time from route start to route
  end, so idle time between stops counts against it; it is not a cap on time
  worked. `new/1` resolves the default, so the struct always carries a concrete
  value
- `:unit_overtime_cost` - Cost per unit of overtime (default: `0`)
- `:overtime_start` - Clock time after which work counts as overtime, on the same
  axis as `:time_windows` (default: `:infinity`). When set, overtime is
  `max(0, route_end - overtime_start)` — time worked past the contracted end,
  regardless of how long the route itself took. When left at `:infinity`,
  overtime is `max(0, duration - shift_duration)` instead — which only ever
  exceeds zero if `:max_duration` was raised above `:shift_duration`.
- `:reload_depots` - List of depot indices where vehicle can reload (default: `[]`)
- `:max_reloads` - Maximum number of reloads per route (default: `:infinity`)
- `:initial_load` - Initial load per dimension (default: `[]`)
- `:name` - Vehicle type name (default: `""`)

## Raises

- `ArgumentError` if `:time_windows` contains invalid tuples (start >= end or negative start)
- `ArgumentError` if `:time_windows` is an empty list
- `ArgumentError` if legacy options `:tw_early`, `:tw_late`, or `:forbidden_windows` are passed

## Examples

    iex> vt = ExVrp.VehicleType.new(num_available: 3, capacity: [100, 50], time_windows: [{0, 28_800}])
    iex> {vt.num_available, vt.capacity, vt.tw_early, vt.tw_late}
    {3, [100, 50], 0, 28_800}

Gaps between windows become forbidden windows:

    iex> vt = ExVrp.VehicleType.new(num_available: 2, capacity: [100], time_windows: [{0, 500}, {600, 1000}])
    iex> {vt.tw_early, vt.tw_late, vt.forbidden_windows}
    {0, 1000, [{500, 600}]}

Passing the derived fields directly is an error:

    iex> ExVrp.VehicleType.new(num_available: 1, capacity: [10], tw_early: 5)
    ** (ArgumentError) [:tw_early] cannot be set directly, use :time_windows instead

---

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