{
  "name": "What one screen printing run spent on ink, screens and setup, over three denominators",
  "page": "https://otherworldlabs.com/ink-cost-per-print-job/",
  "endpoint": "https://otherworldlabs.com/ink-cost-per-print-job/api.json",
  "question": "For one screen printing run, measured by the printer who ran it, what did the ink, the newly burned screens and the setup time come to, and what is that spend over each impression pulled, each piece printed, each piece fit to sell, and each piece fit to sell on a repeat of the same art where the kept screens are not paid for a second time?",
  "answers": "one run at a time. The page keeps no history, this endpoint stores nothing, and neither of them holds a usage figure, a price, a rate or a quote.",
  "version": "2026-10-11",
  "licence": "The arithmetic is not ours to license. Reuse the formulas; the page is free and needs no attribution.",
  "method": "static description plus three worked runs; the same formulas run in the page in the reader's browser, with nothing sent anywhere",
  "currency": "none is named by this engine. Every money input and output is in whatever currency the caller's own figures are in, and the engine neither converts nor labels it. THE CLOSING ROUND (second pass), C-3: the page itself prints every money figure with a dollar sign, as a display convention only, the same mark for whatever currency the reader actually typed in - it is not a claim that the engine, this endpoint or the reader's own figures are in US dollars.",
  "supplies_no_figure": "This engine holds no grams-per-impression figure, no price for ink, no charge for burning a screen and no hourly rate, and it will not derive one from another field. Every one of those four is supplied by the caller or the figures that depend on it are returned null. A grams-per-impression figure depends on mesh, ink, coverage and squeegee, and the only source for it is the printer's own scale: weigh the ink, pull ten prints, weigh it again, divide the difference by ten. The two worked runs below do carry grams figures, because a worked run with no figures in it tests nothing; SPEC.md's ban on a default grams figure governs what a reader of the page is offered, and the page itself offers none - no placeholder, no shipped value, no worked example in its own copy. The figures in those two runs are arbitrary, are nobody's measurements, and are not a starting point for any shop.",
  "two_routes": "The caller picks `route` and the engine reads that route's figures only. route 1 is 'I weighed and invoiced each colour': the per-row fields gramsPerImpression, pricePerKg and screenCost are read and runGramsTotal, runInkCost and runScreenCost are ignored entirely. route 2 is 'I hold one run total': the three run fields are read and the three per-row money-and-grams fields are ignored entirely, so row1Grams to row8Grams and row1InkCost to row8InkCost all come back null. A run total does not say which colour used which share, and this engine will not split one evenly across the rows: that would be a figure it invented about colours it says differ by mesh, ink, coverage and squeegee. route 0 means no route is picked, and then nothing is costed: every money output is null. A state with both routes' fields filled is a state the page's own form cannot reach, and the engine returns the picked route's answer rather than adding the two; that is asserted over the whole sweep by the page's harness.",
  "rounding": {
    "inputs": "EVERY numeric input is read at its own field's fineness and then clamped into that field's declared range, not the counts alone: a count to a whole figure, a money field to the cent, a grams field to a tenth, each by the rule named below for its kind, with the half going toward positive infinity. So ratePerHour, gramsPerImpression, pricePerKg, screenCost, runGramsTotal, runInkCost and runScreenCost are treated exactly as the counts are. A typed 12.04 g is read as 12.0 and a typed 300.00 against a ceiling of 200.00 is read as 200.00, and each time a figure is shortened or clamped the engine says so in a notice that quotes what was typed and what it uses. A caller that works the formulas below from the figures as typed, rather than from the figures as read, will part from this engine on any input finer than its field's step or outside its field's range; that divergence is the read-rounding and the clamp, and it is stated here because round 1's reimplementation found it stated for the counts only. The notice quotes what was typed exactly as typed and every figure the page chose at the field's own fineness - 'you typed 22.004 ... so it uses 22.00', 'at most 200.00, so it uses 200.00', 'you typed 12.04 ... so it uses 12.0' - which round 2 (m3) found printing 22, 200 and 12, the one sentence on the page whose whole subject is the fineness being the one that did not print to it. The figures the engine works with did not change. Round 3 (B2) found the other half of that promise broken in the two CLAMP sentences, which quoted the already-shortened figure under the words 'you typed': a reader who typed 300.004 into a field that keeps the cent was told 'you typed 300.00', which they did not, and a reader who typed 300 flat was told the same. Both clamp sentences then quoted what was typed exactly as typed, and both claim ledgers recorded that figure as what was typed. ROUND 4 (B2) FOUND THE REST OF IT: a field both shortened AND clamped drew TWO sentences, and the first of them said 'so it uses' about a figure the engine never holds - 'you typed 0.4 ... so it uses 0' for a count whose range starts at 1, followed by 'so it uses 1' in the next line, so a reader was told in order that the page uses 0 pieces printed and that it uses 1. 'So it uses X' is a statement about what the engine works from, so there is now ONE of it per field and X is always the figure the engine returns. When both steps happen the one sentence names both in order and ends on the figure it works from: 'The hourly rate you pay for setup: you typed 300.004, which this page reads to the cent as 300.00 and then takes at most 200.00, so it uses 200.00.' One claim is recorded for it, the typed figure against the limit, with the reading in the middle declared in `also` as a figure the claim is not about. A field shortened and not clamped keeps its own sentence unchanged, and a field clamped and not shortened keeps its own: 'you typed 300 and this page takes at most 200.00, so it uses 200.00.' The clamp works at BOTH ENDS and every example above is a ceiling, so here is the floor: a typed 0 in Pieces printed in this run, whose range starts at 1, is read as 1, and the notice reads 'Pieces printed in this run: you typed 0 and this page takes at least 1, so it uses 1.'",
    "counts": "whole. Pieces printed, pieces spoiled and setup minutes are read with Math.round, which puts a half toward positive infinity (240.5 becomes 241), are then clamped into the range named beside each field, and every one of those three says a shortening or a clamp out loud in a notice that quotes what was typed and what the engine uses. TWO FURTHER COUNT INPUTS ARE READ THE SAME WAY AND SAY NOTHING: screenCount, rounded and then held between 1 and 8, and the per-row flag screens[].alreadyBurned, rounded and read as yes at 1 or above. Neither is reachable as a loose figure through the form - the row count is a select and the rack answer is a radio pair - so no reader is owed a notice for either, but a caller of this endpoint can pass screenCount 12 and get eight colour rows and 800 impressions on a 100-piece run with no notice at all, and that silence is stated here rather than left to be found (round 3, m2). The sentence this replaces named the colour-row count and 'the two screen tallies': screensBurnedForThisRun and screensKeptOnTheRack are OUTPUTS derived from the rack flags, are read from no caller and have no range named beside them, and the 0/1 input the rack actually has was missing from the list (round 3, m3). ROUND 4 (m4) ADDS THE FORM'S SIDE OF THE SAME SILENCE: screenCount is what decides how many colour rows are READ, so a reader who fills eight rows and then sets the select back to three has five rows of typed figures that are hidden, not read, not costed and not counted, with no notice about it, and a caller who passes eight rows and screenCount 3 gets the same. The figures are not discarded - they stay in the fields and are read again the moment the count goes back up - and the input block now says so in its own words beside the select, which is where the reader is when they change it.",
    "money": "two decimals for every run-level figure, by round2(x) = Math.round(x * 100) / 100 on the double, which puts a half toward positive infinity rather than away from zero: -0.125 comes out as -0.12 and not as -0.13, and -0.005 comes out as -0.00 and not as -0.01. Negative money is reachable in exactly one place in this engine, the three remainder outputs, which are signed; every other money output is nought or more. round2 is applied where each figure is defined and again to every sum built out of figures already rounded, so no line is worked from a longer number than the one the page prints.",
    "per_piece_money": "four decimals for every per-piece figure, by round4(x) = Math.round(x * 10000) / 10000 on the double, which puts a half toward positive infinity exactly as round2 does, at a positive half and at a negative one alike (-0.00005 comes out as -0.0000, not as -0.0001). Four and not two, because one invoice divided over hundreds of pieces lands well inside a cent and a screens line of 18.00 over 720 impressions is 0.025: rounded to the cent it would be wrong by a fifth of itself. Because each figure is rounded where it is defined, a column's three lines added by hand can land a hundredth of a cent away from that column's own foot, which is rounded on its own; the figure returned for the foot is the one computed by the line that defines it, and the difference is returned as its own output rather than hidden.",
    "grams": "one decimal, by round1(x) = Math.round(x * 10) / 10 on the double, half toward positive infinity, for both the per-impression figure and every grams total built from it.",
    "percentages": "there are none. This engine returns no percentage and the page prints none: every output is money, grams, a count of pieces, a count of impressions, a count of screens or a flag."
  },
  "inputs": [
    {
      "name": "route",
      "type": "integer",
      "unit": "none",
      "required": true,
      "range": "0 to 2",
      "note": "0 no route picked, and then every money output is null; 1 weighed and invoiced per colour; 2 one run total. Nothing is picked by default, because the two routes ask for different figures and the engine will not guess which the caller holds. A fraction is rounded and a figure outside the range is clamped."
    },
    {
      "name": "quantity",
      "type": "integer",
      "unit": "pieces",
      "required": true,
      "range": "1 to 5000",
      "note": "Every piece that went through the press on this run, sellable or not. Blank leaves the impressions count, every per-piece figure and the ink-per-row figures null."
    },
    {
      "name": "spoiled",
      "type": "integer",
      "unit": "pieces",
      "required": true,
      "range": "0 to 500",
      "note": "The caller's own count off the press. Nought is a real answer, and the answer a caller quoting a run that has not happened yet gives, in which case piecesFitToSell equals quantity. A figure larger than quantity is two counts that do not sit one inside the other: piecesFitToSell comes back null, a notice names both figures and their difference, and the two outputs that divide by it come back null rather than negative."
    },
    {
      "name": "setupMinutes",
      "type": "integer",
      "unit": "minutes",
      "required": true,
      "range": "0 to 480",
      "note": "The clock over press setup and registration, paid once for the run however long the run is. Nought is a real answer. Blank leaves setupCost, totalRun and every per-piece figure null."
    },
    {
      "name": "ratePerHour",
      "type": "money",
      "unit": "money an hour",
      "required": true,
      "range": "0 to 200.00",
      "note": "What that time costs the caller. Theirs; this engine has none and proposes none. Nought is a real answer and means no setup was charged to the job. Blank leaves setupCost null, which leaves totalRun null."
    },
    {
      "name": "screenCount",
      "type": "integer",
      "unit": "colour rows",
      "required": true,
      "range": "1 to 8",
      "note": "One colour pulled at one location is one row. Two colours at one location are two rows and one colour at two locations is two rows, because each is a screen and a pull of its own. Rows past this count are ignored; blank falls back to the length of the screens array."
    },
    {
      "name": "screens[].mixName",
      "type": "text",
      "unit": "none",
      "required": false,
      "range": "up to 40 characters",
      "note": "The mix or colour name in the caller's own words, as their mix log names it. Kept as text, trimmed of repeated whitespace, and used in no calculation. It matches no colour and names no ink brand."
    },
    {
      "name": "screens[].location",
      "type": "text",
      "unit": "none",
      "required": false,
      "range": "up to 24 characters",
      "note": "Where that colour is pulled. Kept as text and used in no calculation. The engine counts the distinct non-empty locations and NOTHING IS LIMITED: eight rows can name eight places and every one of them is read, costed and printed. Round 3 (m4) removed a notice that said the page carried at most six, because no part of the page, its printed sheet or its kit spec was laid out for six and the sentence announced a limit the engine did not have."
    },
    {
      "name": "screens[].gramsPerImpression",
      "type": "number",
      "unit": "grams an impression",
      "required": false,
      "range": "0.0 to 60.0",
      "note": "Read on route 1 only. The caller's own weighing, and there is no default anywhere in this engine or on the page: it depends on mesh, ink, coverage and squeegee. Blank leaves that row's grams and ink null and leaves the run's ink null with it, because a run with one row unmeasured is not a costed run."
    },
    {
      "name": "screens[].pricePerKg",
      "type": "money",
      "unit": "money a kilogram",
      "required": false,
      "range": "0 to 200.00",
      "note": "Read on route 1 only. The caller's own supplier invoice. Nought is a real answer. Blank leaves that row's ink cost null."
    },
    {
      "name": "screens[].screenCost",
      "type": "money",
      "unit": "money a screen",
      "required": false,
      "range": "0 to 60.00",
      "note": "Read on route 1 only, on every live row regardless of its alreadyBurned flag - a kept row's figure is still read, shortened and clamped the same way, and the page still announces a shortening or a clamp on it - but only SUMMED into screenCostRun on a row whose alreadyBurned is false; a kept row's own screenCost never adds to the run's screens spend. What coating, exposing, washing out and reclaiming that screen costs the caller, or the flat charge their burn shop makes. Blank on a row that needs summing leaves screenCostRun null; blank on a kept row costs nothing because nothing of that row is summed."
    },
    {
      "name": "screens[].alreadyBurned",
      "type": "integer",
      "unit": "none",
      "required": false,
      "range": "0 to 1",
      "note": "1 if the screen was on the rack from an earlier run of the same art, so this run paid nothing to make it. Read on BOTH routes, because the rack is a fact the caller reads off whether or not they split their invoices. Anything at 1 or above is read as yes; blank and nought are read as no."
    },
    {
      "name": "runGramsTotal",
      "type": "number",
      "unit": "grams",
      "required": false,
      "range": "0.0 to 100000.0",
      "note": "Read on route 2 only. The whole run's weigh-in less its weigh-out, across every screen. The ceiling is the largest run route 1 itself can reach at a plausible lay-down - five thousand pieces, eight colours, two and a half grams an impression - so a figure above it is the weight of a stock of ink rather than of the ink this run used."
    },
    {
      "name": "runInkCost",
      "type": "money",
      "unit": "money",
      "required": false,
      "range": "0 to 20000.00",
      "note": "Read on route 2 only. The whole run's ink spend as one combined invoice or till total. The ceiling is the grams ceiling priced at the top of pricePerKg's range, a hundred kilograms at 200.00 a kilogram."
    },
    {
      "name": "runScreenCost",
      "type": "money",
      "unit": "money",
      "required": false,
      "range": "0 to 480.00",
      "note": "Read on route 2 only. What the screens newly burned for this run cost in total. Nought is a real answer and means the screens cost this run nothing - whether every screen was kept, or a screen was burned at no charge to this job; BLANK is not nought, and leaves screenCostRun null. ROUND 4 (m5): this note used to gloss nought as 'means every screen was kept', which is a fact about the rack that this engine does not check. The disagreement below fires only where the figure is ABOVE nought, so a caller can pass runScreenCost 0 with two rows flagged as burned for this run and the engine returns screensBurnedForThisRun 2, a screens line of 0.00 and screensDisagree false, with nothing contradicted. The one-sided rule is deliberate - a screen can be burned at no charge - and the gloss now says what the figure means rather than what the rack did. Where this figure is above nought and every colour row says the screen was already on the rack, the two readings do not agree: screensDisagree comes back true, screenCostRun and totalRun come back null, and the run is not costed until one of the two is changed. The ceiling is the eight-screen ceiling of screenCost."
    }
  ],
  "outputs": [
    {
      "name": "route",
      "unit": "none",
      "formula": "the route as read: 0, 1 or 2"
    },
    {
      "name": "impressions",
      "unit": "impressions",
      "formula": "quantity x screenCount; null while quantity is blank. One pull of one screen onto one piece."
    },
    {
      "name": "piecesFitToSell",
      "unit": "pieces",
      "formula": "quantity - spoiled; null while either is blank, and null rather than negative where spoiled is larger"
    },
    {
      "name": "screensBurnedForThisRun",
      "unit": "screens",
      "formula": "the count of live colour rows whose alreadyBurned is not set; read on both routes"
    },
    {
      "name": "screensKeptOnTheRack",
      "unit": "screens",
      "formula": "the count of live colour rows whose alreadyBurned is set; screensBurnedForThisRun + screensKeptOnTheRack = screenCount"
    },
    {
      "name": "inkGramsRun",
      "unit": "grams",
      "formula": "route 1: round1(sum over rows of round1(gramsPerImpression x quantity)), null unless rowsWaiting is 0, which is to say unless EVERY live row is complete: a grams figure and a price on every live row, and a screen charge on every live row whose mesh is being burned this time. A row short of its screen charge is a row still being filled in, so the run's own ink total waits with it, even though the ink arithmetic does not read a screen charge. route 2: runGramsTotal as given. route 0: null"
    },
    {
      "name": "inkCostRun",
      "unit": "money",
      "formula": "route 1: round2(sum over rows of round2(rowGrams / 1000 x pricePerKg)), null unless rowsWaiting is 0, on the same reading of complete as inkGramsRun above: the screen charge counts. The per-row figures row1InkCost to row8InkCost do NOT wait on the screen charge and print from a row's own grams and price alone. route 2: runInkCost as given. route 0: null"
    },
    {
      "name": "screenCostRun",
      "unit": "money",
      "formula": "route 1: round2(sum of screenCost over the rows whose alreadyBurned is not set), null while any such row has no figure, and 0 where every screen was kept. route 2: runScreenCost as given, and null where it disagrees with the rack flags. route 0: null"
    },
    {
      "name": "setupCost",
      "unit": "money",
      "formula": "round2(setupMinutes / 60 x ratePerHour); null while either is blank, and null while route is 0, because no money figure is built at all until a route is picked. The same arithmetic on route 1 and route 2, because setup does not depend on how the ink was measured"
    },
    {
      "name": "totalRun",
      "unit": "money",
      "formula": "round2(inkCostRun + screenCostRun + setupCost); null while any of the three is null"
    },
    {
      "name": "repeatTotal",
      "unit": "money",
      "formula": "round2(inkCostRun + setupCost); what the same art pulled again would spend, where the screens of this run are kept and not paid for a second time. null while either line is null"
    },
    {
      "name": "costPerImpression",
      "unit": "money an impression",
      "formula": "round4(totalRun / impressions); null while either is null or impressions is nought"
    },
    {
      "name": "costPerPiecePrinted",
      "unit": "money a piece",
      "formula": "round4(totalRun / quantity); null while either is null"
    },
    {
      "name": "costPerGoodPiece",
      "unit": "money a piece",
      "formula": "round4(totalRun / piecesFitToSell); null while either is null, and null where piecesFitToSell is nought, which is the division this engine names rather than performs"
    },
    {
      "name": "costPerGoodPieceReorder",
      "unit": "money a piece",
      "formula": "round4(repeatTotal / piecesFitToSell); the same art on a repeat of the same quantity with the same spoilage, the kept screens not paid again"
    },
    {
      "name": "spoilageWedge",
      "unit": "money a piece",
      "formula": "round4(costPerGoodPiece - costPerPiecePrinted); what the spoiled pieces add to each piece fit to sell. Nought where nothing was spoiled, and never negative, because piecesFitToSell is never larger than quantity"
    },
    {
      "name": "remainderPerPiecePrinted",
      "unit": "money a piece",
      "formula": "round4(round4(inkPerPiecePrinted + screensPerPiecePrinted + setupPerPiecePrinted) - costPerPiecePrinted), where each of the three is round4(that line / quantity). SIGNED, and the one output of this engine that can be negative. It is the hundredth of a cent that rounding each figure where it is defined can leave between a column's three lines and its own foot"
    },
    {
      "name": "remainderPerGoodPiece",
      "unit": "money a piece",
      "formula": "the same over piecesFitToSell. Signed"
    },
    {
      "name": "remainderPerGoodPieceReorder",
      "unit": "money a piece",
      "formula": "the same over piecesFitToSell for the repeat column, where the screens line is nought. Signed"
    },
    {
      "name": "rowsWaiting",
      "unit": "colour rows",
      "formula": "route 1: the count of live colour rows that still want a figure of their own, a row wanting one while its grams figure, its price per kilogram, or - on a row being burned this time - its screen charge is blank. route 2 and route 0: 0, because no per-colour figure is read on either"
    },
    {
      "name": "screensDisagree",
      "unit": "none",
      "formula": "true on route 2 where runScreenCost is above nought and every live colour row says the screen was already on the rack; false otherwise. The engine withholds screenCostRun and totalRun rather than charging the run for screens it has been told were kept"
    },
    {
      "name": "row1Grams",
      "unit": "grams",
      "formula": "route 1: round1(gramsPerImpression x quantity) for colour row 1. route 2: null, always, because a run total does not say which colour used which share"
    },
    {
      "name": "row1InkCost",
      "unit": "money",
      "formula": "route 1: round2(row1Grams / 1000 x pricePerKg) for colour row 1. route 2: null, always"
    },
    {
      "name": "row2Grams",
      "unit": "grams",
      "formula": "as row1Grams, for colour row 2"
    },
    {
      "name": "row2InkCost",
      "unit": "money",
      "formula": "as row1InkCost, for colour row 2"
    },
    {
      "name": "row3Grams",
      "unit": "grams",
      "formula": "as row1Grams, for colour row 3"
    },
    {
      "name": "row3InkCost",
      "unit": "money",
      "formula": "as row1InkCost, for colour row 3"
    },
    {
      "name": "row4Grams",
      "unit": "grams",
      "formula": "as row1Grams, for colour row 4; null while screenCount is below 4"
    },
    {
      "name": "row4InkCost",
      "unit": "money",
      "formula": "as row1InkCost, for colour row 4"
    },
    {
      "name": "row5Grams",
      "unit": "grams",
      "formula": "as row1Grams, for colour row 5"
    },
    {
      "name": "row5InkCost",
      "unit": "money",
      "formula": "as row1InkCost, for colour row 5"
    },
    {
      "name": "row6Grams",
      "unit": "grams",
      "formula": "as row1Grams, for colour row 6"
    },
    {
      "name": "row6InkCost",
      "unit": "money",
      "formula": "as row1InkCost, for colour row 6"
    },
    {
      "name": "row7Grams",
      "unit": "grams",
      "formula": "as row1Grams, for colour row 7"
    },
    {
      "name": "row7InkCost",
      "unit": "money",
      "formula": "as row1InkCost, for colour row 7"
    },
    {
      "name": "row8Grams",
      "unit": "grams",
      "formula": "as row1Grams, for colour row 8"
    },
    {
      "name": "row8InkCost",
      "unit": "money",
      "formula": "as row1InkCost, for colour row 8"
    }
  ],
  "formula": [
    "impressions = quantity x screenCount",
    "piecesFitToSell = quantity - spoiled",
    "rowNGrams = round1(gramsPerImpressionN x quantity)                        [route 1]",
    "rowNInkCost = round2(rowNGrams / 1000 x pricePerKgN)                      [route 1]",
    "inkCostRun = round2(sum of rowNInkCost)                                   [route 1, and only while rowsWaiting = 0; see the output note]",
    "inkCostRun = runInkCost                                                   [route 2]",
    "screenCostRun = round2(sum of screenCostN over rows not already burned)   [route 1]",
    "screenCostRun = runScreenCost                                             [route 2]",
    "setupCost = round2(setupMinutes / 60 x ratePerHour)                       [route 1 and route 2 only; null on route 0]",
    "totalRun = round2(inkCostRun + screenCostRun + setupCost)",
    "repeatTotal = round2(inkCostRun + setupCost)",
    "costPerImpression = round4(totalRun / impressions)",
    "costPerPiecePrinted = round4(totalRun / quantity)",
    "costPerGoodPiece = round4(totalRun / piecesFitToSell)",
    "costPerGoodPieceReorder = round4(repeatTotal / piecesFitToSell)",
    "spoilageWedge = round4(costPerGoodPiece - costPerPiecePrinted)",
    "remainder(den) = round4(round4(inkCostRun/den) + round4(screenCostRun/den) + round4(setupCost/den) - round4(totalRun/den))"
  ],
  "identities": [
    "screensBurnedForThisRun + screensKeptOnTheRack = screenCount",
    "piecesFitToSell + spoiled = quantity, wherever piecesFitToSell is returned",
    "impressions is at least quantity, because screenCount is at least 1",
    "totalRun = inkCostRun + screenCostRun + setupCost, to the cent",
    "repeatTotal = totalRun - screenCostRun, to the cent",
    "costPerGoodPiece is at least costPerPiecePrinted, because piecesFitToSell is never larger than quantity",
    "costPerPiecePrinted = costPerImpression x screenCount, to within half a hundredth of a cent PER COLOUR ROW of rounding: costPerImpression is fixed at four places where it is defined, so multiplying it out by screenCount carries screenCount halves of a hundredth of a cent with it - nought to four hundredths of a cent at eight rows. The product is the per-impression reading multiplied out, not a second definition of the per-piece reading, which is round4(totalRun / quantity). An earlier version of this line said 'to within a hundredth of a cent' flat, which was false in 850 of the 2,857 states of this page's own sweep that print both figures, every one of them at four colour rows or more (round 2, B4: the engine was right and this sentence was wrong)",
    "spoilageWedge = costPerGoodPiece - costPerPiecePrinted exactly, both operands as returned",
    "costPerGoodPieceReorder is at most costPerGoodPiece, because the repeat drops the screens line and keeps the other two",
    "costPerGoodPiece - costPerGoodPieceReorder is round4(screenCostRun / piecesFitToSell), to within a hundredth of a cent",
    "every remainder is nought where the three lines and the foot of a column happen to round the same way, and is otherwise one or two hundredths of a cent, of either sign",
    "inkCostRun, screenCostRun, setupCost, totalRun, repeatTotal and all five per-piece readings are nought or more; the three remainders are the only signed outputs"
  ],
  "example": {
    "note": "the run the page ships filled in, which is a shape rather than a price: the quantity, the spoilage, the colour rows and the setup clock are there, and no route is picked and no money figure is given, because a grams-per-impression figure and a rate are the two things only the printer holds. So the page's own first screen shows the counts and says what it is waiting for, and so does this.",
    "inputs": {
      "route": 0,
      "quantity": 240,
      "spoiled": 6,
      "setupMinutes": 35,
      "ratePerHour": null,
      "screenCount": 3,
      "runGramsTotal": null,
      "runInkCost": null,
      "runScreenCost": null,
      "screens": [
        {
          "mixName": "Royal blue, 3 parts base to 1 white",
          "location": "Left chest",
          "gramsPerImpression": null,
          "pricePerKg": null,
          "screenCost": null,
          "alreadyBurned": 0
        },
        {
          "mixName": "Cream white, straight from the tub",
          "location": "Left chest",
          "gramsPerImpression": null,
          "pricePerKg": null,
          "screenCost": null,
          "alreadyBurned": 0
        },
        {
          "mixName": "Royal blue, 3 parts base to 1 white",
          "location": "Full back",
          "gramsPerImpression": null,
          "pricePerKg": null,
          "screenCost": null,
          "alreadyBurned": 1
        }
      ]
    },
    "outputs": {
      "route": 0,
      "impressions": 720,
      "piecesFitToSell": 234,
      "screensBurnedForThisRun": 2,
      "screensKeptOnTheRack": 1,
      "inkGramsRun": null,
      "inkCostRun": null,
      "screenCostRun": null,
      "setupCost": null,
      "totalRun": null,
      "repeatTotal": null,
      "costPerImpression": null,
      "costPerPiecePrinted": null,
      "costPerGoodPiece": null,
      "costPerGoodPieceReorder": null,
      "spoilageWedge": null,
      "remainderPerPiecePrinted": null,
      "remainderPerGoodPiece": null,
      "remainderPerGoodPieceReorder": null,
      "rowsWaiting": 0,
      "screensDisagree": false,
      "row1Grams": null,
      "row1InkCost": null,
      "row2Grams": null,
      "row2InkCost": null,
      "row3Grams": null,
      "row3InkCost": null,
      "row4Grams": null,
      "row4InkCost": null,
      "row5Grams": null,
      "row5InkCost": null,
      "row6Grams": null,
      "row6InkCost": null,
      "row7Grams": null,
      "row7InkCost": null,
      "row8Grams": null,
      "row8InkCost": null
    },
    "reading": "Three screens over two locations on two hundred and forty shirts is seven hundred and twenty impressions, and six spoiled leaves two hundred and thirty-four fit to sell. Those four counts need no money at all. Everything else waits, and rowsWaiting is nought rather than three because no route is picked and no per-colour figure is read until one is."
  },
  "worked_per_colour": {
    "note": "the same run costed on route 1, with figures chosen to show the shape of the arithmetic: twelve grams of royal blue at 38.00 a kilogram on the left chest, eight and a half grams of cream white at 31.50, twenty-one and four tenths grams of the same blue on the full back, nine the screen, thirty-five minutes at 22.00 an hour, and the back screen already on the rack. Nobody supplied these numbers and they are nobody's measurements: they are here so a caller can check an implementation, and the one figure the page refuses to offer a reader - grams an impression - is a figure only the shop that weighed it holds. Nothing here is a figure for anybody's shop, including the shop in this example, which does not exist.",
    "inputs": {
      "route": 1,
      "quantity": 240,
      "spoiled": 6,
      "setupMinutes": 35,
      "ratePerHour": 22,
      "screenCount": 3,
      "runGramsTotal": null,
      "runInkCost": null,
      "runScreenCost": null,
      "screens": [
        {
          "mixName": "Royal blue, 3 parts base to 1 white",
          "location": "Left chest",
          "gramsPerImpression": 12,
          "pricePerKg": 38,
          "screenCost": 9,
          "alreadyBurned": 0
        },
        {
          "mixName": "Cream white, straight from the tub",
          "location": "Left chest",
          "gramsPerImpression": 8.5,
          "pricePerKg": 31.5,
          "screenCost": 9,
          "alreadyBurned": 0
        },
        {
          "mixName": "Royal blue, 3 parts base to 1 white",
          "location": "Full back",
          "gramsPerImpression": 21.4,
          "pricePerKg": 38,
          "screenCost": 9,
          "alreadyBurned": 1
        }
      ]
    },
    "outputs": {
      "route": 1,
      "impressions": 720,
      "piecesFitToSell": 234,
      "screensBurnedForThisRun": 2,
      "screensKeptOnTheRack": 1,
      "inkGramsRun": 10056,
      "inkCostRun": 368.87,
      "screenCostRun": 18,
      "setupCost": 12.83,
      "totalRun": 399.7,
      "repeatTotal": 381.7,
      "costPerImpression": 0.5551,
      "costPerPiecePrinted": 1.6654,
      "costPerGoodPiece": 1.7081,
      "costPerGoodPieceReorder": 1.6312,
      "spoilageWedge": 0.0427,
      "remainderPerPiecePrinted": 0.0001,
      "remainderPerGoodPiece": 0,
      "remainderPerGoodPieceReorder": 0,
      "rowsWaiting": 0,
      "screensDisagree": false,
      "row1Grams": 2880,
      "row1InkCost": 109.44,
      "row2Grams": 2040,
      "row2InkCost": 64.26,
      "row3Grams": 5136,
      "row3InkCost": 195.17,
      "row4Grams": null,
      "row4InkCost": null,
      "row5Grams": null,
      "row5InkCost": null,
      "row6Grams": null,
      "row6InkCost": null,
      "row7Grams": null,
      "row7InkCost": null,
      "row8Grams": null,
      "row8InkCost": null
    },
    "reading": "Three hundred and sixty-eight eighty-seven of ink, eighteen of screens, twelve eighty-three of setup: 399.70 for the run, and the ink in it is ten kilograms and fifty-six grams at the two prices typed. The closing round fixed this clause: it opened 'Ten kilograms of ink, eighteen of screens, twelve eighty-three of setup: 399.70 for the run', which puts a weight where the other two terms of a sum are money and does not add up to the figure after the colon. Over the pieces printed that is 1.6654 and over the pieces fit to sell 1.7081, so the six spoiled shirts add 0.0427 to every shirt that can be sold. Pull the same art again and the two kept screens cost nothing, which takes it to 1.6312. remainderPerPiecePrinted is 0.0001, the hundredth of a cent that rounding the three lines and the foot of that column separately leaves behind, and the page prints that line rather than hiding it."
  },
  "worked_run_total": {
    "note": "the same run on route 2, by a shop that weighs the ink once and gets one invoice: ten thousand and fifty grams across the run, 365.50 of ink, 18.00 of screens. The colour rows keep their names, their locations and their rack flags, and every per-colour figure comes back null.",
    "inputs": {
      "route": 2,
      "quantity": 240,
      "spoiled": 6,
      "setupMinutes": 35,
      "ratePerHour": 22,
      "screenCount": 3,
      "runGramsTotal": 10050,
      "runInkCost": 365.5,
      "runScreenCost": 18,
      "screens": [
        {
          "mixName": "Royal blue, 3 parts base to 1 white",
          "location": "Left chest",
          "gramsPerImpression": null,
          "pricePerKg": null,
          "screenCost": null,
          "alreadyBurned": 0
        },
        {
          "mixName": "Cream white, straight from the tub",
          "location": "Left chest",
          "gramsPerImpression": null,
          "pricePerKg": null,
          "screenCost": null,
          "alreadyBurned": 0
        },
        {
          "mixName": "Royal blue, 3 parts base to 1 white",
          "location": "Full back",
          "gramsPerImpression": null,
          "pricePerKg": null,
          "screenCost": null,
          "alreadyBurned": 1
        }
      ]
    },
    "outputs": {
      "route": 2,
      "impressions": 720,
      "piecesFitToSell": 234,
      "screensBurnedForThisRun": 2,
      "screensKeptOnTheRack": 1,
      "inkGramsRun": 10050,
      "inkCostRun": 365.5,
      "screenCostRun": 18,
      "setupCost": 12.83,
      "totalRun": 396.33,
      "repeatTotal": 378.33,
      "costPerImpression": 0.5505,
      "costPerPiecePrinted": 1.6514,
      "costPerGoodPiece": 1.6937,
      "costPerGoodPieceReorder": 1.6168,
      "spoilageWedge": 0.0423,
      "remainderPerPiecePrinted": 0,
      "remainderPerGoodPiece": 0,
      "remainderPerGoodPieceReorder": 0,
      "rowsWaiting": 0,
      "screensDisagree": false,
      "row1Grams": null,
      "row1InkCost": null,
      "row2Grams": null,
      "row2InkCost": null,
      "row3Grams": null,
      "row3InkCost": null,
      "row4Grams": null,
      "row4InkCost": null,
      "row5Grams": null,
      "row5InkCost": null,
      "row6Grams": null,
      "row6InkCost": null,
      "row7Grams": null,
      "row7InkCost": null,
      "row8Grams": null,
      "row8InkCost": null
    },
    "reading": "The cost lines, the total and the three per-piece columns are exact on this route, because they are built from figures the printer read off a scale and an invoice. What is missing is only the split: every rowNGrams and rowNInkCost is null, and the page prints beside each colour row that its share is not known from a run total. An even third of the run would have been an invented figure, and this engine does not return one."
  },
  "not_covered": [
    "how much ink a print takes. There is no usage figure anywhere in this engine and none is derived; the only field for it is the caller's own weighing.",
    "what a print, a shirt or a run is worth, what to quote, what to charge and what margin to add.",
    "any claim of accuracy, exactness or professional pricing. The outputs are divisions of the caller's own measurements and are exactly as right as those are.",
    "colour. A mix name is text. This engine matches no colour, converts no colour system, names no ink brand and holds no recipe.",
    "any comparison with any named software, supply house or printer.",
    "garments, blanks, thread, transfers, packing, shipping, rent, electricity, screens reclaimed for a different job, or any other cost of a shop. The three lines are ink, newly burned screens and setup minutes, and the page says so.",
    "which colour used which share of a run total. On route 2 that is returned as null and is never split evenly.",
    "whether the art will ever run again. The repeat column is arithmetic on an assumption the caller makes, at the same quantity and the same spoilage.",
    "tax of any kind, in any country.",
    "the printer's own records. This endpoint stores nothing and the page keeps no history."
  ],
  "no_advice": "This endpoint returns arithmetic on figures supplied to it. It recommends no price, no rate, no ink and no ink usage, it makes no claim about what a print is worth, and it gives no business, financial, legal or professional advice of any kind."
}
