Group Document Bounding Boxes into Indentation Levels with a 0.01 Left Tolerance
Company: Reducto
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
A document parser returns the text blocks of a document as bounding boxes, listed from top to bottom. Each box has `left`, `top`, `width`, `height` and `page` fields. Group the boxes into indentation levels by their `left` coordinate, treating `left` values that differ by at most `0.01` as the same indentation, number the levels from left to right, and return the level number of every box in the original input order.
Only `left` determines the grouping: the other fields are not used, and boxes on different pages are grouped together. The function therefore receives just the `left` value of each box, in input order.
### Function Signature
```python
def group_indentation_levels(lefts: list[float]) -> list[int]:
```
### Rules
- Two boxes are *close* when their `left` values differ by at most `0.01`.
- Two boxes are in the same level exactly when they are connected by a chain of close boxes: box `a` is close to box `b`, `b` is close to `c`, and so on. Two boxes in the same level can therefore differ by more than `0.01` when boxes between them link them.
- Boxes with equal `left` values are always in the same level.
- Levels are numbered `1, 2, 3, ...` from left to right: every `left` in level `g` is smaller than every `left` in level `g + 1`.
- `answer[i]` is the level number of the box at index `i`. The first box need not be in level 1, and later boxes may return to an earlier level.
### Constraints
- `1 <= len(lefts) <= 10^5`
- `0.0 <= lefts[i] <= 1.0` (normalized page coordinates)
- No two values in `lefts` differ by an amount within `10^-9` of `0.01`, so floating-point rounding never decides whether two boxes are close.
- Every output value is an integer between `1` and `len(lefts)`.
### Examples
**Example 1**
```text
Input: lefts = [0.30, 0.10, 0.305, 0.105]
Output: [2, 1, 2, 1]
```
`0.10` and `0.105` differ by `0.005` and form level 1; `0.30` and `0.305` form level 2. The first box is not in the leftmost level.
**Example 2**
```text
Input: lefts = [0.20, 0.05, 0.12, 0.055, 0.125, 0.20, 0.048]
Output: [3, 1, 2, 1, 2, 3, 1]
```
The levels are `{0.048, 0.05, 0.055}`, `{0.12, 0.125}` and `{0.20, 0.20}`. Later boxes keep returning to earlier levels.
**Example 3**
```text
Input: lefts = [0.116, 0.100, 0.108, 0.300]
Output: [1, 1, 1, 2]
```
`0.100` and `0.116` differ by `0.016`, more than the tolerance, but each is within `0.01` of `0.108`, so the chain puts all three boxes in level 1.
Overview: Given the left coordinates of document bounding boxes listed from top to bottom, group the boxes into indentation levels using a 0.01 tolerance, number the levels from left to right, and return each box's level in input order. Tests tolerance-based grouping of floating-point values, chained closeness and mapping groups back to positions.
A document parser returns the text blocks of a document as bounding boxes, listed from top to bottom. Each box has `left`, `top`, `width`, `height` and `page` fields. Group the boxes into indentation levels by their `left` coordinate, treating `left` values that differ by at most `0.01` as the same indentation, number the levels from left to right, and return the level number of every box in the original input order.
Only `left` determines the grouping: the other fields are not used, and boxes on different pages are grouped together. The function therefore receives just the `left` value of each box, in input order, as the list `lefts`.
Implement `group_indentation_levels(lefts)`, which returns a list `answer` holding one integer per box.
### Rules
- Two boxes are *close* when their `left` values differ by at most `0.01`.
- Two boxes are in the same level exactly when they are connected by a chain of close boxes: box `a` is close to box `b`, `b` is close to `c`, and so on. Two boxes in the same level can therefore differ by more than `0.01` when boxes between them link them.
- Boxes with equal `left` values are always in the same level.
- Levels are numbered `1, 2, 3, ...` from left to right: every `left` in level `g` is smaller than every `left` in level `g + 1`.
- `answer[i]` is the level number of the box at index `i`. The first box need not be in level 1, and later boxes may return to an earlier level.
### Example 1
```text
Input: lefts = [0.30, 0.10, 0.305, 0.105]
Output: [2, 1, 2, 1]
```
`0.10` and `0.105` differ by `0.005` and form level 1; `0.30` and `0.305` form level 2. The first box is not in the leftmost level.
### Example 2
```text
Input: lefts = [0.116, 0.100, 0.108, 0.300]
Output: [1, 1, 1, 2]
```
`0.100` and `0.116` differ by `0.016`, more than the tolerance, but each is within `0.01` of `0.108`, so the chain puts all three boxes in level 1. `0.300` is not close to any of them and forms level 2.
### Constraints
- `1 <= len(lefts) <= 10^5`
- `0.0 <= lefts[i] <= 1.0` (normalized page coordinates)
- No two values in `lefts` differ by an amount within `10^-9` of `0.01`, so floating-point rounding never decides whether two boxes are close.
- Every output value is an integer between `1` and `len(lefts)`.
The inputs are double-precision floating-point numbers (Java `double[]`, C++ `std::vector<double>`). No output value can exceed `10^5`, so none exceeds `2^31 - 1` and a 32-bit integer suffices.
Constraints
- 1 <= len(lefts) <= 10^5
- 0.0 <= lefts[i] <= 1.0 (normalized page coordinates)
- No two values in lefts differ by an amount within 10^-9 of 0.01, so floating-point rounding never decides whether two boxes are close.
- Every output value is an integer between 1 and len(lefts).
Examples
Input: ([0.42],)
Expected Output: [1]
Explanation: Minimum valid input: a single box forms level 1.
Input: ([1.0, 0.0],)
Expected Output: [2, 1]
Explanation: Both coordinate bounds: 0.0 and 1.0 are 1.0 apart, so they are separate levels and the first box is in level 2.
Hints
- Two boxes can share a level even when their lefts differ by more than 0.01. What must lie between them for that to happen?
- Level numbers follow left-to-right position, not the order in which boxes appear in the input.
- Boxes with equal lefts always share a level, so a value that repeats later in the input returns to the level it already has.