zinzen 0.3.1

Algorithm for auto-scheduling time-constrained tasks on a timeline
## ZinZen algorithm

This document contains a high-level overview of the scheduling algorithm.  

### 1) The Calendar is created

`Calendar::new` allocates an index space of fixed-size **slots** running from the
start to the end date, plus a one-day buffer at each end. All time↔index
conversion lives in `models/time_grid.rs` (`SLOTS_PER_HOUR`, `SLOTS_PER_DAY`,
`datetime_at`, `index_at`, ...); today one slot is one hour.

The Calendar is stored as an ordered `Vec<CalendarInterval>`. Each interval is
either `Claimable(HashSet<activity_index>)` (activities that could still take
that span) or `Occupied(activity_index, goal_id)`.

### 2) Activities are created from Goals/Budgets
Each Goal (simple or budget) is translated to one or more `Activity`s by
`activity_generator.rs`. Each Activity carries the set of `compatible_intervals`
that satisfy its Goal/Budget constraints, a `min_block_size`, a `duration_left`,
and a cached `flex`.

After this point the `activity_placer` knows nothing about dates/times — it
reasons purely about integer indices, block sizes and interval lengths. It
picks an Activity and calls `Calendar::register`, which splits the affected
intervals and records the claim; conflict counts are derived from how many
activities claim an interval.

### 3) Simple Goal Activities are scheduled
```
loop {
  Calculate flex of each Activity.
  If there is an Activity with flex == 1, schedule that,
  else schedule the Activity with the highest flexibility
    in the place with the least conflict with other Activities.
}
```

### 4) Budget Goal Activities are calculated

Same loop as in 3, run for the min-day, min-week and top-up-week budget phases.

### 5) Calendar print
`Calendar::print_new` consolidates intervals by claiming activity, splits them
on day boundaries (`SLOTS_PER_DAY`), and groups sequential slots occupied by the
same Activity into a `Task`. The whole pipeline (`run_scheduler`) returns
`Result<FinalTasks, SchedulerError>`; invalid input is reported as a typed error
rather than a panic.


## Why this algorithm?
If you'r not convinced this algorithm is good - please suggest a better one. I'm all ears!  
Here's a short explanation / test case to show that giving priority to least flexible Steps is wrong:     
<img src="/documentation/functional/the-wrong-way-of-scheduling.png" alt="The-wrong-way" width="800"/>  

To make it extra clear, here is the correct way of scheduling:
<img src="/documentation/functional/the-correct-way-of-scheduling.png" alt="The-correct-way" width="800"/>