diff --git a/.gitattributes b/.gitattributes index 6c9987b..0958615 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,3 +12,7 @@ *.woff2 binary *.jar binary *.mmdb binary + +# Stored renderer outputs are compared byte for byte: git must never rewrite them. +renderer/src/test/resources/compat-v1/** -text +renderer/src/test/resources/scene2/golden/** -text diff --git a/bridges/renderer/src/test/java/com/kingsrook/qbits/documentgeneration/renderer/ZplSceneOneCompatibilityTest.java b/bridges/renderer/src/test/java/com/kingsrook/qbits/documentgeneration/renderer/ZplSceneOneCompatibilityTest.java index d79770c..bfc6842 100644 --- a/bridges/renderer/src/test/java/com/kingsrook/qbits/documentgeneration/renderer/ZplSceneOneCompatibilityTest.java +++ b/bridges/renderer/src/test/java/com/kingsrook/qbits/documentgeneration/renderer/ZplSceneOneCompatibilityTest.java @@ -47,6 +47,12 @@ class ZplSceneOneCompatibilityTest { private static final Path CORPUS = Path.of("..", "..", "renderer", "src", "test", "resources", "compat-v1"); + /////////////////////////////////////////////////////////////////////////////////////////////// + // the SHA-256 of `SHA256SUMS` itself, the same value the renderer's test pins, so the // + // outputs and their sums cannot be regenerated together and still pass // + /////////////////////////////////////////////////////////////////////////////////////////////// + private static final String MANIFEST_SHA256 = "cb6ec01d084e9077f3b5f6c8f39ed2f1de1116d92697dbbf529557fc84da5b2a"; + /*************************************************************************** @@ -54,6 +60,7 @@ class ZplSceneOneCompatibilityTest ***************************************************************************/ static List outputs() throws Exception { + assertEquals(MANIFEST_SHA256, HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(CORPUS.resolve("SHA256SUMS")))), "The corpus manifest was regenerated or edited"); List lines = Files.readAllLines(CORPUS.resolve("SHA256SUMS"), StandardCharsets.US_ASCII); assertFalse(lines.isEmpty()); return (lines); diff --git a/bridges/renderer/src/test/java/com/kingsrook/qbits/documentgeneration/renderer/ZplSceneTwoPolicyTest.java b/bridges/renderer/src/test/java/com/kingsrook/qbits/documentgeneration/renderer/ZplSceneTwoPolicyTest.java new file mode 100644 index 0000000..7e8820a --- /dev/null +++ b/bridges/renderer/src/test/java/com/kingsrook/qbits/documentgeneration/renderer/ZplSceneTwoPolicyTest.java @@ -0,0 +1,105 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.documentgeneration.renderer; + + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Stream; +import com.fasterxml.jackson.databind.JsonNode; +import com.kingsrook.qbits.documentgeneration.GenerationPorts; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/******************************************************************************* + ** A version 2 label, as the host receives it: each golden label of the + ** renderer's `scene2` cases passes the unchanged host printer gate for its + ** stock at the resolution it was rendered for. A version 2 label is graphic + ** fields only, which the gate has always admitted. The goldens have one copy, + ** in the renderer module, where the renderer's own test proves this build + ** still emits them byte for byte. + *******************************************************************************/ +class ZplSceneTwoPolicyTest +{ + private static final Path SCENES = Path.of("..", "..", "renderer", "src", "test", "resources", "scene2"); + + + + /*************************************************************************** + ** The stored labels, named `-.zpl`. + ***************************************************************************/ + static List labels() throws Exception + { + try(Stream files = Files.list(SCENES.resolve("golden"))) + { + List labels = files.map(file -> file.getFileName().toString()).filter(name -> name.endsWith(".zpl")).sorted().toList(); + assertFalse(labels.isEmpty()); + return (labels); + } + } + + + + /*************************************************************************** + ** The gate accepts the label for the stock at its render target, and for no + ** other resolution: the page size in the label is the target's. + ***************************************************************************/ + @ParameterizedTest(name = "{0}") + @MethodSource("labels") + void aVersionTwoLabelPassesTheUnchangedHostPrinterGate(String label) throws Exception + { + assertTrue(label.matches("[a-z0-9-]+-(203|300|600)\\.zpl"), label); + String name = label.substring(0, label.length() - 8); + int dpi = Integer.parseInt(label.substring(label.length() - 7, label.length() - 4)); + byte[] bytes = Files.readAllBytes(SCENES.resolve("golden").resolve(label)); + JsonNode stock = RendererJson.JSON.readTree(Files.readAllBytes(SCENES.resolve("cases").resolve(name + ".json"))).path("stock"); + assertDoesNotThrow(() -> ZplOutputPolicy.validate(bytes, stock(stock, dpi)), label); + for(int other : List.of(203, 300, 600)) + { + if(other != dpi) + { + assertEquals("ZPL_STOCK_MISMATCH", assertThrows(IllegalArgumentException.class, () -> ZplOutputPolicy.validate(bytes, stock(stock, other))).getMessage()); + } + } + String text = new String(bytes, StandardCharsets.US_ASCII); + assertTrue(text.contains("^GFA,"), label); + assertFalse(text.contains("^GB") || text.contains("^A0") || text.contains("~"), label); + } + + + + /*************************************************************************** + ** The case's stock at a render target. + ***************************************************************************/ + private static GenerationPorts.Stock stock(JsonNode stock, int dpi) + { + return (new GenerationPorts.Stock(stock.path("widthMicrons").intValue(), stock.path("heightMicrons").intValue(), stock.path("topMicrons").intValue(), + stock.path("rightMicrons").intValue(), stock.path("bottomMicrons").intValue(), stock.path("leftMicrons").intValue(), dpi)); + } +} diff --git a/docs/design/LABEL-LAYOUT-FONTS-PREVIEW.md b/docs/design/LABEL-LAYOUT-FONTS-PREVIEW.md new file mode 100644 index 0000000..96325a9 --- /dev/null +++ b/docs/design/LABEL-LAYOUT-FONTS-PREVIEW.md @@ -0,0 +1,1030 @@ +# Design: great labels, barcodes, page layout and preview + +Status: draft for review, 2026-11-11. No code in this change. Owner decisions are in section 12. + +This document designs four things the document module lacks and the preview James asked for, then gives a work +plan that separate agents can build from. It covers `qbit-document-generation` (foundation, renderer and bridges) +and says what the neighbouring repositories and GG Forge must do. + +1. Layouts in millimetres, inches or points, so one design serves 203, 300 and 600 dpi. +2. Alignment and fit rules for text and barcodes, so a long value shrinks or wraps by a stated rule. +3. A preview inside the QBit that shows the exact bitmap before anything prints. +4. Fonts that work in every environment: a bundled default set, plus uploads that need no object storage. + +## 0. What exists today + +Every statement here was read in code on 2026-11-11. Paths are relative to this repository unless a repository is +named. "Not verified" marks what was not confirmed. Facts about other repositories (Forge, printing, the print +agent, the scanner QBit, Record Files, the frontend and QQQ core) come from read-only surveys of those repositories +made for this task, read at the versions Forge pins where that matters. + +| Area | Today | Where | +|---|---|---| +| Label scene | `qqq-zpl-scene/1`, a JSON list of `text`, `rasterText`, `box`, `image`, `barcode`. Every coordinate and size is an integer printer dot. Stock dpi must be 203, 300 or 600. | `renderer/.../ZplRenderer.java` | +| Printer font text | `text` emits `^A0N` (printer font 0). The renderer cannot measure that font, so it refuses when `x + characters * width` passes the label width. | `ZplRenderer.java` `render`, case `text` | +| TrueType text | `rasterText` draws one supplied TrueType face into a dot box. It already wraps and already has `align` left, center, right. It never shrinks. Overflow is `RASTER_TEXT_DOES_NOT_FIT`. | `renderer/.../RasterText.java` | +| Barcode | 71 catalogue ids. A `fit` box in microns picks the largest whole-dot module for 13 symbologies; the raster sits at the box origin (no centring). Linear bar height scales with the module. No human-readable line. | `BarcodeFit.java`, `Barcodes.java`, `renderer/BARCODE-FIT.md` | +| Fonts | None bundled in the renderer. A font is a template asset whose bytes the host must supply on every render. | `RenderInput.java`, `bridges/renderer/.../IsolatedRendererBridge.java` `resolve` | +| Asset bytes | The foundation stores an asset row only (name, file version id, hash). The only shipped reader is Record Files, whose upload path is a Collaboration attachment and needs a storage bucket. | `GenerationTables.java`, `bridges/record-files/...`, `qbit-record-files` | +| Refusals | Apart from 13 barcode fit codes, every renderer failure is the single code `RENDER_FAILED`, by design, so that no record data leaks. | `RendererMain.java` `errorCode` | +| Output check | The host re-parses the ZPL and allows only `^A0N` text, `^GB` and uncompressed `^GFA`. | `bridges/renderer/.../ZplOutputPolicy.java` | +| Preview | "Preview draft" generates a retained, audited file from a real record. A PDF opens in the shared viewer. A ZPL file cannot be shown as an image. | `GenerationAuthoringProcesses.java`, `DocumentGenerationService.preview`, `qqq-frontend-next` `attachment-download.ts` | +| Stock and dpi | A template has one stock; the stock carries the dpi. A revision copies both. | `GenerationTables.java` | +| Page documents | FreeMarker HTML with paged CSS 2.1 through a patched OpenHTML to PDF; TrueType assets; one starter (packing slip). | `PdfRenderer.java`, `renderer/README.md` | +| Coverage gate | JaCoCo 85% instructions and 90% classes in `foundation` only. `renderer` and the bridges have no gate. | module `pom.xml` files | +| Release | Shared Gitea workflow: `develop` publishes a SNAPSHOT, `release/X.Y` publishes `X.Y.0-RC.N` and tags it. `develop` is `1.4.0-SNAPSHOT`. Renderer images are built by hand from a released version. | `.gitea/workflows/build.yaml`, `renderer/IMAGE-PUBLISH.md` | + +How GG Forge uses it today (read from Forge `origin/develop`): + +- Forge pins the document generation family at `1.1.0-RC.2` and serves `qqq-frontend-next` `1.0.0-RC.41`. +- Forge renders through `RemoteRendererBridge`, so `ZplOutputPolicy` applies to every label. +- `EchoTemplatePublisher` computes all geometry by hand (microns to dots, insets, a module search) and Forge seeds + ten documents: the same designs repeated for 4 x 1 in at 203 and 300 dpi and 3 x 2 in at 203 dpi. +- `EchoPrintingDestinations.resolve` offers a document only when the revision's stock width, height and dpi equal + the media profile the print agent reports from the Windows queue default. When nothing matches, the document + list is empty and no reason is given. This filter is Forge code, not scanner or printing QBit code. +- `EchoBundledFonts` answers one font from Forge's jar because no environment could hold an uploaded font, and a + Liquibase changeset cannot put bytes in a bucket. + +The printing QBit's built-in test label (`qbit-printing` `PrintingTestDocuments`) is the quality bar: it is drawn in +points on a Java canvas, scaled by `dpi / 72`, with a bundled bold TrueType face, and sent as whole-label `^GFA` +strips plus a native `^BC` barcode. + +## 1. Goals and non-goals + +### What a person designing a label experiences + +- They write sizes in millimetres, inches or points. They never compute dots. +- They say "centre this in that box" for any element, and it is centred for every value and every dpi. +- They pick a font by name from a small set that is always there, or upload their own once. +- They state what happens to a long value: shrink to a floor, wrap to a number of lines, cut with a visible mark, + or refuse. The default never changes data silently. +- They open the template and see the label. They change the layout and see it again in seconds, at 203, 300 or + 600 dpi, with sample values or a real record, without printing. +- Beside the picture they read plain warnings: text was shrunk, a barcode is at its smallest safe module, a field + would not fit its longest allowed value. +- Publishing proves the layout fits the longest value each field declares, so a published label is not refused + for length at print time. + +### What a person printing a label experiences + +- The label prints at the printer's own resolution. Nobody creates a 203 dpi copy and a 300 dpi copy. +- When a label cannot print, the message names the field, what it needed and what it had. +- When a document is not offered for a printer, the screen says why (the stock size differs) instead of showing + an empty list. + +### Non-goals in this round + +- A drag-and-drop visual designer. The editor stays source first; the preview makes that workable. +- Raw ZPL templates or any printer command escape hatch. +- Printer languages other than ZPL (EPL, TSPL), colour, RFID. +- Changing how existing `qqq-zpl-scene/1` documents print. +- GS1 conformance grading or certification. The design gives rules and tests, not a verifier. + +## 2. Layout model + +### 2.1 Scene version 2 + +A new schema id, `qqq-zpl-scene/2`, lives in the same `zplSource` field. The renderer chooses the code path from +the `schema` value alone. + +```json +{ + "schema": "qqq-zpl-scene/2", + "design": { "width": "4in", "height": "1in" }, + "defaults": { "font": "qqq-sans-bold", "size": "9pt" }, + "samples": { + "typical": { "record": { "humanReadable": "(01)00012345678905(10)L240611A", "rendererValue": "[01]00012345678905[10]L240611A" } }, + "longest": { "record": { "humanReadable": "(01)00012345678905(10)L240611A-RETURNED-000123", "rendererValue": "[01]00012345678905[10]L240611A-RETURNED-000123" } } + }, + "elements": [ + { "id": "code", "type": "barcode", "symbology": "gs1-128", "valuePath": "/record/rendererValue", + "anchor": "top-center", "y": "0.5mm", "width": "94mm", "height": "11mm", + "align": "center", "valign": "middle", "minModule": "0.25mm", "quiet": 10 }, + { "id": "caption", "type": "text", "valuePath": "/record/humanReadable", + "anchor": "bottom-center", "y": "0.5mm", "width": "94mm", "height": "6.5mm", + "align": "center", "valign": "middle", "size": "11pt", "maxLength": 48, + "fit": { "maxLines": 2, "shrinkTo": "7pt", "overflow": "refuse" } } + ], + "overrides": [ + { "when": { "dpi": [203] }, "set": { "caption": { "threshold": 144 } } } + ] +} +``` + +The example assumes 3 mm margins on 4 x 1 in stock, a printable area of 95.6 x 19.4 mm. + +### 2.2 Units + +Every length is a string made of a number and a unit: `"12.5mm"`, `"0.5in"`, `"9pt"`, `"25%"`, `"1dot"`. + +| Unit | Meaning | Use | +|---|---|---| +| `mm`, `in` | Physical length | Positions and sizes | +| `pt` | 1/72 inch | Font sizes, line thickness | +| `%` | Share of the container's width (for `x`, `width`) or height (for `y`, `height`) | Layouts that adapt to the stock | +| `dot` | One printer dot | Only where a device unit is meant (a hairline). Outside an override it raises the warning `DEVICE_UNIT_USED`. | + +One rule, no ambient unit setting, so a value read out of context is never ambiguous. Up to three decimals are +accepted. Lengths convert to integer micrometres first (the unit the stock already uses), then to dots: + +| Quantity | Rounding | Reason | +|---|---|---| +| Position | Nearest dot, the existing `Stock.dots` rule | Symmetric error | +| Box width and height | Down, the existing `BarcodeFit.dotsInside` rule | Content never leaves its designed box | +| Line and box thickness | Nearest, never less than 1 dot | A thin rule must not vanish | +| Barcode module | Up to a whole dot from the stated minimum (section 4) | Bars must be whole dots | +| Font size | Not rounded; the face is scaled by `points * dpi / 72` | Type scales continuously | + +### 2.3 Stock, printable area and render target + +- Coordinates start at the top left of the printable area: the stock less its margins. A design therefore never + prints in the margin. +- In dots the printable area is `floor((W - L - R) x dpi)` wide and `floor((H - T - B) x dpi)` high, the rounding + of a box size (section 2.2), and it starts at `ceil(L x dpi)`, `ceil(T x dpi)`, moved back by one dot where it + would otherwise pass the page edge (possible only with no margin on the far side). A box of exactly the + printable width and height therefore fits at 203, 300 and 600 dpi alike. No ink is a whole dot inside a margin; + the right and bottom edges may pass theirs by a fraction of a dot. The first implementation took the far edge + rounded down less the near margin rounded up, which can be one dot smaller than that box: 100 mm stock with + 3 mm margins gave 1109 dots at 300 dpi against a 94 mm box of 1110, so a design that fitted at 203 and 600 dpi + was refused at 300. +- `design.width` and `design.height` state the stock the layout was drawn for. At render time the stock must match + within 0.5 mm on each side, otherwise the refusal `STOCK_MISMATCH` reports both sizes. (Exact micrometre + equality, which Forge's filter uses today, fails on driver rounding such as 101.5 against 101.6 mm.) +- Resolution stops being a property of the document. A version 2 revision renders at a **render target** dpi + supplied by the caller: the printer's reported resolution when printing, the chosen value when previewing. The + stock row's `dpi` remains and becomes the default target and the only target for version 1 scenes. +- `DocumentGenerationService.Request` gains an optional `targetDpi` (203, 300 or 600). It is recorded on the + request row in a new nullable column and framed into the intent digest when present, following the pattern used + for the media profile (`docs/EXPECTED-PRINT-INTENT.md`). The renderer refuses a target that differs from the + stock's dpi for a version 1 scene (`TARGET_REQUIRES_SCENE_2`), because only the renderer reads the scene. +- Later round: `design.stock: "atLeast"` lets one design print on a larger stock, with anchors and percentages + resolving against the actual printable area. The first round supports `exact` only. + +### 2.4 Anchors and alignment + +Two separate ideas, both available on every element. + +**Where the box sits (`anchor`, `x`, `y`, `width`, `height`).** `anchor` names one of nine points: +`top-left` (default), `top-center`, `top-right`, `middle-left`, `center`, `middle-right`, `bottom-left`, +`bottom-center`, `bottom-right`. The same point of the element's box is placed on that point of its container. +`x` and `y` are then distances inward from the anchored edge; on a centred axis they shift right or down and may be +negative. An omitted `width` or `height` means the rest of the container on that axis. + +``` + container (printable area or group) + +--------------------------------------------------+ + | anchor: top-center, y: 1mm | + | +------------------------------+ | + | | box (width x height) | | + | | content placed by | | + | | align / valign | | + | +------------------------------+ | + | | + | anchor: bottom-left anchor: bottom-right + | x: 2mm, y: 1mm x: 2mm, y: 1mm | + | +--------+ +--------+ | + +--------------------------------------------------+ +``` + +**Where the content sits in the box (`align`, `valign`).** `align` is `left`, `center` or `right`; `valign` is `top`, +`middle` or `bottom`. They apply to text lines, to a fitted barcode whose size depends on its data, and to a scaled +image. Slack is split with integer division, so a centred element is off by at most one dot. + +`rotation` is 0, 90, 180 or 270 degrees clockwise for text, barcodes and images. The box is given as it appears on +the label, after rotation. + +### 2.5 Containers + +`group` is in: a box with its own `anchor`, position and size, whose children are positioned relative to it. It +lets a block move as a unit and gives percentages something smaller than the label to refer to. Groups nest to a +depth of four. + +Rows and columns that distribute children automatically (`stack`, tables) are out of this round. No label in hand +needs them, and they can be added to version 2 later without breaking a scene that does not use them. + +### 2.6 One design, several resolutions + +A scene has no dots in it, so the same revision renders at any target. Small physical differences between +resolutions are expected (a 0.25 mm module is 2 dots at 203 dpi and 3 dots, 0.254 mm, at 300 dpi) and the +diagnostics report the actual values. + +`overrides` is the only place a scene may depend on resolution. An entry has `when` (`dpi`: a list) and `set` (a +map from element id to replacement properties; `id` and `type` cannot change). Legitimate uses: + +- a heavier face or a different `threshold` for small type at 203 dpi; +- a larger minimum type size at 203 dpi; +- a logo asset prepared for one resolution; +- a `dot` thickness for a hairline. + +An override is not for repositioning the layout per printer. If a design needs that, it is two designs. + +### 2.7 Versions and upgrade + +- **Version 1 is frozen.** A `qqq-zpl-scene/1` source keeps its existing code path and its exact output bytes. A + test corpus pins this (section 10). No new field is added to version 1, including `align` on a barcode, because + published scenes already carry that word and would start printing differently. +- **Version 2 is a separate package** in the renderer. The two share only the barcode encoders and the font + rasteriser. +- **Upgrade is an explicit act by an author, never automatic.** A new authoring action, "Convert layout to physical + units", asks the renderer for a version 2 rendering of the draft's version 1 scene (new operation + `UPGRADE_SCENE`, a pure function that returns the new source and a list of notes). It converts dots to + millimetres with the template stock's dpi, turns `rasterText` into `text` with the same asset, turns a fitted + barcode into a barcode box, and turns printer font `text` into `text` in the default bundled face with a note + that the face changed. The action shows the two previews side by side and saves the result as the draft only. + Publishing stays a separate human step. Nothing rewrites a stored revision. +- A renderer that does not know version 2 answers a version 2 scene with a refusal, and the host reports + `RENDERER_TOO_OLD` (section 11.3). + +## 3. Text + +### 3.1 Fonts + +Version 2 has one kind of text: a TrueType face rasterised by the renderer. Printer font 0 is not offered, because +the renderer cannot measure it or draw it, so it can be neither fitted nor previewed truthfully. Version 1 keeps +its `text` element. + +| Source | How it is named in a scene | Where the bytes live | Works without object storage | +|---|---|---|---| +| Bundled default set | `qqq-sans`, `qqq-sans-bold`, `qqq-condensed-bold`, `qqq-mono`, `qqq-mono-bold` | Inside the renderer jar and image, with licence files and hashes | Yes | +| Built-in asset store | The asset's name in the template manifest | A new table owned by this QBit, bytes in the database (section 3.2) | Yes | +| Record Files (as today) | The asset's name in the template manifest | Collaboration attachment storage | No | + +The prefix `qqq-` is reserved: a version 2 scene whose manifest has an asset with that prefix is refused with +`RESERVED_FONT_NAME`. + +**The bundled set.** Five faces: a proportional sans in regular and bold, a condensed bold for long values on +narrow labels, and a monospace in regular and bold for codes and human-readable lines. Selection criteria: real +bold and condensed cuts (stroke weight comes from the face, not from a threshold; Forge's Abel needed +`threshold` 220 to print dark enough), clear `0/O` and `1/l/I`, Latin Extended coverage, static TrueType outlines +(the renderer accepts only `font/ttf`), a licence that permits bundling. Proposed: IBM Plex Sans (Regular, Bold), +IBM Plex Sans Condensed (Bold) and JetBrains Mono (Regular, Bold). JetBrains Mono Bold already ships in +`qbit-printing` and its OFL text was read there; the IBM Plex licence was not verified in this task and must be +confirmed from the licence file of the exact release taken. Each face ships with its licence text, a provenance +note and a SHA-256, and a test fails the build if any of the three is missing. Licence handling is decision 1 in +section 12. + +The same faces are also registered as CSS families for page documents (section 7). + +### 3.2 Uploaded fonts in every environment + +Today an uploaded font needs Record Files, Collaboration attachments and a bucket, and it cannot be seeded by a +database migration. The design adds a store that needs only the database the QBit already has. + +- **Table** `document_generation_asset_content`: `sha256` (unique), `media_type`, `length`, `bytes`, `file_name`, + `rights_statement`, `created_by`, `create_date`. Limit 2 MiB per asset, the renderer's existing bound. Shipped as + migration scripts for PostgreSQL, MySQL and H2 beside the existing ones; the host applies them (Forge writes its + own changeset). +- **Reference.** An asset row points at it with `fileVersionId = "dg:"`. The bridge resolves that prefix + from the built-in store itself and passes every other id to the host's reader as today. No host code is needed. +- **Upload.** A new authoring action, "Upload font or image", takes a file through the existing `FILE_UPLOAD` field + adornment, a name, and a required rights statement. Permission: `document-generation.configure`. +- **Checks.** The host checks size and leading bytes only. The file is then sent to the isolated renderer + (new operation `INSPECT_ASSET`), which parses it and returns family names, glyph count and the embedding + permission bits. The application JVM never parses a font or an image. A font whose embedding bits forbid + embedding is refused. +- **Seeding.** Because the bytes are a database row, a host can seed a logo or a brand font with a migration, which + is how Forge installs its setup data. +- **When Record Files is absent** nothing is lost: selection lists the built-in store and the bundled faces. When + it is present its released versions appear in the same list, as today. + +This moves a boundary: the README says the QBit "does not own retained file bytes". That stays true for generated +output. Template assets are definition data, like the scene source. This is decision 2 in section 12. + +### 3.3 Size, fit and legibility + +`size` is the em size as a length (`"9pt"`, `"3mm"`). `lineHeight` is a multiple of the size (default from the face +metrics). + +`fit` states what happens when the value does not fit the box: + +| Property | Meaning | Default | +|---|---|---| +| `maxLines` | Wrap to at most this many lines | 1 | +| `shrinkTo` | Smallest size the text may be reduced to | 70% of `size`, not below 5 pt | +| `overflow` | `refuse` or `truncate`, applied when it still does not fit at the floor | `refuse` | + +The order is fixed and stated: lay out at the nominal size within `maxLines`; if it does not fit, find the largest +size down to the floor (0.25 pt steps) that fits within `maxLines`; if none fits, apply `overflow`. `truncate` cuts +at a character boundary, appends an ellipsis and raises the warning `TEXT_TRUNCATED`. It is opt-in per element +because a shortened identifier on a warehouse label can be misread; the default keeps the label from printing +something different from the data. + +`maxLength` (characters) declares the longest value a bound element must hold. Validation at publish lays out a +worst-case string of that length (the widest glyph of the face, repeated) and refuses publication if it does not +fit. At print time a value longer than `maxLength` is refused with `VALUE_TOO_LONG` before layout. Together these +make a length refusal a design-time event. An element without `maxLength` raises the warning `FIT_NOT_PROVEN` at +preview and publish. + +Legibility on thermal heads: + +- Glyphs are drawn antialiased and reduced to black and white at `threshold` (1 to 254, default 128). A higher + value prints heavier. The default is not changed per resolution; an override may. +- The renderer warns `TEXT_SMALL_FOR_DPI` when the capital height at the target is under 12 dots (about 6 pt at + 203 dpi), and `TEXT_LIGHT_FACE` when a regular-weight face is used with a capital height under 20 dots (about + 10 pt at 203 dpi). The numbers are starting values to be confirmed on the specimen label (section 10.6). +- Guidance for the authoring guide: at 203 dpi use a bold face below 10 pt, keep text at or above 6 pt, prefer the + condensed bold over shrinking. + +## 4. Barcodes + +### 4.1 Symbologies + +Supported today: 71 ids (the OkapiBarcode families, four through Zint, Code 93i, Co-op 2 of 5). Fit boxes are +qualified for 13: `code128`, `code39`, `code93`, `itf`, `gs1-128`, `qr`, `ean13`, `ean8`, `upca`, `upce`, +`datamatrix`, `pdf417`, `aztec`. + +Version 2 keeps the catalogue and the dynamic symbology allowlist. To add, in this order: + +1. GS1 Data Matrix and GS1 QR as fit-qualified ids (`gs1-datamatrix`, `gs1-qr`). The encoders already accept a GS1 + data type as an option, but the fit profile admits default options only. +2. SSCC-18 (`nve18` exists in the catalogue) and ITF-14 with bearer bars as fit-qualified ids. +3. GS1 DataBar family fit qualification, when a label needs it. + +### 4.2 GS1 application identifiers and the human-readable line + +Today a host builds the bracketed element string and the caption itself (Forge's `EchoGs1`). Version 2 adds: + +- `gs1`: a list of `{ "ai": "01", "valuePath": "/record/gtin" }` entries in place of `value`. The renderer builds + the element string, checks each identifier's length and character set, checks the check digit of 00, 01 and 02, + and places separators after variable-length fields. An unknown identifier or a bad value is the refusal + `GS1_INVALID` with the identifier and a reason code. (What the encoder library already validates was not + verified here; the builder must not duplicate it.) +- `hri`: `{ "position": "below" | "above" | "none", "font": "qqq-mono", "size": "7pt", "fit": {...} }`. The line is + drawn by the text engine inside the barcode element's box, so bars and caption are centred as one unit. For GS1 + it reads `(01)00012345678905(10)L240611A`. It uses the same fit rules as any text and may wrap between + identifiers. + +A plain `value` in bracket syntax keeps working, so the first release does not depend on the builder. + +### 4.3 Module size, centring and quiet zones + +- Bars are whole dots. The renderer picks the largest module, in dots, that fits the box on both axes including + quiet zones, never smaller than `minModule` and never larger than `maxModule` if given. +- `minModule` is physical. Defaults: 0.25 mm for linear symbologies and 0.375 mm for two-dimensional ones. That is + 2 and 3 dots at 203 dpi, 3 and 5 at 300 dpi, 6 and 9 at 600 dpi. These defaults are proposals to confirm on a + printer (section 10.6). +- For linear symbologies the bar height fills the box height (less the human-readable line) and no longer scales + with the module. In version 1 the height scales with the module, so a short box caps the module. +- The fitted symbol is placed in its box by `align` and `valign`. Left over dots are blank. +- `quiet` is modules on each side, with the existing minimums (10 linear, 4 matrix). Quiet zones are inside the + box and are never cropped. The warning `QUIET_ZONE_INTRUDED` fires when another element's ink lands in one. + +### 4.4 When the data does not fit + +The label never prints a code below its stated minimum module. + +- If even `minModule` does not fit, the refusal is `BARCODE_DOES_NOT_FIT` with the dots needed at the minimum and + the dots available. The message reads, for example: "Barcode 'code': the value needs 742 dots at the 2 dot + minimum module; the box is 690 dots wide at 203 dpi." +- An author who accepts the risk writes `"minModule": "1dot"`. Preview and publish then show the warning + `BARCODE_MODULE_BELOW_RECOMMENDED` every time. +- A later addition, not in the first round: `fallback`, an ordered list of alternative symbologies to try. + +### 4.5 Print quality at 203 dpi + +For the authoring guide, to be confirmed by the physical check: + +- A module is 0.125 mm per dot. Use 2 dots or more for linear codes; 1 dot bars are at the limit of the head. +- Print linear bars parallel to the feed direction (rotation 0 on a normally fed label). Bars across the feed lose + edge quality at speed. +- Keep the quiet zone clear; a centred code inside a wide box is the simplest way. +- Prefer a two-dimensional symbol when the data is long and the label is short. + +### 4.6 Verification by decoding + +Every barcode test decodes what will be printed, not what the encoder returned: the test takes the emitted ZPL, +rebuilds the bitmap from its graphic fields, and decodes it with ZXing (already a test dependency). It asserts the +payload, the symbology, the module size in dots and the blank margins. This runs at 203, 300 and 600 dpi for each +fit-qualified symbology, for short, typical and longest values. + +## 5. Graphics + +- **Image**: an asset (PNG or JPEG, as today) in a physical box. `fit` is `contain` (scale to the box, keep the + ratio; default) or `none` (one image pixel per dot, as version 1). Scaling averages area and then reduces to + black and white by `mode`: `threshold` (default, for line art and logos) or `dither` (for photographs). + `IMAGE_UPSCALED` warns when a small image is enlarged. +- **Box and line**: `box` with `thickness` (a length, at least 1 dot) and optional `fill`. A line is a box whose + height equals its thickness. +- **Reverse print**: `"reverse": true` on a text or box draws white on a black field the size of the box. +- **Order**: elements paint in list order onto one canvas, so text over a filled box behaves as written. + +**How version 2 reaches the printer.** The renderer composes the whole label on one 1-bit canvas at the target +resolution and emits it as `^GFA` strips, trimmed to the rows and columns that hold ink. The output uses only +commands the host output policy already allows, so `ZplOutputPolicy` needs no grammar change. This is the same +method as the printing QBit's test label. The cost is size: a full 4 x 6 in label is about 250 KB of ZPL at 203 dpi, +540 KB at 300 dpi and 2.2 MB at 600 dpi before trimming; a 4 x 1 in label is about 41 KB at 203 dpi. Section 12 +covers the consequences. + +## 6. Preview inside the QBit + +### 6.1 What the preview is + +The preview is the print path stopped before retention. The same `IsolatedRenderer` renders the same revision +content with the same context code, the host runs the same `ZplOutputPolicy`, and the picture is then decoded on +the host **from the ZPL bytes that would be sent**. There is no second renderer and no parallel drawing code, so +the preview cannot differ from the print for a version 2 scene. + +``` + editor / record screen + | template, draft fingerprint, sample name or record id, target dpi + v + DocumentPreviewService (foundation) authorise, build context, no rows written + | PreviewRequest + v + IsolatedRenderer.preview (bridges/renderer, or the remote renderer adapter over it) + | RENDER with diagnostics collected -> isolated renderer (remote service image) + | <- ZPL bytes + diagnostics + | ZplOutputPolicy.validate (unchanged) + | ZplRaster: ^GFA and ^GB -> 1-bit bitmap -> PNG + v + Preview: PNG pages, exact flag, diagnostics, output size and hash +``` + +- **Which component renders.** The isolated renderer, through whatever `IsolatedRenderer` the host configured (the + remote renderer image in Forge). Never in process: the renderer README forbids adding the engine to the + application JVM, and this design keeps that. +- **Who makes the picture.** New class `ZplRaster` in `bridges/renderer`. It walks the already validated output and + paints `^GFA` and `^GB` exactly. PNG encoding of our own bitmap is the only image work in the application JVM. A + preview over 16 million dots is refused (`PREVIEW_TOO_LARGE`); a 4 x 6 in label at 600 dpi is 8.6 million. +- **Version 1 scenes** can be previewed too. Graphics and boxes are exact. Printer font text cannot be drawn, so + each `^A0N` field is shown as a hatched placeholder of its estimated size, the result carries `exact: false` and + the warning `PRINTER_FONT_NOT_SHOWN`. A version 1 scene previews only at its stock's dpi. +- **Refusals in preview.** The request asks the renderer to collect diagnostics instead of stopping at the first + failure. Elements that failed are left out of the bitmap and reported with their boxes, and the result says + `printable: false`. When there is no error, the ZPL is byte for byte what printing produces; a test asserts the + hashes are equal. + +### 6.2 Data for a preview + +- **Sample data.** `samples` in the scene holds up to eight named context objects (64 KiB in total). They are + written by the author and must be synthetic. The first is the default. They travel with the scene, so a seeded + document previews on a fresh database. +- **Stress.** "Longest values" replaces every bound value with the worst-case string for its `maxLength`. +- **A real record.** The author chooses a source record, as in today's "Preview draft". The context is built the + same way as for generation, including the pinned context script when the host has one. + +### 6.3 API and permissions + +- Port: `GenerationPorts.IsolatedRenderer.preview(PreviewRequest)`, a default method that refuses, implemented by + `IsolatedRendererBridge` and delegated by `RemoteRendererBridge`. A host with its own adapter keeps compiling. +- Service: `DocumentPreviewService.preview(templateId, draftFingerprint or published, format, data, targetDpi)` + returns `Preview(pages, exact, printable, diagnostics, outputBytes, outputSha256)`. +- It writes no revision, request, event or file. It logs one structured line (actor, template id, target, outcome, + diagnostic codes) with no content. + +| Preview of | Data | Permission | +|---|---|---| +| A draft | Sample or stress | `document-generation.configure` | +| A draft | A real record | `document-generation.configure` and `.generate`, plus read access to that record, as today | +| The published revision | Sample | `document-generation.view` | +| The published revision | A real record | `document-generation.generate`, plus read access to that record | + +No new permission name is needed. + +### 6.4 How it appears, with no host code + +Forge serves `qqq-frontend-next`. A QBit can show an image there today in three ways that need no frontend change: +a `FILE_PREVIEW` field holding a `data:image/png` value (a thumbnail of at most 512 px that opens a lightbox), a +composite widget with an `IMAGE` block (an inline `` whose `path` may be a data URL, with width and height +styles) and text blocks, or sanitised HTML. The content security policy allows `data:` images. The QBit already +uses standard forms and adornments only; this design keeps to that for the first release. + +**First release (existing components only):** + +1. **On the template record screen**: a new section "Label preview" holding a composite widget + (`QFieldSection.withWidgetName`). Its renderer class, in the foundation, calls `DocumentPreviewService` with the + draft, the default sample and the stock's dpi, and returns an `IMAGE` block (the PNG as a data URL, shown at a + fixed physical scale) followed by one text block per diagnostic. Opening a template shows its label. A + template with no ZPL source shows a line saying so. +2. **Action "Preview layout"** on a template: a form with the data choice (a sample, "longest values", or a + record), the format and the resolution; then a result step that is the same composite widget, seeded from the + process value of the same name, with the list of diagnostics beside the image. Running it again after an edit + takes one click. +3. The existing action is relabelled "Generate retained preview file" so the two are not confused. Its behaviour + does not change. + +The first task of that package is a spike in a running frontend to confirm that a seeded composite widget in a +process step renders an `IMAGE` block from a data URL (the pieces were read in the frontend source, the +combination was not run). The fallback, also already present, is a view form with a `FILE_PREVIEW` field and an +HTML list. + +**Second release (a `qqq-frontend-next` change):** a purpose-built preview component, in the frontend's existing +pattern for QBit screens (`src/components//` plus a widget type): the source editor and the picture side by +side, refresh on save, zoom, a resolution switch, and element boxes and quiet zones drawn over the picture from the +diagnostics' coordinates. The backend contract is the same `Preview` value, so nothing in this repository changes +for it. + +**A host application** gets all of the above by registering the QBit as it does now. Forge adds help entries for +the new process, fields and widget (its help coverage test requires them) and nothing else. + +### 6.5 Page (PDF) templates + +- Today's retained preview keeps working for PDF and stays the way to obtain the exact file. +- Second release: the same "Preview layout" action for PDF. The frontend preview accepts a data URL only for + images, so the isolated renderer rasterises the first pages of the PDF it just produced (PDFBox, at a stated + screen resolution) and returns PNG pages through the same `Preview` value. The result is marked as page images of + the exact PDF. + +## 7. Templates and page layout beyond labels + +**Today.** HTML with FreeMarker (autoescaped), paged CSS 2.1: stock size and margins, tables, repeating headers and +footers, page breaks, absolute positioning, borders, images, TrueType assets, the `` element with fit +boxes. One starter, the packing slip. No flex or grid, no SVG, no script. + +**What great means for page documents.** + +- Default fonts without an upload: the bundled faces are available as CSS families (`'QQQ Sans'`, `'QQQ Sans + Condensed'`, `'QQQ Mono'`). +- A fast preview with sample data (section 6.5) and the same diagnostics list. +- Starters that look finished: packing slip (exists), pick list, pallet content sheet, each with sample data. +- Refusals that name the cause: an unknown asset, an image over the limits, more pages than allowed. + +**In this round:** bundled CSS families; page preview images; refusal codes for the PDF path. **Out:** a visual +designer, flex or grid layout, SVG, new starters beyond one, any change to the OpenHTML fork. + +**Label starters.** A small library of version 2 scenes ships as resources, like the packing slip starter: +GS1-128 on 4 x 1 in, a product label with text and a code on 3 x 2 in, a 4 x 6 in shipping layout, and two tools, +a **type specimen** (each bundled face at 5 to 14 pt and several thresholds) and a **resolution ruler** (rulers +drawn for 203, 300 and 600 dpi; the one that measures true tells the operator the printer's resolution). + +## 8. Errors and diagnosis + +### 8.1 Diagnostics + +The renderer's answer gains a `diagnostics` list, and its error answer gains one diagnostic as `detail`. A +diagnostic is closed vocabulary and numbers only: + +```json +{ "code": "TEXT_DOES_NOT_FIT", "severity": "ERROR", "element": "caption", + "box": [24, 131, 764, 56], "measured": { "widthDots": 902, "sizeCentipoints": 700, "lines": 2 }, + "limit": { "widthDots": 764, "lines": 2 } } +``` + +- `element` is the element's `id`, which the template author wrote and which is limited to + `[A-Za-z][A-Za-z0-9_-]{0,31}`. No record value, context text or exception message is ever included. This keeps the + rule the renderer has today (errors must not carry source data) while giving up the single opaque code. +- The host turns a diagnostic into a sentence with the element, the measured value and the limit, in physical + units as well as dots. `GenerationPorts.FailureCode` grows, and `RenderException` carries the diagnostic. + +| Severity | Codes (first release) | +|---|---| +| ERROR | `SCENE_INVALID` (with the JSON pointer of the offending property), `STOCK_MISMATCH`, `TARGET_REQUIRES_SCENE_2`, `ELEMENT_OUTSIDE_PRINTABLE_AREA`, `TEXT_DOES_NOT_FIT`, `VALUE_MISSING`, `VALUE_TOO_LONG`, `FONT_UNKNOWN`, `FONT_MISSING_GLYPH`, `RESERVED_FONT_NAME`, `BARCODE_DOES_NOT_FIT`, `GS1_INVALID`, `ASSET_UNKNOWN`, `PREVIEW_TOO_LARGE`, `RENDERER_TOO_OLD` | +| WARNING | `TEXT_TRUNCATED`, `TEXT_SMALL_FOR_DPI`, `TEXT_LIGHT_FACE`, `FIT_NOT_PROVEN`, `BARCODE_MODULE_BELOW_RECOMMENDED`, `QUIET_ZONE_INTRUDED`, `ELEMENTS_OVERLAP`, `IMAGE_UPSCALED`, `DEVICE_UNIT_USED`, `PRINTER_FONT_NOT_SHOWN` | +| INFO | `TEXT_SHRUNK` (from and to size), `TEXT_WRAPPED` (lines), `BARCODE_MODULE` (dots and micrometres), `IMAGE_SCALED` | + +Version 1 scenes keep their existing codes and the opaque `RENDER_FAILED`. + +**Decided while building the scene 2 core (P1).** + +- **The pointer of `SCENE_INVALID`.** The diagnostic gains one optional field, `pointer`, on the wire and on + `GenerationPorts.Diagnostic` (a seventh record component; the six-argument constructor stays). It is a JSON + pointer into the scene source whose segments are only array indexes, property names the version 2 schema + declares, and element ids the scene declares (the last occur under `overrides/N/set`). The renderer builds it + from those and checks every segment where the diagnostic is created; the host holds it to the shape + `(/(index|name)){0,16}`. So a pointer cannot carry a record value, a sample or text an author put in an + unknown property name: an unknown property is reported at its parent, with `measured.unknownProperties` as the + count, and never by name. When the place is inside a declared element, `element` carries its id as well, so a + message can say "element 'caption', at /elements/1/fit". Expressing the pointer through `element` and + `measured` alone was rejected: integers cannot name a property, and an index without the property is not + enough to act on. +- **A diagnostic names only a declared id.** The renderer's collector (`scene2.Diagnostics`) is the only maker + of diagnostics and holds the ids declared by the scene being rendered. A diagnostic that names any other + string as its element is a defect and fails inside the worker as the opaque `RENDER_FAILED`; nothing reaches + the host. Every element therefore has a required, unique `id`. +- **An element type this renderer cannot draw** (text, barcodes and images, until their packages) is parsed and + placed and then refused as `RENDERER_TOO_OLD` with the element id and its box, the same code the host uses + for an image that does not know version 2. The scene is valid; the renderer is what must change. +- **A box that resolves to no dots** at the render target (0.1 mm at 203 dpi, or an omitted size that the + offsets leave nothing of) is `SCENE_INVALID` at the element with `measured.widthDots` and `heightDots` against + a limit of 1, rather than a new code. +- **A stock the printer language cannot address** (a resolution other than 203, 300 or 600, more than 9999 by + 32000 dots, no printable area) is the host's mistake, not the scene's, and stays the opaque failure it is for + version 1. The vocabulary has no code for it. +- **`UNSUPPORTED_OPERATION`** (section B.1, for `INSPECT_ASSET` and `UPGRADE_SCENE` until their packages) is a + worker error code, not a diagnostic, and is not a `FailureCode` yet. The package that first calls either + operation from the host adds it. +- **With `"diagnostics": "COLLECT"`** an element's refusal skips that element (and, for a group, its children) + and the rest is drawn; a refusal about the scene as a whole (`SCENE_INVALID` from the parser, + `STOCK_MISMATCH`) ends the pass. Either way the outcome is `REFUSED` and the answer carries the diagnostics and + no label bytes (no `base64`, `sha256`, `length`, `mediaType` or `pageCount`): a host that looked for `base64` + without reading the outcome would otherwise print a label with elements missing. This changes the wire contract + of section B.1 and the source of the picture in section 6.1 ("Refusals in preview"): the partial canvas exists + in the worker (`Scene2.compose`) but does not travel as a label. P5 decides how a refused preview is drawn: + from the diagnostics' boxes alone, or from a separately named field that the print path never reads. +- **A length too long for any stock** is `SCENE_INVALID` at its pointer however long it is. The limit of 2 m is + compared before the micrometres are narrowed to an `int`, so `84600in` (which does not fit one) is refused like + `79in` and never as an arithmetic failure, which the worker would answer as the opaque `RENDER_FAILED`. +- **What one scene may paint** is sixteen times its printable area, an overlap counted each time (a filled box + its area, an outline its four bars, a group nothing), counted when validating as when rendering. The element + that would pass it is `SCENE_INVALID` at the element, with its box and `measured.paintedPercent` against + `limit.paintedPercent` of 1600, rather than a new code, as for a box of no dots. Sixteen was chosen from both + ends: a real label paints less than twice its area (a solid ground, reversed panels, rules, text and codes), + so no layered design is refused, and the worst scene falls from 1000 fills of a 64 million dot canvas (8 GB of + byte writes, seconds of CPU) to sixteen (128 MB). + +### 8.2 A document that is not offered for a printer + +Whether a document can print on a printer has three owners, and the message is assembled from them: + +| Part | Owner | What it provides | +|---|---|---| +| What the document needs | Document generation | New `DocumentAvailability.explain(table, format, target)`: for each published template, offered or not, with a reason code and both sizes | +| What the printer has | Printing | The media profile the agent reports (width, height, dpi), as today | +| Joining the two | The printing bridge in this repository (`bridges/printing`) | New `PrintableDocuments.forPrinter(...)`, so hosts stop writing the match themselves | +| Showing it | The application screen (Echo in Forge) | Lists what is offered and states what is not and why | + +With version 2 and a render target, resolution is no longer a reason; a 203 dpi printer and a 300 dpi printer take +the same document. The remaining reasons are `STOCK_SIZE_MISMATCH` ("needs 4 x 1 in; the printer reports 4 x 2 in") +and, for version 1 documents only, `RESOLUTION_MISMATCH` ("this older document was drawn for 300 dpi; the printer +reports 203 dpi; convert it to physical units"). + +The scanner QBit holds no document logic (its base module was searched), and the Android client only shows what +the server sends, so this needs no scanner or app release. The client's own wording for an empty list was not found and is not changed. + +## 9. Security and data + +- **Template source** is still interpreted only inside the isolated renderer. The version 2 parser, the asset + inspector and the upgrade function all run there. The host's new code handles only bytes it already validated + (`ZplRaster`) and bytes it stores (`document_generation_asset_content`). +- **Uploads.** Fonts: TrueType only (as today), 2 MiB, leading bytes checked on the host, parsed in the renderer, + embedding bits honoured, a rights statement required and stored. Images: PNG or JPEG, the existing dimension and + pixel limits, SVG still refused. Assets are addressed by hash; identical bytes are stored once. +- **Bundled fonts.** Unmodified, each with licence, provenance and hash, listed in `THIRD-PARTY.md` and `NOTICE`. +- **No preview is stored.** No revision, request, event, retained file or log content. The PNG of a real-record + preview exists in the response and, when shown through a process step, in that process's server-side state until + it expires, as any process screen showing record data does. (That store's retention was not verified.) The record + screen widget uses sample data only, so opening a template never renders customer data. +- **Samples** are synthetic by rule, stated in the field help and the authoring guide. +- **Diagnostics and logs** carry element ids and numbers only. +- **Permissions** are the existing five; section 6.3 gives the mapping. Uploading and converting need `configure`. +- **Parser hardening.** The version 2 scene parser and `ZplRaster` take untrusted input. Both get negative tests + and a bounded mutation campaign (section 10.5). + +## 10. Test strategy and coverage + +### 10.1 Compatibility of version 1 (first, before any renderer change) + +A corpus of version 1 scenes with the SHA-256 of their ZPL at 203, 300 and 600 dpi, generated from the tag Forge +runs (`v1.1.0-RC.2`; the renderer sources are identical on `develop` today) and never regenerated. It contains the +shapes stored in Forge's database (plain, scanner fidelity, compact GS1, the raster text document, with synthetic +values), the README examples and one scene per element type and option. The test fails on any byte difference. + +### 10.2 Golden images per resolution + +For each version 2 fixture (every starter and one scene per feature) a PNG at 203, 300 and 600 dpi is committed. +Barcode and box pixels are compared exactly everywhere. Text pixels depend on the Java runtime's rasteriser, so +unit tests on a developer machine compare with a small tolerance and always assert structure (ink bounds inside +the box, centring within one dot), and the exact comparison runs in the image qualification on the pinned runtime, +the existing `*ImageQualification` pattern. A changed golden is reviewed as a picture in the pull request. + +### 10.3 Decode tests + +As section 4.6: decode the bitmap rebuilt from the emitted ZPL, per symbology, per resolution, per value length. +The same rebuild is `ZplRaster`, so the preview code is exercised by every barcode test. + +### 10.4 Property tests for fit + +Seeded, deterministic generators (no clock, fixed seeds) over text, box, size, floor, lines and resolution assert: + +- the result is either a layout whose ink is inside the box or a refusal whose measured value exceeds its limit; +- ink is never clipped; +- the chosen size is never below the floor and never above the nominal size; +- a shorter string never gets a smaller size than a longer one in the same box; +- a published `maxLength` implies no length refusal for any string up to that length in the tested alphabets; +- for barcodes, the module is whole dots, at least the minimum, and the symbol decodes. + +### 10.5 Negative and fuzz tests + +Malformed scenes, lengths and assets; ZPL with wrong lengths for `ZplRaster`. A bounded mutation campaign over the +scene parser and `ZplRaster` runs in the normal test phase, with its seeds, limits and replay command in +`docs/security/fuzzing.md` (new), as the testing standard requires for parsers of untrusted input. + +### 10.6 Physical check before a final release + +Print the specimen, the ruler and the GS1 starter on a 203 dpi Zebra and a 300 dpi Zebra and scan them. This +confirms the proposed defaults (minimum modules, small type thresholds) and is recorded with the release. An RC +may reach dev before it; a final version may not. No agent can do this step. + +### 10.7 Coverage + +The testing standard requires 80% line coverage of changed lines and 100% for published libraries, per package. +The repository today gates only `foundation` at 85% instructions. + +- New packages (`com.kingsrook.qbits.renderer.scene2` and below, and the new foundation and bridge classes) are + gated at 100% line coverage, with exclusions only by explicit marker and written reason. +- JaCoCo is added to `renderer` and `bridges/renderer` with a bundle gate at the measured baseline, never lowered. +- The `foundation` gate is not lowered. +- Changed-line coverage of 80% is checked by the reviewer from the JaCoCo report; the shared Gitea workflow has no + such gate that I could find. + +### 10.8 Review + +Every package goes through a pull request into `develop` with: the full `./mvnw verify` on the branch head with no +skipped gate; an independent review by an agent that did not write it, covering correctness, silent failures and +test adequacy; and the review focus listed for the package. James merges. + +## 11. Repositories, versions and what Forge does + +### 11.1 Order and versions + +| Order | Repository | Change | Released as | +|---|---|---|---| +| 1 | `qbit-document-generation` | First slice (section B) | `1.4.0-RC.1` from a new `release/1.4` | +| 2 | same | Renderer images built from that version | `qqq-document-renderer` and `qqq-remote-renderer`, tag `1.4.0-RC.1` | +| 3 | `gg-forge`, `gg-forge-cd` | Pins, schema, one version 2 document on dev | Forge dev build | +| 4 | `qbit-document-generation` | Follow-on packages | `1.4.0-RC.2` and later | +| 5 | `qqq-frontend-next` | Purpose-built preview component | next `v1.0.0-RC.N` tag | +| 6 | `gg-forge` | Remove workarounds, convert the standard documents | Forge dev, then staging | + +`develop` is `1.4.0-SNAPSHOT` and the workflow names an RC from the branch, so the new line is `release/1.4`. The +`release/1.1` line that Forge runs in production stays untouched for fixes. Only one release-branch merge may be in +flight across QRun repositories. + +### 11.2 Other repositories + +| Repository | Needed for the first slice | Later | +|---|---|---| +| `qbit-printing` | Nothing. A label is opaque ZPL to it, and the default limit is 20 MiB. | Optional: move the resolution ruler into the built-in test documents. | +| `qqq-print-agent` | Nothing. ZPL is written raw to the queue and never parsed. No agent release is needed for anything in this design. | It repeats the payload once per copy; for large 600 dpi labels and many copies that multiplies the spool size. | +| `qqq-print-protocol` | Nothing. | Nothing. | +| `qbit-mobile-scanners` and the Android client | Nothing. | Nothing. | +| `qbit-record-files`, `qbit-collaboration` | Nothing. Record Files remains an optional asset source. | Nothing. | +| `qqq` core | Nothing. The widget, block, adornment and section APIs used exist in the version Forge pins. | Nothing. | +| `qqq-frontend-next` | Nothing. | The preview component (section 6.4). | + +Nothing in this design is held back by a client or agent release. + +### 11.3 Rollout rule for the renderer image + +The bridge accepts only an exact renderer version string, and the new answers have new fields. To allow a rolling +change: + +- Bridge `1.4` accepts renderer `0.1.0` (today's answers) and `0.2.0` (new answers). With an old image, version 1 + printing continues and a version 2 scene or a preview is refused with `RENDERER_TOO_OLD`, which names the fix. +- Renderer `0.2.0` is not accepted by bridges before `1.4`. +- Therefore: upgrade the host library first, then the image. Never the reverse. + +### 11.4 What Forge pins, adds and deletes + +Pins and additions (first slice): + +- The five document generation artifacts at `1.4.0-RC.1`; the two image digests in `gg-forge-cd`, after the host. +- A changeset for the new request column (`target_dpi`). Later a changeset for + `document_generation_asset_content`. +- Help entries for the new process, fields, section and widget; the permission and composition lists its tests + check. +- `EchoDocumentGenerator` passes the printer's reported dpi as the render target. +- One new setup-data changeset that seeds a version 2 GS1 document for 4 x 1 in, using a bundled face. Existing + changesets are not edited. + +Deletions, once the matching upstream piece is on dev and one label has printed: + +| Forge workaround | Retired by | Condition | +|---|---|---| +| `EchoBundledFonts`, its font resources, test and script | Bundled faces in the renderer | No published revision still references the `forge-bundled-font:` asset. The 0050 document is republished by a new changeset as a version 2 scene with a bundled face; its old revision row stays but is never rendered again, because reprints use retained bytes. | +| Hand geometry in `EchoTemplatePublisher` (dot conversion, insets, module search, the centred scene builder) and the duplicate scene builders in `scripts/local` | Version 2 scenes | Standard documents seeded as version 2 | +| One document per resolution (0039 seeds 203 and 300 dpi copies) | Render target | New seeds carry one document per size; old rows remain editable data | +| The exact stock match and silent drop in `EchoPrintingDestinations.resolve` and `EchoService.stockMatches` | `PrintableDocuments` in the printing bridge | Follow-on package | +| Offline bitmaps in `docs/history` used to judge a layout | Preview | First slice | + +Forge's `echoPublishDocument` process and the SQL that re-implements the draft fingerprint are not addressed here. +A declarative import for documents would retire them and is worth its own design. + +## 12. Risks and decisions + +### Decisions for James + +| # | Decision | Recommendation | +|---|---|---| +| 1 | **Bundled fonts and their licence.** The dependency standard's allowlist does not include the SIL Open Font License 1.1, yet `qbit-printing` already ships JetBrains Mono under it and Forge ships Abel under it, with no recorded exception. | Record one exception for unmodified OFL 1.1 fonts shipped as data, covering printing's existing font and the renderer's set. Bundle IBM Plex Sans (Regular, Bold), IBM Plex Sans Condensed (Bold) and JetBrains Mono (Regular, Bold), after confirming each licence file. The alternative with no exception is to restrict the set to fonts published under Apache 2.0. | +| 2 | **Asset bytes in the QBit's own table.** Fonts and logos up to 2 MiB stored in the database, so upload and seeding work everywhere. It moves the stated boundary that the QBit owns no file bytes. | Yes. Assets are definition data. Record Files stays as an optional second source. | +| 3 | **Version 2 prints the whole label as a bitmap and offers no printer font.** This is what makes preview exact and fit measurable. It makes jobs larger (section 5) and gives up printer-drawn text. | Yes. It is how the printing test label already works. Add compression only if 600 dpi job size proves a problem; that would mean widening the host output policy. | +| 4 | **Release line.** A new `release/1.4` for this work, leaving `release/1.1` for production fixes, with the host-first rollout rule of section 11.3. Production later moves from the 1.1 line to the 1.4 line. | Yes. | +| 5 | **Renderer images.** They are built and pushed by hand to `ghcr.io/gg-engineering`, which is the slowest step between a merged change and dev. | Add a build job for them to this repository's CI in the follow-on, and decide whether QRun images belong in a QRun registry. Not required for the first slice. | + +### Decided in this design + +- Version 1 is frozen; version 2 is a separate schema id and code path; upgrade is an explicit authoring action. +- Pull request 11 is replaced, not merged (see below). +- Lengths are strings with a unit; coordinates are relative to the printable area; nine-point anchors plus + `align` and `valign`; `group` is in, rows and tables are out. +- Default text fit is one line, shrink to 70%, then refuse; truncation is opt-in; `maxLength` makes fit provable at + publish. +- The barcode never prints below its stated minimum module; the default refuses. +- The preview picture is decoded on the host from the validated ZPL; the renderer does not draw a second image. +- The first preview UI uses components the frontend already has; the purpose-built component follows. +- Resolution is a render target chosen at print or preview time; flexible stock size comes later. +- Structured diagnostics replace the opaque failure for version 2, carrying ids and numbers only. + +### Pull request 11 + +PR 11 ("align a fitted ZPL barcode inside its fit box; justified font 0 text blocks") changes version 1 in place. + +- Its barcode half is sound and its arithmetic and decode tests are carried into version 2, where every element + has `align` and `valign`. It is not applied to version 1, because published scenes that already carry `align` on + a barcode would begin to print differently. +- Its text half emits `^FB` field blocks. The host output policy (`ZplOutputPolicy.text`) requires `^FH_^FD` + directly after the font command and rejects anything else, and the PR does not change the bridge. Through the + bridge Forge uses, every label with a justified text block would be refused. It also keeps the character-count + estimate and relies on the printer to centre, which cannot be previewed. + +So the design absorbs the idea and replaces the PR. Its tests move into the version 2 barcode package, and the PR +is closed when that package merges. Forge's unused `zplGs1CentredScene`, which targets PR 11, is deleted with the +other hand geometry. + +### Risks + +| Risk | Mitigation | +|---|---| +| Text rasterisation differs between Java builds, so goldens flap | Exact comparison only on the pinned runtime; structural assertions elsewhere (10.2) | +| Proposed module and type defaults are wrong for real media | Physical check before a final version (10.6); defaults are data in one place | +| Larger ZPL exceeds a host limit (Forge sets a 1 MiB output limit) or slows a 600 dpi printer | Ink trimming; documented sizes; the limit is host configuration; compression held in reserve (decision 3) | +| Host and image versions out of step | Section 11.3 rule and the named `RENDERER_TOO_OLD` refusal | +| The seeded composite widget does not render as read | Spike first; `FILE_PREVIEW` fallback (6.4) | +| The record screen widget renders on every open | Renderer admission already bounds concurrency; the widget uses sample data and can be collapsed | +| Scope: the plan is large | The first slice is seven packages; everything else is independent and follows | +| Manual image builds delay dev | Decision 5 | + +--- + +# B. Work plan + +All packages are in `qbit-document-generation` unless a repository is named. Each is one pull request into +`develop`. Branch names never start with `release/`. Sizes: S about a day, M two to three days, L about a week of +agent work including review. + +## B.1 Shared contracts (so packages can be built in parallel) + +**Renderer wire (protocol id unchanged, renderer version `qqq-renderer-0.2.0`).** + +- `RENDER` and `VALIDATE` accept optional `"targetDpi"` and `"diagnostics": "COLLECT"`. +- A successful answer adds `"diagnostics": [ ... ]` (section 8.1). With `COLLECT` and at least one error, the + outcome is `"REFUSED"`, with `diagnostics` and no output (decided in the review of P1, section 8.1: a refused + answer carries no label bytes). +- An error answer adds `"detail": { ... }`, one diagnostic. +- New operations, each routed by `RendererMain` to one class: `INSPECT_ASSET` (`AssetInspector`), `UPGRADE_SCENE` + (`scene2.upgrade.SceneUpgrade`). P1 creates both as classes that refuse with `UNSUPPORTED_OPERATION`. + +**Renderer element contract (package `com.kingsrook.qbits.renderer.scene2`, owned by P1).** + +- `Length` (parse, micrometres, dots by rule), `Frame` (a resolved box in dots), `Layout` (anchor resolution), + `Canvas` (1-bit, paint order, reverse), `Diagnostics` (collector), `SceneElement` with + `Frame place(Layout)` and `void paint(Canvas, Frame, RenderInput, Diagnostics)`, `ElementFactory` keyed by `type`. +- P2, P3 and P4 each add one sub-package and register one factory in a file they own + (`scene2/text/TextFactory`, `scene2/barcode/BarcodeFactory`, `scene2/graphics/GraphicsFactory`); P1 lists the + three class names in `ElementFactory` from the start, with placeholders that refuse. + +**Foundation ports (owned by P6a).** `GenerationPorts.Diagnostic`, `PreviewRequest`, `PreviewPage`, `Preview`, the +new `FailureCode` values, `RenderException(FailureCode, Diagnostic)`, `IsolatedRenderer.preview` as a refusing +default, `Integer targetDpi` on `RenderRequest`. P6a also adds the registration calls for +`GenerationPreviewProcesses`, `GenerationAssetProcesses`, `GenerationUpgradeProcesses` and `GenerationAssetTables` +in the producer, and the call to `GenerationPreviewWidget.section(table)` in `GenerationTables`, each an empty +class or no-op, so later packages fill only their own file. + +## B.2 Packages + +### First slice + +| Id | Package | Branch | Size | Depends on | +|---|---|---|---|---| +| P0 | Version 1 compatibility corpus | `test/v1-compat-corpus` | S | none | +| P13 | Coverage gates and fuzz scaffolding | `test/coverage-and-fuzz-gates` | S | none | +| P6a | Ports and registration hooks | `feat/preview-ports` | S | none | +| P1 | Scene 2 core, canvas, emitter, diagnostics | `feat/scene2-core` | L | P0, P13 | +| P2 | Text: bundled fonts, measurement, fit | `feat/scene2-text` | L | P1 | +| P3a | Barcode: module fit, alignment, decode tests | `feat/scene2-barcode-fit` | M | P1 | +| P5 | Bridge: `ZplRaster`, diagnostics, preview, version rule | `feat/bridge-preview` | M | P6a | +| P6b | Preview service, action and record widget | `feat/preview-ui` | M | P6a (P5 for the integration test) | +| P7 | Render target dpi | `feat/render-target-dpi` | M | P6a | + +**P0. Version 1 compatibility corpus.** +Scope: section 10.1. Files: new tests and resources only, under `renderer/src/test` and `bridges/renderer/src/test` +(`ZplSceneOneCompatibilityTest`, `resources/compat-v1/`). Tests: the corpus itself, run against untouched code, +plus proof that each stored ZPL passes `ZplOutputPolicy`. Review focus: the corpus covers every version 1 element, +option and the Forge scene shapes; values are synthetic; hashes come from the tagged source, not from the branch. + +**P13. Coverage gates and fuzz scaffolding.** +Scope: section 10.7 and 10.5 scaffolding. Files: `renderer/pom.xml`, `bridges/renderer/pom.xml` (JaCoCo with a +per-package rule for new packages and a baseline bundle rule), `docs/security/fuzzing.md`, a seeded mutation test +harness class in each module's test tree. This package is the only one that edits those two `pom.xml` files in the +first slice. Review focus: gates fail when they should (prove with a deliberate miss), no skip switches. + +**P6a. Ports and registration hooks.** +Scope: B.1 foundation contract. Files: `foundation/.../GenerationPorts.java`, `DocumentGenerationQBitProducer.java` +and `GenerationTables.java` (registration calls only), five new empty classes. Tests: record validation, default +refusals, source compatibility of existing constructors. Review focus: nothing breaks an existing host adapter; diagnostics cannot hold free text. + +**P1. Scene 2 core.** +Scope: sections 2, 5 (box, canvas, emission), 8.1 for the renderer; `RendererMain` routing by schema and for the +new operations; renderer version `0.2.0`; `renderer/schema/zpl-scene-2.schema.json`; `box` and `group` elements; +`overrides`; `samples` accepted and ignored by rendering. Files: new package `scene2`; `RendererMain.java`, +`Wire.java` and `RenderInput.java` (target dpi, bundled-name reservation hook). `ZplRenderer.java`, +`RasterText.java` and `BarcodeFit.java` are not edited. Tests: unit tests for every length and rounding rule, +anchor resolution for all nine anchors and negative shifts, row-band emission (the rebuilt bitmap equals the +canvas), goldens for box scenes at three resolutions, negative tests for the parser, P0 still green, 100% line +coverage of the package. Review focus: rounding rules match section 2.2 exactly; no path from context values into +diagnostics; emitted ZPL passes the unchanged `ZplOutputPolicy`; limits on elements, depth and canvas size. + +**P2. Text.** +Scope: section 3.1 (bundled set and reserved names), 3.3 (fit, `maxLength`, legibility warnings), reverse text, +rotation. Files: `scene2/text/*`; `renderer/src/main/resources/fonts/**` with licence, provenance and hash files; +`renderer/THIRD-PARTY.md`, `renderer/NOTICE`. Tests: property tests of section 10.4; goldens; glyph coverage +refusal; a test that every bundled face has licence, provenance and a matching hash; worst-case proof for +`maxLength`. Review focus: the fit order is exactly as stated; truncation only when asked; licences confirmed from +files; determinism (no locale, no installed fonts). + +**P3a. Barcode fit and alignment.** +Scope: sections 4.3, 4.4, 4.6 for the 13 fit-qualified symbologies, with `value` and `valuePath`, dynamic +symbology allowlist and scanner profile carried over. Files: `scene2/barcode/*` (new `ModuleFit`; the version 1 +`BarcodeFit` is called, not changed). The alignment tests of PR 11 are ported here. Tests: decode tests at three +resolutions for short, typical and longest values; centring within one dot; refusal content; below-minimum warning. +Review focus: no code prints below the stated minimum; quiet zones never cropped; integer arithmetic only. + +**P5. Bridge preview.** +Scope: sections 6.1, 6.3 (port implementation), 11.3. Files: `bridges/renderer/.../ZplRaster.java` (new), +`IsolatedRendererBridge.java` (diagnostics parsing, accepted versions, `preview`, target dpi in the request and in +the stock passed to the policy), `RendererJson.java`; `bridges/remote-renderer/adapter/.../RemoteRendererBridge.java` +(delegate). `ZplOutputPolicy.java` grammar is not changed. Tests: raster of every corpus ZPL from P0; refusal of +malformed graphics; the size limit; old-image behaviour (`RENDERER_TOO_OLD`); print and preview hashes equal; +mutation campaign over `ZplRaster`. Review focus: only validated bytes are rasterised; no font or image decoding on +the host; error mapping stays closed vocabulary; the version rule. + +**P6b. Preview service and UI.** +Scope: sections 6.2 to 6.4 first release. Files: new `DocumentPreviewService.java`, +`GenerationPreviewProcesses.java`, `GenerationPreviewWidget.java` (including the "Label preview" section it +attaches to the template); `GenerationAuthoringProcesses.java` (the relabel only); help text and labels for every +new field. It begins with the frontend +spike of section 6.4. Tests: permission matrix of section 6.3; nothing written to any table (assert row counts); +sample, stress and record data paths with a fake renderer; diagnostics to sentences; one browser check in the +host's local stack. Review focus: no persistence and no content in logs; record access checked before context is +built; messages name element, measured value and limit. + +**P7. Render target dpi.** +Scope: section 2.3. Files: `DocumentGenerationService.java`, `GenerationStore.java`, `GenerationProcesses.java` +(option on generate), `GenerationTables.java` (the request field), `DocumentGenerationSchema.java`, three +migration scripts, `docs/EXPECTED-PRINT-INTENT.md`; +`bridges/printing/.../PrintingDispatch.java` only if it must carry the value. Tests: digest framing (absent value +keeps old digests; present value changes them); refusal for version 1; MySQL, PostgreSQL and H2 suites; reprint +unchanged. Review focus: old rows and old digests are untouched; the target is validated against 203, 300, 600. + +### Follow-on + +| Id | Package | Branch | Repository | Size | Depends on | +|---|---|---|---|---|---| +| P3b | GS1 builder and human-readable line | `feat/scene2-gs1-hri` | this | M | P2, P3a | +| P4 | Graphics: scaled images, dither, reverse box | `feat/scene2-graphics` | this | M | P1 | +| P8 | Built-in asset store and upload | `feat/asset-store` | this | L | P6a, P1 | +| P9 | Version 1 to 2 conversion | `feat/scene-upgrade` | this | M | P2, P3a, P6b | +| P10 | Document availability and printer match | `feat/printable-documents` | this | M | P7 | +| P11 | Page documents: bundled CSS families, page preview | `feat/pdf-fonts-preview` | this | M | P2, P5, P6b | +| P12 | Starters, specimen and ruler | `feat/label-starters` | this | S | P2, P3a | +| P14 | Authoring guide and README | `docs/label-authoring` | this | S | first slice | +| P15 | Flexible stock (`atLeast`) | `feat/flexible-stock` | this | M | P10 | +| P16 | Renderer image build in CI | `ci/renderer-images` | this | S | decision 5 | +| F1 | Purpose-built preview component | `feat/document-preview` | `qqq-frontend-next` | L | P6b released | +| G1 | Forge: pins, schema, one version 2 document on dev | per Forge rules | `gg-forge`, `gg-forge-cd` | M | `1.4.0-RC.1` and images | +| G2 | Forge: remove workarounds, convert standard documents | per Forge rules | `gg-forge` | M | G1, P9, P10 | + +Notes for the follow-on packages: + +- **P3b** files: `scene2/barcode/gs1/*`, `scene2/barcode/Hri.java`. Tests: identifier table cases, check digits, + separator placement decoded back, caption wraps between identifiers. Review focus: do not duplicate what the + encoder already validates. +- **P4** files: `scene2/graphics/*`. Tests: goldens, upscaling warning, pixel limits. +- **P8** files: `GenerationAssetTables.java`, `GenerationAssetStore.java`, `GenerationAssetProcesses.java`, + `GenerationAssetChoices.java`, three migration scripts, `renderer/.../AssetInspector.java`, and the `dg:` branch + in `IsolatedRendererBridge.resolve` (coordinate with P5's owner; it is one method). Tests: upload limits and + refusals, hash addressing, permission, selection listing, the three databases. Review focus: no parsing on the + host; rights statement required; bytes never leave through a query API. +- **P9** files: `scene2/upgrade/SceneUpgrade.java`, `GenerationUpgradeProcesses.java`. Tests: each corpus scene + converts, and the converted scene's graphics match the original within one dot. +- **P10** files: `DocumentAvailability.java` (foundation), `bridges/printing/.../PrintableDocuments.java`. +- **P11** files: `PdfRenderer.java` and a new page rasteriser class in the renderer; the PDF branch of the preview. +- **P14** integrates each package's own notes (written under `docs/labels/` by that package) into + `renderer/README.md` and `docs/AUTHORING.md`, which no other package edits. + +## B.3 First on dev + +The smallest set that puts a real improvement on dev is the first slice: P0, P13, P6a, P1, P2, P3a, P5, P6b, P7. +It delivers physical-unit layout, anchors and alignment, measured text with fit rules and bundled fonts, centred +whole-dot barcodes that never go below their minimum, structured refusals, one design for every resolution, and a +preview on the template screen. + +``` + P0 ----+ + P13 ---+--> P1 --+--> P2 ---+ + +--> P3a --+ + P6a ---+--> P5 -------------+--> release/1.4 -> 1.4.0-RC.1 -> images -> G1 (Forge dev) + +--> P6b ------------+ + +--> P7 -------------+ +``` + +P0, P13 and P6a start together. P1 follows; P5, P6b and P7 need only P6a and run beside it. P2 and P3a start when +P1 merges. Then: one merge of `develop` into `release/1.4`, the two images built from `1.4.0-RC.1`, and Forge's G1 +(host pins first, image digests second). The acceptance for the slice is one version 2 GS1 label, previewed in +Forge on dev, then printed on the dev printer at its reported resolution and scanned. + +What follows, in value order: P3b and P12 (GS1 done properly, starters and the ruler), P8 (uploads everywhere), +P9 with G2 (convert the standard documents and delete the Forge workarounds), P10 (say why a document is not +offered), P4, P11, P14, F1, P15, P16. diff --git a/docs/security/fuzzing.md b/docs/security/fuzzing.md index 9517642..225c233 100644 --- a/docs/security/fuzzing.md +++ b/docs/security/fuzzing.md @@ -8,17 +8,36 @@ and the isolated renderer is treated as compromisable, so its output is untruste | Target | Module | Input | Campaign | |---|---|---|---| | Version 1 scene parser, `ZplRenderer.validate` | `renderer` | A `qqq-zpl-scene/1` source | `MutationCampaignTest.theVersionOneSceneParserAcceptsOnlyVersionOneScenes` | +| Version 2 scene parser, canvas and emitter behind the worker's routing, `RendererMain.process` with `VALIDATE` and, for about one input in four, `RENDER` | `renderer` | A `qqq-zpl-scene/2` source | `MutationCampaignTest.theVersionTwoSceneParserAcceptsOnlyVersionTwoScenes` | | Host printer gate, `ZplOutputPolicy.validate` | `bridges/renderer` | ZPL bytes returned by the worker | `MutationCampaignTest.theHostPrinterGateAcceptsOnlyTheRendererCommandSet` | The harness is `MutationCampaign`, one copy in each module's test tree (the modules share no test code). It -mutates valid inputs from the version 1 compatibility corpus (`renderer/src/test/resources/compat-v1`): bit -flips, byte and range edits, truncation, duplication and tokens of the format spliced in. Each campaign checks -unsafe acceptance, not only crashes: +mutates valid inputs from the version 1 compatibility corpus (`renderer/src/test/resources/compat-v1`) and the +version 2 layout cases (`renderer/src/test/resources/scene2/cases`): bit flips, byte and range edits, +truncation, duplication and tokens of the format spliced in. Each campaign checks unsafe acceptance, not only +crashes: -- **Scene parser.** An accepted input, read again by a second and differently configured JSON parser, is a - version 1 scene of known element types within the element limit. Any exception is a refusal, because the - worker turns every exception into its opaque error answer. A stack overflow or a failed acceptance check is a - finding. +- **Version 1 scene parser.** An accepted input, read again by a second and differently configured JSON parser, + is a version 1 scene of known element types within the element limit. A refusal is the parser's + `IllegalArgumentException` or exactly one of the JSON reader's exceptions for text that is not one JSON value + (`JsonParseException`, `JsonEOFException`, `MismatchedInputException`, `StreamConstraintsException`). Any + other exception (a `NullPointerException`, an index out of bounds), a stack overflow or a failed acceptance + check is a finding. +- **Version 2 scene parser.** The target is the worker's own routing, so a mutated source may also become a + version 1 scene, which is then held to the version 1 property. An accepted version 2 scene, read by the + second parser, has only the declared top-level properties, a physical design size, at most 1000 elements + with unique ids of the fixed pattern, only `box` and `group` elements with only their declared properties + (the other types are refused until their packages exist), groups at most four deep, every length a number + and a unit, and overrides only for the three resolutions and for declared ids. About one version 2 input in + four, chosen by its bytes so that a replay does the same, is rendered instead of validated: the label is read + back into a page, must be only the header, uncompressed graphic fields and the end of a label, must put no ink + outside the printable area, and validation of the same input must give the same answer (accepted with the same + findings, or refused with the same diagnostic). A refusal is the scene's own `Refusal`, the exception that + carries a diagnostic, and nothing else: the routing turns unreadable JSON and an unknown schema into one. A + plain `IllegalArgumentException` is a finding, because the package raises those for its own broken invariants + (`CANVAS_BOUNDS`, `CANVAS_LIMIT`, `LENGTH_NOT_ABSOLUTE`, `LENGTH_NOT_PHYSICAL`), which no scene may reach. The + one exception is a source the routing sends to the frozen version 1 parser, whose own refusal is a plain + `IllegalArgumentException`; such sources are validated only. The campaign also asserts that it rendered labels. - **Printer gate.** An accepted input contains only the commands the renderer may emit and no tilde command. A refusal is the gate's `IllegalArgumentException`, or the strict decoder's `CharacterCodingException` for field text that is not UTF-8. Any other failure, or a failed acceptance check, is a finding. @@ -51,18 +70,27 @@ public issue. Limits: inputs are at most 1 MiB, a campaign is at most 100000 iterations, and the corpus entries used are under 20 KiB. The harness writes no files, launches no process and reads no credentials. Generated ZPL is never -sent to a printer and generated scenes are only validated, not rendered. +sent to a printer. Generated version 2 scenes are rendered in memory on a 2 x 1 in stock at 203 dpi, a canvas of +374 by 171 dots; generated version 1 scenes are only validated. ## Adding a target A new parser of untrusted input adds one test method to the `MutationCampaignTest` of its module, with a corpus of valid inputs, a dictionary of the format's tokens, the refusal it declares and an independent check -of what it accepts, and adds a row to the table above. The version 2 scene parser and `ZplRaster` are the next -two targets. +of what it accepts, and adds a row to the table above. `ZplRaster` is the next target. ## Review record -- **Added:** 2026-11-11, with the two campaigns above. +- **Added:** 2026-11-11, with the version 1 scene parser and printer gate campaigns. +- **2026-11-12:** the version 2 scene parser campaign was added with the parser. The version 1 campaign had + counted any exception as a refusal, so a crash in the parser would have passed; it now accepts only the + declared refusals. Both seeds at 20000 iterations, and both at 100000, found nothing under the stricter rule. +- **2026-11-12, review of the scene 2 core:** the version 2 campaign had counted any `IllegalArgumentException` + as a refusal and only validated. It now accepts only a `Refusal` and renders about a quarter of its inputs, + reading each label back. Both seeds at 20000 iterations, and both at 100000, found nothing under the stricter + rule. The defect the review found by reading (a length such as `84600in` overflowing an `int` of micrometres, + an `ArithmeticException`) was already outside the old rule; the mutations did not reach it because they rarely + write a five-digit length, so it is covered by named tests instead (`LengthTest`, `Scene2Test`). - The first run of the printer gate campaign showed the `CharacterCodingException` refusal, which the property had not declared. It is a refusal, not a defect; the property now names it. - Not yet done: a weekly long campaign and the `docs/security/assurance.json` record that K22-TEST-25 also asks diff --git a/foundation/src/main/java/com/kingsrook/qbits/documentgeneration/GenerationPorts.java b/foundation/src/main/java/com/kingsrook/qbits/documentgeneration/GenerationPorts.java index 8b15a70..da1dfd3 100644 --- a/foundation/src/main/java/com/kingsrook/qbits/documentgeneration/GenerationPorts.java +++ b/foundation/src/main/java/com/kingsrook/qbits/documentgeneration/GenerationPorts.java @@ -127,11 +127,16 @@ public final class GenerationPorts /*************************************************************************** ** One renderer finding about a layout: a code from a closed vocabulary, the - ** element id the template author wrote, and numbers. It has no free text, so - ** no record value, context text or exception message can travel in it. + ** element id the template author wrote, numbers, and for a finding about the + ** scene source itself the JSON pointer of the offending place. It has no free + ** text, so no record value, context text or exception message can travel in + ** it: a pointer is only property names of the scene schema, array indexes + ** and declared element ids, which the renderer checks where it creates the + ** diagnostic and this record holds to their shape. ***************************************************************************/ - public record Diagnostic(String code, Severity severity, String element, List box, Map measured, Map limit) + public record Diagnostic(String code, Severity severity, String element, List box, Map measured, Map limit, String pointer) { + private static final java.util.regex.Pattern POINTER = java.util.regex.Pattern.compile("(/(0|[1-9][0-9]{0,3}|[A-Za-z][A-Za-z0-9_-]{0,31})){0,16}"); private static final java.util.Set WARNINGS = java.util.Set.of("TEXT_TRUNCATED", "TEXT_SMALL_FOR_DPI", "TEXT_LIGHT_FACE", "FIT_NOT_PROVEN", "BARCODE_MODULE_BELOW_RECOMMENDED", "QUIET_ZONE_INTRUDED", "ELEMENTS_OVERLAP", "IMAGE_UPSCALED", "DEVICE_UNIT_USED", "PRINTER_FONT_NOT_SHOWN"); private static final java.util.Set INFORMATION = java.util.Set.of("TEXT_SHRUNK", "TEXT_WRAPPED", "BARCODE_MODULE", "IMAGE_SCALED"); @@ -144,14 +149,29 @@ public final class GenerationPorts ERROR, WARNING, INFO } + /************************************************************************ + ** A diagnostic that points at no place in the scene source. + ************************************************************************/ + public Diagnostic(String code, Severity severity, String element, List box, Map measured, Map limit) + { + this(code, severity, element, box, measured, limit, null); + } + /************************************************************************ ** Admit only the vocabulary: an error is a FailureCode name, a warning or ** information is one of the fixed names. The element is absent or an ** author-written id, the box is absent or x, y, width and height in dots, - ** and measured values and limits are at most eight named integers each. + ** measured values and limits are at most eight named integers each, and + ** the pointer is absent or at most sixteen segments, each an array index + ** or a name of the same shape as an element id (the empty pointer is the + ** whole scene). ************************************************************************/ public Diagnostic { + if(pointer != null && !POINTER.matcher(pointer).matches()) + { + throw new IllegalArgumentException("A diagnostic pointer is made of names and indexes."); + } if(severity == null || code == null || !known(code, severity)) { throw new IllegalArgumentException("Unknown diagnostic code for its severity."); diff --git a/foundation/src/test/java/com/kingsrook/qbits/documentgeneration/GenerationPortsContractTest.java b/foundation/src/test/java/com/kingsrook/qbits/documentgeneration/GenerationPortsContractTest.java index 5488f8a..f1d1083 100644 --- a/foundation/src/test/java/com/kingsrook/qbits/documentgeneration/GenerationPortsContractTest.java +++ b/foundation/src/test/java/com/kingsrook/qbits/documentgeneration/GenerationPortsContractTest.java @@ -285,6 +285,29 @@ class GenerationPortsContractTest assertThrows(UnsupportedOperationException.class, () -> diagnostic.measured().put("widthDots", 1)); } + /*************************************************************************** + ** A finding about the scene source says where: a JSON pointer made only of + ** names and array indexes. A value, a sentence or an escaped segment has no + ** way into it, and a diagnostic made without one has none. + ***************************************************************************/ + @Test + void aDiagnosticPointerIsMadeOfNamesAndIndexes() + { + assertNull(new Diagnostic("SCENE_INVALID", Severity.ERROR, "caption", null, null, null).pointer()); + for(String pointer : List.of("", "/schema", "/design/width", "/elements/0", "/elements/12/fit/shrinkTo", "/elements/3/children/0/children/1/children/2/children/9999/thickness", + "/overrides/1/set/order_id-2/threshold", "/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p")) + { + assertEquals(pointer, new Diagnostic("SCENE_INVALID", Severity.ERROR, null, null, null, null, pointer).pointer()); + } + for(String pointer : List.of("elements", "/", "/elements/", "//elements", "/elements/01", "/elements/-1", "/elements/12345", "/Jane Example", "/record/name value", "/elements/0/~1", "/elements/0/fit\n", + "/9caption", "/" + "a".repeat(33), "/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q", "/(01)00012345678905")) + { + assertThrows(IllegalArgumentException.class, () -> new Diagnostic("SCENE_INVALID", Severity.ERROR, null, null, null, null, pointer), pointer); + } + var detail = new Diagnostic("SCENE_INVALID", Severity.ERROR, "caption", null, Map.of("unknownProperties", 2), Map.of("unknownProperties", 0), "/elements/1"); + assertEquals("/elements/1", new GenerationPorts.RenderException(FailureCode.SCENE_INVALID, detail).diagnostic().pointer()); + } + /*************************************************************************** ** A refusal carries only the error diagnostic with its own code, and its ** public message is still the code alone. diff --git a/renderer/README.md b/renderer/README.md index ec32bf7..29a159e 100644 --- a/renderer/README.md +++ b/renderer/README.md @@ -40,7 +40,11 @@ One UTF-8 JSON object followed by a newline enters stdin, and one JSON response `operation` is `RENDER`, `VALIDATE`, or `CATALOG`. A catalog request needs only protocol, requestId and operation and returns versioned barcode IDs and typed option metadata. `VALIDATE` compiles PDF template syntax or checks the ZPL scene structure plus asset identity. It does not evaluate arbitrary context, certify future layout fit, or promise that every source option will render with every record. Production publication should also render representative fixtures inside the qualified boundary. -A successful render returns protocol, requestId, `outcome: "OK"`, `rendererVersion: "qqq-renderer-0.1.0"`, `catalogVersion: "qqq-barcodes-9"`, mediaType, base64, SHA-256, decoded byte length and pageCount. Validation returns no document bytes. Errors return `outcome: "ERROR", code: "RENDER_FAILED"`; no source/context or exception stack is included. The host must independently check request identity, response shape, decoded size/hash, format policy, successful process termination and container removal. Worker-reported metadata is untrusted. An external parent must not persist raw stderr containing dependency diagnostics. +A successful render returns protocol, requestId, `outcome: "OK"`, `rendererVersion: "qqq-renderer-0.2.0"`, `catalogVersion: "qqq-barcodes-9"`, mediaType, base64, SHA-256, decoded byte length, pageCount and `diagnostics` (a list, empty except for a version 2 scene). Validation returns no document bytes. Errors return `outcome: "ERROR", code: "RENDER_FAILED"`; no source/context or exception stack is included. A version 2 scene refusal returns its own code and one diagnostic as `detail` (see "ZPL scene 2"). + +Renderer `0.2.0` adds, all optional: `"targetDpi"` (203, 300 or 600; the resolution a version 2 scene is rendered at, by default the stock's `dpi`; a version 1 scene with a target other than its stock's `dpi` is refused as `TARGET_REQUIRES_SCENE_2`) and `"diagnostics": "COLLECT"` (keep every element's refusal instead of stopping at the first; the outcome is then `REFUSED` when there is at least one, and the answer carries the diagnostics and no document: no `mediaType`, `base64`, `sha256`, `length` or `pageCount`, so a host that looks for `base64` without reading the outcome finds nothing to print). The operations `INSPECT_ASSET` and `UPGRADE_SCENE` are routed and answer `UNSUPPORTED_OPERATION` until their packages exist. A host bridge that accepts only `qqq-renderer-0.1.0` does not accept these answers: upgrade the host library before the image. The host must independently check request identity, response shape, decoded size/hash, format policy, successful process termination and container removal. Worker-reported metadata is untrusted. An external parent must not persist raw stderr containing dependency diagnostics. + +**Compatibility of version 1 answers.** The ZPL bytes of a version 1 scene are unchanged; the corpus in `src/test/resources/compat-v1` holds them byte for byte. The answer envelope around them changed in two ways in `0.2.0`, for a request a `0.1.0` host already sends. Every `OK` answer gains `diagnostics: []`: six fields for `VALIDATE` and eleven for `RENDER`, where there were five and ten. And a ZPL source that is not readable JSON, has no `schema` or has an unknown `schema` now answers `code: "SCENE_INVALID"` with a `detail` diagnostic (pointer `""`, or `/schema`), where `0.1.0` answered the opaque `RENDER_FAILED`. Every other version 1 refusal keeps its code. Nothing in a released host depended on `RENDER_FAILED` for those three cases: it is not a `GenerationPorts.FailureCode`, so `bridges/renderer` rejects such an answer as `WORKER_ERROR_REJECTED`, exactly as it rejects every `0.2.0` answer today (it accepts only `qqq-renderer-0.1.0` and the exact `0.1.0` field counts). The bridge that accepts `0.2.0` must take the extra `diagnostics` field, `detail` on an error, the `REFUSED` outcome, and `SCENE_INVALID` and the other diagnostic codes as failure codes. ## PDF authoring @@ -98,9 +102,42 @@ Before any text font/raster allocation, the scene is bounded to 64 `rasterText` Only `^XA`, `^XZ`, `^PW`, `^LL`, `^LH`, `^CI28`, `^FO`, `^A0N`, `^FH_`, `^FD`, `^FS`, `^GB` and `^GFA` are generated. Text is fully hex-encoded UTF-8, so `^`, `~` and `_` in record values cannot become new commands. Barcode/image raster fields use fixed uppercase hex and split below the 99,999-byte field limit. Placement uses overflow-safe bounds checks. No stored formats, downloads, network commands, persistent settings, cuts or resets are emitted. The printing boundary still needs independent output grammar enforcement because a compromised worker must not be trusted to report safe printer bytes. +## ZPL scene 2 + +A source with schema `qqq-zpl-scene/2` is a layout in physical units that renders at 203, 300 or 600 dpi. The worker chooses the code path from `schema` alone: version 1 is unchanged and frozen, and any other source is refused as `SCENE_INVALID`. The schema is `schema/zpl-scene-2.schema.json`; the design is `docs/design/LABEL-LAYOUT-FONTS-PREVIEW.md`. + +```json +{ + "schema": "qqq-zpl-scene/2", + "design": { "width": "4in", "height": "1in" }, + "elements": [ + { "id": "frame", "type": "box", "thickness": "0.5pt" }, + { "id": "panel", "type": "group", "anchor": "center", "width": "50%", "height": "60%", "children": [ + { "id": "mark", "type": "box", "anchor": "bottom-right", "x": "1mm", "y": "1mm", "width": "6mm", "height": "3mm", "fill": true } ] }, + { "id": "rule", "type": "box", "anchor": "bottom-center", "y": "2mm", "width": "80%", "height": "0.2mm", "thickness": "0.2mm" } + ], + "overrides": [ { "when": { "dpi": [203] }, "set": { "frame": { "thickness": "1dot" } } } ] +} +``` + +- **Lengths** are strings of a number (up to three decimals) and a unit: `mm`, `in`, `pt` (1/72 in), `%` (of the container's width for `x` and `width`, of its height for `y` and `height`) and `dot`. The most a length may be is 2 m (`2000mm`, `78.74in`, `5669.292pt`), `32000dot` or `100%`; anything longer, up to the largest number the syntax can write, is `SCENE_INVALID` at its pointer. A physical length becomes whole micrometres, then dots at the render target: a position to the nearest dot, a box width or height rounded down, a thickness to the nearest dot and never less than one. A `dot` length outside an override raises the warning `DEVICE_UNIT_USED`. +- **Coordinates** start at the top left of the printable area, the stock less its margins. In dots the printable area is `floor((width - left - right) x dpi)` wide and `floor((height - top - bottom) x dpi)` high, which is how a box size is rounded, and it starts at `ceil(left x dpi)`, `ceil(top x dpi)`, moved back by one dot where it would otherwise pass the page edge. So a box of exactly the printable width and height fits at 203, 300 and 600 dpi alike. No ink is ever a whole dot inside a margin; the right and bottom edges may pass theirs by a fraction of a dot (less than 0.125 mm at 203 dpi). `design` states the stock the layout was drawn for; the request's stock must match it within 0.5 mm on each side (`STOCK_MISMATCH`). +- **Every element** has a unique `id` (`[A-Za-z][A-Za-z0-9_-]{0,31}`), a `type`, an `anchor` (one of nine points, default `top-left`: the same point of the box is placed on that point of its container), `x` and `y` (distances inward from the anchored edge; on a centred axis a shift that may be negative), `width` and `height` (omitted: the rest of the container), and `align` and `valign` (where content sits in the box). +- **`box`** has `thickness` and optional `fill`; a line is a box whose width or height is its thickness. **`group`** has `children`, placed relative to its box; groups nest four deep. A scene holds at most 1000 elements, and its elements may paint at most sixteen times its printable area in all, an overlap counted each time: a filled box counts its area, an outline its four bars, a group nothing. The element that would pass that is refused as `SCENE_INVALID` at its pointer, with its box and `measured.paintedPercent` against `limit.paintedPercent` of 1600. A real label paints less than twice its area; the limit holds the worst scene to sixteen fills of the canvas where 1000 were possible. +- **`text`, `barcode` and `image`** are parsed and placed by renderer `0.2.0` and then refused as `RENDERER_TOO_OLD`, with the box they would have had, until their packages are in the renderer. +- **`overrides`** replace properties of declared elements at the listed resolutions and are the only place a scene may depend on resolution. They are checked for all three resolutions whatever the request's target. **`samples`** are accepted and ignored by rendering. +- **Output.** The whole label is composed on one 1-bit canvas and emitted as uncompressed `^GFA` fields: rows are cut into bands wherever eight or more blank rows separate ink, each band is trimmed to its inked columns, and a band is continued in a further field at 99,999 bytes. Only `^XA`, `^PW`, `^LL`, `^LH`, `^CI28`, `^FO`, `^GFA`, `^FS` and `^XZ` are emitted. +- **Diagnostics.** A refusal is one diagnostic: `code`, `severity`, the `element` id, the element's `box` in dots from the top left of the printable area, and `measured` and `limit` as named integers. `SCENE_INVALID` also carries `pointer`, the JSON pointer of the offending place. A diagnostic names only an id the scene declares and points only through schema property names, array indexes and declared ids, so no record value, sample or unknown property name can leave the worker in one. A stock the printer language cannot address stays the opaque `RENDER_FAILED`, as for version 1. + +```json +{ "code": "ELEMENT_OUTSIDE_PRINTABLE_AREA", "severity": "ERROR", "element": "mark", "box": [719, 0, 79, 39], + "measured": { "bottomDots": 39, "leftDots": 719, "rightDots": 798, "topDots": 0 }, + "limit": { "bottomDots": 155, "leftDots": 0, "rightDots": 764, "topDots": 0 } } +``` + ## Limits -Sources: 262,144 characters. Context: depth 16, 10,000 nodes, 65,536 characters per string, 262,144 aggregate text characters. Stock: 1 mm–2 m, valid margins, 72–1200 DPI (ZPL additionally bounds width to 9,999 dots and height to 32,000). Assets: 100 entries, 2 MiB each, 8 MiB aggregate; image dimensions ≤4096 and ≤4 million pixels. Expanded HTML: 2 million characters and 20,000 elements. PDF pages: 1–500, checked after rendering. Output: caller limit, hard maximum 100 MiB; normal host default is 20 MiB. ZPL: 1,000 elements. Barcodes: 4,000-character payload, scale 1–16, bounded quiet zones, ≤4 million pixels. +Sources: 262,144 characters. Context: depth 16, 10,000 nodes, 65,536 characters per string, 262,144 aggregate text characters. Stock: 1 mm–2 m, valid margins, 72–1200 DPI (ZPL additionally bounds width to 9,999 dots and height to 32,000). Assets: 100 entries, 2 MiB each, 8 MiB aggregate; image dimensions ≤4096 and ≤4 million pixels. Expanded HTML: 2 million characters and 20,000 elements. PDF pages: 1–500, checked after rendering. Output: caller limit, hard maximum 100 MiB; normal host default is 20 MiB. ZPL: 1,000 elements; a version 2 scene paints at most 16 times its printable area. Barcodes: 4,000-character payload, scale 1–16, bounded quiet zones, ≤4 million pixels. The PDF engine and font/image decoders can consume resources before post-render limits fire. A qualified container's independent CPU/memory/PID/wall limits remain mandatory for both validation and rendering. These ceilings are not performance guarantees; load testing and font/locale/large-batch qualification remain outstanding. diff --git a/renderer/schema/zpl-scene-2.schema.json b/renderer/schema/zpl-scene-2.schema.json new file mode 100644 index 0000000..502055f --- /dev/null +++ b/renderer/schema/zpl-scene-2.schema.json @@ -0,0 +1,121 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "QQQ ZPL scene 2", + "description": "A label layout in physical units, rendered at 203, 300 or 600 dpi. Coordinates start at the top left of the printable area (the stock less its margins). The renderer is the authority: it also enforces unique element ids, 1000 elements in all, groups four deep, magnitudes (2000 mm, 100 %, 32000 dot), a negative x or y only on a centred axis, overrides only for declared ids, a stock within 0.5 mm of the design, every box inside the printable area and at least one dot in size, and at most sixteen times the printable area painted in all. A refusal is a diagnostic with a code, the element id and numbers; SCENE_INVALID carries the JSON pointer of the offending place.", + "type": "object", + "additionalProperties": false, + "required": ["schema", "design", "elements"], + "properties": { + "schema": {"const": "qqq-zpl-scene/2"}, + "design": { + "description": "The stock the layout was drawn for.", + "type": "object", + "additionalProperties": false, + "required": ["width", "height"], + "properties": {"width": {"$ref": "#/$defs/physical"}, "height": {"$ref": "#/$defs/physical"}} + }, + "defaults": { + "description": "Defaults for text elements.", + "type": "object", + "additionalProperties": false, + "properties": {"font": {"type": "string", "pattern": "^[A-Za-z0-9_-][A-Za-z0-9_.-]{0,99}$"}, "size": {"$ref": "#/$defs/physical"}} + }, + "samples": { + "description": "Named synthetic contexts for a preview. Accepted and ignored by rendering.", + "type": "object", + "maxProperties": 16, + "additionalProperties": {"type": "object"} + }, + "elements": {"type": "array", "items": {"$ref": "#/$defs/element"}}, + "overrides": { + "description": "The only place a scene may depend on resolution. Each entry replaces properties of declared elements at the listed resolutions; later entries win. `id`, `type` and `children` cannot change.", + "type": "array", + "maxItems": 32, + "items": { + "type": "object", + "additionalProperties": false, + "required": ["when", "set"], + "properties": { + "when": { + "type": "object", + "additionalProperties": false, + "required": ["dpi"], + "properties": {"dpi": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"enum": [203, 300, 600]}}} + }, + "set": { + "type": "object", + "minProperties": 1, + "propertyNames": {"$ref": "#/$defs/id"}, + "additionalProperties": {"type": "object", "minProperties": 1, "not": {"anyOf": [{"required": ["id"]}, {"required": ["type"]}, {"required": ["children"]}]}} + } + } + } + } + }, + "$defs": { + "id": {"type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_-]{0,31}$"}, + "length": { + "description": "A number of up to three decimals and a unit. `%` is a share of the container's width (x, width) or height (y, height); `dot` is one printer dot and is whole.", + "type": "string", + "pattern": "^-?(0|[1-9][0-9]{0,4})(\\.[0-9]{1,3})?(mm|in|pt|%)$|^-?(0|[1-9][0-9]{0,4})dot$" + }, + "size": {"type": "string", "pattern": "^(0|[1-9][0-9]{0,4})(\\.[0-9]{1,3})?(mm|in|pt|%)$|^(0|[1-9][0-9]{0,4})dot$"}, + "physical": {"type": "string", "pattern": "^(0|[1-9][0-9]{0,4})(\\.[0-9]{1,3})?(mm|in|pt)$"}, + "absolute": {"type": "string", "pattern": "^(0|[1-9][0-9]{0,4})(\\.[0-9]{1,3})?(mm|in|pt)$|^(0|[1-9][0-9]{0,4})dot$"}, + "placement": { + "type": "object", + "required": ["id", "type"], + "properties": { + "id": {"$ref": "#/$defs/id"}, + "anchor": { + "description": "The point of the box that is placed on the same point of its container.", + "enum": ["top-left", "top-center", "top-right", "middle-left", "center", "middle-right", "bottom-left", "bottom-center", "bottom-right"], + "default": "top-left" + }, + "x": {"$ref": "#/$defs/length", "default": "0mm", "description": "Distance inward from the anchored edge; on a centred axis a shift right, which may be negative."}, + "y": {"$ref": "#/$defs/length", "default": "0mm", "description": "Distance inward from the anchored edge; on a centred axis a shift down, which may be negative."}, + "width": {"$ref": "#/$defs/size", "description": "Rounded down to whole dots. Omitted: the rest of the container."}, + "height": {"$ref": "#/$defs/size", "description": "Rounded down to whole dots. Omitted: the rest of the container."}, + "align": {"enum": ["left", "center", "right"], "default": "left"}, + "valign": {"enum": ["top", "middle", "bottom"], "default": "top"} + } + }, + "element": { + "oneOf": [ + { + "$ref": "#/$defs/placement", + "description": "A rectangle. A line is a box whose width or height is its thickness.", + "unevaluatedProperties": false, + "properties": {"type": {"const": "box"}, "thickness": {"$ref": "#/$defs/absolute", "description": "Nearest dot, never less than one. Required unless `fill` is true."}, "fill": {"type": "boolean", "default": false}}, + "anyOf": [{"required": ["thickness"]}, {"required": ["fill"], "properties": {"fill": {"const": true}}}] + }, + { + "$ref": "#/$defs/placement", + "description": "A box whose children are positioned relative to it.", + "unevaluatedProperties": false, + "required": ["children"], + "properties": {"type": {"const": "group"}, "children": {"type": "array", "items": {"$ref": "#/$defs/element"}}} + }, + { + "$ref": "#/$defs/placement", + "description": "Text. Parsed and placed by renderer 0.2.0 and refused as RENDERER_TOO_OLD until the text package; the values of these properties are not yet read.", + "unevaluatedProperties": false, + "properties": {"type": {"const": "text"}, "value": {}, "valuePath": {}, "font": {}, "size": {}, "lineHeight": {}, "fit": {}, "maxLength": {}, "threshold": {}, "reverse": {}, "rotation": {"enum": [0, 90, 180, 270]}} + }, + { + "$ref": "#/$defs/placement", + "description": "A barcode. Parsed and placed by renderer 0.2.0 and refused as RENDERER_TOO_OLD until the barcode package; the values of these properties are not yet read.", + "unevaluatedProperties": false, + "properties": {"type": {"const": "barcode"}, "symbology": {}, "symbologyPath": {}, "allowedSymbologies": {}, "value": {}, "valuePath": {}, "minModule": {}, "maxModule": {}, "quiet": {}, "gs1": {}, "hri": {}, + "scannerSymbologyPath": {}, "aimIdentifierPath": {}, "rotation": {"enum": [0, 90, 180, 270]}} + }, + { + "$ref": "#/$defs/placement", + "description": "An image. Parsed and placed by renderer 0.2.0 and refused as RENDERER_TOO_OLD until the graphics package; the values of these properties are not yet read.", + "unevaluatedProperties": false, + "properties": {"type": {"const": "image"}, "asset": {}, "fit": {}, "mode": {}, "rotation": {"enum": [0, 90, 180, 270]}} + } + ] + } + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/AssetInspector.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/AssetInspector.java new file mode 100644 index 0000000..c90f28d --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/AssetInspector.java @@ -0,0 +1,54 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer; + + +import java.util.Map; +import com.fasterxml.jackson.databind.JsonNode; + + +/******************************************************************************* + ** `INSPECT_ASSET`: parse an uploaded font or image inside the isolated worker + ** and report what it is, so the application never parses one. The operation is + ** routed here from the first version 2 renderer so the wire is settled; the + ** inspection itself is a later package, and until then the operation is + ** refused by name. + *******************************************************************************/ +public final class AssetInspector +{ + /*************************************************************************** + ** Utility class. + ***************************************************************************/ + private AssetInspector() + { + } + + + + /*************************************************************************** + ** Answer one `INSPECT_ASSET` request. + ***************************************************************************/ + public static Map inspect(JsonNode request) + { + Wire.text(request.get("requestId"), 128); + throw new IllegalArgumentException("UNSUPPORTED_OPERATION"); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/RenderInput.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/RenderInput.java index 498f2fe..0d02807 100644 --- a/renderer/src/main/java/com/kingsrook/qbits/renderer/RenderInput.java +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/RenderInput.java @@ -47,6 +47,15 @@ public final class RenderInput public final Stock stock; public final Map assets = new LinkedHashMap<>(); + ///////////////////////////////////////////////////////////////////////////////////////////////// + // the render target of a version 2 scene, or null for the stock's own resolution; and whether // + // the caller asked for every diagnostic rather than the first refusal // + ///////////////////////////////////////////////////////////////////////////////////////////////// + public final Integer targetDpi; + public final boolean collectDiagnostics; + + public static final String RESERVED_ASSET_PREFIX = "qqq-"; + /******************************************************************************* ** Physical stock uses exact integer microns; dots are checked separately. @@ -95,6 +104,16 @@ public final class RenderInput validateData(context, 0, new int[2]); maximumBytes = Wire.integer(root.get("maximumBytes"), 1, 100 * 1024 * 1024); stock = stock(root.path("stock")); + targetDpi = root.has("targetDpi") ? Integer.valueOf(Wire.integer(root.get("targetDpi"), 203, 600)) : null; + if(targetDpi != null && !java.util.List.of(203, 300, 600).contains(targetDpi)) + { + throw new IllegalArgumentException("INVALID_TARGET_DPI"); + } + collectDiagnostics = root.has("diagnostics"); + if(collectDiagnostics && !"COLLECT".equals(root.get("diagnostics").textValue())) + { + throw new IllegalArgumentException("INVALID_DIAGNOSTICS_MODE"); + } JsonNode supplied = root.path("assets"); if(!supplied.isArray() || supplied.size() > 100) { @@ -135,6 +154,17 @@ public final class RenderInput } + /******************************************************************************* + ** The supplied assets whose names begin with the prefix reserved for the faces + ** bundled in the renderer. A version 2 scene may not carry one; the text + ** package refuses it as `RESERVED_FONT_NAME`. Version 1 never had the rule. + *******************************************************************************/ + public java.util.List reservedAssetNames() + { + return (assets.keySet().stream().filter(name -> name.startsWith(RESERVED_ASSET_PREFIX)).toList()); + } + + /******************************************************************************* ** Shared immutable physical stock parser for rendering and fit measurement. *******************************************************************************/ diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/RendererMain.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/RendererMain.java index 0cd1c57..155a805 100644 --- a/renderer/src/main/java/com/kingsrook/qbits/renderer/RendererMain.java +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/RendererMain.java @@ -29,8 +29,14 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Base64; +import java.util.LinkedHashMap; import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.kingsrook.qbits.renderer.scene2.Diagnostics; +import com.kingsrook.qbits.renderer.scene2.Refusal; +import com.kingsrook.qbits.renderer.scene2.Scene2; +import com.kingsrook.qbits.renderer.scene2.upgrade.SceneUpgrade; import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; @@ -41,7 +47,7 @@ import org.apache.pdfbox.pdmodel.PDDocument; *******************************************************************************/ public final class RendererMain { - public static final String VERSION = "qqq-renderer-0.1.0"; + public static final String VERSION = "qqq-renderer-0.2.0"; /******************************************************************************* @@ -84,8 +90,7 @@ public final class RendererMain catch(Exception expected) { // A failed invocation exposes no template/context text or stack trace. - output.write(Wire.JSON.writeValueAsBytes(Map.of("protocol", Wire.PROTOCOL, "requestId", requestId, "outcome", "ERROR", - "rendererVersion", VERSION, "catalogVersion", Barcodes.VERSION, "code", errorCode(expected)))); + output.write(Wire.JSON.writeValueAsBytes(error(requestId, expected))); output.write('\n'); } } @@ -93,13 +98,40 @@ public final class RendererMain /******************************************************************************* - ** Only fixed public fit refusals cross the process boundary. Encoder messages - ** and all other failures remain opaque because they can contain source data. + ** The error answer. A version 2 refusal adds its one diagnostic as `detail`: + ** a code, a declared element id, a pointer of schema names and numbers. + *******************************************************************************/ + static Map error(String requestId, Exception failure) + { + Map answer = new LinkedHashMap<>(); + answer.put("protocol", Wire.PROTOCOL); + answer.put("requestId", requestId); + answer.put("outcome", "ERROR"); + answer.put("rendererVersion", VERSION); + answer.put("catalogVersion", Barcodes.VERSION); + answer.put("code", errorCode(failure)); + if(failure instanceof Refusal refusal) + { + answer.put("detail", refusal.diagnostic().wire()); + } + return (answer); + } + + + /******************************************************************************* + ** Only fixed public refusals cross the process boundary: the fit refusals, an + ** operation this renderer does not perform, and the code of a version 2 + ** diagnostic. Encoder messages and all other failures remain opaque because + ** they can contain source data. *******************************************************************************/ static String errorCode(Exception failure) { String message = failure.getMessage(); - if(failure instanceof IllegalArgumentException && message != null && java.util.Set.of( + if(failure instanceof Refusal refusal) + { + return (refusal.diagnostic().code()); + } + if(failure instanceof IllegalArgumentException && message != null && java.util.Set.of("UNSUPPORTED_OPERATION", "BARCODE_FIT_TOO_SMALL", "BARCODE_FIT_UNSUPPORTED", "BARCODE_FIT_BOX_OUTSIDE_STOCK", "BARCODE_FIT_GEOMETRY_MISMATCH", "INVALID_BARCODE_FIT_PROFILE", "INVALID_BARCODE_FIT_REQUEST", "BARCODE_SYMBOLOGY_NOT_ALLOWED", "SYMBOLOGY_ALLOWLIST_REQUIRED", "SCANNER_BARCODE_PROFILE_UNSUPPORTED", @@ -154,29 +186,100 @@ public final class RendererMain return (Map.of("protocol", Wire.PROTOCOL, "requestId", Wire.text(root.get("requestId"), 128), "outcome", "OK", "rendererVersion", VERSION, "catalogVersion", Barcodes.VERSION, "fit", BarcodeFit.fit(request).metadata())); } - RenderInput input = new RenderInput(root); - if("VALIDATE".equals(input.operation)) + String operation = root.path("operation").asText(); + if("INSPECT_ASSET".equals(operation) || "UPGRADE_SCENE".equals(operation)) { - if("PDF".equals(input.format)) + if(!Wire.PROTOCOL.equals(root.path("protocol").asText())) { - PdfRenderer.validate(input); + throw new IllegalArgumentException("INVALID_PROTOCOL"); } - else - { - ZplRenderer.validate(input); - } - return (Map.of("protocol", Wire.PROTOCOL, "requestId", input.requestId, "outcome", "OK", "rendererVersion", VERSION, "catalogVersion", Barcodes.VERSION)); + return ("INSPECT_ASSET".equals(operation) ? AssetInspector.inspect(root) : SceneUpgrade.upgrade(root)); } - byte[] bytes = "PDF".equals(input.format) ? PdfRenderer.render(input) : ZplRenderer.render(input); + RenderInput input = new RenderInput(root); + Diagnostics diagnostics = new Diagnostics(input.collectDiagnostics); + byte[] bytes = null; int pageCount = 1; - if("PDF".equals(input.format)) + if("ZPL".equals(input.format)) { + bytes = label(input, diagnostics); + } + else if("VALIDATE".equals(input.operation)) + { + PdfRenderer.validate(input); + } + else + { + bytes = PdfRenderer.render(input); try(PDDocument document = Loader.loadPDF(bytes)) { pageCount = document.getNumberOfPages(); } } - return (Map.of("protocol", Wire.PROTOCOL, "requestId", input.requestId, "outcome", "OK", "rendererVersion", VERSION, "catalogVersion", Barcodes.VERSION, - "mediaType", "PDF".equals(input.format) ? "application/pdf" : "application/vnd.zebra-zpl", "base64", Base64.getEncoder().encodeToString(bytes), "sha256", Wire.sha256(bytes), "length", bytes.length, "pageCount", pageCount)); + Map answer = new LinkedHashMap<>(); + answer.put("protocol", Wire.PROTOCOL); + answer.put("requestId", input.requestId); + answer.put("outcome", diagnostics.refused() ? "REFUSED" : "OK"); + answer.put("rendererVersion", VERSION); + answer.put("catalogVersion", Barcodes.VERSION); + //////////////////////////////////////////////////////////////////////////////////////////// + // a refused answer carries no document: a host that looks for `base64` without reading // + // the outcome must find nothing it could print // + //////////////////////////////////////////////////////////////////////////////////////////// + if(bytes != null && !diagnostics.refused()) + { + answer.put("mediaType", "PDF".equals(input.format) ? "application/pdf" : "application/vnd.zebra-zpl"); + answer.put("base64", Base64.getEncoder().encodeToString(bytes)); + answer.put("sha256", Wire.sha256(bytes)); + answer.put("length", bytes.length); + answer.put("pageCount", pageCount); + } + answer.put("diagnostics", diagnostics.wire()); + return (answer); + } + + + /******************************************************************************* + ** Route a label by the `schema` of its scene alone. Version 1 takes exactly + ** the path it always took and prints at the stock's own resolution; version 2 + ** takes the scene 2 package; anything else is refused by name. The answer is + ** the label, or null when the request only validates or a version 2 scene + ** was refused while its diagnostics were collected. + *******************************************************************************/ + private static byte[] label(RenderInput input, Diagnostics diagnostics) throws Exception + { + boolean validate = "VALIDATE".equals(input.operation); + JsonNode scene; + try + { + scene = Wire.JSON.readTree(input.source); + } + catch(JsonProcessingException expected) + { + throw diagnostics.refuse("SCENE_INVALID", null, "", null, null, null); + } + String schema = scene.path("schema").textValue(); + if(Scene2.SCHEMA.equals(schema)) + { + if(validate) + { + Scene2.validate(input, scene, diagnostics); + return (null); + } + return (Scene2.render(input, scene, diagnostics)); + } + if(!"qqq-zpl-scene/1".equals(schema)) + { + throw diagnostics.refuse("SCENE_INVALID", null, scene.isObject() ? "/schema" : "", null, null, null); + } + if(input.targetDpi != null && input.targetDpi != input.stock.dpi()) + { + throw diagnostics.refuse("TARGET_REQUIRES_SCENE_2", null, null, null, Map.of("dpi", input.targetDpi), Map.of("dpi", input.stock.dpi())); + } + if(validate) + { + ZplRenderer.validate(input); + return (null); + } + return (ZplRenderer.render(input)); } } diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/BoxElement.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/BoxElement.java new file mode 100644 index 0000000..c908fbe --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/BoxElement.java @@ -0,0 +1,156 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.Set; +import com.kingsrook.qbits.renderer.RenderInput; + + +/******************************************************************************* + ** A rectangle: an outline of a stated thickness, or filled. A line is a box + ** whose width or height is its thickness, and on that axis it takes the + ** thickness rule (nearest dot, never less than one) so a thin rule does not + ** vanish; every other size is rounded down. + *******************************************************************************/ +final class BoxElement implements SceneElement +{ + private final String id; + private final Placement placement; + private final Length thickness; + private final boolean fill; + + + + /*************************************************************************** + ** The factory for `type: "box"`. + ***************************************************************************/ + static final class Factory implements ElementFactory + { + /************************************************************************ + ** A box reads its outline thickness and whether it is filled. + ************************************************************************/ + @Override + public Set properties() + { + return (Set.of("thickness", "fill")); + } + + + + /************************************************************************ + ** Build one box. + ************************************************************************/ + @Override + public SceneElement create(ElementSource source) + { + return (new BoxElement(source)); + } + } + + + + /*************************************************************************** + ** An outline needs a thickness that is more than nothing; a filled box + ** needs none. + ***************************************************************************/ + private BoxElement(ElementSource source) + { + id = source.id(); + placement = source.placement(); + fill = source.flag("fill", false); + thickness = source.length("thickness", null, ElementSource.ABSOLUTE); + if(thickness == null ? !fill : thickness.thousandths() <= 0) + { + throw source.invalid("thickness"); + } + } + + + + /*************************************************************************** + ** The id the scene declares for the box. + ***************************************************************************/ + @Override + public String id() + { + return (id); + } + + + + /*************************************************************************** + ** The standing rules, except on an axis where the box is a line. + ***************************************************************************/ + @Override + public Frame place(Layout layout) + { + int width = thickness != null && thickness.equals(placement.width()) ? thickness.thickness(layout.dpi()) : layout.width(placement); + int height = thickness != null && thickness.equals(placement.height()) ? thickness.thickness(layout.dpi()) : layout.height(placement); + return (layout.frame(placement.anchor(), layout.x(placement), layout.y(placement), width, height)); + } + + + + /*************************************************************************** + ** The whole box when it is painted whole, else its four bars; the corners, + ** where two bars cross, are painted twice and counted twice. + ***************************************************************************/ + @Override + public long paintedDots(Frame frame, int dpi) + { + int bar = bar(frame, dpi); + return (bar < 0 ? SceneElement.super.paintedDots(frame, dpi) : 2L * bar * (frame.width() + frame.height())); + } + + + + /*************************************************************************** + ** The thickness of the outline in dots, or -1 when the box is painted whole: + ** it is filled, or its bars would meet. + ***************************************************************************/ + private int bar(Frame frame, int dpi) + { + int bar = fill ? frame.width() : thickness.thickness(dpi); + return (bar * 2 >= Math.min(frame.width(), frame.height()) ? -1 : bar); + } + + + + /*************************************************************************** + ** Four bars inside the box, or the whole box when it is filled or when the + ** bars would meet. + ***************************************************************************/ + @Override + public void paint(Canvas canvas, Frame frame, RenderInput input, Diagnostics diagnostics) + { + int bar = bar(frame, canvas.dpi()); + if(bar < 0) + { + canvas.fill(frame, true); + return; + } + canvas.fill(new Frame(frame.x(), frame.y(), frame.width(), bar), true); + canvas.fill(new Frame(frame.x(), frame.bottom() - bar, frame.width(), bar), true); + canvas.fill(new Frame(frame.x(), frame.y(), bar, frame.height()), true); + canvas.fill(new Frame(frame.right() - bar, frame.y(), bar, frame.height()), true); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Canvas.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Canvas.java new file mode 100644 index 0000000..fc77127 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Canvas.java @@ -0,0 +1,262 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +/******************************************************************************* + ** The whole label as one 1-bit picture at the target resolution: a dot is ink + ** or it is not. Elements paint in list order, so a later element covers an + ** earlier one, white as well as black. Painting outside the canvas is an error + ** in the caller, never a silent clip, so ink cannot be lost at an edge. + *******************************************************************************/ +public final class Canvas +{ + public static final long MAXIMUM_DOTS = 64_000_000L; + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // what one scene may paint, in canvases: the dots its elements paint, an overlap counted each // + // time, may total sixteen times the canvas. A real label paints less than two (a solid // + // ground, reversed panels, rules, text and codes), so sixteen refuses no layered design, and // + // it holds the worst scene to 16 x 64 million dots, 128 MB of byte writes, where the 1000 // + // full-canvas boxes the element limit allows were 8 GB // + ///////////////////////////////////////////////////////////////////////////////////////////////// + public static final int MAXIMUM_COATS = 16; + + private final int width; + private final int height; + private final int dpi; + private final int stride; + private final byte[] bits; + private long painted; + + + + /*************************************************************************** + ** A blank canvas the size of the printable area, in dots. + ***************************************************************************/ + public Canvas(int width, int height, int dpi) + { + if(width < 1 || height < 1 || (long) width * height > MAXIMUM_DOTS) + { + throw new IllegalArgumentException("CANVAS_LIMIT"); + } + this.width = width; + this.height = height; + this.dpi = dpi; + stride = (width + 7) / 8; + bits = new byte[stride * height]; + } + + + + /*************************************************************************** + ** Columns of dots. + ***************************************************************************/ + public int width() + { + return (width); + } + + + + /*************************************************************************** + ** Rows of dots. + ***************************************************************************/ + public int height() + { + return (height); + } + + + + /*************************************************************************** + ** The resolution the canvas was composed at, in dots per inch. + ***************************************************************************/ + public int dpi() + { + return (dpi); + } + + + + /*************************************************************************** + ** Count the dots an element is about to paint. When they would take the + ** scene past `MAXIMUM_COATS` canvases nothing is counted and the answer is + ** no, so that element is refused and a later, smaller one may still paint. + ***************************************************************************/ + public boolean admit(long dots) + { + if(painted + dots > MAXIMUM_COATS * (long) width * height) + { + return (false); + } + painted += dots; + return (true); + } + + + + /*************************************************************************** + ** What the scene would have painted with `dots` more, as a percentage of + ** the canvas, rounded up. + ***************************************************************************/ + public int paintedPercent(long dots) + { + long area = (long) width * height; + return (Math.toIntExact(((painted + dots) * 100 + area - 1) / area)); + } + + + + /*************************************************************************** + ** Whether one dot holds ink. + ***************************************************************************/ + public boolean get(int x, int y) + { + return ((bits[index(x, y)] & (128 >> (x & 7))) != 0); + } + + + + /*************************************************************************** + ** Put ink on one dot, or take it off. + ***************************************************************************/ + public void set(int x, int y, boolean ink) + { + int index = index(x, y); + int mask = 128 >> (x & 7); + bits[index] = (byte) (ink ? bits[index] | mask : bits[index] & ~mask); + } + + + + /*************************************************************************** + ** Paint a box black, or white to clear what an earlier element painted. + ***************************************************************************/ + public void fill(Frame area, boolean ink) + { + paint(area, ink ? Paint.BLACK : Paint.WHITE); + } + + + + /*************************************************************************** + ** Reverse a box: black becomes white and white becomes black. + ***************************************************************************/ + public void invert(Frame area) + { + paint(area, Paint.REVERSE); + } + + + + /*************************************************************************** + ** What painting does to the dots of a box. + ***************************************************************************/ + private enum Paint + { + BLACK, WHITE, REVERSE + } + + + + /*************************************************************************** + ** Paint a box a byte of dots at a time, so that a scene of many large filled + ** boxes costs memory writes rather than a visit to every dot. A box with no + ** dots paints nothing. + ***************************************************************************/ + private void paint(Frame area, Paint paint) + { + if(area.width() < 1 || area.height() < 1) + { + return; + } + index(area.x(), area.y()); + index(area.right() - 1, area.bottom() - 1); + for(int y = area.y(); y < area.bottom(); y++) + { + int x = area.x(); + while(x < area.right()) + { + int first = x & 7; + int count = Math.min(8 - first, area.right() - x); + int mask = (0xFF >> first) & (0xFF << (8 - first - count)); + int index = y * stride + (x >> 3); + int painted = switch(paint) + { + case BLACK -> bits[index] | mask; + case WHITE -> bits[index] & ~mask; + case REVERSE -> bits[index] ^ mask; + }; + bits[index] = (byte) painted; + x += count; + } + } + } + + + + /*************************************************************************** + ** The first inked column of a row, or -1 when the row is blank. + ***************************************************************************/ + public int firstInk(int row) + { + for(int x = 0; x < width; x++) + { + if(get(x, row)) + { + return (x); + } + } + return (-1); + } + + + + /*************************************************************************** + ** The last inked column of a row, or -1 when the row is blank. + ***************************************************************************/ + public int lastInk(int row) + { + for(int x = width - 1; x >= 0; x--) + { + if(get(x, row)) + { + return (x); + } + } + return (-1); + } + + + + /*************************************************************************** + ** The byte that holds a dot, after refusing a dot that is not on the canvas. + ***************************************************************************/ + private int index(int x, int y) + { + if(x < 0 || y < 0 || x >= width || y >= height) + { + throw new IllegalArgumentException("CANVAS_BOUNDS"); + } + return (y * stride + (x >> 3)); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Diagnostic.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Diagnostic.java new file mode 100644 index 0000000..4795d8a --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Diagnostic.java @@ -0,0 +1,213 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.regex.Pattern; + + +/******************************************************************************* + ** One finding about a version 2 scene, in a closed vocabulary: a code, a + ** severity, the id of an element the scene declares, a pointer made of schema + ** names, indexes and declared ids, a box in dots, and named integers. It has no + ** field that can hold a sentence, so no record value, context text or exception + ** message can leave the renderer in it. Only `Diagnostics` creates one, because + ** only the collector knows which ids the scene declares. + *******************************************************************************/ +public final class Diagnostic +{ + private static final Set ERRORS = Set.of("SCENE_INVALID", "STOCK_MISMATCH", "TARGET_REQUIRES_SCENE_2", "ELEMENT_OUTSIDE_PRINTABLE_AREA", "TEXT_DOES_NOT_FIT", "VALUE_MISSING", + "VALUE_TOO_LONG", "FONT_UNKNOWN", "FONT_MISSING_GLYPH", "RESERVED_FONT_NAME", "BARCODE_DOES_NOT_FIT", "GS1_INVALID", "ASSET_UNKNOWN", "PREVIEW_TOO_LARGE", "RENDERER_TOO_OLD"); + private static final Set WARNINGS = Set.of("TEXT_TRUNCATED", "TEXT_SMALL_FOR_DPI", "TEXT_LIGHT_FACE", "FIT_NOT_PROVEN", "BARCODE_MODULE_BELOW_RECOMMENDED", "QUIET_ZONE_INTRUDED", + "ELEMENTS_OVERLAP", "IMAGE_UPSCALED", "DEVICE_UNIT_USED", "PRINTER_FONT_NOT_SHOWN"); + private static final Set INFORMATION = Set.of("TEXT_SHRUNK", "TEXT_WRAPPED", "BARCODE_MODULE", "IMAGE_SCALED"); + private static final Pattern NAME = Pattern.compile("[a-z][A-Za-z0-9]{0,31}"); + + private final String code; + private final Severity severity; + private final String element; + private final String pointer; + private final Frame box; + private final Map measured; + private final Map limit; + + + + /*************************************************************************** + ** An error stops printing; a warning and information do not. + ***************************************************************************/ + public enum Severity + { + ERROR, WARNING, INFO + } + + + + /*************************************************************************** + ** Admit only the vocabulary. A mistake here is a defect in the renderer, not + ** in the scene, so it is an illegal state and stays opaque to the host. + ***************************************************************************/ + Diagnostic(String code, Severity severity, String element, String pointer, Frame box, Map measured, Map limit) + { + Set codes = switch(severity) + { + case ERROR -> ERRORS; + case WARNING -> WARNINGS; + case INFO -> INFORMATION; + }; + if(code == null || !codes.contains(code) || (box != null && (box.width() < 0 || box.height() < 0))) + { + throw new IllegalStateException("DIAGNOSTIC_OUTSIDE_VOCABULARY"); + } + this.code = code; + this.severity = severity; + this.element = element; + this.pointer = pointer; + this.box = box; + this.measured = numbers(measured); + this.limit = numbers(limit); + } + + + + /*************************************************************************** + ** The code, from the closed vocabulary of its severity. + ***************************************************************************/ + public String code() + { + return (code); + } + + + + /*************************************************************************** + ** Whether the finding stops printing. + ***************************************************************************/ + public Severity severity() + { + return (severity); + } + + + + /*************************************************************************** + ** The declared id of the element the finding is about, or null. + ***************************************************************************/ + public String element() + { + return (element); + } + + + + /*************************************************************************** + ** The JSON pointer of the offending property in the scene, or null. + ***************************************************************************/ + public String pointer() + { + return (pointer); + } + + + + /*************************************************************************** + ** The element's resolved box in dots from the printable area's top left, or + ** null. + ***************************************************************************/ + public Frame box() + { + return (box); + } + + + + /*************************************************************************** + ** What was measured, by name. + ***************************************************************************/ + public Map measured() + { + return (measured); + } + + + + /*************************************************************************** + ** What the measured values were held against, by name. + ***************************************************************************/ + public Map limit() + { + return (limit); + } + + + + /*************************************************************************** + ** The diagnostic as the renderer's answer carries it. Absent parts are left + ** out, and names are in a fixed order so equal findings are equal bytes. + ***************************************************************************/ + public Map wire() + { + Map wire = new LinkedHashMap<>(); + wire.put("code", code); + wire.put("severity", severity.name()); + if(element != null) + { + wire.put("element", element); + } + if(pointer != null) + { + wire.put("pointer", pointer); + } + if(box != null) + { + wire.put("box", box.box()); + } + if(!measured.isEmpty()) + { + wire.put("measured", measured); + } + if(!limit.isEmpty()) + { + wire.put("limit", limit); + } + return (wire); + } + + + + /*************************************************************************** + ** Freeze at most eight named integers; a name is a short identifier. + ***************************************************************************/ + private static Map numbers(Map values) + { + Map numbers = new TreeMap<>(values == null ? Map.of() : values); + if(numbers.size() > 8 || numbers.keySet().stream().anyMatch(name -> !NAME.matcher(name).matches()) || numbers.containsValue(null)) + { + throw new IllegalStateException("DIAGNOSTIC_OUTSIDE_VOCABULARY"); + } + return (Collections.unmodifiableMap(numbers)); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Diagnostics.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Diagnostics.java new file mode 100644 index 0000000..2ed0783 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Diagnostics.java @@ -0,0 +1,215 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + + +/******************************************************************************* + ** The collector of one render or validation, and the only maker of + ** diagnostics. It knows the element ids the scene declares, so it is where the + ** rule is enforced that a diagnostic names only a declared id and points only + ** through schema names, indexes and declared ids: a data value has no way in. + ** + ** By default the first error ends the work as a `Refusal`. When the caller asks + ** to collect, errors are kept and the work goes on as far as it can, which is + ** what a preview wants. The findings of one scene are bounded by the scene: at + ** most one error for each element and one warning for each property. + *******************************************************************************/ +public final class Diagnostics +{ + public static final Pattern ID = Pattern.compile("[A-Za-z][A-Za-z0-9_-]{0,31}"); + + private static final Pattern INDEX = Pattern.compile("0|[1-9][0-9]{0,3}"); + + private final boolean collect; + private final Map declared = new LinkedHashMap<>(); + private final List findings = new ArrayList<>(); + private final Set> seen = new HashSet<>(); + private boolean refused; + + + + /*************************************************************************** + ** A collector that stops at the first error, or one that keeps them. + ***************************************************************************/ + public Diagnostics(boolean collect) + { + this.collect = collect; + } + + + + /*************************************************************************** + ** Record an id the scene declares and where its element is. From here on a + ** diagnostic may name it. + ***************************************************************************/ + public void declare(String id, String pointer) + { + if(id == null || !ID.matcher(id).matches() || declared.containsKey(id)) + { + throw new IllegalStateException("ELEMENT_ID_NOT_DECLARABLE"); + } + declared.put(id, pointer); + } + + + + /*************************************************************************** + ** Whether the scene declares this id. + ***************************************************************************/ + public boolean declared(String id) + { + return (declared.containsKey(id)); + } + + + + /*************************************************************************** + ** The pointer of a declared element, or null. + ***************************************************************************/ + public String pointer(String id) + { + return (declared.get(id)); + } + + + + /*************************************************************************** + ** An error, as the exception to throw. Every part is optional but the code. + ***************************************************************************/ + public Refusal refuse(String code, String element, String pointer, Frame box, Map measured, Map limit) + { + return (new Refusal(create(code, Diagnostic.Severity.ERROR, element, pointer, box, measured, limit))); + } + + + + /*************************************************************************** + ** A finding that does not stop printing. Reporting the same one twice keeps + ** one. + ***************************************************************************/ + public void warn(String code, String element, String pointer, Frame box, Map measured, Map limit) + { + add(create(code, Diagnostic.Severity.WARNING, element, pointer, box, measured, limit)); + } + + + + /*************************************************************************** + ** What the renderer chose, for the author to see: a module size, a shrunk + ** type size. + ***************************************************************************/ + public void inform(String code, String element, String pointer, Frame box, Map measured, Map limit) + { + add(create(code, Diagnostic.Severity.INFO, element, pointer, box, measured, limit)); + } + + + + /*************************************************************************** + ** What to do with a refusal that was caught where work can continue: keep it + ** when collecting, otherwise let it end the work. + ***************************************************************************/ + public void keep(Refusal refusal) + { + if(!collect) + { + throw refusal; + } + refused = true; + add(refusal.diagnostic()); + } + + + + /*************************************************************************** + ** Whether an error was kept, so the label must not print. + ***************************************************************************/ + public boolean refused() + { + return (refused); + } + + + + /*************************************************************************** + ** Every finding so far, in the order found. + ***************************************************************************/ + public List findings() + { + return (List.copyOf(findings)); + } + + + + /*************************************************************************** + ** The findings as the renderer's answer carries them. + ***************************************************************************/ + public List> wire() + { + return (findings.stream().map(Diagnostic::wire).toList()); + } + + + + /*************************************************************************** + ** The gate: the element is declared, and every segment of the pointer is an + ** index, a property name of the schema or a declared id. + ***************************************************************************/ + private Diagnostic create(String code, Diagnostic.Severity severity, String element, String pointer, Frame box, Map measured, Map limit) + { + boolean known = element == null || declared.containsKey(element); + if(pointer != null && !pointer.isEmpty()) + { + known &= pointer.startsWith("/"); + for(String segment : pointer.substring(1).split("/", -1)) + { + known &= INDEX.matcher(segment).matches() || ElementFactory.VOCABULARY.contains(segment) || declared.containsKey(segment); + } + } + if(!known) + { + throw new IllegalStateException("DIAGNOSTIC_NOT_DECLARED"); + } + return (new Diagnostic(code, severity, element, pointer, box, measured, limit)); + } + + + + /*************************************************************************** + ** Keep a finding unless an equal one is already there. + ***************************************************************************/ + private void add(Diagnostic diagnostic) + { + if(seen.add(diagnostic.wire())) + { + findings.add(diagnostic); + } + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/ElementFactory.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/ElementFactory.java new file mode 100644 index 0000000..dd5ae45 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/ElementFactory.java @@ -0,0 +1,74 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import com.kingsrook.qbits.renderer.scene2.barcode.BarcodeFactory; +import com.kingsrook.qbits.renderer.scene2.graphics.GraphicsFactory; +import com.kingsrook.qbits.renderer.scene2.text.TextFactory; + + +/******************************************************************************* + ** Builds the elements of one `type`. The core owns `box` and `group`; text, + ** barcodes and images each live in their own sub-package and are registered + ** here by class, so the package that fills one in edits only its own file. + *******************************************************************************/ +public interface ElementFactory +{ + /*************************************************************************** + ** Properties every element may state. The parser reads them into the + ** element's `Placement` before any factory runs. + ***************************************************************************/ + Set COMMON = Set.of("id", "type", "anchor", "x", "y", "width", "height", "align", "valign"); + + /*************************************************************************** + ** The factories by scene `type`. A `group` is built by the parser itself, + ** because its children are elements. + ***************************************************************************/ + Map FACTORIES = Map.of("box", new BoxElement.Factory(), "text", new TextFactory(), "barcode", new BarcodeFactory(), "image", new GraphicsFactory()); + + /*************************************************************************** + ** Every property name the version 2 schema declares. A diagnostic's pointer + ** is made only of these, of array indexes and of declared element ids. + ***************************************************************************/ + Set VOCABULARY = Stream.concat(Stream.of("schema", "design", "defaults", "font", "size", "samples", "elements", "overrides", "when", "dpi", "set", "children"), + Stream.concat(COMMON.stream(), FACTORIES.values().stream().flatMap(factory -> factory.properties().stream()))).collect(Collectors.toUnmodifiableSet()); + + + + /*************************************************************************** + ** The properties this type reads beyond the common ones. The parser refuses + ** an element that states any other. + ***************************************************************************/ + Set properties(); + + + + /*************************************************************************** + ** Build one element from its source, refusing a property value it cannot use + ** with `source.invalid(name)`. + ***************************************************************************/ + SceneElement create(ElementSource source); +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/ElementSource.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/ElementSource.java new file mode 100644 index 0000000..04cc702 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/ElementSource.java @@ -0,0 +1,272 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import com.fasterxml.jackson.databind.JsonNode; + + +/******************************************************************************* + ** One element as the scene states it for one resolution: its own properties + ** with the matching overrides applied, and its common placement already read. + ** A factory reads its own properties through the typed readers here, which + ** refuse a bad value as `SCENE_INVALID` at the property's own pointer (in the + ** element, or in the override that set it). + *******************************************************************************/ +public final class ElementSource +{ + public static final Set PHYSICAL = Set.of(Length.Unit.MM, Length.Unit.IN, Length.Unit.PT); + public static final Set ABSOLUTE = Set.of(Length.Unit.MM, Length.Unit.IN, Length.Unit.PT, Length.Unit.DOT); + public static final Set ANY = Set.of(Length.Unit.values()); + + private final String id; + private final String pointer; + private final Diagnostics diagnostics; + private final Map values = new LinkedHashMap<>(); + private final Map pointers = new LinkedHashMap<>(); + private final Placement placement; + + + + /*************************************************************************** + ** The element at `pointer`, whose id the collector already holds, with the + ** overrides for this resolution: property name to its value and to the + ** pointer of the override that states it. + ***************************************************************************/ + ElementSource(String id, String pointer, JsonNode element, Map overrides, Map overridePointers, Diagnostics diagnostics) + { + this.id = id; + this.pointer = pointer; + this.diagnostics = diagnostics; + element.fieldNames().forEachRemaining(name -> values.put(name, element.get(name))); + values.putAll(overrides); + pointers.putAll(overridePointers); + Placement.Anchor anchor = choice("anchor", Placement.Anchor.TOP_LEFT, Placement.Anchor::of); + placement = new Placement(anchor, offset("x", anchor.horizontal()), offset("y", anchor.vertical()), extent("width"), extent("height"), + choice("align", Placement.Align.LEFT, Placement.Align::of), choice("valign", Placement.Valign.TOP, Placement.Valign::of)); + } + + + + /*************************************************************************** + ** The id the scene declares for the element. + ***************************************************************************/ + public String id() + { + return (id); + } + + + + /*************************************************************************** + ** The element's `type`. + ***************************************************************************/ + public String type() + { + return (values.get("type").textValue()); + } + + + + /*************************************************************************** + ** Anchor, offsets, box and content alignment, as the scene states them. + ***************************************************************************/ + public Placement placement() + { + return (placement); + } + + + + /*************************************************************************** + ** A property's value as JSON, or null when the element does not state it. + ***************************************************************************/ + public JsonNode value(String name) + { + return (values.get(name)); + } + + + + /*************************************************************************** + ** A length in one of the allowed units, or the fallback when the element + ** does not state it. A `dot` length stated outside an override depends on + ** the printer, which the author is told. + ***************************************************************************/ + public Length length(String name, Length fallback, Set units) + { + JsonNode value = values.get(name); + if(value == null) + { + return (fallback); + } + Length length; + try + { + length = Length.parse(value.textValue()); + } + catch(IllegalArgumentException expected) + { + throw invalid(name); + } + if(!units.contains(length.unit())) + { + throw invalid(name); + } + if(length.unit() == Length.Unit.DOT && !pointers.containsKey(name)) + { + diagnostics.warn("DEVICE_UNIT_USED", id, pointer(name), null, null, null); + } + return (length); + } + + + + /*************************************************************************** + ** A true or false property, or the fallback when the element does not state + ** it. + ***************************************************************************/ + public boolean flag(String name, boolean fallback) + { + JsonNode value = values.get(name); + if(value == null) + { + return (fallback); + } + if(!value.isBoolean()) + { + throw invalid(name); + } + return (value.booleanValue()); + } + + + + /*************************************************************************** + ** One word of a fixed set, or the fallback when the element does not state + ** it. `lookup` answers null for a word outside the set. + ***************************************************************************/ + public T choice(String name, T fallback, Function lookup) + { + JsonNode value = values.get(name); + if(value == null) + { + return (fallback); + } + T choice = lookup.apply(value.textValue()); + if(choice == null) + { + throw invalid(name); + } + return (choice); + } + + + + /*************************************************************************** + ** Clockwise rotation of text, a barcode or an image: 0, 90, 180 or 270. + ***************************************************************************/ + public int rotation() + { + JsonNode value = values.get("rotation"); + if(value == null) + { + return (0); + } + if(!value.isIntegralNumber() || !value.canConvertToInt() || !Set.of(0, 90, 180, 270).contains(value.intValue())) + { + throw invalid("rotation"); + } + return (value.intValue()); + } + + + + /*************************************************************************** + ** The refusal for a property the element states wrongly, or lacks. + ***************************************************************************/ + public Refusal invalid(String name) + { + return (diagnostics.refuse("SCENE_INVALID", id, pointer(name), null, null, null)); + } + + + + /*************************************************************************** + ** Refuse an element that states a property its type does not read. The + ** names themselves are not reported: only declared names leave the renderer. + ***************************************************************************/ + void only(Set properties) + { + int unknown = Math.toIntExact(values.keySet().stream().filter(name -> !ElementFactory.COMMON.contains(name) && !properties.contains(name)).count()); + if(unknown > 0) + { + throw diagnostics.refuse("SCENE_INVALID", id, pointer, null, Map.of("unknownProperties", unknown), Map.of("unknownProperties", 0)); + } + } + + + + /*************************************************************************** + ** Where a property is stated: in the override that set it, else in the + ** element. + ***************************************************************************/ + private String pointer(String name) + { + return (pointers.getOrDefault(name, pointer + "/" + name)); + } + + + + /*************************************************************************** + ** A distance inward from the anchored edge. Only about the middle may it be + ** negative, where it shifts left or up. + ***************************************************************************/ + private Length offset(String name, Placement.Position position) + { + Length offset = length(name, Length.ZERO, ANY); + if(offset.thousandths() < 0 && position != Placement.Position.CENTER) + { + throw invalid(name); + } + return (offset); + } + + + + /*************************************************************************** + ** A box width or height, more than nothing; null when the scene leaves it + ** to the rest of the container. + ***************************************************************************/ + private Length extent(String name) + { + Length extent = length(name, null, ANY); + if(extent != null && extent.thousandths() <= 0) + { + throw invalid(name); + } + return (extent); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Frame.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Frame.java new file mode 100644 index 0000000..c3f13d4 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Frame.java @@ -0,0 +1,70 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.List; + + +/******************************************************************************* + ** A resolved box in whole dots. The origin is the top left of the printable + ** area, so a frame at 0, 0 touches the margins and never enters them. + *******************************************************************************/ +public record Frame(int x, int y, int width, int height) +{ + /*************************************************************************** + ** The first column to the right of the box. + ***************************************************************************/ + public int right() + { + return (x + width); + } + + + + /*************************************************************************** + ** The first row below the box. + ***************************************************************************/ + public int bottom() + { + return (y + height); + } + + + + /*************************************************************************** + ** Whether every dot of the other box is a dot of this one. + ***************************************************************************/ + public boolean contains(Frame other) + { + return (other.x >= x && other.y >= y && other.right() <= right() && other.bottom() <= bottom()); + } + + + + /*************************************************************************** + ** The box as a diagnostic carries it: x, y, width and height. + ***************************************************************************/ + public List box() + { + return (List.of(x, y, width, height)); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/GroupElement.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/GroupElement.java new file mode 100644 index 0000000..3db468a --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/GroupElement.java @@ -0,0 +1,105 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.List; +import com.kingsrook.qbits.renderer.RenderInput; + + +/******************************************************************************* + ** A box with its own anchor, position and size whose children are placed + ** relative to it. It moves a block as a unit and gives percentages something + ** smaller than the label to refer to. It paints nothing itself. + *******************************************************************************/ +final class GroupElement implements SceneElement +{ + private final String id; + private final Placement placement; + private final List children; + + + + /*************************************************************************** + ** A group and the elements inside it, in paint order. + ***************************************************************************/ + GroupElement(ElementSource source, List children) + { + id = source.id(); + placement = source.placement(); + this.children = List.copyOf(children); + } + + + + /*************************************************************************** + ** The id the scene declares for the group. + ***************************************************************************/ + @Override + public String id() + { + return (id); + } + + + + /*************************************************************************** + ** The group's box, by the standing rules. + ***************************************************************************/ + @Override + public Frame place(Layout layout) + { + return (layout.place(placement)); + } + + + + /*************************************************************************** + ** A group paints nothing, whatever the size of its box. + ***************************************************************************/ + @Override + public long paintedDots(Frame frame, int dpi) + { + return (0); + } + + + + /*************************************************************************** + ** A group has no ink of its own. + ***************************************************************************/ + @Override + public void paint(Canvas canvas, Frame frame, RenderInput input, Diagnostics diagnostics) + { + // The scene paints the children after their group, inside its box. + } + + + + /*************************************************************************** + ** The elements inside the group. + ***************************************************************************/ + @Override + public List children() + { + return (children); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Layout.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Layout.java new file mode 100644 index 0000000..f666ea2 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Layout.java @@ -0,0 +1,166 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +/******************************************************************************* + ** Anchor resolution inside one container at one resolution: lengths in, whole + ** dots out. The container is the printable area or a group, and percentages + ** refer to it. Integer arithmetic only. + *******************************************************************************/ +public final class Layout +{ + private final Frame container; + private final int dpi; + + + + /*************************************************************************** + ** A container, in dots from the top left of the printable area, and the + ** resolution every length resolves at. + ***************************************************************************/ + public Layout(Frame container, int dpi) + { + this.container = container; + this.dpi = dpi; + } + + + + /*************************************************************************** + ** The box positions and percentages refer to. + ***************************************************************************/ + public Frame container() + { + return (container); + } + + + + /*************************************************************************** + ** The render target, in dots per inch. + ***************************************************************************/ + public int dpi() + { + return (dpi); + } + + + + /*************************************************************************** + ** The same resolution inside a smaller box, for the children of a group. + ***************************************************************************/ + public Layout within(Frame frame) + { + return (new Layout(frame, dpi)); + } + + + + /*************************************************************************** + ** The element's box by the standing rules: position to the nearest dot, size + ** rounded down. + ***************************************************************************/ + public Frame place(Placement placement) + { + return (frame(placement.anchor(), x(placement), y(placement), width(placement), height(placement))); + } + + + + /*************************************************************************** + ** The horizontal distance inward from the anchored edge, or the shift right + ** on a centred axis. + ***************************************************************************/ + public int x(Placement placement) + { + return (placement.x().position(dpi, container.width())); + } + + + + /*************************************************************************** + ** The vertical distance inward from the anchored edge, or the shift down on + ** a centred axis. + ***************************************************************************/ + public int y(Placement placement) + { + return (placement.y().position(dpi, container.height())); + } + + + + /*************************************************************************** + ** The box width, or the rest of the container when the scene states none. + ***************************************************************************/ + public int width(Placement placement) + { + return (extent(placement.width(), x(placement), placement.anchor().horizontal(), container.width())); + } + + + + /*************************************************************************** + ** The box height, or the rest of the container when the scene states none. + ***************************************************************************/ + public int height(Placement placement) + { + return (extent(placement.height(), y(placement), placement.anchor().vertical(), container.height())); + } + + + + /*************************************************************************** + ** A box of known size on an anchor: the same point of the box and of the + ** container coincide, moved by the offsets. + ***************************************************************************/ + public Frame frame(Placement.Anchor anchor, int x, int y, int width, int height) + { + return (new Frame(container.x() + anchor.horizontal().place(container.width(), width, x), container.y() + anchor.vertical().place(container.height(), height, y), width, height)); + } + + + + /*************************************************************************** + ** Content of a measured size inside its box: text lines, a fitted barcode, a + ** scaled image. + ***************************************************************************/ + public static Frame align(Frame box, int width, int height, Placement.Align align, Placement.Valign valign) + { + return (new Frame(box.x() + align.position().place(box.width(), width, 0), box.y() + valign.position().place(box.height(), height, 0), width, height)); + } + + + + /*************************************************************************** + ** A stated size is rounded down. An omitted one is what the offset leaves: + ** from an edge, the container less the offset; about the middle, the + ** container less the shift on both sides, so the box stays centred on it. + ***************************************************************************/ + private int extent(Length size, int offset, Placement.Position position, int available) + { + if(size != null) + { + return (size.size(dpi, available)); + } + return (available - Math.abs(offset) * (position == Placement.Position.CENTER ? 2 : 1)); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Length.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Length.java new file mode 100644 index 0000000..4ac74b6 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Length.java @@ -0,0 +1,250 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/******************************************************************************* + ** A length as a version 2 scene writes it: a number of up to three decimals and + ** a unit, such as `12.5mm`, `0.5in`, `9pt`, `25%` or `1dot`. There is no ambient + ** unit, so a value read out of context is never ambiguous. A physical length + ** becomes whole micrometres first (the unit the stock uses) and then dots by + ** the rule its use states; no floating point takes part in a position or size. + *******************************************************************************/ +public record Length(Unit unit, long thousandths) +{ + public static final Length ZERO = new Length(Unit.MM, 0); + + private static final Pattern SYNTAX = Pattern.compile("(-?)(0|[1-9][0-9]{0,4})(?:\\.([0-9]{1,3}))?(mm|in|pt|%|dot)"); + private static final int MAXIMUM_MICROMETRES = 2_000_000; + private static final int MAXIMUM_DOTS = 32_000; + private static final int MICROMETRES_PER_INCH = 25_400; + private static final Map UNITS = Map.of("mm", Unit.MM, "in", Unit.IN, "pt", Unit.PT, "%", Unit.PERCENT, "dot", Unit.DOT); + + + + /*************************************************************************** + ** The five units of a scene. `%` is a share of the container and `dot` is one + ** printer dot; the other three are physical. + ***************************************************************************/ + public enum Unit + { + MM, IN, PT, PERCENT, DOT + } + + + + /*************************************************************************** + ** How a fraction of a dot is resolved. The magnitude is rounded and the sign + ** restored, so a length and its negative are mirror images. + ***************************************************************************/ + public enum Rounding + { + NEAREST, DOWN, UP + } + + + + /*************************************************************************** + ** Read one length. Anything else, including a bare number, an exponent, a + ** fourth decimal, a fraction of a dot, more than the whole container or more + ** than the largest stock, is refused. + ***************************************************************************/ + public static Length parse(String text) + { + Matcher matcher = SYNTAX.matcher(text == null ? "" : text); + if(!matcher.matches()) + { + throw new IllegalArgumentException("INVALID_LENGTH"); + } + String fraction = matcher.group(3) == null ? "" : matcher.group(3); + long magnitude = Long.parseLong(matcher.group(2)) * 1000 + Long.parseLong((fraction + "000").substring(0, 3)); + Length length = new Length(UNITS.get(matcher.group(4)), matcher.group(1).isEmpty() ? magnitude : -magnitude); + long limit = switch(length.unit) + { + case PERCENT -> 100_000L; + case DOT -> MAXIMUM_DOTS * 1000L; + default -> Long.MAX_VALUE; + }; + if(magnitude > limit || (length.unit == Unit.DOT && !fraction.isEmpty()) || (length.physical() && length.magnitude() > MAXIMUM_MICROMETRES)) + { + throw new IllegalArgumentException("INVALID_LENGTH"); + } + return (length); + } + + + + /*************************************************************************** + ** Millimetres, inches and points are physical: the same on every printer. + ***************************************************************************/ + public boolean physical() + { + return (unit == Unit.MM || unit == Unit.IN || unit == Unit.PT); + } + + + + /*************************************************************************** + ** Whole micrometres, nearest, with a half rounded away from zero. One inch + ** is 25400 micrometres and one point is a 72nd of an inch. + ***************************************************************************/ + public int micrometres() + { + long micrometres = magnitude(); + return (Math.toIntExact(thousandths < 0 ? -micrometres : micrometres)); + } + + + + /*************************************************************************** + ** The micrometres of the magnitude, not yet narrowed, so that the largest + ** number the syntax allows (99999.999 of any unit) is compared with the limit + ** as it is and refused by name, never as an arithmetic overflow. + ***************************************************************************/ + private long magnitude() + { + long magnitude = Math.abs(thousandths); + return (switch(unit) + { + case MM -> magnitude; + case IN -> (magnitude * 254 + 5) / 10; + case PT -> (magnitude * 127 + 180) / 360; + default -> throw new IllegalArgumentException("LENGTH_NOT_PHYSICAL"); + }); + } + + + + /*************************************************************************** + ** Whole dots at a resolution. A percentage is a share of `container` dots; + ** a `dot` length is itself; a physical length goes through micrometres. + ***************************************************************************/ + public int dots(int dpi, int container, Rounding rounding) + { + long numerator; + long denominator; + switch(unit) + { + case PERCENT -> + { + numerator = Math.abs(thousandths) * container; + denominator = 100_000; + } + case DOT -> + { + numerator = Math.abs(thousandths); + denominator = 1000; + } + default -> + { + numerator = (long) Math.abs(micrometres()) * dpi; + denominator = MICROMETRES_PER_INCH; + } + } + long magnitude = switch(rounding) + { + case NEAREST -> (numerator + denominator / 2) / denominator; + case DOWN -> numerator / denominator; + case UP -> (numerator + denominator - 1) / denominator; + }; + return (Math.toIntExact(thousandths < 0 ? -magnitude : magnitude)); + } + + + + /*************************************************************************** + ** A position: the nearest dot, the rule the stock already uses, so the error + ** is symmetric. + ***************************************************************************/ + public int position(int dpi, int container) + { + return (dots(dpi, container, Rounding.NEAREST)); + } + + + + /*************************************************************************** + ** A box width or height: rounded down, so content never leaves its designed + ** box. + ***************************************************************************/ + public int size(int dpi, int container) + { + return (dots(dpi, container, Rounding.DOWN)); + } + + + + /*************************************************************************** + ** A line or box thickness: the nearest dot and never less than one, so a + ** thin rule does not vanish. + ***************************************************************************/ + public int thickness(int dpi) + { + return (Math.max(1, absolute().dots(dpi, 0, Rounding.NEAREST))); + } + + + + /*************************************************************************** + ** A barcode module from its stated minimum: up to a whole dot, because bars + ** are whole dots and may not be narrower than the minimum. + ***************************************************************************/ + public int module(int dpi) + { + return (Math.max(1, absolute().dots(dpi, 0, Rounding.UP))); + } + + + + /*************************************************************************** + ** A font size in dots, not rounded: a face is scaled by points times dpi + ** over 72, so type scales continuously. + ***************************************************************************/ + public double scale(int dpi) + { + return (switch(absolute().unit) + { + case MM -> thousandths * (double) dpi / MICROMETRES_PER_INCH; + case IN -> thousandths * (double) dpi / 1000; + case PT -> thousandths * (double) dpi / 72_000; + default -> thousandths / 1000.0; + }); + } + + + + /*************************************************************************** + ** A thickness, a module and a font size have no container to be a share of. + ***************************************************************************/ + private Length absolute() + { + if(unit == Unit.PERCENT) + { + throw new IllegalArgumentException("LENGTH_NOT_ABSOLUTE"); + } + return (this); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Placement.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Placement.java new file mode 100644 index 0000000..2a0f3ff --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Placement.java @@ -0,0 +1,231 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +/******************************************************************************* + ** What every element states about where it sits: the point it is anchored to, + ** its distances inward from that point, its box, and where content goes inside + ** the box. `width` and `height` are null when the scene leaves them out, which + ** means the rest of the container on that axis. + *******************************************************************************/ +public record Placement(Anchor anchor, Length x, Length y, Length width, Length height, Align align, Valign valign) +{ + /*************************************************************************** + ** One axis, three places: against the start, in the middle or against the end. + ***************************************************************************/ + public enum Position + { + START, CENTER, END; + + + + /************************************************************************ + ** Where `size` dots begin within `available` dots. From an edge, `offset` + ** runs inward. In the middle the slack is split with integer division, so + ** a centred box is off by at most one dot, and `offset` shifts it right or + ** down. + ************************************************************************/ + public int place(int available, int size, int offset) + { + return (switch(this) + { + case START -> offset; + case CENTER -> Math.floorDiv(available - size, 2) + offset; + case END -> available - size - offset; + }); + } + } + + + + /*************************************************************************** + ** Horizontal placement of content in its box. + ***************************************************************************/ + public enum Align + { + LEFT("left", Position.START), CENTER("center", Position.CENTER), RIGHT("right", Position.END); + + private final String word; + private final Position position; + + + + /************************************************************************ + ** The word a scene writes and the place it means. + ************************************************************************/ + Align(String word, Position position) + { + this.word = word; + this.position = position; + } + + + + /************************************************************************ + ** The place on the horizontal axis. + ************************************************************************/ + public Position position() + { + return (position); + } + + + + /************************************************************************ + ** The value a scene word names, or null. + ************************************************************************/ + public static Align of(String word) + { + for(Align align : values()) + { + if(align.word.equals(word)) + { + return (align); + } + } + return (null); + } + } + + + + /*************************************************************************** + ** Vertical placement of content in its box. + ***************************************************************************/ + public enum Valign + { + TOP("top", Position.START), MIDDLE("middle", Position.CENTER), BOTTOM("bottom", Position.END); + + private final String word; + private final Position position; + + + + /************************************************************************ + ** The word a scene writes and the place it means. + ************************************************************************/ + Valign(String word, Position position) + { + this.word = word; + this.position = position; + } + + + + /************************************************************************ + ** The place on the vertical axis. + ************************************************************************/ + public Position position() + { + return (position); + } + + + + /************************************************************************ + ** The value a scene word names, or null. + ************************************************************************/ + public static Valign of(String word) + { + for(Valign valign : values()) + { + if(valign.word.equals(word)) + { + return (valign); + } + } + return (null); + } + } + + + + /*************************************************************************** + ** The nine points of a box. The same point of the element is placed on that + ** point of its container. + ***************************************************************************/ + public enum Anchor + { + TOP_LEFT("top-left", Position.START, Position.START), + TOP_CENTER("top-center", Position.CENTER, Position.START), + TOP_RIGHT("top-right", Position.END, Position.START), + MIDDLE_LEFT("middle-left", Position.START, Position.CENTER), + CENTER("center", Position.CENTER, Position.CENTER), + MIDDLE_RIGHT("middle-right", Position.END, Position.CENTER), + BOTTOM_LEFT("bottom-left", Position.START, Position.END), + BOTTOM_CENTER("bottom-center", Position.CENTER, Position.END), + BOTTOM_RIGHT("bottom-right", Position.END, Position.END); + + private final String word; + private final Position horizontal; + private final Position vertical; + + + + /************************************************************************ + ** The word a scene writes and the place it means on each axis. + ************************************************************************/ + Anchor(String word, Position horizontal, Position vertical) + { + this.word = word; + this.horizontal = horizontal; + this.vertical = vertical; + } + + + + /************************************************************************ + ** The place on the horizontal axis. + ************************************************************************/ + public Position horizontal() + { + return (horizontal); + } + + + + /************************************************************************ + ** The place on the vertical axis. + ************************************************************************/ + public Position vertical() + { + return (vertical); + } + + + + /************************************************************************ + ** The value a scene word names, or null. + ************************************************************************/ + public static Anchor of(String word) + { + for(Anchor anchor : values()) + { + if(anchor.word.equals(word)) + { + return (anchor); + } + } + return (null); + } + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Refusal.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Refusal.java new file mode 100644 index 0000000..3e21b02 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Refusal.java @@ -0,0 +1,55 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +/******************************************************************************* + ** The renderer saying no to a version 2 scene, with the one error diagnostic + ** that says where and by how much. Its message is the diagnostic's code, never + ** anything from the scene or the record. + *******************************************************************************/ +public final class Refusal extends IllegalArgumentException +{ + private static final long serialVersionUID = 1L; + + private final transient Diagnostic diagnostic; + + + + /*************************************************************************** + ** Only the collector raises a refusal, from a diagnostic it has checked. + ***************************************************************************/ + Refusal(Diagnostic diagnostic) + { + super(diagnostic.code()); + this.diagnostic = diagnostic; + } + + + + /*************************************************************************** + ** The error this refusal is. + ***************************************************************************/ + public Diagnostic diagnostic() + { + return (diagnostic); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Scene2.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Scene2.java new file mode 100644 index 0000000..c4b2871 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/Scene2.java @@ -0,0 +1,213 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import com.fasterxml.jackson.databind.JsonNode; +import com.kingsrook.qbits.renderer.RenderInput; + + +/******************************************************************************* + ** A version 2 scene from source to label: one design in physical units, + ** resolved to whole dots at the render target, composed on one canvas and + ** emitted as graphic fields. Coordinates start at the top left of the + ** printable area and every box is held inside it, so nothing a scene says + ** can print in a margin (`Page` states the rule to the dot). + *******************************************************************************/ +public final class Scene2 +{ + public static final String SCHEMA = "qqq-zpl-scene/2"; + public static final List RESOLUTIONS = List.of(203, 300, 600); + + static final int STOCK_TOLERANCE_MICROMETRES = 500; + + private static final int MICROMETRES_PER_INCH = 25_400; + + + + /*************************************************************************** + ** Utility class. + ***************************************************************************/ + private Scene2() + { + } + + + + /*************************************************************************** + ** The page and its printable area in dots at the render target: the request's + ** target when it names one, else the stock's own resolution. + ** + ** The printable area is the stock less its margins, in micrometres, rounded + ** down to whole dots: exactly the size a box of the stated printable width + ** and height resolves to, so a design that fills the printable area fits at + ** every resolution. It starts at the left and top margins rounded up, and is + ** moved back only as far as keeps it on the page. The left and top margins + ** are therefore never entered unless the page itself ends first; the far + ** edge may pass the right or bottom margin by less than one dot. + ***************************************************************************/ + record Page(int dpi, int width, int height, Frame printable) + { + /************************************************************************ + ** Resolve the stock, or refuse one the printer language cannot address. + ************************************************************************/ + static Page of(RenderInput input) + { + RenderInput.Stock stock = input.stock; + int dpi = input.targetDpi == null ? stock.dpi() : input.targetDpi; + if(!RESOLUTIONS.contains(dpi)) + { + throw new IllegalArgumentException("UNSUPPORTED_ZPL_STOCK"); + } + int pageWidth = dots(stock.widthMicrons(), dpi, MICROMETRES_PER_INCH / 2); + int pageHeight = dots(stock.heightMicrons(), dpi, MICROMETRES_PER_INCH / 2); + int width = dots(stock.widthMicrons() - stock.leftMicrons() - stock.rightMicrons(), dpi, 0); + int height = dots(stock.heightMicrons() - stock.topMicrons() - stock.bottomMicrons(), dpi, 0); + int left = Math.min(dots(stock.leftMicrons(), dpi, MICROMETRES_PER_INCH - 1), pageWidth - width); + int top = Math.min(dots(stock.topMicrons(), dpi, MICROMETRES_PER_INCH - 1), pageHeight - height); + Frame printable = new Frame(left, top, width, height); + Page page = new Page(dpi, pageWidth, pageHeight, printable); + if(page.width > 9999 || page.height > 32000 || printable.width() < 1 || printable.height() < 1 || (long) printable.width() * printable.height() > Canvas.MAXIMUM_DOTS) + { + throw new IllegalArgumentException("UNSUPPORTED_ZPL_STOCK"); + } + return (page); + } + + + + /************************************************************************ + ** Micrometres to dots. The addend chooses the rounding: half an inch of + ** micrometres for the nearest dot (the page, as the host gate computes + ** it), one less than an inch to round up (the left and top margins) and + ** nothing to round down (the size of the printable area, as the size of + ** a box is rounded). + ************************************************************************/ + private static int dots(int micrometres, int dpi, int addend) + { + return (Math.toIntExact(((long) micrometres * dpi + addend) / MICROMETRES_PER_INCH)); + } + } + + + + /*************************************************************************** + ** Check a scene without drawing it: its grammar for every resolution, and + ** its layout on this stock at the render target. + ***************************************************************************/ + public static void validate(RenderInput input, JsonNode scene, Diagnostics diagnostics) + { + compose(input, scene, diagnostics, false); + } + + + + /*************************************************************************** + ** Draw a scene and return the label, or null when the collector kept an + ** error: a refused scene has no label, so one with elements missing cannot + ** be printed by mistake. The canvas of such a scene, holding the elements + ** that had no error, is what `compose` answers. + ***************************************************************************/ + public static byte[] render(RenderInput input, JsonNode scene, Diagnostics diagnostics) throws IOException + { + Page page = Page.of(input); + Canvas canvas = compose(input, scene, diagnostics, true); + return (diagnostics.refused() ? null : ZplEmitter.emit(canvas, page.width(), page.height(), page.printable().x(), page.printable().y(), input.maximumBytes)); + } + + + + /*************************************************************************** + ** Parse, lay out and, when asked, paint. A refusal about the scene as a + ** whole ends the pass; a refusal about one element ends that element. + ***************************************************************************/ + static Canvas compose(RenderInput input, JsonNode root, Diagnostics diagnostics, boolean paint) + { + Page page = Page.of(input); + Canvas canvas = new Canvas(page.printable().width(), page.printable().height(), page.dpi()); + try + { + SceneParser.Scene scene = SceneParser.parse(root, page.dpi(), diagnostics); + RenderInput.Stock stock = input.stock; + if(Math.abs(stock.widthMicrons() - scene.width().micrometres()) > STOCK_TOLERANCE_MICROMETRES || Math.abs(stock.heightMicrons() - scene.height().micrometres()) > STOCK_TOLERANCE_MICROMETRES) + { + throw diagnostics.refuse("STOCK_MISMATCH", null, "/design", null, Map.of("widthMicrometres", stock.widthMicrons(), "heightMicrometres", stock.heightMicrons()), + Map.of("widthMicrometres", scene.width().micrometres(), "heightMicrometres", scene.height().micrometres(), "toleranceMicrometres", STOCK_TOLERANCE_MICROMETRES)); + } + walk(scene.elements(), new Layout(new Frame(0, 0, canvas.width(), canvas.height()), page.dpi()), canvas, paint, input, diagnostics); + } + catch(Refusal refusal) + { + diagnostics.keep(refusal); + } + return (canvas); + } + + + + /*************************************************************************** + ** Elements in list order: place, hold the box to the printable area, check, + ** count the dots it would paint against what one scene may paint, paint + ** when asked, then the children inside the box. The dots are counted when a + ** scene is only validated too, so validation refuses what rendering would. + ***************************************************************************/ + private static void walk(List elements, Layout layout, Canvas canvas, boolean paint, RenderInput input, Diagnostics diagnostics) + { + Frame printable = new Frame(0, 0, canvas.width(), canvas.height()); + for(SceneElement element : elements) + { + try + { + Frame frame = element.place(layout); + if(frame.width() < 1 || frame.height() < 1) + { + throw diagnostics.refuse("SCENE_INVALID", element.id(), diagnostics.pointer(element.id()), null, Map.of("widthDots", frame.width(), "heightDots", frame.height()), + Map.of("widthDots", 1, "heightDots", 1)); + } + if(!printable.contains(frame)) + { + throw diagnostics.refuse("ELEMENT_OUTSIDE_PRINTABLE_AREA", element.id(), null, frame, + Map.of("leftDots", frame.x(), "topDots", frame.y(), "rightDots", frame.right(), "bottomDots", frame.bottom()), + Map.of("leftDots", 0, "topDots", 0, "rightDots", printable.right(), "bottomDots", printable.bottom())); + } + element.check(frame, input, diagnostics); + long dots = element.paintedDots(frame, layout.dpi()); + if(!canvas.admit(dots)) + { + throw diagnostics.refuse("SCENE_INVALID", element.id(), diagnostics.pointer(element.id()), frame, Map.of("paintedPercent", canvas.paintedPercent(dots)), + Map.of("paintedPercent", Canvas.MAXIMUM_COATS * 100)); + } + if(paint) + { + element.paint(canvas, frame, input, diagnostics); + } + walk(element.children(), layout.within(frame), canvas, paint, input, diagnostics); + } + catch(Refusal refusal) + { + diagnostics.keep(refusal); + } + } + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/SceneElement.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/SceneElement.java new file mode 100644 index 0000000..b4d12c1 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/SceneElement.java @@ -0,0 +1,85 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.List; +import com.kingsrook.qbits.renderer.RenderInput; + + +/******************************************************************************* + ** One element of a version 2 scene after parsing. The scene walks its elements + ** in list order: place, check that the box is inside the printable area, check + ** the element itself, paint, then the same for its children inside its box. + *******************************************************************************/ +public interface SceneElement +{ + /*************************************************************************** + ** The id the scene declares for the element. + ***************************************************************************/ + String id(); + + + + /*************************************************************************** + ** The element's box, in dots, inside the layout's container. + ***************************************************************************/ + Frame place(Layout layout); + + + + /*************************************************************************** + ** Checks that need no ink, run when a scene is validated and again before it + ** is painted. An element that cannot be drawn refuses here. + ***************************************************************************/ + default void check(Frame frame, RenderInput input, Diagnostics diagnostics) + { + } + + + + /*************************************************************************** + ** The dots the element paints in this box, counted against what one scene + ** may paint (`Canvas.MAXIMUM_COATS`) before any ink. An element paints only + ** inside its box, so the box is the answer unless the element knows less. + ***************************************************************************/ + default long paintedDots(Frame frame, int dpi) + { + return ((long) frame.width() * frame.height()); + } + + + + /*************************************************************************** + ** Draw the element in its box. + ***************************************************************************/ + void paint(Canvas canvas, Frame frame, RenderInput input, Diagnostics diagnostics); + + + + /*************************************************************************** + ** The elements positioned relative to this one's box; only a group has any. + ***************************************************************************/ + default List children() + { + return (List.of()); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/SceneParser.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/SceneParser.java new file mode 100644 index 0000000..1429f52 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/SceneParser.java @@ -0,0 +1,374 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import com.fasterxml.jackson.databind.JsonNode; + + +/******************************************************************************* + ** The parser of `qqq-zpl-scene/2`, a format written by template authors and so + ** untrusted. It reads only the documented grammar: an unknown property, a + ** wrong type, a bad length or a limit passed is `SCENE_INVALID` at the JSON + ** pointer of the offending place. It evaluates nothing and reads no record + ** data. + ** + ** The whole structure is checked first, which declares every element id, and + ** the elements are then built once for each resolution a printer can have, so + ** an override meant for another printer cannot hide a mistake until that + ** printer is used. The elements of the requested resolution are returned. + *******************************************************************************/ +final class SceneParser +{ + static final int MAXIMUM_ELEMENTS = 1000; + static final int MAXIMUM_DEPTH = 4; + static final int MAXIMUM_OVERRIDES = 32; + static final int MAXIMUM_SAMPLES = 16; + + private static final Set TOP = Set.of("schema", "design", "defaults", "samples", "elements", "overrides"); + private static final Set FIXED = Set.of("id", "type", "children"); + private static final Pattern FONT = Pattern.compile("[A-Za-z0-9_-][A-Za-z0-9_.-]{0,99}"); + + private final JsonNode root; + private final Diagnostics diagnostics; + private int elements; + + + + /*************************************************************************** + ** What a scene says once parsed: the stock it was drawn for and its + ** elements for one resolution, in paint order. + ***************************************************************************/ + record Scene(Length width, Length height, List elements) + { + } + + + + /*************************************************************************** + ** One scene and the collector that will hold its ids and findings. + ***************************************************************************/ + private SceneParser(JsonNode root, Diagnostics diagnostics) + { + this.root = root; + this.diagnostics = diagnostics; + } + + + + /*************************************************************************** + ** Parse a scene for one of the three resolutions. + ***************************************************************************/ + static Scene parse(JsonNode root, int dpi, Diagnostics diagnostics) + { + SceneParser parser = new SceneParser(root, diagnostics); + parser.structure(); + Length width = parser.physical(root.get("design").get("width"), "/design/width"); + Length height = parser.physical(root.get("design").get("height"), "/design/height"); + Scene scene = new Scene(width, height, parser.build(root.get("elements"), "/elements", dpi)); + for(int other : Scene2.RESOLUTIONS) + { + if(other != dpi) + { + parser.build(root.get("elements"), "/elements", other); + } + } + return (scene); + } + + + + /*************************************************************************** + ** Everything that does not depend on the resolution. + ***************************************************************************/ + private void structure() + { + object(root, "", TOP); + if(!Scene2.SCHEMA.equals(root.path("schema").textValue())) + { + throw invalid(null, "/schema"); + } + object(root.get("design"), "/design", Set.of("width", "height")); + JsonNode defaults = root.get("defaults"); + if(defaults != null) + { + object(defaults, "/defaults", Set.of("font", "size")); + String font = defaults.path("font").textValue(); + if(defaults.has("font") && (font == null || !FONT.matcher(font).matches())) + { + throw invalid(null, "/defaults/font"); + } + if(defaults.has("size")) + { + physical(defaults.get("size"), "/defaults/size"); + } + } + JsonNode samples = root.get("samples"); + if(samples != null) + { + object(samples, "/samples", null); + if(samples.size() > MAXIMUM_SAMPLES) + { + throw diagnostics.refuse("SCENE_INVALID", null, "/samples", null, Map.of("samples", samples.size()), Map.of("samples", MAXIMUM_SAMPLES)); + } + samples.forEach(sample -> object(sample, "/samples", null)); + } + declare(root.get("elements"), "/elements", 1); + JsonNode overrides = root.get("overrides"); + if(overrides != null) + { + array(overrides, "/overrides"); + if(overrides.size() > MAXIMUM_OVERRIDES) + { + throw diagnostics.refuse("SCENE_INVALID", null, "/overrides", null, Map.of("overrides", overrides.size()), Map.of("overrides", MAXIMUM_OVERRIDES)); + } + for(int index = 0; index < overrides.size(); index++) + { + override(overrides.get(index), "/overrides/" + index); + } + } + } + + + + /*************************************************************************** + ** Declare every element id, depth first, before anything refers to one. An + ** id is the only author text a diagnostic may carry, so it is held to its + ** pattern here and must be unique. + ***************************************************************************/ + private void declare(JsonNode list, String pointer, int depth) + { + array(list, pointer); + for(int index = 0; index < list.size(); index++) + { + JsonNode element = list.get(index); + String at = pointer + "/" + index; + if(++elements > MAXIMUM_ELEMENTS) + { + throw diagnostics.refuse("SCENE_INVALID", null, "/elements", null, Map.of("elements", elements), Map.of("elements", MAXIMUM_ELEMENTS)); + } + object(element, at, null); + String id = element.path("id").textValue(); + if(id == null || !Diagnostics.ID.matcher(id).matches() || diagnostics.declared(id)) + { + throw invalid(null, at + "/id"); + } + diagnostics.declare(id, at); + String type = element.path("type").textValue(); + if("group".equals(type)) + { + if(depth > MAXIMUM_DEPTH) + { + throw diagnostics.refuse("SCENE_INVALID", id, at, null, Map.of("depth", depth), Map.of("depth", MAXIMUM_DEPTH)); + } + declare(element.get("children"), at + "/children", depth + 1); + } + else if(type == null || !ElementFactory.FACTORIES.containsKey(type)) + { + throw invalid(id, at + "/type"); + } + } + } + + + + /*************************************************************************** + ** One override: the resolutions it is for, and replacement properties by + ** element id. It may not change what an element is. + ***************************************************************************/ + private void override(JsonNode override, String pointer) + { + object(override, pointer, Set.of("when", "set")); + JsonNode when = override.get("when"); + JsonNode set = override.get("set"); + object(when, pointer + "/when", Set.of("dpi")); + array(when.get("dpi"), pointer + "/when/dpi"); + Set resolutions = new HashSet<>(); + for(JsonNode dpi : when.get("dpi")) + { + if(!dpi.isIntegralNumber() || !dpi.canConvertToInt() || !Scene2.RESOLUTIONS.contains(dpi.intValue()) || !resolutions.add(dpi.intValue())) + { + throw invalid(null, pointer + "/when/dpi"); + } + } + if(resolutions.isEmpty()) + { + throw invalid(null, pointer + "/when/dpi"); + } + object(set, pointer + "/set", null); + if(set.isEmpty()) + { + throw invalid(null, pointer + "/set"); + } + for(Map.Entry entry : set.properties()) + { + if(!diagnostics.declared(entry.getKey())) + { + throw invalid(null, pointer + "/set"); + } + JsonNode replacement = entry.getValue(); + if(!replacement.isObject() || replacement.isEmpty() || FIXED.stream().anyMatch(replacement::has)) + { + throw invalid(entry.getKey(), pointer + "/set/" + entry.getKey()); + } + } + } + + + + /*************************************************************************** + ** Build a list of elements for one resolution, with the overrides for it. + ***************************************************************************/ + private List build(JsonNode list, String pointer, int dpi) + { + List built = new ArrayList<>(); + for(int index = 0; index < list.size(); index++) + { + JsonNode element = list.get(index); + String at = pointer + "/" + index; + String id = element.get("id").textValue(); + Map overrides = new LinkedHashMap<>(); + Map pointers = new LinkedHashMap<>(); + JsonNode all = root.path("overrides"); + for(int entry = 0; entry < all.size(); entry++) + { + JsonNode replacement = all.get(entry).get("set").get(id); + if(replacement != null && applies(all.get(entry), dpi)) + { + String from = "/overrides/" + entry + "/set/" + id + "/"; + replacement.properties().forEach(property -> + { + overrides.put(property.getKey(), property.getValue()); + pointers.put(property.getKey(), from + property.getKey()); + }); + } + } + ElementSource source = new ElementSource(id, at, element, overrides, pointers, diagnostics); + if("group".equals(source.type())) + { + source.only(Set.of("children")); + built.add(new GroupElement(source, build(element.get("children"), at + "/children", dpi))); + } + else + { + ElementFactory factory = ElementFactory.FACTORIES.get(source.type()); + source.only(factory.properties()); + built.add(factory.create(source)); + } + } + return (built); + } + + + + /*************************************************************************** + ** Whether an override is for a resolution. + ***************************************************************************/ + private static boolean applies(JsonNode override, int dpi) + { + for(JsonNode resolution : override.get("when").get("dpi")) + { + if(resolution.intValue() == dpi) + { + return (true); + } + } + return (false); + } + + + + /*************************************************************************** + ** A physical length that is more than nothing: a stock side or a type size. + ***************************************************************************/ + private Length physical(JsonNode value, String pointer) + { + try + { + Length length = Length.parse(value == null ? null : value.textValue()); + if(length.physical() && length.thousandths() > 0) + { + return (length); + } + } + catch(IllegalArgumentException expected) + { + // Refused below, with the pointer; the cause holds nothing more. + } + throw invalid(null, pointer); + } + + + + /*************************************************************************** + ** A JSON object whose properties are all among `names`, when names are + ** given. Unknown names are counted, never repeated. + ***************************************************************************/ + private void object(JsonNode node, String pointer, Set names) + { + if(node == null || !node.isObject()) + { + throw invalid(null, pointer); + } + if(names != null) + { + int unknown = 0; + for(Map.Entry property : node.properties()) + { + unknown += names.contains(property.getKey()) ? 0 : 1; + } + if(unknown > 0) + { + throw diagnostics.refuse("SCENE_INVALID", null, pointer, null, Map.of("unknownProperties", unknown), Map.of("unknownProperties", 0)); + } + } + } + + + + /*************************************************************************** + ** A JSON array; each caller holds it to its own bound. + ***************************************************************************/ + private void array(JsonNode node, String pointer) + { + if(node == null || !node.isArray()) + { + throw invalid(null, pointer); + } + } + + + + /*************************************************************************** + ** The refusal for a place in the scene that is not what the schema says. + ***************************************************************************/ + private Refusal invalid(String element, String pointer) + { + return (diagnostics.refuse("SCENE_INVALID", element, pointer, null, null, null)); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/UnavailableElement.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/UnavailableElement.java new file mode 100644 index 0000000..622f5b6 --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/UnavailableElement.java @@ -0,0 +1,94 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import com.kingsrook.qbits.renderer.RenderInput; + + +/******************************************************************************* + ** An element the schema declares but this renderer cannot draw yet. It is + ** parsed and placed like any other, so its box is checked and reported, and it + ** is then refused by name: a scene that needs it needs a newer renderer. It is + ** never drawn as something else and never skipped. + *******************************************************************************/ +public final class UnavailableElement implements SceneElement +{ + private final String id; + private final Placement placement; + + + + /*************************************************************************** + ** The placeholder for one element; its rotation is read so a bad one is + ** still refused as the scene's mistake. + ***************************************************************************/ + public UnavailableElement(ElementSource source) + { + id = source.id(); + placement = source.placement(); + source.rotation(); + } + + + + /*************************************************************************** + ** The id the scene declares for the element. + ***************************************************************************/ + @Override + public String id() + { + return (id); + } + + + + /*************************************************************************** + ** The element's box, by the standing rules. + ***************************************************************************/ + @Override + public Frame place(Layout layout) + { + return (layout.place(placement)); + } + + + + /*************************************************************************** + ** Refused when the scene is validated and before any ink. + ***************************************************************************/ + @Override + public void check(Frame frame, RenderInput input, Diagnostics diagnostics) + { + throw diagnostics.refuse("RENDERER_TOO_OLD", id, null, frame, null, null); + } + + + + /*************************************************************************** + ** Never reached through the scene, which checks first; refused all the same. + ***************************************************************************/ + @Override + public void paint(Canvas canvas, Frame frame, RenderInput input, Diagnostics diagnostics) + { + throw diagnostics.refuse("RENDERER_TOO_OLD", id, null, frame, null, null); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/ZplEmitter.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/ZplEmitter.java new file mode 100644 index 0000000..0bed59d --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/ZplEmitter.java @@ -0,0 +1,130 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HexFormat; +import com.kingsrook.qbits.renderer.Wire; + + +/******************************************************************************* + ** A canvas as printer commands: the header version 1 writes, then uncompressed + ** `^GFA` graphic fields, then the end of the label. Nothing else is emitted, + ** so the host's output policy accepts a version 2 label with no change. + ** + ** Only ink is sent. Rows are cut into bands wherever at least `BLANK_ROWS` + ** blank rows separate ink, each band is trimmed to its inked columns, and a + ** band taller than one field may hold is continued in the next field. A band + ** needs at least one inked row and is followed by a gap, so a label of the + ** greatest height has far fewer fields than the policy allows. + *******************************************************************************/ +final class ZplEmitter +{ + static final int BLANK_ROWS = 8; + static final int MAXIMUM_FIELD_BYTES = 99_999; + + + + /*************************************************************************** + ** Utility class. + ***************************************************************************/ + private ZplEmitter() + { + } + + + + /*************************************************************************** + ** The label for a page of `pageWidth` by `pageHeight` dots whose printable + ** area, the canvas, starts at `originX`, `originY`. + ***************************************************************************/ + static byte[] emit(Canvas canvas, int pageWidth, int pageHeight, int originX, int originY, int maximumBytes) throws IOException + { + Wire.Bytes output = new Wire.Bytes(maximumBytes); + write(output, "^XA^PW" + pageWidth + "^LL" + pageHeight + "^LH0,0^CI28\n"); + int row = 0; + while(row < canvas.height()) + { + if(canvas.firstInk(row) < 0) + { + row++; + continue; + } + int last = row; + int left = canvas.width(); + int right = 0; + for(int next = row; next < canvas.height() && next - last <= BLANK_ROWS; next++) + { + int first = canvas.firstInk(next); + if(first >= 0) + { + last = next; + left = Math.min(left, first); + right = Math.max(right, canvas.lastInk(next)); + } + } + int stride = (right - left + 8) / 8; + int rowsPerField = MAXIMUM_FIELD_BYTES / stride; + for(int first = row; first <= last; first += rowsPerField) + { + field(output, canvas, left, right, first, Math.min(rowsPerField, last + 1 - first), originX, originY); + } + row = last + 1; + } + write(output, "^XZ\n"); + return (output.result()); + } + + + + /*************************************************************************** + ** One graphic field: `rows` rows of the canvas from column `left` to column + ** `right`, most significant bit first, with the padding bits left white. + ***************************************************************************/ + private static void field(Wire.Bytes output, Canvas canvas, int left, int right, int top, int rows, int originX, int originY) throws IOException + { + int stride = (right - left + 8) / 8; + byte[] raster = new byte[stride * rows]; + for(int row = 0; row < rows; row++) + { + for(int column = left; column <= right; column++) + { + if(canvas.get(column, top + row)) + { + raster[row * stride + (column - left) / 8] |= (byte) (128 >> (column - left) % 8); + } + } + } + write(output, "^FO" + (originX + left) + "," + (originY + top) + "^GFA," + raster.length + "," + raster.length + "," + stride + "," + HexFormat.of().withUpperCase().formatHex(raster) + "^FS\n"); + } + + + + /*************************************************************************** + ** All output is generated ASCII. + ***************************************************************************/ + private static void write(Wire.Bytes output, String value) throws IOException + { + output.write(value.getBytes(StandardCharsets.US_ASCII)); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/barcode/BarcodeFactory.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/barcode/BarcodeFactory.java new file mode 100644 index 0000000..768056f --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/barcode/BarcodeFactory.java @@ -0,0 +1,61 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2.barcode; + + +import java.util.Set; +import com.kingsrook.qbits.renderer.scene2.ElementFactory; +import com.kingsrook.qbits.renderer.scene2.ElementSource; +import com.kingsrook.qbits.renderer.scene2.SceneElement; +import com.kingsrook.qbits.renderer.scene2.UnavailableElement; + + +/******************************************************************************* + ** Barcodes: a symbol fitted to its box in whole-dot modules, never below its + ** stated minimum. + ** + ** This is the placeholder of the scene 2 core: a `barcode` element is parsed and + ** placed, then refused as `RENDERER_TOO_OLD`. The package that draws it + ** replaces this class and nothing else. + *******************************************************************************/ +public final class BarcodeFactory implements ElementFactory +{ + /*************************************************************************** + ** The property names the design gives a `barcode` element. Their values are + ** not read yet. + ***************************************************************************/ + @Override + public Set properties() + { + return (Set.of("symbology", "symbologyPath", "allowedSymbologies", "value", "valuePath", "minModule", "maxModule", "quiet", "gs1", "hri", "scannerSymbologyPath", "aimIdentifierPath", "rotation")); + } + + + + /*************************************************************************** + ** The element, placed and then refused by name. + ***************************************************************************/ + @Override + public SceneElement create(ElementSource source) + { + return (new UnavailableElement(source)); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/graphics/GraphicsFactory.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/graphics/GraphicsFactory.java new file mode 100644 index 0000000..5887ebe --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/graphics/GraphicsFactory.java @@ -0,0 +1,60 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2.graphics; + + +import java.util.Set; +import com.kingsrook.qbits.renderer.scene2.ElementFactory; +import com.kingsrook.qbits.renderer.scene2.ElementSource; +import com.kingsrook.qbits.renderer.scene2.SceneElement; +import com.kingsrook.qbits.renderer.scene2.UnavailableElement; + + +/******************************************************************************* + ** Images: an asset scaled into a physical box and reduced to black and white. + ** + ** This is the placeholder of the scene 2 core: a `image` element is parsed and + ** placed, then refused as `RENDERER_TOO_OLD`. The package that draws it + ** replaces this class and nothing else. + *******************************************************************************/ +public final class GraphicsFactory implements ElementFactory +{ + /*************************************************************************** + ** The property names the design gives a `image` element. Their values are + ** not read yet. + ***************************************************************************/ + @Override + public Set properties() + { + return (Set.of("asset", "fit", "mode", "rotation")); + } + + + + /*************************************************************************** + ** The element, placed and then refused by name. + ***************************************************************************/ + @Override + public SceneElement create(ElementSource source) + { + return (new UnavailableElement(source)); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/text/TextFactory.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/text/TextFactory.java new file mode 100644 index 0000000..0a4b65f --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/text/TextFactory.java @@ -0,0 +1,61 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2.text; + + +import java.util.Set; +import com.kingsrook.qbits.renderer.scene2.ElementFactory; +import com.kingsrook.qbits.renderer.scene2.ElementSource; +import com.kingsrook.qbits.renderer.scene2.SceneElement; +import com.kingsrook.qbits.renderer.scene2.UnavailableElement; + + +/******************************************************************************* + ** Text: a TrueType face rasterised by the renderer, with size, fit and legibility + ** rules. + ** + ** This is the placeholder of the scene 2 core: a `text` element is parsed and + ** placed, then refused as `RENDERER_TOO_OLD`. The package that draws it + ** replaces this class and nothing else. + *******************************************************************************/ +public final class TextFactory implements ElementFactory +{ + /*************************************************************************** + ** The property names the design gives a `text` element. Their values are + ** not read yet. + ***************************************************************************/ + @Override + public Set properties() + { + return (Set.of("value", "valuePath", "font", "size", "lineHeight", "fit", "maxLength", "threshold", "reverse", "rotation")); + } + + + + /*************************************************************************** + ** The element, placed and then refused by name. + ***************************************************************************/ + @Override + public SceneElement create(ElementSource source) + { + return (new UnavailableElement(source)); + } +} diff --git a/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/upgrade/SceneUpgrade.java b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/upgrade/SceneUpgrade.java new file mode 100644 index 0000000..a41890a --- /dev/null +++ b/renderer/src/main/java/com/kingsrook/qbits/renderer/scene2/upgrade/SceneUpgrade.java @@ -0,0 +1,54 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2.upgrade; + + +import java.util.Map; +import com.fasterxml.jackson.databind.JsonNode; +import com.kingsrook.qbits.renderer.Wire; + + +/******************************************************************************* + ** `UPGRADE_SCENE`: a version 2 rendering of a version 1 scene, for an author to + ** review and save as a draft. The operation is routed here from the first + ** version 2 renderer so the wire is settled; the conversion itself is a later + ** package, and until then the operation is refused by name. + *******************************************************************************/ +public final class SceneUpgrade +{ + /*************************************************************************** + ** Utility class. + ***************************************************************************/ + private SceneUpgrade() + { + } + + + + /*************************************************************************** + ** Answer one `UPGRADE_SCENE` request. + ***************************************************************************/ + public static Map upgrade(JsonNode request) + { + Wire.text(request.get("requestId"), 128); + throw new IllegalArgumentException("UNSUPPORTED_OPERATION"); + } +} diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/MutationCampaignTest.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/MutationCampaignTest.java index 709b02c..96136b8 100644 --- a/renderer/src/test/java/com/kingsrook/qbits/renderer/MutationCampaignTest.java +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/MutationCampaignTest.java @@ -24,12 +24,23 @@ package com.kingsrook.qbits.renderer; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.exc.StreamConstraintsException; +import com.fasterxml.jackson.core.io.JsonEOFException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.kingsrook.qbits.renderer.scene2.Refusal; +import com.kingsrook.qbits.renderer.scene2.SceneFixture; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -40,9 +51,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /******************************************************************************* - ** The mutation harness is deterministic and reports what it should, and one - ** bounded campaign runs in the normal test phase over the version 1 scene - ** parser. Seeds, limits and the replay command: `docs/security/fuzzing.md`. + ** The mutation harness is deterministic and reports what it should, and two + ** bounded campaigns run in the normal test phase: over the version 1 scene + ** parser, and over the version 2 scene parser, canvas and emitter behind the + ** worker's routing. + ** Seeds, limits and the replay command: `docs/security/fuzzing.md`. *******************************************************************************/ class MutationCampaignTest { @@ -55,6 +68,70 @@ class MutationCampaignTest //////////////////////////////////////////////////////////////////////////////////////////////// private static final ObjectMapper INDEPENDENT = new ObjectMapper(); + ///////////////////////////////////////////////////////////////////////////////////////////////// + // what a parser may throw to say no: its own IllegalArgumentException, and exactly the JSON // + // reader's exceptions for text that is not one JSON value. A NullPointerException, an index // + // out of bounds or any other failure is a crash in the parser, and so a finding // + ///////////////////////////////////////////////////////////////////////////////////////////////// + private static final Set> JSON_REFUSALS = Set.of(JsonParseException.class, JsonEOFException.class, MismatchedInputException.class, StreamConstraintsException.class); + + ///////////////////////////////////////////////////////////////////////////////////////////// + // labels the version 2 campaign rendered and read back, so that it cannot pass without // + // having reached the canvas and the emitter // + ///////////////////////////////////////////////////////////////////////////////////////////// + private static final AtomicInteger LABELS = new AtomicInteger(); + + private static final Pattern LENGTH = Pattern.compile("-?[0-9]{1,5}(\\.[0-9]{1,3})?(mm|in|pt|%|dot)"); + private static final Pattern ELEMENT_ID = Pattern.compile("[A-Za-z][A-Za-z0-9_-]{0,31}"); + + + + /*************************************************************************** + ** Whether a failure is a parser saying no in a declared way. + ***************************************************************************/ + private static boolean refusal(Throwable failure) + { + return (failure instanceof IllegalArgumentException || JSON_REFUSALS.contains(failure.getClass())); + } + + + + /*************************************************************************** + ** The version 1 parser's own refusal of a source the routing sent to it. It + ** is a plain `IllegalArgumentException` there, which on the version 2 route + ** is a broken invariant, so the target marks the one that is declared. + ***************************************************************************/ + private static final class VersionOneRefusal extends RuntimeException + { + private static final long serialVersionUID = 1L; + + + + /************************************************************************ + ** The refusal the version 1 parser raised. + ************************************************************************/ + VersionOneRefusal(IllegalArgumentException cause) + { + super(cause); + } + } + + + + /*************************************************************************** + ** Whether a failure is the worker's routing saying no to a label source in + ** a declared way: the version 2 `Refusal`, which carries a diagnostic (the + ** routing raises one for unreadable JSON and for an unknown schema too), or + ** the version 1 parser's refusal of a version 1 source. Any other + ** `IllegalArgumentException` is not a refusal: the scene 2 package raises + ** them for its own broken invariants (`CANVAS_BOUNDS`, `CANVAS_LIMIT`, + ** `LENGTH_NOT_ABSOLUTE`, `LENGTH_NOT_PHYSICAL`), which no scene may reach. + ***************************************************************************/ + private static boolean labelRefusal(Throwable failure) + { + return (failure instanceof Refusal || failure instanceof VersionOneRefusal); + } + /*************************************************************************** @@ -131,8 +208,9 @@ class MutationCampaignTest /*************************************************************************** ** Property: whatever the version 1 scene parser accepts is, read by a second ** parser, a version 1 scene of known elements within the element limit. Every - ** other input is refused with an exception (the worker turns any exception - ** into its opaque error answer); a stack overflow would be a finding. + ** other input is refused in a declared way: the parser's own + ** IllegalArgumentException or the JSON reader's parse exception. Any other + ** exception, and a stack overflow, is a finding. ***************************************************************************/ @Test void theVersionOneSceneParserAcceptsOnlyVersionOneScenes() throws Exception @@ -150,7 +228,7 @@ class MutationCampaignTest int iterations = MutationCampaign.iterations(ITERATIONS); for(long seed : MutationCampaign.seeds(SEEDS)) { - MutationCampaign.Result result = campaign.run(seed, iterations, MutationCampaignTest::validateScene, Exception.class::isInstance); + MutationCampaign.Result result = campaign.run(seed, iterations, MutationCampaignTest::validateScene, MutationCampaignTest::refusal); assertEquals(List.of(), result.findings(), "replay with -Dfuzz.seed=" + seed + " -Dfuzz.iterations=" + iterations); assertFalse(result.accepted() == 0 || result.refused() == 0, "the campaign must reach both acceptance and refusal: " + result); } @@ -158,6 +236,246 @@ class MutationCampaignTest + + /*************************************************************************** + ** The refusal rule itself: a crash is never a refusal. + ***************************************************************************/ + @Test + void onlyDeclaredRefusalsCountAsRefusals() throws Exception + { + assertTrue(refusal(new IllegalArgumentException("INVALID_ZPL_SCENE"))); + assertTrue(refusal(assertThrows(Refusal.class, () -> RendererMain.process(SceneFixture.request("VALIDATE", "{}"))))); + assertTrue(refusal(assertThrows(JsonParseException.class, () -> Wire.JSON.readTree("{\"schema\":")))); + assertTrue(refusal(assertThrows(MismatchedInputException.class, () -> Wire.JSON.readTree("{} {}")))); + for(Throwable crash : List.of(new NullPointerException(), new IndexOutOfBoundsException(), new ClassCastException(), new ArithmeticException(), new IllegalStateException("DIAGNOSTIC_NOT_DECLARED"), + new java.io.IOException("OUTPUT_LIMIT"), new RuntimeException(), new StackOverflowError(), new AssertionError())) + { + assertFalse(refusal(crash), crash.toString()); + } + MutationCampaign campaign = new MutationCampaign(List.of("0123456789".getBytes(StandardCharsets.US_ASCII)), List.of()); + MutationCampaign.Result result = campaign.run(42L, 50, input -> + { + throw new NullPointerException("a parser that crashes on everything"); + }, MutationCampaignTest::refusal); + assertEquals(50, result.findings().size()); + assertEquals(0, result.refused()); + } + + + + /*************************************************************************** + ** The rule of the version 2 campaign: only a refusal that carries a + ** diagnostic, or the version 1 parser's own for a version 1 source. The + ** package's broken invariants are `IllegalArgumentException`s as well, and a + ** campaign that took any of those for a refusal would pass a placement bug. + ***************************************************************************/ + @Test + void onTheLabelRouteOnlyARefusalWithADiagnosticCountsAsARefusal() throws Exception + { + assertTrue(labelRefusal(assertThrows(Refusal.class, () -> RendererMain.process(SceneFixture.request("VALIDATE", "{}"))))); + assertTrue(labelRefusal(assertThrows(Refusal.class, () -> RendererMain.process(SceneFixture.request("RENDER", "{\"schema\":"))))); + assertTrue(labelRefusal(assertThrows(Refusal.class, () -> RendererMain.process(SceneFixture.request("RENDER", SceneFixture.scene("{\"id\":\"a\",\"type\":\"box\",\"width\":\"84600in\",\"fill\":true}")))))); + assertTrue(labelRefusal(assertThrows(VersionOneRefusal.class, () -> checkLabelSource("{\"schema\":\"qqq-zpl-scene/1\",\"elements\":[{\"type\":\"circle\"}]}".getBytes(StandardCharsets.UTF_8))))); + for(Throwable crash : List.of(new IllegalArgumentException("CANVAS_BOUNDS"), new IllegalArgumentException("CANVAS_LIMIT"), new IllegalArgumentException("LENGTH_NOT_ABSOLUTE"), + new IllegalArgumentException("LENGTH_NOT_PHYSICAL"), new IllegalArgumentException("INVALID_ZPL_SCENE"), new ArithmeticException("integer overflow"), new NullPointerException(), new IllegalStateException("DIAGNOSTIC_NOT_DECLARED"), + new java.io.IOException("OUTPUT_LIMIT"), new JsonParseException(null, "not JSON"), new StackOverflowError(), new AssertionError())) + { + assertFalse(labelRefusal(crash), crash.toString()); + } + + //////////////////////////////////////////////////////////////////////////////////////////// + // the share that is rendered is chosen by the input alone, so a replay takes the same // + // path, and it is about a quarter of the version 2 inputs // + //////////////////////////////////////////////////////////////////////////////////////////// + int rendered = 0; + for(int index = 0; index < 400; index++) + { + byte[] source = ("{\"schema\":\"qqq-zpl-scene/2\",\"index\":" + index + "}").getBytes(StandardCharsets.UTF_8); + assertEquals(rendered(source), rendered(source.clone())); + rendered += rendered(source) ? 1 : 0; + } + assertTrue(rendered > 60 && rendered < 140, rendered + " of 400 rendered"); + assertFalse(rendered("{\"schema\":\"qqq-zpl-scene/1\",\"elements\":[],\"n\":3}".getBytes(StandardCharsets.UTF_8))); + } + + + + /*************************************************************************** + ** Property: whatever the worker accepts as a label source is, read by a + ** second parser, either a version 1 scene (as above) or a version 2 scene + ** that keeps every rule this checks on its own: only the declared top-level + ** properties, a physical design size, at most 1000 elements with unique ids + ** of the fixed pattern, only boxes and groups (the types this renderer + ** draws), groups at most four deep, every length a number and a unit, and + ** overrides only for the three resolutions and for declared ids. About one + ** version 2 input in four is rendered instead of validated: its label must + ** then be the commands and graphic fields a version 2 label is made of, with + ** no ink outside the printable area, and validation must give the same + ** answer. Every other input is refused with a `Refusal`, which carries a + ** diagnostic (or, for a version 1 source, with that parser's own refusal). + ** Any other exception, an `IllegalArgumentException` of the package's own + ** invariants among them, is a finding. + ***************************************************************************/ + @Test + void theVersionTwoSceneParserAcceptsOnlyVersionTwoScenes() throws Exception + { + List corpus = new ArrayList<>(); + for(String name : List.of("anchors", "rules", "groups")) + { + try(InputStream stream = MutationCampaignTest.class.getResourceAsStream("/scene2/cases/" + name + ".json")) + { + corpus.add(Wire.JSON.writeValueAsBytes(Wire.JSON.readTree(stream).get("scene"))); + } + } + MutationCampaign campaign = new MutationCampaign(corpus, List.of("\"schema\"", "\"qqq-zpl-scene/2\"", "\"qqq-zpl-scene/1\"", "\"design\"", "\"elements\"", "\"overrides\"", "\"when\"", "\"dpi\"", "\"set\"", + "\"children\"", "\"group\"", "\"box\"", "\"text\"", "\"barcode\"", "\"image\"", "\"id\"", "\"type\"", "\"anchor\"", "\"thickness\"", "\"fill\"", "\"rotation\"", "\"center\"", "true", "false", "null", + "mm", "in", "pt", "%", "dot", "-", ".", "203", "300", "600", "99999999999", "4294967499", "{", "}", "[", "]", ":", ",", "\\u0000")); + int iterations = MutationCampaign.iterations(ITERATIONS); + for(long seed : MutationCampaign.seeds(SEEDS)) + { + LABELS.set(0); + MutationCampaign.Result result = campaign.run(seed, iterations, MutationCampaignTest::checkLabelSource, MutationCampaignTest::labelRefusal); + assertEquals(List.of(), result.findings(), "replay with -Dfuzz.seed=" + seed + " -Dfuzz.iterations=" + iterations); + assertFalse(result.accepted() == 0 || result.refused() == 0, "the campaign must reach both acceptance and refusal: " + result); + assertTrue(LABELS.get() > 0, "the campaign must render labels as well as validate scenes: " + result); + System.out.println("version 2 campaign, seed " + seed + ": " + result.accepted() + " accepted (" + LABELS.get() + " labels rendered and read back), " + result.refused() + " refused"); + } + } + + + + /*************************************************************************** + ** Whether the routing sends a source to the frozen version 1 parser, as the + ** second parser reads it. + ***************************************************************************/ + private static boolean versionOne(byte[] scene) + { + try + { + return ("qqq-zpl-scene/1".equals(INDEPENDENT.readTree(scene).path("schema").textValue())); + } + catch(java.io.IOException notJson) + { + return (false); + } + } + + + + /*************************************************************************** + ** Whether an input is rendered rather than validated: about one in four of + ** those that are not version 1 scenes (which stay frozen and have their own + ** campaign), chosen by the bytes alone so that a replay does the same. + ***************************************************************************/ + private static boolean rendered(byte[] scene) + { + return (!versionOne(scene) && (Arrays.hashCode(scene) & 3) == 0); + } + + + + /*************************************************************************** + ** The target: one label source through the worker's routing, validated or + ** rendered, on the stock the version 2 corpus was drawn for. At 203 dpi its + ** page is 406 by 203 dots and its printable area 374 by 171 dots at 16, 16. + ***************************************************************************/ + private static void checkLabelSource(byte[] scene) throws Exception + { + String source = new String(scene, StandardCharsets.UTF_8); + boolean render = rendered(scene); + JsonNode stock = SceneFixture.stock(50_800, 25_400, 2000, 203); + try + { + Map answer = RendererMain.process(SceneFixture.request(render ? "RENDER" : "VALIDATE", stock, source)); + assertEquals("OK", answer.get("outcome")); + assertEquals(render, answer.containsKey("base64")); + if(render) + { + boolean[][] page = SceneFixture.raster(Base64.getDecoder().decode((String) answer.get("base64"))); + assertTrue(page.length == 203 && page[0].length == 406, "accepted a label of another page"); + for(int y = 0; y < page.length; y++) + { + for(int x = 0; x < page[y].length; x++) + { + assertFalse(page[y][x] && (x < 16 || y < 16 || x >= 390 || y >= 187), "ink outside the printable area"); + } + } + assertEquals(answer.get("diagnostics"), RendererMain.process(SceneFixture.request("VALIDATE", stock, source)).get("diagnostics"), "rendered what validation finds otherwise"); + LABELS.incrementAndGet(); + } + } + catch(Refusal refusal) + { + if(render) + { + Refusal validated = assertThrows(Refusal.class, () -> RendererMain.process(SceneFixture.request("VALIDATE", stock, source)), "refused to render what validation accepts"); + assertEquals(refusal.diagnostic().wire(), validated.diagnostic().wire()); + } + throw refusal; + } + catch(IllegalArgumentException undeclared) + { + if(versionOne(scene)) + { + throw new VersionOneRefusal(undeclared); + } + throw undeclared; + } + JsonNode accepted = INDEPENDENT.readTree(source); + if("qqq-zpl-scene/1".equals(accepted.path("schema").textValue())) + { + assertTrue(accepted.path("elements").isArray() && accepted.path("elements").size() <= 1000); + accepted.path("elements").forEach(element -> assertTrue(ELEMENTS.contains(element.path("type").textValue()), () -> "accepted element type " + element.path("type"))); + return; + } + assertEquals("qqq-zpl-scene/2", accepted.path("schema").textValue()); + accepted.fieldNames().forEachRemaining(name -> assertTrue(Set.of("schema", "design", "defaults", "samples", "elements", "overrides").contains(name), () -> "accepted top-level property " + name)); + for(String side : List.of("width", "height")) + { + String length = accepted.path("design").path(side).textValue(); + assertTrue(length != null && LENGTH.matcher(length).matches() && !length.startsWith("-") && !length.endsWith("%") && !length.endsWith("dot"), () -> "accepted design " + side + " " + length); + } + Set ids = new HashSet<>(); + assertVersionTwoElements(accepted.path("elements"), 1, ids); + assertTrue(ids.size() <= 1000, () -> "accepted " + ids.size() + " elements"); + for(JsonNode override : accepted.path("overrides")) + { + override.path("when").path("dpi").forEach(dpi -> assertTrue(dpi.isInt() && Set.of(203, 300, 600).contains(dpi.intValue()), () -> "accepted override resolution " + dpi)); + assertTrue(override.path("when").path("dpi").size() > 0 && override.path("set").size() > 0); + override.path("set").fieldNames().forEachRemaining(id -> assertTrue(ids.contains(id), () -> "accepted an override for an undeclared id")); + } + } + + + + /*************************************************************************** + ** The independent reading of a version 2 element list. + ***************************************************************************/ + private static void assertVersionTwoElements(JsonNode elements, int depth, Set ids) + { + assertTrue(elements.isArray(), "accepted elements that are not a list"); + for(JsonNode element : elements) + { + String id = element.path("id").textValue(); + assertTrue(id != null && ELEMENT_ID.matcher(id).matches() && ids.add(id), () -> "accepted element id " + element.path("id")); + String type = element.path("type").textValue(); + assertTrue("box".equals(type) || "group".equals(type), () -> "accepted element type " + element.path("type")); + for(String name : List.of("x", "y", "width", "height", "thickness")) + { + assertTrue(!element.has(name) || (element.get(name).isTextual() && LENGTH.matcher(element.get(name).textValue()).matches()), () -> "accepted " + name + " " + element.get(name)); + } + Set allowed = "box".equals(type) ? Set.of("id", "type", "anchor", "x", "y", "width", "height", "align", "valign", "thickness", "fill") + : Set.of("id", "type", "anchor", "x", "y", "width", "height", "align", "valign", "children"); + element.fieldNames().forEachRemaining(name -> assertTrue(allowed.contains(name), () -> "accepted " + type + " property " + name)); + if("group".equals(type)) + { + assertTrue(depth <= 4, "accepted groups more than four deep"); + assertVersionTwoElements(element.path("children"), depth + 1, ids); + } + } + } + + /*************************************************************************** ** The target: static validation of one scene on a stock all corpus scenes fit. ***************************************************************************/ diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/SceneRoutingTest.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/SceneRoutingTest.java new file mode 100644 index 0000000..0ab8464 --- /dev/null +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/SceneRoutingTest.java @@ -0,0 +1,274 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer; + + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.kingsrook.qbits.renderer.scene2.SceneFixture; +import com.kingsrook.qbits.renderer.scene2.upgrade.SceneUpgrade; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/******************************************************************************* + ** The worker routes a label by the `schema` of its scene alone: version 1 takes + ** the path it always took, version 2 takes the scene 2 package, and anything + ** else is refused by name. The request and the answers gain the fields of + ** renderer 0.2.0. + *******************************************************************************/ +class SceneRoutingTest +{ + private static final String VERSION_ONE = "{\"schema\":\"qqq-zpl-scene/1\",\"elements\":[{\"type\":\"box\",\"x\":30,\"y\":30,\"width\":100,\"height\":50,\"thickness\":2}]}"; + private static final String VERSION_TWO = SceneFixture.scene("{\"id\":\"a\",\"type\":\"box\",\"x\":\"6dot\",\"y\":\"6dot\",\"width\":\"100dot\",\"height\":\"50dot\",\"thickness\":\"2dot\"}"); + + + + /*************************************************************************** + ** The label of a request that is accepted. + ***************************************************************************/ + private static String label(Map answer) + { + return (new String(Base64.getDecoder().decode((String) answer.get("base64")), StandardCharsets.US_ASCII)); + } + + + + /*************************************************************************** + ** The same box as a version 1 scene and as a version 2 scene: the first is + ** the printer's own box command, as it always was; the second is a graphic. + ***************************************************************************/ + @Test + void eachSchemaTakesItsOwnPath() throws Exception + { + Map one = RendererMain.process(SceneFixture.request("RENDER", VERSION_ONE)); + assertEquals("^XA^PW812^LL203^LH0,0^CI28\n^FO30,30^GB100,50,2^FS\n^XZ\n", label(one)); + Map two = RendererMain.process(SceneFixture.request("RENDER", VERSION_TWO)); + assertTrue(label(two).startsWith("^XA^PW812^LL203^LH0,0^CI28\n^FO30,30^GFA,650,650,13," + "F".repeat(25) + "0" + "F".repeat(25) + "0C"), label(two)); + for(Map answer : List.of(one, two)) + { + assertEquals("OK", answer.get("outcome")); + assertEquals("qqq-renderer-0.2.0", answer.get("rendererVersion")); + assertEquals("application/vnd.zebra-zpl", answer.get("mediaType")); + assertEquals(1, answer.get("pageCount")); + assertEquals(Wire.sha256(label(answer).getBytes(StandardCharsets.US_ASCII)), answer.get("sha256")); + assertEquals(label(answer).length(), answer.get("length")); + } + assertEquals(List.of(), one.get("diagnostics")); + assertEquals(5, ((List) two.get("diagnostics")).size()); + for(String source : List.of(VERSION_ONE, VERSION_TWO)) + { + Map validated = RendererMain.process(SceneFixture.request("VALIDATE", source)); + assertEquals("OK", validated.get("outcome")); + assertEquals(List.of("protocol", "requestId", "outcome", "rendererVersion", "catalogVersion", "diagnostics"), List.copyOf(validated.keySet())); + } + } + + + + /*************************************************************************** + ** A source that is neither version is refused by name: an unknown or absent + ** schema at `/schema`, and a source that is not a JSON object at the root. + ***************************************************************************/ + @Test + void anythingElseIsRefusedByName() + { + for(String source : List.of("{\"schema\":\"qqq-zpl-scene/3\",\"elements\":[]}", "{\"schema\":\"qqq-zpl-scene/1 \",\"elements\":[]}", "{\"elements\":[]}", "{\"schema\":2,\"elements\":[]}", "{}")) + { + for(String operation : List.of("VALIDATE", "RENDER")) + { + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "pointer", "/schema"), SceneFixture.refusal(() -> RendererMain.process(SceneFixture.request(operation, source))), source); + } + } + for(String source : List.of("[]", "\"qqq-zpl-scene/2\"", "7", "null", "^XA^FO10,10^GB100,50,2^FS^XZ", "{\"schema\":\"qqq-zpl-scene/2\"", "{\"schema\":\"qqq-zpl-scene/2\",\"schema\":\"qqq-zpl-scene/1\"}", "{} {}", "")) + { + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "pointer", ""), SceneFixture.refusal(() -> RendererMain.process(SceneFixture.request("VALIDATE", source))), source); + } + } + + + + /*************************************************************************** + ** A render target is 203, 300 or 600. A version 2 scene renders at it; a + ** version 1 scene has dots in it and prints only at the stock's resolution. + ***************************************************************************/ + @Test + void aRenderTargetNeedsAVersionTwoScene() throws Exception + { + assertEquals("^XA^PW812^LL203^LH0,0^CI28\n^FO30,30^GB100,50,2^FS\n^XZ\n", label(RendererMain.process(SceneFixture.request("RENDER", VERSION_ONE).put("targetDpi", 203)))); + for(String operation : List.of("VALIDATE", "RENDER")) + { + assertEquals(Map.of("code", "TARGET_REQUIRES_SCENE_2", "severity", "ERROR", "measured", Map.of("dpi", 300), "limit", Map.of("dpi", 203)), + SceneFixture.refusal(() -> RendererMain.process(SceneFixture.request(operation, VERSION_ONE).put("targetDpi", 300)))); + } + assertTrue(label(RendererMain.process(SceneFixture.request("RENDER", VERSION_TWO).put("targetDpi", 600))).startsWith("^XA^PW2400^LL600^LH0,0^CI28\n^FO77,77^GFA,")); + assertNull(new RenderInput(SceneFixture.request("RENDER", VERSION_TWO)).targetDpi); + assertEquals(Integer.valueOf(300), new RenderInput(SceneFixture.request("RENDER", VERSION_TWO).put("targetDpi", 300)).targetDpi); + assertEquals("INVALID_TARGET_DPI", assertThrows(IllegalArgumentException.class, () -> new RenderInput(SceneFixture.request("RENDER", VERSION_TWO).put("targetDpi", 250))).getMessage()); + for(ObjectNode request : List.of(SceneFixture.request("RENDER", VERSION_TWO).put("targetDpi", 72), SceneFixture.request("RENDER", VERSION_TWO).put("targetDpi", 1200), SceneFixture.request("RENDER", VERSION_TWO).put("targetDpi", "300"), + SceneFixture.request("RENDER", VERSION_TWO).put("targetDpi", 300.5), SceneFixture.request("RENDER", VERSION_TWO).putNull("targetDpi"))) + { + assertEquals("INVALID_INTEGER", assertThrows(IllegalArgumentException.class, () -> new RenderInput(request)).getMessage(), request.get("targetDpi").toString()); + } + } + + + + /*************************************************************************** + ** The only diagnostics mode is to collect them; any other word is refused. + ***************************************************************************/ + @Test + void theDiagnosticsModeIsCollectOrAbsent() throws Exception + { + assertFalse(new RenderInput(SceneFixture.request("RENDER", VERSION_TWO)).collectDiagnostics); + assertTrue(new RenderInput(SceneFixture.request("RENDER", VERSION_TWO).put("diagnostics", "COLLECT")).collectDiagnostics); + for(ObjectNode request : List.of(SceneFixture.request("RENDER", VERSION_TWO).put("diagnostics", "ALL"), SceneFixture.request("RENDER", VERSION_TWO).put("diagnostics", "collect"), SceneFixture.request("RENDER", VERSION_TWO).put("diagnostics", true), + SceneFixture.request("RENDER", VERSION_TWO).putNull("diagnostics"))) + { + assertEquals("INVALID_DIAGNOSTICS_MODE", assertThrows(IllegalArgumentException.class, () -> new RenderInput(request)).getMessage(), request.get("diagnostics").toString()); + } + Map answer = RendererMain.process(SceneFixture.request("RENDER", VERSION_ONE).put("diagnostics", "COLLECT")); + assertEquals("OK", answer.get("outcome")); + assertEquals(List.of(), answer.get("diagnostics")); + } + + + + /*************************************************************************** + ** The error answer of a version 2 refusal carries its diagnostic as `detail`; + ** every other failure stays the opaque code, with nothing from the source. + ***************************************************************************/ + @Test + void anErrorAnswerCarriesTheDiagnosticOfAVersionTwoRefusal() + { + Exception refusal = assertThrows(IllegalArgumentException.class, () -> RendererMain.process(SceneFixture.request("RENDER", SceneFixture.scene("{\"id\":\"wide\",\"type\":\"box\",\"x\":\"90mm\",\"width\":\"10mm\",\"height\":\"5mm\",\"fill\":true}")))); + assertEquals("ELEMENT_OUTSIDE_PRINTABLE_AREA", RendererMain.errorCode(refusal)); + Map answer = RendererMain.error("request-7", refusal); + assertEquals(List.of("protocol", "requestId", "outcome", "rendererVersion", "catalogVersion", "code", "detail"), List.copyOf(answer.keySet())); + assertEquals(Wire.PROTOCOL, answer.get("protocol")); + assertEquals("request-7", answer.get("requestId")); + assertEquals("ERROR", answer.get("outcome")); + assertEquals("qqq-renderer-0.2.0", answer.get("rendererVersion")); + assertEquals(Barcodes.VERSION, answer.get("catalogVersion")); + assertEquals("ELEMENT_OUTSIDE_PRINTABLE_AREA", answer.get("code")); + assertEquals(Map.of("code", "ELEMENT_OUTSIDE_PRINTABLE_AREA", "severity", "ERROR", "element", "wide", "box", List.of(719, 0, 79, 39), + "measured", Map.of("leftDots", 719, "topDots", 0, "rightDots", 798, "bottomDots", 39), "limit", Map.of("leftDots", 0, "topDots", 0, "rightDots", 764, "bottomDots", 155)), answer.get("detail")); + + Map opaque = RendererMain.error("request-8", new IllegalStateException("Customer Jane Example could not be printed")); + assertEquals(List.of("protocol", "requestId", "outcome", "rendererVersion", "catalogVersion", "code"), List.copyOf(opaque.keySet())); + assertEquals("RENDER_FAILED", opaque.get("code")); + assertEquals("RENDER_FAILED", RendererMain.error("request-9", new IllegalArgumentException("UNSUPPORTED_ZPL_STOCK")).get("code")); + assertEquals("BARCODE_FIT_TOO_SMALL", RendererMain.error("request-9", new IllegalArgumentException("BARCODE_FIT_TOO_SMALL")).get("code")); + } + + + + /*************************************************************************** + ** Nothing a record, a sample or an unknown property holds reaches an answer: + ** the context, the samples and the stray property all carry a marker, the + ** scene is refused in several ways, and no answer contains the marker. + ***************************************************************************/ + @Test + void noAnswerCarriesRecordSampleOrSourceText() throws Exception + { + String marker = "Jane-Example-0012345"; + String head = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"samples\":{\"typical\":{\"record\":{\"name\":\"" + marker + "\"}}},\"elements\":["; + for(String elements : List.of("{\"id\":\"a\",\"type\":\"box\",\"fill\":true,\"" + marker + "\":\"" + marker + "\"}", "{\"id\":\"a\",\"type\":\"box\",\"thickness\":\"" + marker + "\"}", + "{\"id\":\"" + marker + " \",\"type\":\"box\",\"fill\":true}", "{\"id\":\"a\",\"type\":\"" + marker + "\"}", "{\"id\":\"a\",\"type\":\"text\",\"value\":\"" + marker + "\",\"valuePath\":\"/record/name\"}", + "{\"id\":\"a\",\"type\":\"box\",\"x\":\"90mm\",\"width\":\"10mm\",\"height\":\"5mm\",\"fill\":true}", "{\"id\":\"a\",\"type\":\"box\",\"anchor\":\"" + marker + "\",\"fill\":true}")) + { + for(boolean collect : List.of(false, true)) + { + ObjectNode request = SceneFixture.request("RENDER", head + elements + "]}"); + ((ObjectNode) request.get("context")).putObject("record").put("name", marker); + if(collect) + { + request.put("diagnostics", "COLLECT"); + } + Map answer; + try + { + answer = RendererMain.process(request); + assertEquals("REFUSED", answer.get("outcome"), elements); + assertTrue(collect, elements); + } + catch(IllegalArgumentException refusal) + { + answer = RendererMain.error("request-1", refusal); + assertFalse(collect, elements); + assertTrue(answer.containsKey("detail"), elements); + } + assertFalse(Wire.JSON.writeValueAsString(answer).contains("Jane"), elements); + assertFalse(new String(Base64.getDecoder().decode(String.valueOf(answer.getOrDefault("base64", ""))), StandardCharsets.US_ASCII).contains("Jane"), elements); + } + } + } + + + + /*************************************************************************** + ** The two operations of the later packages are routed and, until those + ** packages exist, refused by name. + ***************************************************************************/ + @Test + void theNewOperationsAreRoutedAndRefusedByName() + { + for(String operation : List.of("INSPECT_ASSET", "UPGRADE_SCENE")) + { + ObjectNode request = Wire.JSON.createObjectNode().put("protocol", Wire.PROTOCOL).put("requestId", "request-1").put("operation", operation); + IllegalArgumentException refusal = assertThrows(IllegalArgumentException.class, () -> RendererMain.process(request)); + assertEquals("UNSUPPORTED_OPERATION", refusal.getMessage()); + assertEquals("UNSUPPORTED_OPERATION", RendererMain.errorCode(refusal)); + assertEquals("INVALID_TEXT", assertThrows(IllegalArgumentException.class, () -> RendererMain.process(request.deepCopy().without("requestId"))).getMessage()); + assertEquals("INVALID_PROTOCOL", assertThrows(IllegalArgumentException.class, () -> RendererMain.process(request.deepCopy().put("protocol", "qbit-document-renderer/2"))).getMessage()); + } + assertEquals("UNSUPPORTED_OPERATION", assertThrows(IllegalArgumentException.class, () -> AssetInspector.inspect(Wire.JSON.createObjectNode().put("requestId", "request-1"))).getMessage()); + assertEquals("UNSUPPORTED_OPERATION", assertThrows(IllegalArgumentException.class, () -> SceneUpgrade.upgrade(Wire.JSON.createObjectNode().put("requestId", "request-1"))).getMessage()); + } + + + + /*************************************************************************** + ** The prefix of the bundled faces is reserved, and the input says which + ** supplied assets use it, for the text package to refuse. + ***************************************************************************/ + @Test + void theInputNamesAssetsThatUseTheReservedPrefix() throws Exception + { + byte[] font = {0, 1, 0, 0, 0}; + ObjectNode request = SceneFixture.request("VALIDATE", VERSION_TWO); + for(String name : List.of("qqq-sans", "brand-sans", "qqq-mono-bold", "qqqsans")) + { + ((ArrayNode) request.get("assets")).addObject().put("name", name).put("mediaType", "font/ttf").put("base64", Base64.getEncoder().encodeToString(font)).put("sha256", Wire.sha256(font)).put("fontFamily", "Test Family"); + } + assertEquals("qqq-", RenderInput.RESERVED_ASSET_PREFIX); + assertEquals(List.of("qqq-sans", "qqq-mono-bold"), new RenderInput(request).reservedAssetNames()); + assertEquals(List.of(), new RenderInput(SceneFixture.request("VALIDATE", VERSION_TWO)).reservedAssetNames()); + } +} diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/ZplSceneOneCompatibilityTest.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/ZplSceneOneCompatibilityTest.java index f637c92..99963d1 100644 --- a/renderer/src/test/java/com/kingsrook/qbits/renderer/ZplSceneOneCompatibilityTest.java +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/ZplSceneOneCompatibilityTest.java @@ -56,6 +56,13 @@ class ZplSceneOneCompatibilityTest { private static final String ROOT = "/compat-v1/"; + ///////////////////////////////////////////////////////////////////////////////////////////////// + // the SHA-256 of `SHA256SUMS` itself, as committed with the corpus. The manifest pins every // + // stored output and this pins the manifest, so regenerating the outputs together with their // + // sums still fails here. It changes only when a case is added from a tag that is in production // + ///////////////////////////////////////////////////////////////////////////////////////////////// + static final String MANIFEST_SHA256 = "cb6ec01d084e9077f3b5f6c8f39ed2f1de1116d92697dbbf529557fc84da5b2a"; + //////////////////////////////////////////////////////////////////////////////////////////// // every property a version 1 element reads, by element type; the corpus must use each one // //////////////////////////////////////////////////////////////////////////////////////////// @@ -110,12 +117,14 @@ class ZplSceneOneCompatibilityTest /*************************************************************************** - ** The manifest names exactly the cases, and each stored file has its hash, so - ** a case cannot be dropped or its bytes edited without this failing. + ** The manifest is the committed one, it names exactly the cases, and each + ** stored file has its hash, so a case cannot be dropped, its bytes edited or + ** the whole corpus regenerated without this failing. ***************************************************************************/ @Test void manifestNamesExactlyTheCasesAndTheirStoredBytes() throws Exception { + assertEquals(MANIFEST_SHA256, Wire.sha256(java.util.Objects.requireNonNull(resource("SHA256SUMS"))), "The corpus manifest was regenerated or edited"); Map manifest = manifest(); assertEquals(cases(), List.copyOf(manifest.keySet())); assertFalse(manifest.isEmpty()); diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/CanvasTest.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/CanvasTest.java new file mode 100644 index 0000000..d51c82c --- /dev/null +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/CanvasTest.java @@ -0,0 +1,295 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.io.IOException; +import java.util.List; +import java.util.SplittableRandom; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/******************************************************************************* + ** The 1-bit canvas and its emission: paint order, reverse, bounds, and the + ** proof that the bitmap rebuilt from the emitted fields is the canvas. + *******************************************************************************/ +class CanvasTest +{ + /*************************************************************************** + ** The canvas as a page bitmap, moved by the origin of the printable area. + ***************************************************************************/ + private static boolean[][] page(Canvas canvas, int pageWidth, int pageHeight, int originX, int originY) + { + boolean[][] page = new boolean[pageHeight][pageWidth]; + for(int y = 0; y < canvas.height(); y++) + { + for(int x = 0; x < canvas.width(); x++) + { + page[originY + y][originX + x] = canvas.get(x, y); + } + } + return (page); + } + + + + /*************************************************************************** + ** A new canvas is blank, and a dot is ink or it is not. + ***************************************************************************/ + @Test + void aDotIsInkOrItIsNot() + { + Canvas canvas = new Canvas(19, 5, 203); + assertEquals(19, canvas.width()); + assertEquals(5, canvas.height()); + assertEquals(203, canvas.dpi()); + assertFalse(canvas.get(0, 0) || canvas.get(18, 4)); + assertEquals(-1, canvas.firstInk(2)); + assertEquals(-1, canvas.lastInk(2)); + canvas.set(7, 2, true); + canvas.set(8, 2, true); + canvas.set(18, 2, true); + assertTrue(canvas.get(7, 2) && canvas.get(8, 2) && canvas.get(18, 2)); + assertFalse(canvas.get(6, 2) || canvas.get(9, 2) || canvas.get(7, 1) || canvas.get(7, 3)); + assertEquals(7, canvas.firstInk(2)); + assertEquals(18, canvas.lastInk(2)); + canvas.set(7, 2, false); + assertFalse(canvas.get(7, 2)); + assertEquals(8, canvas.firstInk(2)); + } + + + + /*************************************************************************** + ** Elements paint in order: a later white box clears an earlier black one, + ** and reversing a box swaps black and white inside it only. + ***************************************************************************/ + @Test + void laterPaintCoversEarlierPaintAndReverseSwaps() + { + Canvas canvas = new Canvas(20, 10, 300); + canvas.fill(new Frame(2, 2, 10, 6), true); + canvas.fill(new Frame(4, 4, 3, 2), false); + assertTrue(canvas.get(2, 2) && canvas.get(11, 7) && canvas.get(3, 4) && canvas.get(7, 5)); + assertFalse(canvas.get(4, 4) || canvas.get(6, 5) || canvas.get(12, 2) || canvas.get(2, 8) || canvas.get(1, 2)); + canvas.invert(new Frame(0, 0, 5, 5)); + assertTrue(canvas.get(0, 0) && canvas.get(4, 4) && canvas.get(1, 2)); + assertFalse(canvas.get(2, 2) || canvas.get(3, 4)); + assertTrue(canvas.get(5, 2) && canvas.get(2, 5)); + } + + + + /*************************************************************************** + ** Ink cannot be lost at an edge: painting off the canvas is refused, and so + ** is a canvas of no size or of more dots than the limit. + ***************************************************************************/ + @Test + void paintingOffTheCanvasIsRefused() + { + Canvas canvas = new Canvas(16, 4, 203); + for(int[] dot : new int[][] {{-1, 0}, {0, -1}, {16, 0}, {0, 4}}) + { + assertEquals("CANVAS_BOUNDS", assertThrows(IllegalArgumentException.class, () -> canvas.set(dot[0], dot[1], true)).getMessage()); + assertEquals("CANVAS_BOUNDS", assertThrows(IllegalArgumentException.class, () -> canvas.get(dot[0], dot[1])).getMessage()); + } + assertEquals("CANVAS_BOUNDS", assertThrows(IllegalArgumentException.class, () -> canvas.fill(new Frame(10, 0, 7, 1), true)).getMessage()); + assertEquals("CANVAS_BOUNDS", assertThrows(IllegalArgumentException.class, () -> canvas.invert(new Frame(0, 3, 1, 2))).getMessage()); + assertEquals("CANVAS_LIMIT", assertThrows(IllegalArgumentException.class, () -> new Canvas(0, 4, 203)).getMessage()); + assertEquals("CANVAS_LIMIT", assertThrows(IllegalArgumentException.class, () -> new Canvas(4, 0, 203)).getMessage()); + assertEquals("CANVAS_LIMIT", assertThrows(IllegalArgumentException.class, () -> new Canvas(9999, 6401, 203)).getMessage()); + for(Frame outside : List.of(new Frame(-1, 0, 4, 2), new Frame(0, -1, 4, 2), new Frame(14, 0, 3, 2), new Frame(0, 3, 4, 2), new Frame(16, 0, 1, 1), new Frame(0, 4, 1, 1))) + { + assertEquals("CANVAS_BOUNDS", assertThrows(IllegalArgumentException.class, () -> canvas.fill(outside, true)).getMessage(), outside.toString()); + assertEquals("CANVAS_BOUNDS", assertThrows(IllegalArgumentException.class, () -> canvas.fill(outside, false)).getMessage(), outside.toString()); + assertEquals("CANVAS_BOUNDS", assertThrows(IllegalArgumentException.class, () -> canvas.invert(outside)).getMessage(), outside.toString()); + } + canvas.fill(new Frame(3, 1, 0, 2), true); + canvas.fill(new Frame(3, 1, 2, 0), true); + canvas.invert(new Frame(99, 99, 0, 0)); + for(int row = 0; row < canvas.height(); row++) + { + assertEquals(-1, canvas.firstInk(row), "a box with no dots paints nothing"); + } + } + + + + /*************************************************************************** + ** Property, over seeded boxes on canvases of every width modulo eight: a box + ** is painted a byte at a time, and the result is the same as visiting each + ** dot, at byte boundaries as everywhere else. + ***************************************************************************/ + @Test + void paintingABoxIsPaintingEachOfItsDots() + { + SplittableRandom random = new SplittableRandom(20261112L); + for(int iteration = 0; iteration < 400; iteration++) + { + Canvas canvas = new Canvas(1 + random.nextInt(70), 1 + random.nextInt(12), 203); + boolean[][] expected = new boolean[canvas.height()][canvas.width()]; + for(int box = 0; box < 12; box++) + { + int x = random.nextInt(canvas.width()); + int y = random.nextInt(canvas.height()); + Frame frame = new Frame(x, y, 1 + random.nextInt(canvas.width() - x), 1 + random.nextInt(canvas.height() - y)); + int paint = random.nextInt(3); + if(paint == 2) + { + canvas.invert(frame); + } + else + { + canvas.fill(frame, paint == 1); + } + for(int row = frame.y(); row < frame.bottom(); row++) + { + for(int column = frame.x(); column < frame.right(); column++) + { + expected[row][column] = paint == 2 ? !expected[row][column] : paint == 1; + } + } + } + assertArrayEquals(expected, page(canvas, canvas.width(), canvas.height(), 0, 0), "iteration " + iteration); + } + } + + + + /*************************************************************************** + ** A blank canvas is a label with no fields, and a single dot is one field of + ** one byte at the dot's place on the page. + ***************************************************************************/ + @Test + void onlyInkIsSent() throws IOException + { + Canvas canvas = new Canvas(764, 155, 203); + assertEquals("^XA^PW812^LL203^LH0,0^CI28\n^XZ\n", new String(ZplEmitter.emit(canvas, 812, 203, 24, 24, 1024), java.nio.charset.StandardCharsets.US_ASCII)); + canvas.set(100, 50, true); + assertEquals("^XA^PW812^LL203^LH0,0^CI28\n^FO124,74^GFA,1,1,1,80^FS\n^XZ\n", new String(ZplEmitter.emit(canvas, 812, 203, 24, 24, 1024), java.nio.charset.StandardCharsets.US_ASCII)); + canvas.fill(new Frame(96, 49, 12, 3), true); + assertEquals("^XA^PW812^LL203^LH0,0^CI28\n^FO120,73^GFA,6,6,2,FFF0FFF0FFF0^FS\n^XZ\n", new String(ZplEmitter.emit(canvas, 812, 203, 24, 24, 1024), java.nio.charset.StandardCharsets.US_ASCII)); + assertEquals("OUTPUT_LIMIT", assertThrows(IOException.class, () -> ZplEmitter.emit(canvas, 812, 203, 24, 24, 40)).getMessage()); + } + + + + /*************************************************************************** + ** Rows are cut into bands where at least eight blank rows separate ink, and + ** each band is trimmed to its own columns. Seven blank rows stay inside one + ** band. + ***************************************************************************/ + @Test + void bandsAreCutAtBlankRowsAndTrimmedToTheirColumns() throws IOException + { + Canvas canvas = new Canvas(200, 100, 203); + canvas.fill(new Frame(10, 5, 8, 2), true); + canvas.fill(new Frame(100, 14, 16, 1), true); + canvas.fill(new Frame(40, 23, 8, 1), true); + canvas.fill(new Frame(190, 99, 10, 1), true); + byte[] zpl = ZplEmitter.emit(canvas, 220, 110, 10, 5, 4096); + String fields = new String(zpl, java.nio.charset.StandardCharsets.US_ASCII); + assertEquals(3, SceneFixture.fields(zpl)); + assertTrue(fields.contains("^FO20,10^GFA,140,140,14,"), fields); + assertTrue(fields.contains("^FO50,28^GFA,1,1,1,FF^FS\n"), fields); + assertTrue(fields.contains("^FO200,104^GFA,2,2,2,FFC0^FS\n"), fields); + assertArrayEquals(page(canvas, 220, 110, 10, 5), SceneFixture.raster(zpl)); + } + + + + /*************************************************************************** + ** A band taller than one field may hold continues in the next field: a + ** field is at most 99999 bytes, so 1250 bytes a row leaves 79 rows a field. + ***************************************************************************/ + @Test + void aTallBandContinuesInTheNextField() throws IOException + { + Canvas canvas = new Canvas(9999, 200, 600); + canvas.fill(new Frame(0, 10, 9999, 180), true); + byte[] zpl = ZplEmitter.emit(canvas, 9999, 200, 0, 0, 8 * 1024 * 1024); + assertEquals(3, SceneFixture.fields(zpl)); + String text = new String(zpl, java.nio.charset.StandardCharsets.US_ASCII); + assertTrue(text.contains("^FO0,10^GFA,98750,98750,1250,") && text.contains("^FO0,89^GFA,98750,98750,1250,") && text.contains("^FO0,168^GFA,27500,27500,1250,")); + assertArrayEquals(page(canvas, 9999, 200, 0, 0), SceneFixture.raster(zpl)); + } + + + + /*************************************************************************** + ** The worst case for the number of fields is a label of the greatest height + ** with ink on every ninth row. It stays well inside the 6000 fields the host + ** gate allows. + ***************************************************************************/ + @Test + void theGreatestLabelStaysInsideTheFieldLimit() throws IOException + { + Canvas canvas = new Canvas(8, 32_000, 600); + for(int row = 0; row < canvas.height(); row += ZplEmitter.BLANK_ROWS + 1) + { + canvas.set(3, row, true); + } + byte[] zpl = ZplEmitter.emit(canvas, 8, 32_000, 0, 0, 8 * 1024 * 1024); + assertEquals(3556, SceneFixture.fields(zpl)); + assertArrayEquals(page(canvas, 8, 32_000, 0, 0), SceneFixture.raster(zpl)); + } + + + + /*************************************************************************** + ** Property, over seeded canvases of scattered boxes at every origin: the + ** bitmap rebuilt from the emitted fields equals the canvas, dot for dot, so + ** trimming and banding lose and add nothing. + ***************************************************************************/ + @Test + void theRebuiltBitmapEqualsTheCanvas() throws IOException + { + SplittableRandom random = new SplittableRandom(7919L); + for(int iteration = 0; iteration < 300; iteration++) + { + Canvas canvas = new Canvas(1 + random.nextInt(400), 1 + random.nextInt(300), 203); + int originX = random.nextInt(40); + int originY = random.nextInt(40); + for(int box = random.nextInt(12); box > 0; box--) + { + int x = random.nextInt(canvas.width()); + int y = random.nextInt(canvas.height()); + Frame frame = new Frame(x, y, 1 + random.nextInt(canvas.width() - x), 1 + random.nextInt(Math.min(20, canvas.height() - y))); + if(random.nextInt(4) == 0) + { + canvas.invert(frame); + } + else + { + canvas.fill(frame, random.nextInt(5) != 0); + } + } + byte[] zpl = ZplEmitter.emit(canvas, canvas.width() + originX + random.nextInt(9), canvas.height() + originY + random.nextInt(9), originX, originY, 4 * 1024 * 1024); + boolean[][] rebuilt = SceneFixture.raster(zpl); + assertArrayEquals(page(canvas, rebuilt[0].length, rebuilt.length, originX, originY), rebuilt, "iteration " + iteration); + } + } +} diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/DiagnosticsTest.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/DiagnosticsTest.java new file mode 100644 index 0000000..cbbeb94 --- /dev/null +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/DiagnosticsTest.java @@ -0,0 +1,200 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/******************************************************************************* + ** The gate every diagnostic passes: a closed vocabulary of codes, an element id + ** the scene declares, a pointer of schema names, indexes and declared ids, and + ** named integers. Nothing a record or a context holds has a way in. + *******************************************************************************/ +class DiagnosticsTest +{ + /*************************************************************************** + ** The design's example diagnostic, as the wire carries it. + ***************************************************************************/ + @Test + void aDiagnosticIsACodeADeclaredIdAndNumbers() + { + Diagnostics diagnostics = new Diagnostics(false); + diagnostics.declare("caption", "/elements/1"); + Refusal refusal = diagnostics.refuse("TEXT_DOES_NOT_FIT", "caption", "/elements/1/fit", new Frame(24, 131, 764, 56), + Map.of("widthDots", 902, "sizeCentipoints", 700, "lines", 2), Map.of("widthDots", 764, "lines", 2)); + assertEquals("TEXT_DOES_NOT_FIT", refusal.getMessage()); + Diagnostic diagnostic = refusal.diagnostic(); + assertEquals("TEXT_DOES_NOT_FIT", diagnostic.code()); + assertEquals(Diagnostic.Severity.ERROR, diagnostic.severity()); + assertEquals("caption", diagnostic.element()); + assertEquals("/elements/1/fit", diagnostic.pointer()); + assertEquals(new Frame(24, 131, 764, 56), diagnostic.box()); + assertEquals(Map.of("widthDots", 902, "sizeCentipoints", 700, "lines", 2), diagnostic.measured()); + assertEquals(Map.of("widthDots", 764, "lines", 2), diagnostic.limit()); + assertEquals(Map.of("code", "TEXT_DOES_NOT_FIT", "severity", "ERROR", "element", "caption", "pointer", "/elements/1/fit", "box", List.of(24, 131, 764, 56), + "measured", Map.of("widthDots", 902, "sizeCentipoints", 700, "lines", 2), "limit", Map.of("widthDots", 764, "lines", 2)), diagnostic.wire()); + assertEquals(List.of("code", "severity", "element", "pointer", "box", "measured", "limit"), List.copyOf(diagnostic.wire().keySet())); + assertEquals(List.of("lines", "sizeCentipoints", "widthDots"), List.copyOf(diagnostic.measured().keySet())); + assertThrows(UnsupportedOperationException.class, () -> diagnostic.measured().put("widthDots", 1)); + assertEquals(Map.of("code", "STOCK_MISMATCH", "severity", "ERROR"), diagnostics.refuse("STOCK_MISMATCH", null, null, null, null, null).diagnostic().wire()); + } + + + + /*************************************************************************** + ** An element id travels only when the scene declared it, so a record value + ** put where an id goes is a defect that stays inside the renderer. + ***************************************************************************/ + @Test + void onlyADeclaredIdMayBeNamed() + { + Diagnostics diagnostics = new Diagnostics(false); + assertFalse(diagnostics.declared("caption")); + assertNull(diagnostics.pointer("caption")); + diagnostics.declare("caption", "/elements/0"); + assertTrue(diagnostics.declared("caption")); + assertEquals("/elements/0", diagnostics.pointer("caption")); + for(String element : List.of("Jane Example", "code", "Caption", "caption ", "(01)00012345678905")) + { + assertEquals("DIAGNOSTIC_NOT_DECLARED", assertThrows(IllegalStateException.class, () -> diagnostics.refuse("VALUE_MISSING", element, null, null, null, null)).getMessage(), element); + assertEquals("DIAGNOSTIC_NOT_DECLARED", assertThrows(IllegalStateException.class, () -> diagnostics.warn("FIT_NOT_PROVEN", element, null, null, null, null)).getMessage(), element); + } + for(String id : new String[] {null, "", "9caption", "two words", "a".repeat(33), "caption\n", "/record/name", "caption"}) + { + assertEquals("ELEMENT_ID_NOT_DECLARABLE", assertThrows(IllegalStateException.class, () -> diagnostics.declare(id, "/elements/1")).getMessage(), String.valueOf(id)); + } + diagnostics.declare("order_id-2", "/elements/1"); + assertEquals("order_id-2", diagnostics.refuse("VALUE_MISSING", "order_id-2", null, null, null, null).diagnostic().element()); + } + + + + /*************************************************************************** + ** A pointer is made only of schema property names, array indexes and + ** declared ids. A name the schema does not declare cannot be a segment. + ***************************************************************************/ + @Test + void aPointerIsMadeOfDeclaredNamesAndIndexes() + { + Diagnostics diagnostics = new Diagnostics(false); + diagnostics.declare("caption", "/elements/1"); + for(String pointer : List.of("", "/schema", "/design/width", "/elements/0", "/elements/1/width", "/elements/3/children/0/children/12/thickness", "/overrides/0/when/dpi", "/overrides/1/set/caption/thickness", + "/elements/1/fit", "/elements/1/valuePath", "/elements/1/minModule", "/elements/1/asset", "/samples", "/defaults/size")) + { + assertEquals(pointer, diagnostics.refuse("SCENE_INVALID", null, pointer, null, null, null).diagnostic().pointer()); + } + for(String pointer : List.of("elements", "/elements/", "//elements", "/elements/01", "/elements/-1", "/elements/12345", "/Jane Example", "/record/name", "/overrides/1/set/code/thickness", + "/samples/typical", "/elements/1/customerName", "/elements/1/width/", "/elements/1/~0")) + { + assertEquals("DIAGNOSTIC_NOT_DECLARED", assertThrows(IllegalStateException.class, () -> diagnostics.refuse("SCENE_INVALID", null, pointer, null, null, null)).getMessage(), pointer); + } + } + + + + /*************************************************************************** + ** Codes are a closed vocabulary by severity, and numbers have short names. + ***************************************************************************/ + @Test + void codesAndNumberNamesAreClosed() + { + Diagnostics diagnostics = new Diagnostics(true); + for(String code : List.of("SCENE_INVALID", "STOCK_MISMATCH", "TARGET_REQUIRES_SCENE_2", "ELEMENT_OUTSIDE_PRINTABLE_AREA", "TEXT_DOES_NOT_FIT", "VALUE_MISSING", "VALUE_TOO_LONG", "FONT_UNKNOWN", + "FONT_MISSING_GLYPH", "RESERVED_FONT_NAME", "BARCODE_DOES_NOT_FIT", "GS1_INVALID", "ASSET_UNKNOWN", "PREVIEW_TOO_LARGE", "RENDERER_TOO_OLD")) + { + assertEquals(code, diagnostics.refuse(code, null, null, null, null, null).getMessage()); + assertThrows(IllegalStateException.class, () -> diagnostics.warn(code, null, null, null, null, null)); + assertThrows(IllegalStateException.class, () -> diagnostics.inform(code, null, null, null, null, null)); + } + for(String code : List.of("TEXT_TRUNCATED", "TEXT_SMALL_FOR_DPI", "TEXT_LIGHT_FACE", "FIT_NOT_PROVEN", "BARCODE_MODULE_BELOW_RECOMMENDED", "QUIET_ZONE_INTRUDED", "ELEMENTS_OVERLAP", "IMAGE_UPSCALED", + "DEVICE_UNIT_USED", "PRINTER_FONT_NOT_SHOWN")) + { + diagnostics.warn(code, null, null, null, null, null); + assertThrows(IllegalStateException.class, () -> diagnostics.refuse(code, null, null, null, null, null)); + assertThrows(IllegalStateException.class, () -> diagnostics.inform(code, null, null, null, null, null)); + } + for(String code : List.of("TEXT_SHRUNK", "TEXT_WRAPPED", "BARCODE_MODULE", "IMAGE_SCALED")) + { + diagnostics.inform(code, null, null, null, Map.of("dots", 2, "micrometres", 250), null); + assertThrows(IllegalStateException.class, () -> diagnostics.refuse(code, null, null, null, null, null)); + assertThrows(IllegalStateException.class, () -> diagnostics.warn(code, null, null, null, null, null)); + } + assertEquals(14, diagnostics.findings().size()); + assertFalse(diagnostics.refused()); + assertEquals(List.of(Diagnostic.Severity.WARNING, Diagnostic.Severity.INFO), diagnostics.findings().stream().map(Diagnostic::severity).distinct().toList()); + for(String code : new String[] {"RENDER_FAILED", "Customer Jane Example could not be printed", "", null}) + { + assertEquals("DIAGNOSTIC_OUTSIDE_VOCABULARY", assertThrows(IllegalStateException.class, () -> diagnostics.refuse(code, null, null, null, null, null)).getMessage()); + } + Map unnamed = new HashMap<>(); + unnamed.put("widthDots", null); + Map many = new HashMap<>(); + for(int index = 0; index < 9; index++) + { + many.put("value" + index, index); + } + for(Map numbers : List.of(Map.of("Width", 1), Map.of("width dots", 1), Map.of("the value Jane Example is too long", 1), Map.of("w".repeat(33), 1), unnamed, many)) + { + assertEquals("DIAGNOSTIC_OUTSIDE_VOCABULARY", assertThrows(IllegalStateException.class, () -> diagnostics.refuse("TEXT_DOES_NOT_FIT", null, null, null, numbers, null)).getMessage()); + assertEquals("DIAGNOSTIC_OUTSIDE_VOCABULARY", assertThrows(IllegalStateException.class, () -> diagnostics.refuse("TEXT_DOES_NOT_FIT", null, null, null, null, numbers)).getMessage()); + } + assertThrows(IllegalStateException.class, () -> diagnostics.refuse("ELEMENT_OUTSIDE_PRINTABLE_AREA", null, null, new Frame(0, 0, -1, 1), null, null)); + assertThrows(IllegalStateException.class, () -> diagnostics.refuse("ELEMENT_OUTSIDE_PRINTABLE_AREA", null, null, new Frame(0, 0, 1, -1), null, null)); + assertEquals(List.of(-12, -4, 0, 0), diagnostics.refuse("ELEMENT_OUTSIDE_PRINTABLE_AREA", null, null, new Frame(-12, -4, 0, 0), null, null).diagnostic().wire().get("box")); + } + + + + /*************************************************************************** + ** By default the first error ends the work. A collector keeps errors, in + ** order and once each, and remembers that the label must not print. + ***************************************************************************/ + @Test + void theFirstErrorEndsTheWorkUnlessCollecting() + { + Diagnostics strict = new Diagnostics(false); + Refusal refusal = strict.refuse("STOCK_MISMATCH", null, null, null, null, null); + assertSame(refusal, assertThrows(Refusal.class, () -> strict.keep(refusal))); + assertFalse(strict.refused()); + assertEquals(List.of(), strict.wire()); + + Diagnostics collecting = new Diagnostics(true); + collecting.warn("DEVICE_UNIT_USED", null, "/elements/0/thickness", null, null, null); + collecting.keep(refusal); + collecting.keep(collecting.refuse("STOCK_MISMATCH", null, null, null, null, null)); + collecting.warn("DEVICE_UNIT_USED", null, "/elements/0/thickness", null, null, null); + collecting.keep(collecting.refuse("SCENE_INVALID", null, "/design", null, null, null)); + assertTrue(collecting.refused()); + assertEquals(List.of(Map.of("code", "DEVICE_UNIT_USED", "severity", "WARNING", "pointer", "/elements/0/thickness"), Map.of("code", "STOCK_MISMATCH", "severity", "ERROR"), + Map.of("code", "SCENE_INVALID", "severity", "ERROR", "pointer", "/design")), collecting.wire()); + assertThrows(UnsupportedOperationException.class, () -> collecting.findings().clear()); + } +} diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/LayoutTest.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/LayoutTest.java new file mode 100644 index 0000000..f9c3d41 --- /dev/null +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/LayoutTest.java @@ -0,0 +1,214 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.List; +import java.util.SplittableRandom; +import com.kingsrook.qbits.renderer.scene2.Placement.Align; +import com.kingsrook.qbits.renderer.scene2.Placement.Anchor; +import com.kingsrook.qbits.renderer.scene2.Placement.Valign; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/******************************************************************************* + ** Anchor resolution for all nine anchors, shifts about the middle, omitted + ** sizes and content alignment, and the symmetry the design promises: mirrored + ** anchors give mirrored boxes, and a centred box is off by at most one dot. + *******************************************************************************/ +class LayoutTest +{ + private static final Frame CONTAINER = new Frame(10, 20, 200, 100); + private static final Layout LAYOUT = new Layout(CONTAINER, 203); + + + + /*************************************************************************** + ** A placement in dot units, so the expected numbers are plain to read. + ***************************************************************************/ + private static Placement placement(Anchor anchor, String x, String y, String width, String height) + { + return (new Placement(anchor, Length.parse(x), Length.parse(y), width == null ? null : Length.parse(width), height == null ? null : Length.parse(height), Align.LEFT, Valign.TOP)); + } + + + + /*************************************************************************** + ** The same point of the box and of the container coincide; `x` and `y` run + ** inward from an anchored edge. + ***************************************************************************/ + @Test + void eachOfTheNineAnchorsPlacesTheSamePointOfTheBox() + { + assertEquals(new Frame(15, 23, 40, 20), LAYOUT.place(placement(Anchor.TOP_LEFT, "5dot", "3dot", "40dot", "20dot"))); + assertEquals(new Frame(95, 23, 40, 20), LAYOUT.place(placement(Anchor.TOP_CENTER, "5dot", "3dot", "40dot", "20dot"))); + assertEquals(new Frame(165, 23, 40, 20), LAYOUT.place(placement(Anchor.TOP_RIGHT, "5dot", "3dot", "40dot", "20dot"))); + assertEquals(new Frame(15, 63, 40, 20), LAYOUT.place(placement(Anchor.MIDDLE_LEFT, "5dot", "3dot", "40dot", "20dot"))); + assertEquals(new Frame(95, 63, 40, 20), LAYOUT.place(placement(Anchor.CENTER, "5dot", "3dot", "40dot", "20dot"))); + assertEquals(new Frame(165, 63, 40, 20), LAYOUT.place(placement(Anchor.MIDDLE_RIGHT, "5dot", "3dot", "40dot", "20dot"))); + assertEquals(new Frame(15, 97, 40, 20), LAYOUT.place(placement(Anchor.BOTTOM_LEFT, "5dot", "3dot", "40dot", "20dot"))); + assertEquals(new Frame(95, 97, 40, 20), LAYOUT.place(placement(Anchor.BOTTOM_CENTER, "5dot", "3dot", "40dot", "20dot"))); + assertEquals(new Frame(165, 97, 40, 20), LAYOUT.place(placement(Anchor.BOTTOM_RIGHT, "5dot", "3dot", "40dot", "20dot"))); + assertEquals(CONTAINER, LAYOUT.container()); + assertEquals(203, LAYOUT.dpi()); + } + + + + /*************************************************************************** + ** About the middle a shift may be negative, and odd slack leaves the box one + ** dot nearer the start. + ***************************************************************************/ + @Test + void aCentredBoxShiftsEitherWayAndIsOffByAtMostOneDot() + { + assertEquals(new Frame(85, 57, 40, 20), LAYOUT.place(placement(Anchor.CENTER, "-5dot", "-3dot", "40dot", "20dot"))); + Layout odd = new Layout(new Frame(10, 20, 201, 101), 203); + assertEquals(new Frame(90, 60, 40, 20), odd.place(placement(Anchor.CENTER, "0dot", "0dot", "40dot", "20dot"))); + assertEquals(new Frame(88, 58, 43, 23), LAYOUT.place(placement(Anchor.CENTER, "0dot", "0dot", "43dot", "23dot"))); + } + + + + /*************************************************************************** + ** An omitted width or height is the rest of the container on that axis. + ***************************************************************************/ + @Test + void anOmittedSizeIsTheRestOfTheContainer() + { + assertEquals(new Frame(15, 23, 195, 97), LAYOUT.place(placement(Anchor.TOP_LEFT, "5dot", "3dot", null, null))); + assertEquals(new Frame(10, 20, 195, 97), LAYOUT.place(placement(Anchor.BOTTOM_RIGHT, "5dot", "3dot", null, null))); + assertEquals(new Frame(20, 26, 190, 94), LAYOUT.place(placement(Anchor.CENTER, "5dot", "3dot", null, null))); + assertEquals(new Frame(10, 20, 190, 94), LAYOUT.place(placement(Anchor.CENTER, "-5dot", "-3dot", null, null))); + assertEquals(CONTAINER, LAYOUT.place(placement(Anchor.CENTER, "0dot", "0dot", null, null))); + assertEquals(195, LAYOUT.width(placement(Anchor.TOP_LEFT, "5dot", "3dot", null, null))); + assertEquals(97, LAYOUT.height(placement(Anchor.TOP_LEFT, "5dot", "3dot", null, null))); + } + + + + /*************************************************************************** + ** Physical lengths resolve at the layout's resolution, positions to the + ** nearest dot and sizes down; a percentage is a share of the container, and + ** of a group's box for the group's children. + ***************************************************************************/ + @Test + void lengthsResolveAtTheTargetAndPercentagesReferToTheContainer() + { + Layout page = new Layout(new Frame(0, 0, 764, 155), 203); + assertEquals(new Frame(6, 4, 751, 87), page.place(placement(Anchor.TOP_CENTER, "0mm", "0.5mm", "94mm", "11mm"))); + Layout finer = new Layout(new Frame(0, 0, 1129, 229), 300); + assertEquals(new Frame(9, 6, 1110, 129), finer.place(placement(Anchor.TOP_CENTER, "0mm", "0.5mm", "94mm", "11mm"))); + Frame group = page.place(placement(Anchor.TOP_LEFT, "10%", "20%", "50%", "50%")); + assertEquals(new Frame(76, 31, 382, 77), group); + Layout inside = page.within(group); + assertEquals(203, inside.dpi()); + assertEquals(new Frame(76 + 191, 31 + 39, 191, 38), inside.place(placement(Anchor.BOTTOM_RIGHT, "0%", "0%", "50%", "50%"))); + assertEquals(4, page.x(placement(Anchor.TOP_LEFT, "0.5mm", "1mm", null, null))); + assertEquals(8, page.y(placement(Anchor.TOP_LEFT, "0.5mm", "1mm", null, null))); + } + + + + /*************************************************************************** + ** Content of a measured size sits in its box by `align` and `valign`. + ***************************************************************************/ + @Test + void contentSitsInItsBoxByAlignAndValign() + { + Frame box = new Frame(100, 50, 61, 31); + assertEquals(new Frame(100, 50, 20, 10), Layout.align(box, 20, 10, Align.LEFT, Valign.TOP)); + assertEquals(new Frame(120, 60, 20, 10), Layout.align(box, 20, 10, Align.CENTER, Valign.MIDDLE)); + assertEquals(new Frame(141, 71, 20, 10), Layout.align(box, 20, 10, Align.RIGHT, Valign.BOTTOM)); + assertEquals(box, Layout.align(box, 61, 31, Align.CENTER, Valign.MIDDLE)); + } + + + + /*************************************************************************** + ** The words a scene writes name exactly the nine anchors and the three + ** places on each axis. + ***************************************************************************/ + @Test + void sceneWordsNameTheAnchorsAndAlignments() + { + List anchors = List.of("top-left", "top-center", "top-right", "middle-left", "center", "middle-right", "bottom-left", "bottom-center", "bottom-right"); + assertEquals(List.of(Anchor.values()), anchors.stream().map(Anchor::of).toList()); + assertEquals(List.of(Align.values()), List.of("left", "center", "right").stream().map(Align::of).toList()); + assertEquals(List.of(Valign.values()), List.of("top", "middle", "bottom").stream().map(Valign::of).toList()); + assertNull(Anchor.of("centre")); + assertNull(Anchor.of(null)); + assertNull(Align.of("middle")); + assertNull(Valign.of("center")); + } + + + + /*************************************************************************** + ** Property, over seeded containers, sizes and offsets at every resolution: + ** mirroring the anchor mirrors the box. From an edge the mirror is exact; + ** about the middle, with the shift negated, it is exact when the slack is + ** even and one dot out when it is odd, never more. + ***************************************************************************/ + @Test + void mirroredAnchorsGiveMirroredBoxes() + { + SplittableRandom random = new SplittableRandom(20261112L); + for(int iteration = 0; iteration < 20_000; iteration++) + { + int dpi = Scene2.RESOLUTIONS.get(random.nextInt(3)); + Frame container = new Frame(random.nextInt(50), random.nextInt(50), 1 + random.nextInt(2400), 1 + random.nextInt(1200)); + Layout layout = new Layout(container, dpi); + String x = random.nextBoolean() ? random.nextInt(300) + "dot" : random.nextInt(60) + "." + random.nextInt(10) + "mm"; + String y = random.nextInt(20) + (random.nextBoolean() ? "%" : "mm"); + String width = (1 + random.nextInt(60)) + (random.nextBoolean() ? "mm" : "%"); + String height = (1 + random.nextInt(30)) + (random.nextBoolean() ? "mm" : "pt"); + + Frame left = layout.place(placement(Anchor.TOP_LEFT, x, y, width, height)); + Frame right = layout.place(placement(Anchor.TOP_RIGHT, x, y, width, height)); + assertEquals(left.width(), right.width()); + assertEquals(left.x() - container.x(), container.right() - right.right(), "left and right edges mirror exactly"); + assertEquals(left.y(), right.y()); + + Frame top = layout.place(placement(Anchor.TOP_LEFT, x, y, width, height)); + Frame bottom = layout.place(placement(Anchor.BOTTOM_LEFT, x, y, width, height)); + assertEquals(top.y() - container.y(), container.bottom() - bottom.bottom(), "top and bottom edges mirror exactly"); + + Frame shifted = layout.place(placement(Anchor.CENTER, x, y, width, height)); + Frame opposite = layout.place(placement(Anchor.CENTER, "-" + x, "-" + y, width, height)); + int horizontal = (shifted.x() - container.x()) - (container.right() - opposite.right()); + int vertical = (shifted.y() - container.y()) - (container.bottom() - opposite.bottom()); + assertEquals(-Math.floorMod(container.width() - shifted.width(), 2), horizontal, "centre mirror, horizontal"); + assertEquals(-Math.floorMod(container.height() - shifted.height(), 2), vertical, "centre mirror, vertical"); + assertTrue(Math.abs(horizontal) <= 1 && Math.abs(vertical) <= 1); + + Frame content = Layout.align(shifted, Math.max(0, shifted.width() - random.nextInt(9)), Math.max(0, shifted.height() - random.nextInt(9)), Align.CENTER, Valign.MIDDLE); + int before = content.x() - shifted.x(); + int after = shifted.right() - content.right(); + assertTrue(shifted.contains(content) && (after == before || after == before + 1), "centred content is off by at most one dot"); + assertEquals(Layout.align(shifted, content.width(), content.height(), Align.LEFT, Valign.TOP).x() - shifted.x(), + shifted.right() - Layout.align(shifted, content.width(), content.height(), Align.RIGHT, Valign.BOTTOM).right()); + } + } +} diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/LengthTest.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/LengthTest.java new file mode 100644 index 0000000..e0dd9f5 --- /dev/null +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/LengthTest.java @@ -0,0 +1,262 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.List; +import com.kingsrook.qbits.renderer.BarcodeFit; +import com.kingsrook.qbits.renderer.RenderInput; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/******************************************************************************* + ** Every unit and every rounding rule of the design's section 2.2, with the + ** numbers worked by hand. + *******************************************************************************/ +class LengthTest +{ + /*************************************************************************** + ** A length is a number of up to three decimals and one of five units. + ***************************************************************************/ + @Test + void aLengthIsANumberAndAUnit() + { + assertEquals(new Length(Length.Unit.MM, 12_500), Length.parse("12.5mm")); + assertEquals(new Length(Length.Unit.IN, 500), Length.parse("0.5in")); + assertEquals(new Length(Length.Unit.PT, 9000), Length.parse("9pt")); + assertEquals(new Length(Length.Unit.PERCENT, 25_000), Length.parse("25%")); + assertEquals(new Length(Length.Unit.DOT, 1000), Length.parse("1dot")); + assertEquals(new Length(Length.Unit.MM, -2000), Length.parse("-2mm")); + assertEquals(new Length(Length.Unit.MM, 1), Length.parse("0.001mm")); + assertEquals(new Length(Length.Unit.MM, 120), Length.parse("0.12mm")); + assertEquals(Length.ZERO, Length.parse("0mm")); + assertEquals(Length.ZERO, Length.parse("-0mm")); + assertTrue(Length.parse("1mm").physical() && Length.parse("1in").physical() && Length.parse("1pt").physical()); + assertFalse(Length.parse("1%").physical() || Length.parse("1dot").physical()); + } + + + + /*************************************************************************** + ** Nothing else is a length: no bare number, no ambient unit, no exponent, no + ** fourth decimal, no fraction of a dot, nothing past the container or the + ** largest stock. + ***************************************************************************/ + @Test + void anythingElseIsRefused() + { + for(String text : List.of("", "12", "mm", "1.2345mm", "1e3mm", " 1mm", "1mm ", "1 mm", "1MM", "01mm", ".5mm", "1.mm", "+1mm", "--1mm", "1px", "1cm", "1mm1mm", "0x10mm", + "1.5dot", "32001dot", "100.001%", "101%", "-101%", "2000.001mm", "-2000.001mm", "78.741in", "5669.293pt", "123456mm")) + { + assertEquals("INVALID_LENGTH", assertThrows(IllegalArgumentException.class, () -> Length.parse(text), text).getMessage(), text); + } + assertEquals("INVALID_LENGTH", assertThrows(IllegalArgumentException.class, () -> Length.parse(null)).getMessage()); + for(String text : List.of("2000mm", "-2000mm", "78.74in", "5669.291pt", "32000dot", "100%", "-100%")) + { + assertDoesNotThrow(() -> Length.parse(text), text); + } + } + + + + /*************************************************************************** + ** Each unit at its limit and just past it, and then as far past as the + ** syntax allows. The physical limit is two metres whatever the unit, and it + ** is applied before a length is narrowed: 84547 in is the first whole inch + ** whose micrometres do not fit an `int`, and it is the same named refusal as + ** 78.741 in, never an arithmetic failure. + ***************************************************************************/ + @Test + void everyUnitIsBoundedAtItsLimitAndFarPastIt() + { + assertEquals(2_000_000, Length.parse("2000mm").micrometres()); + assertEquals(-2_000_000, Length.parse("-2000mm").micrometres()); + assertEquals(1_999_996, Length.parse("78.74in").micrometres()); + assertEquals(-1_999_996, Length.parse("-78.74in").micrometres()); + assertEquals(2_000_000, Length.parse("5669.291pt").micrometres()); + assertEquals(2_000_000, Length.parse("5669.292pt").micrometres()); + assertEquals(-2_000_000, Length.parse("-5669.292pt").micrometres()); + assertEquals(32_000, Length.parse("32000dot").position(203, 0)); + assertEquals(100_000, Length.parse("100%").thousandths()); + for(String text : List.of("2000.001mm", "2001mm", "99999mm", "99999.999mm", "-99999.999mm", + "78.741in", "79in", "84546in", "84547in", "84600in", "99999in", "99999.999in", "-84600in", "-99999.999in", + "5669.293pt", "5670pt", "99999pt", "99999.999pt", "-99999.999pt", + "32001dot", "99999dot", "-32001dot", "100.001%", "99999.999%", "-99999.999%")) + { + assertEquals("INVALID_LENGTH", assertThrows(IllegalArgumentException.class, () -> Length.parse(text), text).getMessage(), text); + } + } + + + + /*************************************************************************** + ** A physical length is whole micrometres first: an inch is 25400 and a point + ** is a 72nd of an inch, to the nearest micrometre. + ***************************************************************************/ + @Test + void physicalLengthsBecomeWholeMicrometres() + { + assertEquals(12_500, Length.parse("12.5mm").micrometres()); + assertEquals(25_400, Length.parse("1in").micrometres()); + assertEquals(12_700, Length.parse("0.5in").micrometres()); + assertEquals(101_600, Length.parse("4in").micrometres()); + assertEquals(25, Length.parse("0.001in").micrometres()); + assertEquals(25_400, Length.parse("72pt").micrometres()); + assertEquals(3175, Length.parse("9pt").micrometres()); + assertEquals(353, Length.parse("1pt").micrometres()); + assertEquals(2469, Length.parse("7pt").micrometres()); + assertEquals(-353, Length.parse("-1pt").micrometres()); + assertEquals(-12_700, Length.parse("-0.5in").micrometres()); + assertEquals("LENGTH_NOT_PHYSICAL", assertThrows(IllegalArgumentException.class, () -> Length.parse("50%").micrometres()).getMessage()); + assertEquals("LENGTH_NOT_PHYSICAL", assertThrows(IllegalArgumentException.class, () -> Length.parse("5dot").micrometres()).getMessage()); + } + + + + /*************************************************************************** + ** A position goes to the nearest dot, exactly as the stock already converts + ** micrometres, and a negative position is the mirror of the positive one. + ***************************************************************************/ + @Test + void aPositionIsTheNearestDotAsTheStockRounds() + { + assertEquals(4, Length.parse("0.5mm").position(203, 0)); + assertEquals(-4, Length.parse("-0.5mm").position(203, 0)); + assertEquals(751, Length.parse("94mm").position(203, 0)); + assertEquals(1110, Length.parse("94mm").position(300, 0)); + assertEquals(2220, Length.parse("94mm").position(600, 0)); + assertEquals(5, Length.parse("5dot").position(203, 0)); + assertEquals(5, Length.parse("5dot").position(600, 0)); + assertEquals(382, Length.parse("50%").position(203, 764)); + assertEquals(39, Length.parse("25%").position(203, 155)); + assertEquals(-51, Length.parse("-50%").position(203, 101)); + for(int dpi : Scene2.RESOLUTIONS) + { + RenderInput.Stock stock = new RenderInput.Stock(101_600, 25_400, 0, 0, 0, 0, dpi); + for(int micrometres = 0; micrometres <= 120_000; micrometres += 37) + { + Length length = new Length(Length.Unit.MM, micrometres); + assertEquals(stock.dots(micrometres), length.position(dpi, 0)); + assertEquals(-stock.dots(micrometres), new Length(Length.Unit.MM, -micrometres).position(dpi, 0)); + } + } + } + + + + /*************************************************************************** + ** A box width or height is rounded down, exactly as the version 1 fit box + ** rounds inward, so content never leaves the designed box. + ***************************************************************************/ + @Test + void aBoxSizeIsRoundedDownAsTheFitBoxRounds() + { + assertEquals(751, Length.parse("94mm").size(203, 0)); + assertEquals(87, Length.parse("11mm").size(203, 0)); + assertEquals(129, Length.parse("11mm").size(300, 0)); + assertEquals(259, Length.parse("11mm").size(600, 0)); + assertEquals(3, Length.parse("0.5mm").size(203, 0)); + assertEquals(38, Length.parse("25%").size(203, 155)); + assertEquals(764, Length.parse("100%").size(203, 764)); + assertEquals(7, Length.parse("7dot").size(300, 0)); + for(int dpi : Scene2.RESOLUTIONS) + { + for(int micrometres = 0; micrometres <= 120_000; micrometres += 41) + { + assertEquals(BarcodeFit.dotsInside(micrometres, dpi), new Length(Length.Unit.MM, micrometres).size(dpi, 0)); + } + } + } + + + + /*************************************************************************** + ** A thickness is the nearest dot and never less than one. + ***************************************************************************/ + @Test + void aThicknessIsTheNearestDotAndNeverVanishes() + { + assertEquals(1, Length.parse("0.1mm").thickness(203)); + assertEquals(1, Length.parse("0.01mm").thickness(203)); + assertEquals(3, Length.parse("1pt").thickness(203)); + assertEquals(4, Length.parse("1pt").thickness(300)); + assertEquals(8, Length.parse("1pt").thickness(600)); + assertEquals(1, Length.parse("1dot").thickness(600)); + assertEquals(3, Length.parse("3dot").thickness(203)); + assertEquals("LENGTH_NOT_ABSOLUTE", assertThrows(IllegalArgumentException.class, () -> Length.parse("5%").thickness(203)).getMessage()); + } + + + + /*************************************************************************** + ** A barcode module goes up to a whole dot from its stated minimum: the + ** design's defaults are 2, 3 and 6 dots for 0.25 mm and 3, 5 and 9 dots for + ** 0.375 mm at the three resolutions. + ***************************************************************************/ + @Test + void aModuleIsRoundedUpFromItsMinimum() + { + assertEquals(List.of(2, 3, 6), Scene2.RESOLUTIONS.stream().map(dpi -> Length.parse("0.25mm").module(dpi)).toList()); + assertEquals(List.of(3, 5, 9), Scene2.RESOLUTIONS.stream().map(dpi -> Length.parse("0.375mm").module(dpi)).toList()); + assertEquals(1, Length.parse("1dot").module(203)); + assertEquals(1, Length.parse("0.001mm").module(203)); + assertEquals("LENGTH_NOT_ABSOLUTE", assertThrows(IllegalArgumentException.class, () -> Length.parse("5%").module(203)).getMessage()); + } + + + + /*************************************************************************** + ** A font size is not rounded: points times dpi over 72. + ***************************************************************************/ + @Test + void aFontSizeScalesContinuously() + { + assertEquals(25.375, Length.parse("9pt").scale(203), 1e-9); + assertEquals(3000 * 300 / 25_400.0, Length.parse("3mm").scale(300), 1e-9); + assertEquals(300.0, Length.parse("0.5in").scale(600), 1e-9); + assertEquals(12.0, Length.parse("12dot").scale(203), 1e-9); + assertEquals("LENGTH_NOT_ABSOLUTE", assertThrows(IllegalArgumentException.class, () -> Length.parse("5%").scale(203)).getMessage()); + } + + + + /*************************************************************************** + ** The three roundings on one value, 2.5 dots: nearest goes away from zero, + ** down toward it and up away from it, with the sign restored after. + ***************************************************************************/ + @Test + void roundingIsOfTheMagnitude() + { + Length half = new Length(Length.Unit.PERCENT, 50_000); + assertEquals(3, half.dots(203, 5, Length.Rounding.NEAREST)); + assertEquals(2, half.dots(203, 5, Length.Rounding.DOWN)); + assertEquals(3, half.dots(203, 5, Length.Rounding.UP)); + Length negative = new Length(Length.Unit.PERCENT, -50_000); + assertEquals(-3, negative.dots(203, 5, Length.Rounding.NEAREST)); + assertEquals(-2, negative.dots(203, 5, Length.Rounding.DOWN)); + assertEquals(-3, negative.dots(203, 5, Length.Rounding.UP)); + } +} diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/Scene2Test.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/Scene2Test.java new file mode 100644 index 0000000..96363dc --- /dev/null +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/Scene2Test.java @@ -0,0 +1,946 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.SplittableRandom; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.kingsrook.qbits.renderer.RendererMain; +import com.kingsrook.qbits.renderer.Wire; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/******************************************************************************* + ** Version 2 scenes through the worker's entry point, on the example stock of + ** the design (4 x 1 in, 3 mm margins: 764 x 155 printable dots at 203 dpi). + ** Every refusal is asserted as the whole diagnostic the wire carries: code, + ** element id, pointer, box and numbers. + *******************************************************************************/ +class Scene2Test +{ + private static final String BOX = "{\"id\":\"a\",\"type\":\"box\",\"width\":\"10mm\",\"height\":\"5mm\",\"thickness\":\"1pt\"}"; + + + + /*************************************************************************** + ** Validate a scene on the example stock at 203 dpi and return its refusal. + ***************************************************************************/ + private static Map refused(String source) + { + return (SceneFixture.refusal(() -> RendererMain.process(SceneFixture.request("VALIDATE", source)))); + } + + + + /*************************************************************************** + ** Validate a scene holding these elements and return its refusal. + ***************************************************************************/ + private static Map refusedElements(String elements) + { + return (refused(SceneFixture.scene(elements))); + } + + + + /*************************************************************************** + ** The diagnostic of a place in the scene that is not what the schema says. + ***************************************************************************/ + private static Map invalid(String pointer) + { + return (Map.of("code", "SCENE_INVALID", "severity", "ERROR", "pointer", pointer)); + } + + + + /*************************************************************************** + ** The same, inside a declared element. + ***************************************************************************/ + private static Map invalid(String element, String pointer) + { + return (Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", element, "pointer", pointer)); + } + + + + /*************************************************************************** + ** The diagnostic of an object with properties the schema does not declare. + ***************************************************************************/ + private static Map unknown(String pointer, int count) + { + return (Map.of("code", "SCENE_INVALID", "severity", "ERROR", "pointer", pointer, "measured", Map.of("unknownProperties", count), "limit", Map.of("unknownProperties", 0))); + } + + + + /*************************************************************************** + ** The diagnostics of a request that is accepted. + ***************************************************************************/ + private static Object accepted(JsonNode request) throws Exception + { + Map answer = RendererMain.process(request); + assertEquals("OK", answer.get("outcome")); + return (answer.get("diagnostics")); + } + + + + /*************************************************************************** + ** The ink of a page: left, top, right and bottom, the last two exclusive. + ***************************************************************************/ + private static List ink(boolean[][] page) + { + int left = Integer.MAX_VALUE; + int top = Integer.MAX_VALUE; + int right = -1; + int bottom = -1; + for(int y = 0; y < page.length; y++) + { + for(int x = 0; x < page[y].length; x++) + { + if(page[y][x]) + { + left = Math.min(left, x); + top = Math.min(top, y); + right = Math.max(right, x + 1); + bottom = Math.max(bottom, y + 1); + } + } + } + return (List.of(left, top, right, bottom)); + } + + + + /*************************************************************************** + ** A scene with boxes, a group, defaults, samples and an override validates + ** with no findings, and an empty scene is a blank label. + ***************************************************************************/ + @Test + void aSceneOfBoxesAndGroupsIsAccepted() throws Exception + { + String source = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"defaults\":{\"font\":\"qqq-sans-bold\",\"size\":\"9pt\"}," + + "\"samples\":{\"typical\":{\"record\":{\"name\":\"TEST-0001\"}},\"longest\":{}}," + + "\"elements\":[{\"id\":\"frame\",\"type\":\"box\",\"thickness\":\"0.5pt\"}," + + "{\"id\":\"panel\",\"type\":\"group\",\"anchor\":\"center\",\"x\":\"-2mm\",\"width\":\"50%\",\"height\":\"50%\",\"align\":\"center\",\"valign\":\"middle\",\"children\":[" + + "{\"id\":\"mark\",\"type\":\"box\",\"anchor\":\"bottom-right\",\"width\":\"25%\",\"height\":\"25%\",\"fill\":true}]}]," + + "\"overrides\":[{\"when\":{\"dpi\":[203,600]},\"set\":{\"frame\":{\"thickness\":\"1dot\"}}}]}"; + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", source))); + assertEquals(List.of(), accepted(SceneFixture.request("RENDER", source))); + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", SceneFixture.scene("")))); + assertEquals("^XA^PW812^LL203^LH0,0^CI28\n^XZ\n", new String(SceneFixture.render(SceneFixture.request("RENDER", SceneFixture.scene(""))), java.nio.charset.StandardCharsets.US_ASCII)); + } + + + + /*************************************************************************** + ** A box prints where its anchor and lengths put it, inside the margins: the + ** printable area starts 24 dots in at 203 dpi. + ***************************************************************************/ + @Test + void aBoxPrintsWhereItsAnchorPutsIt() throws Exception + { + boolean[][] page = SceneFixture.raster(SceneFixture.render(SceneFixture.request("RENDER", SceneFixture.scene("{\"id\":\"a\",\"type\":\"box\",\"anchor\":\"bottom-right\",\"x\":\"2mm\",\"y\":\"1mm\"," + + "\"width\":\"10mm\",\"height\":\"5mm\",\"thickness\":\"1pt\"}")))); + assertEquals(203, page.length); + assertEquals(812, page[0].length); + //////////////////////////////////////////////////////////////////////////////////////////////// + // 10 mm is 79 dots and 5 mm is 39 dots (down); 2 mm is 16 dots and 1 mm is 8 dots (nearest) // + // right edge 24 + 764 - 16 = 772, bottom edge 24 + 155 - 8 = 171; 1 pt is 3 dots // + //////////////////////////////////////////////////////////////////////////////////////////////// + assertEquals(List.of(693, 132, 772, 171), ink(page)); + assertTrue(page[132][693] && page[134][700] && page[170][771] && page[150][695] && page[150][769]); + assertFalse(page[135][700] || page[150][696] || page[150][768] || page[167][700]); + } + + + + /*************************************************************************** + ** One design, three resolutions: the same scene at each render target, with + ** the page and the ink scaled and nothing in the scene changed. + ***************************************************************************/ + @Test + void oneDesignRendersAtEveryTarget() throws Exception + { + String source = SceneFixture.scene("{\"id\":\"a\",\"type\":\"box\",\"x\":\"10mm\",\"y\":\"5mm\",\"width\":\"20mm\",\"height\":\"10mm\",\"fill\":true}"); + List> expected = List.of(List.of(812, 203, 104, 64, 263, 143), List.of(1200, 300, 154, 95, 390, 213), List.of(2400, 600, 307, 189, 779, 425)); + for(int index = 0; index < 3; index++) + { + ObjectNode request = SceneFixture.request("RENDER", source).put("targetDpi", Scene2.RESOLUTIONS.get(index)); + boolean[][] page = SceneFixture.raster(SceneFixture.render(request)); + List actual = new ArrayList<>(List.of(page[0].length, page.length)); + actual.addAll(ink(page)); + assertEquals(expected.get(index), actual, "target " + Scene2.RESOLUTIONS.get(index)); + } + ObjectNode native300 = SceneFixture.request("RENDER", SceneFixture.stock(SceneFixture.WIDTH, SceneFixture.HEIGHT, SceneFixture.MARGIN, 300), source); + assertEquals(Wire.sha256(SceneFixture.render(SceneFixture.request("RENDER", source).put("targetDpi", 300))), Wire.sha256(SceneFixture.render(native300))); + } + + + + /*************************************************************************** + ** The top level: an object with the declared properties and nothing else. + ***************************************************************************/ + @Test + void theTopLevelIsHeldToTheSchema() throws Exception + { + Diagnostics diagnostics = new Diagnostics(false); + assertEquals(invalid(""), SceneFixture.refusal(() -> Scene2.validate(SceneFixture.input(SceneFixture.request("VALIDATE", "[]")), Wire.JSON.readTree("[]"), diagnostics))); + assertEquals(invalid("/schema"), SceneFixture.refusal(() -> Scene2.validate(SceneFixture.input(SceneFixture.request("VALIDATE", "{}")), Wire.JSON.readTree("{\"schema\":\"qqq-zpl-scene/3\"}"), diagnostics))); + assertEquals(unknown("", 2), refused("{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"elements\":[],\"title\":\"TEST-0001\",\"units\":\"mm\"}")); + assertEquals(invalid("/design"), refused("{\"schema\":\"qqq-zpl-scene/2\",\"elements\":[]}")); + assertEquals(invalid("/design"), refused("{\"schema\":\"qqq-zpl-scene/2\",\"design\":\"4in\",\"elements\":[]}")); + assertEquals(unknown("/design", 1), refused("{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\",\"stock\":\"atLeast\"},\"elements\":[]}")); + assertEquals(invalid("/design/width"), refused("{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"height\":\"1in\"},\"elements\":[]}")); + for(String width : List.of("\"50%\"", "\"812dot\"", "\"0in\"", "\"-4in\"", "\"4\"", "4", "null", "[\"4in\"]")) + { + assertEquals(invalid("/design/width"), refused("{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":" + width + ",\"height\":\"1in\"},\"elements\":[]}"), width); + } + assertEquals(invalid("/design/height"), refused("{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"0mm\"},\"elements\":[]}")); + assertEquals(invalid("/elements"), refused("{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"}}")); + assertEquals(invalid("/elements"), refused("{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"elements\":{}}")); + } + + + + /*************************************************************************** + ** Defaults and samples are accepted in their declared shape only. Samples + ** are data for a preview and are ignored by rendering. + ***************************************************************************/ + @Test + void defaultsAndSamplesAreHeldToTheirShape() throws Exception + { + String head = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"elements\":[],"; + for(String defaults : List.of("{}", "{\"font\":\"qqq-mono\"}", "{\"size\":\"3mm\"}", "{\"font\":\"Brand_Sans.Bold-2\",\"size\":\"0.125in\"}")) + { + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", head + "\"defaults\":" + defaults + ",\"samples\":{}}")), defaults); + } + assertEquals(invalid("/defaults"), refused(head + "\"defaults\":[]}")); + assertEquals(unknown("/defaults", 1), refused(head + "\"defaults\":{\"colour\":\"black\"}}")); + assertEquals(invalid("/defaults/font"), refused(head + "\"defaults\":{\"font\":12}}")); + assertEquals(invalid("/defaults/font"), refused(head + "\"defaults\":{\"font\":\"Jane Example\"}}")); + assertEquals(invalid("/defaults/size"), refused(head + "\"defaults\":{\"size\":\"9dot\"}}")); + assertEquals(invalid("/defaults/size"), refused(head + "\"defaults\":{\"size\":\"0pt\"}}")); + assertEquals(invalid("/samples"), refused(head + "\"samples\":[]}")); + assertEquals(invalid("/samples"), refused(head + "\"samples\":{\"typical\":\"TEST-0001\"}}")); + StringBuilder samples = new StringBuilder(); + for(int index = 0; index < 17; index++) + { + samples.append(index == 0 ? "" : ",").append("\"s").append(index).append("\":{}"); + } + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "pointer", "/samples", "measured", Map.of("samples", 17), "limit", Map.of("samples", 16)), refused(head + "\"samples\":{" + samples + "}}")); + } + + + + /*************************************************************************** + ** Every element declares a unique id of the fixed pattern and a known type, + ** before anything else about it is read. + ***************************************************************************/ + @Test + void everyElementDeclaresAnIdAndAType() + { + assertEquals(invalid("/elements/0"), refusedElements("\"box\"")); + assertEquals(invalid("/elements/0/id"), refusedElements("{\"type\":\"box\",\"thickness\":\"1pt\"}")); + for(String id : List.of("\"\"", "\"9a\"", "\"two words\"", "\"" + "a".repeat(33) + "\"", "12", "null", "\"Jane Example, 1 Test Street\"")) + { + assertEquals(invalid("/elements/0/id"), refusedElements("{\"id\":" + id + ",\"type\":\"box\",\"thickness\":\"1pt\"}"), id); + } + assertEquals(invalid("/elements/1/id"), refusedElements(BOX + "," + BOX)); + assertEquals(invalid("/elements/1/children/0/id"), refusedElements(BOX + ",{\"id\":\"g\",\"type\":\"group\",\"children\":[" + BOX + "]}")); + assertEquals(invalid("a", "/elements/0/type"), refusedElements("{\"id\":\"a\"}")); + assertEquals(invalid("a", "/elements/0/type"), refusedElements("{\"id\":\"a\",\"type\":\"circle\"}")); + assertEquals(invalid("a", "/elements/0/type"), refusedElements("{\"id\":\"a\",\"type\":7}")); + assertEquals(invalid("/elements/0/children"), refusedElements("{\"id\":\"g\",\"type\":\"group\"}")); + assertEquals(invalid("/elements/0/children"), refusedElements("{\"id\":\"g\",\"type\":\"group\",\"children\":{}}")); + } + + + + /*************************************************************************** + ** The limits: 1000 elements in all, and groups four deep. + ***************************************************************************/ + @Test + void elementsAndDepthAreLimited() throws Exception + { + StringBuilder many = new StringBuilder(); + for(int index = 0; index < 999; index++) + { + many.append("{\"id\":\"b").append(index).append("\",\"type\":\"box\",\"width\":\"2mm\",\"height\":\"2mm\",\"fill\":true},"); + } + String group = "{\"id\":\"g\",\"type\":\"group\",\"children\":[%s]}"; + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", SceneFixture.scene(many + String.format(group, ""))))); + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "pointer", "/elements", "measured", Map.of("elements", 1001), "limit", Map.of("elements", 1000)), + refusedElements(many + String.format(group, "{\"id\":\"last\",\"type\":\"box\",\"fill\":true}"))); + + String nested = "{\"id\":\"g1\",\"type\":\"group\",\"children\":[{\"id\":\"g2\",\"type\":\"group\",\"children\":[{\"id\":\"g3\",\"type\":\"group\",\"children\":[{\"id\":\"g4\",\"type\":\"group\",\"children\":[%s]}]}]}]}"; + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", SceneFixture.scene(String.format(nested, BOX))))); + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "g5", "pointer", "/elements/0/children/0/children/0/children/0/children/0", "measured", Map.of("depth", 5), "limit", Map.of("depth", 4)), + refusedElements(String.format(nested, "{\"id\":\"g5\",\"type\":\"group\",\"children\":[]}"))); + } + + + + /*************************************************************************** + ** The common placement properties: a word of the fixed set, a length in a + ** known unit, a negative offset only about the middle, a size above nothing. + ***************************************************************************/ + @Test + void placementPropertiesAreHeldToTheSchema() throws Exception + { + String box = "{\"id\":\"a\",\"type\":\"box\",\"thickness\":\"1pt\",\"width\":\"10mm\",\"height\":\"5mm\",%s}"; + for(String anchor : List.of("\"centre\"", "\"TOP-LEFT\"", "5", "null")) + { + assertEquals(invalid("a", "/elements/0/anchor"), refusedElements(String.format(box, "\"anchor\":" + anchor)), anchor); + } + for(String x : List.of("\"5\"", "5", "\"5px\"", "\"-1mm\"", "\"-1%\"", "\"1.2345mm\"", "true")) + { + assertEquals(invalid("a", "/elements/0/x"), refusedElements(String.format(box, "\"x\":" + x)), x); + assertEquals(invalid("a", "/elements/0/y"), refusedElements(String.format(box, "\"anchor\":\"top-center\",\"y\":" + x)), x); + } + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", SceneFixture.scene(String.format(box, "\"anchor\":\"center\",\"x\":\"-1mm\",\"y\":\"-10%\""))))); + assertEquals(invalid("a", "/elements/0/x"), refusedElements(String.format(box, "\"anchor\":\"middle-right\",\"x\":\"-1mm\",\"y\":\"-1mm\""))); + assertEquals(invalid("a", "/elements/0/y"), refusedElements(String.format(box, "\"anchor\":\"bottom-center\",\"x\":\"-1mm\",\"y\":\"-1mm\""))); + for(String size : List.of("\"0mm\"", "\"-1mm\"", "12", "\"12\"", "\"0%\"")) + { + assertEquals(invalid("a", "/elements/0/width"), refusedElements("{\"id\":\"a\",\"type\":\"box\",\"thickness\":\"1pt\",\"width\":" + size + "}"), size); + assertEquals(invalid("a", "/elements/0/height"), refusedElements("{\"id\":\"a\",\"type\":\"box\",\"thickness\":\"1pt\",\"height\":" + size + "}"), size); + } + assertEquals(invalid("a", "/elements/0/align"), refusedElements(String.format(box, "\"align\":\"middle\""))); + assertEquals(invalid("a", "/elements/0/valign"), refusedElements(String.format(box, "\"valign\":\"center\""))); + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", SceneFixture.scene(String.format(box, "\"align\":\"right\",\"valign\":\"bottom\""))))); + } + + + + /*************************************************************************** + ** A property the element's type does not read is counted and refused, and a + ** box needs a thickness above nothing unless it is filled. + ***************************************************************************/ + @Test + void aBoxReadsItsThicknessAndFillAndNothingElse() throws Exception + { + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "a", "pointer", "/elements/0", "measured", Map.of("unknownProperties", 2), "limit", Map.of("unknownProperties", 0)), + refusedElements("{\"id\":\"a\",\"type\":\"box\",\"thickness\":\"1pt\",\"colour\":\"red\",\"rotation\":90}")); + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "g", "pointer", "/elements/0", "measured", Map.of("unknownProperties", 1), "limit", Map.of("unknownProperties", 0)), + refusedElements("{\"id\":\"g\",\"type\":\"group\",\"children\":[],\"thickness\":\"1pt\"}")); + assertEquals(invalid("a", "/elements/0/thickness"), refusedElements("{\"id\":\"a\",\"type\":\"box\"}")); + assertEquals(invalid("a", "/elements/0/thickness"), refusedElements("{\"id\":\"a\",\"type\":\"box\",\"fill\":false}")); + for(String thickness : List.of("\"5%\"", "\"0pt\"", "\"-1pt\"", "1", "\"thin\"")) + { + assertEquals(invalid("a", "/elements/0/thickness"), refusedElements("{\"id\":\"a\",\"type\":\"box\",\"thickness\":" + thickness + "}"), thickness); + } + assertEquals(invalid("a", "/elements/0/fill"), refusedElements("{\"id\":\"a\",\"type\":\"box\",\"fill\":\"yes\"}")); + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", SceneFixture.scene("{\"id\":\"a\",\"type\":\"box\",\"fill\":true},{\"id\":\"b\",\"type\":\"box\",\"fill\":true,\"thickness\":\"1pt\"}")))); + } + + + + /*************************************************************************** + ** An override names resolutions and declared elements, cannot change what an + ** element is, and its values are held to the schema for every resolution, + ** not only the one being rendered. + ***************************************************************************/ + @Test + void overridesAreHeldToTheSchemaForEveryResolution() throws Exception + { + String head = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"elements\":[" + BOX + "],\"overrides\":"; + assertEquals(invalid("/overrides"), refused(head + "{}}")); + assertEquals(invalid("/overrides/0"), refused(head + "[7]}")); + assertEquals(unknown("/overrides/0", 1), refused(head + "[{\"when\":{\"dpi\":[203]},\"set\":{\"a\":{\"fill\":true}},\"note\":\"x\"}]}")); + assertEquals(invalid("/overrides/0/when"), refused(head + "[{\"set\":{\"a\":{\"fill\":true}}}]}")); + assertEquals(unknown("/overrides/0/when", 1), refused(head + "[{\"when\":{\"dpi\":[203],\"model\":\"ZD421\"},\"set\":{\"a\":{\"fill\":true}}}]}")); + for(String dpi : List.of("203", "[]", "[72]", "[203,203]", "[\"203\"]", "[203.5]", "[4294967499]", "[203,300,600,203]")) + { + assertEquals(invalid("/overrides/0/when/dpi"), refused(head + "[{\"when\":{\"dpi\":" + dpi + "},\"set\":{\"a\":{\"fill\":true}}}]}"), dpi); + } + assertEquals(invalid("/overrides/0/when/dpi"), refused(head + "[{\"when\":{},\"set\":{\"a\":{\"fill\":true}}}]}")); + assertEquals(invalid("/overrides/0/set"), refused(head + "[{\"when\":{\"dpi\":[203]}}]}")); + assertEquals(invalid("/overrides/0/set"), refused(head + "[{\"when\":{\"dpi\":[203]},\"set\":{}}]}")); + assertEquals(invalid("/overrides/0/set"), refused(head + "[{\"when\":{\"dpi\":[203]},\"set\":{\"TEST-0001 Jane Example\":{\"fill\":true}}}]}")); + for(String replacement : List.of("true", "{}", "{\"id\":\"b\"}", "{\"type\":\"group\"}", "{\"children\":[]}")) + { + assertEquals(invalid("a", "/overrides/1/set/a"), refused(head + "[{\"when\":{\"dpi\":[300]},\"set\":{\"a\":{\"fill\":true}}},{\"when\":{\"dpi\":[203]},\"set\":{\"a\":" + replacement + "}}]}"), replacement); + } + StringBuilder many = new StringBuilder(); + for(int index = 0; index < 33; index++) + { + many.append(index == 0 ? "" : ",").append("{\"when\":{\"dpi\":[203]},\"set\":{\"a\":{\"fill\":true}}}"); + } + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "pointer", "/overrides", "measured", Map.of("overrides", 33), "limit", Map.of("overrides", 32)), refused(head + "[" + many + "]}")); + + /////////////////////////////////////////////////////////////////////////////////////////////// + // the request renders at 203 dpi; the mistake is in what a 600 dpi printer would be given // + /////////////////////////////////////////////////////////////////////////////////////////////// + assertEquals(invalid("a", "/overrides/0/set/a/thickness"), refused(head + "[{\"when\":{\"dpi\":[600]},\"set\":{\"a\":{\"thickness\":\"thin\"}}}]}")); + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "a", "pointer", "/elements/0", "measured", Map.of("unknownProperties", 1), "limit", Map.of("unknownProperties", 0)), + refused(head + "[{\"when\":{\"dpi\":[300]},\"set\":{\"a\":{\"colour\":\"red\"}}}]}")); + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", head + "[{\"when\":{\"dpi\":[203]},\"set\":{\"a\":{\"thickness\":\"2dot\"}}},{\"when\":{\"dpi\":[300,600]},\"set\":{\"a\":{\"fill\":true}}}]}"))); + } + + + + /*************************************************************************** + ** An override changes the element at its resolutions only, later entries + ** winning: a 3 dot outline at 203 dpi, a filled box at 600 dpi. + ***************************************************************************/ + @Test + void anOverrideAppliesAtItsResolutionsOnly() throws Exception + { + String source = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"elements\":[{\"id\":\"a\",\"type\":\"box\",\"width\":\"10mm\",\"height\":\"5mm\",\"thickness\":\"1dot\"}]," + + "\"overrides\":[{\"when\":{\"dpi\":[203,600]},\"set\":{\"a\":{\"thickness\":\"9dot\"}}},{\"when\":{\"dpi\":[203]},\"set\":{\"a\":{\"thickness\":\"3dot\"}}},{\"when\":{\"dpi\":[600]},\"set\":{\"a\":{\"fill\":true}}}]}"; + boolean[][] coarse = SceneFixture.raster(SceneFixture.render(SceneFixture.request("RENDER", source))); + assertTrue(coarse[24 + 2][24 + 40] && !coarse[24 + 3][24 + 40] && coarse[24 + 20][24 + 2] && !coarse[24 + 20][24 + 3]); + boolean[][] middle = SceneFixture.raster(SceneFixture.render(SceneFixture.request("RENDER", source).put("targetDpi", 300))); + assertTrue(middle[36][36 + 60] && !middle[37][36 + 60]); + boolean[][] fine = SceneFixture.raster(SceneFixture.render(SceneFixture.request("RENDER", source).put("targetDpi", 600))); + assertTrue(fine[71 + 59][71 + 118] && fine[71][71] && fine[71 + 117][71 + 235] && !fine[71 + 118][71 + 235] && !fine[71 + 117][71 + 236]); + } + + + + /*************************************************************************** + ** A `dot` length outside an override depends on the printer, so the author + ** is warned once for each place; inside an override it is the intended use. + ***************************************************************************/ + @Test + void aDeviceUnitOutsideAnOverrideIsWarnedAbout() throws Exception + { + String source = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"elements\":[{\"id\":\"a\",\"type\":\"box\",\"x\":\"5dot\",\"width\":\"10mm\",\"height\":\"5mm\",\"thickness\":\"1dot\"}," + + "{\"id\":\"b\",\"type\":\"box\",\"y\":\"8mm\",\"width\":\"10mm\",\"height\":\"5mm\",\"thickness\":\"1pt\"}],\"overrides\":[{\"when\":{\"dpi\":[203]},\"set\":{\"b\":{\"thickness\":\"1dot\"}}}]}"; + List> expected = List.of(Map.of("code", "DEVICE_UNIT_USED", "severity", "WARNING", "element", "a", "pointer", "/elements/0/x"), + Map.of("code", "DEVICE_UNIT_USED", "severity", "WARNING", "element", "a", "pointer", "/elements/0/thickness")); + assertEquals(expected, accepted(SceneFixture.request("VALIDATE", source))); + assertEquals(expected, accepted(SceneFixture.request("RENDER", source))); + } + + + + /*************************************************************************** + ** The stock must be the one the design was drawn for, within half a + ** millimetre on each side; the refusal reports both sizes. + ***************************************************************************/ + @Test + void theStockMustMatchTheDesign() throws Exception + { + String scene = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"%s\",\"height\":\"%s\"},\"elements\":[]}"; + assertEquals(Map.of("code", "STOCK_MISMATCH", "severity", "ERROR", "pointer", "/design", "measured", Map.of("widthMicrometres", 101_600, "heightMicrometres", 25_400), + "limit", Map.of("widthMicrometres", 101_600, "heightMicrometres", 50_800, "toleranceMicrometres", 500)), refused(String.format(scene, "4in", "2in"))); + assertEquals(Map.of("code", "STOCK_MISMATCH", "severity", "ERROR", "pointer", "/design", "measured", Map.of("widthMicrometres", 101_600, "heightMicrometres", 25_400), + "limit", Map.of("widthMicrometres", 102_101, "heightMicrometres", 25_400, "toleranceMicrometres", 500)), refused(String.format(scene, "102.101mm", "25.4mm"))); + for(List design : List.of(List.of("101.5mm", "25.4mm"), List.of("102.1mm", "24.9mm"), List.of("101.1mm", "25.9mm"), List.of("288pt", "72pt"))) + { + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", String.format(scene, design.get(0), design.get(1)))), design.toString()); + } + } + + + + /*************************************************************************** + ** A box that leaves the printable area is refused with its box and the four + ** edges against the area's, and one that resolves to no dots at this + ** resolution is refused with its size rather than vanishing. + ***************************************************************************/ + @Test + void aBoxMustLieInsideThePrintableAreaAndHaveSize() throws Exception + { + assertEquals(Map.of("code", "ELEMENT_OUTSIDE_PRINTABLE_AREA", "severity", "ERROR", "element", "wide", "box", List.of(719, 0, 79, 39), + "measured", Map.of("leftDots", 719, "topDots", 0, "rightDots", 798, "bottomDots", 39), "limit", Map.of("leftDots", 0, "topDots", 0, "rightDots", 764, "bottomDots", 155)), + refusedElements("{\"id\":\"wide\",\"type\":\"box\",\"x\":\"90mm\",\"width\":\"10mm\",\"height\":\"5mm\",\"thickness\":\"1pt\"}")); + assertEquals(Map.of("code", "ELEMENT_OUTSIDE_PRINTABLE_AREA", "severity", "ERROR", "element", "tall", "box", List.of(342, -22, 79, 199), + "measured", Map.of("leftDots", 342, "topDots", -22, "rightDots", 421, "bottomDots", 177), "limit", Map.of("leftDots", 0, "topDots", 0, "rightDots", 764, "bottomDots", 155)), + refusedElements("{\"id\":\"tall\",\"type\":\"box\",\"anchor\":\"center\",\"width\":\"10mm\",\"height\":\"25mm\",\"fill\":true}")); + assertEquals(Map.of("code", "ELEMENT_OUTSIDE_PRINTABLE_AREA", "severity", "ERROR", "element", "inner", "box", List.of(700, 100, 79, 79), + "measured", Map.of("leftDots", 700, "topDots", 100, "rightDots", 779, "bottomDots", 179), "limit", Map.of("leftDots", 0, "topDots", 0, "rightDots", 764, "bottomDots", 155)), + refusedElements("{\"id\":\"g\",\"type\":\"group\",\"anchor\":\"bottom-right\",\"width\":\"80dot\",\"height\":\"80dot\",\"children\":[{\"id\":\"inner\",\"type\":\"box\",\"x\":\"16dot\",\"y\":\"25dot\",\"width\":\"10mm\",\"height\":\"10mm\",\"fill\":true}]}")); + + String hairline = SceneFixture.scene("{\"id\":\"thin\",\"type\":\"box\",\"width\":\"0.1mm\",\"height\":\"5mm\",\"fill\":true}"); + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "thin", "pointer", "/elements/0", "measured", Map.of("widthDots", 0, "heightDots", 39), "limit", Map.of("widthDots", 1, "heightDots", 1)), + refused(hairline)); + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", hairline).put("targetDpi", 600))); + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "none", "pointer", "/elements/0", "measured", Map.of("widthDots", -36, "heightDots", 155), "limit", Map.of("widthDots", 1, "heightDots", 1)), + refusedElements("{\"id\":\"none\",\"type\":\"box\",\"anchor\":\"top-center\",\"x\":\"50mm\",\"fill\":true}")); + } + + + + /*************************************************************************** + ** A line is a box whose height or width is its thickness, and it does not + ** vanish: 0.1 mm is less than a dot at 203 dpi and prints as one dot. + ***************************************************************************/ + @Test + void aThinLineDoesNotVanish() throws Exception + { + boolean[][] page = SceneFixture.raster(SceneFixture.render(SceneFixture.request("RENDER", SceneFixture.scene("{\"id\":\"rule\",\"type\":\"box\",\"anchor\":\"bottom-left\",\"width\":\"50%\",\"height\":\"0.1mm\",\"thickness\":\"0.1mm\"}," + + "{\"id\":\"post\",\"type\":\"box\",\"anchor\":\"top-right\",\"width\":\"1pt\",\"height\":\"50%\",\"thickness\":\"1pt\"}")))); + assertEquals(List.of(24, 24, 788, 179), ink(page)); + assertTrue(page[178][24] && page[178][405] && !page[178][406] && !page[177][24]); + assertTrue(page[24][785] && page[24][787] && !page[24][784] && page[100][786] && !page[101][786]); + } + + + + /*************************************************************************** + ** Text, barcodes and images are parsed and placed by this renderer and then + ** refused by name, with the box they would have had. The design's own + ** example scene is the case. + ***************************************************************************/ + @Test + void anElementThisRendererCannotDrawIsRefusedByName() throws Exception + { + String example = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"defaults\":{\"font\":\"qqq-sans-bold\",\"size\":\"9pt\"}," + + "\"samples\":{\"typical\":{\"record\":{\"humanReadable\":\"(01)00012345678905(10)L240611A\",\"rendererValue\":\"[01]00012345678905[10]L240611A\"}}}," + + "\"elements\":[{\"id\":\"code\",\"type\":\"barcode\",\"symbology\":\"gs1-128\",\"valuePath\":\"/record/rendererValue\",\"anchor\":\"top-center\",\"y\":\"0.5mm\",\"width\":\"94mm\",\"height\":\"11mm\"," + + "\"align\":\"center\",\"valign\":\"middle\",\"minModule\":\"0.25mm\",\"quiet\":10}," + + "{\"id\":\"caption\",\"type\":\"text\",\"valuePath\":\"/record/humanReadable\",\"anchor\":\"bottom-center\",\"y\":\"0.5mm\",\"width\":\"94mm\",\"height\":\"6.5mm\"," + + "\"align\":\"center\",\"valign\":\"middle\",\"size\":\"11pt\",\"maxLength\":48,\"fit\":{\"maxLines\":2,\"shrinkTo\":\"7pt\",\"overflow\":\"refuse\"}}]," + + "\"overrides\":[{\"when\":{\"dpi\":[203]},\"set\":{\"caption\":{\"threshold\":144}}}]}"; + Map code = Map.of("code", "RENDERER_TOO_OLD", "severity", "ERROR", "element", "code", "box", List.of(6, 4, 751, 87)); + Map caption = Map.of("code", "RENDERER_TOO_OLD", "severity", "ERROR", "element", "caption", "box", List.of(6, 100, 751, 51)); + assertEquals(code, refused(example)); + assertEquals(code, SceneFixture.refusal(() -> RendererMain.process(SceneFixture.request("RENDER", example)))); + + Map answer = RendererMain.process(SceneFixture.request("RENDER", example).put("diagnostics", "COLLECT")); + assertEquals("REFUSED", answer.get("outcome")); + assertEquals(List.of(code, caption), answer.get("diagnostics")); + assertFalse(answer.containsKey("base64")); + + assertEquals(Map.of("code", "RENDERER_TOO_OLD", "severity", "ERROR", "element", "logo", "box", List.of(0, 0, 79, 79)), + refusedElements("{\"id\":\"logo\",\"type\":\"image\",\"asset\":\"logo\",\"width\":\"10mm\",\"height\":\"10mm\",\"rotation\":90}")); + for(String rotation : List.of("45", "\"90\"", "4294967386", "90.0", "-90")) + { + assertEquals(invalid("logo", "/elements/0/rotation"), refusedElements("{\"id\":\"logo\",\"type\":\"image\",\"asset\":\"logo\",\"rotation\":" + rotation + "}"), rotation); + } + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "t", "pointer", "/elements/0", "measured", Map.of("unknownProperties", 1), "limit", Map.of("unknownProperties", 0)), + refusedElements("{\"id\":\"t\",\"type\":\"text\",\"value\":\"TEST-0001\",\"symbology\":\"qr\"}")); + + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "t", "pointer", "/elements/0", "measured", Map.of("widthDots", 0, "heightDots", 155), "limit", Map.of("widthDots", 1, "heightDots", 1)), + refusedElements("{\"id\":\"t\",\"type\":\"text\",\"value\":\"TEST-0001\",\"width\":\"0.1mm\"}")); + + Diagnostics diagnostics = new Diagnostics(false); + diagnostics.declare("t", "/elements/0"); + ElementSource source = new ElementSource("t", "/elements/0", Wire.JSON.readTree("{\"id\":\"t\",\"type\":\"text\",\"fit\":{\"maxLines\":2}}"), Map.of("threshold", Wire.JSON.readTree("144")), Map.of("threshold", "/overrides/0/set/t/threshold"), + diagnostics); + assertEquals("t", source.id()); + assertEquals("text", source.type()); + assertEquals(2, source.value("fit").get("maxLines").intValue()); + assertEquals(144, source.value("threshold").intValue()); + assertNull(source.value("size")); + assertEquals(new Placement(Placement.Anchor.TOP_LEFT, Length.ZERO, Length.ZERO, null, null, Placement.Align.LEFT, Placement.Valign.TOP), source.placement()); + SceneElement placeholder = ElementFactory.FACTORIES.get("text").create(source); + assertEquals("t", placeholder.id()); + assertEquals(List.of(), placeholder.children()); + assertEquals(Map.of("code", "RENDERER_TOO_OLD", "severity", "ERROR", "element", "t", "box", List.of(1, 2, 3, 4)), + SceneFixture.refusal(() -> placeholder.paint(new Canvas(8, 8, 203), new Frame(1, 2, 3, 4), null, diagnostics))); + } + + + + /*************************************************************************** + ** Asked to collect, the renderer keeps every element's refusal and goes on + ** with the rest. The answer is refused and carries no label, whether the + ** request validated or rendered; the canvas holds the elements that had no + ** error. + ***************************************************************************/ + @Test + void collectingKeepsEveryRefusalAndTheRestOfTheLabel() throws Exception + { + String source = SceneFixture.scene("{\"id\":\"good\",\"type\":\"box\",\"x\":\"5dot\",\"width\":\"16dot\",\"height\":\"4dot\",\"fill\":true}," + + "{\"id\":\"wide\",\"type\":\"box\",\"x\":\"90mm\",\"width\":\"10mm\",\"height\":\"5mm\",\"thickness\":\"1pt\"}," + + "{\"id\":\"g\",\"type\":\"group\",\"width\":\"0.1mm\",\"children\":[{\"id\":\"unseen\",\"type\":\"text\",\"value\":\"TEST-0001\"}]}," + + "{\"id\":\"words\",\"type\":\"text\",\"value\":\"TEST-0001\",\"y\":\"10mm\",\"height\":\"5mm\"}," + + "{\"id\":\"also\",\"type\":\"box\",\"anchor\":\"bottom-left\",\"width\":\"8dot\",\"height\":\"2dot\",\"fill\":true}"); + List> expected = List.of( + Map.of("code", "DEVICE_UNIT_USED", "severity", "WARNING", "element", "good", "pointer", "/elements/0/x"), + Map.of("code", "DEVICE_UNIT_USED", "severity", "WARNING", "element", "good", "pointer", "/elements/0/width"), + Map.of("code", "DEVICE_UNIT_USED", "severity", "WARNING", "element", "good", "pointer", "/elements/0/height"), + Map.of("code", "DEVICE_UNIT_USED", "severity", "WARNING", "element", "also", "pointer", "/elements/4/width"), + Map.of("code", "DEVICE_UNIT_USED", "severity", "WARNING", "element", "also", "pointer", "/elements/4/height"), + Map.of("code", "ELEMENT_OUTSIDE_PRINTABLE_AREA", "severity", "ERROR", "element", "wide", "box", List.of(719, 0, 79, 39), + "measured", Map.of("leftDots", 719, "topDots", 0, "rightDots", 798, "bottomDots", 39), "limit", Map.of("leftDots", 0, "topDots", 0, "rightDots", 764, "bottomDots", 155)), + Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "g", "pointer", "/elements/2", "measured", Map.of("widthDots", 0, "heightDots", 155), "limit", Map.of("widthDots", 1, "heightDots", 1)), + Map.of("code", "RENDERER_TOO_OLD", "severity", "ERROR", "element", "words", "box", List.of(0, 80, 764, 39))); + Map validated = RendererMain.process(SceneFixture.request("VALIDATE", source).put("diagnostics", "COLLECT")); + assertEquals("REFUSED", validated.get("outcome")); + assertEquals(expected, validated.get("diagnostics")); + assertFalse(validated.containsKey("base64")); + + /////////////////////////////////////////////////////////////////////////////////////////// + // a refused answer is the same for a render as for a validation: findings and no label, // + // so a host that looks for `base64` without reading the outcome has nothing to print // + /////////////////////////////////////////////////////////////////////////////////////////// + ObjectNode request = SceneFixture.request("RENDER", source).put("diagnostics", "COLLECT"); + Map rendered = RendererMain.process(request); + assertEquals("REFUSED", rendered.get("outcome")); + assertEquals(expected, rendered.get("diagnostics")); + assertEquals(validated.keySet(), rendered.keySet()); + assertEquals(List.of("protocol", "requestId", "outcome", "rendererVersion", "catalogVersion", "diagnostics"), List.copyOf(rendered.keySet())); + assertNull(Scene2.render(SceneFixture.input(request), Wire.JSON.readTree(source), new Diagnostics(true))); + + ////////////////////////////////////////////////////////////////////////////////////////// + // the canvas of the pass still holds the elements that had no error, for a preview: // + // `good` at 5, 0 (16 by 4 dots) and `also` in the bottom left corner (8 by 2 dots) // + ////////////////////////////////////////////////////////////////////////////////////////// + Canvas canvas = Scene2.compose(SceneFixture.input(request), Wire.JSON.readTree(source), new Diagnostics(true), true); + for(int row = 0; row < canvas.height(); row++) + { + int first = row < 4 ? 5 : row >= 153 ? 0 : -1; + int last = row < 4 ? 20 : row >= 153 ? 7 : -1; + assertEquals(List.of(first, last), List.of(canvas.firstInk(row), canvas.lastInk(row)), "row " + row); + } + + //////////////////////////////////////////////////////////////////////////////////////// + // a refusal about the scene as a whole ends the pass: nothing is drawn, one finding // + //////////////////////////////////////////////////////////////////////////////////////// + Map whole = RendererMain.process(SceneFixture.request("RENDER", "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"2in\"},\"elements\":[" + BOX + "]}").put("diagnostics", "COLLECT")); + assertEquals("REFUSED", whole.get("outcome")); + assertEquals(1, ((List) whole.get("diagnostics")).size()); + for(String name : List.of("base64", "sha256", "length", "mediaType", "pageCount")) + { + assertFalse(whole.containsKey(name) || rendered.containsKey(name), name); + } + + //////////////////////////////////////////////////////////////////////////////////// + // and a scene with warnings only is not refused: it keeps its label and findings // + //////////////////////////////////////////////////////////////////////////////////// + Map warned = RendererMain.process(SceneFixture.request("RENDER", SceneFixture.scene("{\"id\":\"good\",\"type\":\"box\",\"width\":\"16dot\",\"height\":\"4mm\",\"fill\":true}")).put("diagnostics", "COLLECT")); + assertEquals("OK", warned.get("outcome")); + assertEquals(1, ((List) warned.get("diagnostics")).size()); + assertTrue(Base64.getDecoder().decode((String) warned.get("base64")).length > 0); + } + + + + /*************************************************************************** + ** A length too long for any stock is the scene's mistake wherever it is + ** written, however long: `SCENE_INVALID` at its own pointer, in the element + ** or in the override that states it, when validating and when rendering. + ** 84600 in and 99999 in do not fit an `int` of micrometres; they are refused + ** like 79 in, and a collecting pass answers with the finding. + ***************************************************************************/ + @Test + void aLengthTooLongForAnyStockIsRefusedAtItsPointer() throws Exception + { + String design = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"%s\",\"height\":\"%s\"},\"elements\":[]}"; + String override = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"elements\":[" + BOX + "],\"overrides\":[{\"when\":{\"dpi\":[600]},\"set\":{\"a\":{\"width\":\"%s\"}}}]}"; + for(String length : List.of("79in", "84547in", "84600in", "99999in", "99999.999in", "2000.001mm", "99999.999mm", "5669.293pt", "99999.999pt")) + { + assertEquals(invalid("/design/width"), refused(String.format(design, length, "1in")), length); + assertEquals(invalid("/design/height"), refused(String.format(design, "4in", length)), length); + assertEquals(invalid("/defaults/size"), refused(String.format("{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"defaults\":{\"size\":\"%s\"},\"elements\":[]}", length)), length); + assertEquals(invalid("a", "/overrides/0/set/a/width"), refused(String.format(override, length)), length); + for(String property : List.of("x", "y", "width", "height", "thickness")) + { + String source = SceneFixture.scene(BOX.replace("}", ",\"" + property + "\":\"" + length + "\"}").replaceFirst("\"" + property + "\":\"[0-9a-z]+\",", "")); + assertEquals(invalid("a", "/elements/0/" + property), refused(source), source); + assertEquals(invalid("a", "/elements/0/" + property), SceneFixture.refusal(() -> RendererMain.process(SceneFixture.request("RENDER", source))), source); + Map collected = RendererMain.process(SceneFixture.request("RENDER", source).put("diagnostics", "COLLECT")); + assertEquals("REFUSED", collected.get("outcome")); + assertEquals(List.of(invalid("a", "/elements/0/" + property)), collected.get("diagnostics")); + } + } + } + + + + /*************************************************************************** + ** The printable area is the stock less its margins rounded down to whole + ** dots, which is what a box of that width and height resolves to, so a + ** design that fills the printable area fits at every resolution. The case of + ** the review: 100 mm stock, 3 mm margins, a box 94 mm wide. The far edge is + ** floor(97 mm) less ceil(3 mm) at 300 dpi, 1109 dots, one fewer than the box. + ***************************************************************************/ + @Test + void aBoxOfExactlyThePrintableAreaFitsAtEveryResolution() throws Exception + { + String source = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"100mm\",\"height\":\"30mm\"},\"elements\":[{\"id\":\"all\",\"type\":\"box\",\"x\":\"0mm\",\"width\":\"94mm\",\"height\":\"24mm\",\"fill\":true}]}"; + List expected = List.of(new Frame(24, 24, 751, 191), new Frame(36, 36, 1110, 283), new Frame(71, 71, 2220, 566)); + for(int index = 0; index < 3; index++) + { + ObjectNode request = SceneFixture.request("RENDER", SceneFixture.stock(100_000, 30_000, 3000, 203), source).put("targetDpi", Scene2.RESOLUTIONS.get(index)); + Frame printable = Scene2.Page.of(SceneFixture.input(request)).printable(); + assertEquals(expected.get(index), printable); + assertEquals(List.of(printable.x(), printable.y(), printable.right(), printable.bottom()), ink(SceneFixture.raster(SceneFixture.render(request)))); + assertEquals(List.of(), accepted(request.put("operation", "VALIDATE"))); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + // one dot more than the printable area is still refused, on either axis, at every target // + ////////////////////////////////////////////////////////////////////////////////////////////// + for(int dpi : Scene2.RESOLUTIONS) + { + for(String larger : List.of(source.replace("94mm", "94.2mm"), source.replace("24mm", "24.2mm"))) + { + assertEquals("ELEMENT_OUTSIDE_PRINTABLE_AREA", SceneFixture.refusal(() -> RendererMain.process(SceneFixture.request("VALIDATE", SceneFixture.stock(100_000, 30_000, 3000, 203), larger).put("targetDpi", dpi))).get("code")); + } + } + } + + + + /*************************************************************************** + ** Property, over seeded stocks with margins of any fraction of a dot (and + ** none) at every target: the printable area is as many whole dots as the + ** stock less its margins holds; it lies on the page; it starts at the left + ** and top margins rounded up unless the page ends first, and never a whole + ** dot inside any margin; and a box of exactly the printable width and + ** height is accepted and inks exactly the printable area. + ***************************************************************************/ + @Test + void thePrintableAreaHoldsABoxOfItsOwnSizeOnAnyStock() throws Exception + { + SplittableRandom random = new SplittableRandom(20261113L); + int moved = 0; + for(int iteration = 0; iteration < 150; iteration++) + { + int width = 20_000 + random.nextInt(90_000); + int height = 10_000 + random.nextInt(50_000); + int left = random.nextInt(4) == 0 ? 0 : random.nextInt(6000); + int right = random.nextInt(4) == 0 ? 0 : random.nextInt(6000); + int top = random.nextInt(4) == 0 ? 0 : random.nextInt(4000); + int bottom = random.nextInt(4) == 0 ? 0 : random.nextInt(4000); + JsonNode stock = Wire.JSON.valueToTree(Map.of("widthMicrons", width, "heightMicrons", height, "topMicrons", top, "rightMicrons", right, "bottomMicrons", bottom, "leftMicrons", left, "dpi", 203)); + String source = String.format("{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"%s\",\"height\":\"%s\"},\"elements\":[{\"id\":\"all\",\"type\":\"box\",\"width\":\"%s\",\"height\":\"%s\",\"fill\":true}]}", + millimetres(width), millimetres(height), millimetres(width - left - right), millimetres(height - top - bottom)); + for(int dpi : Scene2.RESOLUTIONS) + { + ObjectNode request = SceneFixture.request(iteration % 3 == 0 ? "RENDER" : "VALIDATE", stock, source).put("targetDpi", dpi); + String name = stock + " at " + dpi; + Scene2.Page page = Scene2.Page.of(SceneFixture.input(request)); + Frame printable = page.printable(); + assertEquals((long) (width - left - right) * dpi / 25_400, printable.width(), name); + assertEquals((long) (height - top - bottom) * dpi / 25_400, printable.height(), name); + assertTrue(printable.x() >= 0 && printable.y() >= 0 && printable.right() <= page.width() && printable.bottom() <= page.height(), name); + long ceilLeft = ((long) left * dpi + 25_399) / 25_400; + long ceilTop = ((long) top * dpi + 25_399) / 25_400; + assertTrue(printable.x() == ceilLeft || (printable.x() == ceilLeft - 1 && printable.right() == page.width()), name); + assertTrue(printable.y() == ceilTop || (printable.y() == ceilTop - 1 && printable.bottom() == page.height()), name); + moved += printable.x() == ceilLeft && printable.y() == ceilTop ? 0 : 1; + assertTrue((long) printable.right() * 25_400 < (long) (width - right) * dpi + 25_400 && (long) printable.bottom() * 25_400 < (long) (height - bottom) * dpi + 25_400, name); + if(iteration % 3 == 0) + { + assertEquals(List.of(printable.x(), printable.y(), printable.right(), printable.bottom()), ink(SceneFixture.raster(SceneFixture.render(request))), name); + } + else + { + assertEquals(List.of(), accepted(request), name); + } + } + } + assertTrue(moved > 0, "no stock in the campaign had a printable area that would have passed the page edge"); + + /////////////////////////////////////////////////////////////////////////////////////////////// + // the move, by hand: 50.5 mm with a 3 mm left margin and none on the right at 300 dpi is // + // a page of 596 dots (596.46) and a printable area of 561 (47.5 mm, 561.02); 3 mm is // + // 35.43 dots, and 36 + 561 would be one dot past the page // + /////////////////////////////////////////////////////////////////////////////////////////////// + JsonNode edge = Wire.JSON.valueToTree(Map.of("widthMicrons", 50_500, "heightMicrons", 25_400, "topMicrons", 0, "rightMicrons", 0, "bottomMicrons", 0, "leftMicrons", 3000, "dpi", 300)); + Scene2.Page page = Scene2.Page.of(SceneFixture.input(SceneFixture.request("VALIDATE", edge, "{}"))); + assertEquals(596, page.width()); + assertEquals(new Frame(35, 0, 561, 300), page.printable()); + } + + + + /*************************************************************************** + ** Micrometres as a length in millimetres. + ***************************************************************************/ + private static String millimetres(int micrometres) + { + return (micrometres / 1000 + "." + String.format("%03d", micrometres % 1000) + "mm"); + } + + + + /*************************************************************************** + ** One scene may paint sixteen times its printable area, an overlap counted + ** each time. The element that would pass that is refused where it is, with + ** what the scene would have painted, when validating as when rendering; an + ** outline counts its bars, and a group counts nothing. So 1000 boxes that + ** each fill the label cost at most sixteen fills, not 1000. + ***************************************************************************/ + @Test + void aSceneMayPaintSixteenTimesItsPrintableAreaAndNoMore() throws Exception + { + String fill = "{\"id\":\"f%d\",\"type\":\"box\",\"fill\":true}"; + String outline = "{\"id\":\"o%d\",\"type\":\"box\",\"thickness\":\"1dot\"}"; + List sixteen = new ArrayList<>(); + for(int index = 0; index < 16; index++) + { + sixteen.add(String.format(fill, index)); + sixteen.add("{\"id\":\"g" + index + "\",\"type\":\"group\",\"children\":[]}"); + } + String most = SceneFixture.scene(String.join(",", sixteen)); + assertEquals(List.of(), accepted(SceneFixture.request("VALIDATE", most))); + assertEquals(List.of(24, 24, 788, 179), ink(SceneFixture.raster(SceneFixture.render(SceneFixture.request("RENDER", most))))); + + /////////////////////////////////////////////////////////////////////////////////////////////// + // one dot more: a one-dot outline of the 764 by 155 printable area is 2 x (764 + 155) // + // dots, 1.55% of it, and the percentage is rounded up // + /////////////////////////////////////////////////////////////////////////////////////////////// + Map outlined = Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "o0", "pointer", "/elements/32", "box", List.of(0, 0, 764, 155), + "measured", Map.of("paintedPercent", 1602), "limit", Map.of("paintedPercent", 1600)); + assertEquals(outlined, refused(SceneFixture.scene(String.join(",", sixteen) + "," + String.format(outline, 0)))); + Map filled = Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "f16", "pointer", "/elements/32", "box", List.of(0, 0, 764, 155), + "measured", Map.of("paintedPercent", 1700), "limit", Map.of("paintedPercent", 1600)); + String over = SceneFixture.scene(String.join(",", sixteen) + "," + String.format(fill, 16)); + assertEquals(filled, refused(over)); + assertEquals(filled, SceneFixture.refusal(() -> RendererMain.process(SceneFixture.request("RENDER", over)))); + + //////////////////////////////////////////////////////////////////////////////////////////// + // outlines are counted as their bars: 500 of the whole label are 7.8 times its area // + //////////////////////////////////////////////////////////////////////////////////////////// + List outlines = new ArrayList<>(); + for(int index = 0; index < 500; index++) + { + outlines.add(String.format(outline, index)); + } + assertEquals(500, ((List) accepted(SceneFixture.request("RENDER", SceneFixture.scene(String.join(",", outlines))))).size()); + + ///////////////////////////////////////////////////////////////////////////////////////////// + // the worst scene the element limit allows, collected: fifteen fills and a small box are // + // painted, and every later fill is refused where it is and costs nothing // + ///////////////////////////////////////////////////////////////////////////////////////////// + List thousand = new ArrayList<>(); + for(int index = 0; index < 999; index++) + { + thousand.add(String.format(fill, index)); + } + thousand.add(15, "{\"id\":\"small\",\"type\":\"box\",\"width\":\"1mm\",\"height\":\"1mm\",\"fill\":true}"); + ObjectNode request = SceneFixture.request("RENDER", SceneFixture.stock(400_000, 400_000, 0, 203), "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"400mm\",\"height\":\"400mm\"},\"elements\":[" + String.join(",", thousand) + "]}") + .put("targetDpi", 300).put("diagnostics", "COLLECT"); + Map worst = RendererMain.process(request); + assertEquals("REFUSED", worst.get("outcome")); + List findings = (List) worst.get("diagnostics"); + assertEquals(984, findings.size()); + assertEquals(Map.of("code", "SCENE_INVALID", "severity", "ERROR", "element", "f15", "pointer", "/elements/16", "box", List.of(0, 0, 4724, 4724), "measured", Map.of("paintedPercent", 1601), "limit", Map.of("paintedPercent", 1600)), + findings.get(0)); + assertEquals(findings, RendererMain.process(request.put("operation", "VALIDATE")).get("diagnostics")); + + Canvas canvas = new Canvas(8, 8, 203); + assertTrue(canvas.admit(1000)); + assertFalse(canvas.admit(25)); + assertEquals(1602, canvas.paintedPercent(25)); + assertTrue(canvas.admit(24)); + assertFalse(canvas.admit(1)); + assertEquals(1600, canvas.paintedPercent(0)); + } + + + + /*************************************************************************** + ** A stock the printer language cannot address is the host's mistake, not the + ** scene's, and is refused as it is for version 1. + ***************************************************************************/ + @Test + void anUnaddressableStockIsRefusedAsForVersionOne() + { + String source = "{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"elements\":[]}"; + for(ObjectNode stock : List.of(SceneFixture.stock(SceneFixture.WIDTH, SceneFixture.HEIGHT, SceneFixture.MARGIN, 150), SceneFixture.stock(2_000_000, 25_400, 0, 203), SceneFixture.stock(25_400, 2_000_000, 0, 600), + SceneFixture.stock(400_000, 1_300_000, 0, 600), SceneFixture.stock(1000, 25_400, 499, 203), SceneFixture.stock(25_400, 1000, 499, 203))) + { + for(String operation : List.of("VALIDATE", "RENDER")) + { + IllegalArgumentException refusal = assertThrows(IllegalArgumentException.class, () -> RendererMain.process(SceneFixture.request(operation, stock, source)), stock.toString()); + assertEquals("UNSUPPORTED_ZPL_STOCK", refusal.getMessage()); + assertFalse(refusal instanceof Refusal); + } + } + } + + + + /*************************************************************************** + ** Property, over seeded scenes of boxes and groups at every target: a scene + ** is accepted and then no ink is outside the printable area, or it is + ** refused and the refusal's measured values pass its limits. + ***************************************************************************/ + @Test + void resolvedBoxesNeverLeaveThePrintableArea() throws Exception + { + SplittableRandom random = new SplittableRandom(20261112L); + List anchors = List.of("top-left", "top-center", "top-right", "middle-left", "center", "middle-right", "bottom-left", "bottom-center", "bottom-right"); + int accepted = 0; + int outside = 0; + int empty = 0; + for(int iteration = 0; iteration < 600; iteration++) + { + int dpi = Scene2.RESOLUTIONS.get(random.nextInt(3)); + StringBuilder elements = new StringBuilder(); + for(int index = 0; index < 1 + random.nextInt(4); index++) + { + String anchor = anchors.get(random.nextInt(9)); + boolean centred = anchor.contains("center"); + String x = (centred && random.nextBoolean() ? "-" : "") + random.nextInt(30) + "." + random.nextInt(10) + "mm"; + String y = random.nextInt(8) + (random.nextBoolean() ? "mm" : "%"); + String size = random.nextInt(5) == 0 ? "" : ",\"width\":\"" + (random.nextBoolean() ? (1 + random.nextInt(60)) + "mm" : (1 + random.nextInt(100)) + "%") + "\",\"height\":\"" + random.nextInt(20) + "." + (1 + random.nextInt(9)) + "mm\""; + String box = "\"type\":\"box\",\"anchor\":\"" + anchor + "\",\"x\":\"" + x + "\",\"y\":\"" + y + "\"" + size + ",\"fill\":" + random.nextBoolean() + ",\"thickness\":\"0.3mm\""; + elements.append(index == 0 ? "" : ","); + if(random.nextInt(3) == 0) + { + elements.append("{\"id\":\"g").append(index).append("\",\"type\":\"group\",\"anchor\":\"").append(anchors.get(random.nextInt(9))).append("\",\"width\":\"").append(20 + random.nextInt(80)).append("%\",\"height\":\"") + .append(20 + random.nextInt(80)).append("%\",\"children\":[{\"id\":\"e").append(index).append("\",").append(box).append("}]}"); + } + else + { + elements.append("{\"id\":\"e").append(index).append("\",").append(box).append("}"); + } + } + ObjectNode request = SceneFixture.request("RENDER", SceneFixture.scene(elements.toString())).put("targetDpi", dpi); + try + { + boolean[][] page = SceneFixture.raster(SceneFixture.render(request)); + Scene2.Page area = Scene2.Page.of(SceneFixture.input(request)); + List ink = ink(page); + assertTrue(ink.get(0) >= area.printable().x() && ink.get(1) >= area.printable().y() && ink.get(2) <= area.printable().right() && ink.get(3) <= area.printable().bottom(), "ink in a margin: " + elements); + accepted++; + } + catch(Refusal refusal) + { + Map measured = refusal.diagnostic().measured(); + Map limit = refusal.diagnostic().limit(); + if("ELEMENT_OUTSIDE_PRINTABLE_AREA".equals(refusal.getMessage())) + { + assertTrue(measured.get("leftDots") < limit.get("leftDots") || measured.get("topDots") < limit.get("topDots") || measured.get("rightDots") > limit.get("rightDots") + || measured.get("bottomDots") > limit.get("bottomDots"), "refused but inside: " + elements); + assertEquals(Scene2.Page.of(SceneFixture.input(request)).printable().width(), limit.get("rightDots")); + outside++; + } + else + { + assertEquals("SCENE_INVALID", refusal.getMessage(), elements.toString()); + assertTrue(measured.get("widthDots") < 1 || measured.get("heightDots") < 1, "refused but has size: " + elements); + empty++; + } + } + } + assertTrue(accepted > 50 && outside > 50 && empty > 5, accepted + " accepted, " + outside + " outside, " + empty + " without size"); + } +} diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/SceneFixture.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/SceneFixture.java new file mode 100644 index 0000000..596cb97 --- /dev/null +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/SceneFixture.java @@ -0,0 +1,202 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.kingsrook.qbits.renderer.RenderInput; +import com.kingsrook.qbits.renderer.RendererMain; +import com.kingsrook.qbits.renderer.Wire; +import org.junit.jupiter.api.function.Executable; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/******************************************************************************* + ** Shared test support for version 2 scenes: requests on a known stock, and a + ** reader that rebuilds the page bitmap from emitted ZPL, so tests look at what + ** a printer would be sent rather than at the renderer's own canvas. + *******************************************************************************/ +public final class SceneFixture +{ + //////////////////////////////////////////////////////////////////////////////////////////////// + // the stock of the design document's example: 4 x 1 in with 3 mm margins. At 203 dpi the // + // page is 812 x 203 dots and the printable area is 764 x 155 dots starting at 24, 24 // + //////////////////////////////////////////////////////////////////////////////////////////////// + public static final int WIDTH = 101_600; + public static final int HEIGHT = 25_400; + public static final int MARGIN = 3000; + + private static final Pattern HEADER = Pattern.compile("\\^XA\\^PW([0-9]+)\\^LL([0-9]+)\\^LH0,0\\^CI28\n"); + private static final Pattern FIELD = Pattern.compile("\\^FO([0-9]+),([0-9]+)\\^GFA,([0-9]+),([0-9]+),([0-9]+),([0-9A-F]*)\\^FS\n"); + + + + /*************************************************************************** + ** Utility class. + ***************************************************************************/ + private SceneFixture() + { + } + + + + /*************************************************************************** + ** A stock with the same margin on every side. + ***************************************************************************/ + public static ObjectNode stock(int widthMicrons, int heightMicrons, int marginMicrons, int dpi) + { + return (Wire.JSON.valueToTree(Map.of("widthMicrons", widthMicrons, "heightMicrons", heightMicrons, "topMicrons", marginMicrons, "rightMicrons", marginMicrons, + "bottomMicrons", marginMicrons, "leftMicrons", marginMicrons, "dpi", dpi))); + } + + + + /*************************************************************************** + ** One protocol request for a ZPL source on a stock. + ***************************************************************************/ + public static ObjectNode request(String operation, JsonNode stock, String source) + { + ObjectNode request = Wire.JSON.createObjectNode(); + request.put("protocol", Wire.PROTOCOL).put("requestId", "scene2-test").put("operation", operation).put("format", "ZPL").put("maximumBytes", 8 * 1024 * 1024).put("source", source); + request.set("stock", stock); + request.putObject("context"); + request.putArray("assets"); + return (request); + } + + + + /*************************************************************************** + ** A request for a scene on the example stock at 203 dpi. + ***************************************************************************/ + public static ObjectNode request(String operation, String source) + { + return (request(operation, stock(WIDTH, HEIGHT, MARGIN, 203), source)); + } + + + + /*************************************************************************** + ** A version 2 scene for the example stock holding these elements. + ***************************************************************************/ + public static String scene(String elements) + { + return ("{\"schema\":\"qqq-zpl-scene/2\",\"design\":{\"width\":\"4in\",\"height\":\"1in\"},\"elements\":[" + elements + "]}"); + } + + + + /*************************************************************************** + ** The validated input of a request, as the worker builds it. + ***************************************************************************/ + public static RenderInput input(JsonNode request) throws Exception + { + return (new RenderInput(request)); + } + + + + /*************************************************************************** + ** Run a request through the worker's entry point and return its label. + ***************************************************************************/ + public static byte[] render(JsonNode request) throws Exception + { + Map answer = RendererMain.process(request); + assertEquals("OK", answer.get("outcome")); + return (Base64.getDecoder().decode((String) answer.get("base64"))); + } + + + + /*************************************************************************** + ** The one diagnostic of the refusal a request ends in, as the wire carries + ** it. + ***************************************************************************/ + public static Map refusal(Executable work) + { + return (assertThrows(Refusal.class, work).diagnostic().wire()); + } + + + + /*************************************************************************** + ** The page a printer would draw from a label: `[row][column]`, true for ink. + ** Only the header, uncompressed graphic fields and the end of the label are + ** understood, which is all a version 2 label may contain. + ***************************************************************************/ + public static boolean[][] raster(byte[] zpl) + { + String text = new String(zpl, StandardCharsets.US_ASCII); + Matcher header = HEADER.matcher(text); + assertTrue(header.lookingAt(), "label header"); + boolean[][] page = new boolean[Integer.parseInt(header.group(2))][Integer.parseInt(header.group(1))]; + int position = header.end(); + Matcher field = FIELD.matcher(text); + while(field.find(position) && field.start() == position) + { + int x = Integer.parseInt(field.group(1)); + int y = Integer.parseInt(field.group(2)); + int stride = Integer.parseInt(field.group(5)); + String hex = field.group(6); + assertEquals(field.group(3), field.group(4)); + assertEquals(Integer.parseInt(field.group(3)) * 2, hex.length()); + assertTrue(Integer.parseInt(field.group(3)) <= 99_999 && hex.length() % (stride * 2) == 0, "field size"); + for(int index = 0; index < hex.length() / 2; index++) + { + int value = Integer.parseInt(hex.substring(index * 2, index * 2 + 2), 16); + for(int bit = 0; bit < 8; bit++) + { + if((value & (128 >> bit)) != 0) + { + page[y + index / stride][x + index % stride * 8 + bit] = true; + } + } + } + position = field.end(); + } + assertEquals("^XZ\n", text.substring(position)); + return (page); + } + + + + /*************************************************************************** + ** How many graphic fields a label holds. + ***************************************************************************/ + public static int fields(byte[] zpl) + { + Matcher field = FIELD.matcher(new String(zpl, StandardCharsets.US_ASCII)); + int count = 0; + while(field.find()) + { + count++; + } + return (count); + } +} diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/SceneGoldenTest.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/SceneGoldenTest.java new file mode 100644 index 0000000..ff6a480 --- /dev/null +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/SceneGoldenTest.java @@ -0,0 +1,162 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.kingsrook.qbits.renderer.Wire; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + + +/******************************************************************************* + ** Golden labels for the layout cases in `scene2/cases`, at 203, 300 and 600 + ** dpi. Each case has a picture (the page a printer would draw, compared dot + ** for dot) and the exact ZPL. A box is whole dots of pure ink, so both are + ** exact on every machine. A golden changes only with a reviewed change to the + ** layout rules: what this build drew is left in `target/scene2-actual`. + *******************************************************************************/ +class SceneGoldenTest +{ + private static final String ROOT = "/scene2/"; + + + + /*************************************************************************** + ** Every case at every resolution. + ***************************************************************************/ + static List goldens() throws Exception + { + List goldens = new ArrayList<>(); + try(Stream files = Files.list(Path.of(SceneGoldenTest.class.getResource(ROOT + "cases").toURI()))) + { + for(String name : files.map(file -> file.getFileName().toString()).filter(name -> name.endsWith(".json")).map(name -> name.substring(0, name.length() - 5)).sorted().toList()) + { + Scene2.RESOLUTIONS.forEach(dpi -> goldens.add(Arguments.of(name, dpi))); + } + } + return (goldens); + } + + + + /*************************************************************************** + ** The label of one case at one render target is the stored ZPL, and the page + ** rebuilt from it is the stored picture. + ***************************************************************************/ + @ParameterizedTest(name = "{0} at {1} dpi") + @MethodSource("goldens") + void rendersTheGoldenLabel(String name, int dpi) throws Exception + { + JsonNode definition = Wire.JSON.readTree(resource("cases/" + name + ".json")); + ObjectNode request = SceneFixture.request("RENDER", definition.get("stock"), Wire.JSON.writeValueAsString(definition.get("scene"))).put("targetDpi", dpi); + byte[] actual = SceneFixture.render(request); + boolean[][] page = SceneFixture.raster(actual); + BufferedImage picture = new BufferedImage(page[0].length, page.length, BufferedImage.TYPE_BYTE_BINARY); + for(int y = 0; y < page.length; y++) + { + for(int x = 0; x < page[y].length; x++) + { + picture.setRGB(x, y, page[y][x] ? 0x000000 : 0xFFFFFF); + } + } + byte[] expected = resource("golden/" + name + "-" + dpi + ".zpl"); + byte[] stored = resource("golden/" + name + "-" + dpi + ".png"); + if(expected == null || stored == null || !Arrays.equals(expected, actual)) + { + Path directory = Files.createDirectories(Path.of("target", "scene2-actual")); + Files.write(directory.resolve(name + "-" + dpi + ".zpl"), actual); + ImageIO.write(picture, "png", directory.resolve(name + "-" + dpi + ".png").toFile()); + } + assertNotNull(expected, "No golden ZPL is stored for " + name + " at " + dpi + " dpi"); + assertNotNull(stored, "No golden picture is stored for " + name + " at " + dpi + " dpi"); + int difference = Arrays.mismatch(expected, actual); + if(difference >= 0) + { + fail("Scene " + name + " at " + dpi + " dpi no longer renders its golden label: first difference at byte " + difference + " (golden " + expected.length + " bytes, now " + actual.length + ")"); + } + BufferedImage golden = ImageIO.read(new ByteArrayInputStream(stored)); + assertEquals(picture.getWidth(), golden.getWidth()); + assertEquals(picture.getHeight(), golden.getHeight()); + for(int y = 0; y < page.length; y++) + { + for(int x = 0; x < page[y].length; x++) + { + if(page[y][x] != ((golden.getRGB(x, y) & 0xFFFFFF) == 0)) + { + fail("Scene " + name + " at " + dpi + " dpi differs from its golden picture at dot " + x + ", " + y); + } + } + } + } + + + + /*************************************************************************** + ** The stored goldens are exactly the cases at the three resolutions, so a + ** case cannot be dropped and leave its files behind. + ***************************************************************************/ + @Test + void goldensAreExactlyTheCasesAtEveryResolution() throws Exception + { + List expected = new ArrayList<>(); + for(Arguments golden : goldens()) + { + expected.add(golden.get()[0] + "-" + golden.get()[1] + ".png"); + expected.add(golden.get()[0] + "-" + golden.get()[1] + ".zpl"); + } + assertTrue(expected.size() >= 18); + try(Stream files = Files.list(Path.of(SceneGoldenTest.class.getResource(ROOT + "golden").toURI()))) + { + assertEquals(expected.stream().sorted().toList(), files.map(file -> file.getFileName().toString()).sorted().toList()); + } + } + + + + /*************************************************************************** + ** Test resource bytes, or null when there is no such file. + ***************************************************************************/ + private static byte[] resource(String path) throws Exception + { + try(InputStream stream = SceneGoldenTest.class.getResourceAsStream(ROOT + path)) + { + return (stream == null ? null : stream.readAllBytes()); + } + } +} diff --git a/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/SceneSchemaTest.java b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/SceneSchemaTest.java new file mode 100644 index 0000000..feca5fc --- /dev/null +++ b/renderer/src/test/java/com/kingsrook/qbits/renderer/scene2/SceneSchemaTest.java @@ -0,0 +1,168 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.kingsrook.qbits.renderer.scene2; + + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Pattern; +import com.fasterxml.jackson.databind.JsonNode; +import com.kingsrook.qbits.renderer.Wire; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/******************************************************************************* + ** The published schema, `renderer/schema/zpl-scene-2.schema.json`, describes + ** what the parser reads: the same element types, the same properties for each, + ** the same words and the same length syntax. The parser stays the authority; + ** this keeps the document an author reads from drifting away from it. + *******************************************************************************/ +class SceneSchemaTest +{ + /*************************************************************************** + ** The schema document. + ***************************************************************************/ + private static JsonNode schema() throws Exception + { + return (Wire.JSON.readTree(Files.readAllBytes(Path.of("schema", "zpl-scene-2.schema.json")))); + } + + + + /*************************************************************************** + ** The property names of a schema object. + ***************************************************************************/ + private static Set names(JsonNode properties) + { + Set names = new TreeSet<>(); + properties.fieldNames().forEachRemaining(names::add); + return (names); + } + + + + /*************************************************************************** + ** Each element type of the schema is one the parser builds, with exactly the + ** properties its factory reads beside the common ones. + ***************************************************************************/ + @Test + void theSchemaDeclaresTheTypesAndPropertiesTheParserReads() throws Exception + { + JsonNode schema = schema(); + assertEquals("qqq-zpl-scene/2", schema.at("/properties/schema/const").textValue()); + assertEquals(Scene2.SCHEMA, schema.at("/properties/schema/const").textValue()); + assertEquals(Set.of("schema", "design", "defaults", "samples", "elements", "overrides"), names(schema.at("/properties"))); + Set common = names(schema.at("/$defs/placement/properties")); + common.add("type"); + assertEquals(new TreeSet<>(ElementFactory.COMMON), common); + Set types = new HashSet<>(); + for(JsonNode element : schema.at("/$defs/element/oneOf")) + { + String type = element.at("/properties/type/const").textValue(); + assertTrue(types.add(type), type); + Set own = names(element.get("properties")); + own.remove("type"); + assertEquals("group".equals(type) ? Set.of("children") : new TreeSet<>(ElementFactory.FACTORIES.get(type).properties()), own, type); + assertFalse(element.get("unevaluatedProperties").booleanValue(), type); + } + Set expected = new HashSet<>(ElementFactory.FACTORIES.keySet()); + expected.add("group"); + assertEquals(expected, types); + for(String name : names(schema.at("/properties"))) + { + assertTrue(ElementFactory.VOCABULARY.contains(name), name); + } + assertEquals(SceneParser.MAXIMUM_OVERRIDES, schema.at("/properties/overrides/maxItems").intValue()); + assertEquals(SceneParser.MAXIMUM_SAMPLES, schema.at("/properties/samples/maxProperties").intValue()); + assertEquals(Scene2.RESOLUTIONS, List.of(203, 300, 600)); + assertEquals("[203,300,600]", schema.at("/properties/overrides/items/properties/when/properties/dpi/items/enum").toString()); + } + + + + /*************************************************************************** + ** The words and patterns of the schema are the parser's. + ***************************************************************************/ + @Test + void theSchemaUsesTheWordsAndLengthsOfTheParser() throws Exception + { + JsonNode schema = schema(); + for(JsonNode word : schema.at("/$defs/placement/properties/anchor/enum")) + { + assertNotNull(Placement.Anchor.of(word.textValue()), word.textValue()); + } + assertEquals(Placement.Anchor.values().length, schema.at("/$defs/placement/properties/anchor/enum").size()); + for(JsonNode word : schema.at("/$defs/placement/properties/align/enum")) + { + assertNotNull(Placement.Align.of(word.textValue()), word.textValue()); + } + for(JsonNode word : schema.at("/$defs/placement/properties/valign/enum")) + { + assertNotNull(Placement.Valign.of(word.textValue()), word.textValue()); + } + assertEquals(Diagnostics.ID.pattern(), schema.at("/$defs/id/pattern").textValue().replaceAll("^\\^|\\$$", "")); + Pattern length = Pattern.compile(schema.at("/$defs/length/pattern").textValue()); + Pattern physical = Pattern.compile(schema.at("/$defs/physical/pattern").textValue()); + for(String text : List.of("12.5mm", "0.5in", "9pt", "25%", "1dot", "-2mm", "0mm", "2000mm", "-100%", "0.001mm")) + { + assertTrue(length.matcher(text).find(), text); + assertNotNull(Length.parse(text), text); + } + for(String text : List.of("", "12", "mm", "1.2345mm", "1e3mm", " 1mm", "1 mm", "1MM", "01mm", ".5mm", "1.mm", "+1mm", "1px", "1.5dot", "123456mm")) + { + assertFalse(length.matcher(text).find(), text); + } + assertTrue(physical.matcher("4in").find() && physical.matcher("101.6mm").find() && physical.matcher("288pt").find()); + assertFalse(physical.matcher("50%").find() || physical.matcher("812dot").find() || physical.matcher("-4in").find()); + } + + + + /*************************************************************************** + ** The example scene of `renderer/README.md` is a scene: it validates and + ** renders on the 4 x 1 in stock at every resolution, with no findings. + ***************************************************************************/ + @Test + void theReadmeExampleIsAScene() throws Exception + { + String readme = Files.readString(Path.of("README.md")); + int start = readme.lastIndexOf('{', readme.indexOf("\"schema\": \"qqq-zpl-scene/2\"")); + assertTrue(start > 0, "the README has a version 2 example"); + String example = readme.substring(start, readme.indexOf("```", start)); + for(int dpi : Scene2.RESOLUTIONS) + { + for(String operation : List.of("VALIDATE", "RENDER")) + { + java.util.Map answer = com.kingsrook.qbits.renderer.RendererMain.process(SceneFixture.request(operation, example).put("targetDpi", dpi)); + assertEquals("OK", answer.get("outcome")); + assertEquals(List.of(), answer.get("diagnostics")); + } + } + } +} diff --git a/renderer/src/test/resources/scene2/cases/anchors.json b/renderer/src/test/resources/scene2/cases/anchors.json new file mode 100644 index 0000000..6e43e9c --- /dev/null +++ b/renderer/src/test/resources/scene2/cases/anchors.json @@ -0,0 +1,19 @@ +{ + "stock": {"widthMicrons": 50800, "heightMicrons": 25400, "topMicrons": 2000, "rightMicrons": 2000, "bottomMicrons": 2000, "leftMicrons": 2000, "dpi": 203}, + "scene": { + "schema": "qqq-zpl-scene/2", + "design": {"width": "2in", "height": "1in"}, + "elements": [ + {"id": "top-left", "type": "box", "anchor": "top-left", "x": "1mm", "y": "1mm", "width": "6mm", "height": "3mm", "thickness": "0.3mm"}, + {"id": "top-center", "type": "box", "anchor": "top-center", "y": "1mm", "width": "6mm", "height": "3mm", "thickness": "0.3mm"}, + {"id": "top-right", "type": "box", "anchor": "top-right", "x": "1mm", "y": "1mm", "width": "6mm", "height": "3mm", "thickness": "0.3mm"}, + {"id": "middle-left", "type": "box", "anchor": "middle-left", "x": "1mm", "width": "6mm", "height": "3mm", "thickness": "0.3mm"}, + {"id": "center", "type": "box", "anchor": "center", "width": "6mm", "height": "3mm", "fill": true}, + {"id": "left-of-center", "type": "box", "anchor": "center", "x": "-8mm", "y": "-2mm", "width": "4mm", "height": "2mm", "thickness": "0.3mm"}, + {"id": "middle-right", "type": "box", "anchor": "middle-right", "x": "1mm", "width": "6mm", "height": "3mm", "thickness": "0.3mm"}, + {"id": "bottom-left", "type": "box", "anchor": "bottom-left", "x": "1mm", "y": "1mm", "width": "6mm", "height": "3mm", "thickness": "0.3mm"}, + {"id": "bottom-center", "type": "box", "anchor": "bottom-center", "y": "1mm", "width": "6mm", "height": "3mm", "thickness": "0.3mm"}, + {"id": "bottom-right", "type": "box", "anchor": "bottom-right", "x": "1mm", "y": "1mm", "width": "6mm", "height": "3mm", "thickness": "0.3mm"} + ] + } +} diff --git a/renderer/src/test/resources/scene2/cases/groups.json b/renderer/src/test/resources/scene2/cases/groups.json new file mode 100644 index 0000000..50efde4 --- /dev/null +++ b/renderer/src/test/resources/scene2/cases/groups.json @@ -0,0 +1,19 @@ +{ + "stock": {"widthMicrons": 50800, "heightMicrons": 25400, "topMicrons": 2000, "rightMicrons": 2000, "bottomMicrons": 2000, "leftMicrons": 2000, "dpi": 203}, + "scene": { + "schema": "qqq-zpl-scene/2", + "design": {"width": "2in", "height": "1in"}, + "elements": [ + {"id": "panel", "type": "group", "anchor": "center", "width": "30mm", "height": "12mm", "children": [ + {"id": "border", "type": "box", "thickness": "0.3mm"}, + {"id": "inner", "type": "group", "anchor": "bottom-right", "x": "1mm", "y": "1mm", "width": "50%", "height": "50%", "children": [ + {"id": "dot", "type": "box", "anchor": "center", "width": "50%", "height": "50%", "fill": true}, + {"id": "corner", "type": "box", "width": "25%", "height": "25%", "thickness": "0.2mm"} + ]} + ]}, + {"id": "tag", "type": "group", "width": "20%", "height": "30%", "children": [ + {"id": "bar", "type": "box", "anchor": "middle-left", "width": "100%", "height": "1mm", "fill": true} + ]} + ] + } +} diff --git a/renderer/src/test/resources/scene2/cases/margins.json b/renderer/src/test/resources/scene2/cases/margins.json new file mode 100644 index 0000000..47d806f --- /dev/null +++ b/renderer/src/test/resources/scene2/cases/margins.json @@ -0,0 +1,14 @@ +{ + "stock": {"widthMicrons": 100000, "heightMicrons": 30000, "topMicrons": 2500, "rightMicrons": 3200, "bottomMicrons": 1700, "leftMicrons": 3000, "dpi": 203}, + "scene": { + "schema": "qqq-zpl-scene/2", + "design": {"width": "100mm", "height": "30mm"}, + "elements": [ + {"id": "edge", "type": "box", "x": "0mm", "y": "0mm", "width": "93.8mm", "height": "25.8mm", "thickness": "0.3mm"}, + {"id": "first", "type": "box", "width": "2mm", "height": "2mm", "fill": true}, + {"id": "last", "type": "box", "anchor": "bottom-right", "width": "2mm", "height": "2mm", "fill": true}, + {"id": "middle", "type": "box", "anchor": "center", "width": "20mm", "height": "8mm", "thickness": "0.5mm"}, + {"id": "side", "type": "box", "anchor": "middle-right", "x": "3mm", "width": "25%", "height": "50%", "fill": true} + ] + } +} diff --git a/renderer/src/test/resources/scene2/cases/rules.json b/renderer/src/test/resources/scene2/cases/rules.json new file mode 100644 index 0000000..baaadf4 --- /dev/null +++ b/renderer/src/test/resources/scene2/cases/rules.json @@ -0,0 +1,18 @@ +{ + "stock": {"widthMicrons": 50800, "heightMicrons": 25400, "topMicrons": 2000, "rightMicrons": 2000, "bottomMicrons": 2000, "leftMicrons": 2000, "dpi": 203}, + "scene": { + "schema": "qqq-zpl-scene/2", + "design": {"width": "2in", "height": "1in"}, + "elements": [ + {"id": "frame", "type": "box", "x": "1mm", "y": "1mm", "width": "30mm", "height": "8mm", "thickness": "0.5pt"}, + {"id": "block", "type": "box", "x": "2mm", "y": "2mm", "width": "4mm", "height": "4mm", "fill": true}, + {"id": "rule", "type": "box", "anchor": "bottom-left", "x": "1mm", "y": "2mm", "width": "60%", "height": "0.2mm", "thickness": "0.2mm"}, + {"id": "post", "type": "box", "anchor": "top-right", "x": "2mm", "y": "1mm", "width": "0.75pt", "height": "50%", "thickness": "0.75pt"}, + {"id": "thick", "type": "box", "anchor": "bottom-right", "x": "1mm", "y": "1mm", "width": "3mm", "height": "3mm", "thickness": "2mm"} + ], + "overrides": [ + {"when": {"dpi": [203]}, "set": {"frame": {"thickness": "1dot"}}}, + {"when": {"dpi": [600]}, "set": {"block": {"fill": false, "thickness": "0.5mm"}}} + ] + } +} diff --git a/renderer/src/test/resources/scene2/golden/anchors-203.png b/renderer/src/test/resources/scene2/golden/anchors-203.png new file mode 100644 index 0000000..026db40 Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/anchors-203.png differ diff --git a/renderer/src/test/resources/scene2/golden/anchors-203.zpl b/renderer/src/test/resources/scene2/golden/anchors-203.zpl new file mode 100644 index 0000000..130636e --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/anchors-203.zpl @@ -0,0 +1,5 @@ +^XA^PW406^LL203^LH0,0^CI28 +^FO24,24^GFA,1035,1035,45,FFFFFFFFFFFE000000000000000000000000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFCFFFFFFFFFFFE000000000000000000000000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFCC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CFFFFFFFFFFFE000000000000000000000000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFCFFFFFFFFFFFE000000000000000000000000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFC^FS +^FO24,78^GFA,1575,1575,45,0000000000000000000000001FFFFFFFC0000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000000000000000018000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000018000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000018000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000018000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000018000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000018000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000018000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000018000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000018000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000018000000C000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFE00000000000018000000C000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFCFFFFFFFFFFFE0000000000001FFFFFFFC000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFCC000000000060000000000001FFFFFFFC000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CC00000000006000000000000000000000000001FFFFFFFFFFFC00000000000000000000000000180000000000CFFFFFFFFFFFE000000000000000000000000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFCFFFFFFFFFFFE000000000000000000000000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFC^FS +^FO24,156^GFA,1035,1035,45,FFFFFFFFFFFE000000000000000000000000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFCFFFFFFFFFFFE000000000000000000000000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFCC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CC0000000000600000000000000000000000000180000000000C00000000000000000000000000180000000000CFFFFFFFFFFFE000000000000000000000000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFCFFFFFFFFFFFE000000000000000000000000001FFFFFFFFFFFC000000000000000000000000001FFFFFFFFFFFC^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/anchors-300.png b/renderer/src/test/resources/scene2/golden/anchors-300.png new file mode 100644 index 0000000..e1821a1 Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/anchors-300.png differ diff --git a/renderer/src/test/resources/scene2/golden/anchors-300.zpl b/renderer/src/test/resources/scene2/golden/anchors-300.zpl new file mode 100644 index 0000000..bf734e4 --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/anchors-300.zpl @@ -0,0 +1,5 @@ +^XA^PW600^LL300^LH0,0^CI28 +^FO36,36^GFA,2310,2310,66,FFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFF^FS +^FO36,114^GFA,3498,3498,66,0000000000000000000000000000000000003FFFFFFFFFFF8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFF8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFF8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFF8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C00000000078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0000000007800000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFC0000000000000000003C00000000078000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000003FFFFFFFFFFF8000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000003FFFFFFFFFFF8000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000003FFFFFFFFFFF8000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFF0000000000000003C0000000000000000003FFFFFFFFFFF8000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FF0000000000000003C0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003C000000000000000FFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFF^FS +^FO36,229^GFA,2310,2310,66,FFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FF0000000000000003C00000000000000000000000000000000000000078000000000000001E0000000000000000000000000000000000000003C000000000000000FFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007FFFFFFFFFFFFFFFFE0000000000000000000000000000000000000003FFFFFFFFFFFFFFFFF^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/anchors-600.png b/renderer/src/test/resources/scene2/golden/anchors-600.png new file mode 100644 index 0000000..63b2e47 Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/anchors-600.png differ diff --git a/renderer/src/test/resources/scene2/golden/anchors-600.zpl b/renderer/src/test/resources/scene2/golden/anchors-600.zpl new file mode 100644 index 0000000..47844bd --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/anchors-600.zpl @@ -0,0 +1,5 @@ +^XA^PW1200^LL600^LH0,0^CI28 +^FO72,72^GFA,9310,9310,133,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80^FS +^FO72,230^GFA,13965,13965,133,0000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000001FC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000FE00000000000000000001FC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000FE00000000000000000001FC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000FE00000000000000000001FC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000FE00000000000000000001FC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000FE00000000000000000001FC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FE00000000000000000000000000000003F80000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80^FS +^FO72,459^GFA,9310,9310,133,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FE00000000000000000000000000000003F80000000000000000000000000000000000000000000000000000000000000000000000000000003F80000000000000000000000000000000FE0000000000000000000000000000000000000000000000000000000000000000000000000000000FE00000000000000000000000000000003F80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/groups-203.png b/renderer/src/test/resources/scene2/golden/groups-203.png new file mode 100644 index 0000000..218b560 Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/groups-203.png differ diff --git a/renderer/src/test/resources/scene2/golden/groups-203.zpl b/renderer/src/test/resources/scene2/golden/groups-203.zpl new file mode 100644 index 0000000..ddd435d --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/groups-203.zpl @@ -0,0 +1,4 @@ +^XA^PW406^LL203^LH0,0^CI28 +^FO16,38^GFA,70,70,10,FFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFC0^FS +^FO83,54^GFA,2850,2850,30,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C000000000000000000000000000FFFFFFF8000000000000000000000006C000000000000000000000000000FFFFFFF8000000000000000000000006C000000000000000000000000000C0000018000000000000000000000006C000000000000000000000000000C0000018000000000000000000000006C000000000000000000000000000C0000018000000000000000000000006C000000000000000000000000000C0000018000000000000000000000006C000000000000000000000000000C0000018000000000000000000000006C000000000000000000000000000C0000018000000000000000000000006C000000000000000000000000000C0000018000000000000000000000006C000000000000000000000000000FFFFFFF8000000000000000000000006C000000000000000000000000000FFFFFFF8000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000003FFFFFFFFFFFFFF8000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006C00000000000000000000000000000000000000000000000000000000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/groups-300.png b/renderer/src/test/resources/scene2/golden/groups-300.png new file mode 100644 index 0000000..9982a5c Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/groups-300.png differ diff --git a/renderer/src/test/resources/scene2/golden/groups-300.zpl b/renderer/src/test/resources/scene2/golden/groups-300.zpl new file mode 100644 index 0000000..74c9786 --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/groups-300.zpl @@ -0,0 +1,4 @@ +^XA^PW600^LL300^LH0,0^CI28 +^FO24,56^GFA,154,154,14,FFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFC^FS +^FO123,79^GFA,6345,6345,45,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F00000000000000000000000000000000000000007FFFFFFFFFF800000000000000000000000000000000003C0F00000000000000000000000000000000000000007FFFFFFFFFF800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F000000000000000000000000000000000000000060000000001800000000000000000000000000000000003C0F00000000000000000000000000000000000000007FFFFFFFFFF800000000000000000000000000000000003C0F00000000000000000000000000000000000000007FFFFFFFFFF800000000000000000000000000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F0000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFF80000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/groups-600.png b/renderer/src/test/resources/scene2/golden/groups-600.png new file mode 100644 index 0000000..ae70f5f Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/groups-600.png differ diff --git a/renderer/src/test/resources/scene2/golden/groups-600.zpl b/renderer/src/test/resources/scene2/golden/groups-600.zpl new file mode 100644 index 0000000..fa164e4 --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/groups-600.zpl @@ -0,0 +1,4 @@ +^XA^PW1200^LL600^LH0,0^CI28 +^FO48,112^GFA,644,644,28,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8^FS +^FO246,159^GFA,25187,25187,89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003E00000000000000000007C00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000000000000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/margins-203.png b/renderer/src/test/resources/scene2/golden/margins-203.png new file mode 100644 index 0000000..cad383e Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/margins-203.png differ diff --git a/renderer/src/test/resources/scene2/golden/margins-203.zpl b/renderer/src/test/resources/scene2/golden/margins-203.zpl new file mode 100644 index 0000000..2e39fe8 --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/margins-203.zpl @@ -0,0 +1,3 @@ +^XA^PW799^LL240^LH0,0^CI28 +^FO24,20^GFA,19364,19364,94,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018FFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000003C000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/margins-300.png b/renderer/src/test/resources/scene2/golden/margins-300.png new file mode 100644 index 0000000..9ffbb05 Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/margins-300.png differ diff --git a/renderer/src/test/resources/scene2/golden/margins-300.zpl b/renderer/src/test/resources/scene2/golden/margins-300.zpl new file mode 100644 index 0000000..dfac2a0 --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/margins-300.zpl @@ -0,0 +1,3 @@ +^XA^PW1181^LL354^LH0,0^CI28 +^FO36,30^GFA,42256,42256,139,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0FFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F800000000000000000000000000000000000000000000000000000007E0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFE0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/margins-600.png b/renderer/src/test/resources/scene2/golden/margins-600.png new file mode 100644 index 0000000..1f6f87b Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/margins-600.png differ diff --git a/renderer/src/test/resources/scene2/golden/margins-600.zpl b/renderer/src/test/resources/scene2/golden/margins-600.zpl new file mode 100644 index 0000000..de2a312 --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/margins-600.zpl @@ -0,0 +1,4 @@ +^XA^PW2362^LL709^LH0,0^CI28 +^FO71,60^GFA,99997,99997,277,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FE^FS +^FO71,421^GFA,68696,68696,277,FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/rules-203.png b/renderer/src/test/resources/scene2/golden/rules-203.png new file mode 100644 index 0000000..0023030 Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/rules-203.png differ diff --git a/renderer/src/test/resources/scene2/golden/rules-203.zpl b/renderer/src/test/resources/scene2/golden/rules-203.zpl new file mode 100644 index 0000000..4113d08 --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/rules-203.zpl @@ -0,0 +1,4 @@ +^XA^PW406^LL203^LH0,0^CI28 +^FO24,24^GFA,3740,3740,44,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C80FFFFFFFE00000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000C800000000000000000000000000000000000000000000000000000000002000000000000000000000000000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C^FS +^FO24,156^GFA,1035,1035,45,000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000001FFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFC^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/rules-300.png b/renderer/src/test/resources/scene2/golden/rules-300.png new file mode 100644 index 0000000..ccec55b Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/rules-300.png differ diff --git a/renderer/src/test/resources/scene2/golden/rules-300.zpl b/renderer/src/test/resources/scene2/golden/rules-300.zpl new file mode 100644 index 0000000..26773e0 --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/rules-300.zpl @@ -0,0 +1,4 @@ +^XA^PW600^LL300^LH0,0^CI28 +^FO36,36^GFA,8190,8190,65,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000070FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C00FFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000070FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC00000000000000000000000000000000000000070FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070^FS +^FO36,229^GFA,2310,2310,66,0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFFF^FS +^XZ diff --git a/renderer/src/test/resources/scene2/golden/rules-600.png b/renderer/src/test/resources/scene2/golden/rules-600.png new file mode 100644 index 0000000..c91308e Binary files /dev/null and b/renderer/src/test/resources/scene2/golden/rules-600.png differ diff --git a/renderer/src/test/resources/scene2/golden/rules-600.zpl b/renderer/src/test/resources/scene2/golden/rules-600.zpl new file mode 100644 index 0000000..c27d035 --- /dev/null +++ b/renderer/src/test/resources/scene2/golden/rules-600.zpl @@ -0,0 +1,4 @@ +^XA^PW1200^LL600^LH0,0^CI28 +^FO72,72^GFA,32760,32760,130,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFE000000000000000007FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F00001FFFFFFFFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000FC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC0^FS +^FO72,459^GFA,9310,9310,133,0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFF80^FS +^XZ