Evidence Region Validator
Evidence Region Validator
A course-materials assistant answers questions about PDF handouts. Each answer cites the page region it relied on, so the reader can open the handout with that area highlighted. The citation is part of the model's output, so it is untrusted input. The expected document revision and the page count come from the document store, which the application trusts; they are never taken from the model.
Implement validateRegion(region, expectedRevision, pageCount). If every check passes, return a new object { documentRevision, pageIndex, box } built from the validated values. Any other field in region is dropped, and neither region nor its box is modified. If a check fails, throw an Error whose message is exactly the text in the table below.
Coordinate convention
pageIndexis zero-based: a document with 3 pages has pages 0, 1, and 2.boxis[x0, y0, x1, y1]in normalized page coordinates, with the origin at the upper-left corner of the unrotated page.xruns from 0 (left edge) to 1 (right edge), andyfrom 0 (top edge) to 1 (bottom edge).- A valid box has positive width and height and stays inside the page. Touching an edge is allowed, so
[0, 0, 1, 1]is the whole page.
Checks, in order
Run the checks in this order and throw at the first one that fails.
| Order | Message | Fails when |
|---|---|---|
| 1 | Invalid expected revision | expectedRevision is not a string, or is the empty string |
| 2 | Invalid page count | pageCount is not an integer of at least 1 (a boolean is not an integer) |
| 3 | Region must be an object | region is not a non-array object, for example null, an array, or a string |
| 4 | Wrong document revision | region.documentRevision is not exactly equal to expectedRevision (a missing revision is not equal) |
| 5 | Invalid page index | region.pageIndex is not an integer with 0 <= pageIndex < pageCount |
| 6 | Expected four coordinates | region.box is not an array of length 4 |
| 7 | Coordinates must be finite numbers | any of the four values is not a finite number: NaN, Infinity, true, "0.5", null, and so on (in Python: not an int or float that converts to a finite float) |
| 8 | Invalid normalized rectangle | the box does not satisfy both 0 <= x0 < x1 <= 1 and 0 <= y0 < y1 <= 1 |
Check 7 looks at all four values before check 8 runs, so [0.6, 0.2, 0.1, NaN] fails with the finite-number message even though its x range is also reversed.
Fixture
Every row uses expected revision "handout-v2" and page count 3.
| Row | Region revision | Page index | Box | Other input fields |
|---|---|---|---|---|
| R1 | "handout-v2" | 2 | [0.1, 0.2, 0.6, 0.25] | none |
| R2 | "handout-v1" | 2 | [0.1, 0.2, 0.6, 0.25] | none |
| R3 | "handout-v2" | 3 | [0.1, 0.2, 0.6, 0.25] | none |
| R4 | "handout-v2" | 0 | [0, 0, 1, 1] | none |
| R5 | "handout-v2" | 2 | [0.6, 0.2, 0.1, 0.25] | none |
| R6 | "handout-v2" | -1 | [0.1, 0.2, 0.6, 0.25] | none |
| R7 | "handout-v2" | 2 | [0.1, 0.2, 0.1, 0.25] | none |
| R8 | "handout-v2" | 2 | [0.1, NaN, 0.6, 0.25] | none |
| R9 | "handout-v2" | 2 | [0, 0, 1.2, 0.5] | none |
| R10 | "handout-v2" | true | [0.1, 0.2, 0.6, 0.25] | none |
| R11 | "handout-v2" | 2 | [true, 0.2, 0.6, 0.25] | none |
| R12 | "handout-v1" | 5 | [0.1, 0.2, 0.6, 0.25] | none |
| R13 | "handout-v2" | 2 | [0.1, 0.2, 0.6] | none |
| R14 | "handout-v2" | 1 | [0.25, 0.5, 0.75, 0.625] | label: "answer key", verified: true |
Work out each row's result from the checks above. The tests assert all fourteen: a returned record must match exactly, with no extra fields, and an error must carry the exact message of the first check that fails.
Examples
validateRegion(
{ documentRevision: 'notes-r3', pageIndex: 4, box: [0.5, 0.75, 1, 1], quote: 'loop invariant' },
'notes-r3',
5,
);
// { documentRevision: 'notes-r3', pageIndex: 4, box: [0.5, 0.75, 1, 1] }
// quote is dropped, and x1 = y1 = 1 is allowed because a box may touch the page edge.
validateRegion({ documentRevision: 'notes-r3', pageIndex: 5, box: [0.1, 0.2, 0.6, 0.25] }, 'notes-r3', 5);
// throws Error('Invalid page index'): a 5-page document has pages 0 to 4.
validateRegion({ documentRevision: 'handout-v2', pageIndex: 2, box: [0, 0, true, 0.5] }, 'handout-v2', 3);
// throws Error('Coordinates must be finite numbers'): true is not a number, even though true <= 1.Python
Implement validate_region(region, expected_revision, page_count) with the same checks, order, and messages, raising ValueError. region is a dict, and box may be a list or a tuple.
| JavaScript | Python |
|---|---|
expectedRevision (argument) | expected_revision |
pageCount (argument) | page_count |
documentRevision (input and result key) | document_revision |
pageIndex (input and result key) | page_index |
box (input and result key) | box |
boolis a subclass ofintin Python, soTrueandFalsemust be rejected explicitly as a page count, a page index, or a coordinate.- A page count or page index must be an
int. A coordinate may be anintor afloatthat converts to a finitefloat. - The returned
boxis always a newlistof fourfloatvalues, even when the input was a tuple or used0and1. - A huge
intsuch as10**400has nofloatvalue:float(10**400)andmath.isfinite(10**400)both raiseOverflowError. It fails check 7 withCoordinates must be finite numbers, never with a crash. (In JavaScript the same literal is alreadyInfinity, which fails the same check.)
Constraints
regioncomes from model output and can be any JSON value.expectedRevisionandpageCountcome from trusted metadata, but they are still checked first.- Do not modify
regionor itsbox.
Expected complexity
O(1) time and O(1) extra space: a fixed number of checks over four coordinates.
0 views
Solution
Hints
