[
  {
    "path": ".devcontainer/Dockerfile",
    "content": "# FROM mcr.microsoft.com/devcontainers/universal:2\nFROM mcr.microsoft.com/devcontainers/base:ubuntu\n\nRUN apt-get update && apt-get install -y \\\n    ripgrep \\\n    pigz\n\nUSER vscode\nENV ELAN_HOME=\"/home/vscode/.elan\"\nENV PATH=\"${ELAN_HOME}/bin:${PATH}\"\nRUN sh -c 'curl https://elan.lean-lang.org/elan-init.sh -sSf | sh -s -- -y'\nRUN elan self update\nRUN elan default leanprover/lean4:stable\nRUN lean --version\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n    \"build\": {\n        \"dockerfile\": \"Dockerfile\"\n    },\n    \"customizations\": {\n        \"vscode\": {\n            \"extensions\": [\n                \"leanprover.lean4\"\n            ]\n        }\n    }\n}\n\n"
  },
  {
    "path": ".gitignore",
    "content": ".lake"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"cSpell.words\": [\n        \"biochamber\",\n        \"Bioflux\",\n        \"biosulfur\",\n        \"electromagnetics\",\n        \"Factorio\",\n        \"foldl\",\n        \"Functorio\",\n        \"jellynut\",\n        \"nauvis\",\n        \"pentapod\",\n        \"println\",\n        \"repr\",\n        \"roboport\",\n        \"Roboports\",\n        \"Supercapacitor\",\n        \"yumako\"\n    ],\n\n    \"files.exclude\": {\n        \"**/.git\": true,\n        \"**/.DS_Store\": true,\n        \".lake\": true\n    },\n}"
  },
  {
    "path": "Fulgora150.lean",
    "content": "import Functorio\n\ninstance : Config where\n  generateBigPoles := true\n  generateRoboports := true\n  providerChestCapacity := 3\n  adapterMinHeight := 3\n\ndef makeWater : Ice 120 -> Bus (Water 2400) :=\n  busAssemblyLine (recipe .iceMelting) 2\n\ndef makeLightOil : Water 900 -> HeavyOil 1200 -> Bus (LightOil 900) :=\n  busAssemblyLine (recipe .heavyOilCracking) 1\n\ndef makeHolmiumSolution : Water 960 -> HolmiumOre 192 -> Stone 96 -> Bus (HolmiumSolution 9600) :=\n  busAssemblyLine (recipe .holmiumSolution) 16\n\ndef makeElectrolyte : HeavyOil 2400 -> HolmiumSolution 2400 -> Stone 240 -> Bus (Electrolyte 3600) :=\n  busAssemblyLine (recipe .electrolyte) 10\n\ndef makeHolmiumPlate : HolmiumSolution 4500 -> Bus (Holmium 225) :=\n  busAssemblyLine (recipe .holmiumPlate) 3\n\ndef makeSuperconductor : LightOil 240 -> Holmium 48 -> Copper 48 -> Plastic 48 -> Bus (Superconductor 144) :=\n  busAssemblyLine (recipe .superconductor) 2\n\ndef makeSupercapacitor : Electrolyte 720 -> Holmium 144 -> Superconductor 144 -> GreenCircuit 288 -> Battery 72 -> Bus (Supercapacitor 108) :=\n  busAssemblyLine (recipe .supercapacitor) 6\n\ndef makeAccumulator : Iron 144 -> Battery 360 -> Bus (Accumulator 108) :=\n  busAssemblyLine { recipe := .accumulator, fabricator := .electromagneticPlant } 6\n\ndef makeElectromagneticScience : Electrolyte 2700 -> HolmiumSolution 2700 -> Supercapacitor 108 -> Accumulator 108 -> Bus (ElectromagneticScience 162) :=\n  busAssemblyLine (recipe .electromagneticSciencePack) 9\n\ndef makeRocketFuel : LightOil 200 -> SolidFuel 200 -> Bus (RocketFuel 20) :=\n  busAssemblyLine (recipe .rocketFuel) 4\n\ndef makeRocket : BlueCircuit 20 -> LowDensityStructure 20 -> RocketFuel 20 -> Bus Unit :=\n  busAssemblyLine (recipe .rocketPart) 1\n\ndef fulgora150 := bus do\n  let ice <- input .ice 120\n  let stone <- input .stone 336\n  let holmiumOre <- input .holmiumOre 192\n  let iron <- input .ironPlate 144\n  let copper <- input .copperPlate 48\n  let solidFuel <- input .solidFuel 200\n  let plastic <- input .plasticBar 48\n  let lowDensityStruct <- input .lowDensityStructure 20\n  let greenCircuit <- input .electronicCircuit 288\n  let blueCircuit <- input .processingUnit 20\n  let battery <- input .battery 432\n  let heavyOil <- input .heavyOil 3600\n\n  let (stone0, stone1) <- split stone\n  let (battery0, battery1) <- split battery\n  let (heavyOil0, heavyOil1) <- split heavyOil\n\n  let water <- makeWater ice\n  let (water0, water1) <- split water\n  let lightOil <- makeLightOil water0 heavyOil0\n  let (lightOil0, lightOil1) <- split lightOil\n  let rocketFuel <- makeRocketFuel lightOil0 solidFuel\n  let holmiumSolution <- makeHolmiumSolution water1.less holmiumOre stone0\n  let (holmiumSolution0, holmiumSolution) <- split holmiumSolution\n  let (holmiumSolution1, holmiumSolution2) <- split holmiumSolution\n  let electrolyte <- makeElectrolyte heavyOil1 holmiumSolution0 stone1\n  let (electrolyte0, electrolyte1) <- split electrolyte\n  let holmiumPlate <- makeHolmiumPlate holmiumSolution1\n  let (holmiumPlate0, holmiumPlate) <- split holmiumPlate\n  let (holmiumPlate1, _holmiumExport) <- split holmiumPlate\n\n  let superconductor <- makeSuperconductor lightOil.less holmiumPlate0 copper plastic\n  let supercapacitor <- makeSupercapacitor electrolyte0 holmiumPlate1 superconductor.less greenCircuit battery0\n  let accumulator <- makeAccumulator iron battery1\n  let _ <- makeElectromagneticScience electrolyte1.less holmiumSolution2 supercapacitor accumulator.less\n\n  makeRocket blueCircuit lowDensityStruct rocketFuel\n\ndef main : IO Unit :=\n  IO.println (fulgora150.toBlueprint) --  (bootstrap := true))\n"
  },
  {
    "path": "Functorio/Abbreviations.lean",
    "content": "import Functorio.Bus\n\nabbrev Coal := BusLane .coal\nabbrev Stone := BusLane .stone\nabbrev CopperOre := BusLane .copperOre\nabbrev IronOre := BusLane .ironOre\n\nabbrev Copper := BusLane .copperPlate\nabbrev Iron := BusLane .ironPlate\nabbrev Steel := BusLane .steelPlate\nabbrev Brick := BusLane .stoneBrick\n\nabbrev Water := BusLane .water\nabbrev CrudeOil := BusLane .crudeOil\nabbrev LightOil := BusLane .lightOil\nabbrev HeavyOil := BusLane .heavyOil\nabbrev Lubricant := BusLane .lubricant\nabbrev Acid := BusLane .sulfuricAcid\nabbrev Petrolium := BusLane .petroleumGas\n\nabbrev Cable := BusLane .copperCable\nabbrev Plastic := BusLane .plasticBar\nabbrev GreenCircuit := BusLane .electronicCircuit\nabbrev RedCircuit := BusLane .advancedCircuit\nabbrev BlueCircuit := BusLane .processingUnit\nabbrev Gear := BusLane .ironGearWheel\nabbrev RobotFrame := BusLane .flyingRobotFrame\nabbrev LowDensityStructure := BusLane .lowDensityStructure\nabbrev ElectricEngine := BusLane .electricEngineUnit\nabbrev Battery := BusLane .battery\nabbrev Engine := BusLane .engineUnit\nabbrev IronStick := BusLane .ironStick\nabbrev Sulfur := BusLane .sulfur\nabbrev RocketFuel := BusLane .rocketFuel\n\nabbrev Pipe := BusLane .pipe\nabbrev Inserter := BusLane .inserter\nabbrev YellowBelt := BusLane .transportBelt\nabbrev Furnace := BusLane .electricFurnace\nabbrev Rail := BusLane .rail\nabbrev ProdModule := BusLane .productivityModule\nabbrev Wall := BusLane .stoneWall\n\nabbrev YellowAmmo := BusLane .firearmMagazine\nabbrev RedAmmo := BusLane .piercingRoundsMagazine\nabbrev Grenade := BusLane .grenade\n\nabbrev RedScience := BusLane .automationSciencePack\nabbrev GreenScience := BusLane .logisticSciencePack\nabbrev BlackScience := BusLane .militarySciencePack\nabbrev BlueScience := BusLane .chemicalSciencePack\nabbrev PurpleScience := BusLane .productionSciencePack\nabbrev YellowScience := BusLane .utilitySciencePack\n\nabbrev HotFluoroketone := BusLane .fluoroketoneHot\nabbrev ColdFluoroketone := BusLane .fluoroketoneCold\nabbrev Ice := BusLane .ice\nabbrev LithiumPlate := BusLane .lithiumPlate\nabbrev Lithium := BusLane .lithium\nabbrev LithiumBrine := BusLane .lithiumBrine\nabbrev Fluorine := BusLane .fluorine\nabbrev SolidFuel := BusLane .solidFuel\nabbrev Ammonia := BusLane .ammonia\nabbrev IcePlatform := BusLane .icePlatform\nabbrev AmmoniacalSolution := BusLane .ammoniacalSolution\nabbrev CryogenicScience := BusLane .cryogenicSciencePack\n\nabbrev Electrolyte := BusLane .electrolyte\nabbrev HolmiumOre := BusLane .holmiumOre\nabbrev Holmium := BusLane .holmiumPlate\nabbrev HolmiumSolution := BusLane .holmiumSolution\nabbrev Superconductor := BusLane .superconductor\nabbrev Supercapacitor := BusLane .supercapacitor\nabbrev Accumulator := BusLane .accumulator\nabbrev ElectromagneticScience := BusLane .electromagneticSciencePack\n\nabbrev Carbon := BusLane .carbon\nabbrev Calcite := BusLane .calcite\nabbrev ThrusterFuel := BusLane .thrusterFuel\nabbrev ThrusterOxidizer := BusLane .thrusterOxidizer\nabbrev MoltenIron := BusLane .moltenIron\nabbrev MoltenCopper:= BusLane .moltenCopper\nabbrev Explosives := BusLane .explosives\nabbrev Rocket := BusLane .rocket\nabbrev RailgunAmmo := BusLane .railgunAmmo\n\nabbrev AgriculturalScience := BusLane .agriculturalSciencePack\nabbrev Bioflux := BusLane .bioflux\nabbrev PentapodEgg := BusLane .pentapodEgg\nabbrev Jelly := BusLane .jelly\nabbrev Spoilage := BusLane .spoilage\nabbrev YumakoMash := BusLane .yumakoMash\nabbrev IronBacteria := BusLane .ironBacteria\nabbrev CopperBacteria := BusLane .copperBacteria\nabbrev Nutrients := BusLane .nutrients\nabbrev Jellynut := BusLane .jellynut\nabbrev JellynutSeed := BusLane .jellynutSeed\nabbrev Yumako := BusLane .yumako\nabbrev YumakoSeed := BusLane .yumakoSeed\n"
  },
  {
    "path": "Functorio/Adapter.lean",
    "content": "import Functorio.Factory\nimport Functorio.Util\n\nprivate inductive Orientation where\n| vertical\n| horizontalTopLtBot\n| horizontalTopGtBot\n| turnNE\n| turnES\n| turnSW\n| turnWN\n\nprivate def connector (x y : Nat) (isLiquid:Bool) (dir:DirectionV) (orient:Orientation) : Entity :=\n  if isLiquid then pipe x y\n  else belt x y (\n    match dir, orient with\n    | .N, .vertical => .N\n    | .N, .horizontalTopLtBot => .W\n    | .N, .horizontalTopGtBot => .E\n    | .N, .turnNE => .N\n    | .N, .turnES => .E\n    | .N, .turnSW => .W\n    | .N, .turnWN => .N\n    | .S, .vertical => .S\n    | .S, .horizontalTopLtBot => .E\n    | .S, .horizontalTopGtBot => .W\n    | .S, .turnNE => .E\n    | .S, .turnES => .S\n    | .S, .turnSW => .S\n    | .S, .turnWN => .W\n  )\n\nprivate def singleConnection (isLiquid:Bool) (dir:DirectionV) (topOffset botOffset : InterfaceImpl) (height crossingY : Nat) : Array Entity :=\n  if height == 0 then #[] else\n\n  let lowX := min topOffset botOffset\n  let highX := max topOffset botOffset\n  let diff := highX - lowX - 1\n\n  let vertical := (List.range height).flatMap fun y =>\n    if (y < crossingY) then [connector topOffset y isLiquid dir .vertical] else\n    if (y > crossingY) then [connector botOffset y isLiquid dir .vertical] else\n    []\n\n  let horizontal :=\n    if topOffset < botOffset then\n      (List.range' (topOffset + 1) diff).map fun x => connector x crossingY isLiquid dir .horizontalTopLtBot else\n    if topOffset > botOffset then\n      (List.range' (botOffset + 1) diff).map fun x => connector x crossingY isLiquid dir .horizontalTopGtBot\n    else\n      [connector topOffset crossingY isLiquid dir Orientation.vertical]\n\n  let turns :=\n    if topOffset < botOffset then\n      [\n        connector topOffset crossingY isLiquid dir .turnNE,\n        connector botOffset crossingY isLiquid dir .turnSW\n      ] else\n    if topOffset > botOffset then\n      [\n        connector topOffset crossingY isLiquid dir .turnWN,\n        connector botOffset crossingY isLiquid dir .turnES\n      ]\n    else\n      []\n\n  (vertical ++ horizontal ++ turns).toArray\n\ndef createAdapter (interfaces:List InterfaceV) (top bot:List InterfaceImpl) (height:Nat) : List Entity × Nat := Id.run do\n  let mut topUsed := 0  -- Amount of space already used at the top by the adapter.\n  let mut botUsed := 0  -- Amount of space already used at the bottom by the adapter.\n  let mut neededHeight := 0\n  let mut entities : Array Entity := #[]\n\n  for ((ingredient, direction), i) in interfaces.zipIdx do\n    let isLiquid := ingredient.isLiquid\n    let topOffset := top[i]!\n    let botOffset := bot[i]!\n\n    -- Adjacent pipes need some padding between them\n    let paddingNeeded := isLiquid && i+1 < interfaces.length && interfaces[i+1]!.fst.isLiquid\n\n    -- If the interfaces line up, connect them directly\n    if (topOffset == botOffset) then\n      entities := entities ++ singleConnection isLiquid direction topOffset botOffset height 0\n      topUsed := 0\n      botUsed := 0\n    -- If the top interface is to the left of the bottom interface, build connector along the bottom.\n    else if (topOffset < botOffset) then\n      entities := entities ++ singleConnection isLiquid direction topOffset botOffset height (height - botUsed - 1)\n      topUsed := 0\n      botUsed := botUsed + (if paddingNeeded then 2 else 1)\n    -- If the bottom interface is to the left of the top interface, build connector along the top.\n    else\n      entities := entities ++ singleConnection isLiquid direction topOffset botOffset height topUsed\n      topUsed := topUsed + (if paddingNeeded then 2 else 1)\n      botUsed := 0\n\n    neededHeight := max (max topUsed botUsed) neededHeight\n\n  (entities.toList, neededHeight)\n\ndef adapterV {interface}\n  (top:Vector InterfaceImpl interface.length)\n  (bot:Vector InterfaceImpl interface.length)\n  (width := (top ++ bot).toList.max?.getD 0)\n  (minHeight := 0)\n  : Factory interface [] interface []\n  :=\n  let initialHeight := (interface.map fun (ingredient,_) => if ingredient.isLiquid then 2 else 1).sum\n  let (_,neededHeight) := createAdapter interface top.toList bot.toList initialHeight\n  let height := max neededHeight minHeight\n  let (entities,_) := createAdapter interface top.toList bot.toList height\n  {\n    width := width\n    height:= height\n    entities:= entities\n    wires := []\n    interface := {\n      n := top\n      e := Array.toVector #[]\n      s := bot\n      w := Array.toVector #[]\n    }\n    name := \"adapter\"\n  }\n"
  },
  {
    "path": "Functorio/AdapterTest.lean",
    "content": "import Functorio.Adapter\nimport Functorio.Ascii\n\n#guard (adapterV (interface:=[(.coal,.N), (.coal,.N)]) #v[0,1] #v[0,1]).toAscii == s!\"\n ^^\n ^^\n\"\n\n#guard (adapterV (interface:=[(.coal,.N), (.coal,.N)]) #v[0,1] #v[2,3]).toAscii == s!\"\n ^^\n ↑↑←←\n ↑←←↑\n   ^^\n\"\n\n#guard (adapterV (interface:=[(.coal,.N), (.coal,.N)]) #v[2,3] #v[0,1]).toAscii == s!\"\n   ^^\n →→↑↑\n ↑→→↑\n ^^\n\"\n\n#guard (adapterV (interface:=[(.coal,.N), (.coal,.N)]) #v[0,1] #v[0,3]).toAscii == s!\"\n ^^\n ↑↑←←\n ^  ^\n\"\n\n#guard (adapterV (interface:=[(.water,.N), (.lubricant,.N), (.crudeOil,.N)]) #v[0,2,4] #v[5,8,10]).toAscii == s!\"\n ^ ^ ^\n | | |||||||\n | |       |\n | ||||||| |\n |       | |\n ||||||  | |\n      ^  ^ ^\n\"\n\n#guard (adapterV (interface:=[(.water,.N), (.ironOre,.N), (.crudeOil,.N)]) #v[0,2,4] #v[5,8,10]).toAscii == s!\"\n ^ ^ ^\n | ↑ |||||||\n | ↑←←←←←← |\n ||||||  ↑ |\n      ^  ^ ^\n\"\n"
  },
  {
    "path": "Functorio/Ascii.lean",
    "content": "import Functorio.Entity\nimport Functorio.Factory\n\n/-\nLegend:\n\n↑ belt\n⤒ belt going under-ground\n↥ belt going above-ground\n\n| pipe (no directionality)\n┴ pipe going underground (above-ground on the top, under-ground on the bottom)\n\n⇨ inserter (moving items from left to right)\n↠ long inserter\n\n⚡ power pole\n↯ big power pole\n\nA assembly machine\nC chemical plant\nR refinery\nF furnace\n\n^ interface direction (pointing north)\n* building\n! overlapping entities\n-/\n\nprivate def entitySymbol (e:Entity) : Option Char :=\n  match e.type with\n  | .belt .N _ => '↑'\n  | .belt .E _ => '→'\n  | .belt .S _ => '↓'\n  | .belt .W _ => '←'\n\n  | .beltDown .N => '⤒'\n  | .beltDown .E => '⇥'\n  | .beltDown .S => '⤓'\n  | .beltDown .W => '⇤'\n\n  | .beltUp .N => '↥'\n  | .beltUp .E => '↦'\n  | .beltUp .S => '↧'\n  | .beltUp .W => '↤'\n\n  | .pipe => '|'\n  | .pipeToGround .N => '┴'\n  | .pipeToGround .E => '├'\n  | .pipeToGround .S => '┬'\n  | .pipeToGround .W => '┤'\n\n  | .inserter .N _ => '⇩'\n  | .inserter .E _ => '⇦'\n  | .inserter .S _ => '⇧'\n  | .inserter .W _ => '⇨'\n\n  | .longInserter .N _ => '↡'\n  | .longInserter .E _ => '↞'\n  | .longInserter .S _ => '↟'\n  | .longInserter .W _ => '↠'\n\n  | .pole => '⚡'\n  | .bigPole => '↯'\n\n  | .splitter _ _ _ => 'S'\n  | .fabricator .assemblingMachine3 _ _ _ => 'A'\n  | .fabricator .electricFurnace _ _ _ => 'F'\n  | .fabricator .stoneFurnace _ _ _ => 'F'\n  | .fabricator .steelFurnace _ _ _ => 'F'\n  | .fabricator .electromagneticPlant _ _ _ => 'E'\n  | .fabricator .biochamber _ _ _ => 'B'\n  | .fabricator .chemicalPlant _ _ _ => 'C'\n  | .fabricator .oilRefinery _ _ _ => 'O'\n  | .fabricator .rocketSilo _ _ _ => 'L'  -- L is for Launch-site\n  | .deciderCombinator _ _ _ => '≥'\n  | .arithmeticCombinator _ _ => '+'\n  | .heatingTower => 'H'\n\n  | .roboport => 'R'\n  | .pump _ => 'P'\n  | .passiveProviderChest _ => '🄿'\n  | .ironChest => '☐'\n\n  | .refinedConcrete => .none\n\n  | _ =>\n    dbg_trace s!\"Couldn't print {reprStr e}\"\n    '?'\n\nprivate def set {w h} (v:Vector (Vector Char w) h) (x y:Nat) (c:Char) : Vector (Vector Char w) h :=\n  -- Mark overlapping entities with !\n  v.modify y fun inner => inner.modify x fun element => if element == ' ' then c else '!'\n\nprivate def dirSymbol (dir:Direction) : Char :=\n  match dir with\n  | .N => '^'\n  | .E => '>'\n  | .S => 'v'\n  | .W => 'w'\n\nnamespace Factory\n\ndef toAscii {n e s w} (f:Factory n e s w) : String := Id.run do\n  let mut data := Vector.replicate (f.height + 2) (Vector.replicate (f.width + 2) ' ')\n\n  -- draw entities\n  for entity in f.entities do\n    match entitySymbol entity with\n    | .none => pure ()\n    | .some symbol =>\n      -- draw entity symbol and a box if it's a big entity\n      for dx in List.range (entity.width) do\n        for dy in List.range (entity.height) do\n          let widerSymbol :=\n            if dx == entity.width/2 && dy == entity.height/2\n            then symbol\n            else '*'\n          -- +1 so we have space for interface indicators\n          data := set data (entity.x + dx + 1) (entity.y + dy + 1) widerSymbol\n\n  -- draw interfaces\n  for (offset,i) in f.interface.n.zipIdx do\n    data := set data (offset + 1) 0 (dirSymbol n[i]!.snd)\n  for (offset,i) in f.interface.e.zipIdx do\n    data := set data (f.width + 1) (offset + 1) (dirSymbol e[i]!.snd)\n  for (offset,i) in f.interface.s.zipIdx do\n    data := set data (offset + 1) (f.height+1) (dirSymbol s[i]!.snd)\n  for (offset,i) in f.interface.w.zipIdx do\n    data := set data 0 (offset + 1) (dirSymbol w[i]!.snd)\n\n  return \"\\n\" ++ String.intercalate \"\\n\" (data.toList.map fun inner => (String.mk inner.toList).trimRight) ++ \"\\n\"\n\nend Factory\n"
  },
  {
    "path": "Functorio/AssemblyLine.lean",
    "content": "import Functorio.Entity\nimport Functorio.Factory\nimport Functorio.Crop\nimport Functorio.Recipe\nimport Functorio.Row\nimport Functorio.Bus\nimport Functorio.Cap\nimport Functorio.Fraction\nimport Functorio.Util\nimport Functorio.Config\nimport Functorio.AssemblyStation\n\n-- Item's per minute\n@[simp]\ndef inputThroughput (process:Process) (stations:Fraction) (items:Fraction) : Fraction :=\n  stations * items * process.fabricator.speedup * 60 / process.getRecipe.time\n\n@[simp]\ndef outputThroughput (process:Process) (stations:Fraction) (ingredient:Ingredient) (items:Fraction) : Fraction :=\n  let t := stations * items * process.fabricator.speedup * (1 + process.fabricator.productivity) * 60 / process.getRecipe.time\n  if ingredient.isLiquid then t else min expressBeltThroughput t\n\ndef expressBeltHalfThroughput := expressBeltThroughput / 2\n\ndef findGap (offsets : Array Nat) (minGap:Nat) := Id.run do\n  let mut leftBound := 0\n  for offset in offsets do\n    let gap := offset - leftBound\n    if gap >= minGap then\n      return (leftBound, gap)\n    leftBound := offset + 1\n\n  error! s!\"Couldn't find a gap of {minGap}\"\n\ndef bigPoleInsert [config:Config] {interface} (offsets : Vector InterfaceImpl interface.length) : Factory interface [] interface [] :=\n  if !config.generateBigPoles then emptyFactoryH offsets else\n\n  let factory := (emptyFactoryH offsets).expand .S 2\n  let (gapStart, gapWidth) := (findGap offsets.toArray 2)\n\n  {\n    factory with\n    entities := factory.entities ++ [bigPole (gapStart + (gapWidth - 2) / 2) 0]\n  }\n\ndef roboportInsert [config:Config] {interface} (offsets : Vector InterfaceImpl interface.length) : Factory interface [] interface [] :=\n  if !config.generateRoboports then emptyFactoryH offsets else\n\n  let factory := (emptyFactoryH offsets).expand .S 4\n    let (gapStart, gapWidth) := (findGap offsets.toArray 2)\n\n  {\n    factory with\n    entities :=\n      factory.entities ++\n      [roboport (gapStart + (gapWidth - 4) / 2) 0] ++\n      if gapWidth > 4 then [pole (gapStart + gapWidth - 1) 3] else []\n  }\n\ndef providerChestInsert [config:Config] {interface} (process:Process) (offsets : Vector InterfaceImpl interface.length) : Factory interface [] interface [] :=\n  let recipe := process.getRecipe\n  if config.providerChestCapacity == 0 || recipe.outputs.isEmpty || recipe.outputs[0]!.snd.isLiquid then emptyFactoryH offsets else\n\n  let outputOffset :=\n    (offsets.zipIdx.filter fun (_, i) => interface[i]!.snd == .S)[0]!.fst\n\n  let chest := [\n    passiveProviderChest (outputOffset - 2) 1 (capacity:=config.providerChestCapacity),\n    inserter (outputOffset - 1) 1 .E\n  ]\n\n  let factory := (emptyFactoryH offsets).expand .S 3\n  {\n    factory with\n    entities := (eraseRectangle (outputOffset - 1) 0 1 3 factory.entities) ++ chest ++\n      if ((recipe.inputs ++ recipe.outputs).filter (fun input => !input.snd.isLiquid)).length <= 2 then [pole (outputOffset - 3) 1]\n      else [pole (outputOffset - 4) 1,\n            beltUp (outputOffset - 1) 0 .N,\n            beltDown (outputOffset - 1) 2 .N]\n  }\n\ndef outputBalancerInsert {interface} (offsets : Vector InterfaceImpl interface.length) : Factory interface [] interface [] :=\n  let factory := (emptyFactoryH offsets).expand .S 4\n\n  let (x,_) := findGap offsets.toArray 5\n  let balancer : List Entity := [\n    belt (x+2) 0 .S,\n    belt (x+3) 0 .W,\n    splitter (x+4) 0 .W (outputPriority := \"right\"),\n\n    belt x 1 .S,\n    belt (x+1) 1 .W,\n    belt (x+2) 1 .W,\n    belt (x+3) 1 .S,\n\n    belt x 2 .S,\n    belt (x+2) 2 .N,\n    belt (x+3) 2 .W,\n    belt (x+4) 2 .E,\n\n    belt x 3 .E,\n    beltDown (x+1) 3 .E,\n    pole (x+2) 3,\n    beltUp (x+3) 3 .E,\n    belt (x+4) 3 .N,\n  ]\n\n  let outputOffset :=\n    (offsets.zipIdx.filter fun (_, i) => interface[i]!.snd == .S)[0]!.fst\n\n  let adapter :=\n    let x := outputOffset\n    [belt x 0 .S, belt x 1 .W, belt x 2 .S, belt x 3 .S]\n\n  let avoider :=\n    let x := outputOffset - 1\n    [beltUp x 0 .N, belt x 1 .W, belt x 2 .E, beltDown x 3 .N]\n\n  let connector :=\n    match outputOffset - x with\n    | 5 => adapter\n    | 6 => adapter ++ avoider\n    | _ => error! s!\"Couldn't generate outputBalancerInsert for {reprStr interface}, x:= {x}, outputOffset := {outputOffset}\"\n\n  {\n    factory with\n    entities := (eraseRectangle (outputOffset - 1) 0 2 4 factory.entities) ++\n                balancer ++ connector\n  }\n\ndef maxRoboportLogisticsDistance := 46\n\ndef assemblyLineNoReturnedInputs [Config] (process:Process) (stations:Nat) : Factory (stationInterface process) [] (stationInterface process) [] :=\n  Id.run do\n    let output := process.getRecipe.outputs[0]!\n    let stationOutput := inputThroughput process 1 output.fst\n    let station := station process\n    let mut factories : Array (Factory (stationInterface process) [] (stationInterface process) []) := #[\n      bigPoleInsert station.interface.s,\n      providerChestInsert process station.interface.s,\n      roboportInsert station.interface.s\n    ]\n    let mut outputSinceBalance : Fraction := 0\n    let mut distanceFromRoboport : Nat := 0\n\n    for _ in List.range stations do\n      if !output.snd.isLiquid && outputSinceBalance + stationOutput > expressBeltHalfThroughput && outputSinceBalance != 0 then\n        factories := factories.push (outputBalancerInsert station.interface.s)\n        outputSinceBalance := 0\n        distanceFromRoboport := distanceFromRoboport + 4\n\n      if distanceFromRoboport + station.height > maxRoboportLogisticsDistance then\n        factories := factories.push (roboportInsert station.interface.s)\n        distanceFromRoboport := 0\n\n      factories := factories.push station\n      outputSinceBalance := outputSinceBalance + stationOutput\n      distanceFromRoboport := distanceFromRoboport + station.height\n\n    columnList factories.toList.reverse\n\ndef assemblyLineInterface (process:Process) : List InterfaceV :=\n  process.inputIngredients.map (., .N) ++\n  process.outputIngredients.map (., .S) ++\n  process.returnedInputs.map fun (_,ingredient) => (ingredient, .S)\n\nnamespace List\n\ndef lastIdxOf {A} [BEq A] (l:List A) (a:A) : Nat := Id.run do\n  let mut index := l.length\n  for (a', i) in l.zipIdx do\n    if a == a' then\n      index := i\n  return index\n\nend List\n\ndef filterInterfaceN {n e s w} (factory:Factory n e s w) (n':List InterfaceV) : Factory n' e s w :=\n  {\n    factory with\n    interface := {\n      n := n'.toVector.map fun interface => factory.interface.n[n.lastIdxOf interface]!\n      e := factory.interface.e\n      s := factory.interface.s\n      w := factory.interface.w\n    }\n  }\n\ndef connectorInterface (process:Process) : List InterfaceV :=\n  (process.returnedInputs.reverse.map fun (_,ingredient) => (ingredient, .N)) ++\n  (process.returnedInputs.map fun (_,ingredient) => (ingredient, .S))\n\ndef returnedInputsConnector (process:Process) : Factory [] [] (connectorInterface process)  [] :=\n  let n := process.returnedInputs.length\n  let leftEntities : List Entity :=\n    (List.range n).flatMap fun x =>\n      (List.range n).map fun y =>\n        belt x y (if x < y then .N else .E)\n\n  let rightEntities : List Entity  :=\n    (List.range n).flatMap fun x =>\n      (List.range n).map fun y =>\n        belt (x+n) y (if (n-x-1) <= y then .S else .E)\n\n  {\n    entities := leftEntities ++ rightEntities\n    interface := {\n      n := #v[]\n      e := #v[]\n      s := Vector.range (connectorInterface process).length\n      w := #v[]\n    }\n    width := 2*n\n    height := n\n    wires := []\n    name := s!\"returnedInputsConnector {reprStr process.returnedInputs}\"\n  }\n\ndef connectReturnedInputs (process:Process) (factory:Factory (assemblyLineInterface process) [] (assemblyLineInterface process) []) :Factory [] [] (assemblyLineInterface process) [] :=\n  if process.returnedInputs.isEmpty then capN factory else\n  let filteredFactory := (filterInterfaceN factory (connectorInterface process)).expand .N 1\n  column (returnedInputsConnector process) filteredFactory\n\ndef assemblyLine [Config] (process:Process) (stations:Nat) : Factory [] [] (assemblyLineInterface process) [] :=\n  let line := assemblyLineNoReturnedInputs process stations\n  let returns := emptyFactoryH\n  let factory := row line returns\n  connectReturnedInputs process factory\n\ndef tupleType {T} (ts:List T) (type:T->Type) : Type :=\n  match ts with\n  | [] => Unit\n  | [t] => type t\n  | t::types => type t × tupleType types type\n\ndef tuple {T} {ts:List T} {type:T->Type} (value : (t:T) -> Nat -> type t) (index:=0) : tupleType ts type :=\n  match ts with\n  | [] => ()\n  | [t] => value t index\n  | t::_::_ => (value t index, tuple value (index + 1))\n\n@[simp]\ndef BusAssemblyLineReturn (process:Process) (stations:Fraction) : Type :=\n  let outputs := (process.getRecipe.outputs.map fun (items, ingredient) => (outputThroughput process stations ingredient items, ingredient)) ++ process.returnedInputs\n  Bus (tupleType outputs fun (throughput, ingredient) => BusLane ingredient throughput)\n\n@[simp]\ndef BusAssemblyLineType (process:Process) (stations:Fraction) (remainingInputs: List (Fraction × Ingredient) := process.getRecipe.inputs): Type :=\n  match remainingInputs with\n  | [] => BusAssemblyLineReturn process stations\n  | (items,ingredient)::inputs =>\n    let throughput :=\n      inputThroughput process stations items +\n      List.sum (process.returnedInputs.map fun (throughput, ingredient') => if ingredient' == ingredient then throughput else 0)\n    BusLane ingredient throughput -> BusAssemblyLineType process stations inputs\n\ndef processBusAssemblyLineArguments\n  (process:Process)\n  (stations:Fraction)\n  (processor: List BusLane' -> BusAssemblyLineReturn process stations)\n  (remainingInputs: List (Fraction × Ingredient) := process.getRecipe.inputs)\n  (args: List BusLane' := [])\n: BusAssemblyLineType process stations remainingInputs := by\n  revert args\n  refine (match remainingInputs with\n  | [] => processor\n  | _::inputs => fun args arg =>\n    processBusAssemblyLineArguments process stations processor inputs (args ++ [arg.toBusLane'])\n  )\n\ndef busAssemblyLine [config:Config] (process: Process) (stations:Fraction) : BusAssemblyLineType process stations :=\n  processBusAssemblyLineArguments process stations fun inputs => do\n    let factory := assemblyLine process stations.roundUp\n    let namedFactory := factory.setName s!\"{stations}x{reprStr process.recipe}\"\n    let indexes <- busTapGeneric\n      inputs\n      ((process.getRecipe.outputs ++ process.returnedInputs).map Prod.snd)\n      (unsafeFactoryCast namedFactory)\n      (adapterMinHeight := config.adapterMinHeight)\n    return tuple (fun (_, _) i => {index:=indexes[i]!})\n"
  },
  {
    "path": "Functorio/AssemblyLineTest.lean",
    "content": "import Functorio.AssemblyLine\nimport Functorio.Ascii\n\n#guard (assemblyLine (recipe .advancedCircuit) 3).toAscii == s!\"\n\n ↑↑⇨***⇦↑↓\n ↑↑↠*A*↠↑↓\n ↑↑⚡***⚡↑↓\n ↑↑⇨***⇦↑↓\n ↑↑↠*A*↠↑↓\n ↑↑⚡***⚡↑↓\n ↑↑⇨***⇦↑↓\n ↑↑↠*A*↠↑↓\n ↑↑⚡***⚡↑↓\n ^^     ^v\n\"\n\n#guard (bus do\n  let acid <- input .sulfuricAcid (75/2)\n  let green <- input .electronicCircuit 150\n  let red <- input .advancedCircuit 15\n  let _ <- busAssemblyLine (recipe .processingUnit) 1 acid.exact green red\n).toAscii == s!\"\n\n  | ↑⇨***⇦↑↓\n  |┤↑├*A*↠↑↓\n  | ↑⚡***⚡↑↓\n  |→↑     ↑↓\n  |↑→→→→→→↑↓\n  |↑↑↓←←←←←←\n  |↑↑↓\n>||↑↑→→→→→→→>\n>→→↑↑\n>→→→↑\n\n\"\n\n#guard (bus do\n  let copper <- input .copperPlate 750\n  let _ <- busAssemblyLine (recipe .copperCable) 5 copper\n).toAscii == s!\"\n\n ↑⇨***⇨↓\n ↑ *A* ↓\n ↑⚡***⚡↓\n ↑  ↓←*↓\n ↑↓←←↓S←\n ↑↓ ↑←→↓\n ↑→⇥⚡↦↑↓\n ↑⇨***⇨↓\n ↑ *A* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *A* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *A* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *A* ↓\n ↑⚡***⚡↓\n ↑←↓←←←←\n  ↑↓\n>→↑→→→→→>\n\n\"\n\n#guard (bus do\n  let _ <- input .copperPlate 4\n  let water <- input .water 600\n  let crude <- input .crudeOil 1200\n  let _ <- busAssemblyLine (recipe .advancedOilProcessing) 1 water crude\n).toAscii == s!\"\n\n  | | *****|| | |\n  |┤|├***** | | |\n  | |⚡**O**┤|├| |\n  | ||***** | | |\n  | | *****┤| |├|\n  | | ||||||| | |\n  | | |       | |\n  | | | ||||||| |\n  | | | |       |\n  | | | |   |||||\n  | | | |   |\n>⇥| | | |↦→⇥|↦→→→→>\n>|| | | |   ||||||>\n    | | |\n>|||| |┤|├||||||||>\n        |\n        ||||||||||>\n\n\"\n\n#guard (bus do\n  let nutrients <- input .nutrients 15\n  let jellynut <- input .jellynut 120\n  let _ <- busAssemblyLine (recipe .jellynutProcessing) 1 nutrients jellynut\n).toAscii == s!\"\n\n ↑↑⇨***⇨↓↓\n ↑↑↠*B*↠↓↓\n ↑↑⚡***⚡↓↓\n ↑↑←↓←←←←↓\n ↑←↑↓↓←←←←\n  ↑↑↓↓\n>→↑↑↓→→→→→>\n>→→↑→→→→→→>\n\n\"\n\n#guard (bus do\n  let nutrients <- input .nutrients 115\n  let jellynut <- input .jellynut 120\n  let _ <- busAssemblyLine (recipe .jellynutProcessing [(100, .nutrients)]) 1 nutrients jellynut\n).toAscii == s!\"\n\n →↓\n ↑→→→→→→→→↓\n ↑        ↓\n ↑↑⇨***⇨↓↓↓\n ↑↑↠*B*↠↓↓↓\n ↑↑⚡***⚡↓↓↓\n ↑↑ ↓←←←←↓↓\n ↑↑←↓↓←←←←↓\n ↑←↑↓↓↓←←←←\n  ↑↑↓↓↓\n>→↑↑↓↓→→→→→>\n>→→↑↓→→→→→→>\n    →→→→→→→>\n\n\"\n\n#guard (bus do\n  let nutrients <- input .nutrients 15\n  let mash <- input .yumakoMash 300\n  let jelly <- input .jelly 240\n  let _ <- busAssemblyLine (recipe .bioflux) 1 nutrients mash jelly\n).toAscii == s!\"\n\n ↑↑⇨***⇦↑↓\n ↑↑↠*B*↠↑↓\n ↑↑⚡***⚡↑↓\n ↑↑←→→→→↑↓\n ↑←↑↑↓←←←←\n  ↑↑↑↓\n>→↑↑↑→→→→→>\n>→→↑↑\n>→→→↑\n\n\"\n\n#guard (bus do\n  let nutrients <- input .nutrients 100\n  let mash <- input .yumakoMash 400\n  let jelly <- input .jelly 400\n  let _ <- busAssemblyLine (recipe .bioflux [(160, .jelly), (100, .yumakoMash), (85, .nutrients) ]) 1 nutrients mash jelly\n).toAscii == s!\"\n\n →→→→→↓\n ↑→→→↓↓\n ↑↑→↓↓↓\n ↑↑↑↓↓→→→→→→↓\n ↑↑↑↓→→→→→→↓↓\n ↑↑↑→→→→→→↓↓↓\n ↑↑↑←←←←← ↓↓↓\n ↑↑     ↑ ↓↓↓\n ↑↑⇨***⇦↑↓↓↓↓\n ↑↑↠*B*↠↑↓↓↓↓\n ↑↑⚡***⚡↑↓↓↓↓\n ↑↑ →→→→↑↓↓↓↓\n ↑↑ ↑↓←←←←↓↓↓\n ↑↑ ↑↓↓←←←←↓↓\n ↑↑←↑↓↓↓←←←←↓\n ↑←↑↑↓↓↓↓←←←←\n  ↑↑↑↓↓↓↓\n>→↑↑↑↓↓↓→→→→→>\n>→→↑↑↓↓→→→→→→>\n>→→→↑↓→→→→→→→>\n     →→→→→→→→>\n\n\"\n\ninstance : Config where\n  generateBigPoles := true\n  generateRoboports := true\n  providerChestCapacity := 3\n  adapterMinHeight := 3\n\n#guard (bus do\n  let ice <- input .ice 60\n  let _ <- busAssemblyLine (recipe .iceMelting) 1 ice\n).toAscii == s!\"\n\n ***┤↑├|\n *C*⇦↑ |\n ***⚡↑ |\n ****↑ |\n ****↑ |\n **R*↑ |\n ****↑ |\n  ** ↑ |\n  *↯ ↑ |\n     ↑||\n     ↑|\n     ↑|\n     ↑|\n>→→→→↑|||>\n\n\"\n\n#guard (bus do\n  let ironOre <- input .ironOre 600\n  let _ <- busAssemblyLine (recipe .ironPlate) 16 ironOre\n).toAscii = s!\"\n\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑**** ↓\n ↑**** ↓\n ↑**R* ↓\n ↑****⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ↑**** ↓\n ↑**** ↓\n ↑**R* ↓\n ↑****⚡↓\n ↑     ↓\n ↑  ⚡🄿⇦↓\n ↑     ↓\n ↑ **  ↓\n ↑ *↯  ↓\n ↑ ↓←←←←\n ↑ ↓\n ↑←↓\n  ↑↓\n>→↑→→→→→>\n\n\"\n\n#guard (bus do\n  let stone <- input .stone 750\n  let steel <- input .steelPlate 750\n  let stick <- input .ironStick 750\n  let _ <- busAssemblyLine (recipe .rail) 5 stone stick steel\n).toAscii == s!\"\n\n ↑↑⇨***⇦↑↓\n ↑↑↠*A*↠↑↓\n ↑↑ ***↠↑↓\n ↑↑  ⚡  ↑↓\n ↑↑  ↓←*↥↓\n ↑↑↓←←↓S←←\n ↑↑↓ ↑←→→↓\n ↑↑→⇥⚡↦↑⤒↓\n ↑↑⇨***⇦↑↓\n ↑↑↠*A*↠↑↓\n ↑↑ ***↠↑↓\n ↑↑  ⚡  ↑↓\n ↑↑⇨***⇦↑↓\n ↑↑↠*A*↠↑↓\n ↑↑ ***↠↑↓\n ↑↑  ⚡  ↑↓\n ↑↑⇨***⇦↑↓\n ↑↑↠*A*↠↑↓\n ↑↑ ***↠↑↓\n ↑↑  ⚡  ↑↓\n ↑↑⇨***⇦↑↓\n ↑↑↠*A*↠↑↓\n ↑↑ ***↠↑↓\n ↑↑  ⚡  ↑↓\n ↑↑**** ↑↓\n ↑↑**** ↑↓\n ↑↑**R* ↑↓\n ↑↑****⚡↑↓\n ↑↑     ↥↓\n ↑↑  ⚡ 🄿⇦↓\n ↑↑     ⤒↓\n ↑↑ **  ↑↓\n ↑↑ *↯  ↑↓\n ↑↑  →→→↑↓\n ↑↑← ↑↓←←←\n ↑←↑ ↑↓\n  ↑↑ ↑↓\n>→↑↑ ↑→→→→>\n>→⇥↑↦↑\n>→→↑\n\n\"\n"
  },
  {
    "path": "Functorio/AssemblyStation.lean",
    "content": "import Functorio.Recipe\nimport Functorio.Factory\nimport Functorio.Util\nimport Functorio.Row\n\nstructure Process where\n  recipe:RecipeName\n  fabricator:Fabricator\n  returnedInputs : List (Fraction × Ingredient) := []\n  -- fabricatorOk : fabricator.handlesCategory recipeName.getRecipe.category := by decide\n  deriving Repr, DecidableEq\n\nnamespace Process\n\n@[simp]\ndef getRecipe (process:Process) : Recipe :=\n  let original := process.recipe.getRecipe\n  if process.fabricator != .biochamber then original else\n\n  let liquidInputs := original.inputs.filter fun input => input.snd.isLiquid\n  let solidInputs := original.inputs.filter fun input => !input.snd.isLiquid\n\n  let extraNutrients := original.time / process.fabricator.speedup / 4\n  let solidInputs :=\n    if solidInputs.any fun (_,ingredient) => ingredient == .nutrients\n    then solidInputs.map fun (items, ingredient) => (items + if ingredient == .nutrients then extraNutrients else 0, ingredient)\n    else [( extraNutrients , Ingredient.nutrients )] ++ solidInputs\n\n  { original with\n    inputs := liquidInputs ++ solidInputs\n  }\n\ndef liquidInputs (process:Process) : List Ingredient :=\n  (process.getRecipe.inputs.map Prod.snd).filter Ingredient.isLiquid\n\ndef liquidOutputs (process:Process): List Ingredient :=\n  (process.getRecipe.outputs.map Prod.snd).filter Ingredient.isLiquid\n\ndef solidInputs (process:Process): List Ingredient :=\n  (process.getRecipe.inputs.map Prod.snd).filter (!Ingredient.isLiquid .)\n\ndef solidOutputs (process:Process): List Ingredient :=\n  (process.getRecipe.outputs.map Prod.snd).filter (!Ingredient.isLiquid .)\n\n@[simp]\ndef inputIngredients (process:Process) : List Ingredient :=\n  process.getRecipe.inputs.map (Prod.snd)\n\n@[simp]\ndef outputIngredients (process:Process) : List Ingredient :=\n  process.getRecipe.outputs.map (Prod.snd)\n\nend Process\n\n@[simp]\ndef defaultCategoryFabricator : RecipeCategory -> Fabricator\n| .chemistry\n| .organicOrChemistry\n| .chemistryOrCryogenics => .chemicalPlant\n| .basicCrafting\n| .crafting\n| .advancedCrafting\n| .craftingWithFluid\n| .craftingWithFluidOrMetallurgy\n| .electronics\n| .electronicsOrAssembling\n| .electronicsWithFluid\n| .metallurgyOrAssembling\n| .organicOrAssembling\n| .pressing\n| .parameters\n| .cryogenicsOrAssembling => .assemblingMachine3\n| .oilProcessing => .oilRefinery\n| .rocketBuilding => .rocketSilo\n| .smelting => .electricFurnace\n| .captiveSpawnerProcess => .captiveBiterSpawner\n| .centrifuging => .centrifuge\n| .crushing => .crusher\n| .cryogenics => .cryogenicPlant\n| .electromagnetics => .electromagneticPlant\n| .metallurgy => .foundry\n| .organic\n| .organicOrHandCrafting => .biochamber\n| .recyclingOrHandCrafting\n| .recycling => .recycler\n\n@[simp]\ndef recipe (recipe:RecipeName) (returnedInputs : List (Fraction × Ingredient) := []) : Process := {\n  recipe := recipe,\n  fabricator := defaultCategoryFabricator recipe.getRecipe.category,\n  returnedInputs := returnedInputs\n}\n\nstructure FabricatorConfig where\n  direction : Direction\n  mirror : Bool\n  inputOffsets : List Nat\n  outputOffsets : List Nat\n\ndef fabricatorConfig : Fabricator -> FabricatorConfig\n| .assemblingMachine1\n| .recycler\n| .steelFurnace\n| .stoneFurnace\n| .electricFurnace\n| .rocketSilo\n| .centrifuge\n| .crusher\n| .captiveBiterSpawner => {\n  direction := .N\n  mirror := false\n  inputOffsets := []\n  outputOffsets := []\n}\n| .assemblingMachine2\n| .assemblingMachine3 => {\n  direction := .W\n  mirror := false\n  inputOffsets := [1]\n  outputOffsets := [1]\n}\n| .chemicalPlant\n| .biochamber => {\n  direction := .W\n  mirror := true\n  inputOffsets := [0,2]\n  outputOffsets := [0,2]\n}\n| .oilRefinery => {\n  direction := .E\n  mirror := false\n  inputOffsets := [1,3]\n  outputOffsets := [0,2,4]\n}\n| .cryogenicPlant => {\n  direction := .E\n  mirror := false\n  inputOffsets := [0,2,4]\n  outputOffsets := [0,2,4]\n}\n| .foundry => {\n  direction := .E\n  mirror := false\n  inputOffsets := [1,3]\n  outputOffsets := [1,3]\n}\n| .electromagneticPlant => {\n  direction := .N\n  mirror := false\n  inputOffsets := [2]\n  outputOffsets := [1]\n}\n\ndef interfaceE (process:Process) : List InterfaceH :=\n  process.liquidOutputs.map (.,.E)\n\ndef interfaceW (process:Process) : List InterfaceH :=\n  process.liquidInputs.map (.,.E)\n\ndef plainStation (process:Process) : Factory [] (interfaceE process) [] (interfaceW process) :=\n  let details := fabricatorConfig process.fabricator\n  {\n    entities := [\n      fabricator 0 0 process.fabricator process.recipe details.direction details.mirror\n    ]\n    wires := []\n    width := process.fabricator.width\n    height := process.fabricator.height\n    interface := {\n      n := #v[]\n      e := (details.outputOffsets.splitAt process.liquidOutputs.length).fst.castToVector!\n      s := #v[]\n      w := (details.inputOffsets.splitAt process.liquidInputs.length).fst.castToVector!\n    }\n    name := reprStr process.getRecipe.name\n  }\n\nprivate def beltline (x:Nat) (dir:Direction) (height:Nat) : List Entity :=\n  (List.range height).map (fun y => belt x y dir)\n\nprivate def pipeline (x:Nat) (height:Nat): List Entity :=\n  (List.range height).map fun y => pipe x y\n\nstructure Access where\n  direction: DirectionV\n  ingredient: Ingredient\n\n-- factoryDir = direction in which the factory lies\ndef accessEntities (x:Nat) (height:Nat) (ewOffsets:List InterfaceImpl) (ns : List InterfaceV) (factoryDir: DirectionH) (numSolidOutputs:Nat) : List Entity :=\n  Id.run do\n    let mut entities : Vector (Option EntityType) height := Vector.replicate height .none\n\n    let inputInserterDir : DirectionH := match factoryDir with | .E => .W | .W => .E\n    let outputInserterDir : DirectionH := factoryDir\n\n    for y in ewOffsets do\n      entities := entities.set! y (.some (.pipeToGround factoryDir))\n\n    for ((ingredient, dir), i) in ns.zipIdx do\n      for y in List.range height do\n        if entities[y]! == .none then\n          let inserterDir := match dir with | .N => inputInserterDir | .S => outputInserterDir\n          let filter := if dir == .S && numSolidOutputs > 1 then [ingredient] ++ ingredient.spoilResult.toList else []\n          match i with\n          | 0 => entities := entities.set! y (.some (.inserter inserterDir filter))\n          | 1 => entities := entities.set! y (.some (.longInserter inserterDir filter))\n          | _ => error! s!\"More than 2 belt inputs/outputs per side are not supported, belt index = {i}\"\n          break\n\n    for y in (List.range height).reverse do\n      if entities[y]! == .none then\n        entities := entities.set! y (.some .pole)\n        break\n\n    return entities.toList.zipIdx.flatMap fun (type, y) =>\n      match type with | .none => [] | .some type => [{x:=x, y:=y, type:=type}]\n\ndef rightAccessor {ew} (ewOffsets: Vector InterfaceImpl ew.length) (height:Nat) (ns : List InterfaceV) (numSolidOutputs:Nat): Factory ns ew ns ew :=\n  match ns with\n  | [] => emptyFactoryV ewOffsets\n  | _ =>\n    {\n      width := ns.length + 1\n      height := height\n      wires := []\n      interface := {\n        n := (ns.mapIdx fun i _ => i + 1).castToVector!\n        e := ewOffsets\n        s := (ns.mapIdx fun i _ => i + 1).castToVector!\n        w := ewOffsets\n      }\n      name := \"rightAccessor\"\n      entities :=\n        accessEntities 0 height ewOffsets.toList ns .W numSolidOutputs ++\n        (ns.zipIdx.flatMap fun ((_, dir), i) => beltline (i + 1) dir height)\n    }\n\ndef leftAccessor {ew} (ewOffsets: Vector InterfaceImpl ew.length) (height:Nat) (ns : List InterfaceV) (numSolidOutputs:Nat): Factory ns ew ns ew :=\n  match ns with\n  | [] => emptyFactoryV ewOffsets\n  | _ =>\n    {\n      width := ns.length + 1\n      height := height\n      wires := []\n      interface := {\n        n := (ns.mapIdx fun i _ => i).castToVector!\n        e := ewOffsets\n        s := (ns.mapIdx fun i _ => i).castToVector!\n        w := ewOffsets\n      }\n      name := \"leftAccessor\"\n      entities :=\n        accessEntities ns.length height ewOffsets.toList ns .E numSolidOutputs ++\n        (ns.zipIdx.flatMap fun ((_, dir), i) => beltline i dir height)\n    }\n\n\ndef interfaceNS (process:Process) : List InterfaceV :=\n  process.solidInputs.map (.,.N) ++ process.solidOutputs.map (.,.S)\n\ndef pipesOnSideStation (process:Process) : Factory\n  (interfaceNS process)\n  (interfaceE process)\n  (interfaceNS process)\n  (interfaceW process)\n:=\n  let station := plainStation process\n  let ns := interfaceNS process\n  let (leftNS, rightNS) := ns.splitAt (ns.length / 2)\n  let leftAccess := leftAccessor station.interface.w station.height leftNS process.solidOutputs.length\n  let rightAccess := rightAccessor station.interface.e station.height rightNS process.solidOutputs.length\n  unsafeFactoryCast (row3 leftAccess station rightAccess)\n\nprivate def pipesIn (ingredients:List Ingredient) (underground:Bool := false) (powerPole:Bool := false)\n: Factory (ingredients.map (.,.N)) (ingredients.map (.,.E)) (ingredients.map (.,.N)) []\n:=\n  let pipes := ingredients.length\n  let width := if pipes == 0 then 0 else pipes * 2 + 1\n  let height := if powerPole then max 2 (pipes * 2 - 1) else pipes * 2 - 1\n\n  let pipelines : List Entity :=\n    (List.range pipes).flatMap fun i =>\n      (List.range height).map fun y =>\n        let x := i * 2 + 1\n        pipe x y\n\n  let goDown : List Entity :=\n    (List.range pipes).map fun i =>\n      let x := 2 * i + 2\n      let y := 2 * i\n\n      if i == pipes - 1 && !underground\n      then pipe x y\n      else pipeToGround x y .W\n\n  let comeUp : List Entity :=\n    (List.range pipes).flatMap fun i =>\n      let x := 2 * pipes\n      let y := 2 * i\n\n      if i == pipes - 1 || underground\n      then []\n      else [pipeToGround x y .E]\n\n  let power : List Entity :=\n    if powerPole then [pole (width-1) 1] else []\n\n  let interfaceNS := ingredients.toVector.mapIdx fun i _ => (i * 2 + 1 : InterfaceImpl)\n  let interfaceE := ingredients.toVector.mapIdx fun i _ => (i * 2 : InterfaceImpl)\n\n  {\n    width := width\n    height := height\n    wires := []\n    entities := pipelines ++ goDown ++ comeUp ++ power\n    interface := {\n      n := cast (by simp) interfaceNS\n      e := cast (by simp) interfaceE\n      s := cast (by simp) interfaceNS\n      w := #v[]\n    }\n    name := s!\"pipesIn {reprStr ingredients}\"\n  }\n\nprivate def pipesOut (ingredients:List Ingredient) (underground:Bool := false)\n: Factory (ingredients.map (.,.S)) [] (ingredients.map (.,.S)) (ingredients.map (.,.E))\n:=\n  let pipes := ingredients.length\n  let width := if pipes == 0 then 0 else pipes * 2 + 1\n  let height := pipes * 2 - 1\n\n  let pipelines : List Entity :=\n    (List.range pipes).flatMap fun i =>\n      (List.range height).map fun y =>\n        let x := i * 2 + 1\n        pipe x y\n\n  let goDown : List Entity :=\n    (List.range pipes).map fun i =>\n      let x := 2 * i\n      let y := 2 * i\n\n      if i == 0 && !underground\n      then pipe x y\n      else pipeToGround x y .E\n\n  let comeUp : List Entity :=\n    (List.range pipes).flatMap fun i =>\n      let x := 0\n      let y := 2 * i\n\n      if i == 0 || underground\n      then []\n      else [pipeToGround x y .W]\n\n  let interfaceNS := ingredients.toVector.mapIdx fun i _ => (i * 2 + 1 : InterfaceImpl)\n  let interfaceW := ingredients.toVector.mapIdx fun i _ => (i * 2 : InterfaceImpl)\n\n  {\n    wires := []\n    width := width\n    height := height\n    entities := pipelines ++ goDown ++ comeUp\n    interface := {\n      n := cast (by simp) interfaceNS\n      e := #v[]\n      s := cast (by simp) interfaceNS\n      w := cast (by simp) interfaceW\n    }\n    name := s!\"pipesOut {reprStr ingredients}\"\n  }\n\ndef stationInterface (process:Process) : List InterfaceV :=\n  process.inputIngredients.map (., .N) ++\n  process.outputIngredients.map (., .S)\n\nabbrev Station process := Factory (stationInterface process) [] (stationInterface process) []\n\ndef stationWithoutOverride (process:Process) : Station process :=\n  let station := pipesOnSideStation process\n  let height := process.fabricator.height\n\n  let ns := interfaceNS process\n  let (leftNS, rightNS) := ns.splitAt (ns.length / 2)\n\n  unsafeFactoryCast (row3\n    (pipesIn process.liquidInputs (underground:=!leftNS.isEmpty) (powerPole:=\n      ns.length == 0 ||\n      process.liquidInputs.length + leftNS.length >= height))\n    station\n    (pipesOut process.liquidOutputs (underground:=!rightNS.isEmpty)))\n\n-- Special case, because it takes 4 inputs.\nprivate def flyingRobotFrameStation : Station (recipe .flyingRobotFrame) :=\n  let height := 3\n  let entities : List Entity :=\n    beltline (x:=0) .N height ++\n    beltline (x:=1) .N height ++\n    [\n      beltUp 2 0 .N,\n      longInserter 2 1 .W,\n      beltDown 2 2 .N,\n\n      longInserter 3 0 .W,\n      pole 3 1,\n      inserter 3 2 .W,\n\n      assembler RecipeName.flyingRobotFrame 4 0,\n\n      longInserter 7 0 .W,\n      pole 7 1,\n      inserter 7 2 .E\n    ] ++\n    beltline (x:=8) .N height ++\n    beltline (x:=9) .S height\n\n  {\n    wires := []\n    width:= 10, height:=height, entities := entities\n    interface := {\n      n := #v[0,1,2,8,9]\n      e := #v[]\n      s := #v[0,1,2,8,9]\n      w := #v[]\n    }\n    name := \"flyingRobotFrame\"\n  }\n\n-- Special case, because it has incredible requirements on output speed\nprivate def nutrientsFromBiofluxStation : Station (recipe .nutrientsFromBioflux) :=\n  let height := 5\n  let entities : List Entity :=\n    beltline (x:=0) .N height ++\n    beltline (x:=1) .N height ++\n    [\n      inserter 2 0 .W,\n      longInserter 2 1 .W,\n      longInserter 2 2 .W,\n      pole 2 3,\n\n      fabricator 3 0 .biochamber .nutrientsFromBioflux,\n\n      inserter 6 0 .W,\n      inserter 6 1 .W,\n      inserter 6 2 .W,\n\n      inserter 3 3 .N,\n      inserter 4 3 .N,\n      inserter 5 3 .N,\n      pole 6 3,\n\n      belt 3 4 .E,\n      belt 4 4 .E,\n      belt 5 4 .E,\n      belt 6 4 .E\n    ] ++\n    beltline (x:=7) .S height\n\n  {\n    wires := []\n    width:= 8, height:=height, entities := entities\n    interface := {\n      n := #v[0,1,7]\n      e := #v[]\n      s := #v[0,1,7]\n      w := #v[]\n    }\n    name := \"nutrientsFromBioflux\"\n  }\n\n-- Special case, needs two output inserters to keep up with the production rate.\ndef railStation : Station (recipe .rail) :=\n  let factory := (pipesOnSideStation (recipe .rail)).expand .S 1\n  let removedLeftPole := eraseRectangle 2 2 1 1 factory.entities\n  let removedPoles := eraseRectangle 6 2 1 1 removedLeftPole\n  {factory with\n    entities := removedPoles.append [\n      longInserter 6 2 .W,\n      pole 4 3\n    ]\n  }\n\n-- Special case, needs more powerpoles to covert the huge size of the building\ndef rocketPart : Station (recipe .rocketPart) :=\n  let factory := pipesOnSideStation (recipe .rocketPart)\n  {factory with\n    entities := factory.entities.append [\n      pole 1 3, pole 11 3,\n    ]\n  }\n\ndef acccessPipe (x:Nat) (ingredient:Ingredient): Factory [] [] [] [(ingredient, .E)] := {\n  width := 4, height := 1\n  wires := []\n  entities := [pipe x 0, pipeToGround (x-1) 0 .E]\n  name := \"accessPipe\"\n  interface := {n := #v[], e := #v[], s := #v[], w := #v[0]}\n}\n\n-- Special case, because the plant's pipes come out in weird spots\ndef electrolyteStation : Station (recipe .electrolyte) :=\n  {\n    wires := []\n    width := 13, height := 6,\n    name := \".electrolyte\"\n    interface := {n := #v[1,3,5,11], e := #v[], s := #v[1,3,5,11], w := #v[]}\n    entities :=\n      pipeline 1 6 ++\n      pipeline 3 6 ++\n      beltline 5 .N 6 ++\n      [\n        pipeToGround 2 0 .W, pipeToGround 7 0 .E, pipe 8 0,\n        pipeToGround 4 5 .W, pipeToGround 8 5 .E, pipe 9 5,\n        inserter 6 1 .W, pole 6 4,\n        fabricator 7 1 .electromagneticPlant .electrolyte .E,\n      ] ++\n      pipeline 11 6\n  }\n\n-- Special case, because the plant's pipes come out in weird spots\ndef electromagneticScienceStation : Station (recipe .electromagneticSciencePack) :=\n  {\n    wires := []\n    width := 14, height := 6,\n    name := \".electromagneticSciencePack\"\n    interface := {n := #v[1,3,5,6,13], e := #v[], s := #v[1,3,5,6,13], w := #v[]}\n    entities :=\n      pipeline 1 6 ++\n      pipeline 3 6 ++\n      beltline 5 .N 6 ++\n      beltline 6 .N 6 ++\n      [\n        pipeToGround 2 0 .W, pipeToGround 8 0 .E, pipe 9 0,\n        pipeToGround 4 5 .W, pipeToGround 9 5 .E, pipe 10 5,\n        inserter 7 1 .W, longInserter 7 2 .W, pole 7 4,\n        fabricator 8 1 .electromagneticPlant .electromagneticSciencePack .E,\n        inserter 12 1 .W, pole 12 4,\n      ] ++\n      beltline 13 .S 6\n  }\n\n-- Special case, because of 4 solid inputs\nprivate def supercapacitorStation : Station (recipe .supercapacitor) :=\n  let height := 4\n  let entities : List Entity :=\n    pipeline (x:=1) height ++\n    beltline (x:=3) .N height ++\n    beltline (x:=4) .N height ++\n    [\n      beltUp 5 0 .N,\n      longInserter 5 1 .W,\n      beltDown 5 2 .N,\n      belt 5 3 .N,\n\n      inserter 6 0 .W,\n      longInserter 6 1 .W,\n      pipeToGround 2 2 .W, pipeToGround 6 2 .E,\n      pole 6 3,\n\n      fabricator 7 0 .electromagneticPlant .supercapacitor,\n\n      longInserter 11 0 .W,\n      inserter 11 1 .E,\n      pole 11 3,\n    ] ++\n    beltline (x:=12) .N height ++\n    beltline (x:=13) .S height\n\n  {\n    wires := []\n    width:= 14, height:=height, entities := entities\n    interface := {\n      n := #v[1,3,4,5,12,13]\n      e := #v[]\n      s := #v[1,3,4,5,12,13]\n      w := #v[]\n    }\n    name := \".supercapacitor\"\n  }\n\ndef station (process:Process) : Station process :=\n  match process.recipe with\n  | .flyingRobotFrame => unsafeFactoryCast flyingRobotFrameStation\n  | .electrolyte => unsafeFactoryCast electrolyteStation\n  | .electromagneticSciencePack => unsafeFactoryCast electromagneticScienceStation\n  | .supercapacitor => unsafeFactoryCast supercapacitorStation\n  | .rail => unsafeFactoryCast railStation\n  | .rocketPart => unsafeFactoryCast rocketPart\n  | .nutrientsFromBioflux => unsafeFactoryCast nutrientsFromBiofluxStation\n  | _ => stationWithoutOverride process\n"
  },
  {
    "path": "Functorio/AssemblyStationTest.lean",
    "content": "import Functorio.AssemblyStation\nimport Functorio.Ascii\n\n#guard (station (recipe .ironPlate)).toAscii == s!\"\n ^     v\n ↑⇨***⇨↓\n ↑ *F* ↓\n ↑⚡***⚡↓\n ^     v\n\"\n\n#guard (station (recipe .electronicCircuit)).toAscii == s!\"\n ^     ^v\n ↑⇨***⇦↑↓\n ↑ *A*↠↑↓\n ↑⚡***⚡↑↓\n ^     ^v\n\"\n\n#guard (station (recipe .advancedCircuit)).toAscii == s!\"\n ^^     ^v\n ↑↑⇨***⇦↑↓\n ↑↑↠*A*↠↑↓\n ↑↑⚡***⚡↑↓\n ^^     ^v\n\"\n\n#guard (station (recipe .superconductor)).toAscii == s!\"\n  ^ ^^      ^v\n  | ↑↑⇨****⇦↑↓\n  | ↑↑↠****↠↑↓\n  |┤↑↑├**E* ↑↓\n  | ↑↑⚡****⚡↑↓\n  ^ ^^      ^v\n\"\n\n#guard (station { recipe := .accumulator, fabricator := .electromagneticPlant }).toAscii == s!\"\n ^      ^v\n ↑⇨****⇦↑↓\n ↑ ****↠↑↓\n ↑ **E* ↑↓\n ↑⚡****⚡↑↓\n ^      ^v\n\"\n\n#guard (station (recipe .supercapacitor)).toAscii == s!\"\n  ^ ^^^      ^v\n  | ↑↑↥⇨****↠↑↓\n  | ↑↑↠↠****⇦↑↓\n  |┤↑↑⤒├**E* ↑↓\n  | ↑↑↑⚡****⚡↑↓\n  ^ ^^^      ^v\n\"\n\n#guard (station (recipe .electromagneticSciencePack)).toAscii == s!\"\n  ^ ^ ^^      v\n  |┤| ↑↑ ├|   ↓\n  | | ↑↑⇨****⇨↓\n  | | ↑↑↠**** ↓\n  | | ↑↑ **E* ↓\n  | | ↑↑⚡****⚡↓\n  | |┤↑↑  ├|  ↓\n  ^ ^ ^^      v\n\"\n\n#guard (station (recipe .electrolyte)).toAscii == s!\"\n  ^ ^ ^     v\n  |┤| ↑ ├|  |\n  | | ↑⇨****|\n  | | ↑ ****|\n  | | ↑ **E*|\n  | | ↑⚡****|\n  | |┤↑  ├| |\n  ^ ^ ^     v\n\"\n\n#guard (station (recipe .flyingRobotFrame)).toAscii == s!\"\n ^^^     ^v\n ↑↑↥↠***↠↑↓\n ↑↑↠⚡*A*⚡↑↓\n ↑↑⤒⇨***⇦↑↓\n ^^^     ^v\n\"\n\n#guard (station (recipe .rail)).toAscii == s!\"\n ^^     ^v\n ↑↑⇨***⇦↑↓\n ↑↑↠*A*↠↑↓\n ↑↑ ***↠↑↓\n ↑↑  ⚡  ↑↓\n ^^     ^v\n\"\n\n#guard (station (recipe .processingUnit)).toAscii == s!\"\n  ^ ^     ^v\n  | ↑⇨***⇦↑↓\n  |┤↑├*A*↠↑↓\n  | ↑⚡***⚡↑↓\n  ^ ^     ^v\n\"\n\n#guard (station (recipe .battery)).toAscii == s!\"\n  ^ ^     ^v\n  |┤↑├***⇦↑↓\n  | ↑⇨*C*↠↑↓\n  | ↑⚡***⚡↑↓\n  ^ ^     ^v\n\"\n\n#guard (station (recipe .sulfuricAcid)).toAscii == s!\"\n  ^ ^     ^ v\n  |┤↑├***┤↑├|\n  | ↑⇨*C*⇦↑ |\n  | ↑⚡***⚡↑ |\n  ^ ^     ^ v\n\"\n\n#guard (station (recipe .sulfur)).toAscii == s!\"\n  ^ ^     v\n  |┤|├***⇨↓\n  | | *C* ↓\n  | ||***⚡↓\n  ^ ^     v\n\"\n\n#guard (station (recipe .solidFuelFromLightOil)).toAscii == s!\"\n  ^     v\n  ||***⇨↓\n  | *C* ↓\n  | ***⚡↓\n  ^     v\n\"\n\n#guard (station (recipe .rocketFuel)).toAscii == s!\"\n  ^ ^     v\n  | ↑⇨***⇨↓\n  |┤↑├*A* ↓\n  | ↑⚡***⚡↓\n  ^ ^     v\n\"\n\n#guard (station (recipe .heavyOilCracking)).toAscii == s!\"\n  ^ ^     v\n  |┤|├***||\n  | |⚡*C* |\n  | ||*** |\n  ^ ^     v\n\"\n\n#guard (station (recipe .advancedOilProcessing)).toAscii == s!\"\n  ^ ^       v v v\n  | | *****|| | |\n  |┤|├***** | | |\n  | |⚡**O**┤|├| |\n  | ||***** | | |\n  | | *****┤| |├|\n  ^ ^       v v v\n\"\n\n#guard (station (recipe .rocketPart)).toAscii == s!\"\n ^           ^^\n ↑⇨*********⇦↑↑\n ↑ *********↞↑↑\n ↑ ********* ↑↑\n ↑⚡*********⚡↑↑\n ↑ ****L**** ↑↑\n ↑ ********* ↑↑\n ↑ ********* ↑↑\n ↑ ********* ↑↑\n ↑⚡*********⚡↑↑\n ^           ^^\n\"\n\n#guard (station (recipe .pentapodEgg)).toAscii == s!\"\n  ^ ^     ^v\n  |┤↑├***⇦↑↓\n  | ↑⇨*B*↠↑↓\n  | ↑⚡***⚡↑↓\n  ^ ^     ^v\n\"\n\n#guard (station (recipe .nutrientsFromBioflux)).toAscii == s!\"\n ^^     v\n ↑↑⇨***⇨↓\n ↑↑↠*B*⇨↓\n ↑↑↠***⇨↓\n ↑↑⚡⇩⇩⇩⚡↓\n ↑↑ →→→→↓\n ^^     v\n\"\n"
  },
  {
    "path": "Functorio/Blueprint.lean",
    "content": "import Lean.Data.Json\nimport Lean.Data.Json.FromToJson\n\nimport Functorio.Entity\nimport Functorio.Factory\n\nopen Lean\nopen Lean (Json)\n\ndef coppperWire := 5\n\nprivate def wireTypeNumber (type:WireType) : Nat :=\n  match type with\n  | .redInput => 1\n  | .greenInput => 2\n  | .redOutput => 3\n  | .greenOutput => 4\n  | .copper => coppperWire\n\nprivate structure BlueprintInner where\n  entities: List Json\n  tiles: List Json\n  wires : List (List Nat)\n  item: String\n  version: Nat\n  deriving ToJson\n\nprivate structure Blueprint where\n  blueprint: BlueprintInner\n  deriving ToJson\n\nprivate structure Position where\n  x : Float\n  y : Float\n  deriving ToJson, Repr\n\nprivate def entityName (e:Entity) : String :=\n  match e.type with\n  | .belt _ _ => \"express-transport-belt\"\n  | .beltDown _ | .beltUp _ => \"express-underground-belt\"\n  | .splitter _ _ _ => \"express-splitter\"\n  | .pipe => \"pipe\"\n  | .pipeToGround _ => \"pipe-to-ground\"\n  | .pump _ => \"pump\"\n  | .inserter _ _ => \"bulk-inserter\"\n  | .longInserter _ _ => \"long-handed-inserter\"\n  | .pole => \"medium-electric-pole\"\n  | .bigPole => \"big-electric-pole\"\n  | .roboport => \"roboport\"\n  | .ironChest => \"iron-chest\"\n  | .passiveProviderChest _ => \"passive-provider-chest\"\n  | .refinedConcrete => \"refined-concrete\"\n  | .heatingTower => \"heating-tower\"\n  | .deciderCombinator _ _ _ => \"decider-combinator\"\n  | .arithmeticCombinator _ _ => \"arithmetic-combinator\"\n  | .fabricator f _ _ _ => f.name\n\nprivate def entityDirection (e:Entity) : Option Direction :=\n  match e.type with\n  | .belt d _ | .beltDown d | .beltUp d | .splitter d _ _\n  | .pipeToGround d | .pump d | .inserter d _ | .longInserter d _\n  | .fabricator _ _ d _ | .deciderCombinator d _ _ | .arithmeticCombinator d _ => d\n  | .pipe | .pole | .bigPole | .roboport | .ironChest | .passiveProviderChest _\n  | .heatingTower | .refinedConcrete => .none\n\nprivate def signalToJson (s:Signal) : Json :=\n  Json.mkObj ([\n      (\"name\", Json.str s.name)\n    ] ++\n    (s.type.map (\"type\", Json.str .)).toList)\n\nprivate def conditionToJson (c:Condition) : Json :=\n  Json.mkObj ([\n      (\"first_signal\", signalToJson c.firstSignal),\n      (\"comparator\", c.comparator)\n    ] ++\n    (c.constantValue.map (\"constant\", Json.num .)).toList ++\n    (c.secondSignal.map (\"second_signal\", signalToJson .)).toList)\n\nprivate def outputToJson (o:Output) : Json :=\n  Json.mkObj [\n    (\"signal\", signalToJson o.signal),\n    (\"copy_count_from_input\", o.copyCountFromInput)\n  ]\n\nprivate def arithmeticConditionToJson (c:ArithmeticCondition) : Json :=\n  Json.mkObj [\n    (\"first_signal\", signalToJson c.firstSignal),\n    (\"second_constant\", c.secondConstant),\n    (\"operation\", c.operation),\n    (\"output_signal\", signalToJson c.outputSignal)\n  ]\n\nprivate def entityProps (e:Entity) : List (String × Json) :=\n  match e.type with\n  | .pipe | .pipeToGround _ | .pump _\n  | .pole | .bigPole | .roboport | .ironChest | .heatingTower | .refinedConcrete => []\n  | .inserter _ filter | .longInserter _ filter => [\n    (\"use_filters\", !filter.isEmpty),\n    (\"filters\", Json.arr (filter.mapIdx (fun i ingredient =>\n      Json.mkObj [\n        (\"index\", s!\"{i + 1}\"),\n        (\"name\", ingredient.name),\n        (\"quality\", \"normal\"),\n        (\"comparator\", \"=\"),\n      ]\n    )).toArray)\n  ]\n  | .deciderCombinator _ conditions outputs => [\n    (\"control_behavior\", Json.mkObj [\n      (\"decider_conditions\", Json.mkObj [\n        (\"conditions\", Json.arr (conditions.map conditionToJson).toArray),\n        (\"outputs\", Json.arr (outputs.map outputToJson).toArray),\n      ])\n    ])\n  ]\n  | .arithmeticCombinator _ condition => [\n    (\"control_behavior\", Json.mkObj [\n      (\"arithmetic_conditions\", arithmeticConditionToJson condition)\n    ])\n  ]\n  | .belt _ behavior => [\n    (\"control_behavior\",\n      if behavior.circuitCondition.isNone\n      then Json.mkObj [\n        (\"circuit_enabled\", false),\n        (\"circuit_read_hand_contents\", behavior.circuitReadHandContents),\n        (\"circuit_contents_read_mode\", behavior.circuitContentsReadMode)\n      ]\n      else Json.mkObj [\n        (\"circuit_enabled\", true),\n        (\"circuit_condition\", conditionToJson behavior.circuitCondition.get!),\n        (\"circuit_read_hand_contents\", behavior.circuitReadHandContents),\n        (\"circuit_contents_read_mode\", behavior.circuitContentsReadMode)\n      ]\n    ),\n  ]\n  | .beltDown _ => [(\"type\", \"input\")]\n  | .beltUp _ => [(\"type\", \"output\")]\n  | .passiveProviderChest capacity => match capacity with | .none => [] | .some capacity => [(\"bar\", capacity)]\n  | .splitter _ priority filter =>\n    match priority with | .none => [] | .some p => [(\"output_priority\", Json.str p)] ++\n    match filter with | .none => [] | .some f => [\n      (\"filter\", Json.mkObj [\n        (\"name\", f.name),\n        (\"quality\", \"normal\"),\n        (\"comparator\", \"=\")\n      ])\n    ]\n  | .fabricator _ r _ m => [(\"recipe\", r.getRecipe.name), (\"recipe_quality\", \"normal\"), (\"mirror\", m)]\n\nprivate def directionToNat (d:Direction) :=\n  match d with\n  | .N => 0\n  | .E => 4\n  | .S => 8\n  | .W => 12\n\nprivate def entityToJson (id:Nat) (e:Entity) : Json :=\n  let dir := entityDirection e\n  let p : Position := {\n    x := Float.ofInt e.x + e.width.toFloat/2,\n    y := Float.ofInt e.y + e.height.toFloat/2\n  }\n\n  Json.mkObj ([\n    ([\n      (\"name\", entityName e),\n      (\"position\", ToJson.toJson p),\n      (\"entity_number\", id),\n    ] : List (String × Json)),\n    match dir with | .none => [] | .some dir => [ (\"direction\", directionToNat dir) ],\n    entityProps e\n  ].flatten)\n\n\nprivate def tileToJson (e:Entity) : Json :=\n  let p : Position := {\n    x := Float.ofInt e.x\n    y := Float.ofInt e.y\n  }\n\n  Json.mkObj [\n    (\"name\", entityName e),\n    (\"position\", ToJson.toJson p),\n  ]\n\ndef isTile (e:Entity) : Bool :=\n  match e.type with\n  | .refinedConcrete => true\n  | _ => false\n\nprivate def distance (a b : Entity) : Nat :=\n  ((Int.ofNat a.x) - (Int.ofNat b.x)).natAbs +\n  ((Int.ofNat a.y) - (Int.ofNat b.y)).natAbs\n\nnamespace Factory\n\ndef withinDistance (a b:Entity) :=\n  let d := distance a b\n  d <= 9 || (a.type == .bigPole && b.type == .bigPole && d <= 30)\n\ndef neededForBootStrap (e:Entity) : Bool :=\n  match e.type with\n  | .pole | .bigPole | .roboport => true\n  | _ => false\n\ndef wireToJson (wire:Wire) : List Nat :=\n  [wire.src, wireTypeNumber wire.srcType, wire.dst, wireTypeNumber wire.dstType]\n\ndef toBlueprint {n e s w} (factory:Factory n e s w) (bootstrap := false) : String :=\n  let entities := (factory.entities.filter (fun e =>\n    !isTile e && (!bootstrap || neededForBootStrap e))).zipIdx\n  let tiles := factory.entities.filter isTile\n  let poles := entities.filter (fun (e,_) => e.type == .pole || e.type == .bigPole)\n  let wires := poles.flatMap (fun (a,aIdx) => poles.flatMap (fun (b,bIdx) =>\n    if aIdx < bIdx && withinDistance a b then [[\n      aIdx, coppperWire, bIdx, coppperWire\n    ]] else []\n  ))\n\n  let blueprint : Blueprint := {\n    blueprint := {\n      entities:=entities.map (fun (e,idx) => entityToJson idx e),\n      tiles:=tiles.map tileToJson,\n      wires:= wires ++ factory.wires.map wireToJson,\n      item:=\"blueprint\",\n      version:= 562949957025792\n    }\n  }\n  Lean.Json.pretty (ToJson.toJson blueprint)\n\nend Factory\n"
  },
  {
    "path": "Functorio/Bus.lean",
    "content": "import Functorio.Entity\nimport Functorio.Factory\nimport Functorio.Column\nimport Functorio.Row\nimport Functorio.Fraction\nimport Functorio.Util\n\nstructure LaneConfig where\n  ingredient: Ingredient\n  refCount : Nat\n  deriving Inhabited, Repr\n\ndef depletedLane : LaneConfig := {\n  ingredient := default, refCount := 0\n}\n\nnamespace LaneConfig\n\ndef depleted (l:LaneConfig) : Bool :=\n  l.refCount == 0\n\nend LaneConfig\n\nabbrev LaneConfigs := List LaneConfig\n\nnamespace LaneConfigs\n\ndef height (lanes:LaneConfigs) : Nat :=\n  lanes.length\n\ndef useLane (index:Nat) (lanes:LaneConfigs) : LaneConfigs :=\n  lanes.modify index fun lane =>\n    if lane.refCount == 0 then error! s!\"Accessing depleted lane {index}\" else\n    {\n      refCount := lane.refCount - 1\n      ingredient := if lane.refCount <= 1 then default else lane.ingredient\n    }\n\ndef allocLane (ingredient:Ingredient) (lanes:LaneConfigs) (skip : Nat:=0): (Nat × LaneConfigs) := Id.run do\n  let newLane := {ingredient := ingredient, refCount := 1}\n  let mut skip := skip\n\n  for (lane, i) in lanes.zipIdx do\n    if i == 0 then continue   -- Don't alloc the 0th lane, we need the space for exits\n\n    if lane.depleted then\n      -- We found an empty lane!\n\n      if ingredient.isLiquid then\n        -- Don't alloc if the previous or next lane is already a pipe\n        if i > 1 && lanes[i - 1]!.ingredient.isLiquid then continue\n        if i < lanes.length - 1 && lanes[i + 1]!.ingredient.isLiquid then continue\n\n      if skip > 0 then\n        skip := skip - 1\n        continue\n\n      return (i, lanes.set i newLane)\n\n  -- Couldn't find a depleted lane, so we need to alloc a new one\n  if ingredient.isLiquid && skip == 0 && lanes.length > 0 && lanes[lanes.length - 1]!.ingredient.isLiquid then\n    -- Padding between pipes\n    (lanes.length + 1, lanes ++ [depletedLane, newLane])\n  else\n    let newLanes := lanes ++ List.replicate skip depletedLane ++  [newLane]\n    (newLanes.length - 1, newLanes)\n\ndef available (lanes:LaneConfigs): List (LaneConfig × Nat) :=\n  lanes.zipIdx.filter fun (lane, _) => !lane.depleted\n\nend LaneConfigs\n\nabbrev Throughput := Fraction -- items per minute\n\nstructure BusLane (_ : Ingredient) (throughput:Throughput) where\n  index: Nat\n  deriving Inhabited, Repr\n\nstructure BusLane' where\n  ingredient:Ingredient\n  throughput:Throughput\n  index: Nat\n  deriving Inhabited, Repr\n\nnamespace BusLane\n\ndef less {i m n} (l:BusLane i m) (_: n≤m := by decide) : BusLane i n :=\n  {index := l.index}\n\n-- TODO: why is this needed?\ndef exact {i n m} (l:BusLane i m) (_: n=m := by decide): BusLane i n :=\n  {index := l.index}\n\ndef toBusLane' {i n} (l:BusLane i n) : BusLane' :=\n  {ingredient := i, throughput := n, index := l.index }\n\nend BusLane\n\ninstance {i n} : CoeOut (BusLane i n) BusLane' where\n  coe l := l.toBusLane'\n\n@[simp]\ndef busInterface (lanes:LaneConfigs) : List InterfaceH :=\n  lanes.available.map fun (lane, _) => (lane.ingredient, .E)\n\ndef busInterfaceImpl (lanes:LaneConfigs) : Vector InterfaceImpl (busInterface lanes).length :=\n  cast (by simp) (lanes.available.toVector.map Prod.snd)\n\nstructure BusState where\n  input: LaneConfigs\n  output: LaneConfigs\n  factory : Factory [] (busInterface output) [] (busInterface input)\n  deriving Inhabited, Repr\n\nprivate def emptyBusState : BusState := {\n  input := [depletedLane]\n  output := [depletedLane]\n  factory := emptyFactoryV.setName \"emptyBus\"\n}\n\nabbrev Bus T := StateM BusState T\n\nprivate abbrev BusFactory (bus:Bus Unit) : Type :=\n  Factory [] (busInterface (bus.run emptyBusState).snd.output) [] (busInterface (bus emptyBusState).snd.input)\n\ndef bus (b:Bus Unit) : BusFactory b :=\n  (b emptyBusState).snd.factory\n\ndef input (ingredient:Ingredient) (throughput:Fraction) : Bus (BusLane ingredient throughput) :=\n  fun state =>\n    if (state.factory.width != 0) then error! s!\"input must be called at beginning; here bus is already {state.factory.width} tiles wide\" else\n      let (index, newConfig) := state.output.allocLane ingredient\n      ({index:=index}, {\n      input := newConfig\n      output := newConfig\n      factory := (emptyFactoryV (busInterfaceImpl newConfig))\n    })\n\ndef inputs (n:Nat) (ingredient:Ingredient) (throughput:Fraction) : Bus (Vector (BusLane ingredient throughput) n) := do\n  let mut lanes : Array (BusLane ingredient throughput) := #[]\n  for _ in List.range n do\n    let lane <- input ingredient throughput\n    lanes := lanes.push lane\n  return lanes.toList.castToVector!\n\ninductive Cell where\n| entity (type:EntityType)\n| blocked    -- blocked by another entity that's larger than 1 cell\n| empty\nderiving Repr, DecidableEq, Inhabited\n\nprivate inductive AccessType\n| put\n| get\nderiving Inhabited, DecidableEq, Repr\n\nabbrev Matrix w h := Vector (Vector Cell h) w\n\nnamespace Matrix\n\ndef modifyCell {w h} (matrix:Matrix w h) (x y : Nat) (cell:Cell -> Cell) : Matrix w h :=\n  matrix.modify x fun column => column.modify y cell\n\ndef setCell {w h} (matrix:Matrix w h) (x y : Nat) (cell:Cell) : Matrix w h :=\n  modifyCell matrix x y fun _ => cell\n\ndef canApplyEntities {w h} (matrix:Matrix w h) (entities:Option (List Entity)) : Bool :=\n  match entities with\n  | .none => false | .some entities =>\n    entities.all fun e =>\n      if e.x >= w then error! s!\"Bus is wider than the maximum supported {e.x}\" else\n      if e.y >= h then error! s!\"Bus is taller than the maximum supported {e.y}\" else\n\n      match matrix[e.x]![e.y]!, e.type with\n      -- Belt/pipe down followed by up cancels out to just a straight belt/pipe, so overriding is fine.\n      | .entity (.beltDown .E), .beltUp .E => true\n      | .entity (.pipeToGround .W), .pipeToGround .E => true\n      -- For splitters, the box below also has to be free.\n      | .empty, .splitter .E .none .none =>\n        matrix[e.x]![e.y + 1]! == .empty\n      | .empty, _ => true\n      | _,_ => false\n\ndef applyEntities {w h} (matrix:Matrix w h) (entities:Option (List Entity)) : Matrix w h := Id.run do\n  match entities with\n  | .none => error! s!\"Trying to apply invalid entities {reprStr entities}\"\n  | .some entities =>\n\n    let mut matrix := matrix\n\n    for e in entities do\n      if e.type ==  .splitter .E .none .none then\n        matrix := matrix.setCell e.x e.y (.entity e.type)\n        matrix := matrix.setCell e.x (e.y + 1) .blocked\n      else\n        matrix := matrix.modifyCell e.x e.y fun cell =>\n          match cell, e.type with\n          -- Belt/pipe down followed by up cancels out to just a straight belt/pipe.\n          | .entity (.beltDown .E), .beltUp .E => .entity (.belt .E)\n          | .entity (.pipeToGround .W), .pipeToGround .E => .entity .pipe\n          | .empty,_ => .entity e.type\n          | _,_ => error! s!\"Trying to override entity {reprStr cell} with {reprStr e}\"\n\n    return matrix\n\ndef toEntities {w h} (matrix : Matrix w h) : List Entity := Id.run do\n  let mut entities : Array Entity := #[]\n\n  for (column, x) in matrix.zipIdx do\n    for (cell, y) in column.zipIdx do\n      match cell with\n      | .entity type =>\n        entities := entities.push {x:=x, y:=y, type:=type}\n      | _ =>\n        pure ()\n\n  return entities.toList\n\ndef reduceUndergroundEntities {w h} (matrix:Matrix w h) : Matrix w h := Id.run do\n  let mut matrix := matrix\n  let width := w\n  let height := h\n\n  -- Go through all the columns\n  for x in List.range matrix.size do\n    let mut isUndergroundPipe := false\n    let mut isUndergroundBeltN := false\n    let mut isUndergroundBeltS := false\n\n    for y in List.range height do\n      if y + 1 >= height then continue\n\n      let cell := matrix[x]![y]!\n      let nextCell := matrix[x]![y+1]!\n\n      -- Simplify bus belt gets\n      if cell == .entity (.beltUp .N) && nextCell == .empty then\n        matrix := matrix.setCell x y (.entity (.belt .N))\n        matrix := matrix.setCell x (y+1) (.entity (.beltUp .N))\n\n      if cell == .entity (.beltUp .N) && nextCell == .entity (.beltDown .N) then\n        matrix := matrix.setCell x y (.entity (.belt .N))\n        matrix := matrix.setCell x (y+1) (.entity (.belt .N))\n\n      if isUndergroundBeltN && cell == .empty && nextCell == .empty then\n        matrix := matrix.setCell x y (.entity (.beltDown .N))\n        matrix := matrix.setCell x (y+1) (.entity (.beltUp .N))\n\n      if isUndergroundBeltN && cell == .empty && nextCell == .entity (.beltDown .N) then\n        matrix := matrix.setCell x y (.entity (.beltDown .N))\n        matrix := matrix.setCell x (y+1) (.entity (.belt .N))\n\n      -- Simplify bus belt puts\n      if cell == .entity (.beltDown .S) && nextCell == .empty then\n        matrix := matrix.setCell x y (.entity (.belt .S))\n        matrix := matrix.setCell x (y+1) (.entity (.beltDown .S))\n\n      if cell == .entity (.beltDown .S) && nextCell == .entity (.beltUp .S) then\n        matrix := matrix.setCell x y (.entity (.belt .S))\n        matrix := matrix.setCell x (y+1) (.entity (.belt .S))\n\n      if isUndergroundBeltS && cell == .empty && nextCell == .empty then\n        matrix := matrix.setCell x y (.entity (.beltUp .S))\n        matrix := matrix.setCell x (y+1) (.entity (.beltDown .S))\n\n      if isUndergroundBeltS && cell == .empty && nextCell == .entity (.beltUp .N) then\n        matrix := matrix.setCell x y (.entity (.beltUp .S))\n        matrix := matrix.setCell x (y+1) (.entity (.belt .S))\n\n      -- Simplify pipe put/get\n      if cell == .entity (.pipeToGround .N) && nextCell == .empty then\n        matrix := matrix.setCell x y (.entity .pipe)\n        matrix := matrix.setCell x (y+1) (.entity (.pipeToGround .N))\n\n      if cell == .entity (.pipeToGround .N) && nextCell == .entity (.pipeToGround .S) then\n        matrix := matrix.setCell x y (.entity .pipe)\n        matrix := matrix.setCell x (y+1) (.entity .pipe)\n\n      if isUndergroundPipe && cell == .empty && nextCell == .empty then\n        matrix := matrix.setCell x y (.entity (.pipeToGround .S))\n        matrix := matrix.setCell x (y+1) (.entity (.pipeToGround .N))\n\n      if isUndergroundPipe && cell == .empty && nextCell == .entity (.pipeToGround .S) then\n        matrix := matrix.setCell x y (.entity (.pipeToGround .S))\n        matrix := matrix.setCell x (y+1) (.entity .pipe)\n\n      -- Determine whether we are underground\n      let cell := matrix[x]![y]!\n      if cell == .entity (.beltUp .N) then isUndergroundBeltN := true  -- yes, beltUp means y + 1 is underground\n      if cell == .entity (.beltDown .N) then isUndergroundBeltN := false\n      if cell == .entity (.beltUp .S) then isUndergroundBeltS := false\n      if cell == .entity (.beltDown .S) then isUndergroundBeltS := true\n      if cell == .entity (.pipeToGround .N) then isUndergroundPipe := true\n      if cell == .entity (.pipeToGround .S) then isUndergroundPipe := false\n\n  -- Go through all the rows\n  for y in List.range height do\n    let mut isUndergroundPipe := false\n    let mut isUndergroundBelt := false\n\n    for x in List.range width do\n      if x + 1 >= width then continue\n\n      let cell := matrix[x]![y]!\n      let nextCell := matrix[x+1]![y]!\n\n      -- Simplify belt lanes\n      if cell == .entity (.beltDown .E) && nextCell == .empty then\n        matrix := matrix.setCell x y (.entity (.belt .E))\n        matrix := matrix.setCell (x+1) y (.entity (.beltDown .E))\n\n      if cell == .entity (.beltDown .E) && nextCell == .entity (.beltUp .E) then\n        matrix := matrix.setCell x y (.entity (.belt .E))\n        matrix := matrix.setCell (x+1) y (.entity (.belt .E))\n\n      if isUndergroundBelt && cell == .empty && nextCell == .empty then\n        matrix := matrix.setCell x y (.entity (.beltUp .E))\n        matrix := matrix.setCell (x+1) y (.entity (.beltDown .E))\n\n      if isUndergroundBelt && cell == .empty && nextCell == .entity (.beltUp .E)  then\n        matrix := matrix.setCell x y (.entity (.beltUp .E))\n        matrix := matrix.setCell (x+1) y (.entity (.belt .E))\n\n      -- Simplify pipe lanes\n      if cell == .entity (.pipeToGround .W) && nextCell == .empty then\n        matrix := matrix.setCell x y (.entity (.pipe))\n        matrix := matrix.setCell (x+1) y (.entity (.pipeToGround .W))\n\n      if cell == .entity (.pipeToGround .W) && nextCell == .entity (.pipeToGround .E) then\n        matrix := matrix.setCell x y (.entity .pipe)\n        matrix := matrix.setCell (x+1) y (.entity .pipe)\n\n      if isUndergroundPipe && cell == .empty && nextCell == .empty then\n        matrix := matrix.setCell x y (.entity (.pipeToGround .E))\n        matrix := matrix.setCell (x+1) y (.entity (.pipeToGround .W))\n\n      if isUndergroundPipe && cell == .empty && nextCell == .entity (.pipeToGround .E) then\n        matrix := matrix.setCell x y (.entity (.pipeToGround .E))\n        matrix := matrix.setCell (x+1) y (.entity .pipe)\n\n      -- Determine whether we are underground\n      let cell := matrix[x]![y]!\n      if cell == .entity (.beltDown .E) then isUndergroundBelt := true\n      if cell == .entity (.beltUp .E) then isUndergroundBelt := false\n      if cell == .entity (.pipeToGround .W) then isUndergroundPipe := true\n      if cell == .entity (.pipeToGround .E) then isUndergroundPipe := false\n\n  return matrix\n\nend Matrix\n\ndef busTapInterface (inputs: List BusLane') (outputIngredients: List Ingredient) : List InterfaceV :=\n  (inputs.map fun input => (input.ingredient, .N)) ++\n  (outputIngredients.map fun output => (output, .S))\n\ndef pipeAccess (type:AccessType) (split:Bool) (x y:Nat) : List Entity :=\n  let ramp :=\n    match y with\n    | 0 => error! s!\"0th lane should never be accessed {y}\"\n    | 1 => [pipe x 0]\n    | _ => [pipeToGround x 0 .N, pipeToGround x (y-1) .S]\n\n  let lane :=\n    match type with\n    | .put => [pipe x y, pipeToGround (x+1) y .W]\n    | .get =>\n      if split then [pipeToGround (x-1) y .E, pipe x y, pipeToGround (x+1) y .W]\n      else [pipeToGround (x-1) y .E, pipe x y]\n\n  ramp ++ lane\n\ndef beltAccessGetWithSplit (x y:Nat) : Option (List Entity) :=\n  -- We don't have enough room to access a belt with a splitter if x < 2\n  if x < 2 then .none else\n\n  let ramp :=\n    match y with\n    | 0 => error! s!\"0th lane should never be accessed {y}\"\n    | 1 => []\n    | 2 => [belt x 0 .N]\n    | _ => [beltDown x (y-2) .N, beltUp x 0 .N]\n\n  .some (ramp ++ [\n    beltUp (x-2) y .E,\n    splitter (x-1) (y-1) .E,\n    beltDown x y .E,\n    belt x (y-1) .N,\n  ])\n\ndef beltAccessGetWithoutSplit (x y:Nat) : List Entity :=\n  let lane :=\n    match x with\n    | 0 => []\n    | _ => [beltUp (x-1) y .E]\n\n  let ramp :=\n    match y with\n    | 0 => error! s!\"0th lane should never be accessed {y}\"\n    | 1 => [belt x 0 .N]\n    | _ => [beltDown x (y-1) .N, beltUp x 0 .N]\n\n  lane ++ [belt x y .N] ++ ramp\n\ndef beltAccessPut (x y:Nat) : List Entity :=\n  let ramp :=\n    match y with\n    | 0 => error! s!\"0th lane should never be allocated {y}\"\n    | 1 => [belt x 0 .S]\n    | _ => [beltUp x (y-1) .S, beltDown x 0 .S]\n\n  [belt x y .E, beltDown (x+1) y .E] ++ ramp\n\ndef laneAccess (type:AccessType) (config:LaneConfigs) (ingredient:Ingredient) (yIndex:Nat) (x:Nat) : Option (List Entity) :=\n  let split := config[yIndex]!.refCount > 0\n\n  if ingredient.isLiquid\n  then pipeAccess type split x yIndex\n  else\n    match type with\n    | .get =>\n      if split\n      then beltAccessGetWithSplit x yIndex\n      else beltAccessGetWithoutSplit x yIndex\n    | .put =>\n      beltAccessPut x yIndex\n\ndef bringDownAllLanes {w h} (lanes:LaneConfigs) (x:Nat) (matrix:Matrix w h) : Nat × Matrix w h := Id.run do\n  let mut x := x\n  let mut matrix := matrix\n\n  let entities x :=\n    lanes.available.map fun (lane, y) =>\n      if lane.ingredient.isLiquid then pipeToGround x y .W else beltDown x y .E\n  matrix := matrix.applyEntities (entities x)\n\n  return (x+1, matrix)\n\ndef bringUpAllLanes {w h} (lanes:LaneConfigs) (x:Nat) (matrix:Matrix w h) : Nat × Matrix w h := Id.run do\n  let mut x := x\n  let mut matrix := matrix\n\n  let entities x :=\n    lanes.available.map fun (lane, y) =>\n      if lane.ingredient.isLiquid then pipeToGround x y .E else beltUp x y .E\n  while !matrix.canApplyEntities (entities x) do x := x + 1\n  matrix := matrix.applyEntities (entities x)\n\n  return (x+1, matrix)\n\ndef busTapGeneric\n  (inputs:List BusLane')\n  (outputs:List Ingredient)\n  (factory:Factory [] [] (busTapInterface inputs outputs) [])\n  (adapterMinHeight:=0)\n: Bus (List Nat) := fun state => Id.run do\n  -- Worst case, all outputs are pipes so we add 2 that\n  let height := state.output.height + outputs.length * 2\n  -- Longer than that, and we'll probably have problems\n  -- with underground pipes running out of length anyway.\n  let width := 15\n\n  let mut matrix : Matrix width height := Vector.replicate width (Vector.replicate height .empty)\n  let mut offsets : Array Nat := #[]\n  let mut lanes := state.output\n  let mut previousWasLiquid := false\n  let mut x := 0\n\n  (x, matrix) := bringDownAllLanes lanes x matrix\n\n  -- Handle all inputs\n  for input in inputs do\n    lanes := lanes.useLane input.index\n\n    if previousWasLiquid && input.ingredient.isLiquid then x := x + 1  -- Gap between pipes so they don't connect.\n    let entities x := laneAccess .get lanes input.ingredient input.index x\n    while !matrix.canApplyEntities (entities x) do x := x + 1\n    matrix := matrix.applyEntities (entities x)\n\n    previousWasLiquid := input.ingredient.isLiquid\n    offsets := offsets.push x\n    x := x + 1\n\n  -- Handle all outputs\n  let mut outputLanes : Array Nat := #[]\n  for (ingredient, i) in outputs.zipIdx do\n    if previousWasLiquid && ingredient.isLiquid then x := x + 1  -- Gap between pipes so they don't connect.\n\n    if x > 8 then\n      (x, matrix) := bringUpAllLanes lanes x matrix\n      (x, matrix) := bringDownAllLanes lanes x matrix\n\n    let (index, newLanes) := lanes.allocLane ingredient (skip := outputs.length - 1 - i)\n    lanes := newLanes\n\n    let entities x := laneAccess .put lanes ingredient index x\n    while !matrix.canApplyEntities (entities x) do x := x + 1\n    matrix := matrix.applyEntities (entities x)\n\n    previousWasLiquid := ingredient.isLiquid\n    offsets := offsets.push x\n    outputLanes := outputLanes.push index\n    x := x + 1\n\n  (x, matrix) := bringUpAllLanes lanes x matrix\n\n  let tapFactory : Factory (busTapInterface inputs outputs) (busInterface lanes) [] (busInterface state.output) := {\n    width:= x\n    height:= max state.output.height lanes.height\n    wires := []\n    entities := matrix.reduceUndergroundEntities.toEntities\n    interface := {\n      n := offsets.toList.castToVector!\n      e := busInterfaceImpl lanes\n      s := #v[]\n      w := busInterfaceImpl state.output\n    }\n    name := \"tapBus'\"\n  }\n  -- TODO: make adapterMinHeight part of Config\n  let factoryAndTap := column factory tapFactory adapterMinHeight\n  let busFactory := row state.factory factoryAndTap\n\n  (outputLanes.toList, {\n    input := state.input\n    output := lanes\n    factory := busFactory\n  })\n\ndef busTapNoOutput\n  (inputs:List BusLane')\n  (factory:Factory [] [] (busTapInterface inputs []) [])\n  (adapterMinHeight:=0)\n: Bus Unit := do\n  let _ <- busTapGeneric inputs [] factory adapterMinHeight\n\ndef busTap\n  {outputIngredient} {outputThroughput} (inputs:List BusLane')\n  (factory:Factory [] [] (busTapInterface inputs [outputIngredient]) [])\n  (adapterMinHeight:=0)\n: Bus (BusLane outputIngredient outputThroughput) := do\n  let outputs <- busTapGeneric inputs [outputIngredient] factory adapterMinHeight\n  return {index := outputs[0]!}\n\ndef busTap2\n  {outputIngredient} {outputThroughput} {outputIngredient'} {outputThroughput'} (inputs:List BusLane')\n  (factory:Factory [] [] (busTapInterface inputs [outputIngredient, outputIngredient']) [])\n  (adapterMinHeight:=0)\n: Bus (BusLane outputIngredient outputThroughput × BusLane outputIngredient' outputThroughput') := do\n  let outputs <- busTapGeneric inputs [outputIngredient, outputIngredient'] factory adapterMinHeight\n  return ({index := outputs[0]!}, {index := outputs[1]!})\n\ndef split {i left input} (l:BusLane i input) (right := input - left) (_:left + right = input := by decide) : Bus (BusLane i left × BusLane i right) :=\n  fun state =>\n    (({index:= l.index}, {index:=l.index}),\n        {state with\n          output :=\n            state.output.modify l.index fun lane => {lane with\n              refCount := lane.refCount + 1\n            }\n          factory := unsafeFactoryCast state.factory\n        }\n    )\n\n@[simp]\ndef expressBeltThroughput : Throughput := 45 * 60  -- 2700\n\ndef mergeSolid {i a b} (l:BusLane i a) (l':BusLane i b) : Bus (BusLane i (a + b)) :=\n  busTap [l.toBusLane',l'.toBusLane'] {\n    entities:=[\n      pole 0 0,\n      belt 1 0 .E,\n      belt 2 0 .S,\n      belt 1 1 .N,\n      beltDown 2 1 .S,\n      belt 0 2 .E,\n      belt 1 2 .N,\n      belt 2 2 .W,\n      belt 0 3 .N,\n      belt 1 3 .E,\n      belt 2 3 .N,\n      splitter 0 4 .N (outputPriority:=\"left\"),\n      beltUp 2 4 .S,\n    ],\n    wires := []\n    width:=3,\n    height:=5,\n    interface:={\n      n := #v[]\n      e := #v[]\n      s := #v[0,1,2]\n      w := #v[]\n    }\n    name := s!\"merge {reprStr i}\"\n  }\n\ndef mergeLiquid {i a b} (l:BusLane i a) (l':BusLane i b) : Bus (BusLane i (a + b)) :=\n  busTap [l.toBusLane',l'.toBusLane'] {\n    entities:= (List.range 5).map (pipe . 0)\n    width:=5,\n    height:=1,\n    wires := []\n    interface:={\n      n := #v[]\n      e := #v[]\n      s := #v[0,2,4]\n      w := #v[]\n    }\n    name := s!\"merge {reprStr i}\"\n  }\n\ndef merge {i a b} (l:BusLane i a) (l':BusLane i b) (_ : i.isLiquid || a+b ≤ expressBeltThroughput := by decide) : Bus (BusLane i (a + b)) :=\n  if i.isLiquid then mergeLiquid l l' else mergeSolid l l'\n\ndef splitBalanced {i left input} (l:BusLane i input) (right := input - left) (h:left + right = input := by decide) : Bus (BusLane i left × BusLane i right) :=\n  let inputSignal : Signal := {name:= i.name, type := .none}\n  let leftSignal : Signal := {name:=\"signal-L\", type:=\"virtual\"}\n  let rightSignal : Signal := {name:=\"signal-R\", type:=\"virtual\"}\n  let enableSignal : Signal := {name:=\"signal-check\", type:=\"virtual\"}\n\n  let counter x y :=\n    deciderCombinator x y .N [\n        {\n          firstSignal := inputSignal\n          secondSignal := .none\n          constantValue := .some 0\n          comparator:= \"≥\"\n        }\n      ] [\n        {\n          signal:= inputSignal\n          copyCountFromInput:=true\n        }\n      ]\n\n  let multiplier x y outputSignal c :=\n    arithmeticCombinator x y .N\n      {\n        firstSignal:=inputSignal\n        outputSignal := outputSignal\n        secondConstant:=c\n        operation:= \"*\"\n      }\n\n  if i.isLiquid then split l right h else\n\n  let fraction := left / right\n\n  busTap2 [l.toBusLane'] {\n    width:=4,\n    height:=8,\n    wires := [\n      -- Hookup left\n      { src:= 0, dst:= 1, srcType:= .greenInput, dstType:= .greenInput},\n      { src:= 1, dst:= 1, srcType:= .redOutput, dstType:= .redInput},\n      { src:= 1, dst:= 2, srcType:= .greenOutput, dstType:= .greenInput},\n      -- Hookup right\n      { src:= 3, dst:= 4, srcType:= .greenInput, dstType:= .greenInput},\n      { src:= 4, dst:= 4, srcType:= .redOutput, dstType:= .redInput},\n      { src:= 4, dst:= 5, srcType:= .greenOutput, dstType:= .greenInput},\n      -- Hookup combiner\n      { src:= 2, dst:= 6, srcType:= .greenOutput, dstType:= .greenInput},\n      { src:= 5, dst:= 6, srcType:= .greenOutput, dstType:= .greenInput},\n      { src:= 6, dst:= 0, srcType:= .redOutput, dstType:= .redInput},\n      { src:= 6, dst:= 3, srcType:= .redOutput, dstType:= .redInput},\n    ]\n    entities:= [\n      -- Left logic\n      belt 0 6 .N {\n        circuitCondition := .some {\n          firstSignal:= enableSignal, secondSignal:=.none, constantValue:=.some 1, comparator:=\"=\"\n        }\n        circuitReadHandContents := true\n        circuitContentsReadMode := 0\n      },\n      counter 0 2,\n      multiplier 0 0 leftSignal fraction.num,\n\n      -- Right logic\n      belt 1 6 .N {\n        circuitCondition := .some {\n          firstSignal:= enableSignal, secondSignal:=.none, constantValue:=.some 1, comparator:=\"≠\"\n        }\n        circuitReadHandContents := true\n        circuitContentsReadMode := 0\n      },\n      counter 1 2,\n      multiplier 1 0 rightSignal fraction.den,\n\n      -- Combine left and right\n      deciderCombinator 2 2 .S [\n        {\n          firstSignal:= leftSignal\n          secondSignal:= rightSignal\n          constantValue:=.none\n          comparator:= \"<\"\n        }\n      ] [\n        {\n          signal:=enableSignal\n          copyCountFromInput:=false\n        }\n      ],\n\n      pole 2 1,\n      splitter 0 7 .N,\n      belt 2 7 .S,\n      belt 3 7 .S,\n\n      belt 2 6 .S,\n      belt 3 6 .S,\n\n      belt 0 5 .N,\n      belt 1 5 .E,\n      belt 2 5 .S,\n      belt 3 5 .S,\n\n      belt 0 4 .E,\n      belt 1 4 .E,\n      belt 2 4 .E,\n      belt 3 4 .S,\n    ]\n    interface:={\n      n := #v[]\n      e := #v[]\n      s := #v[1,2,3]\n      w := #v[]\n    }\n    name := s!\"splitBalanced {reprStr i}\"\n  }\n\ndef bigPoleFactory : Factory [] [] [] [] := {\n  entities := [bigPole 0 0]\n  width := 2, height := 2\n  name := \"bigPole\"\n  wires := []\n  interface := { n:= #v[], e:= #v[], s:= #v[], w:= #v[] }\n}\n\ndef pipePumps : Bus Unit :=\n  fun state =>\n    let config := state.output\n\n    let entities := config.zipIdx.flatMap fun (lane, y) =>\n      let poles := if y % 7 == 0 then [pole 1 y] else []\n\n      let lanes :=\n        if lane.depleted then []\n        else if lane.ingredient.isLiquid then\n          [pump 3 y .E] ++\n          if y % 7 == 0\n          then [pipeToGround 0 y .E, pipeToGround 2 y .E]\n          else [pipe 0 y, pipe 1 y, pipe 2 y]\n        else\n          [belt 3 y .E, belt 4 y .E] ++\n          if y % 7 == 0\n          then [ beltDown 0 y .E, beltUp 2 y .E ]\n          else [ belt 0 y .E, belt 1 y .E, belt 2 y .E]\n\n      poles ++ lanes\n\n    let factory : Factory [] (busInterface config) [] (busInterface config) := {\n      entities := entities\n      width := 5\n      wires := []\n      height := config.height\n      name := \"pipePumps\"\n      interface := {\n        n:= #v[]\n        e:= busInterfaceImpl config\n        s:= #v[]\n        w:= busInterfaceImpl config\n      }\n    }\n\n    ((), {\n      input := state.input\n      output := config\n      factory := row state.factory (column bigPoleFactory factory)\n    })\n\ndef spoilingChamber {n} {input:Ingredient} {output:Ingredient} (bacteria:BusLane input n) : Bus (BusLane output n) :=\n  let factory : Factory [] [] [(input, .N), (output, .S)] [] := {\n    name := s!\"spoilingChamber {reprStr input}\",\n    width := 4,\n    height := 9,\n    entities := [\n      belt 1 0 .E,\n      belt 2 0 .E,\n      belt 3 0 .S,\n\n      belt 0 1 .E,\n      belt 1 1 .N,\n      belt 2 1 .W,\n      belt 3 1 .S,\n\n      belt 0 2 .N,\n      splitter 1 2 .N,\n      belt 3 2 .S,\n\n      belt 0 3 .N,\n      splitter 1 3 .E,\n      belt 2 3 .N,\n      belt 3 3 .S,\n\n      beltUp 0 4 .N,\n      beltUp 2 4 .N,\n      belt 3 4 .S,\n\n      inserter 0 5 .S [output, .spoilage],\n      inserter 1 5 .S [output, .spoilage],\n      inserter 2 5 .S [output, .spoilage],\n      beltDown 3 5 .S,\n\n      ironChest 0 6,\n      ironChest 1 6,\n      ironChest 2 6,\n      pole 3 6,\n\n      inserter 0 7 .S,\n      inserter 1 7 .S,\n      inserter 2 7 .S,\n      beltUp 3 7 .S,\n\n      belt 0 8 .W,\n      belt 1 8 .W,\n      belt 2 8 .W,\n      belt 3 8 .S,\n    ],\n    wires := [],\n    interface := {\n      n := #v[],\n      e := #v[],\n      s := #v[2, 3],\n      w := #v[]\n    }\n  }\n  busTap [bacteria] (unsafeFactoryCast factory)\n\ndef removeExcess {i n} (l:BusLane i n) : Bus (BusLane i n × BusLane i 0) :=\n  busTap2 [l.toBusLane'] {\n    entities:=[\n      belt 0 0 .E,\n      belt 1 0 .S,\n\n      belt 0 1 .N,\n      beltDown 1 1 .S,\n      pole 2 1,\n\n      belt 0 2 .N,\n      belt 1 2 .E,\n      belt 2 2 .S,\n\n      splitter 0 3 .N (outputPriority:=\"left\"),\n      belt 2 3 .S,\n\n      belt 0 4 .N,\n      belt 1 4 .E,\n      belt 2 4 .S,\n\n      splitter 0 5 .N (outputPriority:=\"right\") (filter:=Ingredient.spoilage),\n      belt 2 5 .S,\n\n      belt 0 6 .N,\n      beltUp 1 6 .S,\n      belt 2 6 .S,\n    ],\n    wires := []\n    width:=3,\n    height:=7,\n    interface:={\n      n := #v[]\n      e := #v[]\n      s := #v[0,1,2]\n      w := #v[]\n    }\n    name := s!\"removeExcess {reprStr i}\"\n  }\n"
  },
  {
    "path": "Functorio/BusTest.lean",
    "content": "import Functorio.Bus\nimport Functorio.Cap\nimport Functorio.Ascii\n\nnamespace Test\n\n#guard (bus do\n  let iron <- inputs 10 .ironOre 2700\n  busTapNoOutput [iron[9], iron[1]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↑↑\n>⇥↥↑↦>\n>→→↑\n>⇥⤒↦→>\n>⇥↑↦→>\n>⇥↑↦→>\n>⇥↑↦→>\n>⇥↑↦→>\n>⇥↑↦→>\n>⇥↑↦→>\n>→↑\n\n\"\n\n#guard (bus do\n  let iron <- inputs 6 .ironOre 2700\n  busTapNoOutput [iron[4], iron[1]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↑↑\n>⇥↥↑↦>\n>→→↑\n>⇥⤒↦→>\n>⇥↑↦→>\n>→↑\n>→→→→>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 5 .ironOre 2700\n  busTapNoOutput [iron[0], iron[1], iron[2]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↑↑↑\n>→↑↑↑\n>→→↑↑\n>→→→↑\n>→→→→→>\n>→→→→→>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 5 .ironOre 2700\n  busTapNoOutput [iron[0], iron[2], iron[4]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↑↑↑\n>→↑↑↑\n>→⇥↑↑↦>\n>→→↑↑\n>→→⇥↑↦>\n>→→→↑\n\n\"\n\n#guard (bus do\n  let iron <- inputs 5 .ironOre 2700\n  busTapNoOutput [iron[4], iron[3]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↑↑←\n  ↑ ↑\n>⇥↑ ↑↦>\n>⇥↑ ↑↦>\n>⇥↑ ↑↦>\n>⇥↑↦↑\n>→↑\n\n\"\n\n#guard (bus do\n  let iron <- inputs 5 .ironOre 2700\n  busTapNoOutput [iron[4], iron[2]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↑↑\n>⇥↑↑↦>\n>⇥↥↑↦>\n>→→↑\n>⇥⤒↦→>\n>→↑\n\n\"\n\n#guard (bus do\n  let iron <- inputs 5 .ironOre 2700\n  busTapNoOutput [iron[4], iron[0], iron[1], iron[2]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↥↑↑↑\n>→→↑↑↑\n>⇥⤒↦↑↑\n>⇥↑↦→↑\n>⇥↑↦→→→>\n>→↑\n\n\"\n\n#guard (bus do\n  let petrol <- input .petroleumGas 1\n  let water <- input .water 1\n  let _ <- inputs 5 .ironOre 2700\n  busTapNoOutput [petrol, water] (capN (emptyFactoryH #v[0,2]))\n).toAscii == s!\"\n\n  | |\n>|| |\n>→→⇥|↦>\n>||||\n>→→→→→>\n>→→→→→>\n>→→→→→>\n>→→→→→>\n\n\"\n\n#guard (bus do\n  let _ <- inputs 5 .ironOre 2700\n  let petrol <- input .petroleumGas 1\n  let water <- input .water 1\n  busTapNoOutput [petrol, water] (capN (emptyFactoryH #v[0,2]))\n).toAscii == s!\"\n\n  | |\n>⇥| |↦>\n>⇥| |↦>\n>⇥| |↦>\n>⇥| |↦>\n>⇥| |↦>\n>|| |\n    |\n>||||\n\n\"\n\n#guard (bus do\n  let iron <- inputs 5 .ironOre 2700\n  let petrol <- input .petroleumGas 1\n  let water <- input .water 1\n  busTapNoOutput [petrol, iron[2], water] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  |↑|\n>⇥|↑|↦>\n>⇥┴↑|↦>\n>→→↑|\n>⇥┬ |↦>\n>⇥| |↦>\n>|| |\n    |\n>||||\n\n\"\n\n#guard (bus do\n  let iron <- inputs 2 .ironOre 2700\n  let _coal : BusLane .coal 100 <- busTap [iron[0]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↑↓\n>→↑→→>\n>→→→→>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 5 .ironOre 2700\n  let _coal : BusLane .coal 100  <- busTap [iron[2]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↑↓\n>⇥↑↓↦>\n>⇥↑↓↦>\n>→↑→→>\n>→→→→>\n>→→→→>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 5 .ironOre 2700\n  let _gear : BusLane .coal 100 <- busTap [iron[1], iron[3]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↑↑↓\n>⇥↑↑↓↦>\n>→↑↑→→>\n>→⇥↑↦→>\n>→→↑\n>→→→→→>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 5 .ironOre 2700\n  let _water : BusLane .water 100 <- busTap [iron[2]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  ↑|\n>⇥↑|↦>\n>⇥↑|↦>\n>→↑||>\n>→→→→>\n>→→→→>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 3 .ironOre 2700\n  let (iron0, _) <- split iron[0] (left:=100)\n  busTapNoOutput [iron0] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  *↑\n>→S→→>\n>→→→→>\n>→→→→>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 4 .ironOre 2700\n  let (iron0, _) <- split iron[2] (left:=100)\n  busTapNoOutput [iron0] (capN emptyFactoryH)\n).toAscii == s!\"\n\n   ↑\n>→⇥↑↦>\n>⇥*↑↦>\n>→S→→>\n>→→→→>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 4 .ironOre 2700\n  let water <- input .water 1000\n  let (iron0, _) <- split iron[2] (left:=100)\n  busTapNoOutput [water, iron0] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  |↑\n>⇥┴↑↦>\n>⇥*↑↦>\n>→S→→>\n>⇥┬↦→>\n>||\n\n\"\n\n#guard (bus do\n  let iron <- inputs 5 .ironOre 2700\n  let petrol <- input .petroleumGas 1\n  let water <- input .water 10\n  let (water0, _) <- split (left:=5) water\n  busTapNoOutput [petrol, iron[2], water0] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  |↑|\n>⇥|↑|↦>\n>⇥┴↑|↦>\n>→→↑|\n>⇥┬ |↦>\n>⇥| |↦>\n>|| |\n    |\n>|||||>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 10 .ironOre 2700\n  let petrol <- input .petroleumGas 1\n  let water <- input .water 10\n  let (water0, _) <- split (left:=5) water\n  busTapNoOutput [petrol, iron[2], water0] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  |↑|\n>⇥|↑|↦>\n>⇥┴↑|↦>\n>→→↑|\n>⇥┬ |↦>\n>⇥| |↦>\n>⇥| |↦>\n>⇥| |↦>\n>⇥| |↦>\n>⇥| |↦>\n>⇥| |↦>\n>|| |\n    |\n>|||||>\n\n\"\n\n#guard (bus do\n  let iron0 <- inputs 3 .ironOre 2700\n  let water <- input .water 1000\n  let iron1 <- inputs 3 .ironOre 2700\n  let (water0, _) <- split (left:=1) water\n  busTapNoOutput [water0, iron1[0], iron1[1], iron0[0], iron0[1], iron0[2]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  |↑↑↑↑↑←\n  |↑↑↑↑←↑\n  |↑↑↑←↑↑\n  |↑↑←↑↑↑\n  |↑←↑↑↑↑\n  | ↑↥↑↑↑\n>⇥| ↑↦↑↑↑\n>⇥| ↑⤒↦↑↑\n>⇥| ↑↑↦→↑\n>||┤↑↑├|||>\n>→→→↑↑\n>→→→→↑\n>→→→→→→→→→>\n\n\"\n\n#guard (bus do\n  let iron0 <- inputs 3 .ironOre 2700\n  let water <- input .water 1000\n  let iron1 <- inputs 3 .ironOre 2700\n  let (water0, _) <- split (left:=1) water\n  busTapNoOutput [water0, iron1[0], iron1[1], iron0[0]] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  |↑↑↑←\n  |↑↑←↑\n  |↑←↑↑\n  | ↑↥↑\n>⇥| ↑↦↑\n>⇥| ↑⤒↦→>\n>⇥| ↑↑↦→>\n>||┤↑↑├|>\n>→→→↑↑\n>→→→→↑\n>→→→→→→→>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 6 .ironOre 2700\n  let water <- input .water 1000\n  let (iron0, _) <- split iron[2] (left:=100)\n  let (iron1, _) <- split iron[4] (left:=100)\n  busTapNoOutput [water, iron0, iron1] (capN emptyFactoryH)\n).toAscii == s!\"\n\n  |↑↑\n>⇥┴↑↑↦>\n>⇥*↑↑↦>\n>→S⇥↑↦>\n>→⇥*↑↦>\n>→→S→→>\n>⇥┬↦→→>\n>||\n\n\"\n\n#guard (bus do\n  let iron <- inputs 10 .ironOre 2700\n  let (iron0, _) <- split iron[6] (left:=100)\n  let (iron1, _) <- split iron[4] (left:=100)\n  let (iron2, _) <- split iron[2] (left:=100)\n  let _coal : BusLane .coal 4 <- busTap [iron0, iron1, iron2] (capN emptyFactoryH)\n).toAscii == s!\"\n\n   ↑↑↑→→→→→→↓\n   ↑↑↑←←←←  ↓\n   ↑↑←←  ↑  ↓\n   ↑  ↑  ↑  ↓\n>→⇥↑↦⇥↑↦⇥↑↦⇥↓↦>\n>→⇥↑↦⇥↑ *↑↦⇥↓↦>\n>→⇥↑↦⇥↑↦S→→⇥↓↦>\n>→⇥↑ *↑↦→→→⇥↓↦>\n>→⇥↑↦S→→→→→⇥↓↦>\n>⇥*↑↦→→→→→→⇥↓↦>\n>→S→→→→→→→→⇥↓↦>\n>→→→→→→→→→→⇥↓↦>\n>→→→→→→→→→→⇥↓↦>\n>→→→→→→→→→→⇥↓↦>\n            →→>\n\n\"\n\n#guard (bus do\n  let iron <- inputs 15 .ironOre 2700\n  let petrol <- input .petroleumGas 2\n  let (petrol0, petrol1) <- split (left:=1) petrol\n  let water <- input .water 2\n  let (water0, water1) <- split (left:=1) water\n  busTapNoOutput [petrol0, iron[0], water0] (capN emptyFactoryH)\n  pipePumps\n  busTapNoOutput [water1, iron[5], petrol1] (capN emptyFactoryH)\n).toAscii == s!\"\n\n      **\n      *↯\n  ┴↑|  ⚡    |↑|\n>→→↑|       |↑|\n>⇥┬ |↦→→→→→⇥|↑|↦>\n>⇥| |↦→→→→→⇥|↑|↦>\n>⇥| |↦→→→→→⇥|↑|↦>\n>⇥| |↦→→→→→⇥┴↑|↦>\n>⇥| |↦→→→→→→→↑|\n>⇥| |↦⇥⚡↦→→⇥┬ |↦>\n>⇥| |↦→→→→→⇥| |↦>\n>⇥| |↦→→→→→⇥| |↦>\n>⇥| |↦→→→→→⇥| |↦>\n>⇥| |↦→→→→→⇥| |↦>\n>⇥| |↦→→→→→⇥| |↦>\n>⇥| |↦→→→→→⇥| |↦>\n>⇥| |↦⇥⚡↦→→⇥| |↦>\n>⇥| |↦→→→→→⇥| |↦>\n>||┤|├|||*P┤|├|\n    |       |\n>||||||||*P||\n\n\"\n\n#guard (bus do\n  let iron0 <- input .ironPlate 10\n  let iron1 <- input .ironPlate 15\n  let _ <- merge iron0 iron1\n).toAscii == s!\"\n\n  ⚡→↓\n   ↑⤓\n  →↑←\n  ↑→↑\n  *S↧\n  ↑↑↓\n>→↑↑→→>\n>→→↑\n\n\"\n\n#guard (bus do\n  let water0 <- input .water 10\n  let water1 <- input .water 15\n  let _ <- merge water0 water1\n).toAscii == s!\"\n\n  |||||\n  | | |\n>|| | ||>\n    |\n>||||\n\n\"\n\n#guard (bus do\n  let iron <- input .ironOre 10\n  let _ <- splitBalanced iron (left:=3)\n).toAscii == s!\"\n\n **\n ++⚡\n ***\n ≥≥≥\n →→→↓\n ↑→↓↓\n ↑↑↓↓\n *S↓↓\n  ↑↓↓\n>→↑↓→→>\n   →→→>\n\n\"\n\n#guard (bus do\n  let bacteria <- input .ironBacteria 10\n  let _iron : BusLane .ironOre 10 <- spoilingChamber bacteria\n).toAscii == s!\"\n\n  →→↓\n →↑←↓\n ↑*S↓\n ↑*↑↓\n ↥S↥↓\n ⇧⇧⇧⤓\n ☐☐☐⚡\n ⇧⇧⇧↧\n ←←←↓\n  →↑↓\n  ↑↓←\n  ↑↓\n>→↑→→>\n\n\"\n"
  },
  {
    "path": "Functorio/Cap.lean",
    "content": "import Functorio.Factory\n\ndef capN {n e s w} (f:Factory n e s w) : Factory [] e s w :=\n  {\n    width:= f.width,\n    height:= f.height,\n    entities := f.entities,\n    wires := f.wires,\n    interface := {\n      n := #v[]\n      e := f.interface.e\n      s := f.interface.s\n      w := f.interface.w\n    }\n    name := f.name\n  }\n\ndef capE {n e s w} (f:Factory n e s w) : Factory n [] s w :=\n  {\n    width:= f.width,\n    height:= f.height,\n    entities := f.entities,\n    wires := f.wires,\n    interface := {\n      n := f.interface.n\n      e := #v[]\n      s := f.interface.s\n      w := f.interface.w\n    }\n    name := f.name\n  }\n\ndef capS {n e s w} (f:Factory n e s w) : Factory n e [] w :=\n  {\n    width:= f.width,\n    height:= f.height,\n    entities := f.entities,\n    wires := f.wires,\n    interface := {\n      n := f.interface.n\n      e := f.interface.e\n      s := #v[]\n      w := f.interface.w\n    }\n    name := f.name\n  }\n\ndef capW {n e s w} (f:Factory n e s w) : Factory n e s [] :=\n  {\n    width:= f.width\n    height:= f.height\n    entities := f.entities\n    wires := f.wires\n    interface := {\n      n := f.interface.n\n      e := f.interface.e\n      s := f.interface.s\n      w := #v[]\n    }\n    name := f.name\n  }\n\ndef capAll {n e s w} (f:Factory n e s w) : Factory [] [] [] [] :=\n  capN (capE (capS (capW f)))\n"
  },
  {
    "path": "Functorio/Column.lean",
    "content": "import Functorio.Factory\nimport Functorio.Expand\nimport Functorio.Adapter\nimport Functorio.Util\n\nprivate def columnPerfect {n e s w e' s' w'} (top : Factory n e s w) (bot:Factory s e' s' w'): Factory n (e ++ e') s' (w ++ w') :=\n  if top.interface.s != bot.interface.n then impossible! \"perfect column must have exactly the same interface positions\" else\n  if top.width != bot.width then impossible! \"perfect column must have exactly the same width\" else\n  let width := top.width\n  {\n    width := width,\n    height := top.height + bot.height,\n    entities := top.entities ++ bot.entities.map (Entity.offsetPosition 0 top.height)\n    wires := top.wires ++ bot.wires.map fun wire => wire.incrementLabels top.entities.length\n    interface :=\n    {\n      n := top.interface.n\n      e := cast (by simp) (top.interface.e ++ increaseOffset top.height (bot.interface.e))\n      s := bot.interface.s\n      w := cast (by simp) (top.interface.w ++ increaseOffset top.height (bot.interface.w))\n    }\n    name := s!\"col({top.name},{bot.name})\"\n  }\n\nprivate def columnSameWidth {n e s w e' s' w'} (top : Factory n e s w) (bot:Factory s e' s' w') (adapterMinHeight:Nat) : Factory n (e ++ e') s' (w ++ w') :=\n  if top.width != bot.width then impossible! s!\"same width of factories expected {top.width}\" else\n  let width := top.width\n  let adapter := adapterV (top.interface.s) (bot.interface.n) width adapterMinHeight\n  columnPerfect top (columnPerfect adapter bot)\n\ndef column {n e s w e' s' w'} (top : Factory n e s w) (bot:Factory s e' s' w') (adapterMinHeight := 0) : Factory n (e ++ e') s' (w ++ w') :=\n  -- Similar code in rowTwo\n\n  -- Align the two factories on their 0th interface,\n  -- then expand the smaller one to match the bigger one's size.\n  if (top.width > bot.width) then\n    let diff := top.width - bot.width\n    let shift := if top.interface.s.isEmpty then 0\n                 else min diff ((top.interface.s)[0]! - (bot.interface.n)[0]!)\n    columnSameWidth top ((bot.expand .E (diff - shift)).expand .W shift) adapterMinHeight else\n  if (top.width < bot.width) then\n    let diff := bot.width - top.width\n    let shift := if top.interface.s.isEmpty then 0\n                 else min diff ((bot.interface.n)[0]! - (top.interface.s)[0]!)\n    columnSameWidth ((top.expand .E (diff - shift)).expand .W shift) bot adapterMinHeight else\n  columnSameWidth top bot adapterMinHeight\n\ndef columnList {i} (fs:List (Factory i [] i [])) : Factory i [] i [] :=\n  match fs with\n  | f::fs => fs.foldl column f\n  | [] => error! \"can't make a column with empty list of factories yet\"\n\ndef column3 {n e s w e' s' w' e'' s'' w''} (top : Factory n e s w) (middle:Factory s e' s' w') (bot:Factory s'  e'' s'' w'') : Factory n (e ++ e' ++ e'') s'' (w ++ w' ++ w'') :=\n  column (column top middle) bot\n"
  },
  {
    "path": "Functorio/Config.lean",
    "content": "class Config where\n  generateRoboports : Bool := false\n  generateBigPoles : Bool := false\n  providerChestCapacity : Nat := 0\n  adapterMinHeight : Nat := 0\n\ninstance : Config where\n"
  },
  {
    "path": "Functorio/Crop.lean",
    "content": "import Functorio.Factory\n\nprivate def cropOption {n e s w} (f:Factory n e s w) : Option (Factory n e s w) :=\n  let xs := f.entities.map (fun e => e.x)\n  let ys := f.entities.map (fun e => e.y)\n  do\n    let xMin <- List.min? xs\n    let xMax <- List.max? xs\n    let yMin <- List.min? ys\n    let yMax <- List.max? ys\n    some {\n      width := xMax - xMin + 1,\n      height:= yMax - yMin + 1,\n      entities:= f.entities.map (fun e => {\n        x := e.x - xMin,\n        y := e.y - yMin,\n        type := e.type\n      }),\n      wires := f.wires\n      interface := {\n        n := decreaseOffset xMin (f.interface.n)\n        e := decreaseOffset yMin (f.interface.e)\n        s := decreaseOffset xMin (f.interface.s)\n        w := decreaseOffset yMin (f.interface.w)\n      }\n      name := f.name\n    }\n\n-- Removes empty space from all sides of the factory.\ndef crop {n e s w} (f:Factory n e s w) : Factory n e s w :=\n  (cropOption f).getD f\n"
  },
  {
    "path": "Functorio/Direction.lean",
    "content": "\ninductive Direction where\n  | N\n  | E\n  | S\n  | W\n  deriving DecidableEq, Repr, Inhabited\n"
  },
  {
    "path": "Functorio/Entity.lean",
    "content": "import Lean.Data.Json\nimport Lean.Data.Json.Printer\nimport Lean.Data.Json.FromToJson\n\nimport Functorio.Direction\nimport Functorio.Recipe\n\nopen Lean\nopen Lean (Json)\n\nstructure Signal where\n  type: Option String\n  name: String\n  deriving DecidableEq, Inhabited, Repr\n\nstructure Condition where\n  firstSignal: Signal\n  secondSignal: Option Signal\n  constantValue: Option Nat\n  comparator: String\n  deriving DecidableEq, Inhabited, Repr\n\nstructure Output where\n  signal: Signal\n  copyCountFromInput : Bool\n  deriving DecidableEq, Inhabited, Repr\n\nstructure BeltControlBehavior where\n  circuitCondition: Option Condition\n  circuitReadHandContents: Bool\n  circuitContentsReadMode: Nat\n  deriving DecidableEq, Inhabited, Repr\n\nstructure ArithmeticCondition where\n  firstSignal: Signal\n  secondConstant: Nat\n  operation: String\n  outputSignal: Signal\n  deriving DecidableEq, Inhabited, Repr\n\ninductive EntityType\n  | belt (dir:Direction) (behavior:BeltControlBehavior := {\n      circuitCondition := .none\n      circuitReadHandContents := false\n      circuitContentsReadMode := 0\n    })\n  | beltDown (direction:Direction)\n  | beltUp (direction:Direction)\n  | splitter (direction:Direction) (outputPriority:Option String) (filter:Option Ingredient)\n  | pipe\n  | pipeToGround (direction:Direction)\n  | pump (direction:Direction)\n  | inserter (direction:Direction) (filter:List Ingredient)\n  | longInserter (direction:Direction) (filter:List Ingredient)\n  | pole\n  | bigPole\n  | fabricator (fabricator:Fabricator) (recipe:RecipeName) (direction:Direction) (mirror:Bool)\n  | heatingTower\n  | roboport\n  | ironChest\n  | passiveProviderChest (capacity:Option Nat)\n  | deciderCombinator (direction:Direction) (conditions:List Condition) (outputs:List Output)\n  | arithmeticCombinator (direction:Direction) (condition:ArithmeticCondition)\n  | refinedConcrete\n  deriving DecidableEq, Inhabited, Repr\n\nstructure Entity where\n  x : Nat\n  y : Nat\n  type : EntityType\n  deriving DecidableEq, Inhabited, Repr\n\ndef belt x y d (behavior : BeltControlBehavior := {\n    circuitCondition := .none\n    circuitReadHandContents := false\n    circuitContentsReadMode := 0\n  })\n:=\n  ({x:=x,y:=y,type:=.belt d behavior} : Entity)\n\ndef beltDown x y d := ({x:=x,y:=y,type:=.beltDown d} : Entity)\n\ndef beltUp x y d := ({x:=x,y:=y,type:=.beltUp d} : Entity)\n\ndef splitter x y d (outputPriority : Option String := .none) (filter := Option.none) := ({x:=x,y:=y,type:=.splitter d outputPriority filter} : Entity)\n\ndef pipe x y := ({x:=x,y:=y,type:=.pipe} : Entity)\n\ndef pipeToGround x y d := ({x:=x,y:=y,type:=.pipeToGround d} : Entity)\n\ndef pump x y d := ({x:=x,y:=y,type:=.pump d} : Entity)\n\ndef inserter x y d (filter:=[]) := ({x:=x,y:=y,type:=.inserter d filter} : Entity)\n\ndef longInserter x y d (filter:=[]) := ({x:=x,y:=y,type:=.longInserter d filter} : Entity)\n\ndef pole x y := ({x:=x,y:=y,type:=.pole} : Entity)\n\ndef bigPole x y := ({x:=x,y:=y,type:=.bigPole} : Entity)\n\ndef fabricator x y f r (d := Direction.N) (mirror:=false) := ({x:=x,y:=y,type:=.fabricator f r d mirror} : Entity)\n\ndef heatingTower x y := ({x:=x,y:=y,type:=.heatingTower} : Entity)\n\ndef assembler r x y (d := Direction.W) := fabricator x y .assemblingMachine3 r d\n\ndef furnace r x y := fabricator x y .electricFurnace r\n\ndef chemicalPlant r x y (mirror:=false) (d := Direction.W) := fabricator x y .chemicalPlant r d mirror\n\ndef refinery r x y (mirror:=false) (d := Direction.E) := fabricator x y .oilRefinery r d mirror\n\ndef rocketSilo x y := fabricator x y .rocketSilo .rocketPart\n\ndef roboport x y := ({x:=x,y:=y,type:=.roboport} : Entity)\n\ndef ironChest x y := ({x:=x,y:=y,type:=.ironChest} : Entity)\n\ndef passiveProviderChest x y (capacity : Option Nat := .none) := ({x:=x,y:=y,type:=.passiveProviderChest capacity} : Entity)\n\ndef refinedConcrete x y := ({x:=x,y:=y,type:=.refinedConcrete} : Entity)\n\ndef deciderCombinator x y (direction:Direction) (conditions:List Condition) (outputs:List Output) :=\n  ({x:=x, y:=y, type:=.deciderCombinator direction conditions outputs} : Entity)\n\ndef arithmeticCombinator x y (direction:Direction) (condition:ArithmeticCondition) :=\n  ({x:=x, y:=y, type:=.arithmeticCombinator direction condition} : Entity)\n\ndef recyler x y d (mirror:=false) := ({x:=x,y:=y,type:=.fabricator .recycler RecipeName.itemUnknownRecycling d mirror} : Entity)\n\nnamespace Entity\n\ndef width (e:Entity) : Nat :=\n  match e.type with\n  | .belt _ _ | .beltDown _ | .beltUp _ | .pipe | .pipeToGround _ | .inserter _ _ | .longInserter _ _\n  | .pole | .ironChest | .passiveProviderChest _ | .refinedConcrete => 1\n  | .bigPole => 2\n  | .splitter dir _ _ => if dir == .N || dir == .S then 2 else 1\n  | .deciderCombinator dir _ _ | .arithmeticCombinator dir _ | .pump dir => if dir == .N || dir == .S then 1 else 2\n  | .heatingTower => 3\n  | .roboport => 4\n  | .fabricator f _ dir _ => if dir == .N || dir == .S then f.width else f.height\n\ndef height (e:Entity) : Nat :=\n  match e.type with\n  | .belt _ _ | .beltDown _ | .beltUp _ | .pipe | .pipeToGround _ | .inserter _ _\n  | .longInserter _ _ | .pole | .ironChest | .passiveProviderChest _ | .refinedConcrete => 1\n  | .bigPole => 2\n  | .splitter dir _ _ => if dir == .N || dir == .S then 1 else 2\n  | .deciderCombinator dir _ _ | .arithmeticCombinator dir _ | .pump dir => if dir == .N || dir == .S then 2 else 1\n  | .heatingTower => 3\n  | .roboport => 4\n  | .fabricator f _ dir _ => if dir == .N || dir == .S then f.height else f.width\n\ndef offsetPosition (dx dy:Nat) (entity:Entity) : Entity := {\n  x := entity.x + dx\n  y := entity.y + dy\n  type := entity.type\n}\n\nend Entity\n\ndef eraseRectangle (x y width height:Nat) (es:List Entity) : List Entity :=\n  es.filter fun e => !(\n    x <= e.x && e.x < x + width &&\n    y <= e.y && e.y < y + height\n  )\n"
  },
  {
    "path": "Functorio/Expand.lean",
    "content": "import Functorio.Factory\n\nprivate def expansionEntity (ingredient:Ingredient) (direction:Direction) (x y:Nat) : Entity :=\n  if ingredient.isLiquid then pipe x y else belt x y direction\n\nprivate def expandS {n e s w} (distance:Nat) (f:Factory n e s w) : Factory n e s w:=\n  let expansion := (List.range' f.height distance).flatMap fun y =>\n    s.mapIdx fun i (ingredient, dir) =>\n      let x := f.interface.s[i]!\n      expansionEntity ingredient dir x y\n\n  {\n    width := f.width,\n    height:= f.height + distance,\n    entities:= f.entities ++ expansion\n    wires := f.wires,\n    interface := f.interface\n    name := f.name\n  }\n\nprivate def expandN {n e s w} (distance:Nat) (f:Factory n e s w) : Factory n e s w:=\n  let expansion := (List.range distance).flatMap fun y =>\n    n.mapIdx fun i (ingredient, dir) =>\n      let x := f.interface.n[i]!\n      expansionEntity ingredient dir x y\n\n  {\n    width := f.width\n    height:= f.height + distance\n    entities:= f.entities.map (Entity.offsetPosition 0 distance) ++ expansion\n    wires := f.wires\n    interface := {\n      n := f.interface.n\n      e := increaseOffset distance (f.interface.e)\n      s := f.interface.s\n      w := increaseOffset distance (f.interface.w)\n    }\n    name := f.name\n  }\n\nprivate def expandE {n e s w} (distance:Nat) (f:Factory n e s w) : Factory n e s w:=\n  let expansion := (List.range' f.width distance).flatMap fun x =>\n    e.mapIdx fun i (ingredient, dir) =>\n      let y := f.interface.e[i]!\n      expansionEntity ingredient dir x y\n\n  {\n    width := f.width + distance,\n    height:= f.height,\n    entities:= f.entities ++ expansion,\n    wires := f.wires\n    interface := f.interface\n    name := f.name\n  }\n\nprivate def expandW {n e s w} (distance:Nat) (f:Factory n e s w) : Factory n e s w:=\n  let expansion := (List.range distance).flatMap fun x =>\n    w.mapIdx fun i (ingredient, dir) =>\n      let y := f.interface.w[i]!\n      expansionEntity ingredient dir x y\n\n  {\n    width := f.width + distance,\n    height:= f.height,\n    entities:= f.entities.map (Entity.offsetPosition distance 0) ++ expansion\n    wires := f.wires\n    interface := {\n      n := increaseOffset distance f.interface.n\n      e := f.interface.e\n      s := increaseOffset distance (f.interface.s)\n      w := f.interface.w\n    }\n    name := f.name\n  }\n\nnamespace Factory\n\ndef expand {n e s w} (direction:Direction) (distance:Nat) (f:Factory n e s w) : Factory n e s w :=\n  match direction with\n  | .N => expandN distance f\n  | .S => expandS distance f\n  | .E => expandE distance f\n  | .W => expandW distance f\n\nend Factory\n"
  },
  {
    "path": "Functorio/ExpandTest.lean",
    "content": "import Functorio.Factory\nimport Functorio.Expand\nimport Functorio.Column\nimport Functorio.Ascii\n\n#guard (\n  (@emptyFactoryH [(.coal,.S), (.coal,.N), (.water,.N)] #v[0,2,4]).expand .S 3\n).toAscii == s!\"\n v ^ ^\n ↓ ↑ |\n ↓ ↑ |\n ↓ ↑ |\n v ^ ^\n\"\n\n#guard (column\n  ((@emptyFactoryH [(.coal,.S), (.coal,.N), (.water,.N)] #v[0,2,4]).expand .N 2)\n  ((@emptyFactoryH [(.coal,.S), (.coal,.N), (.water,.N)] #v[0,2,4]).expand .S 3)\n).toAscii == s!\"\n v ^ ^\n ↓ ↑ |\n ↓ ↑ |\n ↓ ↑ |\n ↓ ↑ |\n ↓ ↑ |\n v ^ ^\n\"\n"
  },
  {
    "path": "Functorio/Factory.lean",
    "content": "import Functorio.Entity\nimport Functorio.Util\nimport Functorio.Recipe\n\n-- coordinates increase from N to S, and W to E\n--\n--   +--->\n--   |\n--   V\n\nabbrev InterfaceImpl := Nat  -- offset\n\ndef increaseOffset {l} (n:Nat) (is:Vector InterfaceImpl l) : Vector InterfaceImpl l :=\n  is.map fun offset => offset + n\n\ndef decreaseOffset {l} (n:Nat) (is:Vector InterfaceImpl l) : Vector InterfaceImpl l :=\n  is.map fun offset => offset - n\n\n-- Horizontal (H) directions\ninductive DirectionH where\n  | E\n  | W\n  deriving DecidableEq, Inhabited, Repr\n\ninstance : Coe DirectionH Direction where\n  coe d := match d with | .E => .E | .W => .W\n\n-- Vertical (V) directions\ninductive DirectionV where\n  | N\n  | S\n  deriving DecidableEq, Inhabited, Repr\n\ninstance : Coe DirectionV Direction where\n  coe d := match d with | .N => .N | .S => .S\n\nabbrev InterfaceH := Ingredient × DirectionH\nabbrev InterfaceV := Ingredient × DirectionV\n\nstructure InterfaceImpls (n:List InterfaceV) (e:List InterfaceH) (s:List InterfaceV) (w:List InterfaceH) where\n  n: Vector InterfaceImpl (n.length)\n  e: Vector InterfaceImpl (e.length)\n  s: Vector InterfaceImpl (s.length)\n  w: Vector InterfaceImpl (w.length)\n  deriving Inhabited, Repr\n\ninductive WireType where\n  | redInput\n  | greenInput\n  | redOutput\n  | greenOutput\n  | copper\n  deriving DecidableEq, Inhabited, Repr\n\nstructure Wire where\n  src: Nat\n  srcType: WireType\n  dst: Nat\n  dstType: WireType\n  deriving DecidableEq, Inhabited, Repr\n\nnamespace Wire\n\ndef incrementLabels (wire:Wire) (n:Nat) : Wire :=\n  {src:=wire.src+n, dst:=wire.dst+n, srcType:=wire.srcType, dstType:=wire.dstType}\n\nend Wire\n\nstructure Factory (n:List InterfaceV) (e:List InterfaceH) (s:List InterfaceV) (w:List InterfaceH) where\n  width: Nat\n  height: Nat\n  entities: List Entity\n  wires: List Wire\n  interface: InterfaceImpls n e s w\n  name: String\n  deriving Repr\n\ndef errorFactory {n e s w} : Factory n e s w := {\n  entities := []\n  wires := []\n  height := max n.length s.length\n  width := max e.length w.length\n  interface := {\n    n := Vector.range (n.length)\n    e := Vector.range (e.length)\n    s := Vector.range (s.length)\n    w := Vector.range (w.length)\n  }\n  name := \"error\"\n}\n\ninstance {n e s w} : Inhabited (Factory n e s w) where\n  default := errorFactory\n\ninstance {n e s w} : ToString (Factory n e s w) where\n  toString f := f.name\n\ndef unsafeFactoryCast {n e s w n' e' s' w'} (f:Factory n e s w) : Factory n' e' s' w' :=\n  match decEq n n', decEq e e', decEq s s', decEq w w' with\n  | isTrue _, isTrue _, isTrue _, isTrue _  => cast (by subst_eqs; simp) f\n  | _, _, _, _ => error! s!\"unsafeFactoryCast failed for {f.name}\"\n\nnamespace Factory\n\ndef setName {n e s w} (name:String) (f:Factory n e s w) : Factory n e s w :=\n  {\n    width:=f.width\n    height:=f.height\n    entities:=f.entities\n    wires := f.wires\n    interface:=f.interface\n    name:=name\n  }\n\nend Factory\n\n\ndef emptyFactoryV {i} (offsets:Vector InterfaceImpl i.length := Vector.range i.length) : Factory [] i [] i := {\n  entities := []\n  wires := []\n  interface := {\n    n := #v[]\n    e := offsets\n    s := #v[]\n    w := offsets\n  }\n  width := 0\n  height := if i.isEmpty then 0 else offsets[i.length-1]! + 1\n  name := s!\"emptyFactoryV {reprStr i}\"\n}\n\ndef emptyFactoryH {i} (offsets:Vector InterfaceImpl i.length := Vector.range i.length) : Factory i [] i [] := {\n  entities := []\n  wires := []\n  interface := {\n    n := offsets\n    e := #v[]\n    s := offsets\n    w := #v[]\n  }\n  width := if i.isEmpty then 0 else offsets[i.length-1]! + 1\n  height := 0\n  name := s!\"emptyFactoryH {reprStr i}\"\n}\n\nnamespace Factory\n\ndef refinedConcreteFloor {n e s w} (f:Factory n e s w) : Factory n e s w := Id.run do\n  let mut tiles : Array Entity := #[]\n\n  for x in List.range f.width do\n    for y in List.range f.height do\n      tiles := tiles.push (refinedConcrete x y)\n\n  { f with entities := f.entities ++ tiles.toList }\n\nend Factory\n"
  },
  {
    "path": "Functorio/Fraction.lean",
    "content": "-- We define our structure, because the standard library\n-- adds a proof to the structure. This means that equal\n-- numbers may not be definitionally equal.\n\nabbrev Fraction := Nat × Nat\n\nnamespace Fraction\n\n@[simp]\ndef num (f:Fraction) : Nat := f.fst\n\n@[simp]\ndef den (f:Fraction) : Nat := f.snd\n\n@[simp]\ndef mk (n m:Nat) : Fraction := (n,m)\n\ndef roundUp (f:Fraction) : Nat :=\n  if f.den == 1\n  then f.num\n  else (f.num / f.den) + 1\n\nend Fraction\n\ninstance : ToString Fraction where\n  toString r := s!\"{r.num}/{r.den}\"\n\n@[simp]\ninstance (n:Nat) : OfNat Fraction n where\n  ofNat := Fraction.mk n 1\n\nattribute [simp] OfNat.ofNat\n\n-- attribute [simp] Nat.gcd\n\n@[simp]\ndef normalize (r:Fraction) : Fraction :=\n  let gcd : Nat := Nat.gcd r.num r.den\n  Fraction.mk (r.num / gcd) (r.den / gcd)\n\n@[simp]\ninstance : Zero Fraction where\n  zero := 0\n\n@[simp]\ninstance : HMul Fraction Fraction Fraction where\n  hMul a b := normalize (Fraction.mk (a.num * b.num) (a.den * b.den))\n\n@[simp]\ninstance : HDiv Fraction Fraction Fraction where\n  hDiv a b := normalize (Fraction.mk (a.num * b.den) (a.den * b.num))\n\n@[simp]\ninstance : HAdd Fraction Fraction Fraction where\n  hAdd a b := normalize (Fraction.mk (a.num * b.den + b.num * a.den) (a.den * b.den))\n\n@[simp]\ninstance : Add Fraction where\n  add a b := normalize (Fraction.mk (a.num * b.den + b.num * a.den) (a.den * b.den))\n\n@[simp]\ninstance : HSub Fraction Fraction Fraction where\n  hSub a b := normalize (Fraction.mk (a.num * b.den - b.num * a.den) (a.den * b.den))\n\n@[simp]\ninstance : Coe Nat Fraction where\n  coe n := Fraction.mk n 1\n\n@[simp]\ninstance : LE Fraction where\n  le a b := a.num * b.den ≤ b.num * a.den\n\n@[simp]\ninstance : LT Fraction where\n  lt a b := a.num * b.den < b.num * a.den\n\n@[simp]\ninstance : DecidableLE Fraction :=\n  fun a b => Nat.decLe (a.num * b.den) (b.num * a.den)\n\n@[simp]\ninstance : DecidableLT Fraction :=\n  fun a b => Nat.decLt (a.num * b.den) (b.num * a.den)\n\n@[simp]\ninstance : Min Fraction where\n  min a b := if a ≤ b then a else b\n"
  },
  {
    "path": "Functorio/Readme.lean",
    "content": "import Functorio.Abbreviations\nimport Functorio.AssemblyLine\nimport Functorio.Bus\n\nnamespace Readme\n\ndef makeIron : IronOre 300 -> Bus (Iron 300) :=\n  busAssemblyLine (recipe .ironPlate) 8\n\ndef makeCopper : CopperOre 150 -> Bus (Copper 150) :=\n  busAssemblyLine (recipe .copperPlate) 4\n\ndef makeGear : Iron 300 -> Bus (Gear 150) :=\n  busAssemblyLine (recipe .ironGearWheel) 1\n\ndef makeRedScience : Copper 150 -> Gear 150 -> Bus (RedScience 150) :=\n  busAssemblyLine (recipe .automationSciencePack) 10\n\ndef makeSteel : Iron 2700 -> Bus (Steel 540) :=\n  busAssemblyLine (recipe .steelPlate) 72\n\ndef makeBelt : Iron 150 -> Gear 150 -> Bus (YellowBelt 300):=\n  busAssemblyLine (recipe .transportBelt) 1\n\ndef makeAcid : Water 6000 -> Sulfur 300 -> Iron 60 ->Bus (Acid 3000) :=\n  busAssemblyLine (recipe .sulfuricAcid) 1\n\ndef redScience := bus do\n  let ironOre <- input .ironOre 300\n  let copperOre <- input .copperOre 150\n\n  let iron : Iron 300 <- makeIron ironOre\n  let copper : Copper 150 <- makeCopper copperOre\n  let gear : Gear 150 <- makeGear iron\n  let _science : RedScience 150 <- makeRedScience copper gear\n\ndef gearFactory := bus do\n  let ironOre : IronOre 300 <- input .ironOre 300\n  let iron : Iron 300 <- makeIron ironOre\n  let _gear <- makeGear iron\n\ndef splitIron := bus do\n  let iron : Iron 450 <- input .ironPlate 450\n  let (iron0, iron1) <- split (left:=300) (right:=150) iron\n  let gear <- makeGear iron0\n  let belt <- makeBelt iron1 gear\n\ndef mergeSteel := bus do\n  let iron0 <- input .ironPlate 2700\n  let iron1 <- input .ironPlate 2700\n  let steel0 <- makeSteel iron0\n  let steel1 <- makeSteel iron1\n  let steel <- merge steel0 steel1\n\ndef acidFactory := bus do\n  let iron <- input .ironPlate 60\n  let sulfur <- input .sulfur 300\n  let water <- input .water 6000\n  let _ <- makeAcid water sulfur iron\n\ndef gearFactory1 :=\n  station (recipe .ironGearWheel)\n\ndef gearFactory3 :=\n  assemblyLine (recipe .ironGearWheel) 3\n\ndef readme :=\n  columnList [\n    capAll redScience.refinedConcreteFloor,\n    capAll gearFactory.refinedConcreteFloor,\n    capAll splitIron.refinedConcreteFloor,\n    capAll mergeSteel.refinedConcreteFloor,\n    capAll acidFactory.refinedConcreteFloor,\n    capAll gearFactory1.refinedConcreteFloor,\n    capAll gearFactory3.refinedConcreteFloor\n  ]\n\nend Readme\n"
  },
  {
    "path": "Functorio/Recipe.lean",
    "content": "-- Generated by generate-recipe.py. Do not modify.\n\nimport Functorio.Fraction\n\nimport Functorio.Direction\n\ninductive Ingredient\n  | accumulator\n  | activeProviderChest\n  | advancedCircuit\n  | agriculturalSciencePack\n  | agriculturalTower\n  | ammonia\n  | ammoniacalSolution\n  | arithmeticCombinator\n  | artificialJellynutSoil\n  | artificialYumakoSoil\n  | artilleryShell\n  | artilleryTurret\n  | artilleryWagon\n  | assemblingMachine1\n  | assemblingMachine2\n  | assemblingMachine3\n  | asteroidCollector\n  | atomicBomb\n  | automationSciencePack\n  | barrel\n  | battery\n  | batteryEquipment\n  | batteryMk2Equipment\n  | batteryMk3Equipment\n  | beacon\n  | beltImmunityEquipment\n  | bigElectricPole\n  | bigMiningDrill\n  | biochamber\n  | bioflux\n  | biolab\n  | biterEgg\n  | blueprint\n  | blueprintBook\n  | boiler\n  | bottomlessChest\n  | bufferChest\n  | bulkInserter\n  | burnerGenerator\n  | burnerInserter\n  | burnerMiningDrill\n  | calcite\n  | cannonShell\n  | captiveBiterSpawner\n  | captureRobotRocket\n  | car\n  | carbon\n  | carbonFiber\n  | carbonicAsteroidChunk\n  | cargoBay\n  | cargoLandingPad\n  | cargoWagon\n  | centrifuge\n  | chemicalPlant\n  | chemicalSciencePack\n  | cliffExplosives\n  | clusterGrenade\n  | coal\n  | coin\n  | combatShotgun\n  | concrete\n  | constantCombinator\n  | constructionRobot\n  | copperBacteria\n  | copperCable\n  | copperOre\n  | copperPlate\n  | crudeOil\n  | crudeOilBarrel\n  | crusher\n  | cryogenicPlant\n  | cryogenicSciencePack\n  | deciderCombinator\n  | deconstructionPlanner\n  | defenderCapsule\n  | depletedUraniumFuelCell\n  | destroyerCapsule\n  | dischargeDefenseEquipment\n  | displayPanel\n  | distractorCapsule\n  | efficiencyModule\n  | efficiencyModule2\n  | efficiencyModule3\n  | electricEnergyInterface\n  | electricEngineUnit\n  | electricFurnace\n  | electricMiningDrill\n  | electrolyte\n  | electromagneticPlant\n  | electromagneticSciencePack\n  | electronicCircuit\n  | emptyModuleSlot\n  | energyShieldEquipment\n  | energyShieldMk2Equipment\n  | engineUnit\n  | exoskeletonEquipment\n  | explosiveCannonShell\n  | explosiveRocket\n  | explosiveUraniumCannonShell\n  | explosives\n  | expressLoader\n  | expressSplitter\n  | expressTransportBelt\n  | expressUndergroundBelt\n  | fastInserter\n  | fastLoader\n  | fastSplitter\n  | fastTransportBelt\n  | fastUndergroundBelt\n  | firearmMagazine\n  | fissionReactorEquipment\n  | flamethrower\n  | flamethrowerAmmo\n  | flamethrowerTurret\n  | fluidWagon\n  | fluorine\n  | fluoroketoneCold\n  | fluoroketoneColdBarrel\n  | fluoroketoneHot\n  | fluoroketoneHotBarrel\n  | flyingRobotFrame\n  | foundation\n  | foundry\n  | fusionGenerator\n  | fusionPowerCell\n  | fusionReactor\n  | fusionReactorEquipment\n  | gate\n  | grenade\n  | gunTurret\n  | hazardConcrete\n  | heatExchanger\n  | heatInterface\n  | heatPipe\n  | heatingTower\n  | heavyArmor\n  | heavyOil\n  | heavyOilBarrel\n  | holmiumOre\n  | holmiumPlate\n  | holmiumSolution\n  | ice\n  | icePlatform\n  | infinityCargoWagon\n  | infinityChest\n  | infinityPipe\n  | inserter\n  | ironBacteria\n  | ironChest\n  | ironGearWheel\n  | ironOre\n  | ironPlate\n  | ironStick\n  | itemUnknown\n  | jelly\n  | jellynut\n  | jellynutSeed\n  | lab\n  | landMine\n  | landfill\n  | laneSplitter\n  | laserTurret\n  | lava\n  | lightArmor\n  | lightOil\n  | lightOilBarrel\n  | lightningCollector\n  | lightningRod\n  | linkedBelt\n  | linkedChest\n  | lithium\n  | lithiumBrine\n  | lithiumPlate\n  | loader\n  | locomotive\n  | logisticRobot\n  | logisticSciencePack\n  | longHandedInserter\n  | lowDensityStructure\n  | lubricant\n  | lubricantBarrel\n  | mechArmor\n  | mediumElectricPole\n  | metallicAsteroidChunk\n  | metallurgicSciencePack\n  | militarySciencePack\n  | modularArmor\n  | moltenCopper\n  | moltenIron\n  | nightVisionEquipment\n  | nuclearFuel\n  | nuclearReactor\n  | nutrients\n  | offshorePump\n  | oilRefinery\n  | oneWayValve\n  | overflowValve\n  | overgrowthJellynutSoil\n  | overgrowthYumakoSoil\n  | oxideAsteroidChunk\n  | passiveProviderChest\n  | pentapodEgg\n  | personalLaserDefenseEquipment\n  | personalRoboportEquipment\n  | personalRoboportMk2Equipment\n  | petroleumGas\n  | petroleumGasBarrel\n  | piercingRoundsMagazine\n  | piercingShotgunShell\n  | pipe\n  | pipeToGround\n  | pistol\n  | plasticBar\n  | poisonCapsule\n  | powerArmor\n  | powerArmorMk2\n  | powerSwitch\n  | processingUnit\n  | productionSciencePack\n  | productivityModule\n  | productivityModule2\n  | productivityModule3\n  | programmableSpeaker\n  | promethiumAsteroidChunk\n  | promethiumSciencePack\n  | proxyContainer\n  | pump\n  | pumpjack\n  | qualityModule\n  | qualityModule2\n  | qualityModule3\n  | quantumProcessor\n  | radar\n  | rail\n  | railChainSignal\n  | railRamp\n  | railSignal\n  | railSupport\n  | railgun\n  | railgunAmmo\n  | railgunTurret\n  | rawFish\n  | recycler\n  | refinedConcrete\n  | refinedHazardConcrete\n  | repairPack\n  | requesterChest\n  | roboport\n  | rocket\n  | rocketFuel\n  | rocketLauncher\n  | rocketPart\n  | rocketSilo\n  | rocketTurret\n  | science\n  | scrap\n  | selectionTool\n  | selectorCombinator\n  | shotgun\n  | shotgunShell\n  | simpleEntityWithForce\n  | simpleEntityWithOwner\n  | slowdownCapsule\n  | smallElectricPole\n  | smallLamp\n  | solarPanel\n  | solarPanelEquipment\n  | solidFuel\n  | spacePlatformFoundation\n  | spacePlatformHub\n  | spacePlatformStarterPack\n  | spaceSciencePack\n  | speedModule\n  | speedModule2\n  | speedModule3\n  | spidertron\n  | splitter\n  | spoilage\n  | stackInserter\n  | steam\n  | steamEngine\n  | steamTurbine\n  | steelChest\n  | steelFurnace\n  | steelPlate\n  | stone\n  | stoneBrick\n  | stoneFurnace\n  | stoneWall\n  | storageChest\n  | storageTank\n  | submachineGun\n  | substation\n  | sulfur\n  | sulfuricAcid\n  | sulfuricAcidBarrel\n  | supercapacitor\n  | superconductor\n  | tank\n  | teslaAmmo\n  | teslaTurret\n  | teslagun\n  | thruster\n  | thrusterFuel\n  | thrusterOxidizer\n  | toolbeltEquipment\n  | topUpValve\n  | trainStop\n  | transportBelt\n  | treeSeed\n  | tungstenCarbide\n  | tungstenOre\n  | tungstenPlate\n  | turboLoader\n  | turboSplitter\n  | turboTransportBelt\n  | turboUndergroundBelt\n  | undergroundBelt\n  | upgradePlanner\n  | uranium235\n  | uranium238\n  | uraniumCannonShell\n  | uraniumFuelCell\n  | uraniumOre\n  | uraniumRoundsMagazine\n  | utilitySciencePack\n  | water\n  | waterBarrel\n  | wood\n  | woodenChest\n  | yumako\n  | yumakoMash\n  | yumakoSeed\n  deriving DecidableEq, Repr, Inhabited\n\ninductive RecipeCategory\n  | advancedCrafting\n  | basicCrafting\n  | captiveSpawnerProcess\n  | centrifuging\n  | chemistry\n  | chemistryOrCryogenics\n  | crafting\n  | craftingWithFluid\n  | craftingWithFluidOrMetallurgy\n  | crushing\n  | cryogenics\n  | cryogenicsOrAssembling\n  | electromagnetics\n  | electronics\n  | electronicsOrAssembling\n  | electronicsWithFluid\n  | metallurgy\n  | metallurgyOrAssembling\n  | oilProcessing\n  | organic\n  | organicOrAssembling\n  | organicOrChemistry\n  | organicOrHandCrafting\n  | parameters\n  | pressing\n  | recycling\n  | recyclingOrHandCrafting\n  | rocketBuilding\n  | smelting\n  deriving DecidableEq, Repr, Inhabited\n\nnamespace Ingredient\n\ndef isLiquid : Ingredient -> Bool\n| .ammonia => true\n| .ammoniacalSolution => true\n| .crudeOil => true\n| .electrolyte => true\n| .fluorine => true\n| .fluoroketoneCold => true\n| .fluoroketoneHot => true\n| .heavyOil => true\n| .holmiumSolution => true\n| .lava => true\n| .lightOil => true\n| .lithiumBrine => true\n| .lubricant => true\n| .moltenCopper => true\n| .moltenIron => true\n| .petroleumGas => true\n| .steam => true\n| .sulfuricAcid => true\n| .thrusterFuel => true\n| .thrusterOxidizer => true\n| .water => true\n| _ => false\n\ndef spoilResult : Ingredient -> Option Ingredient\n| .agriculturalSciencePack => .some spoilage\n| .bioflux => .some spoilage\n| .copperBacteria => .some copperOre\n| .ironBacteria => .some ironOre\n| .jelly => .some spoilage\n| .jellynut => .some spoilage\n| .nutrients => .some spoilage\n| .rawFish => .some spoilage\n| .yumako => .some spoilage\n| .yumakoMash => .some spoilage\n| _ => .none\n\ndef name : Ingredient -> String\n| .accumulator => \"accumulator\"\n| .activeProviderChest => \"active-provider-chest\"\n| .advancedCircuit => \"advanced-circuit\"\n| .agriculturalSciencePack => \"agricultural-science-pack\"\n| .agriculturalTower => \"agricultural-tower\"\n| .ammonia => \"ammonia\"\n| .ammoniacalSolution => \"ammoniacal-solution\"\n| .arithmeticCombinator => \"arithmetic-combinator\"\n| .artificialJellynutSoil => \"artificial-jellynut-soil\"\n| .artificialYumakoSoil => \"artificial-yumako-soil\"\n| .artilleryShell => \"artillery-shell\"\n| .artilleryTurret => \"artillery-turret\"\n| .artilleryWagon => \"artillery-wagon\"\n| .assemblingMachine1 => \"assembling-machine-1\"\n| .assemblingMachine2 => \"assembling-machine-2\"\n| .assemblingMachine3 => \"assembling-machine-3\"\n| .asteroidCollector => \"asteroid-collector\"\n| .atomicBomb => \"atomic-bomb\"\n| .automationSciencePack => \"automation-science-pack\"\n| .barrel => \"barrel\"\n| .battery => \"battery\"\n| .batteryEquipment => \"battery-equipment\"\n| .batteryMk2Equipment => \"battery-mk2-equipment\"\n| .batteryMk3Equipment => \"battery-mk3-equipment\"\n| .beacon => \"beacon\"\n| .beltImmunityEquipment => \"belt-immunity-equipment\"\n| .bigElectricPole => \"big-electric-pole\"\n| .bigMiningDrill => \"big-mining-drill\"\n| .biochamber => \"biochamber\"\n| .bioflux => \"bioflux\"\n| .biolab => \"biolab\"\n| .biterEgg => \"biter-egg\"\n| .blueprint => \"blueprint\"\n| .blueprintBook => \"blueprint-book\"\n| .boiler => \"boiler\"\n| .bottomlessChest => \"bottomless-chest\"\n| .bufferChest => \"buffer-chest\"\n| .bulkInserter => \"bulk-inserter\"\n| .burnerGenerator => \"burner-generator\"\n| .burnerInserter => \"burner-inserter\"\n| .burnerMiningDrill => \"burner-mining-drill\"\n| .calcite => \"calcite\"\n| .cannonShell => \"cannon-shell\"\n| .captiveBiterSpawner => \"captive-biter-spawner\"\n| .captureRobotRocket => \"capture-robot-rocket\"\n| .car => \"car\"\n| .carbon => \"carbon\"\n| .carbonFiber => \"carbon-fiber\"\n| .carbonicAsteroidChunk => \"carbonic-asteroid-chunk\"\n| .cargoBay => \"cargo-bay\"\n| .cargoLandingPad => \"cargo-landing-pad\"\n| .cargoWagon => \"cargo-wagon\"\n| .centrifuge => \"centrifuge\"\n| .chemicalPlant => \"chemical-plant\"\n| .chemicalSciencePack => \"chemical-science-pack\"\n| .cliffExplosives => \"cliff-explosives\"\n| .clusterGrenade => \"cluster-grenade\"\n| .coal => \"coal\"\n| .coin => \"coin\"\n| .combatShotgun => \"combat-shotgun\"\n| .concrete => \"concrete\"\n| .constantCombinator => \"constant-combinator\"\n| .constructionRobot => \"construction-robot\"\n| .copperBacteria => \"copper-bacteria\"\n| .copperCable => \"copper-cable\"\n| .copperOre => \"copper-ore\"\n| .copperPlate => \"copper-plate\"\n| .crudeOil => \"crude-oil\"\n| .crudeOilBarrel => \"crude-oil-barrel\"\n| .crusher => \"crusher\"\n| .cryogenicPlant => \"cryogenic-plant\"\n| .cryogenicSciencePack => \"cryogenic-science-pack\"\n| .deciderCombinator => \"decider-combinator\"\n| .deconstructionPlanner => \"deconstruction-planner\"\n| .defenderCapsule => \"defender-capsule\"\n| .depletedUraniumFuelCell => \"depleted-uranium-fuel-cell\"\n| .destroyerCapsule => \"destroyer-capsule\"\n| .dischargeDefenseEquipment => \"discharge-defense-equipment\"\n| .displayPanel => \"display-panel\"\n| .distractorCapsule => \"distractor-capsule\"\n| .efficiencyModule => \"efficiency-module\"\n| .efficiencyModule2 => \"efficiency-module-2\"\n| .efficiencyModule3 => \"efficiency-module-3\"\n| .electricEnergyInterface => \"electric-energy-interface\"\n| .electricEngineUnit => \"electric-engine-unit\"\n| .electricFurnace => \"electric-furnace\"\n| .electricMiningDrill => \"electric-mining-drill\"\n| .electrolyte => \"electrolyte\"\n| .electromagneticPlant => \"electromagnetic-plant\"\n| .electromagneticSciencePack => \"electromagnetic-science-pack\"\n| .electronicCircuit => \"electronic-circuit\"\n| .emptyModuleSlot => \"empty-module-slot\"\n| .energyShieldEquipment => \"energy-shield-equipment\"\n| .energyShieldMk2Equipment => \"energy-shield-mk2-equipment\"\n| .engineUnit => \"engine-unit\"\n| .exoskeletonEquipment => \"exoskeleton-equipment\"\n| .explosiveCannonShell => \"explosive-cannon-shell\"\n| .explosiveRocket => \"explosive-rocket\"\n| .explosiveUraniumCannonShell => \"explosive-uranium-cannon-shell\"\n| .explosives => \"explosives\"\n| .expressLoader => \"express-loader\"\n| .expressSplitter => \"express-splitter\"\n| .expressTransportBelt => \"express-transport-belt\"\n| .expressUndergroundBelt => \"express-underground-belt\"\n| .fastInserter => \"fast-inserter\"\n| .fastLoader => \"fast-loader\"\n| .fastSplitter => \"fast-splitter\"\n| .fastTransportBelt => \"fast-transport-belt\"\n| .fastUndergroundBelt => \"fast-underground-belt\"\n| .firearmMagazine => \"firearm-magazine\"\n| .fissionReactorEquipment => \"fission-reactor-equipment\"\n| .flamethrower => \"flamethrower\"\n| .flamethrowerAmmo => \"flamethrower-ammo\"\n| .flamethrowerTurret => \"flamethrower-turret\"\n| .fluidWagon => \"fluid-wagon\"\n| .fluorine => \"fluorine\"\n| .fluoroketoneCold => \"fluoroketone-cold\"\n| .fluoroketoneColdBarrel => \"fluoroketone-cold-barrel\"\n| .fluoroketoneHot => \"fluoroketone-hot\"\n| .fluoroketoneHotBarrel => \"fluoroketone-hot-barrel\"\n| .flyingRobotFrame => \"flying-robot-frame\"\n| .foundation => \"foundation\"\n| .foundry => \"foundry\"\n| .fusionGenerator => \"fusion-generator\"\n| .fusionPowerCell => \"fusion-power-cell\"\n| .fusionReactor => \"fusion-reactor\"\n| .fusionReactorEquipment => \"fusion-reactor-equipment\"\n| .gate => \"gate\"\n| .grenade => \"grenade\"\n| .gunTurret => \"gun-turret\"\n| .hazardConcrete => \"hazard-concrete\"\n| .heatExchanger => \"heat-exchanger\"\n| .heatInterface => \"heat-interface\"\n| .heatPipe => \"heat-pipe\"\n| .heatingTower => \"heating-tower\"\n| .heavyArmor => \"heavy-armor\"\n| .heavyOil => \"heavy-oil\"\n| .heavyOilBarrel => \"heavy-oil-barrel\"\n| .holmiumOre => \"holmium-ore\"\n| .holmiumPlate => \"holmium-plate\"\n| .holmiumSolution => \"holmium-solution\"\n| .ice => \"ice\"\n| .icePlatform => \"ice-platform\"\n| .infinityCargoWagon => \"infinity-cargo-wagon\"\n| .infinityChest => \"infinity-chest\"\n| .infinityPipe => \"infinity-pipe\"\n| .inserter => \"inserter\"\n| .ironBacteria => \"iron-bacteria\"\n| .ironChest => \"iron-chest\"\n| .ironGearWheel => \"iron-gear-wheel\"\n| .ironOre => \"iron-ore\"\n| .ironPlate => \"iron-plate\"\n| .ironStick => \"iron-stick\"\n| .itemUnknown => \"item-unknown\"\n| .jelly => \"jelly\"\n| .jellynut => \"jellynut\"\n| .jellynutSeed => \"jellynut-seed\"\n| .lab => \"lab\"\n| .landMine => \"land-mine\"\n| .landfill => \"landfill\"\n| .laneSplitter => \"lane-splitter\"\n| .laserTurret => \"laser-turret\"\n| .lava => \"lava\"\n| .lightArmor => \"light-armor\"\n| .lightOil => \"light-oil\"\n| .lightOilBarrel => \"light-oil-barrel\"\n| .lightningCollector => \"lightning-collector\"\n| .lightningRod => \"lightning-rod\"\n| .linkedBelt => \"linked-belt\"\n| .linkedChest => \"linked-chest\"\n| .lithium => \"lithium\"\n| .lithiumBrine => \"lithium-brine\"\n| .lithiumPlate => \"lithium-plate\"\n| .loader => \"loader\"\n| .locomotive => \"locomotive\"\n| .logisticRobot => \"logistic-robot\"\n| .logisticSciencePack => \"logistic-science-pack\"\n| .longHandedInserter => \"long-handed-inserter\"\n| .lowDensityStructure => \"low-density-structure\"\n| .lubricant => \"lubricant\"\n| .lubricantBarrel => \"lubricant-barrel\"\n| .mechArmor => \"mech-armor\"\n| .mediumElectricPole => \"medium-electric-pole\"\n| .metallicAsteroidChunk => \"metallic-asteroid-chunk\"\n| .metallurgicSciencePack => \"metallurgic-science-pack\"\n| .militarySciencePack => \"military-science-pack\"\n| .modularArmor => \"modular-armor\"\n| .moltenCopper => \"molten-copper\"\n| .moltenIron => \"molten-iron\"\n| .nightVisionEquipment => \"night-vision-equipment\"\n| .nuclearFuel => \"nuclear-fuel\"\n| .nuclearReactor => \"nuclear-reactor\"\n| .nutrients => \"nutrients\"\n| .offshorePump => \"offshore-pump\"\n| .oilRefinery => \"oil-refinery\"\n| .oneWayValve => \"one-way-valve\"\n| .overflowValve => \"overflow-valve\"\n| .overgrowthJellynutSoil => \"overgrowth-jellynut-soil\"\n| .overgrowthYumakoSoil => \"overgrowth-yumako-soil\"\n| .oxideAsteroidChunk => \"oxide-asteroid-chunk\"\n| .passiveProviderChest => \"passive-provider-chest\"\n| .pentapodEgg => \"pentapod-egg\"\n| .personalLaserDefenseEquipment => \"personal-laser-defense-equipment\"\n| .personalRoboportEquipment => \"personal-roboport-equipment\"\n| .personalRoboportMk2Equipment => \"personal-roboport-mk2-equipment\"\n| .petroleumGas => \"petroleum-gas\"\n| .petroleumGasBarrel => \"petroleum-gas-barrel\"\n| .piercingRoundsMagazine => \"piercing-rounds-magazine\"\n| .piercingShotgunShell => \"piercing-shotgun-shell\"\n| .pipe => \"pipe\"\n| .pipeToGround => \"pipe-to-ground\"\n| .pistol => \"pistol\"\n| .plasticBar => \"plastic-bar\"\n| .poisonCapsule => \"poison-capsule\"\n| .powerArmor => \"power-armor\"\n| .powerArmorMk2 => \"power-armor-mk2\"\n| .powerSwitch => \"power-switch\"\n| .processingUnit => \"processing-unit\"\n| .productionSciencePack => \"production-science-pack\"\n| .productivityModule => \"productivity-module\"\n| .productivityModule2 => \"productivity-module-2\"\n| .productivityModule3 => \"productivity-module-3\"\n| .programmableSpeaker => \"programmable-speaker\"\n| .promethiumAsteroidChunk => \"promethium-asteroid-chunk\"\n| .promethiumSciencePack => \"promethium-science-pack\"\n| .proxyContainer => \"proxy-container\"\n| .pump => \"pump\"\n| .pumpjack => \"pumpjack\"\n| .qualityModule => \"quality-module\"\n| .qualityModule2 => \"quality-module-2\"\n| .qualityModule3 => \"quality-module-3\"\n| .quantumProcessor => \"quantum-processor\"\n| .radar => \"radar\"\n| .rail => \"rail\"\n| .railChainSignal => \"rail-chain-signal\"\n| .railRamp => \"rail-ramp\"\n| .railSignal => \"rail-signal\"\n| .railSupport => \"rail-support\"\n| .railgun => \"railgun\"\n| .railgunAmmo => \"railgun-ammo\"\n| .railgunTurret => \"railgun-turret\"\n| .rawFish => \"raw-fish\"\n| .recycler => \"recycler\"\n| .refinedConcrete => \"refined-concrete\"\n| .refinedHazardConcrete => \"refined-hazard-concrete\"\n| .repairPack => \"repair-pack\"\n| .requesterChest => \"requester-chest\"\n| .roboport => \"roboport\"\n| .rocket => \"rocket\"\n| .rocketFuel => \"rocket-fuel\"\n| .rocketLauncher => \"rocket-launcher\"\n| .rocketPart => \"rocket-part\"\n| .rocketSilo => \"rocket-silo\"\n| .rocketTurret => \"rocket-turret\"\n| .science => \"science\"\n| .scrap => \"scrap\"\n| .selectionTool => \"selection-tool\"\n| .selectorCombinator => \"selector-combinator\"\n| .shotgun => \"shotgun\"\n| .shotgunShell => \"shotgun-shell\"\n| .simpleEntityWithForce => \"simple-entity-with-force\"\n| .simpleEntityWithOwner => \"simple-entity-with-owner\"\n| .slowdownCapsule => \"slowdown-capsule\"\n| .smallElectricPole => \"small-electric-pole\"\n| .smallLamp => \"small-lamp\"\n| .solarPanel => \"solar-panel\"\n| .solarPanelEquipment => \"solar-panel-equipment\"\n| .solidFuel => \"solid-fuel\"\n| .spacePlatformFoundation => \"space-platform-foundation\"\n| .spacePlatformHub => \"space-platform-hub\"\n| .spacePlatformStarterPack => \"space-platform-starter-pack\"\n| .spaceSciencePack => \"space-science-pack\"\n| .speedModule => \"speed-module\"\n| .speedModule2 => \"speed-module-2\"\n| .speedModule3 => \"speed-module-3\"\n| .spidertron => \"spidertron\"\n| .splitter => \"splitter\"\n| .spoilage => \"spoilage\"\n| .stackInserter => \"stack-inserter\"\n| .steam => \"steam\"\n| .steamEngine => \"steam-engine\"\n| .steamTurbine => \"steam-turbine\"\n| .steelChest => \"steel-chest\"\n| .steelFurnace => \"steel-furnace\"\n| .steelPlate => \"steel-plate\"\n| .stone => \"stone\"\n| .stoneBrick => \"stone-brick\"\n| .stoneFurnace => \"stone-furnace\"\n| .stoneWall => \"stone-wall\"\n| .storageChest => \"storage-chest\"\n| .storageTank => \"storage-tank\"\n| .submachineGun => \"submachine-gun\"\n| .substation => \"substation\"\n| .sulfur => \"sulfur\"\n| .sulfuricAcid => \"sulfuric-acid\"\n| .sulfuricAcidBarrel => \"sulfuric-acid-barrel\"\n| .supercapacitor => \"supercapacitor\"\n| .superconductor => \"superconductor\"\n| .tank => \"tank\"\n| .teslaAmmo => \"tesla-ammo\"\n| .teslaTurret => \"tesla-turret\"\n| .teslagun => \"teslagun\"\n| .thruster => \"thruster\"\n| .thrusterFuel => \"thruster-fuel\"\n| .thrusterOxidizer => \"thruster-oxidizer\"\n| .toolbeltEquipment => \"toolbelt-equipment\"\n| .topUpValve => \"top-up-valve\"\n| .trainStop => \"train-stop\"\n| .transportBelt => \"transport-belt\"\n| .treeSeed => \"tree-seed\"\n| .tungstenCarbide => \"tungsten-carbide\"\n| .tungstenOre => \"tungsten-ore\"\n| .tungstenPlate => \"tungsten-plate\"\n| .turboLoader => \"turbo-loader\"\n| .turboSplitter => \"turbo-splitter\"\n| .turboTransportBelt => \"turbo-transport-belt\"\n| .turboUndergroundBelt => \"turbo-underground-belt\"\n| .undergroundBelt => \"underground-belt\"\n| .upgradePlanner => \"upgrade-planner\"\n| .uranium235 => \"uranium-235\"\n| .uranium238 => \"uranium-238\"\n| .uraniumCannonShell => \"uranium-cannon-shell\"\n| .uraniumFuelCell => \"uranium-fuel-cell\"\n| .uraniumOre => \"uranium-ore\"\n| .uraniumRoundsMagazine => \"uranium-rounds-magazine\"\n| .utilitySciencePack => \"utility-science-pack\"\n| .water => \"water\"\n| .waterBarrel => \"water-barrel\"\n| .wood => \"wood\"\n| .woodenChest => \"wooden-chest\"\n| .yumako => \"yumako\"\n| .yumakoMash => \"yumako-mash\"\n| .yumakoSeed => \"yumako-seed\"\n\nend Ingredient\n\nstructure Recipe where\n  name: String\n  -- The `Fraction` indicates how many items are needed to execute the recipe.\n  inputs : List (Fraction × Ingredient)\n  -- The `Fraction` indicates how many output items are generated by executing the recipe.\n  outputs : List (Fraction × Ingredient)\n  category : RecipeCategory\n  -- Number of seconds that it takes the user to execute the recipe.\n  time : Fraction\n  deriving DecidableEq, Repr, Inhabited\n\ninductive FluidBoxType where\n  | input\n  | output\n  | inputOutput\n  deriving DecidableEq, Repr, Inhabited\n\nstructure FluidBox where\n  side: Direction\n  offset: Nat\n  type: FluidBoxType\n  deriving DecidableEq, Repr, Inhabited\n\nstructure FabricatorDetails where\n  name : String\n  width : Nat\n  height : Nat\n  speedup : Fraction\n  productivity : Fraction\n  moduleSlots : Nat\n  fluidBoxes : List FluidBox\n  categories : List RecipeCategory\n  deriving DecidableEq, Repr, Inhabited\n\ninductive RecipeName\n  | accumulator\n  | accumulatorRecycling\n  | acidNeutralisation\n  | activeProviderChest\n  | activeProviderChestRecycling\n  | advancedCarbonicAsteroidCrushing\n  | advancedCircuit\n  | advancedCircuitRecycling\n  | advancedMetallicAsteroidCrushing\n  | advancedOilProcessing\n  | advancedOxideAsteroidCrushing\n  | advancedThrusterFuel\n  | advancedThrusterOxidizer\n  | agriculturalSciencePack\n  | agriculturalSciencePackRecycling\n  | agriculturalTower\n  | agriculturalTowerRecycling\n  | ammoniaRocketFuel\n  | ammoniacalSolutionSeparation\n  | arithmeticCombinator\n  | arithmeticCombinatorRecycling\n  | artificialJellynutSoil\n  | artificialJellynutSoilRecycling\n  | artificialYumakoSoil\n  | artificialYumakoSoilRecycling\n  | artilleryShell\n  | artilleryShellRecycling\n  | artilleryTurret\n  | artilleryTurretRecycling\n  | artilleryWagon\n  | artilleryWagonRecycling\n  | assemblingMachine1\n  | assemblingMachine1Recycling\n  | assemblingMachine2\n  | assemblingMachine2Recycling\n  | assemblingMachine3\n  | assemblingMachine3Recycling\n  | asteroidCollector\n  | asteroidCollectorRecycling\n  | atomicBomb\n  | atomicBombRecycling\n  | automationSciencePack\n  | automationSciencePackRecycling\n  | barrel\n  | barrelRecycling\n  | basicOilProcessing\n  | battery\n  | batteryEquipment\n  | batteryEquipmentRecycling\n  | batteryMk2Equipment\n  | batteryMk2EquipmentRecycling\n  | batteryMk3Equipment\n  | batteryMk3EquipmentRecycling\n  | batteryRecycling\n  | beacon\n  | beaconRecycling\n  | beltImmunityEquipment\n  | beltImmunityEquipmentRecycling\n  | bigElectricPole\n  | bigElectricPoleRecycling\n  | bigMiningDrill\n  | bigMiningDrillRecycling\n  | biochamber\n  | biochamberRecycling\n  | bioflux\n  | biofluxRecycling\n  | biolab\n  | biolabRecycling\n  | biolubricant\n  | bioplastic\n  | biosulfur\n  | biterEgg\n  | biterEggRecycling\n  | blueprintBookRecycling\n  | blueprintRecycling\n  | boiler\n  | boilerRecycling\n  | bottomlessChestRecycling\n  | bufferChest\n  | bufferChestRecycling\n  | bulkInserter\n  | bulkInserterRecycling\n  | burnerGeneratorRecycling\n  | burnerInserter\n  | burnerInserterRecycling\n  | burnerMiningDrill\n  | burnerMiningDrillRecycling\n  | burntSpoilage\n  | calciteRecycling\n  | cannonShell\n  | cannonShellRecycling\n  | captiveBiterSpawner\n  | captiveBiterSpawnerRecycling\n  | captureRobotRocket\n  | captureRobotRocketRecycling\n  | car\n  | carRecycling\n  | carbon\n  | carbonFiber\n  | carbonFiberRecycling\n  | carbonRecycling\n  | carbonicAsteroidChunkRecycling\n  | carbonicAsteroidCrushing\n  | carbonicAsteroidReprocessing\n  | cargoBay\n  | cargoBayRecycling\n  | cargoLandingPad\n  | cargoLandingPadRecycling\n  | cargoWagon\n  | cargoWagonRecycling\n  | castingCopper\n  | castingCopperCable\n  | castingIron\n  | castingIronGearWheel\n  | castingIronStick\n  | castingLowDensityStructure\n  | castingPipe\n  | castingPipeToGround\n  | castingSteel\n  | centrifuge\n  | centrifugeRecycling\n  | chemicalPlant\n  | chemicalPlantRecycling\n  | chemicalSciencePack\n  | chemicalSciencePackRecycling\n  | cliffExplosives\n  | cliffExplosivesRecycling\n  | clusterGrenade\n  | clusterGrenadeRecycling\n  | coalLiquefaction\n  | coalRecycling\n  | coalSynthesis\n  | coinRecycling\n  | combatShotgun\n  | combatShotgunRecycling\n  | concrete\n  | concreteFromMoltenIron\n  | concreteRecycling\n  | constantCombinator\n  | constantCombinatorRecycling\n  | constructionRobot\n  | constructionRobotRecycling\n  | copperBacteria\n  | copperBacteriaCultivation\n  | copperBacteriaRecycling\n  | copperCable\n  | copperCableRecycling\n  | copperOreRecycling\n  | copperPlate\n  | copperPlateRecycling\n  | crudeOilBarrel\n  | crudeOilBarrelRecycling\n  | crusher\n  | crusherRecycling\n  | cryogenicPlant\n  | cryogenicPlantRecycling\n  | cryogenicSciencePack\n  | cryogenicSciencePackRecycling\n  | deciderCombinator\n  | deciderCombinatorRecycling\n  | deconstructionPlannerRecycling\n  | defenderCapsule\n  | defenderCapsuleRecycling\n  | depletedUraniumFuelCellRecycling\n  | destroyerCapsule\n  | destroyerCapsuleRecycling\n  | dischargeDefenseEquipment\n  | dischargeDefenseEquipmentRecycling\n  | displayPanel\n  | displayPanelRecycling\n  | distractorCapsule\n  | distractorCapsuleRecycling\n  | efficiencyModule\n  | efficiencyModule2\n  | efficiencyModule2Recycling\n  | efficiencyModule3\n  | efficiencyModule3Recycling\n  | efficiencyModuleRecycling\n  | electricEnergyInterfaceRecycling\n  | electricEngineUnit\n  | electricEngineUnitRecycling\n  | electricFurnace\n  | electricFurnaceRecycling\n  | electricMiningDrill\n  | electricMiningDrillRecycling\n  | electrolyte\n  | electromagneticPlant\n  | electromagneticPlantRecycling\n  | electromagneticSciencePack\n  | electromagneticSciencePackRecycling\n  | electronicCircuit\n  | electronicCircuitRecycling\n  | emptyCrudeOilBarrel\n  | emptyFluoroketoneColdBarrel\n  | emptyFluoroketoneHotBarrel\n  | emptyHeavyOilBarrel\n  | emptyLightOilBarrel\n  | emptyLubricantBarrel\n  | emptyModuleSlotRecycling\n  | emptyPetroleumGasBarrel\n  | emptySulfuricAcidBarrel\n  | emptyWaterBarrel\n  | energyShieldEquipment\n  | energyShieldEquipmentRecycling\n  | energyShieldMk2Equipment\n  | energyShieldMk2EquipmentRecycling\n  | engineUnit\n  | engineUnitRecycling\n  | exoskeletonEquipment\n  | exoskeletonEquipmentRecycling\n  | explosiveCannonShell\n  | explosiveCannonShellRecycling\n  | explosiveRocket\n  | explosiveRocketRecycling\n  | explosiveUraniumCannonShell\n  | explosiveUraniumCannonShellRecycling\n  | explosives\n  | explosivesRecycling\n  | expressLoader\n  | expressLoaderRecycling\n  | expressSplitter\n  | expressSplitterRecycling\n  | expressTransportBelt\n  | expressTransportBeltRecycling\n  | expressUndergroundBelt\n  | expressUndergroundBeltRecycling\n  | fastInserter\n  | fastInserterRecycling\n  | fastLoader\n  | fastLoaderRecycling\n  | fastSplitter\n  | fastSplitterRecycling\n  | fastTransportBelt\n  | fastTransportBeltRecycling\n  | fastUndergroundBelt\n  | fastUndergroundBeltRecycling\n  | firearmMagazine\n  | firearmMagazineRecycling\n  | fishBreeding\n  | fissionReactorEquipment\n  | fissionReactorEquipmentRecycling\n  | flamethrower\n  | flamethrowerAmmo\n  | flamethrowerAmmoRecycling\n  | flamethrowerRecycling\n  | flamethrowerTurret\n  | flamethrowerTurretRecycling\n  | fluidWagon\n  | fluidWagonRecycling\n  | fluoroketone\n  | fluoroketoneColdBarrel\n  | fluoroketoneColdBarrelRecycling\n  | fluoroketoneCooling\n  | fluoroketoneHotBarrel\n  | fluoroketoneHotBarrelRecycling\n  | flyingRobotFrame\n  | flyingRobotFrameRecycling\n  | foundation\n  | foundationRecycling\n  | foundry\n  | foundryRecycling\n  | fusionGenerator\n  | fusionGeneratorRecycling\n  | fusionPowerCell\n  | fusionPowerCellRecycling\n  | fusionReactor\n  | fusionReactorEquipment\n  | fusionReactorEquipmentRecycling\n  | fusionReactorRecycling\n  | gate\n  | gateRecycling\n  | grenade\n  | grenadeRecycling\n  | gunTurret\n  | gunTurretRecycling\n  | hazardConcrete\n  | hazardConcreteRecycling\n  | heatExchanger\n  | heatExchangerRecycling\n  | heatInterface\n  | heatInterfaceRecycling\n  | heatPipe\n  | heatPipeRecycling\n  | heatingTower\n  | heatingTowerRecycling\n  | heavyArmor\n  | heavyArmorRecycling\n  | heavyOilBarrel\n  | heavyOilBarrelRecycling\n  | heavyOilCracking\n  | holmiumOreRecycling\n  | holmiumPlate\n  | holmiumPlateRecycling\n  | holmiumSolution\n  | iceMelting\n  | icePlatform\n  | icePlatformRecycling\n  | iceRecycling\n  | infinityCargoWagonRecycling\n  | infinityChest\n  | infinityChestRecycling\n  | infinityPipe\n  | infinityPipeRecycling\n  | inserter\n  | inserterRecycling\n  | ironBacteria\n  | ironBacteriaCultivation\n  | ironBacteriaRecycling\n  | ironChest\n  | ironChestRecycling\n  | ironGearWheel\n  | ironGearWheelRecycling\n  | ironOreRecycling\n  | ironPlate\n  | ironPlateRecycling\n  | ironStick\n  | ironStickRecycling\n  | itemUnknownRecycling\n  | jellyRecycling\n  | jellynutProcessing\n  | jellynutRecycling\n  | jellynutSeedRecycling\n  | kovarexEnrichmentProcess\n  | lab\n  | labRecycling\n  | landMine\n  | landMineRecycling\n  | landfill\n  | landfillRecycling\n  | laneSplitterRecycling\n  | laserTurret\n  | laserTurretRecycling\n  | lightArmor\n  | lightArmorRecycling\n  | lightOilBarrel\n  | lightOilBarrelRecycling\n  | lightOilCracking\n  | lightningCollector\n  | lightningCollectorRecycling\n  | lightningRod\n  | lightningRodRecycling\n  | linkedBeltRecycling\n  | linkedChestRecycling\n  | lithium\n  | lithiumPlate\n  | lithiumPlateRecycling\n  | lithiumRecycling\n  | loader\n  | loaderRecycling\n  | locomotive\n  | locomotiveRecycling\n  | logisticRobot\n  | logisticRobotRecycling\n  | logisticSciencePack\n  | logisticSciencePackRecycling\n  | longHandedInserter\n  | longHandedInserterRecycling\n  | lowDensityStructure\n  | lowDensityStructureRecycling\n  | lubricant\n  | lubricantBarrel\n  | lubricantBarrelRecycling\n  | mechArmor\n  | mechArmorRecycling\n  | mediumElectricPole\n  | mediumElectricPoleRecycling\n  | metallicAsteroidChunkRecycling\n  | metallicAsteroidCrushing\n  | metallicAsteroidReprocessing\n  | metallurgicSciencePack\n  | metallurgicSciencePackRecycling\n  | militarySciencePack\n  | militarySciencePackRecycling\n  | modularArmor\n  | modularArmorRecycling\n  | moltenCopper\n  | moltenCopperFromLava\n  | moltenIron\n  | moltenIronFromLava\n  | nightVisionEquipment\n  | nightVisionEquipmentRecycling\n  | nuclearFuel\n  | nuclearFuelRecycling\n  | nuclearFuelReprocessing\n  | nuclearReactor\n  | nuclearReactorRecycling\n  | nutrientsFromBioflux\n  | nutrientsFromBiterEgg\n  | nutrientsFromFish\n  | nutrientsFromSpoilage\n  | nutrientsFromYumakoMash\n  | nutrientsRecycling\n  | offshorePump\n  | offshorePumpRecycling\n  | oilRefinery\n  | oilRefineryRecycling\n  | oneWayValveRecycling\n  | overflowValveRecycling\n  | overgrowthJellynutSoil\n  | overgrowthJellynutSoilRecycling\n  | overgrowthYumakoSoil\n  | overgrowthYumakoSoilRecycling\n  | oxideAsteroidChunkRecycling\n  | oxideAsteroidCrushing\n  | oxideAsteroidReprocessing\n  | parameter0\n  | parameter1\n  | parameter2\n  | parameter3\n  | parameter4\n  | parameter5\n  | parameter6\n  | parameter7\n  | parameter8\n  | parameter9\n  | passiveProviderChest\n  | passiveProviderChestRecycling\n  | pentapodEgg\n  | pentapodEggRecycling\n  | personalLaserDefenseEquipment\n  | personalLaserDefenseEquipmentRecycling\n  | personalRoboportEquipment\n  | personalRoboportEquipmentRecycling\n  | personalRoboportMk2Equipment\n  | personalRoboportMk2EquipmentRecycling\n  | petroleumGasBarrel\n  | petroleumGasBarrelRecycling\n  | piercingRoundsMagazine\n  | piercingRoundsMagazineRecycling\n  | piercingShotgunShell\n  | piercingShotgunShellRecycling\n  | pipe\n  | pipeRecycling\n  | pipeToGround\n  | pipeToGroundRecycling\n  | pistol\n  | pistolRecycling\n  | plasticBar\n  | plasticBarRecycling\n  | poisonCapsule\n  | poisonCapsuleRecycling\n  | powerArmor\n  | powerArmorMk2\n  | powerArmorMk2Recycling\n  | powerArmorRecycling\n  | powerSwitch\n  | powerSwitchRecycling\n  | processingUnit\n  | processingUnitRecycling\n  | productionSciencePack\n  | productionSciencePackRecycling\n  | productivityModule\n  | productivityModule2\n  | productivityModule2Recycling\n  | productivityModule3\n  | productivityModule3Recycling\n  | productivityModuleRecycling\n  | programmableSpeaker\n  | programmableSpeakerRecycling\n  | promethiumAsteroidChunkRecycling\n  | promethiumSciencePack\n  | promethiumSciencePackRecycling\n  | proxyContainerRecycling\n  | pump\n  | pumpRecycling\n  | pumpjack\n  | pumpjackRecycling\n  | qualityModule\n  | qualityModule2\n  | qualityModule2Recycling\n  | qualityModule3\n  | qualityModule3Recycling\n  | qualityModuleRecycling\n  | quantumProcessor\n  | quantumProcessorRecycling\n  | radar\n  | radarRecycling\n  | rail\n  | railChainSignal\n  | railChainSignalRecycling\n  | railRamp\n  | railRampRecycling\n  | railRecycling\n  | railSignal\n  | railSignalRecycling\n  | railSupport\n  | railSupportRecycling\n  | railgun\n  | railgunAmmo\n  | railgunAmmoRecycling\n  | railgunRecycling\n  | railgunTurret\n  | railgunTurretRecycling\n  | rawFishRecycling\n  | recycler\n  | recyclerRecycling\n  | refinedConcrete\n  | refinedConcreteRecycling\n  | refinedHazardConcrete\n  | refinedHazardConcreteRecycling\n  | repairPack\n  | repairPackRecycling\n  | requesterChest\n  | requesterChestRecycling\n  | roboport\n  | roboportRecycling\n  | rocket\n  | rocketFuel\n  | rocketFuelFromJelly\n  | rocketFuelRecycling\n  | rocketLauncher\n  | rocketLauncherRecycling\n  | rocketPart\n  | rocketRecycling\n  | rocketSilo\n  | rocketSiloRecycling\n  | rocketTurret\n  | rocketTurretRecycling\n  | scienceRecycling\n  | scrapRecycling\n  | selectionToolRecycling\n  | selectorCombinator\n  | selectorCombinatorRecycling\n  | shotgun\n  | shotgunRecycling\n  | shotgunShell\n  | shotgunShellRecycling\n  | simpleCoalLiquefaction\n  | simpleEntityWithForceRecycling\n  | simpleEntityWithOwnerRecycling\n  | slowdownCapsule\n  | slowdownCapsuleRecycling\n  | smallElectricPole\n  | smallElectricPoleRecycling\n  | smallLamp\n  | smallLampRecycling\n  | solarPanel\n  | solarPanelEquipment\n  | solarPanelEquipmentRecycling\n  | solarPanelRecycling\n  | solidFuelFromAmmonia\n  | solidFuelFromHeavyOil\n  | solidFuelFromLightOil\n  | solidFuelFromPetroleumGas\n  | solidFuelRecycling\n  | spacePlatformFoundation\n  | spacePlatformFoundationRecycling\n  | spacePlatformHubRecycling\n  | spacePlatformStarterPack\n  | spacePlatformStarterPackRecycling\n  | spaceSciencePack\n  | spaceSciencePackRecycling\n  | speedModule\n  | speedModule2\n  | speedModule2Recycling\n  | speedModule3\n  | speedModule3Recycling\n  | speedModuleRecycling\n  | spidertron\n  | spidertronRecycling\n  | splitter\n  | splitterRecycling\n  | spoilageRecycling\n  | stackInserter\n  | stackInserterRecycling\n  | steamCondensation\n  | steamEngine\n  | steamEngineRecycling\n  | steamTurbine\n  | steamTurbineRecycling\n  | steelChest\n  | steelChestRecycling\n  | steelFurnace\n  | steelFurnaceRecycling\n  | steelPlate\n  | steelPlateRecycling\n  | stoneBrick\n  | stoneBrickRecycling\n  | stoneFurnace\n  | stoneFurnaceRecycling\n  | stoneRecycling\n  | stoneWall\n  | stoneWallRecycling\n  | storageChest\n  | storageChestRecycling\n  | storageTank\n  | storageTankRecycling\n  | submachineGun\n  | submachineGunRecycling\n  | substation\n  | substationRecycling\n  | sulfur\n  | sulfurRecycling\n  | sulfuricAcid\n  | sulfuricAcidBarrel\n  | sulfuricAcidBarrelRecycling\n  | supercapacitor\n  | supercapacitorRecycling\n  | superconductor\n  | superconductorRecycling\n  | tank\n  | tankRecycling\n  | teslaAmmo\n  | teslaAmmoRecycling\n  | teslaTurret\n  | teslaTurretRecycling\n  | teslagun\n  | teslagunRecycling\n  | thruster\n  | thrusterFuel\n  | thrusterOxidizer\n  | thrusterRecycling\n  | toolbeltEquipment\n  | toolbeltEquipmentRecycling\n  | topUpValveRecycling\n  | trainStop\n  | trainStopRecycling\n  | transportBelt\n  | transportBeltRecycling\n  | treeSeedRecycling\n  | tungstenCarbide\n  | tungstenCarbideRecycling\n  | tungstenOreRecycling\n  | tungstenPlate\n  | tungstenPlateRecycling\n  | turboLoader\n  | turboLoaderRecycling\n  | turboSplitter\n  | turboSplitterRecycling\n  | turboTransportBelt\n  | turboTransportBeltRecycling\n  | turboUndergroundBelt\n  | turboUndergroundBeltRecycling\n  | undergroundBelt\n  | undergroundBeltRecycling\n  | upgradePlannerRecycling\n  | uranium235Recycling\n  | uranium238Recycling\n  | uraniumCannonShell\n  | uraniumCannonShellRecycling\n  | uraniumFuelCell\n  | uraniumFuelCellRecycling\n  | uraniumOreRecycling\n  | uraniumProcessing\n  | uraniumRoundsMagazine\n  | uraniumRoundsMagazineRecycling\n  | utilitySciencePack\n  | utilitySciencePackRecycling\n  | waterBarrel\n  | waterBarrelRecycling\n  | woodProcessing\n  | woodRecycling\n  | woodenChest\n  | woodenChestRecycling\n  | yumakoMashRecycling\n  | yumakoProcessing\n  | yumakoRecycling\n  | yumakoSeedRecycling\n  deriving DecidableEq, Repr, Inhabited\n\nnamespace RecipeName\n\ndef getRecipe : RecipeName -> Recipe\n| .accumulator => {\n  name := \"accumulator\",\n  inputs := [(2, .ironPlate), (5, .battery)],\n  outputs := [(1, .accumulator)],\n  category := .electronics\n  time := 10\n}\n| .accumulatorRecycling => {\n  name := \"accumulator-recycling\",\n  inputs := [(1, .accumulator)],\n  outputs := [(5/4, .battery), (1/2, .ironPlate)],\n  category := .recycling\n  time := 5/8\n}\n| .acidNeutralisation => {\n  name := \"acid-neutralisation\",\n  inputs := [(1000, .sulfuricAcid), (1, .calcite)],\n  outputs := [(10000, .steam)],\n  category := .chemistryOrCryogenics\n  time := 5\n}\n| .activeProviderChest => {\n  name := \"active-provider-chest\",\n  inputs := [(1, .steelChest), (3, .electronicCircuit), (1, .advancedCircuit)],\n  outputs := [(1, .activeProviderChest)],\n  category := .crafting\n  time := 1/2\n}\n| .activeProviderChestRecycling => {\n  name := \"active-provider-chest-recycling\",\n  inputs := [(1, .activeProviderChest)],\n  outputs := [(3/4, .electronicCircuit), (1/4, .steelChest), (1/4, .advancedCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .advancedCarbonicAsteroidCrushing => {\n  name := \"advanced-carbonic-asteroid-crushing\",\n  inputs := [(1, .carbonicAsteroidChunk)],\n  outputs := [(5, .carbon), (2, .sulfur), (1/20, .carbonicAsteroidChunk)],\n  category := .crushing\n  time := 5\n}\n| .advancedCircuit => {\n  name := \"advanced-circuit\",\n  inputs := [(2, .electronicCircuit), (2, .plasticBar), (4, .copperCable)],\n  outputs := [(1, .advancedCircuit)],\n  category := .electronics\n  time := 6\n}\n| .advancedCircuitRecycling => {\n  name := \"advanced-circuit-recycling\",\n  inputs := [(1, .advancedCircuit)],\n  outputs := [(1, .copperCable), (1/2, .electronicCircuit), (1/2, .plasticBar)],\n  category := .recycling\n  time := 3/8\n}\n| .advancedMetallicAsteroidCrushing => {\n  name := \"advanced-metallic-asteroid-crushing\",\n  inputs := [(1, .metallicAsteroidChunk)],\n  outputs := [(10, .ironOre), (4, .copperOre), (1/20, .metallicAsteroidChunk)],\n  category := .crushing\n  time := 5\n}\n| .advancedOilProcessing => {\n  name := \"advanced-oil-processing\",\n  inputs := [(50, .water), (100, .crudeOil)],\n  outputs := [(25, .heavyOil), (45, .lightOil), (55, .petroleumGas)],\n  category := .oilProcessing\n  time := 5\n}\n| .advancedOxideAsteroidCrushing => {\n  name := \"advanced-oxide-asteroid-crushing\",\n  inputs := [(1, .oxideAsteroidChunk)],\n  outputs := [(3, .ice), (2, .calcite), (1/20, .oxideAsteroidChunk)],\n  category := .crushing\n  time := 5\n}\n| .advancedThrusterFuel => {\n  name := \"advanced-thruster-fuel\",\n  inputs := [(100, .water), (2, .carbon), (1, .calcite)],\n  outputs := [(1500, .thrusterFuel)],\n  category := .chemistry\n  time := 10\n}\n| .advancedThrusterOxidizer => {\n  name := \"advanced-thruster-oxidizer\",\n  inputs := [(100, .water), (2, .ironOre), (1, .calcite)],\n  outputs := [(1500, .thrusterOxidizer)],\n  category := .chemistry\n  time := 10\n}\n| .agriculturalSciencePack => {\n  name := \"agricultural-science-pack\",\n  inputs := [(1, .bioflux), (1, .pentapodEgg)],\n  outputs := [(1, .agriculturalSciencePack)],\n  category := .organic\n  time := 4\n}\n| .agriculturalSciencePackRecycling => {\n  name := \"agricultural-science-pack-recycling\",\n  inputs := [(1, .agriculturalSciencePack)],\n  outputs := [(1/4, .agriculturalSciencePack)],\n  category := .recycling\n  time := 1/4\n}\n| .agriculturalTower => {\n  name := \"agricultural-tower\",\n  inputs := [(10, .steelPlate), (3, .electronicCircuit), (20, .spoilage), (1, .landfill)],\n  outputs := [(1, .agriculturalTower)],\n  category := .crafting\n  time := 10\n}\n| .agriculturalTowerRecycling => {\n  name := \"agricultural-tower-recycling\",\n  inputs := [(1, .agriculturalTower)],\n  outputs := [(5, .spoilage), (5/2, .steelPlate), (3/4, .electronicCircuit), (1/4, .landfill)],\n  category := .recycling\n  time := 5/8\n}\n| .ammoniaRocketFuel => {\n  name := \"ammonia-rocket-fuel\",\n  inputs := [(50, .water), (500, .ammonia), (10, .solidFuel)],\n  outputs := [(1, .rocketFuel)],\n  category := .chemistryOrCryogenics\n  time := 10\n}\n| .ammoniacalSolutionSeparation => {\n  name := \"ammoniacal-solution-separation\",\n  inputs := [(50, .ammoniacalSolution)],\n  outputs := [(5, .ice), (50, .ammonia)],\n  category := .chemistryOrCryogenics\n  time := 1\n}\n| .arithmeticCombinator => {\n  name := \"arithmetic-combinator\",\n  inputs := [(5, .copperCable), (5, .electronicCircuit)],\n  outputs := [(1, .arithmeticCombinator)],\n  category := .crafting\n  time := 1/2\n}\n| .arithmeticCombinatorRecycling => {\n  name := \"arithmetic-combinator-recycling\",\n  inputs := [(1, .arithmeticCombinator)],\n  outputs := [(5/4, .copperCable), (5/4, .electronicCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .artificialJellynutSoil => {\n  name := \"artificial-jellynut-soil\",\n  inputs := [(2, .jellynutSeed), (50, .nutrients), (5, .landfill)],\n  outputs := [(10, .artificialJellynutSoil)],\n  category := .crafting\n  time := 2\n}\n| .artificialJellynutSoilRecycling => {\n  name := \"artificial-jellynut-soil-recycling\",\n  inputs := [(1, .artificialJellynutSoil)],\n  outputs := [(5/4, .nutrients), (1/8, .landfill), (1/20, .jellynutSeed)],\n  category := .recycling\n  time := 1/8\n}\n| .artificialYumakoSoil => {\n  name := \"artificial-yumako-soil\",\n  inputs := [(2, .yumakoSeed), (50, .nutrients), (5, .landfill)],\n  outputs := [(10, .artificialYumakoSoil)],\n  category := .crafting\n  time := 2\n}\n| .artificialYumakoSoilRecycling => {\n  name := \"artificial-yumako-soil-recycling\",\n  inputs := [(1, .artificialYumakoSoil)],\n  outputs := [(5/4, .nutrients), (1/8, .landfill), (1/20, .yumakoSeed)],\n  category := .recycling\n  time := 1/8\n}\n| .artilleryShell => {\n  name := \"artillery-shell\",\n  inputs := [(1, .radar), (1, .calcite), (4, .tungstenPlate), (8, .explosives)],\n  outputs := [(1, .artilleryShell)],\n  category := .crafting\n  time := 15\n}\n| .artilleryShellRecycling => {\n  name := \"artillery-shell-recycling\",\n  inputs := [(1, .artilleryShell)],\n  outputs := [(2, .explosives), (1, .tungstenPlate), (1/4, .radar), (1/4, .calcite)],\n  category := .recycling\n  time := 15/16\n}\n| .artilleryTurret => {\n  name := \"artillery-turret\",\n  inputs := [(60, .tungstenPlate), (60, .refinedConcrete), (40, .ironGearWheel), (10, .processingUnit)],\n  outputs := [(1, .artilleryTurret)],\n  category := .crafting\n  time := 40\n}\n| .artilleryTurretRecycling => {\n  name := \"artillery-turret-recycling\",\n  inputs := [(1, .artilleryTurret)],\n  outputs := [(15, .tungstenPlate), (15, .refinedConcrete), (10, .ironGearWheel), (5/2, .processingUnit)],\n  category := .recycling\n  time := 5/2\n}\n| .artilleryWagon => {\n  name := \"artillery-wagon\",\n  inputs := [(60, .engineUnit), (60, .tungstenPlate), (60, .refinedConcrete), (40, .ironGearWheel), (10, .processingUnit)],\n  outputs := [(1, .artilleryWagon)],\n  category := .crafting\n  time := 4\n}\n| .artilleryWagonRecycling => {\n  name := \"artillery-wagon-recycling\",\n  inputs := [(1, .artilleryWagon)],\n  outputs := [(15, .engineUnit), (15, .tungstenPlate), (15, .refinedConcrete), (10, .ironGearWheel), (5/2, .processingUnit)],\n  category := .recycling\n  time := 1/4\n}\n| .assemblingMachine1 => {\n  name := \"assembling-machine-1\",\n  inputs := [(3, .electronicCircuit), (5, .ironGearWheel), (9, .ironPlate)],\n  outputs := [(1, .assemblingMachine1)],\n  category := .crafting\n  time := 1/2\n}\n| .assemblingMachine1Recycling => {\n  name := \"assembling-machine-1-recycling\",\n  inputs := [(1, .assemblingMachine1)],\n  outputs := [(9/4, .ironPlate), (5/4, .ironGearWheel), (3/4, .electronicCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .assemblingMachine2 => {\n  name := \"assembling-machine-2\",\n  inputs := [(2, .steelPlate), (3, .electronicCircuit), (5, .ironGearWheel), (1, .assemblingMachine1)],\n  outputs := [(1, .assemblingMachine2)],\n  category := .crafting\n  time := 1/2\n}\n| .assemblingMachine2Recycling => {\n  name := \"assembling-machine-2-recycling\",\n  inputs := [(1, .assemblingMachine2)],\n  outputs := [(5/4, .ironGearWheel), (3/4, .electronicCircuit), (1/2, .steelPlate), (1/4, .assemblingMachine1)],\n  category := .recycling\n  time := 1/32\n}\n| .assemblingMachine3 => {\n  name := \"assembling-machine-3\",\n  inputs := [(2, .assemblingMachine2), (4, .speedModule)],\n  outputs := [(1, .assemblingMachine3)],\n  category := .crafting\n  time := 1/2\n}\n| .assemblingMachine3Recycling => {\n  name := \"assembling-machine-3-recycling\",\n  inputs := [(1, .assemblingMachine3)],\n  outputs := [(1, .speedModule), (1/2, .assemblingMachine2)],\n  category := .recycling\n  time := 1/32\n}\n| .asteroidCollector => {\n  name := \"asteroid-collector\",\n  inputs := [(20, .lowDensityStructure), (8, .electricEngineUnit), (5, .processingUnit)],\n  outputs := [(1, .asteroidCollector)],\n  category := .crafting\n  time := 10\n}\n| .asteroidCollectorRecycling => {\n  name := \"asteroid-collector-recycling\",\n  inputs := [(1, .asteroidCollector)],\n  outputs := [(5, .lowDensityStructure), (2, .electricEngineUnit), (5/4, .processingUnit)],\n  category := .recycling\n  time := 5/8\n}\n| .atomicBomb => {\n  name := \"atomic-bomb\",\n  inputs := [(10, .processingUnit), (10, .explosives), (100, .uranium235)],\n  outputs := [(1, .atomicBomb)],\n  category := .crafting\n  time := 50\n}\n| .atomicBombRecycling => {\n  name := \"atomic-bomb-recycling\",\n  inputs := [(1, .atomicBomb)],\n  outputs := [(25, .uranium235), (5/2, .processingUnit), (5/2, .explosives)],\n  category := .recycling\n  time := 25/8\n}\n| .automationSciencePack => {\n  name := \"automation-science-pack\",\n  inputs := [(1, .copperPlate), (1, .ironGearWheel)],\n  outputs := [(1, .automationSciencePack)],\n  category := .crafting\n  time := 5\n}\n| .automationSciencePackRecycling => {\n  name := \"automation-science-pack-recycling\",\n  inputs := [(1, .automationSciencePack)],\n  outputs := [(1/4, .automationSciencePack)],\n  category := .recycling\n  time := 5/16\n}\n| .barrel => {\n  name := \"barrel\",\n  inputs := [(1, .steelPlate)],\n  outputs := [(1, .barrel)],\n  category := .crafting\n  time := 1\n}\n| .barrelRecycling => {\n  name := \"barrel-recycling\",\n  inputs := [(1, .barrel)],\n  outputs := [(1/4, .steelPlate)],\n  category := .recycling\n  time := 1/16\n}\n| .basicOilProcessing => {\n  name := \"basic-oil-processing\",\n  inputs := [(100, .crudeOil)],\n  outputs := [(45, .petroleumGas)],\n  category := .oilProcessing\n  time := 5\n}\n| .battery => {\n  name := \"battery\",\n  inputs := [(20, .sulfuricAcid), (1, .ironPlate), (1, .copperPlate)],\n  outputs := [(1, .battery)],\n  category := .chemistryOrCryogenics\n  time := 4\n}\n| .batteryEquipment => {\n  name := \"battery-equipment\",\n  inputs := [(5, .battery), (10, .steelPlate)],\n  outputs := [(1, .batteryEquipment)],\n  category := .crafting\n  time := 10\n}\n| .batteryEquipmentRecycling => {\n  name := \"battery-equipment-recycling\",\n  inputs := [(1, .batteryEquipment)],\n  outputs := [(5/2, .steelPlate), (5/4, .battery)],\n  category := .recycling\n  time := 5/8\n}\n| .batteryMk2Equipment => {\n  name := \"battery-mk2-equipment\",\n  inputs := [(10, .batteryEquipment), (15, .processingUnit), (5, .lowDensityStructure)],\n  outputs := [(1, .batteryMk2Equipment)],\n  category := .crafting\n  time := 10\n}\n| .batteryMk2EquipmentRecycling => {\n  name := \"battery-mk2-equipment-recycling\",\n  inputs := [(1, .batteryMk2Equipment)],\n  outputs := [(15/4, .processingUnit), (5/2, .batteryEquipment), (5/4, .lowDensityStructure)],\n  category := .recycling\n  time := 5/8\n}\n| .batteryMk3Equipment => {\n  name := \"battery-mk3-equipment\",\n  inputs := [(5, .batteryMk2Equipment), (10, .supercapacitor)],\n  outputs := [(1, .batteryMk3Equipment)],\n  category := .crafting\n  time := 10\n}\n| .batteryMk3EquipmentRecycling => {\n  name := \"battery-mk3-equipment-recycling\",\n  inputs := [(1, .batteryMk3Equipment)],\n  outputs := [(5/2, .supercapacitor), (5/4, .batteryMk2Equipment)],\n  category := .recycling\n  time := 5/8\n}\n| .batteryRecycling => {\n  name := \"battery-recycling\",\n  inputs := [(1, .battery)],\n  outputs := [(1/4, .ironPlate), (1/4, .copperPlate)],\n  category := .recycling\n  time := 1/4\n}\n| .beacon => {\n  name := \"beacon\",\n  inputs := [(20, .electronicCircuit), (20, .advancedCircuit), (10, .steelPlate), (10, .copperCable)],\n  outputs := [(1, .beacon)],\n  category := .electronics\n  time := 15\n}\n| .beaconRecycling => {\n  name := \"beacon-recycling\",\n  inputs := [(1, .beacon)],\n  outputs := [(5, .electronicCircuit), (5, .advancedCircuit), (5/2, .steelPlate), (5/2, .copperCable)],\n  category := .recycling\n  time := 15/16\n}\n| .beltImmunityEquipment => {\n  name := \"belt-immunity-equipment\",\n  inputs := [(5, .advancedCircuit), (10, .steelPlate)],\n  outputs := [(1, .beltImmunityEquipment)],\n  category := .crafting\n  time := 10\n}\n| .beltImmunityEquipmentRecycling => {\n  name := \"belt-immunity-equipment-recycling\",\n  inputs := [(1, .beltImmunityEquipment)],\n  outputs := [(5/2, .steelPlate), (5/4, .advancedCircuit)],\n  category := .recycling\n  time := 5/8\n}\n| .bigElectricPole => {\n  name := \"big-electric-pole\",\n  inputs := [(8, .ironStick), (5, .steelPlate), (4, .copperCable)],\n  outputs := [(1, .bigElectricPole)],\n  category := .electronics\n  time := 1/2\n}\n| .bigElectricPoleRecycling => {\n  name := \"big-electric-pole-recycling\",\n  inputs := [(1, .bigElectricPole)],\n  outputs := [(2, .ironStick), (5/4, .steelPlate), (1, .copperCable)],\n  category := .recycling\n  time := 1/32\n}\n| .bigMiningDrill => {\n  name := \"big-mining-drill\",\n  inputs := [(200, .moltenIron), (1, .electricMiningDrill), (20, .tungstenCarbide), (10, .electricEngineUnit), (10, .advancedCircuit)],\n  outputs := [(1, .bigMiningDrill)],\n  category := .metallurgy\n  time := 30\n}\n| .bigMiningDrillRecycling => {\n  name := \"big-mining-drill-recycling\",\n  inputs := [(1, .bigMiningDrill)],\n  outputs := [(5, .tungstenCarbide), (5/2, .electricEngineUnit), (5/2, .advancedCircuit), (1/4, .electricMiningDrill)],\n  category := .recycling\n  time := 15/8\n}\n| .biochamber => {\n  name := \"biochamber\",\n  inputs := [(5, .nutrients), (1, .pentapodEgg), (20, .ironPlate), (5, .electronicCircuit), (1, .landfill)],\n  outputs := [(1, .biochamber)],\n  category := .organicOrAssembling\n  time := 20\n}\n| .biochamberRecycling => {\n  name := \"biochamber-recycling\",\n  inputs := [(1, .biochamber)],\n  outputs := [(5, .ironPlate), (5/4, .nutrients), (5/4, .electronicCircuit), (1/4, .pentapodEgg), (1/4, .landfill)],\n  category := .recycling\n  time := 5/4\n}\n| .bioflux => {\n  name := \"bioflux\",\n  inputs := [(15, .yumakoMash), (12, .jelly)],\n  outputs := [(4, .bioflux)],\n  category := .organic\n  time := 6\n}\n| .biofluxRecycling => {\n  name := \"bioflux-recycling\",\n  inputs := [(1, .bioflux)],\n  outputs := [(1/4, .bioflux)],\n  category := .recycling\n  time := 3/8\n}\n| .biolab => {\n  name := \"biolab\",\n  inputs := [(1, .lab), (10, .biterEgg), (25, .refinedConcrete), (2, .captureRobotRocket), (3, .uranium235)],\n  outputs := [(1, .biolab)],\n  category := .crafting\n  time := 10\n}\n| .biolabRecycling => {\n  name := \"biolab-recycling\",\n  inputs := [(1, .biolab)],\n  outputs := [(1/4, .biolab)],\n  category := .recycling\n  time := 5/8\n}\n| .biolubricant => {\n  name := \"biolubricant\",\n  inputs := [(60, .jelly)],\n  outputs := [(20, .lubricant)],\n  category := .organic\n  time := 3\n}\n| .bioplastic => {\n  name := \"bioplastic\",\n  inputs := [(1, .bioflux), (4, .yumakoMash)],\n  outputs := [(3, .plasticBar)],\n  category := .organic\n  time := 2\n}\n| .biosulfur => {\n  name := \"biosulfur\",\n  inputs := [(5, .spoilage), (1, .bioflux)],\n  outputs := [(2, .sulfur)],\n  category := .organic\n  time := 2\n}\n| .biterEgg => {\n  name := \"biter-egg\",\n  inputs := [],\n  outputs := [(5, .biterEgg)],\n  category := .captiveSpawnerProcess\n  time := 10\n}\n| .biterEggRecycling => {\n  name := \"biter-egg-recycling\",\n  inputs := [(1, .biterEgg)],\n  outputs := [(1/4, .biterEgg)],\n  category := .recycling\n  time := 5/8\n}\n| .blueprintBookRecycling => {\n  name := \"blueprint-book-recycling\",\n  inputs := [(1, .blueprintBook)],\n  outputs := [(1/4, .blueprintBook)],\n  category := .recycling\n  time := 1/32\n}\n| .blueprintRecycling => {\n  name := \"blueprint-recycling\",\n  inputs := [(1, .blueprint)],\n  outputs := [(1/4, .blueprint)],\n  category := .recycling\n  time := 1/32\n}\n| .boiler => {\n  name := \"boiler\",\n  inputs := [(1, .stoneFurnace), (4, .pipe)],\n  outputs := [(1, .boiler)],\n  category := .crafting\n  time := 1/2\n}\n| .boilerRecycling => {\n  name := \"boiler-recycling\",\n  inputs := [(1, .boiler)],\n  outputs := [(1, .pipe), (1/4, .stoneFurnace)],\n  category := .recycling\n  time := 1/32\n}\n| .bottomlessChestRecycling => {\n  name := \"bottomless-chest-recycling\",\n  inputs := [(1, .bottomlessChest)],\n  outputs := [(1/4, .bottomlessChest)],\n  category := .recycling\n  time := 1/32\n}\n| .bufferChest => {\n  name := \"buffer-chest\",\n  inputs := [(1, .steelChest), (3, .electronicCircuit), (1, .advancedCircuit)],\n  outputs := [(1, .bufferChest)],\n  category := .crafting\n  time := 1/2\n}\n| .bufferChestRecycling => {\n  name := \"buffer-chest-recycling\",\n  inputs := [(1, .bufferChest)],\n  outputs := [(3/4, .electronicCircuit), (1/4, .steelChest), (1/4, .advancedCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .bulkInserter => {\n  name := \"bulk-inserter\",\n  inputs := [(15, .ironGearWheel), (15, .electronicCircuit), (1, .advancedCircuit), (1, .fastInserter)],\n  outputs := [(1, .bulkInserter)],\n  category := .crafting\n  time := 1/2\n}\n| .bulkInserterRecycling => {\n  name := \"bulk-inserter-recycling\",\n  inputs := [(1, .bulkInserter)],\n  outputs := [(15/4, .ironGearWheel), (15/4, .electronicCircuit), (1/4, .advancedCircuit), (1/4, .fastInserter)],\n  category := .recycling\n  time := 1/32\n}\n| .burnerGeneratorRecycling => {\n  name := \"burner-generator-recycling\",\n  inputs := [(1, .burnerGenerator)],\n  outputs := [(1/4, .burnerGenerator)],\n  category := .recycling\n  time := 1/32\n}\n| .burnerInserter => {\n  name := \"burner-inserter\",\n  inputs := [(1, .ironPlate), (1, .ironGearWheel)],\n  outputs := [(1, .burnerInserter)],\n  category := .crafting\n  time := 1/2\n}\n| .burnerInserterRecycling => {\n  name := \"burner-inserter-recycling\",\n  inputs := [(1, .burnerInserter)],\n  outputs := [(1/4, .ironPlate), (1/4, .ironGearWheel)],\n  category := .recycling\n  time := 1/32\n}\n| .burnerMiningDrill => {\n  name := \"burner-mining-drill\",\n  inputs := [(3, .ironGearWheel), (1, .stoneFurnace), (3, .ironPlate)],\n  outputs := [(1, .burnerMiningDrill)],\n  category := .crafting\n  time := 2\n}\n| .burnerMiningDrillRecycling => {\n  name := \"burner-mining-drill-recycling\",\n  inputs := [(1, .burnerMiningDrill)],\n  outputs := [(3/4, .ironGearWheel), (3/4, .ironPlate), (1/4, .stoneFurnace)],\n  category := .recycling\n  time := 1/8\n}\n| .burntSpoilage => {\n  name := \"burnt-spoilage\",\n  inputs := [(6, .spoilage)],\n  outputs := [(1, .carbon)],\n  category := .organic\n  time := 12\n}\n| .calciteRecycling => {\n  name := \"calcite-recycling\",\n  inputs := [(1, .calcite)],\n  outputs := [(1/4, .calcite)],\n  category := .recycling\n  time := 1/32\n}\n| .cannonShell => {\n  name := \"cannon-shell\",\n  inputs := [(2, .steelPlate), (2, .plasticBar), (1, .explosives)],\n  outputs := [(1, .cannonShell)],\n  category := .crafting\n  time := 8\n}\n| .cannonShellRecycling => {\n  name := \"cannon-shell-recycling\",\n  inputs := [(1, .cannonShell)],\n  outputs := [(1/2, .steelPlate), (1/2, .plasticBar), (1/4, .explosives)],\n  category := .recycling\n  time := 1/2\n}\n| .captiveBiterSpawner => {\n  name := \"captive-biter-spawner\",\n  inputs := [(100, .fluoroketoneCold), (10, .biterEgg), (1, .captureRobotRocket), (15, .uranium235)],\n  outputs := [(1, .captiveBiterSpawner)],\n  category := .cryogenics\n  time := 10\n}\n| .captiveBiterSpawnerRecycling => {\n  name := \"captive-biter-spawner-recycling\",\n  inputs := [(1, .captiveBiterSpawner)],\n  outputs := [(1/4, .captiveBiterSpawner)],\n  category := .recycling\n  time := 5/8\n}\n| .captureRobotRocket => {\n  name := \"capture-robot-rocket\",\n  inputs := [(1, .flyingRobotFrame), (2, .steelPlate), (20, .bioflux), (2, .processingUnit)],\n  outputs := [(1, .captureRobotRocket)],\n  category := .crafting\n  time := 10\n}\n| .captureRobotRocketRecycling => {\n  name := \"capture-robot-rocket-recycling\",\n  inputs := [(1, .captureRobotRocket)],\n  outputs := [(5, .bioflux), (1/2, .steelPlate), (1/2, .processingUnit), (1/4, .flyingRobotFrame)],\n  category := .recycling\n  time := 5/8\n}\n| .car => {\n  name := \"car\",\n  inputs := [(8, .engineUnit), (20, .ironPlate), (5, .steelPlate)],\n  outputs := [(1, .car)],\n  category := .crafting\n  time := 2\n}\n| .carRecycling => {\n  name := \"car-recycling\",\n  inputs := [(1, .car)],\n  outputs := [(5, .ironPlate), (2, .engineUnit), (5/4, .steelPlate)],\n  category := .recycling\n  time := 1/8\n}\n| .carbon => {\n  name := \"carbon\",\n  inputs := [(20, .sulfuricAcid), (2, .coal)],\n  outputs := [(1, .carbon)],\n  category := .chemistryOrCryogenics\n  time := 1\n}\n| .carbonFiber => {\n  name := \"carbon-fiber\",\n  inputs := [(10, .yumakoMash), (1, .carbon)],\n  outputs := [(1, .carbonFiber)],\n  category := .organic\n  time := 5\n}\n| .carbonFiberRecycling => {\n  name := \"carbon-fiber-recycling\",\n  inputs := [(1, .carbonFiber)],\n  outputs := [(1/4, .carbonFiber)],\n  category := .recycling\n  time := 5/16\n}\n| .carbonRecycling => {\n  name := \"carbon-recycling\",\n  inputs := [(1, .carbon)],\n  outputs := [(1/4, .carbon)],\n  category := .recycling\n  time := 1/16\n}\n| .carbonicAsteroidChunkRecycling => {\n  name := \"carbonic-asteroid-chunk-recycling\",\n  inputs := [(1, .carbonicAsteroidChunk)],\n  outputs := [(1/4, .carbonicAsteroidChunk)],\n  category := .recycling\n  time := 1/32\n}\n| .carbonicAsteroidCrushing => {\n  name := \"carbonic-asteroid-crushing\",\n  inputs := [(1, .carbonicAsteroidChunk)],\n  outputs := [(10, .carbon), (1/5, .carbonicAsteroidChunk)],\n  category := .crushing\n  time := 2\n}\n| .carbonicAsteroidReprocessing => {\n  name := \"carbonic-asteroid-reprocessing\",\n  inputs := [(1, .carbonicAsteroidChunk)],\n  outputs := [(2/5, .carbonicAsteroidChunk), (1/5, .metallicAsteroidChunk), (1/5, .oxideAsteroidChunk)],\n  category := .crushing\n  time := 2\n}\n| .cargoBay => {\n  name := \"cargo-bay\",\n  inputs := [(20, .steelPlate), (20, .lowDensityStructure), (5, .processingUnit)],\n  outputs := [(1, .cargoBay)],\n  category := .crafting\n  time := 10\n}\n| .cargoBayRecycling => {\n  name := \"cargo-bay-recycling\",\n  inputs := [(1, .cargoBay)],\n  outputs := [(5, .steelPlate), (5, .lowDensityStructure), (5/4, .processingUnit)],\n  category := .recycling\n  time := 5/8\n}\n| .cargoLandingPad => {\n  name := \"cargo-landing-pad\",\n  inputs := [(200, .concrete), (25, .steelPlate), (10, .processingUnit)],\n  outputs := [(1, .cargoLandingPad)],\n  category := .crafting\n  time := 30\n}\n| .cargoLandingPadRecycling => {\n  name := \"cargo-landing-pad-recycling\",\n  inputs := [(1, .cargoLandingPad)],\n  outputs := [(50, .concrete), (25/4, .steelPlate), (5/2, .processingUnit)],\n  category := .recycling\n  time := 15/8\n}\n| .cargoWagon => {\n  name := \"cargo-wagon\",\n  inputs := [(10, .ironGearWheel), (20, .ironPlate), (20, .steelPlate)],\n  outputs := [(1, .cargoWagon)],\n  category := .crafting\n  time := 1\n}\n| .cargoWagonRecycling => {\n  name := \"cargo-wagon-recycling\",\n  inputs := [(1, .cargoWagon)],\n  outputs := [(5, .ironPlate), (5, .steelPlate), (5/2, .ironGearWheel)],\n  category := .recycling\n  time := 1/16\n}\n| .castingCopper => {\n  name := \"casting-copper\",\n  inputs := [(20, .moltenCopper)],\n  outputs := [(2, .copperPlate)],\n  category := .metallurgy\n  time := 16/5\n}\n| .castingCopperCable => {\n  name := \"casting-copper-cable\",\n  inputs := [(5, .moltenCopper)],\n  outputs := [(2, .copperCable)],\n  category := .metallurgy\n  time := 1\n}\n| .castingIron => {\n  name := \"casting-iron\",\n  inputs := [(20, .moltenIron)],\n  outputs := [(2, .ironPlate)],\n  category := .metallurgy\n  time := 16/5\n}\n| .castingIronGearWheel => {\n  name := \"casting-iron-gear-wheel\",\n  inputs := [(10, .moltenIron)],\n  outputs := [(1, .ironGearWheel)],\n  category := .metallurgy\n  time := 1\n}\n| .castingIronStick => {\n  name := \"casting-iron-stick\",\n  inputs := [(20, .moltenIron)],\n  outputs := [(4, .ironStick)],\n  category := .metallurgy\n  time := 1\n}\n| .castingLowDensityStructure => {\n  name := \"casting-low-density-structure\",\n  inputs := [(80, .moltenIron), (250, .moltenCopper), (5, .plasticBar)],\n  outputs := [(1, .lowDensityStructure)],\n  category := .metallurgy\n  time := 15\n}\n| .castingPipe => {\n  name := \"casting-pipe\",\n  inputs := [(10, .moltenIron)],\n  outputs := [(1, .pipe)],\n  category := .metallurgy\n  time := 1\n}\n| .castingPipeToGround => {\n  name := \"casting-pipe-to-ground\",\n  inputs := [(50, .moltenIron), (10, .pipe)],\n  outputs := [(2, .pipeToGround)],\n  category := .metallurgy\n  time := 1\n}\n| .castingSteel => {\n  name := \"casting-steel\",\n  inputs := [(30, .moltenIron)],\n  outputs := [(1, .steelPlate)],\n  category := .metallurgy\n  time := 16/5\n}\n| .centrifuge => {\n  name := \"centrifuge\",\n  inputs := [(100, .concrete), (50, .steelPlate), (100, .advancedCircuit), (100, .ironGearWheel)],\n  outputs := [(1, .centrifuge)],\n  category := .crafting\n  time := 4\n}\n| .centrifugeRecycling => {\n  name := \"centrifuge-recycling\",\n  inputs := [(1, .centrifuge)],\n  outputs := [(25, .concrete), (25, .advancedCircuit), (25, .ironGearWheel), (25/2, .steelPlate)],\n  category := .recycling\n  time := 1/4\n}\n| .chemicalPlant => {\n  name := \"chemical-plant\",\n  inputs := [(5, .steelPlate), (5, .ironGearWheel), (5, .electronicCircuit), (5, .pipe)],\n  outputs := [(1, .chemicalPlant)],\n  category := .crafting\n  time := 5\n}\n| .chemicalPlantRecycling => {\n  name := \"chemical-plant-recycling\",\n  inputs := [(1, .chemicalPlant)],\n  outputs := [(5/4, .steelPlate), (5/4, .ironGearWheel), (5/4, .electronicCircuit), (5/4, .pipe)],\n  category := .recycling\n  time := 5/16\n}\n| .chemicalSciencePack => {\n  name := \"chemical-science-pack\",\n  inputs := [(2, .engineUnit), (3, .advancedCircuit), (1, .sulfur)],\n  outputs := [(2, .chemicalSciencePack)],\n  category := .crafting\n  time := 24\n}\n| .chemicalSciencePackRecycling => {\n  name := \"chemical-science-pack-recycling\",\n  inputs := [(1, .chemicalSciencePack)],\n  outputs := [(1/4, .chemicalSciencePack)],\n  category := .recycling\n  time := 3/2\n}\n| .cliffExplosives => {\n  name := \"cliff-explosives\",\n  inputs := [(10, .explosives), (10, .calcite), (1, .grenade), (1, .barrel)],\n  outputs := [(1, .cliffExplosives)],\n  category := .crafting\n  time := 8\n}\n| .cliffExplosivesRecycling => {\n  name := \"cliff-explosives-recycling\",\n  inputs := [(1, .cliffExplosives)],\n  outputs := [(5/2, .explosives), (5/2, .calcite), (1/4, .grenade), (1/4, .barrel)],\n  category := .recycling\n  time := 1/2\n}\n| .clusterGrenade => {\n  name := \"cluster-grenade\",\n  inputs := [(7, .grenade), (5, .explosives), (5, .steelPlate)],\n  outputs := [(1, .clusterGrenade)],\n  category := .crafting\n  time := 8\n}\n| .clusterGrenadeRecycling => {\n  name := \"cluster-grenade-recycling\",\n  inputs := [(1, .clusterGrenade)],\n  outputs := [(7/4, .grenade), (5/4, .explosives), (5/4, .steelPlate)],\n  category := .recycling\n  time := 1/2\n}\n| .coalLiquefaction => {\n  name := \"coal-liquefaction\",\n  inputs := [(25, .heavyOil), (50, .steam), (10, .coal)],\n  outputs := [(90, .heavyOil), (20, .lightOil), (10, .petroleumGas)],\n  category := .oilProcessing\n  time := 5\n}\n| .coalRecycling => {\n  name := \"coal-recycling\",\n  inputs := [(1, .coal)],\n  outputs := [(1/4, .coal)],\n  category := .recycling\n  time := 1/32\n}\n| .coalSynthesis => {\n  name := \"coal-synthesis\",\n  inputs := [(10, .water), (5, .carbon), (1, .sulfur)],\n  outputs := [(1, .coal)],\n  category := .chemistry\n  time := 2\n}\n| .coinRecycling => {\n  name := \"coin-recycling\",\n  inputs := [(1, .coin)],\n  outputs := [(1/4, .coin)],\n  category := .recycling\n  time := 1/32\n}\n| .combatShotgun => {\n  name := \"combat-shotgun\",\n  inputs := [(15, .steelPlate), (5, .ironGearWheel), (10, .copperPlate), (10, .wood)],\n  outputs := [(1, .combatShotgun)],\n  category := .crafting\n  time := 10\n}\n| .combatShotgunRecycling => {\n  name := \"combat-shotgun-recycling\",\n  inputs := [(1, .combatShotgun)],\n  outputs := [(15/4, .steelPlate), (5/2, .copperPlate), (5/2, .wood), (5/4, .ironGearWheel)],\n  category := .recycling\n  time := 5/8\n}\n| .concrete => {\n  name := \"concrete\",\n  inputs := [(100, .water), (5, .stoneBrick), (1, .ironOre)],\n  outputs := [(10, .concrete)],\n  category := .craftingWithFluid\n  time := 10\n}\n| .concreteFromMoltenIron => {\n  name := \"concrete-from-molten-iron\",\n  inputs := [(20, .moltenIron), (100, .water), (5, .stoneBrick)],\n  outputs := [(10, .concrete)],\n  category := .metallurgy\n  time := 10\n}\n| .concreteRecycling => {\n  name := \"concrete-recycling\",\n  inputs := [(1, .concrete)],\n  outputs := [(1/8, .stoneBrick), (1/40, .ironOre)],\n  category := .recycling\n  time := 5/8\n}\n| .constantCombinator => {\n  name := \"constant-combinator\",\n  inputs := [(5, .copperCable), (2, .electronicCircuit)],\n  outputs := [(1, .constantCombinator)],\n  category := .crafting\n  time := 1/2\n}\n| .constantCombinatorRecycling => {\n  name := \"constant-combinator-recycling\",\n  inputs := [(1, .constantCombinator)],\n  outputs := [(5/4, .copperCable), (1/2, .electronicCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .constructionRobot => {\n  name := \"construction-robot\",\n  inputs := [(1, .flyingRobotFrame), (2, .electronicCircuit)],\n  outputs := [(1, .constructionRobot)],\n  category := .crafting\n  time := 1/2\n}\n| .constructionRobotRecycling => {\n  name := \"construction-robot-recycling\",\n  inputs := [(1, .constructionRobot)],\n  outputs := [(1/2, .electronicCircuit), (1/4, .flyingRobotFrame)],\n  category := .recycling\n  time := 1/32\n}\n| .copperBacteria => {\n  name := \"copper-bacteria\",\n  inputs := [(3, .yumakoMash)],\n  outputs := [(1/10, .copperBacteria), (1, .spoilage)],\n  category := .organicOrHandCrafting\n  time := 1\n}\n| .copperBacteriaCultivation => {\n  name := \"copper-bacteria-cultivation\",\n  inputs := [(1, .copperBacteria), (1, .bioflux)],\n  outputs := [(4, .copperBacteria)],\n  category := .organic\n  time := 4\n}\n| .copperBacteriaRecycling => {\n  name := \"copper-bacteria-recycling\",\n  inputs := [(1, .copperBacteria)],\n  outputs := [(1/4, .copperBacteria)],\n  category := .recycling\n  time := 1/16\n}\n| .copperCable => {\n  name := \"copper-cable\",\n  inputs := [(1, .copperPlate)],\n  outputs := [(2, .copperCable)],\n  category := .electronics\n  time := 1/2\n}\n| .copperCableRecycling => {\n  name := \"copper-cable-recycling\",\n  inputs := [(1, .copperCable)],\n  outputs := [(1/8, .copperPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .copperOreRecycling => {\n  name := \"copper-ore-recycling\",\n  inputs := [(1, .copperOre)],\n  outputs := [(1/4, .copperOre)],\n  category := .recycling\n  time := 1/32\n}\n| .copperPlate => {\n  name := \"copper-plate\",\n  inputs := [(1, .copperOre)],\n  outputs := [(1, .copperPlate)],\n  category := .smelting\n  time := 16/5\n}\n| .copperPlateRecycling => {\n  name := \"copper-plate-recycling\",\n  inputs := [(1, .copperPlate)],\n  outputs := [(1/4, .copperPlate)],\n  category := .recycling\n  time := 1/5\n}\n| .crudeOilBarrel => {\n  name := \"crude-oil-barrel\",\n  inputs := [(50, .crudeOil), (1, .barrel)],\n  outputs := [(1, .crudeOilBarrel)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .crudeOilBarrelRecycling => {\n  name := \"crude-oil-barrel-recycling\",\n  inputs := [(1, .crudeOilBarrel)],\n  outputs := [(1/4, .barrel)],\n  category := .recycling\n  time := 1/80\n}\n| .crusher => {\n  name := \"crusher\",\n  inputs := [(20, .lowDensityStructure), (10, .steelPlate), (10, .electricEngineUnit)],\n  outputs := [(1, .crusher)],\n  category := .crafting\n  time := 10\n}\n| .crusherRecycling => {\n  name := \"crusher-recycling\",\n  inputs := [(1, .crusher)],\n  outputs := [(5, .lowDensityStructure), (5/2, .steelPlate), (5/2, .electricEngineUnit)],\n  category := .recycling\n  time := 5/8\n}\n| .cryogenicPlant => {\n  name := \"cryogenic-plant\",\n  inputs := [(40, .refinedConcrete), (20, .superconductor), (20, .processingUnit), (20, .lithiumPlate)],\n  outputs := [(1, .cryogenicPlant)],\n  category := .cryogenicsOrAssembling\n  time := 10\n}\n| .cryogenicPlantRecycling => {\n  name := \"cryogenic-plant-recycling\",\n  inputs := [(1, .cryogenicPlant)],\n  outputs := [(10, .refinedConcrete), (5, .superconductor), (5, .processingUnit), (5, .lithiumPlate)],\n  category := .recycling\n  time := 5/8\n}\n| .cryogenicSciencePack => {\n  name := \"cryogenic-science-pack\",\n  inputs := [(6, .fluoroketoneCold), (3, .ice), (1, .lithiumPlate)],\n  outputs := [(1, .cryogenicSciencePack), (3, .fluoroketoneHot)],\n  category := .cryogenics\n  time := 20\n}\n| .cryogenicSciencePackRecycling => {\n  name := \"cryogenic-science-pack-recycling\",\n  inputs := [(1, .cryogenicSciencePack)],\n  outputs := [(1/4, .cryogenicSciencePack)],\n  category := .recycling\n  time := 5/4\n}\n| .deciderCombinator => {\n  name := \"decider-combinator\",\n  inputs := [(5, .copperCable), (5, .electronicCircuit)],\n  outputs := [(1, .deciderCombinator)],\n  category := .crafting\n  time := 1/2\n}\n| .deciderCombinatorRecycling => {\n  name := \"decider-combinator-recycling\",\n  inputs := [(1, .deciderCombinator)],\n  outputs := [(5/4, .copperCable), (5/4, .electronicCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .deconstructionPlannerRecycling => {\n  name := \"deconstruction-planner-recycling\",\n  inputs := [(1, .deconstructionPlanner)],\n  outputs := [(1/4, .deconstructionPlanner)],\n  category := .recycling\n  time := 1/32\n}\n| .defenderCapsule => {\n  name := \"defender-capsule\",\n  inputs := [(3, .piercingRoundsMagazine), (3, .electronicCircuit), (3, .ironGearWheel)],\n  outputs := [(1, .defenderCapsule)],\n  category := .crafting\n  time := 8\n}\n| .defenderCapsuleRecycling => {\n  name := \"defender-capsule-recycling\",\n  inputs := [(1, .defenderCapsule)],\n  outputs := [(3/4, .piercingRoundsMagazine), (3/4, .electronicCircuit), (3/4, .ironGearWheel)],\n  category := .recycling\n  time := 1/2\n}\n| .depletedUraniumFuelCellRecycling => {\n  name := \"depleted-uranium-fuel-cell-recycling\",\n  inputs := [(1, .depletedUraniumFuelCell)],\n  outputs := [(1/4, .depletedUraniumFuelCell)],\n  category := .recycling\n  time := 1/32\n}\n| .destroyerCapsule => {\n  name := \"destroyer-capsule\",\n  inputs := [(4, .distractorCapsule), (4, .steelPlate), (1, .processingUnit)],\n  outputs := [(1, .destroyerCapsule)],\n  category := .crafting\n  time := 15\n}\n| .destroyerCapsuleRecycling => {\n  name := \"destroyer-capsule-recycling\",\n  inputs := [(1, .destroyerCapsule)],\n  outputs := [(1, .distractorCapsule), (1, .steelPlate), (1/4, .processingUnit)],\n  category := .recycling\n  time := 15/16\n}\n| .dischargeDefenseEquipment => {\n  name := \"discharge-defense-equipment\",\n  inputs := [(5, .processingUnit), (20, .steelPlate), (10, .laserTurret)],\n  outputs := [(1, .dischargeDefenseEquipment)],\n  category := .electronics\n  time := 10\n}\n| .dischargeDefenseEquipmentRecycling => {\n  name := \"discharge-defense-equipment-recycling\",\n  inputs := [(1, .dischargeDefenseEquipment)],\n  outputs := [(5, .steelPlate), (5/2, .laserTurret), (5/4, .processingUnit)],\n  category := .recycling\n  time := 5/8\n}\n| .displayPanel => {\n  name := \"display-panel\",\n  inputs := [(1, .ironPlate), (1, .electronicCircuit)],\n  outputs := [(1, .displayPanel)],\n  category := .crafting\n  time := 1/2\n}\n| .displayPanelRecycling => {\n  name := \"display-panel-recycling\",\n  inputs := [(1, .displayPanel)],\n  outputs := [(1/4, .ironPlate), (1/4, .electronicCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .distractorCapsule => {\n  name := \"distractor-capsule\",\n  inputs := [(4, .defenderCapsule), (3, .advancedCircuit)],\n  outputs := [(1, .distractorCapsule)],\n  category := .crafting\n  time := 15\n}\n| .distractorCapsuleRecycling => {\n  name := \"distractor-capsule-recycling\",\n  inputs := [(1, .distractorCapsule)],\n  outputs := [(1, .defenderCapsule), (3/4, .advancedCircuit)],\n  category := .recycling\n  time := 15/16\n}\n| .efficiencyModule => {\n  name := \"efficiency-module\",\n  inputs := [(5, .advancedCircuit), (5, .electronicCircuit)],\n  outputs := [(1, .efficiencyModule)],\n  category := .electronics\n  time := 15\n}\n| .efficiencyModule2 => {\n  name := \"efficiency-module-2\",\n  inputs := [(4, .efficiencyModule), (5, .advancedCircuit), (5, .processingUnit)],\n  outputs := [(1, .efficiencyModule2)],\n  category := .electronics\n  time := 30\n}\n| .efficiencyModule2Recycling => {\n  name := \"efficiency-module-2-recycling\",\n  inputs := [(1, .efficiencyModule2)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .processingUnit), (1, .efficiencyModule)],\n  category := .recycling\n  time := 15/8\n}\n| .efficiencyModule3 => {\n  name := \"efficiency-module-3\",\n  inputs := [(4, .efficiencyModule2), (5, .advancedCircuit), (5, .processingUnit), (5, .spoilage)],\n  outputs := [(1, .efficiencyModule3)],\n  category := .electronics\n  time := 60\n}\n| .efficiencyModule3Recycling => {\n  name := \"efficiency-module-3-recycling\",\n  inputs := [(1, .efficiencyModule3)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .processingUnit), (5/4, .spoilage), (1, .efficiencyModule2)],\n  category := .recycling\n  time := 15/4\n}\n| .efficiencyModuleRecycling => {\n  name := \"efficiency-module-recycling\",\n  inputs := [(1, .efficiencyModule)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .electronicCircuit)],\n  category := .recycling\n  time := 15/16\n}\n| .electricEnergyInterfaceRecycling => {\n  name := \"electric-energy-interface-recycling\",\n  inputs := [(1, .electricEnergyInterface)],\n  outputs := [(1/4, .electricEnergyInterface)],\n  category := .recycling\n  time := 1/32\n}\n| .electricEngineUnit => {\n  name := \"electric-engine-unit\",\n  inputs := [(15, .lubricant), (1, .engineUnit), (2, .electronicCircuit)],\n  outputs := [(1, .electricEngineUnit)],\n  category := .craftingWithFluid\n  time := 10\n}\n| .electricEngineUnitRecycling => {\n  name := \"electric-engine-unit-recycling\",\n  inputs := [(1, .electricEngineUnit)],\n  outputs := [(1/2, .electronicCircuit), (1/4, .engineUnit)],\n  category := .recycling\n  time := 5/8\n}\n| .electricFurnace => {\n  name := \"electric-furnace\",\n  inputs := [(10, .steelPlate), (5, .advancedCircuit), (10, .stoneBrick)],\n  outputs := [(1, .electricFurnace)],\n  category := .crafting\n  time := 5\n}\n| .electricFurnaceRecycling => {\n  name := \"electric-furnace-recycling\",\n  inputs := [(1, .electricFurnace)],\n  outputs := [(5/2, .steelPlate), (5/2, .stoneBrick), (5/4, .advancedCircuit)],\n  category := .recycling\n  time := 5/16\n}\n| .electricMiningDrill => {\n  name := \"electric-mining-drill\",\n  inputs := [(3, .electronicCircuit), (5, .ironGearWheel), (10, .ironPlate)],\n  outputs := [(1, .electricMiningDrill)],\n  category := .crafting\n  time := 2\n}\n| .electricMiningDrillRecycling => {\n  name := \"electric-mining-drill-recycling\",\n  inputs := [(1, .electricMiningDrill)],\n  outputs := [(5/2, .ironPlate), (5/4, .ironGearWheel), (3/4, .electronicCircuit)],\n  category := .recycling\n  time := 1/8\n}\n| .electrolyte => {\n  name := \"electrolyte\",\n  inputs := [(10, .heavyOil), (10, .holmiumSolution), (1, .stone)],\n  outputs := [(10, .electrolyte)],\n  category := .electromagnetics\n  time := 5\n}\n| .electromagneticPlant => {\n  name := \"electromagnetic-plant\",\n  inputs := [(150, .holmiumPlate), (50, .steelPlate), (50, .processingUnit), (50, .refinedConcrete)],\n  outputs := [(1, .electromagneticPlant)],\n  category := .electronicsOrAssembling\n  time := 10\n}\n| .electromagneticPlantRecycling => {\n  name := \"electromagnetic-plant-recycling\",\n  inputs := [(1, .electromagneticPlant)],\n  outputs := [(75/2, .holmiumPlate), (25/2, .steelPlate), (25/2, .processingUnit), (25/2, .refinedConcrete)],\n  category := .recycling\n  time := 5/8\n}\n| .electromagneticSciencePack => {\n  name := \"electromagnetic-science-pack\",\n  inputs := [(25, .electrolyte), (25, .holmiumSolution), (1, .supercapacitor), (1, .accumulator)],\n  outputs := [(1, .electromagneticSciencePack)],\n  category := .electromagnetics\n  time := 10\n}\n| .electromagneticSciencePackRecycling => {\n  name := \"electromagnetic-science-pack-recycling\",\n  inputs := [(1, .electromagneticSciencePack)],\n  outputs := [(1/4, .electromagneticSciencePack)],\n  category := .recycling\n  time := 5/8\n}\n| .electronicCircuit => {\n  name := \"electronic-circuit\",\n  inputs := [(1, .ironPlate), (3, .copperCable)],\n  outputs := [(1, .electronicCircuit)],\n  category := .electronics\n  time := 1/2\n}\n| .electronicCircuitRecycling => {\n  name := \"electronic-circuit-recycling\",\n  inputs := [(1, .electronicCircuit)],\n  outputs := [(3/4, .copperCable), (1/4, .ironPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .emptyCrudeOilBarrel => {\n  name := \"empty-crude-oil-barrel\",\n  inputs := [(1, .crudeOilBarrel)],\n  outputs := [(1, .barrel), (50, .crudeOil)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .emptyFluoroketoneColdBarrel => {\n  name := \"empty-fluoroketone-cold-barrel\",\n  inputs := [(1, .fluoroketoneColdBarrel)],\n  outputs := [(1, .barrel), (50, .fluoroketoneCold)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .emptyFluoroketoneHotBarrel => {\n  name := \"empty-fluoroketone-hot-barrel\",\n  inputs := [(1, .fluoroketoneHotBarrel)],\n  outputs := [(1, .barrel), (50, .fluoroketoneHot)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .emptyHeavyOilBarrel => {\n  name := \"empty-heavy-oil-barrel\",\n  inputs := [(1, .heavyOilBarrel)],\n  outputs := [(1, .barrel), (50, .heavyOil)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .emptyLightOilBarrel => {\n  name := \"empty-light-oil-barrel\",\n  inputs := [(1, .lightOilBarrel)],\n  outputs := [(1, .barrel), (50, .lightOil)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .emptyLubricantBarrel => {\n  name := \"empty-lubricant-barrel\",\n  inputs := [(1, .lubricantBarrel)],\n  outputs := [(1, .barrel), (50, .lubricant)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .emptyModuleSlotRecycling => {\n  name := \"empty-module-slot-recycling\",\n  inputs := [(1, .emptyModuleSlot)],\n  outputs := [(1/4, .emptyModuleSlot)],\n  category := .recycling\n  time := 1/32\n}\n| .emptyPetroleumGasBarrel => {\n  name := \"empty-petroleum-gas-barrel\",\n  inputs := [(1, .petroleumGasBarrel)],\n  outputs := [(1, .barrel), (50, .petroleumGas)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .emptySulfuricAcidBarrel => {\n  name := \"empty-sulfuric-acid-barrel\",\n  inputs := [(1, .sulfuricAcidBarrel)],\n  outputs := [(1, .barrel), (50, .sulfuricAcid)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .emptyWaterBarrel => {\n  name := \"empty-water-barrel\",\n  inputs := [(1, .waterBarrel)],\n  outputs := [(1, .barrel), (50, .water)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .energyShieldEquipment => {\n  name := \"energy-shield-equipment\",\n  inputs := [(5, .advancedCircuit), (10, .steelPlate)],\n  outputs := [(1, .energyShieldEquipment)],\n  category := .crafting\n  time := 10\n}\n| .energyShieldEquipmentRecycling => {\n  name := \"energy-shield-equipment-recycling\",\n  inputs := [(1, .energyShieldEquipment)],\n  outputs := [(5/2, .steelPlate), (5/4, .advancedCircuit)],\n  category := .recycling\n  time := 5/8\n}\n| .energyShieldMk2Equipment => {\n  name := \"energy-shield-mk2-equipment\",\n  inputs := [(10, .energyShieldEquipment), (5, .processingUnit), (5, .lowDensityStructure)],\n  outputs := [(1, .energyShieldMk2Equipment)],\n  category := .crafting\n  time := 10\n}\n| .energyShieldMk2EquipmentRecycling => {\n  name := \"energy-shield-mk2-equipment-recycling\",\n  inputs := [(1, .energyShieldMk2Equipment)],\n  outputs := [(5/2, .energyShieldEquipment), (5/4, .processingUnit), (5/4, .lowDensityStructure)],\n  category := .recycling\n  time := 5/8\n}\n| .engineUnit => {\n  name := \"engine-unit\",\n  inputs := [(1, .steelPlate), (1, .ironGearWheel), (2, .pipe)],\n  outputs := [(1, .engineUnit)],\n  category := .advancedCrafting\n  time := 10\n}\n| .engineUnitRecycling => {\n  name := \"engine-unit-recycling\",\n  inputs := [(1, .engineUnit)],\n  outputs := [(1/2, .pipe), (1/4, .steelPlate), (1/4, .ironGearWheel)],\n  category := .recycling\n  time := 5/8\n}\n| .exoskeletonEquipment => {\n  name := \"exoskeleton-equipment\",\n  inputs := [(10, .processingUnit), (30, .electricEngineUnit), (20, .steelPlate)],\n  outputs := [(1, .exoskeletonEquipment)],\n  category := .crafting\n  time := 10\n}\n| .exoskeletonEquipmentRecycling => {\n  name := \"exoskeleton-equipment-recycling\",\n  inputs := [(1, .exoskeletonEquipment)],\n  outputs := [(15/2, .electricEngineUnit), (5, .steelPlate), (5/2, .processingUnit)],\n  category := .recycling\n  time := 5/8\n}\n| .explosiveCannonShell => {\n  name := \"explosive-cannon-shell\",\n  inputs := [(2, .steelPlate), (2, .plasticBar), (2, .explosives)],\n  outputs := [(1, .explosiveCannonShell)],\n  category := .crafting\n  time := 8\n}\n| .explosiveCannonShellRecycling => {\n  name := \"explosive-cannon-shell-recycling\",\n  inputs := [(1, .explosiveCannonShell)],\n  outputs := [(1/2, .steelPlate), (1/2, .plasticBar), (1/2, .explosives)],\n  category := .recycling\n  time := 1/2\n}\n| .explosiveRocket => {\n  name := \"explosive-rocket\",\n  inputs := [(1, .rocket), (2, .explosives)],\n  outputs := [(1, .explosiveRocket)],\n  category := .crafting\n  time := 8\n}\n| .explosiveRocketRecycling => {\n  name := \"explosive-rocket-recycling\",\n  inputs := [(1, .explosiveRocket)],\n  outputs := [(1/2, .explosives), (1/4, .rocket)],\n  category := .recycling\n  time := 1/2\n}\n| .explosiveUraniumCannonShell => {\n  name := \"explosive-uranium-cannon-shell\",\n  inputs := [(1, .explosiveCannonShell), (1, .uranium238)],\n  outputs := [(1, .explosiveUraniumCannonShell)],\n  category := .crafting\n  time := 12\n}\n| .explosiveUraniumCannonShellRecycling => {\n  name := \"explosive-uranium-cannon-shell-recycling\",\n  inputs := [(1, .explosiveUraniumCannonShell)],\n  outputs := [(1/4, .explosiveCannonShell), (1/4, .uranium238)],\n  category := .recycling\n  time := 3/4\n}\n| .explosives => {\n  name := \"explosives\",\n  inputs := [(10, .water), (1, .sulfur), (1, .coal)],\n  outputs := [(2, .explosives)],\n  category := .chemistryOrCryogenics\n  time := 4\n}\n| .explosivesRecycling => {\n  name := \"explosives-recycling\",\n  inputs := [(1, .explosives)],\n  outputs := [(1/4, .explosives)],\n  category := .recycling\n  time := 1/4\n}\n| .expressLoader => {\n  name := \"express-loader\",\n  inputs := [(5, .expressTransportBelt), (1, .fastLoader)],\n  outputs := [(1, .expressLoader)],\n  category := .crafting\n  time := 10\n}\n| .expressLoaderRecycling => {\n  name := \"express-loader-recycling\",\n  inputs := [(1, .expressLoader)],\n  outputs := [(5/4, .expressTransportBelt), (1/4, .fastLoader)],\n  category := .recycling\n  time := 5/8\n}\n| .expressSplitter => {\n  name := \"express-splitter\",\n  inputs := [(80, .lubricant), (1, .fastSplitter), (10, .ironGearWheel), (10, .advancedCircuit)],\n  outputs := [(1, .expressSplitter)],\n  category := .craftingWithFluidOrMetallurgy\n  time := 2\n}\n| .expressSplitterRecycling => {\n  name := \"express-splitter-recycling\",\n  inputs := [(1, .expressSplitter)],\n  outputs := [(5/2, .ironGearWheel), (5/2, .advancedCircuit), (1/4, .fastSplitter)],\n  category := .recycling\n  time := 1/8\n}\n| .expressTransportBelt => {\n  name := \"express-transport-belt\",\n  inputs := [(20, .lubricant), (10, .ironGearWheel), (1, .fastTransportBelt)],\n  outputs := [(1, .expressTransportBelt)],\n  category := .craftingWithFluidOrMetallurgy\n  time := 1/2\n}\n| .expressTransportBeltRecycling => {\n  name := \"express-transport-belt-recycling\",\n  inputs := [(1, .expressTransportBelt)],\n  outputs := [(5/2, .ironGearWheel), (1/4, .fastTransportBelt)],\n  category := .recycling\n  time := 1/32\n}\n| .expressUndergroundBelt => {\n  name := \"express-underground-belt\",\n  inputs := [(40, .lubricant), (80, .ironGearWheel), (2, .fastUndergroundBelt)],\n  outputs := [(2, .expressUndergroundBelt)],\n  category := .craftingWithFluidOrMetallurgy\n  time := 2\n}\n| .expressUndergroundBeltRecycling => {\n  name := \"express-underground-belt-recycling\",\n  inputs := [(1, .expressUndergroundBelt)],\n  outputs := [(10, .ironGearWheel), (1/4, .fastUndergroundBelt)],\n  category := .recycling\n  time := 1/8\n}\n| .fastInserter => {\n  name := \"fast-inserter\",\n  inputs := [(2, .electronicCircuit), (2, .ironPlate), (1, .inserter)],\n  outputs := [(1, .fastInserter)],\n  category := .crafting\n  time := 1/2\n}\n| .fastInserterRecycling => {\n  name := \"fast-inserter-recycling\",\n  inputs := [(1, .fastInserter)],\n  outputs := [(1/2, .electronicCircuit), (1/2, .ironPlate), (1/4, .inserter)],\n  category := .recycling\n  time := 1/32\n}\n| .fastLoader => {\n  name := \"fast-loader\",\n  inputs := [(5, .fastTransportBelt), (1, .loader)],\n  outputs := [(1, .fastLoader)],\n  category := .crafting\n  time := 3\n}\n| .fastLoaderRecycling => {\n  name := \"fast-loader-recycling\",\n  inputs := [(1, .fastLoader)],\n  outputs := [(5/4, .fastTransportBelt), (1/4, .loader)],\n  category := .recycling\n  time := 3/16\n}\n| .fastSplitter => {\n  name := \"fast-splitter\",\n  inputs := [(1, .splitter), (10, .ironGearWheel), (10, .electronicCircuit)],\n  outputs := [(1, .fastSplitter)],\n  category := .pressing\n  time := 2\n}\n| .fastSplitterRecycling => {\n  name := \"fast-splitter-recycling\",\n  inputs := [(1, .fastSplitter)],\n  outputs := [(5/2, .ironGearWheel), (5/2, .electronicCircuit), (1/4, .splitter)],\n  category := .recycling\n  time := 1/8\n}\n| .fastTransportBelt => {\n  name := \"fast-transport-belt\",\n  inputs := [(5, .ironGearWheel), (1, .transportBelt)],\n  outputs := [(1, .fastTransportBelt)],\n  category := .pressing\n  time := 1/2\n}\n| .fastTransportBeltRecycling => {\n  name := \"fast-transport-belt-recycling\",\n  inputs := [(1, .fastTransportBelt)],\n  outputs := [(5/4, .ironGearWheel), (1/4, .transportBelt)],\n  category := .recycling\n  time := 1/32\n}\n| .fastUndergroundBelt => {\n  name := \"fast-underground-belt\",\n  inputs := [(40, .ironGearWheel), (2, .undergroundBelt)],\n  outputs := [(2, .fastUndergroundBelt)],\n  category := .pressing\n  time := 2\n}\n| .fastUndergroundBeltRecycling => {\n  name := \"fast-underground-belt-recycling\",\n  inputs := [(1, .fastUndergroundBelt)],\n  outputs := [(5, .ironGearWheel), (1/4, .undergroundBelt)],\n  category := .recycling\n  time := 1/8\n}\n| .firearmMagazine => {\n  name := \"firearm-magazine\",\n  inputs := [(4, .ironPlate)],\n  outputs := [(1, .firearmMagazine)],\n  category := .crafting\n  time := 1\n}\n| .firearmMagazineRecycling => {\n  name := \"firearm-magazine-recycling\",\n  inputs := [(1, .firearmMagazine)],\n  outputs := [(1, .ironPlate)],\n  category := .recycling\n  time := 1/16\n}\n| .fishBreeding => {\n  name := \"fish-breeding\",\n  inputs := [(100, .water), (2, .rawFish), (100, .nutrients)],\n  outputs := [(3, .rawFish)],\n  category := .organicOrChemistry\n  time := 6\n}\n| .fissionReactorEquipment => {\n  name := \"fission-reactor-equipment\",\n  inputs := [(200, .processingUnit), (50, .lowDensityStructure), (4, .uraniumFuelCell)],\n  outputs := [(1, .fissionReactorEquipment)],\n  category := .crafting\n  time := 10\n}\n| .fissionReactorEquipmentRecycling => {\n  name := \"fission-reactor-equipment-recycling\",\n  inputs := [(1, .fissionReactorEquipment)],\n  outputs := [(50, .processingUnit), (25/2, .lowDensityStructure), (1, .uraniumFuelCell)],\n  category := .recycling\n  time := 5/8\n}\n| .flamethrower => {\n  name := \"flamethrower\",\n  inputs := [(5, .steelPlate), (10, .ironGearWheel)],\n  outputs := [(1, .flamethrower)],\n  category := .crafting\n  time := 10\n}\n| .flamethrowerAmmo => {\n  name := \"flamethrower-ammo\",\n  inputs := [(100, .crudeOil), (5, .steelPlate)],\n  outputs := [(1, .flamethrowerAmmo)],\n  category := .chemistry\n  time := 6\n}\n| .flamethrowerAmmoRecycling => {\n  name := \"flamethrower-ammo-recycling\",\n  inputs := [(1, .flamethrowerAmmo)],\n  outputs := [(1/4, .flamethrowerAmmo)],\n  category := .recycling\n  time := 3/8\n}\n| .flamethrowerRecycling => {\n  name := \"flamethrower-recycling\",\n  inputs := [(1, .flamethrower)],\n  outputs := [(5/2, .ironGearWheel), (5/4, .steelPlate)],\n  category := .recycling\n  time := 5/8\n}\n| .flamethrowerTurret => {\n  name := \"flamethrower-turret\",\n  inputs := [(30, .steelPlate), (15, .ironGearWheel), (10, .pipe), (5, .engineUnit)],\n  outputs := [(1, .flamethrowerTurret)],\n  category := .crafting\n  time := 20\n}\n| .flamethrowerTurretRecycling => {\n  name := \"flamethrower-turret-recycling\",\n  inputs := [(1, .flamethrowerTurret)],\n  outputs := [(15/2, .steelPlate), (15/4, .ironGearWheel), (5/2, .pipe), (5/4, .engineUnit)],\n  category := .recycling\n  time := 5/4\n}\n| .fluidWagon => {\n  name := \"fluid-wagon\",\n  inputs := [(10, .ironGearWheel), (16, .steelPlate), (8, .pipe), (1, .storageTank)],\n  outputs := [(1, .fluidWagon)],\n  category := .crafting\n  time := 3/2\n}\n| .fluidWagonRecycling => {\n  name := \"fluid-wagon-recycling\",\n  inputs := [(1, .fluidWagon)],\n  outputs := [(4, .steelPlate), (5/2, .ironGearWheel), (2, .pipe), (1/4, .storageTank)],\n  category := .recycling\n  time := 3/32\n}\n| .fluoroketone => {\n  name := \"fluoroketone\",\n  inputs := [(50, .fluorine), (50, .ammonia), (1, .solidFuel), (1, .lithium)],\n  outputs := [(50, .fluoroketoneHot)],\n  category := .cryogenics\n  time := 10\n}\n| .fluoroketoneColdBarrel => {\n  name := \"fluoroketone-cold-barrel\",\n  inputs := [(50, .fluoroketoneCold), (1, .barrel)],\n  outputs := [(1, .fluoroketoneColdBarrel)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .fluoroketoneColdBarrelRecycling => {\n  name := \"fluoroketone-cold-barrel-recycling\",\n  inputs := [(1, .fluoroketoneColdBarrel)],\n  outputs := [(1/4, .barrel)],\n  category := .recycling\n  time := 1/80\n}\n| .fluoroketoneCooling => {\n  name := \"fluoroketone-cooling\",\n  inputs := [(10, .fluoroketoneHot)],\n  outputs := [(10, .fluoroketoneCold)],\n  category := .cryogenics\n  time := 5\n}\n| .fluoroketoneHotBarrel => {\n  name := \"fluoroketone-hot-barrel\",\n  inputs := [(50, .fluoroketoneHot), (1, .barrel)],\n  outputs := [(1, .fluoroketoneHotBarrel)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .fluoroketoneHotBarrelRecycling => {\n  name := \"fluoroketone-hot-barrel-recycling\",\n  inputs := [(1, .fluoroketoneHotBarrel)],\n  outputs := [(1/4, .barrel)],\n  category := .recycling\n  time := 1/80\n}\n| .flyingRobotFrame => {\n  name := \"flying-robot-frame\",\n  inputs := [(1, .electricEngineUnit), (2, .battery), (1, .steelPlate), (3, .electronicCircuit)],\n  outputs := [(1, .flyingRobotFrame)],\n  category := .crafting\n  time := 20\n}\n| .flyingRobotFrameRecycling => {\n  name := \"flying-robot-frame-recycling\",\n  inputs := [(1, .flyingRobotFrame)],\n  outputs := [(3/4, .electronicCircuit), (1/2, .battery), (1/4, .electricEngineUnit), (1/4, .steelPlate)],\n  category := .recycling\n  time := 5/4\n}\n| .foundation => {\n  name := \"foundation\",\n  inputs := [(20, .fluoroketoneCold), (4, .tungstenPlate), (4, .lithiumPlate), (4, .carbonFiber), (20, .stone)],\n  outputs := [(1, .foundation)],\n  category := .craftingWithFluid\n  time := 30\n}\n| .foundationRecycling => {\n  name := \"foundation-recycling\",\n  inputs := [(1, .foundation)],\n  outputs := [(5, .stone), (1, .tungstenPlate), (1, .lithiumPlate), (1, .carbonFiber)],\n  category := .recycling\n  time := 15/8\n}\n| .foundry => {\n  name := \"foundry\",\n  inputs := [(20, .lubricant), (50, .tungstenCarbide), (50, .steelPlate), (30, .electronicCircuit), (20, .refinedConcrete)],\n  outputs := [(1, .foundry)],\n  category := .metallurgyOrAssembling\n  time := 10\n}\n| .foundryRecycling => {\n  name := \"foundry-recycling\",\n  inputs := [(1, .foundry)],\n  outputs := [(25/2, .tungstenCarbide), (25/2, .steelPlate), (15/2, .electronicCircuit), (5, .refinedConcrete)],\n  category := .recycling\n  time := 5/8\n}\n| .fusionGenerator => {\n  name := \"fusion-generator\",\n  inputs := [(100, .tungstenPlate), (100, .superconductor), (50, .quantumProcessor)],\n  outputs := [(1, .fusionGenerator)],\n  category := .cryogenics\n  time := 30\n}\n| .fusionGeneratorRecycling => {\n  name := \"fusion-generator-recycling\",\n  inputs := [(1, .fusionGenerator)],\n  outputs := [(25, .tungstenPlate), (25, .superconductor), (25/2, .quantumProcessor)],\n  category := .recycling\n  time := 15/8\n}\n| .fusionPowerCell => {\n  name := \"fusion-power-cell\",\n  inputs := [(100, .ammonia), (5, .lithiumPlate), (1, .holmiumPlate)],\n  outputs := [(1, .fusionPowerCell)],\n  category := .cryogenics\n  time := 10\n}\n| .fusionPowerCellRecycling => {\n  name := \"fusion-power-cell-recycling\",\n  inputs := [(1, .fusionPowerCell)],\n  outputs := [(1/4, .fusionPowerCell)],\n  category := .recycling\n  time := 5/8\n}\n| .fusionReactor => {\n  name := \"fusion-reactor\",\n  inputs := [(200, .tungstenPlate), (200, .superconductor), (250, .quantumProcessor)],\n  outputs := [(1, .fusionReactor)],\n  category := .cryogenics\n  time := 60\n}\n| .fusionReactorEquipment => {\n  name := \"fusion-reactor-equipment\",\n  inputs := [(1, .fissionReactorEquipment), (10, .fusionPowerCell), (250, .tungstenPlate), (100, .carbonFiber), (25, .supercapacitor), (250, .quantumProcessor)],\n  outputs := [(1, .fusionReactorEquipment)],\n  category := .crafting\n  time := 30\n}\n| .fusionReactorEquipmentRecycling => {\n  name := \"fusion-reactor-equipment-recycling\",\n  inputs := [(1, .fusionReactorEquipment)],\n  outputs := [(125/2, .tungstenPlate), (125/2, .quantumProcessor), (25, .carbonFiber), (25/4, .supercapacitor), (5/2, .fusionPowerCell), (1/4, .fissionReactorEquipment)],\n  category := .recycling\n  time := 15/8\n}\n| .fusionReactorRecycling => {\n  name := \"fusion-reactor-recycling\",\n  inputs := [(1, .fusionReactor)],\n  outputs := [(125/2, .quantumProcessor), (50, .tungstenPlate), (50, .superconductor)],\n  category := .recycling\n  time := 15/4\n}\n| .gate => {\n  name := \"gate\",\n  inputs := [(1, .stoneWall), (2, .steelPlate), (2, .electronicCircuit)],\n  outputs := [(1, .gate)],\n  category := .crafting\n  time := 1/2\n}\n| .gateRecycling => {\n  name := \"gate-recycling\",\n  inputs := [(1, .gate)],\n  outputs := [(1/2, .steelPlate), (1/2, .electronicCircuit), (1/4, .stoneWall)],\n  category := .recycling\n  time := 1/32\n}\n| .grenade => {\n  name := \"grenade\",\n  inputs := [(5, .ironPlate), (10, .coal)],\n  outputs := [(1, .grenade)],\n  category := .crafting\n  time := 8\n}\n| .grenadeRecycling => {\n  name := \"grenade-recycling\",\n  inputs := [(1, .grenade)],\n  outputs := [(5/2, .coal), (5/4, .ironPlate)],\n  category := .recycling\n  time := 1/2\n}\n| .gunTurret => {\n  name := \"gun-turret\",\n  inputs := [(10, .ironGearWheel), (10, .copperPlate), (20, .ironPlate)],\n  outputs := [(1, .gunTurret)],\n  category := .crafting\n  time := 8\n}\n| .gunTurretRecycling => {\n  name := \"gun-turret-recycling\",\n  inputs := [(1, .gunTurret)],\n  outputs := [(5, .ironPlate), (5/2, .ironGearWheel), (5/2, .copperPlate)],\n  category := .recycling\n  time := 1/2\n}\n| .hazardConcrete => {\n  name := \"hazard-concrete\",\n  inputs := [(10, .concrete)],\n  outputs := [(10, .hazardConcrete)],\n  category := .crafting\n  time := 1/4\n}\n| .hazardConcreteRecycling => {\n  name := \"hazard-concrete-recycling\",\n  inputs := [(1, .hazardConcrete)],\n  outputs := [(1/4, .concrete)],\n  category := .recycling\n  time := 1/64\n}\n| .heatExchanger => {\n  name := \"heat-exchanger\",\n  inputs := [(10, .steelPlate), (100, .copperPlate), (10, .pipe)],\n  outputs := [(1, .heatExchanger)],\n  category := .crafting\n  time := 3\n}\n| .heatExchangerRecycling => {\n  name := \"heat-exchanger-recycling\",\n  inputs := [(1, .heatExchanger)],\n  outputs := [(25, .copperPlate), (5/2, .steelPlate), (5/2, .pipe)],\n  category := .recycling\n  time := 3/16\n}\n| .heatInterface => {\n  name := \"heat-interface\",\n  inputs := [(1, .heatPipe), (5, .electronicCircuit)],\n  outputs := [(1, .heatInterface)],\n  category := .crafting\n  time := 1/2\n}\n| .heatInterfaceRecycling => {\n  name := \"heat-interface-recycling\",\n  inputs := [(1, .heatInterface)],\n  outputs := [(5/4, .electronicCircuit), (1/4, .heatPipe)],\n  category := .recycling\n  time := 1/32\n}\n| .heatPipe => {\n  name := \"heat-pipe\",\n  inputs := [(10, .steelPlate), (20, .copperPlate)],\n  outputs := [(1, .heatPipe)],\n  category := .crafting\n  time := 1\n}\n| .heatPipeRecycling => {\n  name := \"heat-pipe-recycling\",\n  inputs := [(1, .heatPipe)],\n  outputs := [(5, .copperPlate), (5/2, .steelPlate)],\n  category := .recycling\n  time := 1/16\n}\n| .heatingTower => {\n  name := \"heating-tower\",\n  inputs := [(2, .boiler), (5, .heatPipe), (20, .concrete)],\n  outputs := [(1, .heatingTower)],\n  category := .crafting\n  time := 10\n}\n| .heatingTowerRecycling => {\n  name := \"heating-tower-recycling\",\n  inputs := [(1, .heatingTower)],\n  outputs := [(5, .concrete), (5/4, .heatPipe), (1/2, .boiler)],\n  category := .recycling\n  time := 5/8\n}\n| .heavyArmor => {\n  name := \"heavy-armor\",\n  inputs := [(100, .copperPlate), (50, .steelPlate)],\n  outputs := [(1, .heavyArmor)],\n  category := .crafting\n  time := 8\n}\n| .heavyArmorRecycling => {\n  name := \"heavy-armor-recycling\",\n  inputs := [(1, .heavyArmor)],\n  outputs := [(25, .copperPlate), (25/2, .steelPlate)],\n  category := .recycling\n  time := 1/2\n}\n| .heavyOilBarrel => {\n  name := \"heavy-oil-barrel\",\n  inputs := [(50, .heavyOil), (1, .barrel)],\n  outputs := [(1, .heavyOilBarrel)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .heavyOilBarrelRecycling => {\n  name := \"heavy-oil-barrel-recycling\",\n  inputs := [(1, .heavyOilBarrel)],\n  outputs := [(1/4, .barrel)],\n  category := .recycling\n  time := 1/80\n}\n| .heavyOilCracking => {\n  name := \"heavy-oil-cracking\",\n  inputs := [(30, .water), (40, .heavyOil)],\n  outputs := [(30, .lightOil)],\n  category := .organicOrChemistry\n  time := 2\n}\n| .holmiumOreRecycling => {\n  name := \"holmium-ore-recycling\",\n  inputs := [(1, .holmiumOre)],\n  outputs := [(1/4, .holmiumOre)],\n  category := .recycling\n  time := 1/32\n}\n| .holmiumPlate => {\n  name := \"holmium-plate\",\n  inputs := [(20, .holmiumSolution)],\n  outputs := [(1, .holmiumPlate)],\n  category := .craftingWithFluidOrMetallurgy\n  time := 1\n}\n| .holmiumPlateRecycling => {\n  name := \"holmium-plate-recycling\",\n  inputs := [(1, .holmiumPlate)],\n  outputs := [(1/4, .holmiumPlate)],\n  category := .recycling\n  time := 1/16\n}\n| .holmiumSolution => {\n  name := \"holmium-solution\",\n  inputs := [(10, .water), (2, .holmiumOre), (1, .stone)],\n  outputs := [(100, .holmiumSolution)],\n  category := .chemistry\n  time := 10\n}\n| .iceMelting => {\n  name := \"ice-melting\",\n  inputs := [(1, .ice)],\n  outputs := [(20, .water)],\n  category := .chemistry\n  time := 1\n}\n| .icePlatform => {\n  name := \"ice-platform\",\n  inputs := [(400, .ammonia), (50, .ice)],\n  outputs := [(1, .icePlatform)],\n  category := .craftingWithFluid\n  time := 30\n}\n| .icePlatformRecycling => {\n  name := \"ice-platform-recycling\",\n  inputs := [(1, .icePlatform)],\n  outputs := [(25/2, .ice)],\n  category := .recycling\n  time := 15/8\n}\n| .iceRecycling => {\n  name := \"ice-recycling\",\n  inputs := [(1, .ice)],\n  outputs := [(1/4, .ice)],\n  category := .recycling\n  time := 1/32\n}\n| .infinityCargoWagonRecycling => {\n  name := \"infinity-cargo-wagon-recycling\",\n  inputs := [(1, .infinityCargoWagon)],\n  outputs := [(1/4, .infinityCargoWagon)],\n  category := .recycling\n  time := 1/32\n}\n| .infinityChest => {\n  name := \"infinity-chest\",\n  inputs := [(1, .steelChest), (5, .electronicCircuit)],\n  outputs := [(1, .infinityChest)],\n  category := .crafting\n  time := 1/2\n}\n| .infinityChestRecycling => {\n  name := \"infinity-chest-recycling\",\n  inputs := [(1, .infinityChest)],\n  outputs := [(5/4, .electronicCircuit), (1/4, .steelChest)],\n  category := .recycling\n  time := 1/32\n}\n| .infinityPipe => {\n  name := \"infinity-pipe\",\n  inputs := [(1, .pipe), (5, .electronicCircuit)],\n  outputs := [(1, .infinityPipe)],\n  category := .crafting\n  time := 1/2\n}\n| .infinityPipeRecycling => {\n  name := \"infinity-pipe-recycling\",\n  inputs := [(1, .infinityPipe)],\n  outputs := [(5/4, .electronicCircuit), (1/4, .pipe)],\n  category := .recycling\n  time := 1/32\n}\n| .inserter => {\n  name := \"inserter\",\n  inputs := [(1, .electronicCircuit), (1, .ironGearWheel), (1, .ironPlate)],\n  outputs := [(1, .inserter)],\n  category := .crafting\n  time := 1/2\n}\n| .inserterRecycling => {\n  name := \"inserter-recycling\",\n  inputs := [(1, .inserter)],\n  outputs := [(1/4, .electronicCircuit), (1/4, .ironGearWheel), (1/4, .ironPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .ironBacteria => {\n  name := \"iron-bacteria\",\n  inputs := [(6, .jelly)],\n  outputs := [(4, .spoilage), (1/10, .ironBacteria)],\n  category := .organicOrHandCrafting\n  time := 1\n}\n| .ironBacteriaCultivation => {\n  name := \"iron-bacteria-cultivation\",\n  inputs := [(1, .ironBacteria), (1, .bioflux)],\n  outputs := [(4, .ironBacteria)],\n  category := .organic\n  time := 4\n}\n| .ironBacteriaRecycling => {\n  name := \"iron-bacteria-recycling\",\n  inputs := [(1, .ironBacteria)],\n  outputs := [(1/4, .ironBacteria)],\n  category := .recycling\n  time := 1/16\n}\n| .ironChest => {\n  name := \"iron-chest\",\n  inputs := [(8, .ironPlate)],\n  outputs := [(1, .ironChest)],\n  category := .crafting\n  time := 1/2\n}\n| .ironChestRecycling => {\n  name := \"iron-chest-recycling\",\n  inputs := [(1, .ironChest)],\n  outputs := [(2, .ironPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .ironGearWheel => {\n  name := \"iron-gear-wheel\",\n  inputs := [(2, .ironPlate)],\n  outputs := [(1, .ironGearWheel)],\n  category := .crafting\n  time := 1/2\n}\n| .ironGearWheelRecycling => {\n  name := \"iron-gear-wheel-recycling\",\n  inputs := [(1, .ironGearWheel)],\n  outputs := [(1/2, .ironPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .ironOreRecycling => {\n  name := \"iron-ore-recycling\",\n  inputs := [(1, .ironOre)],\n  outputs := [(1/4, .ironOre)],\n  category := .recycling\n  time := 1/32\n}\n| .ironPlate => {\n  name := \"iron-plate\",\n  inputs := [(1, .ironOre)],\n  outputs := [(1, .ironPlate)],\n  category := .smelting\n  time := 16/5\n}\n| .ironPlateRecycling => {\n  name := \"iron-plate-recycling\",\n  inputs := [(1, .ironPlate)],\n  outputs := [(1/4, .ironPlate)],\n  category := .recycling\n  time := 1/5\n}\n| .ironStick => {\n  name := \"iron-stick\",\n  inputs := [(1, .ironPlate)],\n  outputs := [(2, .ironStick)],\n  category := .crafting\n  time := 1/2\n}\n| .ironStickRecycling => {\n  name := \"iron-stick-recycling\",\n  inputs := [(1, .ironStick)],\n  outputs := [(1/8, .ironPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .itemUnknownRecycling => {\n  name := \"item-unknown-recycling\",\n  inputs := [(1, .itemUnknown)],\n  outputs := [(1/4, .itemUnknown)],\n  category := .recycling\n  time := 1/32\n}\n| .jellyRecycling => {\n  name := \"jelly-recycling\",\n  inputs := [(1, .jelly)],\n  outputs := [(1/4, .jelly)],\n  category := .recycling\n  time := 1/32\n}\n| .jellynutProcessing => {\n  name := \"jellynut-processing\",\n  inputs := [(1, .jellynut)],\n  outputs := [(4, .jelly), (1/50, .jellynutSeed)],\n  category := .organicOrHandCrafting\n  time := 1\n}\n| .jellynutRecycling => {\n  name := \"jellynut-recycling\",\n  inputs := [(1, .jellynut)],\n  outputs := [(1/4, .jellynut)],\n  category := .recycling\n  time := 1/32\n}\n| .jellynutSeedRecycling => {\n  name := \"jellynut-seed-recycling\",\n  inputs := [(1, .jellynutSeed)],\n  outputs := [(1/4, .jellynutSeed)],\n  category := .recycling\n  time := 1/32\n}\n| .kovarexEnrichmentProcess => {\n  name := \"kovarex-enrichment-process\",\n  inputs := [(40, .uranium235), (5, .uranium238)],\n  outputs := [(41, .uranium235), (2, .uranium238)],\n  category := .centrifuging\n  time := 60\n}\n| .lab => {\n  name := \"lab\",\n  inputs := [(10, .electronicCircuit), (10, .ironGearWheel), (4, .transportBelt)],\n  outputs := [(1, .lab)],\n  category := .crafting\n  time := 2\n}\n| .labRecycling => {\n  name := \"lab-recycling\",\n  inputs := [(1, .lab)],\n  outputs := [(5/2, .electronicCircuit), (5/2, .ironGearWheel), (1, .transportBelt)],\n  category := .recycling\n  time := 1/8\n}\n| .landMine => {\n  name := \"land-mine\",\n  inputs := [(1, .steelPlate), (2, .explosives)],\n  outputs := [(4, .landMine)],\n  category := .crafting\n  time := 5\n}\n| .landMineRecycling => {\n  name := \"land-mine-recycling\",\n  inputs := [(1, .landMine)],\n  outputs := [(1/8, .explosives), (1/16, .steelPlate)],\n  category := .recycling\n  time := 5/16\n}\n| .landfill => {\n  name := \"landfill\",\n  inputs := [(50, .stone)],\n  outputs := [(1, .landfill)],\n  category := .crafting\n  time := 1/2\n}\n| .landfillRecycling => {\n  name := \"landfill-recycling\",\n  inputs := [(1, .landfill)],\n  outputs := [(1/4, .landfill)],\n  category := .recycling\n  time := 1/32\n}\n| .laneSplitterRecycling => {\n  name := \"lane-splitter-recycling\",\n  inputs := [(1, .laneSplitter)],\n  outputs := [(1/4, .laneSplitter)],\n  category := .recycling\n  time := 1/32\n}\n| .laserTurret => {\n  name := \"laser-turret\",\n  inputs := [(20, .steelPlate), (20, .electronicCircuit), (12, .battery)],\n  outputs := [(1, .laserTurret)],\n  category := .crafting\n  time := 20\n}\n| .laserTurretRecycling => {\n  name := \"laser-turret-recycling\",\n  inputs := [(1, .laserTurret)],\n  outputs := [(5, .steelPlate), (5, .electronicCircuit), (3, .battery)],\n  category := .recycling\n  time := 5/4\n}\n| .lightArmor => {\n  name := \"light-armor\",\n  inputs := [(40, .ironPlate)],\n  outputs := [(1, .lightArmor)],\n  category := .crafting\n  time := 3\n}\n| .lightArmorRecycling => {\n  name := \"light-armor-recycling\",\n  inputs := [(1, .lightArmor)],\n  outputs := [(10, .ironPlate)],\n  category := .recycling\n  time := 3/16\n}\n| .lightOilBarrel => {\n  name := \"light-oil-barrel\",\n  inputs := [(50, .lightOil), (1, .barrel)],\n  outputs := [(1, .lightOilBarrel)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .lightOilBarrelRecycling => {\n  name := \"light-oil-barrel-recycling\",\n  inputs := [(1, .lightOilBarrel)],\n  outputs := [(1/4, .barrel)],\n  category := .recycling\n  time := 1/80\n}\n| .lightOilCracking => {\n  name := \"light-oil-cracking\",\n  inputs := [(30, .water), (30, .lightOil)],\n  outputs := [(20, .petroleumGas)],\n  category := .organicOrChemistry\n  time := 2\n}\n| .lightningCollector => {\n  name := \"lightning-collector\",\n  inputs := [(80, .electrolyte), (1, .lightningRod), (8, .supercapacitor), (1, .accumulator)],\n  outputs := [(1, .lightningCollector)],\n  category := .electromagnetics\n  time := 5\n}\n| .lightningCollectorRecycling => {\n  name := \"lightning-collector-recycling\",\n  inputs := [(1, .lightningCollector)],\n  outputs := [(2, .supercapacitor), (1/4, .lightningRod), (1/4, .accumulator)],\n  category := .recycling\n  time := 5/16\n}\n| .lightningRod => {\n  name := \"lightning-rod\",\n  inputs := [(12, .copperCable), (8, .steelPlate), (4, .stoneBrick)],\n  outputs := [(1, .lightningRod)],\n  category := .electronics\n  time := 5\n}\n| .lightningRodRecycling => {\n  name := \"lightning-rod-recycling\",\n  inputs := [(1, .lightningRod)],\n  outputs := [(3, .copperCable), (2, .steelPlate), (1, .stoneBrick)],\n  category := .recycling\n  time := 5/16\n}\n| .linkedBeltRecycling => {\n  name := \"linked-belt-recycling\",\n  inputs := [(1, .linkedBelt)],\n  outputs := [(1/4, .linkedBelt)],\n  category := .recycling\n  time := 1/32\n}\n| .linkedChestRecycling => {\n  name := \"linked-chest-recycling\",\n  inputs := [(1, .linkedChest)],\n  outputs := [(1/4, .linkedChest)],\n  category := .recycling\n  time := 1/32\n}\n| .lithium => {\n  name := \"lithium\",\n  inputs := [(50, .lithiumBrine), (50, .ammonia), (1, .holmiumPlate)],\n  outputs := [(5, .lithium)],\n  category := .chemistryOrCryogenics\n  time := 20\n}\n| .lithiumPlate => {\n  name := \"lithium-plate\",\n  inputs := [(1, .lithium)],\n  outputs := [(1, .lithiumPlate)],\n  category := .smelting\n  time := 32/5\n}\n| .lithiumPlateRecycling => {\n  name := \"lithium-plate-recycling\",\n  inputs := [(1, .lithiumPlate)],\n  outputs := [(1/4, .lithiumPlate)],\n  category := .recycling\n  time := 2/5\n}\n| .lithiumRecycling => {\n  name := \"lithium-recycling\",\n  inputs := [(1, .lithium)],\n  outputs := [(1/4, .lithium)],\n  category := .recycling\n  time := 5/4\n}\n| .loader => {\n  name := \"loader\",\n  inputs := [(5, .inserter), (5, .electronicCircuit), (5, .ironGearWheel), (5, .ironPlate), (5, .transportBelt)],\n  outputs := [(1, .loader)],\n  category := .crafting\n  time := 1\n}\n| .loaderRecycling => {\n  name := \"loader-recycling\",\n  inputs := [(1, .loader)],\n  outputs := [(5/4, .inserter), (5/4, .electronicCircuit), (5/4, .ironGearWheel), (5/4, .ironPlate), (5/4, .transportBelt)],\n  category := .recycling\n  time := 1/16\n}\n| .locomotive => {\n  name := \"locomotive\",\n  inputs := [(20, .engineUnit), (10, .electronicCircuit), (30, .steelPlate)],\n  outputs := [(1, .locomotive)],\n  category := .crafting\n  time := 4\n}\n| .locomotiveRecycling => {\n  name := \"locomotive-recycling\",\n  inputs := [(1, .locomotive)],\n  outputs := [(15/2, .steelPlate), (5, .engineUnit), (5/2, .electronicCircuit)],\n  category := .recycling\n  time := 1/4\n}\n| .logisticRobot => {\n  name := \"logistic-robot\",\n  inputs := [(1, .flyingRobotFrame), (2, .advancedCircuit)],\n  outputs := [(1, .logisticRobot)],\n  category := .crafting\n  time := 1/2\n}\n| .logisticRobotRecycling => {\n  name := \"logistic-robot-recycling\",\n  inputs := [(1, .logisticRobot)],\n  outputs := [(1/2, .advancedCircuit), (1/4, .flyingRobotFrame)],\n  category := .recycling\n  time := 1/32\n}\n| .logisticSciencePack => {\n  name := \"logistic-science-pack\",\n  inputs := [(1, .inserter), (1, .transportBelt)],\n  outputs := [(1, .logisticSciencePack)],\n  category := .crafting\n  time := 6\n}\n| .logisticSciencePackRecycling => {\n  name := \"logistic-science-pack-recycling\",\n  inputs := [(1, .logisticSciencePack)],\n  outputs := [(1/4, .logisticSciencePack)],\n  category := .recycling\n  time := 3/8\n}\n| .longHandedInserter => {\n  name := \"long-handed-inserter\",\n  inputs := [(1, .ironGearWheel), (1, .ironPlate), (1, .inserter)],\n  outputs := [(1, .longHandedInserter)],\n  category := .crafting\n  time := 1/2\n}\n| .longHandedInserterRecycling => {\n  name := \"long-handed-inserter-recycling\",\n  inputs := [(1, .longHandedInserter)],\n  outputs := [(1/4, .ironGearWheel), (1/4, .ironPlate), (1/4, .inserter)],\n  category := .recycling\n  time := 1/32\n}\n| .lowDensityStructure => {\n  name := \"low-density-structure\",\n  inputs := [(2, .steelPlate), (20, .copperPlate), (5, .plasticBar)],\n  outputs := [(1, .lowDensityStructure)],\n  category := .crafting\n  time := 15\n}\n| .lowDensityStructureRecycling => {\n  name := \"low-density-structure-recycling\",\n  inputs := [(1, .lowDensityStructure)],\n  outputs := [(5, .copperPlate), (5/4, .plasticBar), (1/2, .steelPlate)],\n  category := .recycling\n  time := 15/16\n}\n| .lubricant => {\n  name := \"lubricant\",\n  inputs := [(10, .heavyOil)],\n  outputs := [(10, .lubricant)],\n  category := .chemistry\n  time := 1\n}\n| .lubricantBarrel => {\n  name := \"lubricant-barrel\",\n  inputs := [(50, .lubricant), (1, .barrel)],\n  outputs := [(1, .lubricantBarrel)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .lubricantBarrelRecycling => {\n  name := \"lubricant-barrel-recycling\",\n  inputs := [(1, .lubricantBarrel)],\n  outputs := [(1/4, .barrel)],\n  category := .recycling\n  time := 1/80\n}\n| .mechArmor => {\n  name := \"mech-armor\",\n  inputs := [(1, .powerArmorMk2), (200, .holmiumPlate), (100, .processingUnit), (50, .superconductor), (50, .supercapacitor)],\n  outputs := [(1, .mechArmor)],\n  category := .crafting\n  time := 60\n}\n| .mechArmorRecycling => {\n  name := \"mech-armor-recycling\",\n  inputs := [(1, .mechArmor)],\n  outputs := [(50, .holmiumPlate), (25, .processingUnit), (25/2, .superconductor), (25/2, .supercapacitor), (1/4, .powerArmorMk2)],\n  category := .recycling\n  time := 15/4\n}\n| .mediumElectricPole => {\n  name := \"medium-electric-pole\",\n  inputs := [(4, .ironStick), (2, .steelPlate), (2, .copperCable)],\n  outputs := [(1, .mediumElectricPole)],\n  category := .electronics\n  time := 1/2\n}\n| .mediumElectricPoleRecycling => {\n  name := \"medium-electric-pole-recycling\",\n  inputs := [(1, .mediumElectricPole)],\n  outputs := [(1, .ironStick), (1/2, .steelPlate), (1/2, .copperCable)],\n  category := .recycling\n  time := 1/32\n}\n| .metallicAsteroidChunkRecycling => {\n  name := \"metallic-asteroid-chunk-recycling\",\n  inputs := [(1, .metallicAsteroidChunk)],\n  outputs := [(1/4, .metallicAsteroidChunk)],\n  category := .recycling\n  time := 1/32\n}\n| .metallicAsteroidCrushing => {\n  name := \"metallic-asteroid-crushing\",\n  inputs := [(1, .metallicAsteroidChunk)],\n  outputs := [(20, .ironOre), (1/5, .metallicAsteroidChunk)],\n  category := .crushing\n  time := 2\n}\n| .metallicAsteroidReprocessing => {\n  name := \"metallic-asteroid-reprocessing\",\n  inputs := [(1, .metallicAsteroidChunk)],\n  outputs := [(2/5, .metallicAsteroidChunk), (1/5, .carbonicAsteroidChunk), (1/5, .oxideAsteroidChunk)],\n  category := .crushing\n  time := 2\n}\n| .metallurgicSciencePack => {\n  name := \"metallurgic-science-pack\",\n  inputs := [(200, .moltenCopper), (3, .tungstenCarbide), (2, .tungstenPlate)],\n  outputs := [(1, .metallurgicSciencePack)],\n  category := .metallurgy\n  time := 10\n}\n| .metallurgicSciencePackRecycling => {\n  name := \"metallurgic-science-pack-recycling\",\n  inputs := [(1, .metallurgicSciencePack)],\n  outputs := [(1/4, .metallurgicSciencePack)],\n  category := .recycling\n  time := 5/8\n}\n| .militarySciencePack => {\n  name := \"military-science-pack\",\n  inputs := [(1, .piercingRoundsMagazine), (1, .grenade), (2, .stoneWall)],\n  outputs := [(2, .militarySciencePack)],\n  category := .crafting\n  time := 10\n}\n| .militarySciencePackRecycling => {\n  name := \"military-science-pack-recycling\",\n  inputs := [(1, .militarySciencePack)],\n  outputs := [(1/4, .militarySciencePack)],\n  category := .recycling\n  time := 5/8\n}\n| .modularArmor => {\n  name := \"modular-armor\",\n  inputs := [(30, .advancedCircuit), (50, .steelPlate)],\n  outputs := [(1, .modularArmor)],\n  category := .crafting\n  time := 15\n}\n| .modularArmorRecycling => {\n  name := \"modular-armor-recycling\",\n  inputs := [(1, .modularArmor)],\n  outputs := [(25/2, .steelPlate), (15/2, .advancedCircuit)],\n  category := .recycling\n  time := 15/16\n}\n| .moltenCopper => {\n  name := \"molten-copper\",\n  inputs := [(50, .copperOre), (1, .calcite)],\n  outputs := [(500, .moltenCopper)],\n  category := .metallurgy\n  time := 32\n}\n| .moltenCopperFromLava => {\n  name := \"molten-copper-from-lava\",\n  inputs := [(500, .lava), (1, .calcite)],\n  outputs := [(15, .stone), (250, .moltenCopper)],\n  category := .metallurgy\n  time := 16\n}\n| .moltenIron => {\n  name := \"molten-iron\",\n  inputs := [(50, .ironOre), (1, .calcite)],\n  outputs := [(500, .moltenIron)],\n  category := .metallurgy\n  time := 32\n}\n| .moltenIronFromLava => {\n  name := \"molten-iron-from-lava\",\n  inputs := [(500, .lava), (1, .calcite)],\n  outputs := [(10, .stone), (250, .moltenIron)],\n  category := .metallurgy\n  time := 16\n}\n| .nightVisionEquipment => {\n  name := \"night-vision-equipment\",\n  inputs := [(5, .advancedCircuit), (10, .steelPlate)],\n  outputs := [(1, .nightVisionEquipment)],\n  category := .crafting\n  time := 10\n}\n| .nightVisionEquipmentRecycling => {\n  name := \"night-vision-equipment-recycling\",\n  inputs := [(1, .nightVisionEquipment)],\n  outputs := [(5/2, .steelPlate), (5/4, .advancedCircuit)],\n  category := .recycling\n  time := 5/8\n}\n| .nuclearFuel => {\n  name := \"nuclear-fuel\",\n  inputs := [(1, .uranium235), (1, .rocketFuel)],\n  outputs := [(1, .nuclearFuel)],\n  category := .centrifuging\n  time := 90\n}\n| .nuclearFuelRecycling => {\n  name := \"nuclear-fuel-recycling\",\n  inputs := [(1, .nuclearFuel)],\n  outputs := [(1/4, .uranium235), (1/4, .rocketFuel)],\n  category := .recycling\n  time := 45/8\n}\n| .nuclearFuelReprocessing => {\n  name := \"nuclear-fuel-reprocessing\",\n  inputs := [(5, .depletedUraniumFuelCell)],\n  outputs := [(3, .uranium238)],\n  category := .centrifuging\n  time := 60\n}\n| .nuclearReactor => {\n  name := \"nuclear-reactor\",\n  inputs := [(500, .concrete), (500, .steelPlate), (500, .advancedCircuit), (500, .copperPlate)],\n  outputs := [(1, .nuclearReactor)],\n  category := .crafting\n  time := 8\n}\n| .nuclearReactorRecycling => {\n  name := \"nuclear-reactor-recycling\",\n  inputs := [(1, .nuclearReactor)],\n  outputs := [(125, .concrete), (125, .steelPlate), (125, .advancedCircuit), (125, .copperPlate)],\n  category := .recycling\n  time := 1/2\n}\n| .nutrientsFromBioflux => {\n  name := \"nutrients-from-bioflux\",\n  inputs := [(5, .bioflux)],\n  outputs := [(40, .nutrients)],\n  category := .organic\n  time := 2\n}\n| .nutrientsFromBiterEgg => {\n  name := \"nutrients-from-biter-egg\",\n  inputs := [(1, .biterEgg)],\n  outputs := [(20, .nutrients)],\n  category := .organicOrAssembling\n  time := 2\n}\n| .nutrientsFromFish => {\n  name := \"nutrients-from-fish\",\n  inputs := [(1, .rawFish)],\n  outputs := [(20, .nutrients)],\n  category := .organicOrAssembling\n  time := 2\n}\n| .nutrientsFromSpoilage => {\n  name := \"nutrients-from-spoilage\",\n  inputs := [(10, .spoilage)],\n  outputs := [(1, .nutrients)],\n  category := .organicOrAssembling\n  time := 2\n}\n| .nutrientsFromYumakoMash => {\n  name := \"nutrients-from-yumako-mash\",\n  inputs := [(4, .yumakoMash)],\n  outputs := [(6, .nutrients)],\n  category := .organic\n  time := 4\n}\n| .nutrientsRecycling => {\n  name := \"nutrients-recycling\",\n  inputs := [(1, .nutrients)],\n  outputs := [(5/2, .spoilage)],\n  category := .recycling\n  time := 1/8\n}\n| .offshorePump => {\n  name := \"offshore-pump\",\n  inputs := [(3, .pipe), (2, .ironGearWheel)],\n  outputs := [(1, .offshorePump)],\n  category := .crafting\n  time := 1/2\n}\n| .offshorePumpRecycling => {\n  name := \"offshore-pump-recycling\",\n  inputs := [(1, .offshorePump)],\n  outputs := [(3/4, .pipe), (1/2, .ironGearWheel)],\n  category := .recycling\n  time := 1/32\n}\n| .oilRefinery => {\n  name := \"oil-refinery\",\n  inputs := [(15, .steelPlate), (10, .ironGearWheel), (10, .stoneBrick), (10, .electronicCircuit), (10, .pipe)],\n  outputs := [(1, .oilRefinery)],\n  category := .crafting\n  time := 8\n}\n| .oilRefineryRecycling => {\n  name := \"oil-refinery-recycling\",\n  inputs := [(1, .oilRefinery)],\n  outputs := [(15/4, .steelPlate), (5/2, .ironGearWheel), (5/2, .stoneBrick), (5/2, .electronicCircuit), (5/2, .pipe)],\n  category := .recycling\n  time := 1/2\n}\n| .oneWayValveRecycling => {\n  name := \"one-way-valve-recycling\",\n  inputs := [(1, .oneWayValve)],\n  outputs := [(1/4, .oneWayValve)],\n  category := .recycling\n  time := 1/32\n}\n| .overflowValveRecycling => {\n  name := \"overflow-valve-recycling\",\n  inputs := [(1, .overflowValve)],\n  outputs := [(1/4, .overflowValve)],\n  category := .recycling\n  time := 1/32\n}\n| .overgrowthJellynutSoil => {\n  name := \"overgrowth-jellynut-soil\",\n  inputs := [(100, .water), (2, .artificialJellynutSoil), (5, .jellynutSeed), (10, .biterEgg), (50, .spoilage)],\n  outputs := [(1, .overgrowthJellynutSoil)],\n  category := .craftingWithFluid\n  time := 10\n}\n| .overgrowthJellynutSoilRecycling => {\n  name := \"overgrowth-jellynut-soil-recycling\",\n  inputs := [(1, .overgrowthJellynutSoil)],\n  outputs := [(25/2, .spoilage), (5/2, .biterEgg), (5/4, .jellynutSeed), (1/2, .artificialJellynutSoil)],\n  category := .recycling\n  time := 5/8\n}\n| .overgrowthYumakoSoil => {\n  name := \"overgrowth-yumako-soil\",\n  inputs := [(100, .water), (2, .artificialYumakoSoil), (5, .yumakoSeed), (10, .biterEgg), (50, .spoilage)],\n  outputs := [(1, .overgrowthYumakoSoil)],\n  category := .craftingWithFluid\n  time := 10\n}\n| .overgrowthYumakoSoilRecycling => {\n  name := \"overgrowth-yumako-soil-recycling\",\n  inputs := [(1, .overgrowthYumakoSoil)],\n  outputs := [(25/2, .spoilage), (5/2, .biterEgg), (5/4, .yumakoSeed), (1/2, .artificialYumakoSoil)],\n  category := .recycling\n  time := 5/8\n}\n| .oxideAsteroidChunkRecycling => {\n  name := \"oxide-asteroid-chunk-recycling\",\n  inputs := [(1, .oxideAsteroidChunk)],\n  outputs := [(1/4, .oxideAsteroidChunk)],\n  category := .recycling\n  time := 1/32\n}\n| .oxideAsteroidCrushing => {\n  name := \"oxide-asteroid-crushing\",\n  inputs := [(1, .oxideAsteroidChunk)],\n  outputs := [(5, .ice), (1/5, .oxideAsteroidChunk)],\n  category := .crushing\n  time := 2\n}\n| .oxideAsteroidReprocessing => {\n  name := \"oxide-asteroid-reprocessing\",\n  inputs := [(1, .oxideAsteroidChunk)],\n  outputs := [(2/5, .oxideAsteroidChunk), (1/5, .metallicAsteroidChunk), (1/5, .carbonicAsteroidChunk)],\n  category := .crushing\n  time := 1\n}\n| .parameter0 => {\n  name := \"parameter-0\",\n  inputs := [],\n  outputs := [],\n  category := .parameters\n  time := 1/2\n}\n| .parameter1 => {\n  name := \"parameter-1\",\n  inputs := [],\n  outputs := [],\n  category := .parameters\n  time := 1/2\n}\n| .parameter2 => {\n  name := \"parameter-2\",\n  inputs := [],\n  outputs := [],\n  category := .parameters\n  time := 1/2\n}\n| .parameter3 => {\n  name := \"parameter-3\",\n  inputs := [],\n  outputs := [],\n  category := .parameters\n  time := 1/2\n}\n| .parameter4 => {\n  name := \"parameter-4\",\n  inputs := [],\n  outputs := [],\n  category := .parameters\n  time := 1/2\n}\n| .parameter5 => {\n  name := \"parameter-5\",\n  inputs := [],\n  outputs := [],\n  category := .parameters\n  time := 1/2\n}\n| .parameter6 => {\n  name := \"parameter-6\",\n  inputs := [],\n  outputs := [],\n  category := .parameters\n  time := 1/2\n}\n| .parameter7 => {\n  name := \"parameter-7\",\n  inputs := [],\n  outputs := [],\n  category := .parameters\n  time := 1/2\n}\n| .parameter8 => {\n  name := \"parameter-8\",\n  inputs := [],\n  outputs := [],\n  category := .parameters\n  time := 1/2\n}\n| .parameter9 => {\n  name := \"parameter-9\",\n  inputs := [],\n  outputs := [],\n  category := .parameters\n  time := 1/2\n}\n| .passiveProviderChest => {\n  name := \"passive-provider-chest\",\n  inputs := [(1, .steelChest), (3, .electronicCircuit), (1, .advancedCircuit)],\n  outputs := [(1, .passiveProviderChest)],\n  category := .crafting\n  time := 1/2\n}\n| .passiveProviderChestRecycling => {\n  name := \"passive-provider-chest-recycling\",\n  inputs := [(1, .passiveProviderChest)],\n  outputs := [(3/4, .electronicCircuit), (1/4, .steelChest), (1/4, .advancedCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .pentapodEgg => {\n  name := \"pentapod-egg\",\n  inputs := [(60, .water), (1, .pentapodEgg), (30, .nutrients)],\n  outputs := [(2, .pentapodEgg)],\n  category := .organic\n  time := 15\n}\n| .pentapodEggRecycling => {\n  name := \"pentapod-egg-recycling\",\n  inputs := [(1, .pentapodEgg)],\n  outputs := [(1/4, .pentapodEgg)],\n  category := .recycling\n  time := 15/16\n}\n| .personalLaserDefenseEquipment => {\n  name := \"personal-laser-defense-equipment\",\n  inputs := [(20, .processingUnit), (5, .lowDensityStructure), (5, .laserTurret)],\n  outputs := [(1, .personalLaserDefenseEquipment)],\n  category := .crafting\n  time := 10\n}\n| .personalLaserDefenseEquipmentRecycling => {\n  name := \"personal-laser-defense-equipment-recycling\",\n  inputs := [(1, .personalLaserDefenseEquipment)],\n  outputs := [(5, .processingUnit), (5/4, .lowDensityStructure), (5/4, .laserTurret)],\n  category := .recycling\n  time := 5/8\n}\n| .personalRoboportEquipment => {\n  name := \"personal-roboport-equipment\",\n  inputs := [(10, .advancedCircuit), (40, .ironGearWheel), (20, .steelPlate), (45, .battery)],\n  outputs := [(1, .personalRoboportEquipment)],\n  category := .crafting\n  time := 10\n}\n| .personalRoboportEquipmentRecycling => {\n  name := \"personal-roboport-equipment-recycling\",\n  inputs := [(1, .personalRoboportEquipment)],\n  outputs := [(45/4, .battery), (10, .ironGearWheel), (5, .steelPlate), (5/2, .advancedCircuit)],\n  category := .recycling\n  time := 5/8\n}\n| .personalRoboportMk2Equipment => {\n  name := \"personal-roboport-mk2-equipment\",\n  inputs := [(5, .personalRoboportEquipment), (50, .processingUnit), (50, .superconductor)],\n  outputs := [(1, .personalRoboportMk2Equipment)],\n  category := .crafting\n  time := 20\n}\n| .personalRoboportMk2EquipmentRecycling => {\n  name := \"personal-roboport-mk2-equipment-recycling\",\n  inputs := [(1, .personalRoboportMk2Equipment)],\n  outputs := [(25/2, .processingUnit), (25/2, .superconductor), (5/4, .personalRoboportEquipment)],\n  category := .recycling\n  time := 5/4\n}\n| .petroleumGasBarrel => {\n  name := \"petroleum-gas-barrel\",\n  inputs := [(50, .petroleumGas), (1, .barrel)],\n  outputs := [(1, .petroleumGasBarrel)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .petroleumGasBarrelRecycling => {\n  name := \"petroleum-gas-barrel-recycling\",\n  inputs := [(1, .petroleumGasBarrel)],\n  outputs := [(1/4, .barrel)],\n  category := .recycling\n  time := 1/80\n}\n| .piercingRoundsMagazine => {\n  name := \"piercing-rounds-magazine\",\n  inputs := [(2, .firearmMagazine), (1, .steelPlate), (2, .copperPlate)],\n  outputs := [(2, .piercingRoundsMagazine)],\n  category := .crafting\n  time := 6\n}\n| .piercingRoundsMagazineRecycling => {\n  name := \"piercing-rounds-magazine-recycling\",\n  inputs := [(1, .piercingRoundsMagazine)],\n  outputs := [(1/4, .firearmMagazine), (1/4, .copperPlate), (1/8, .steelPlate)],\n  category := .recycling\n  time := 3/8\n}\n| .piercingShotgunShell => {\n  name := \"piercing-shotgun-shell\",\n  inputs := [(2, .shotgunShell), (5, .copperPlate), (2, .steelPlate)],\n  outputs := [(1, .piercingShotgunShell)],\n  category := .crafting\n  time := 8\n}\n| .piercingShotgunShellRecycling => {\n  name := \"piercing-shotgun-shell-recycling\",\n  inputs := [(1, .piercingShotgunShell)],\n  outputs := [(5/4, .copperPlate), (1/2, .shotgunShell), (1/2, .steelPlate)],\n  category := .recycling\n  time := 1/2\n}\n| .pipe => {\n  name := \"pipe\",\n  inputs := [(1, .ironPlate)],\n  outputs := [(1, .pipe)],\n  category := .crafting\n  time := 1/2\n}\n| .pipeRecycling => {\n  name := \"pipe-recycling\",\n  inputs := [(1, .pipe)],\n  outputs := [(1/4, .ironPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .pipeToGround => {\n  name := \"pipe-to-ground\",\n  inputs := [(10, .pipe), (5, .ironPlate)],\n  outputs := [(2, .pipeToGround)],\n  category := .crafting\n  time := 1/2\n}\n| .pipeToGroundRecycling => {\n  name := \"pipe-to-ground-recycling\",\n  inputs := [(1, .pipeToGround)],\n  outputs := [(5/4, .pipe), (5/8, .ironPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .pistol => {\n  name := \"pistol\",\n  inputs := [(5, .copperPlate), (5, .ironPlate)],\n  outputs := [(1, .pistol)],\n  category := .crafting\n  time := 5\n}\n| .pistolRecycling => {\n  name := \"pistol-recycling\",\n  inputs := [(1, .pistol)],\n  outputs := [(5/4, .copperPlate), (5/4, .ironPlate)],\n  category := .recycling\n  time := 5/16\n}\n| .plasticBar => {\n  name := \"plastic-bar\",\n  inputs := [(20, .petroleumGas), (1, .coal)],\n  outputs := [(2, .plasticBar)],\n  category := .chemistryOrCryogenics\n  time := 1\n}\n| .plasticBarRecycling => {\n  name := \"plastic-bar-recycling\",\n  inputs := [(1, .plasticBar)],\n  outputs := [(1/4, .plasticBar)],\n  category := .recycling\n  time := 1/16\n}\n| .poisonCapsule => {\n  name := \"poison-capsule\",\n  inputs := [(3, .steelPlate), (3, .electronicCircuit), (10, .coal)],\n  outputs := [(1, .poisonCapsule)],\n  category := .crafting\n  time := 8\n}\n| .poisonCapsuleRecycling => {\n  name := \"poison-capsule-recycling\",\n  inputs := [(1, .poisonCapsule)],\n  outputs := [(5/2, .coal), (3/4, .steelPlate), (3/4, .electronicCircuit)],\n  category := .recycling\n  time := 1/2\n}\n| .powerArmor => {\n  name := \"power-armor\",\n  inputs := [(40, .processingUnit), (20, .electricEngineUnit), (40, .steelPlate)],\n  outputs := [(1, .powerArmor)],\n  category := .crafting\n  time := 20\n}\n| .powerArmorMk2 => {\n  name := \"power-armor-mk2\",\n  inputs := [(100, .efficiencyModule), (100, .speedModule), (60, .processingUnit), (40, .electricEngineUnit), (30, .lowDensityStructure)],\n  outputs := [(1, .powerArmorMk2)],\n  category := .crafting\n  time := 25\n}\n| .powerArmorMk2Recycling => {\n  name := \"power-armor-mk2-recycling\",\n  inputs := [(1, .powerArmorMk2)],\n  outputs := [(25, .efficiencyModule), (25, .speedModule), (15, .processingUnit), (10, .electricEngineUnit), (15/2, .lowDensityStructure)],\n  category := .recycling\n  time := 25/16\n}\n| .powerArmorRecycling => {\n  name := \"power-armor-recycling\",\n  inputs := [(1, .powerArmor)],\n  outputs := [(10, .processingUnit), (10, .steelPlate), (5, .electricEngineUnit)],\n  category := .recycling\n  time := 5/4\n}\n| .powerSwitch => {\n  name := \"power-switch\",\n  inputs := [(5, .ironPlate), (5, .copperCable), (2, .electronicCircuit)],\n  outputs := [(1, .powerSwitch)],\n  category := .crafting\n  time := 2\n}\n| .powerSwitchRecycling => {\n  name := \"power-switch-recycling\",\n  inputs := [(1, .powerSwitch)],\n  outputs := [(5/4, .ironPlate), (5/4, .copperCable), (1/2, .electronicCircuit)],\n  category := .recycling\n  time := 1/8\n}\n| .processingUnit => {\n  name := \"processing-unit\",\n  inputs := [(5, .sulfuricAcid), (20, .electronicCircuit), (2, .advancedCircuit)],\n  outputs := [(1, .processingUnit)],\n  category := .electronicsWithFluid\n  time := 10\n}\n| .processingUnitRecycling => {\n  name := \"processing-unit-recycling\",\n  inputs := [(1, .processingUnit)],\n  outputs := [(5, .electronicCircuit), (1/2, .advancedCircuit)],\n  category := .recycling\n  time := 5/8\n}\n| .productionSciencePack => {\n  name := \"production-science-pack\",\n  inputs := [(1, .electricFurnace), (1, .productivityModule), (30, .rail)],\n  outputs := [(3, .productionSciencePack)],\n  category := .crafting\n  time := 21\n}\n| .productionSciencePackRecycling => {\n  name := \"production-science-pack-recycling\",\n  inputs := [(1, .productionSciencePack)],\n  outputs := [(1/4, .productionSciencePack)],\n  category := .recycling\n  time := 21/16\n}\n| .productivityModule => {\n  name := \"productivity-module\",\n  inputs := [(5, .advancedCircuit), (5, .electronicCircuit)],\n  outputs := [(1, .productivityModule)],\n  category := .electronics\n  time := 15\n}\n| .productivityModule2 => {\n  name := \"productivity-module-2\",\n  inputs := [(4, .productivityModule), (5, .advancedCircuit), (5, .processingUnit)],\n  outputs := [(1, .productivityModule2)],\n  category := .electronics\n  time := 30\n}\n| .productivityModule2Recycling => {\n  name := \"productivity-module-2-recycling\",\n  inputs := [(1, .productivityModule2)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .processingUnit), (1, .productivityModule)],\n  category := .recycling\n  time := 15/8\n}\n| .productivityModule3 => {\n  name := \"productivity-module-3\",\n  inputs := [(4, .productivityModule2), (5, .advancedCircuit), (5, .processingUnit), (1, .biterEgg)],\n  outputs := [(1, .productivityModule3)],\n  category := .electronics\n  time := 60\n}\n| .productivityModule3Recycling => {\n  name := \"productivity-module-3-recycling\",\n  inputs := [(1, .productivityModule3)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .processingUnit), (1, .productivityModule2), (1/4, .biterEgg)],\n  category := .recycling\n  time := 15/4\n}\n| .productivityModuleRecycling => {\n  name := \"productivity-module-recycling\",\n  inputs := [(1, .productivityModule)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .electronicCircuit)],\n  category := .recycling\n  time := 15/16\n}\n| .programmableSpeaker => {\n  name := \"programmable-speaker\",\n  inputs := [(3, .ironPlate), (4, .ironStick), (5, .copperCable), (4, .electronicCircuit)],\n  outputs := [(1, .programmableSpeaker)],\n  category := .crafting\n  time := 2\n}\n| .programmableSpeakerRecycling => {\n  name := \"programmable-speaker-recycling\",\n  inputs := [(1, .programmableSpeaker)],\n  outputs := [(5/4, .copperCable), (1, .ironStick), (1, .electronicCircuit), (3/4, .ironPlate)],\n  category := .recycling\n  time := 1/8\n}\n| .promethiumAsteroidChunkRecycling => {\n  name := \"promethium-asteroid-chunk-recycling\",\n  inputs := [(1, .promethiumAsteroidChunk)],\n  outputs := [(1/4, .promethiumAsteroidChunk)],\n  category := .recycling\n  time := 1/32\n}\n| .promethiumSciencePack => {\n  name := \"promethium-science-pack\",\n  inputs := [(25, .promethiumAsteroidChunk), (1, .quantumProcessor), (10, .biterEgg)],\n  outputs := [(10, .promethiumSciencePack)],\n  category := .cryogenics\n  time := 5\n}\n| .promethiumSciencePackRecycling => {\n  name := \"promethium-science-pack-recycling\",\n  inputs := [(1, .promethiumSciencePack)],\n  outputs := [(1/4, .promethiumSciencePack)],\n  category := .recycling\n  time := 5/16\n}\n| .proxyContainerRecycling => {\n  name := \"proxy-container-recycling\",\n  inputs := [(1, .proxyContainer)],\n  outputs := [(1/4, .proxyContainer)],\n  category := .recycling\n  time := 1/32\n}\n| .pump => {\n  name := \"pump\",\n  inputs := [(1, .engineUnit), (1, .steelPlate), (1, .pipe)],\n  outputs := [(1, .pump)],\n  category := .crafting\n  time := 2\n}\n| .pumpRecycling => {\n  name := \"pump-recycling\",\n  inputs := [(1, .pump)],\n  outputs := [(1/4, .engineUnit), (1/4, .steelPlate), (1/4, .pipe)],\n  category := .recycling\n  time := 1/8\n}\n| .pumpjack => {\n  name := \"pumpjack\",\n  inputs := [(5, .steelPlate), (10, .ironGearWheel), (5, .electronicCircuit), (10, .pipe)],\n  outputs := [(1, .pumpjack)],\n  category := .crafting\n  time := 5\n}\n| .pumpjackRecycling => {\n  name := \"pumpjack-recycling\",\n  inputs := [(1, .pumpjack)],\n  outputs := [(5/2, .ironGearWheel), (5/2, .pipe), (5/4, .steelPlate), (5/4, .electronicCircuit)],\n  category := .recycling\n  time := 5/16\n}\n| .qualityModule => {\n  name := \"quality-module\",\n  inputs := [(5, .electronicCircuit), (5, .advancedCircuit)],\n  outputs := [(1, .qualityModule)],\n  category := .electronics\n  time := 15\n}\n| .qualityModule2 => {\n  name := \"quality-module-2\",\n  inputs := [(4, .qualityModule), (5, .advancedCircuit), (5, .processingUnit)],\n  outputs := [(1, .qualityModule2)],\n  category := .electronics\n  time := 30\n}\n| .qualityModule2Recycling => {\n  name := \"quality-module-2-recycling\",\n  inputs := [(1, .qualityModule2)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .processingUnit), (1, .qualityModule)],\n  category := .recycling\n  time := 15/8\n}\n| .qualityModule3 => {\n  name := \"quality-module-3\",\n  inputs := [(4, .qualityModule2), (5, .advancedCircuit), (5, .processingUnit), (1, .superconductor)],\n  outputs := [(1, .qualityModule3)],\n  category := .electronics\n  time := 60\n}\n| .qualityModule3Recycling => {\n  name := \"quality-module-3-recycling\",\n  inputs := [(1, .qualityModule3)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .processingUnit), (1, .qualityModule2), (1/4, .superconductor)],\n  category := .recycling\n  time := 15/4\n}\n| .qualityModuleRecycling => {\n  name := \"quality-module-recycling\",\n  inputs := [(1, .qualityModule)],\n  outputs := [(5/4, .electronicCircuit), (5/4, .advancedCircuit)],\n  category := .recycling\n  time := 15/16\n}\n| .quantumProcessor => {\n  name := \"quantum-processor\",\n  inputs := [(10, .fluoroketoneCold), (1, .tungstenCarbide), (1, .processingUnit), (1, .superconductor), (1, .carbonFiber), (2, .lithiumPlate)],\n  outputs := [(1, .quantumProcessor), (5, .fluoroketoneHot)],\n  category := .electromagnetics\n  time := 30\n}\n| .quantumProcessorRecycling => {\n  name := \"quantum-processor-recycling\",\n  inputs := [(1, .quantumProcessor)],\n  outputs := [(1/2, .lithiumPlate), (1/4, .tungstenCarbide), (1/4, .processingUnit), (1/4, .superconductor), (1/4, .carbonFiber)],\n  category := .recycling\n  time := 15/8\n}\n| .radar => {\n  name := \"radar\",\n  inputs := [(5, .electronicCircuit), (5, .ironGearWheel), (10, .ironPlate)],\n  outputs := [(1, .radar)],\n  category := .crafting\n  time := 1/2\n}\n| .radarRecycling => {\n  name := \"radar-recycling\",\n  inputs := [(1, .radar)],\n  outputs := [(5/2, .ironPlate), (5/4, .electronicCircuit), (5/4, .ironGearWheel)],\n  category := .recycling\n  time := 1/32\n}\n| .rail => {\n  name := \"rail\",\n  inputs := [(1, .stone), (1, .ironStick), (1, .steelPlate)],\n  outputs := [(2, .rail)],\n  category := .crafting\n  time := 1/2\n}\n| .railChainSignal => {\n  name := \"rail-chain-signal\",\n  inputs := [(1, .electronicCircuit), (5, .ironPlate)],\n  outputs := [(1, .railChainSignal)],\n  category := .crafting\n  time := 1/2\n}\n| .railChainSignalRecycling => {\n  name := \"rail-chain-signal-recycling\",\n  inputs := [(1, .railChainSignal)],\n  outputs := [(5/4, .ironPlate), (1/4, .electronicCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .railRamp => {\n  name := \"rail-ramp\",\n  inputs := [(100, .refinedConcrete), (8, .rail), (10, .steelPlate)],\n  outputs := [(1, .railRamp)],\n  category := .crafting\n  time := 1/2\n}\n| .railRampRecycling => {\n  name := \"rail-ramp-recycling\",\n  inputs := [(1, .railRamp)],\n  outputs := [(25, .refinedConcrete), (5/2, .steelPlate), (2, .rail)],\n  category := .recycling\n  time := 1/32\n}\n| .railRecycling => {\n  name := \"rail-recycling\",\n  inputs := [(1, .rail)],\n  outputs := [(1/8, .stone), (1/8, .ironStick), (1/8, .steelPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .railSignal => {\n  name := \"rail-signal\",\n  inputs := [(1, .electronicCircuit), (5, .ironPlate)],\n  outputs := [(1, .railSignal)],\n  category := .crafting\n  time := 1/2\n}\n| .railSignalRecycling => {\n  name := \"rail-signal-recycling\",\n  inputs := [(1, .railSignal)],\n  outputs := [(5/4, .ironPlate), (1/4, .electronicCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .railSupport => {\n  name := \"rail-support\",\n  inputs := [(20, .refinedConcrete), (10, .steelPlate)],\n  outputs := [(1, .railSupport)],\n  category := .crafting\n  time := 1/2\n}\n| .railSupportRecycling => {\n  name := \"rail-support-recycling\",\n  inputs := [(1, .railSupport)],\n  outputs := [(5, .refinedConcrete), (5/2, .steelPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .railgun => {\n  name := \"railgun\",\n  inputs := [(10, .fluoroketoneCold), (10, .tungstenPlate), (10, .superconductor), (20, .quantumProcessor)],\n  outputs := [(1, .railgun)],\n  category := .cryogenics\n  time := 10\n}\n| .railgunAmmo => {\n  name := \"railgun-ammo\",\n  inputs := [(5, .steelPlate), (10, .copperCable), (2, .explosives)],\n  outputs := [(1, .railgunAmmo)],\n  category := .crafting\n  time := 25\n}\n| .railgunAmmoRecycling => {\n  name := \"railgun-ammo-recycling\",\n  inputs := [(1, .railgunAmmo)],\n  outputs := [(5/2, .copperCable), (5/4, .steelPlate), (1/2, .explosives)],\n  category := .recycling\n  time := 25/16\n}\n| .railgunRecycling => {\n  name := \"railgun-recycling\",\n  inputs := [(1, .railgun)],\n  outputs := [(5, .quantumProcessor), (5/2, .tungstenPlate), (5/2, .superconductor)],\n  category := .recycling\n  time := 5/8\n}\n| .railgunTurret => {\n  name := \"railgun-turret\",\n  inputs := [(100, .fluoroketoneCold), (100, .quantumProcessor), (30, .tungstenPlate), (50, .superconductor), (20, .carbonFiber)],\n  outputs := [(1, .railgunTurret)],\n  category := .cryogenics\n  time := 10\n}\n| .railgunTurretRecycling => {\n  name := \"railgun-turret-recycling\",\n  inputs := [(1, .railgunTurret)],\n  outputs := [(25, .quantumProcessor), (25/2, .superconductor), (15/2, .tungstenPlate), (5, .carbonFiber)],\n  category := .recycling\n  time := 5/8\n}\n| .rawFishRecycling => {\n  name := \"raw-fish-recycling\",\n  inputs := [(1, .rawFish)],\n  outputs := [(1/4, .rawFish)],\n  category := .recycling\n  time := 1/32\n}\n| .recycler => {\n  name := \"recycler\",\n  inputs := [(6, .processingUnit), (20, .steelPlate), (40, .ironGearWheel), (20, .concrete)],\n  outputs := [(1, .recycler)],\n  category := .crafting\n  time := 3\n}\n| .recyclerRecycling => {\n  name := \"recycler-recycling\",\n  inputs := [(1, .recycler)],\n  outputs := [(10, .ironGearWheel), (5, .steelPlate), (5, .concrete), (3/2, .processingUnit)],\n  category := .recycling\n  time := 3/16\n}\n| .refinedConcrete => {\n  name := \"refined-concrete\",\n  inputs := [(100, .water), (20, .concrete), (8, .ironStick), (1, .steelPlate)],\n  outputs := [(10, .refinedConcrete)],\n  category := .craftingWithFluid\n  time := 15\n}\n| .refinedConcreteRecycling => {\n  name := \"refined-concrete-recycling\",\n  inputs := [(1, .refinedConcrete)],\n  outputs := [(1/2, .concrete), (1/5, .ironStick), (1/40, .steelPlate)],\n  category := .recycling\n  time := 15/16\n}\n| .refinedHazardConcrete => {\n  name := \"refined-hazard-concrete\",\n  inputs := [(10, .refinedConcrete)],\n  outputs := [(10, .refinedHazardConcrete)],\n  category := .crafting\n  time := 1/4\n}\n| .refinedHazardConcreteRecycling => {\n  name := \"refined-hazard-concrete-recycling\",\n  inputs := [(1, .refinedHazardConcrete)],\n  outputs := [(1/4, .refinedConcrete)],\n  category := .recycling\n  time := 1/64\n}\n| .repairPack => {\n  name := \"repair-pack\",\n  inputs := [(2, .electronicCircuit), (2, .ironGearWheel)],\n  outputs := [(1, .repairPack)],\n  category := .crafting\n  time := 1/2\n}\n| .repairPackRecycling => {\n  name := \"repair-pack-recycling\",\n  inputs := [(1, .repairPack)],\n  outputs := [(1/2, .electronicCircuit), (1/2, .ironGearWheel)],\n  category := .recycling\n  time := 1/32\n}\n| .requesterChest => {\n  name := \"requester-chest\",\n  inputs := [(1, .steelChest), (3, .electronicCircuit), (1, .advancedCircuit)],\n  outputs := [(1, .requesterChest)],\n  category := .crafting\n  time := 1/2\n}\n| .requesterChestRecycling => {\n  name := \"requester-chest-recycling\",\n  inputs := [(1, .requesterChest)],\n  outputs := [(3/4, .electronicCircuit), (1/4, .steelChest), (1/4, .advancedCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .roboport => {\n  name := \"roboport\",\n  inputs := [(45, .steelPlate), (45, .ironGearWheel), (45, .advancedCircuit)],\n  outputs := [(1, .roboport)],\n  category := .crafting\n  time := 5\n}\n| .roboportRecycling => {\n  name := \"roboport-recycling\",\n  inputs := [(1, .roboport)],\n  outputs := [(45/4, .steelPlate), (45/4, .ironGearWheel), (45/4, .advancedCircuit)],\n  category := .recycling\n  time := 5/16\n}\n| .rocket => {\n  name := \"rocket\",\n  inputs := [(1, .explosives), (2, .ironPlate)],\n  outputs := [(1, .rocket)],\n  category := .crafting\n  time := 4\n}\n| .rocketFuel => {\n  name := \"rocket-fuel\",\n  inputs := [(10, .lightOil), (10, .solidFuel)],\n  outputs := [(1, .rocketFuel)],\n  category := .organicOrAssembling\n  time := 15\n}\n| .rocketFuelFromJelly => {\n  name := \"rocket-fuel-from-jelly\",\n  inputs := [(30, .water), (30, .jelly), (2, .bioflux)],\n  outputs := [(1, .rocketFuel)],\n  category := .organic\n  time := 10\n}\n| .rocketFuelRecycling => {\n  name := \"rocket-fuel-recycling\",\n  inputs := [(1, .rocketFuel)],\n  outputs := [(5/2, .solidFuel)],\n  category := .recycling\n  time := 15/16\n}\n| .rocketLauncher => {\n  name := \"rocket-launcher\",\n  inputs := [(5, .ironPlate), (5, .ironGearWheel), (5, .electronicCircuit)],\n  outputs := [(1, .rocketLauncher)],\n  category := .crafting\n  time := 10\n}\n| .rocketLauncherRecycling => {\n  name := \"rocket-launcher-recycling\",\n  inputs := [(1, .rocketLauncher)],\n  outputs := [(5/4, .ironPlate), (5/4, .ironGearWheel), (5/4, .electronicCircuit)],\n  category := .recycling\n  time := 5/8\n}\n| .rocketPart => {\n  name := \"rocket-part\",\n  inputs := [(1, .processingUnit), (1, .lowDensityStructure), (1, .rocketFuel)],\n  outputs := [],\n  category := .rocketBuilding\n  time := 3\n}\n| .rocketRecycling => {\n  name := \"rocket-recycling\",\n  inputs := [(1, .rocket)],\n  outputs := [(1/2, .ironPlate), (1/4, .explosives)],\n  category := .recycling\n  time := 1/4\n}\n| .rocketSilo => {\n  name := \"rocket-silo\",\n  inputs := [(1000, .steelPlate), (1000, .concrete), (100, .pipe), (200, .processingUnit), (200, .electricEngineUnit)],\n  outputs := [(1, .rocketSilo)],\n  category := .crafting\n  time := 30\n}\n| .rocketSiloRecycling => {\n  name := \"rocket-silo-recycling\",\n  inputs := [(1, .rocketSilo)],\n  outputs := [(250, .steelPlate), (250, .concrete), (50, .processingUnit), (50, .electricEngineUnit), (25, .pipe)],\n  category := .recycling\n  time := 15/8\n}\n| .rocketTurret => {\n  name := \"rocket-turret\",\n  inputs := [(4, .rocketLauncher), (4, .processingUnit), (20, .carbonFiber), (20, .steelPlate), (20, .ironGearWheel)],\n  outputs := [(1, .rocketTurret)],\n  category := .crafting\n  time := 10\n}\n| .rocketTurretRecycling => {\n  name := \"rocket-turret-recycling\",\n  inputs := [(1, .rocketTurret)],\n  outputs := [(5, .carbonFiber), (5, .steelPlate), (5, .ironGearWheel), (1, .rocketLauncher), (1, .processingUnit)],\n  category := .recycling\n  time := 5/8\n}\n| .scienceRecycling => {\n  name := \"science-recycling\",\n  inputs := [(1, .science)],\n  outputs := [(1/4, .science)],\n  category := .recycling\n  time := 1/32\n}\n| .scrapRecycling => {\n  name := \"scrap-recycling\",\n  inputs := [(1, .scrap)],\n  outputs := [(1/5, .ironGearWheel), (7/100, .solidFuel), (3/50, .concrete), (1/20, .ice), (1/25, .steelPlate), (1/25, .battery), (1/25, .stone), (3/100, .advancedCircuit), (3/100, .copperCable), (1/50, .processingUnit), (1/100, .lowDensityStructure), (1/100, .holmiumOre)],\n  category := .recyclingOrHandCrafting\n  time := 1/5\n}\n| .selectionToolRecycling => {\n  name := \"selection-tool-recycling\",\n  inputs := [(1, .selectionTool)],\n  outputs := [(1/4, .selectionTool)],\n  category := .recycling\n  time := 1/32\n}\n| .selectorCombinator => {\n  name := \"selector-combinator\",\n  inputs := [(2, .advancedCircuit), (5, .deciderCombinator)],\n  outputs := [(1, .selectorCombinator)],\n  category := .crafting\n  time := 1/2\n}\n| .selectorCombinatorRecycling => {\n  name := \"selector-combinator-recycling\",\n  inputs := [(1, .selectorCombinator)],\n  outputs := [(5/4, .deciderCombinator), (1/2, .advancedCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .shotgun => {\n  name := \"shotgun\",\n  inputs := [(15, .ironPlate), (5, .ironGearWheel), (10, .copperPlate), (5, .wood)],\n  outputs := [(1, .shotgun)],\n  category := .crafting\n  time := 10\n}\n| .shotgunRecycling => {\n  name := \"shotgun-recycling\",\n  inputs := [(1, .shotgun)],\n  outputs := [(15/4, .ironPlate), (5/2, .copperPlate), (5/4, .ironGearWheel), (5/4, .wood)],\n  category := .recycling\n  time := 5/8\n}\n| .shotgunShell => {\n  name := \"shotgun-shell\",\n  inputs := [(2, .copperPlate), (2, .ironPlate)],\n  outputs := [(1, .shotgunShell)],\n  category := .crafting\n  time := 3\n}\n| .shotgunShellRecycling => {\n  name := \"shotgun-shell-recycling\",\n  inputs := [(1, .shotgunShell)],\n  outputs := [(1/2, .copperPlate), (1/2, .ironPlate)],\n  category := .recycling\n  time := 3/16\n}\n| .simpleCoalLiquefaction => {\n  name := \"simple-coal-liquefaction\",\n  inputs := [(25, .sulfuricAcid), (10, .coal), (2, .calcite)],\n  outputs := [(50, .heavyOil)],\n  category := .oilProcessing\n  time := 5\n}\n| .simpleEntityWithForceRecycling => {\n  name := \"simple-entity-with-force-recycling\",\n  inputs := [(1, .simpleEntityWithForce)],\n  outputs := [(1/4, .simpleEntityWithForce)],\n  category := .recycling\n  time := 1/32\n}\n| .simpleEntityWithOwnerRecycling => {\n  name := \"simple-entity-with-owner-recycling\",\n  inputs := [(1, .simpleEntityWithOwner)],\n  outputs := [(1/4, .simpleEntityWithOwner)],\n  category := .recycling\n  time := 1/32\n}\n| .slowdownCapsule => {\n  name := \"slowdown-capsule\",\n  inputs := [(2, .steelPlate), (2, .electronicCircuit), (5, .coal)],\n  outputs := [(1, .slowdownCapsule)],\n  category := .crafting\n  time := 8\n}\n| .slowdownCapsuleRecycling => {\n  name := \"slowdown-capsule-recycling\",\n  inputs := [(1, .slowdownCapsule)],\n  outputs := [(5/4, .coal), (1/2, .steelPlate), (1/2, .electronicCircuit)],\n  category := .recycling\n  time := 1/2\n}\n| .smallElectricPole => {\n  name := \"small-electric-pole\",\n  inputs := [(1, .wood), (2, .copperCable)],\n  outputs := [(2, .smallElectricPole)],\n  category := .electronics\n  time := 1/2\n}\n| .smallElectricPoleRecycling => {\n  name := \"small-electric-pole-recycling\",\n  inputs := [(1, .smallElectricPole)],\n  outputs := [(1/4, .copperCable), (1/8, .wood)],\n  category := .recycling\n  time := 1/32\n}\n| .smallLamp => {\n  name := \"small-lamp\",\n  inputs := [(1, .electronicCircuit), (3, .copperCable), (1, .ironPlate)],\n  outputs := [(1, .smallLamp)],\n  category := .crafting\n  time := 1/2\n}\n| .smallLampRecycling => {\n  name := \"small-lamp-recycling\",\n  inputs := [(1, .smallLamp)],\n  outputs := [(3/4, .copperCable), (1/4, .electronicCircuit), (1/4, .ironPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .solarPanel => {\n  name := \"solar-panel\",\n  inputs := [(5, .steelPlate), (15, .electronicCircuit), (5, .copperPlate)],\n  outputs := [(1, .solarPanel)],\n  category := .electronics\n  time := 10\n}\n| .solarPanelEquipment => {\n  name := \"solar-panel-equipment\",\n  inputs := [(1, .solarPanel), (2, .advancedCircuit), (5, .steelPlate)],\n  outputs := [(1, .solarPanelEquipment)],\n  category := .crafting\n  time := 10\n}\n| .solarPanelEquipmentRecycling => {\n  name := \"solar-panel-equipment-recycling\",\n  inputs := [(1, .solarPanelEquipment)],\n  outputs := [(5/4, .steelPlate), (1/2, .advancedCircuit), (1/4, .solarPanel)],\n  category := .recycling\n  time := 5/8\n}\n| .solarPanelRecycling => {\n  name := \"solar-panel-recycling\",\n  inputs := [(1, .solarPanel)],\n  outputs := [(15/4, .electronicCircuit), (5/4, .steelPlate), (5/4, .copperPlate)],\n  category := .recycling\n  time := 5/8\n}\n| .solidFuelFromAmmonia => {\n  name := \"solid-fuel-from-ammonia\",\n  inputs := [(15, .ammonia), (6, .crudeOil)],\n  outputs := [(1, .solidFuel)],\n  category := .chemistryOrCryogenics\n  time := 1/2\n}\n| .solidFuelFromHeavyOil => {\n  name := \"solid-fuel-from-heavy-oil\",\n  inputs := [(20, .heavyOil)],\n  outputs := [(1, .solidFuel)],\n  category := .chemistry\n  time := 1\n}\n| .solidFuelFromLightOil => {\n  name := \"solid-fuel-from-light-oil\",\n  inputs := [(10, .lightOil)],\n  outputs := [(1, .solidFuel)],\n  category := .chemistry\n  time := 1\n}\n| .solidFuelFromPetroleumGas => {\n  name := \"solid-fuel-from-petroleum-gas\",\n  inputs := [(20, .petroleumGas)],\n  outputs := [(1, .solidFuel)],\n  category := .chemistry\n  time := 1\n}\n| .solidFuelRecycling => {\n  name := \"solid-fuel-recycling\",\n  inputs := [(1, .solidFuel)],\n  outputs := [(1/4, .solidFuel)],\n  category := .recycling\n  time := 1/32\n}\n| .spacePlatformFoundation => {\n  name := \"space-platform-foundation\",\n  inputs := [(20, .steelPlate), (20, .copperCable)],\n  outputs := [(1, .spacePlatformFoundation)],\n  category := .crafting\n  time := 10\n}\n| .spacePlatformFoundationRecycling => {\n  name := \"space-platform-foundation-recycling\",\n  inputs := [(1, .spacePlatformFoundation)],\n  outputs := [(5, .steelPlate), (5, .copperCable)],\n  category := .recycling\n  time := 5/8\n}\n| .spacePlatformHubRecycling => {\n  name := \"space-platform-hub-recycling\",\n  inputs := [(1, .spacePlatformHub)],\n  outputs := [(1/4, .spacePlatformHub)],\n  category := .recycling\n  time := 1/32\n}\n| .spacePlatformStarterPack => {\n  name := \"space-platform-starter-pack\",\n  inputs := [(60, .spacePlatformFoundation), (20, .steelPlate), (20, .processingUnit)],\n  outputs := [(1, .spacePlatformStarterPack)],\n  category := .crafting\n  time := 60\n}\n| .spacePlatformStarterPackRecycling => {\n  name := \"space-platform-starter-pack-recycling\",\n  inputs := [(1, .spacePlatformStarterPack)],\n  outputs := [(15, .spacePlatformFoundation), (5, .steelPlate), (5, .processingUnit)],\n  category := .recycling\n  time := 15/4\n}\n| .spaceSciencePack => {\n  name := \"space-science-pack\",\n  inputs := [(2, .ironPlate), (1, .carbon), (1, .ice)],\n  outputs := [(5, .spaceSciencePack)],\n  category := .crafting\n  time := 15\n}\n| .spaceSciencePackRecycling => {\n  name := \"space-science-pack-recycling\",\n  inputs := [(1, .spaceSciencePack)],\n  outputs := [(1/4, .spaceSciencePack)],\n  category := .recycling\n  time := 15/16\n}\n| .speedModule => {\n  name := \"speed-module\",\n  inputs := [(5, .advancedCircuit), (5, .electronicCircuit)],\n  outputs := [(1, .speedModule)],\n  category := .electronics\n  time := 15\n}\n| .speedModule2 => {\n  name := \"speed-module-2\",\n  inputs := [(4, .speedModule), (5, .advancedCircuit), (5, .processingUnit)],\n  outputs := [(1, .speedModule2)],\n  category := .electronics\n  time := 30\n}\n| .speedModule2Recycling => {\n  name := \"speed-module-2-recycling\",\n  inputs := [(1, .speedModule2)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .processingUnit), (1, .speedModule)],\n  category := .recycling\n  time := 15/8\n}\n| .speedModule3 => {\n  name := \"speed-module-3\",\n  inputs := [(4, .speedModule2), (5, .advancedCircuit), (5, .processingUnit), (1, .tungstenCarbide)],\n  outputs := [(1, .speedModule3)],\n  category := .electronics\n  time := 60\n}\n| .speedModule3Recycling => {\n  name := \"speed-module-3-recycling\",\n  inputs := [(1, .speedModule3)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .processingUnit), (1, .speedModule2), (1/4, .tungstenCarbide)],\n  category := .recycling\n  time := 15/4\n}\n| .speedModuleRecycling => {\n  name := \"speed-module-recycling\",\n  inputs := [(1, .speedModule)],\n  outputs := [(5/4, .advancedCircuit), (5/4, .electronicCircuit)],\n  category := .recycling\n  time := 15/16\n}\n| .spidertron => {\n  name := \"spidertron\",\n  inputs := [(4, .exoskeletonEquipment), (2, .fissionReactorEquipment), (1, .rocketTurret), (2, .radar), (1, .rawFish)],\n  outputs := [(1, .spidertron)],\n  category := .crafting\n  time := 10\n}\n| .spidertronRecycling => {\n  name := \"spidertron-recycling\",\n  inputs := [(1, .spidertron)],\n  outputs := [(1, .exoskeletonEquipment), (1/2, .fissionReactorEquipment), (1/2, .radar), (1/4, .rocketTurret), (1/4, .rawFish)],\n  category := .recycling\n  time := 5/8\n}\n| .splitter => {\n  name := \"splitter\",\n  inputs := [(5, .electronicCircuit), (5, .ironPlate), (4, .transportBelt)],\n  outputs := [(1, .splitter)],\n  category := .pressing\n  time := 1\n}\n| .splitterRecycling => {\n  name := \"splitter-recycling\",\n  inputs := [(1, .splitter)],\n  outputs := [(5/4, .electronicCircuit), (5/4, .ironPlate), (1, .transportBelt)],\n  category := .recycling\n  time := 1/16\n}\n| .spoilageRecycling => {\n  name := \"spoilage-recycling\",\n  inputs := [(1, .spoilage)],\n  outputs := [(1/4, .spoilage)],\n  category := .recycling\n  time := 1/32\n}\n| .stackInserter => {\n  name := \"stack-inserter\",\n  inputs := [(1, .bulkInserter), (1, .processingUnit), (2, .carbonFiber), (10, .jelly)],\n  outputs := [(1, .stackInserter)],\n  category := .crafting\n  time := 1/2\n}\n| .stackInserterRecycling => {\n  name := \"stack-inserter-recycling\",\n  inputs := [(1, .stackInserter)],\n  outputs := [(5/2, .jelly), (1/2, .carbonFiber), (1/4, .bulkInserter), (1/4, .processingUnit)],\n  category := .recycling\n  time := 1/32\n}\n| .steamCondensation => {\n  name := \"steam-condensation\",\n  inputs := [(1000, .steam)],\n  outputs := [(90, .water)],\n  category := .chemistryOrCryogenics\n  time := 1\n}\n| .steamEngine => {\n  name := \"steam-engine\",\n  inputs := [(8, .ironGearWheel), (5, .pipe), (10, .ironPlate)],\n  outputs := [(1, .steamEngine)],\n  category := .crafting\n  time := 1/2\n}\n| .steamEngineRecycling => {\n  name := \"steam-engine-recycling\",\n  inputs := [(1, .steamEngine)],\n  outputs := [(5/2, .ironPlate), (2, .ironGearWheel), (5/4, .pipe)],\n  category := .recycling\n  time := 1/32\n}\n| .steamTurbine => {\n  name := \"steam-turbine\",\n  inputs := [(50, .ironGearWheel), (50, .copperPlate), (20, .pipe)],\n  outputs := [(1, .steamTurbine)],\n  category := .crafting\n  time := 3\n}\n| .steamTurbineRecycling => {\n  name := \"steam-turbine-recycling\",\n  inputs := [(1, .steamTurbine)],\n  outputs := [(25/2, .ironGearWheel), (25/2, .copperPlate), (5, .pipe)],\n  category := .recycling\n  time := 3/16\n}\n| .steelChest => {\n  name := \"steel-chest\",\n  inputs := [(8, .steelPlate)],\n  outputs := [(1, .steelChest)],\n  category := .crafting\n  time := 1/2\n}\n| .steelChestRecycling => {\n  name := \"steel-chest-recycling\",\n  inputs := [(1, .steelChest)],\n  outputs := [(2, .steelPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .steelFurnace => {\n  name := \"steel-furnace\",\n  inputs := [(6, .steelPlate), (10, .stoneBrick)],\n  outputs := [(1, .steelFurnace)],\n  category := .crafting\n  time := 3\n}\n| .steelFurnaceRecycling => {\n  name := \"steel-furnace-recycling\",\n  inputs := [(1, .steelFurnace)],\n  outputs := [(5/2, .stoneBrick), (3/2, .steelPlate)],\n  category := .recycling\n  time := 3/16\n}\n| .steelPlate => {\n  name := \"steel-plate\",\n  inputs := [(5, .ironPlate)],\n  outputs := [(1, .steelPlate)],\n  category := .smelting\n  time := 16\n}\n| .steelPlateRecycling => {\n  name := \"steel-plate-recycling\",\n  inputs := [(1, .steelPlate)],\n  outputs := [(1/4, .steelPlate)],\n  category := .recycling\n  time := 1\n}\n| .stoneBrick => {\n  name := \"stone-brick\",\n  inputs := [(2, .stone)],\n  outputs := [(1, .stoneBrick)],\n  category := .smelting\n  time := 16/5\n}\n| .stoneBrickRecycling => {\n  name := \"stone-brick-recycling\",\n  inputs := [(1, .stoneBrick)],\n  outputs := [(1/4, .stoneBrick)],\n  category := .recycling\n  time := 1/5\n}\n| .stoneFurnace => {\n  name := \"stone-furnace\",\n  inputs := [(5, .stone)],\n  outputs := [(1, .stoneFurnace)],\n  category := .crafting\n  time := 1/2\n}\n| .stoneFurnaceRecycling => {\n  name := \"stone-furnace-recycling\",\n  inputs := [(1, .stoneFurnace)],\n  outputs := [(5/4, .stone)],\n  category := .recycling\n  time := 1/32\n}\n| .stoneRecycling => {\n  name := \"stone-recycling\",\n  inputs := [(1, .stone)],\n  outputs := [(1/4, .stone)],\n  category := .recycling\n  time := 1/32\n}\n| .stoneWall => {\n  name := \"stone-wall\",\n  inputs := [(5, .stoneBrick)],\n  outputs := [(1, .stoneWall)],\n  category := .crafting\n  time := 1/2\n}\n| .stoneWallRecycling => {\n  name := \"stone-wall-recycling\",\n  inputs := [(1, .stoneWall)],\n  outputs := [(5/4, .stoneBrick)],\n  category := .recycling\n  time := 1/32\n}\n| .storageChest => {\n  name := \"storage-chest\",\n  inputs := [(1, .steelChest), (3, .electronicCircuit), (1, .advancedCircuit)],\n  outputs := [(1, .storageChest)],\n  category := .crafting\n  time := 1/2\n}\n| .storageChestRecycling => {\n  name := \"storage-chest-recycling\",\n  inputs := [(1, .storageChest)],\n  outputs := [(3/4, .electronicCircuit), (1/4, .steelChest), (1/4, .advancedCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .storageTank => {\n  name := \"storage-tank\",\n  inputs := [(20, .ironPlate), (5, .steelPlate)],\n  outputs := [(1, .storageTank)],\n  category := .crafting\n  time := 3\n}\n| .storageTankRecycling => {\n  name := \"storage-tank-recycling\",\n  inputs := [(1, .storageTank)],\n  outputs := [(5, .ironPlate), (5/4, .steelPlate)],\n  category := .recycling\n  time := 3/16\n}\n| .submachineGun => {\n  name := \"submachine-gun\",\n  inputs := [(10, .ironGearWheel), (5, .copperPlate), (10, .ironPlate)],\n  outputs := [(1, .submachineGun)],\n  category := .crafting\n  time := 10\n}\n| .submachineGunRecycling => {\n  name := \"submachine-gun-recycling\",\n  inputs := [(1, .submachineGun)],\n  outputs := [(5/2, .ironGearWheel), (5/2, .ironPlate), (5/4, .copperPlate)],\n  category := .recycling\n  time := 5/8\n}\n| .substation => {\n  name := \"substation\",\n  inputs := [(10, .steelPlate), (5, .advancedCircuit), (6, .copperCable)],\n  outputs := [(1, .substation)],\n  category := .electronics\n  time := 1/2\n}\n| .substationRecycling => {\n  name := \"substation-recycling\",\n  inputs := [(1, .substation)],\n  outputs := [(5/2, .steelPlate), (3/2, .copperCable), (5/4, .advancedCircuit)],\n  category := .recycling\n  time := 1/32\n}\n| .sulfur => {\n  name := \"sulfur\",\n  inputs := [(30, .water), (30, .petroleumGas)],\n  outputs := [(2, .sulfur)],\n  category := .chemistryOrCryogenics\n  time := 1\n}\n| .sulfurRecycling => {\n  name := \"sulfur-recycling\",\n  inputs := [(1, .sulfur)],\n  outputs := [(1/4, .sulfur)],\n  category := .recycling\n  time := 1/16\n}\n| .sulfuricAcid => {\n  name := \"sulfuric-acid\",\n  inputs := [(100, .water), (5, .sulfur), (1, .ironPlate)],\n  outputs := [(50, .sulfuricAcid)],\n  category := .chemistryOrCryogenics\n  time := 1\n}\n| .sulfuricAcidBarrel => {\n  name := \"sulfuric-acid-barrel\",\n  inputs := [(50, .sulfuricAcid), (1, .barrel)],\n  outputs := [(1, .sulfuricAcidBarrel)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .sulfuricAcidBarrelRecycling => {\n  name := \"sulfuric-acid-barrel-recycling\",\n  inputs := [(1, .sulfuricAcidBarrel)],\n  outputs := [(1/4, .barrel)],\n  category := .recycling\n  time := 1/80\n}\n| .supercapacitor => {\n  name := \"supercapacitor\",\n  inputs := [(10, .electrolyte), (2, .holmiumPlate), (2, .superconductor), (4, .electronicCircuit), (1, .battery)],\n  outputs := [(1, .supercapacitor)],\n  category := .electromagnetics\n  time := 10\n}\n| .supercapacitorRecycling => {\n  name := \"supercapacitor-recycling\",\n  inputs := [(1, .supercapacitor)],\n  outputs := [(1, .electronicCircuit), (1/2, .holmiumPlate), (1/2, .superconductor), (1/4, .battery)],\n  category := .recycling\n  time := 5/8\n}\n| .superconductor => {\n  name := \"superconductor\",\n  inputs := [(5, .lightOil), (1, .holmiumPlate), (1, .copperPlate), (1, .plasticBar)],\n  outputs := [(2, .superconductor)],\n  category := .electromagnetics\n  time := 5\n}\n| .superconductorRecycling => {\n  name := \"superconductor-recycling\",\n  inputs := [(1, .superconductor)],\n  outputs := [(1/4, .superconductor)],\n  category := .recycling\n  time := 5/16\n}\n| .tank => {\n  name := \"tank\",\n  inputs := [(32, .engineUnit), (50, .steelPlate), (15, .ironGearWheel), (10, .advancedCircuit)],\n  outputs := [(1, .tank)],\n  category := .crafting\n  time := 5\n}\n| .tankRecycling => {\n  name := \"tank-recycling\",\n  inputs := [(1, .tank)],\n  outputs := [(25/2, .steelPlate), (8, .engineUnit), (15/4, .ironGearWheel), (5/2, .advancedCircuit)],\n  category := .recycling\n  time := 5/16\n}\n| .teslaAmmo => {\n  name := \"tesla-ammo\",\n  inputs := [(10, .electrolyte), (1, .supercapacitor), (1, .plasticBar)],\n  outputs := [(1, .teslaAmmo)],\n  category := .electromagnetics\n  time := 30\n}\n| .teslaAmmoRecycling => {\n  name := \"tesla-ammo-recycling\",\n  inputs := [(1, .teslaAmmo)],\n  outputs := [(1/4, .supercapacitor), (1/4, .plasticBar)],\n  category := .recycling\n  time := 15/8\n}\n| .teslaTurret => {\n  name := \"tesla-turret\",\n  inputs := [(500, .electrolyte), (1, .teslagun), (10, .supercapacitor), (10, .processingUnit), (50, .superconductor)],\n  outputs := [(1, .teslaTurret)],\n  category := .electromagnetics\n  time := 30\n}\n| .teslaTurretRecycling => {\n  name := \"tesla-turret-recycling\",\n  inputs := [(1, .teslaTurret)],\n  outputs := [(25/2, .superconductor), (5/2, .supercapacitor), (5/2, .processingUnit), (1/4, .teslagun)],\n  category := .recycling\n  time := 15/8\n}\n| .teslagun => {\n  name := \"teslagun\",\n  inputs := [(100, .electrolyte), (10, .holmiumPlate), (10, .superconductor), (30, .plasticBar)],\n  outputs := [(1, .teslagun)],\n  category := .electromagnetics\n  time := 30\n}\n| .teslagunRecycling => {\n  name := \"teslagun-recycling\",\n  inputs := [(1, .teslagun)],\n  outputs := [(15/2, .plasticBar), (5/2, .holmiumPlate), (5/2, .superconductor)],\n  category := .recycling\n  time := 15/8\n}\n| .thruster => {\n  name := \"thruster\",\n  inputs := [(10, .steelPlate), (10, .processingUnit), (5, .electricEngineUnit)],\n  outputs := [(1, .thruster)],\n  category := .crafting\n  time := 10\n}\n| .thrusterFuel => {\n  name := \"thruster-fuel\",\n  inputs := [(10, .water), (2, .carbon)],\n  outputs := [(75, .thrusterFuel)],\n  category := .chemistry\n  time := 2\n}\n| .thrusterOxidizer => {\n  name := \"thruster-oxidizer\",\n  inputs := [(10, .water), (2, .ironOre)],\n  outputs := [(75, .thrusterOxidizer)],\n  category := .chemistry\n  time := 2\n}\n| .thrusterRecycling => {\n  name := \"thruster-recycling\",\n  inputs := [(1, .thruster)],\n  outputs := [(5/2, .steelPlate), (5/2, .processingUnit), (5/4, .electricEngineUnit)],\n  category := .recycling\n  time := 5/8\n}\n| .toolbeltEquipment => {\n  name := \"toolbelt-equipment\",\n  inputs := [(3, .advancedCircuit), (10, .carbonFiber)],\n  outputs := [(1, .toolbeltEquipment)],\n  category := .crafting\n  time := 10\n}\n| .toolbeltEquipmentRecycling => {\n  name := \"toolbelt-equipment-recycling\",\n  inputs := [(1, .toolbeltEquipment)],\n  outputs := [(5/2, .carbonFiber), (3/4, .advancedCircuit)],\n  category := .recycling\n  time := 5/8\n}\n| .topUpValveRecycling => {\n  name := \"top-up-valve-recycling\",\n  inputs := [(1, .topUpValve)],\n  outputs := [(1/4, .topUpValve)],\n  category := .recycling\n  time := 1/32\n}\n| .trainStop => {\n  name := \"train-stop\",\n  inputs := [(5, .electronicCircuit), (6, .ironPlate), (6, .ironStick), (3, .steelPlate)],\n  outputs := [(1, .trainStop)],\n  category := .crafting\n  time := 1/2\n}\n| .trainStopRecycling => {\n  name := \"train-stop-recycling\",\n  inputs := [(1, .trainStop)],\n  outputs := [(3/2, .ironPlate), (3/2, .ironStick), (5/4, .electronicCircuit), (3/4, .steelPlate)],\n  category := .recycling\n  time := 1/32\n}\n| .transportBelt => {\n  name := \"transport-belt\",\n  inputs := [(1, .ironPlate), (1, .ironGearWheel)],\n  outputs := [(2, .transportBelt)],\n  category := .pressing\n  time := 1/2\n}\n| .transportBeltRecycling => {\n  name := \"transport-belt-recycling\",\n  inputs := [(1, .transportBelt)],\n  outputs := [(1/8, .ironPlate), (1/8, .ironGearWheel)],\n  category := .recycling\n  time := 1/32\n}\n| .treeSeedRecycling => {\n  name := \"tree-seed-recycling\",\n  inputs := [(1, .treeSeed)],\n  outputs := [(1/4, .treeSeed)],\n  category := .recycling\n  time := 1/32\n}\n| .tungstenCarbide => {\n  name := \"tungsten-carbide\",\n  inputs := [(10, .sulfuricAcid), (2, .tungstenOre), (1, .carbon)],\n  outputs := [(1, .tungstenCarbide)],\n  category := .craftingWithFluid\n  time := 1\n}\n| .tungstenCarbideRecycling => {\n  name := \"tungsten-carbide-recycling\",\n  inputs := [(1, .tungstenCarbide)],\n  outputs := [(1/4, .tungstenCarbide)],\n  category := .recycling\n  time := 1/16\n}\n| .tungstenOreRecycling => {\n  name := \"tungsten-ore-recycling\",\n  inputs := [(1, .tungstenOre)],\n  outputs := [(1/4, .tungstenOre)],\n  category := .recycling\n  time := 1/32\n}\n| .tungstenPlate => {\n  name := \"tungsten-plate\",\n  inputs := [(10, .moltenIron), (4, .tungstenOre)],\n  outputs := [(1, .tungstenPlate)],\n  category := .metallurgy\n  time := 10\n}\n| .tungstenPlateRecycling => {\n  name := \"tungsten-plate-recycling\",\n  inputs := [(1, .tungstenPlate)],\n  outputs := [(1/4, .tungstenPlate)],\n  category := .recycling\n  time := 5/8\n}\n| .turboLoader => {\n  name := \"turbo-loader\",\n  inputs := [(5, .turboTransportBelt), (1, .expressLoader)],\n  outputs := [(1, .turboLoader)],\n  category := .crafting\n  time := 20\n}\n| .turboLoaderRecycling => {\n  name := \"turbo-loader-recycling\",\n  inputs := [(1, .turboLoader)],\n  outputs := [(5/4, .turboTransportBelt), (1/4, .expressLoader)],\n  category := .recycling\n  time := 5/4\n}\n| .turboSplitter => {\n  name := \"turbo-splitter\",\n  inputs := [(80, .lubricant), (1, .expressSplitter), (15, .tungstenPlate), (2, .processingUnit)],\n  outputs := [(1, .turboSplitter)],\n  category := .metallurgy\n  time := 2\n}\n| .turboSplitterRecycling => {\n  name := \"turbo-splitter-recycling\",\n  inputs := [(1, .turboSplitter)],\n  outputs := [(15/4, .tungstenPlate), (1/2, .processingUnit), (1/4, .expressSplitter)],\n  category := .recycling\n  time := 1/8\n}\n| .turboTransportBelt => {\n  name := \"turbo-transport-belt\",\n  inputs := [(20, .lubricant), (5, .tungstenPlate), (1, .expressTransportBelt)],\n  outputs := [(1, .turboTransportBelt)],\n  category := .metallurgy\n  time := 1/2\n}\n| .turboTransportBeltRecycling => {\n  name := \"turbo-transport-belt-recycling\",\n  inputs := [(1, .turboTransportBelt)],\n  outputs := [(5/4, .tungstenPlate), (1/4, .expressTransportBelt)],\n  category := .recycling\n  time := 1/32\n}\n| .turboUndergroundBelt => {\n  name := \"turbo-underground-belt\",\n  inputs := [(40, .lubricant), (40, .tungstenPlate), (2, .expressUndergroundBelt)],\n  outputs := [(2, .turboUndergroundBelt)],\n  category := .metallurgy\n  time := 2\n}\n| .turboUndergroundBeltRecycling => {\n  name := \"turbo-underground-belt-recycling\",\n  inputs := [(1, .turboUndergroundBelt)],\n  outputs := [(5, .tungstenPlate), (1/4, .expressUndergroundBelt)],\n  category := .recycling\n  time := 1/8\n}\n| .undergroundBelt => {\n  name := \"underground-belt\",\n  inputs := [(10, .ironPlate), (5, .transportBelt)],\n  outputs := [(2, .undergroundBelt)],\n  category := .pressing\n  time := 1\n}\n| .undergroundBeltRecycling => {\n  name := \"underground-belt-recycling\",\n  inputs := [(1, .undergroundBelt)],\n  outputs := [(5/4, .ironPlate), (5/8, .transportBelt)],\n  category := .recycling\n  time := 1/16\n}\n| .upgradePlannerRecycling => {\n  name := \"upgrade-planner-recycling\",\n  inputs := [(1, .upgradePlanner)],\n  outputs := [(1/4, .upgradePlanner)],\n  category := .recycling\n  time := 1/32\n}\n| .uranium235Recycling => {\n  name := \"uranium-235-recycling\",\n  inputs := [(1, .uranium235)],\n  outputs := [(1/4, .uranium235)],\n  category := .recycling\n  time := 1/32\n}\n| .uranium238Recycling => {\n  name := \"uranium-238-recycling\",\n  inputs := [(1, .uranium238)],\n  outputs := [(1/4, .uranium238)],\n  category := .recycling\n  time := 1/32\n}\n| .uraniumCannonShell => {\n  name := \"uranium-cannon-shell\",\n  inputs := [(1, .cannonShell), (1, .uranium238)],\n  outputs := [(1, .uraniumCannonShell)],\n  category := .crafting\n  time := 12\n}\n| .uraniumCannonShellRecycling => {\n  name := \"uranium-cannon-shell-recycling\",\n  inputs := [(1, .uraniumCannonShell)],\n  outputs := [(1/4, .cannonShell), (1/4, .uranium238)],\n  category := .recycling\n  time := 3/4\n}\n| .uraniumFuelCell => {\n  name := \"uranium-fuel-cell\",\n  inputs := [(10, .ironPlate), (1, .uranium235), (19, .uranium238)],\n  outputs := [(10, .uraniumFuelCell)],\n  category := .crafting\n  time := 10\n}\n| .uraniumFuelCellRecycling => {\n  name := \"uranium-fuel-cell-recycling\",\n  inputs := [(1, .uraniumFuelCell)],\n  outputs := [(1/4, .uraniumFuelCell)],\n  category := .recycling\n  time := 5/8\n}\n| .uraniumOreRecycling => {\n  name := \"uranium-ore-recycling\",\n  inputs := [(1, .uraniumOre)],\n  outputs := [(1/4, .uraniumOre)],\n  category := .recycling\n  time := 1/32\n}\n| .uraniumProcessing => {\n  name := \"uranium-processing\",\n  inputs := [(10, .uraniumOre)],\n  outputs := [(7/1000, .uranium235), (993/1000, .uranium238)],\n  category := .centrifuging\n  time := 12\n}\n| .uraniumRoundsMagazine => {\n  name := \"uranium-rounds-magazine\",\n  inputs := [(1, .piercingRoundsMagazine), (1, .uranium238)],\n  outputs := [(1, .uraniumRoundsMagazine)],\n  category := .crafting\n  time := 10\n}\n| .uraniumRoundsMagazineRecycling => {\n  name := \"uranium-rounds-magazine-recycling\",\n  inputs := [(1, .uraniumRoundsMagazine)],\n  outputs := [(1/4, .piercingRoundsMagazine), (1/4, .uranium238)],\n  category := .recycling\n  time := 5/8\n}\n| .utilitySciencePack => {\n  name := \"utility-science-pack\",\n  inputs := [(3, .lowDensityStructure), (2, .processingUnit), (1, .flyingRobotFrame)],\n  outputs := [(3, .utilitySciencePack)],\n  category := .crafting\n  time := 21\n}\n| .utilitySciencePackRecycling => {\n  name := \"utility-science-pack-recycling\",\n  inputs := [(1, .utilitySciencePack)],\n  outputs := [(1/4, .utilitySciencePack)],\n  category := .recycling\n  time := 21/16\n}\n| .waterBarrel => {\n  name := \"water-barrel\",\n  inputs := [(50, .water), (1, .barrel)],\n  outputs := [(1, .waterBarrel)],\n  category := .craftingWithFluid\n  time := 1/5\n}\n| .waterBarrelRecycling => {\n  name := \"water-barrel-recycling\",\n  inputs := [(1, .waterBarrel)],\n  outputs := [(1/4, .barrel)],\n  category := .recycling\n  time := 1/80\n}\n| .woodProcessing => {\n  name := \"wood-processing\",\n  inputs := [(2, .wood)],\n  outputs := [(1, .treeSeed)],\n  category := .organicOrAssembling\n  time := 2\n}\n| .woodRecycling => {\n  name := \"wood-recycling\",\n  inputs := [(1, .wood)],\n  outputs := [(1/4, .wood)],\n  category := .recycling\n  time := 1/32\n}\n| .woodenChest => {\n  name := \"wooden-chest\",\n  inputs := [(2, .wood)],\n  outputs := [(1, .woodenChest)],\n  category := .crafting\n  time := 1/2\n}\n| .woodenChestRecycling => {\n  name := \"wooden-chest-recycling\",\n  inputs := [(1, .woodenChest)],\n  outputs := [(1/2, .wood)],\n  category := .recycling\n  time := 1/32\n}\n| .yumakoMashRecycling => {\n  name := \"yumako-mash-recycling\",\n  inputs := [(1, .yumakoMash)],\n  outputs := [(1/4, .yumakoMash)],\n  category := .recycling\n  time := 1/32\n}\n| .yumakoProcessing => {\n  name := \"yumako-processing\",\n  inputs := [(1, .yumako)],\n  outputs := [(2, .yumakoMash), (1/50, .yumakoSeed)],\n  category := .organicOrHandCrafting\n  time := 1\n}\n| .yumakoRecycling => {\n  name := \"yumako-recycling\",\n  inputs := [(1, .yumako)],\n  outputs := [(1/4, .yumako)],\n  category := .recycling\n  time := 1/32\n}\n| .yumakoSeedRecycling => {\n  name := \"yumako-seed-recycling\",\n  inputs := [(1, .yumakoSeed)],\n  outputs := [(1/4, .yumakoSeed)],\n  category := .recycling\n  time := 1/32\n}\n\nend RecipeName\n\ninductive Fabricator\n  | assemblingMachine1\n  | assemblingMachine2\n  | assemblingMachine3\n  | biochamber\n  | captiveBiterSpawner\n  | centrifuge\n  | chemicalPlant\n  | crusher\n  | cryogenicPlant\n  | electricFurnace\n  | electromagneticPlant\n  | foundry\n  | oilRefinery\n  | recycler\n  | rocketSilo\n  | steelFurnace\n  | stoneFurnace\n  deriving DecidableEq, Repr, Inhabited\n\nnamespace Fabricator\n\n@[simp]\ndef details : Fabricator -> FabricatorDetails\n| .assemblingMachine1 => {\n  name := \"assembling-machine-1\"\n  speedup := 1/2\n  productivity := 0\n  moduleSlots := 0\n  width := 3\n  height := 3\n  fluidBoxes := [\n  ]\n  categories := [\n    .advancedCrafting,\n    .basicCrafting,\n    .crafting,\n    .electronics,\n    .pressing,\n  ]\n}\n| .assemblingMachine2 => {\n  name := \"assembling-machine-2\"\n  speedup := 3/4\n  productivity := 0\n  moduleSlots := 2\n  width := 3\n  height := 3\n  fluidBoxes := [\n    {\n      offset := 1\n      side := .N\n      type := .input\n    },\n    {\n      offset := 1\n      side := .S\n      type := .output\n    },\n  ]\n  categories := [\n    .advancedCrafting,\n    .basicCrafting,\n    .crafting,\n    .craftingWithFluid,\n    .craftingWithFluidOrMetallurgy,\n    .cryogenicsOrAssembling,\n    .electronics,\n    .electronicsOrAssembling,\n    .electronicsWithFluid,\n    .metallurgyOrAssembling,\n    .organicOrAssembling,\n    .organicOrHandCrafting,\n    .pressing,\n  ]\n}\n| .assemblingMachine3 => {\n  name := \"assembling-machine-3\"\n  speedup := 5/4\n  productivity := 0\n  moduleSlots := 4\n  width := 3\n  height := 3\n  fluidBoxes := [\n    {\n      offset := 1\n      side := .N\n      type := .input\n    },\n    {\n      offset := 1\n      side := .S\n      type := .output\n    },\n  ]\n  categories := [\n    .advancedCrafting,\n    .basicCrafting,\n    .crafting,\n    .craftingWithFluid,\n    .craftingWithFluidOrMetallurgy,\n    .cryogenicsOrAssembling,\n    .electronics,\n    .electronicsOrAssembling,\n    .electronicsWithFluid,\n    .metallurgyOrAssembling,\n    .organicOrAssembling,\n    .organicOrHandCrafting,\n    .pressing,\n  ]\n}\n| .biochamber => {\n  name := \"biochamber\"\n  speedup := 2\n  productivity := 1/2\n  moduleSlots := 4\n  width := 3\n  height := 3\n  fluidBoxes := [\n    {\n      offset := 0\n      side := .N\n      type := .input\n    },\n    {\n      offset := 2\n      side := .N\n      type := .input\n    },\n    {\n      offset := 2\n      side := .S\n      type := .output\n    },\n    {\n      offset := 0\n      side := .S\n      type := .output\n    },\n  ]\n  categories := [\n    .organic,\n    .organicOrAssembling,\n    .organicOrChemistry,\n    .organicOrHandCrafting,\n  ]\n}\n| .captiveBiterSpawner => {\n  name := \"captive-biter-spawner\"\n  speedup := 1\n  productivity := 0\n  moduleSlots := 0\n  width := 5\n  height := 5\n  fluidBoxes := [\n  ]\n  categories := [\n    .captiveSpawnerProcess,\n  ]\n}\n| .centrifuge => {\n  name := \"centrifuge\"\n  speedup := 1\n  productivity := 0\n  moduleSlots := 2\n  width := 3\n  height := 3\n  fluidBoxes := [\n  ]\n  categories := [\n    .centrifuging,\n  ]\n}\n| .chemicalPlant => {\n  name := \"chemical-plant\"\n  speedup := 1\n  productivity := 0\n  moduleSlots := 3\n  width := 3\n  height := 3\n  fluidBoxes := [\n    {\n      offset := 0\n      side := .N\n      type := .input\n    },\n    {\n      offset := 2\n      side := .N\n      type := .input\n    },\n    {\n      offset := 0\n      side := .S\n      type := .output\n    },\n    {\n      offset := 2\n      side := .S\n      type := .output\n    },\n  ]\n  categories := [\n    .chemistry,\n    .chemistryOrCryogenics,\n    .organicOrChemistry,\n  ]\n}\n| .crusher => {\n  name := \"crusher\"\n  speedup := 1\n  productivity := 0\n  moduleSlots := 2\n  width := 2\n  height := 3\n  fluidBoxes := [\n  ]\n  categories := [\n    .crushing,\n  ]\n}\n| .cryogenicPlant => {\n  name := \"cryogenic-plant\"\n  speedup := 2\n  productivity := 0\n  moduleSlots := 8\n  width := 5\n  height := 5\n  fluidBoxes := [\n    {\n      offset := 0\n      side := .S\n      type := .input\n    },\n    {\n      offset := 2\n      side := .S\n      type := .input\n    },\n    {\n      offset := 4\n      side := .S\n      type := .input\n    },\n    {\n      offset := 0\n      side := .N\n      type := .output\n    },\n    {\n      offset := 2\n      side := .N\n      type := .output\n    },\n    {\n      offset := 4\n      side := .N\n      type := .output\n    },\n  ]\n  categories := [\n    .chemistryOrCryogenics,\n    .cryogenics,\n    .cryogenicsOrAssembling,\n  ]\n}\n| .electricFurnace => {\n  name := \"electric-furnace\"\n  speedup := 2\n  productivity := 0\n  moduleSlots := 2\n  width := 3\n  height := 3\n  fluidBoxes := [\n  ]\n  categories := [\n    .smelting,\n  ]\n}\n| .electromagneticPlant => {\n  name := \"electromagnetic-plant\"\n  speedup := 2\n  productivity := 1/2\n  moduleSlots := 5\n  width := 4\n  height := 4\n  fluidBoxes := [\n    {\n      offset := 2\n      side := .W\n      type := .inputOutput\n    },\n    {\n      offset := 1\n      side := .E\n      type := .inputOutput\n    },\n    {\n      offset := 2\n      side := .S\n      type := .inputOutput\n    },\n    {\n      offset := 1\n      side := .N\n      type := .inputOutput\n    },\n  ]\n  categories := [\n    .electromagnetics,\n    .electronics,\n    .electronicsOrAssembling,\n    .electronicsWithFluid,\n  ]\n}\n| .foundry => {\n  name := \"foundry\"\n  speedup := 4\n  productivity := 1/2\n  moduleSlots := 4\n  width := 5\n  height := 5\n  fluidBoxes := [\n    {\n      offset := 1\n      side := .S\n      type := .input\n    },\n    {\n      offset := 3\n      side := .S\n      type := .input\n    },\n    {\n      offset := 1\n      side := .N\n      type := .output\n    },\n    {\n      offset := 3\n      side := .N\n      type := .output\n    },\n  ]\n  categories := [\n    .craftingWithFluidOrMetallurgy,\n    .metallurgy,\n    .metallurgyOrAssembling,\n    .pressing,\n  ]\n}\n| .oilRefinery => {\n  name := \"oil-refinery\"\n  speedup := 1\n  productivity := 0\n  moduleSlots := 3\n  width := 5\n  height := 5\n  fluidBoxes := [\n    {\n      offset := 1\n      side := .S\n      type := .input\n    },\n    {\n      offset := 3\n      side := .S\n      type := .input\n    },\n    {\n      offset := 0\n      side := .N\n      type := .output\n    },\n    {\n      offset := 2\n      side := .N\n      type := .output\n    },\n    {\n      offset := 4\n      side := .N\n      type := .output\n    },\n  ]\n  categories := [\n    .oilProcessing,\n  ]\n}\n| .recycler => {\n  name := \"recycler\"\n  speedup := 1/2\n  productivity := 0\n  moduleSlots := 4\n  width := 1\n  height := 3\n  fluidBoxes := [\n  ]\n  categories := [\n    .recycling,\n    .recyclingOrHandCrafting,\n  ]\n}\n| .rocketSilo => {\n  name := \"rocket-silo\"\n  speedup := 1\n  productivity := 0\n  moduleSlots := 4\n  width := 9\n  height := 9\n  fluidBoxes := [\n  ]\n  categories := [\n    .rocketBuilding,\n  ]\n}\n| .steelFurnace => {\n  name := \"steel-furnace\"\n  speedup := 2\n  productivity := 0\n  moduleSlots := 0\n  width := 1\n  height := 2\n  fluidBoxes := [\n  ]\n  categories := [\n    .smelting,\n  ]\n}\n| .stoneFurnace => {\n  name := \"stone-furnace\"\n  speedup := 1\n  productivity := 0\n  moduleSlots := 0\n  width := 1\n  height := 2\n  fluidBoxes := [\n  ]\n  categories := [\n    .smelting,\n  ]\n}\n\ndef name (f:Fabricator) := f.details.name\n\ndef width (f:Fabricator) := f.details.width\n\ndef height (f:Fabricator) := f.details.height\n\n@[simp]\ndef speedup (f:Fabricator) := f.details.speedup\n\n@[simp]\ndef productivity (f:Fabricator) := f.details.productivity\n\n@[simp]\ndef moduleSlots (f:Fabricator) := f.details.moduleSlots\n\ndef fluidBoxes (f:Fabricator) := f.details.fluidBoxes\n\ndef handlesCategory (f:Fabricator) (c:RecipeCategory) :=\n  f.details.categories.contains c\n\nend Fabricator\n"
  },
  {
    "path": "Functorio/Row.lean",
    "content": "import Functorio.Factory\nimport Functorio.Expand\nimport Functorio.Util\n\nprivate def rowPerfect {n e s w n' e' s'} (left : Factory n e s w) (right:Factory n' e' s' e): Factory (n ++ n') e' (s ++ s') w :=\n  if left.interface.e != right.interface.w then unimplemented! s!\"\n    Rows currently require that factory interfaces line up perfectly (no adapter is being generated yet).\n    Left offsets: {reprStr left.interface.e}\n    Right offsets: {reprStr right.interface.w}\n    Offending factories row(left,right); left := {left.name} right := {right.name}\" else\n  if left.height != right.height then impossible! \"Row factories don't have the same height after expanding their dimensions.\" else\n  let height := left.height\n\n  {\n    width := left.width + right.width,\n    height := height,\n    entities := left.entities ++ right.entities.map (Entity.offsetPosition left.width 0)\n    wires := left.wires ++ right.wires.map fun wire => wire.incrementLabels left.entities.length\n    interface := {\n      n := cast (by simp) (left.interface.n ++ increaseOffset left.width (right.interface.n))\n      e := right.interface.e\n      s := cast (by simp) (left.interface.s ++ increaseOffset left.width (right.interface.s))\n      w := left.interface.w\n    }\n    name := s!\"row({left.name},{right.name})\"\n  }\n\ndef row' {n e s w n' e' s'} (left : Factory n e s w) (right:Factory n' e' s' e): Factory (n ++ n') e' (s ++ s') w :=\n  -- Similar code in columnTwo\n\n  -- Align the two factories on their 0th interface (if possible),\n  -- then expand the smaller one to match the bigger one's size.\n  if (left.height > right.height) then\n    let diff := left.height - right.height\n    let shift := if left.interface.e.isEmpty then 0\n                 else min diff (left.interface.e[0]! - right.interface.w[0]!)\n    rowPerfect left ((right.expand .S (diff - shift)).expand .N shift)\n  else if (left.height < right.height) then\n    let diff := right.height - left.height\n    let shift := if left.interface.e.isEmpty then 0\n                 else min diff ((right.interface.w)[0]! - (left.interface.e)[0]!)\n    rowPerfect ((left.expand .S (diff - shift)).expand .N shift) right\n  else\n    rowPerfect left right\n\ndef row {n e s w n' e' s'} (left : Factory n e s w) (right:Factory n' e' s' e): Factory (n ++ n') e' (s ++ s') w :=\n  -- Align the two factories on their 0th interface,\n  -- then expand both interfaces to match their sizes.\n\n  let diff := Int.ofNat left.height - Int.ofNat right.height\n  let shift := if left.interface.e.isEmpty then Int.ofNat 0\n               else Int.ofNat left.interface.e[0]! -\n                    Int.ofNat right.interface.w[0]!\n  rowPerfect ((left.expand .S (shift - diff).toNat).expand .N (-shift).toNat)\n                ((right.expand .S (diff - shift).toNat).expand .N shift.toNat)\n\ndef row3 {n e s w n' e' s' n'' e'' s''} (left : Factory n e s w) (middle:Factory n' e' s' e) (right:Factory n'' e'' s'' e') : Factory (n ++ n' ++ n'') e'' (s ++ s' ++ s'') w :=\n  (row (row left middle) right)\n\ndef rowList {i} (fs:List (Factory [] i [] i)) : Factory [] i [] i:=\n  match fs with\n  | f::fs => fs.foldl row f\n  | [] => unimplemented! \"Cannot make a row from empty list of factories yet\"\n"
  },
  {
    "path": "Functorio/Test.lean",
    "content": "import Functorio.AssemblyLine\nimport Functorio.Blueprint\nimport Functorio.Column\nimport Functorio.Bus\nimport Functorio.Expand\nimport Functorio.Ascii\n\nnamespace Test\n\ninstance : Config where\n  generateBigPoles := false\n  generateRoboports := false\n  providerChestCapacity := 0\n  adapterMinHeight := 3\n\n#guard (bus do\n  let iron <- input .ironPlate 300\n  let copper <- input .copperPlate 15\n  let gear <- busAssemblyLine (recipe .ironGearWheel) 1 iron\n  let _ <- busAssemblyLine (recipe .automationSciencePack) 1 copper gear.less\n).toAscii == s!\"\n\n ↑⇨***⇨↓↑⇨***⇦↑↓\n ↑ *A* ↓↑ *A*↠↑↓\n ↑⚡***⚡↓↑⚡***⚡↑↓\n ↑ ↓←←←←↑  →→→↑↓\n ↑ ↓    ↑  ↑↓←←←\n ↑←↓    ↑← ↑↓\n  ↑↓     ↑ ↑↓\n>→↑→→→→→⇥↑↦↑→→→→>\n>→→→→→→→→↑\n\n\"\n\n#guard (bus do\n  let water <- input .water 11400\n  let petrol <- input .petroleumGas 5400\n  let iron <- input .ironPlate 60\n  let (water0, water1) <- split (left:=5400) water\n  let sulfur <- busAssemblyLine (recipe .sulfur) 3 water0 petrol\n  let _ <- busAssemblyLine (recipe .sulfuricAcid) 1 water1.exact sulfur.less iron\n).toAscii == s!\"\n\n  |┤|├***⇨↓\n  | | *C* ↓\n  | ||***⚡↓\n  |┤|├***⇨↓\n  | | *C* ↓\n  | ||***⚡↓\n  |┤|├***⇨↓ |┤↑├***┤↑├|\n  | | *C* ↓ | ↑⇨*C*⇦↑ |\n  | ||***⚡↓ | ↑⚡***⚡↑ |\n  | |↓←←←←← |→↑     ↑ |\n  | |↓      |↑ →→→→→↑ |\n  | |↓      |↑ ↑|||||||\n  | |↓      |↑ ↑|\n>||┤|↓├||||||↑ ↑||||||||>\n>→→⇥|↓↦→→→→→⇥↑↦↑\n>||||→→→→→→→→↑\n\n\"\n\n#guard (bus do\n  let copper <- input .copperPlate 300\n  let iron <- input .ironPlate 1050\n\n  let (iron0, rest) <- split (left:=600) iron\n  let (iron1, rest) <- split (left:=150) rest\n  let (iron2, rest) <- split (left:=150) rest\n  let iron3 : BusLane .ironPlate 150 := rest\n\n  let gear <- busAssemblyLine (recipe .ironGearWheel) 2 iron0\n  let (gear0, rest) <- split (left:=150) gear\n  let gear1 : BusLane .ironGearWheel 150 := rest.less\n\n  let belt <- busAssemblyLine (recipe .transportBelt) 1 iron1 gear0\n  let cable <- busAssemblyLine (recipe .copperCable) 2 copper\n  let circuit <- busAssemblyLine (recipe .electronicCircuit) 1 iron2 cable.less\n  let inserter <- busAssemblyLine (recipe .inserter) 1 circuit gear1 iron3\n\n  let _ <- busAssemblyLine (recipe .logisticSciencePack) 1 inserter.less belt.less\n).toAscii == s!\"\n\n ↑⇨***⇨↓        ↑⇨***⇨↓\n ↑ *A* ↓        ↑ *A* ↓\n ↑⚡***⚡↓        ↑⚡***⚡↓\n ↑⇨***⇨↓↑⇨***⇦↑↓↑⇨***⇨↓↑⇨***⇦↑↓↑↑⇨***⇦↑↓↑⇨***⇦↑↓\n ↑ *A* ↓↑ *A*↠↑↓↑ *A* ↓↑ *A*↠↑↓↑↑↠*A*↠↑↓↑ *A*↠↑↓\n ↑⚡***⚡↓↑⚡***⚡↑↓↑⚡***⚡↓↑⚡***⚡↑↓↑↑⚡***⚡↑↓↑⚡***⚡↑↓\n ↑  ↓←←←↑   →→↑↓↑ ↓←←←←↑   →→↑↓↑↑  →→→↑↓↑ →→→→↑↓\n ↑  ↓   ↑   ↑↓←←↑ ↓    ↑   ↑↓←←↑↑← ↑↓←←←↑ ↑↓←←←←\n ↑←←↓   ↑←← ↑↓  ↑←↓    ↑←← ↑↓  ↑←↑ ↑↓   ↑←↑↓\n   ↑↓     ↑ ↑↓   ↑↓      ↑ ↑↓   ↑↑ ↑↓    ↑↑↓\n>⇥*↑↓↦→→⇥*↑ ↑↓↦→→↑→→→→→⇥*↑↦↑→→→→↑↑ ↑→→→→→↑↑→→→→→>\n>→S⇥↓↦→→→S⇥*↑↓↦→→→→→→→→→S→→→→→→→⇥↑↦↑      ↑\n    →→→→→→→S⇥↓↦→→→→→→→→→→→→→→→→→→↑        ↑\n             →→→→→→→→→→→→→→→→→→→→→→→→→→→→→↑\n\n\"\n\nend Test\n"
  },
  {
    "path": "Functorio/Util.lean",
    "content": "\ndef error! {T} [Inhabited T] (msg:String) : T :=\n  dbg_trace (\"ERROR: \" ++ msg)\n  default\n\ndef unimplemented! {T} [Inhabited T] (msg:String) : T :=\n  dbg_trace (\"UNIMPLEMENTED: \" ++ msg ++  \" (please file a feature request)\")\n  default\n\ndef impossible! {T} [Inhabited T] (msg:String) : T :=\n  dbg_trace (\"UNREACHABLE: \" ++ msg ++ \" (please report a bug)\")\n  default\n\nnamespace List\n\ndef toVector {T} (l:List T) : Vector T (l.length) :=\n  l.toArray.toVector\n\ndef castToVector! {T} [Inhabited T] [Repr T] {n} (l:List T) : Vector T n :=\n  match decEq l.length n with\n  | isTrue _ => cast (by subst_eqs; simp) l.toVector\n  | isFalse _ => error! s!\"castToVector! failed for {reprStr l}\"\n\nend List\n\nnamespace Array\n\ndef toVector! {T} [Inhabited T] [Repr T] {n} (l:Array T) : Vector T n :=\n  match decEq l.size n with\n  | isTrue _ => cast (by subst_eqs; simp) l.toVector\n  | isFalse _ => error! s!\"toVector! failed for {reprStr l}\"\n\nend Array\n\nnamespace Vector\n\n@[inline] def modify {α n} (xs : Vector α n) (i : Nat) (f : α -> α) : Vector α n :=\n  ⟨xs.toArray.modify i f, by simp⟩\n\nend Vector\n"
  },
  {
    "path": "Functorio.lean",
    "content": "import Functorio.Abbreviations\nimport Functorio.Adapter\nimport Functorio.AdapterTest\nimport Functorio.Ascii\nimport Functorio.AssemblyStation\nimport Functorio.AssemblyStationTest\nimport Functorio.AssemblyLine\nimport Functorio.AssemblyLineTest\nimport Functorio.Blueprint\nimport Functorio.Bus\nimport Functorio.BusTest\nimport Functorio.Cap\nimport Functorio.Column\nimport Functorio.Config\nimport Functorio.Crop\nimport Functorio.Entity\nimport Functorio.Expand\nimport Functorio.ExpandTest\nimport Functorio.Factory\nimport Functorio.Fraction\nimport Functorio.Readme\nimport Functorio.Recipe\nimport Functorio.Row\nimport Functorio.Test\nimport Functorio.Util\n"
  },
  {
    "path": "Gleba300.lean",
    "content": "import Functorio\nimport Functorio.AssemblyLine\n\ninstance : Config where\n  generateBigPoles := true\n  generateRoboports := true\n  adapterMinHeight := 3\n\ndef makeBootstrapMash : Yumako 405 -> Bus (YumakoMash 810 × YumakoSeed (81/10)) :=\n  busAssemblyLine { recipe := .yumakoProcessing, fabricator := .assemblingMachine2} 9\n\ndef makeNutrientsFromSpoilage: Spoilage 225 → Bus (Nutrients (45/2)) := by exact\n  (busAssemblyLine { recipe := .nutrientsFromSpoilage, fabricator := .assemblingMachine2} 1)\n\ndef makeNutrientsFromYumakoMash0 : Nutrients (45/2) → YumakoMash 585 → Bus (Nutrients 270 × YumakoMash 465 × Nutrients (15/2)) := by exact\n  (busAssemblyLine (recipe .nutrientsFromYumakoMash [(465, .yumakoMash), (15/2, .nutrients)]) 1)\n\ndef makeNutrientsFromYumakoMash1 : Nutrients (555/2) → YumakoMash 465 → Bus (Nutrients 810 × YumakoMash 105 × Nutrients (465/2)) :=\n  by exact (busAssemblyLine (recipe .nutrientsFromYumakoMash [(105, .yumakoMash), (465/2, .nutrients)]) 3)\n\ndef bootstrapNutrients (yumako:Yumako 405) : Bus (Nutrients 810 × YumakoSeed (81/10) × YumakoMash 105) := do\n  let (mash, seeds) <- makeBootstrapMash yumako\n  let (mashToSpoil, mash) <- splitBalanced (left:=225) mash\n\n  let spoilage <- spoilingChamber mashToSpoil\n  let nutrients <- makeNutrientsFromSpoilage spoilage\n\n  let (nutrients0, mash, nutrients1) <- makeNutrientsFromYumakoMash0 nutrients mash\n  let nutrients <- merge nutrients0 nutrients1\n  let (nutrients0, mash, nutrients1) <- makeNutrientsFromYumakoMash1 nutrients mash\n  let nutrients <- merge nutrients0 nutrients1\n\n  return (nutrients.less, seeds, mash)\n\ndef makeBioflux0 : Nutrients 345 -> YumakoMash 2700 -> Jelly 2700 -> Bus (Bioflux 1080 × Jelly 540 × YumakoMash 0 × Nutrients 210) :=\n  busAssemblyLine (recipe .bioflux [(540, .jelly), (0, .yumakoMash), (210, .nutrients) ]) 9\n\ndef makeBioflux1 : Nutrients 210 -> YumakoMash 2700 -> Jelly 2700 -> Bus (Bioflux 1080 × Jelly 540 × YumakoMash 0 × Nutrients 75) :=\n  busAssemblyLine (recipe .bioflux [(540, .jelly), (0, .yumakoMash), (75, .nutrients) ]) 9\n\ndef makeNutrients0 : Nutrients 75 -> Bioflux 2160 -> Bus (Nutrients 2700 × Bioflux 1860 × Nutrients 60) :=\n  busAssemblyLine (recipe .nutrientsFromBioflux [(1860, .bioflux), (60, .nutrients)]) 1\n\ndef makeNutrients1 : Nutrients 60 -> Bioflux 1860 -> Bus (Nutrients 2700 × Bioflux 1560 × Nutrients 45) :=\n  busAssemblyLine (recipe .nutrientsFromBioflux [(1560, .bioflux), (45, .nutrients)]) 1\n\ndef loopbackPentapodEggs (water:Water 3840) (nutrients:Nutrients 2700) : Bus (Nutrients 660 × PentapodEgg 128) := do\n  let process := recipe .pentapodEgg [(660, .nutrients), (128, .pentapodEgg)]\n  let factoryNoLoopback := assemblyLine process 8\n  let loopback : Factory (assemblyLineInterface process) [] [(.water, .N), (.nutrients, .N), (.nutrients, .S), (.pentapodEgg, .S)] [] := {\n    entities := [\n      pipe 1 0, pipe 1 1, pipe 1 2,\n      belt 3 0 .N, belt 3 1 .N,\n      belt 4 1 .W, belt 5 1 .W, belt 6 1 .W, belt 7 1 .W, belt 8 1 .W, belt 9 1 .W, belt 10 1 .W,\n      beltUp 9 0 .N, beltDown 9 2 .N,\n      belt 10 0 .S,\n      belt 11 0 .S, belt 11 1 .S, belt 11 2 .S,\n      belt 12 0 .S, belt 12 1 .S, belt 12 2 .S,\n    ]\n    interface := {\n      n := #v[1, 3, 9, 10, 11, 12],\n      e := #v[],\n      s := #v[1, 9, 11, 12],\n      w := #v[],\n    }\n    wires := []\n    width := 13\n    height := 3\n    name := \"makePentapodEggsLoopbackInner\"\n  }\n\n  let factory := column factoryNoLoopback loopback\n  let namedFactory := factory.setName \"makePentapodEggsLoopback\"\n\n  let indexes <- busTapGeneric\n    [water.toBusLane', nutrients.toBusLane']\n    [.nutrients, .pentapodEgg]\n    namedFactory\n\n  return ({index:=indexes[0]!}, {index:=indexes[1]!})\n\ndef amplifyPentapodEggs : Water 4800 -> PentapodEgg 128 -> Nutrients 2700 -> Bus (PentapodEgg 240 × Nutrients 150 × PentapodEgg 48) :=\n  busAssemblyLine (recipe .pentapodEgg [(150, .nutrients), (48, .pentapodEgg)]) 10\n\ndef makePentapodEggs (water:Water 8640) (nutrients0 : Nutrients 2700) (nutrients1 : Nutrients 2700) : Bus (PentapodEgg 288 × Nutrients 810) := do\n  let (water0, water1) <- split water\n  let (nutrients0, eggs) <- loopbackPentapodEggs water0 nutrients0\n  let (eggs0, nutrients1, eggs1) <- amplifyPentapodEggs water1 eggs nutrients1\n\n  let eggs <- merge eggs0 eggs1\n  let nutrients <- merge nutrients0 nutrients1\n\n  return (eggs, nutrients)\n\ndef makeAgriculturalScience : Nutrients 810 -> Bioflux 1560 -> PentapodEgg 288 -> Bus (AgriculturalScience 315 × PentapodEgg 78 × Bioflux 1350 × Nutrients 705) :=\n  busAssemblyLine (recipe .agriculturalSciencePack [(78, .pentapodEgg), (1350, .bioflux), (705, .nutrients)]) 7\n\ndef cultivateCopperBacteria0 : Nutrients 705 -> YumakoMash 1080 -> Bus (CopperBacteria 36 × Spoilage 360 × YumakoMash 360 × Nutrients 675):=\n  busAssemblyLine (recipe .copperBacteria [(360, .yumakoMash), (675, .nutrients)]) 2\n\ndef cultivateCopperBacteria1 : Nutrients 675 -> CopperBacteria 36 -> Bioflux 1350 -> Bus (CopperBacteria 180 × Bioflux 1320 × CopperBacteria 6 × Nutrients 660):=\n  busAssemblyLine (recipe .copperBacteriaCultivation [ (1320, .bioflux), (6, .copperBacteria), (660, .nutrients)]) 1\n\ndef cultivateCopperBacteria2 : Nutrients 660 -> CopperBacteria 186 -> Bioflux 1320 -> Bus (CopperBacteria 360 × Bioflux 1260 × CopperBacteria 126 × Nutrients 630):=\n  busAssemblyLine (recipe .copperBacteriaCultivation [ (1260, .bioflux), (126, .copperBacteria), (630, .nutrients)]) 2\n\ndef cultivateCopperBacteria3 : Nutrients 630 -> CopperBacteria 486 -> Bioflux 1260 -> Bus (CopperBacteria 1620 × Bioflux 990 × CopperBacteria 216 × Nutrients 495):=\n  busAssemblyLine (recipe .copperBacteriaCultivation [(990, .bioflux), (216, .copperBacteria), (495, .nutrients)]) 9\n\ndef makeBacteriaCopper (nutrients:Nutrients 705) (mash:YumakoMash 1080) (bioflux:Bioflux 1350) : Bus (CopperOre 1836 × YumakoMash 360 × Bioflux 990 × Spoilage 360 × Nutrients 495) := do\n  let (bacteria, spoilage, mash, nutrients) <- cultivateCopperBacteria0 nutrients mash\n\n  let (bacteria0, bioflux, bacteria1, nutrients) <- cultivateCopperBacteria1 nutrients bacteria bioflux\n  let bacteria <- merge bacteria0 bacteria1\n  let (bacteria0, bioflux, bacteria1, nutrients) <- cultivateCopperBacteria2 nutrients bacteria bioflux\n  let bacteria <- merge bacteria0 bacteria1\n  let (bacteria0, bioflux, bacteria1, nutrients) <- cultivateCopperBacteria3 nutrients bacteria bioflux\n  let bacteria <- merge bacteria0 bacteria1\n\n  let ore <- spoilingChamber bacteria\n  return (ore, mash, bioflux, spoilage, nutrients)\n\ndef cultivateIronBacteria0 : Nutrients 495 -> Jelly 2700 -> Bus (Spoilage 1440 × IronBacteria 36 × Jelly 1260 × Nutrients 465):=\n  busAssemblyLine (recipe .ironBacteria [(1260, .jelly), (465, .nutrients)]) 2\n\ndef cultivateIronBacteria1 : Nutrients 465 -> IronBacteria 36 -> Bioflux 990 -> Bus (IronBacteria 180 × Bioflux 960 × IronBacteria 6 × Nutrients 450) :=\n  busAssemblyLine (recipe .ironBacteriaCultivation [(960, .bioflux), (6, .ironBacteria), (450, .nutrients)]) 1\n\ndef cultivateIronBacteria2 : Nutrients 450 -> IronBacteria 186 -> Bioflux 960 -> Bus (IronBacteria 900 × Bioflux 810 × IronBacteria 36 × Nutrients 375):=\n  busAssemblyLine (recipe .ironBacteriaCultivation [(810, .bioflux), (36, .ironBacteria), (375, .nutrients)]) 5\n\ndef makeBacteriaIron (nutrients:Nutrients 495) (jelly:Jelly 2700) (bioflux:Bioflux 990) : Bus (IronOre 936 × Jelly 1260 × Bioflux 810 × Spoilage 1440 × Nutrients 375) := do\n  let (spoilage, bacteria, jelly, nutrients) <- cultivateIronBacteria0 nutrients jelly\n\n  let (bacteria0, bioflux, bacteria1, nutrients) <- cultivateIronBacteria1 nutrients bacteria bioflux\n  let bacteria <- merge bacteria0 bacteria1\n  let (bacteria0, bioflux, bacteria1, nutrients) <- cultivateIronBacteria2 nutrients bacteria bioflux\n  let bacteria <- merge bacteria0 bacteria1\n\n  let ore <- spoilingChamber bacteria\n  return (ore, jelly, bioflux, spoilage, nutrients)\n\ndef makeBioSulfur : Nutrients 375 -> Spoilage 1800 -> Bioflux 810 -> Bus (Sulfur 540 × Bioflux 630 × Spoilage 900 × Nutrients 330) :=\n  busAssemblyLine (recipe .biosulfur [(630, .bioflux), (900, .spoilage), (330, .nutrients)]) 3\n\ndef makeBioPlastic : Nutrients 330 -> Bioflux 630 -> YumakoMash 360 -> Bus (Plastic 270 × YumakoMash 120 × Bioflux 570 × Nutrients 315) :=\n  busAssemblyLine (recipe .bioplastic [(120, .yumakoMash), (570, .bioflux), (315, .nutrients)]) 1\n\ndef makeLowDensityStructure : Steel 40 -> Copper 400 -> Plastic 100 -> Bus (LowDensityStructure 20) :=\n  busAssemblyLine (recipe .lowDensityStructure) 4\n\ndef makeAcid : Water 6000 -> Sulfur 300 -> Iron 60 -> Bus (Acid 3000) :=\n  busAssemblyLine (recipe .sulfuricAcid) 1\n\ndef makeCopper : CopperOre 1500 -> Bus (Copper 1500) :=\n  busAssemblyLine (recipe .copperPlate) 40\n\ndef makeIron : IronOre 900 -> Bus (Iron 900) :=\n  busAssemblyLine (recipe .ironPlate) 24\n\ndef makeSteel : Iron 225 -> Bus (Steel 45) :=\n  busAssemblyLine (recipe .steelPlate) 6\n\ndef makeBioRocketFuel : Water 720 -> Nutrients 315 -> Jelly 1260 -> Bioflux 570 -> Bus (RocketFuel 36 × Bioflux 522 × Jelly 540 × Nutrients 285) :=\n  busAssemblyLine (recipe .rocketFuelFromJelly [(522, .bioflux), (540, .jelly), (285, .nutrients)]) 2\n\ndef makeCable : Copper 1050 -> Bus (Cable 2100) :=\n  busAssemblyLine (recipe .copperCable) 7\n\ndef makeGreenCircuit : Iron 600 -> Cable 1800 -> Bus (GreenCircuit 600) :=\n  busAssemblyLine (recipe .electronicCircuit) 4\n\ndef makeRedCircuit : GreenCircuit 100 -> Plastic 100 -> Cable 200 -> Bus (RedCircuit 50) :=\n  busAssemblyLine (recipe .advancedCircuit) 4\n\ndef makeBlueCircuit : Acid (225/2) -> GreenCircuit 450 -> RedCircuit 45 -> Bus (BlueCircuit (45/2)) :=\n  busAssemblyLine (recipe .processingUnit) 3\n\ndef makeRocket : BlueCircuit 20 -> LowDensityStructure 20 -> RocketFuel 20 -> Bus Unit :=\n  busAssemblyLine (recipe .rocketPart) 1\n\ndef makeMashFullBelt : Nutrients 120 -> Yumako 960 -> Bus (YumakoMash 2700 × YumakoSeed (144/5)) :=\n  busAssemblyLine (recipe .yumakoProcessing) 8\n\ndef makeMashPartialBelt : Nutrients 45 -> Yumako 360 -> Bus (YumakoMash 1080 × YumakoSeed (54/5)) :=\n  busAssemblyLine (recipe .yumakoProcessing) 3\n\ndef makeNonBiologicalComponents\n  (copperOre : CopperOre 1500) (ironOre: IronOre 900) (water:Water 6000)\n  (sulfur : Sulfur 540) (plastic : Plastic 270) (rocketFuel : RocketFuel 36)\n  : Bus Unit :=\ndo\n  let (plastic0, plastic1) <- split plastic\n\n  let copper <- makeCopper copperOre\n  let iron <- makeIron ironOre\n  let (iron0, iron) <- split iron\n  let (iron1, iron2) <- split iron\n  let (copper0, copper1) <- split copper\n  let steel <- makeSteel iron0\n\n  let acid <- makeAcid water sulfur.less iron1\n  let lowDensityStruct <- makeLowDensityStructure steel.less copper0 plastic0\n  let cable <- makeCable copper1.less\n  let (cable0, cable1) <- split cable\n  let greenCircuit <- makeGreenCircuit iron2.less cable0\n  let (greenCircuit0, greenCircuit1) <- split greenCircuit\n  let redCircuit <- makeRedCircuit greenCircuit0 plastic1.less cable1.less\n  let blueCircuit <- makeBlueCircuit acid.less greenCircuit1.less redCircuit.less\n\n  let _ <- makeRocket blueCircuit.less lowDensityStruct rocketFuel.less\n\ndef makeJelly (nutrients:Nutrients 810) (jellynut:Jellynut 1440) : Bus (Jelly 2700 × Jelly 2700 × Jelly 2700 × JellynutSeed (216 / 5) × Nutrients 630):= do\n  let (jellynut0, jellynut) <- split jellynut\n  let (jellynut1, jellynut2) <- split jellynut\n\n  let (jelly0, jellySeed0, nutrients) <- busAssemblyLine (recipe .jellynutProcessing [(750, .nutrients)]) 4 nutrients jellynut0\n  let (jelly1, jellySeed1, nutrients) <- busAssemblyLine (recipe .jellynutProcessing [(690, .nutrients)]) 4 nutrients jellynut1\n  let (jelly2, jellySeed2, nutrients) <- busAssemblyLine (recipe .jellynutProcessing [(630, .nutrients)]) 4 nutrients jellynut2\n\n  let jellySeed <- merge jellySeed0 jellySeed1\n  let jellySeed <- merge jellySeed jellySeed2\n\n  return (jelly0, jelly1, jelly2, jellySeed, nutrients)\n\ndef makeMash (nutrients:Nutrients 630) (yumako:Yumako 2280) : Bus (YumakoMash 2700 × YumakoMash 2700 × YumakoMash 1080 × YumakoSeed (342 / 5) × Nutrients 345):= do\n  let (yumako0, yumako) <- split yumako\n  let (yumako1, yumako2) <- split yumako\n\n  let (mash0, yumakoSeed0, nutrients) <- busAssemblyLine (recipe .yumakoProcessing [(510, .nutrients)]) 8 nutrients yumako0\n  let (mash1, yumakoSeed1, nutrients) <- busAssemblyLine (recipe .yumakoProcessing [(390, .nutrients)]) 8 nutrients yumako1\n  let (mash2, yumakoSeed2, nutrients) <- busAssemblyLine (recipe .yumakoProcessing [(345, .nutrients)]) 3 nutrients yumako2\n\n  let yumakoSeed <- merge yumakoSeed0 yumakoSeed1\n  let yumakoSeed <- merge yumakoSeed yumakoSeed2\n\n  return (mash0, mash1, mash2, yumakoSeed, nutrients)\n\ndef makeCarbonFiber :=\n  busAssemblyLine (recipe .carbonFiber)\n\ndef glebaFactory := bus do\n  let water <- input .water 15360\n  let yumako <- input .yumako 2685\n  let jellynut <- input .jellynut 1440\n\n  let (water0, water) <- split (left:=8640) water\n  let (water1, water2) <- split (right:=6000) water\n\n  let (yumako0, yumako1) <- split (left:=405) (right:=2280) yumako\n  let (nutrients, yumakoSeed0, _) <- bootstrapNutrients yumako0\n\n  let (jelly0, jelly1, jelly2, jellySeed, nutrients) <- makeJelly nutrients jellynut\n  let (mash0, mash1, mash2, yumakoSeed1, nutrients) <- makeMash nutrients yumako1\n  let yumakoSeed <- merge yumakoSeed0 yumakoSeed1\n\n  let (bioflux0, _, _, nutrients) <- makeBioflux0 nutrients mash0 jelly0\n  let (bioflux1, _, _, nutrients) <- makeBioflux1 nutrients mash1 jelly1\n  let bioflux <- merge bioflux0 bioflux1\n\n  let (eggNutrients0, bioflux, nutrients) <- makeNutrients0 nutrients bioflux\n  let (eggNutrients1, bioflux, nutrients) <- makeNutrients1 nutrients bioflux\n  let (eggs, moreNutrients) <- makePentapodEggs water0 eggNutrients0 eggNutrients1\n  let nutrients <- merge nutrients moreNutrients\n\n  let (_, _, bioflux, nutrients) <- makeAgriculturalScience nutrients.less bioflux eggs\n\n  pipePumps   -- Right around here, the pipes on the bus are so long that they need pumps.\n\n  let (copperOre, mash, bioflux, spoilage0, nutrients) <- makeBacteriaCopper nutrients mash2 bioflux\n  let (copperOre, _) <- removeExcess copperOre\n  let (ironOre, jelly, bioflux, spoilage1, nutrients) <- makeBacteriaIron nutrients jelly2 bioflux\n  let (ironOre, _) <- removeExcess ironOre\n  let spoilage <- merge spoilage0 spoilage1\n\n  let (sulfur, bioflux, _, nutrients) <- makeBioSulfur nutrients spoilage bioflux\n  let (sulfur, _) <- removeExcess sulfur\n  let (plastic, _, bioflux, nutrients) <- makeBioPlastic nutrients bioflux mash\n  let (plastic, _) <- removeExcess plastic\n  let (rocketFuel, _, _, _) <- makeBioRocketFuel water1 nutrients jelly bioflux\n  let (rocketFuel, _) <- removeExcess rocketFuel\n\n  makeNonBiologicalComponents copperOre.less ironOre.less water2 sulfur plastic rocketFuel\n\ndef main : IO Unit :=\n  IO.println (glebaFactory.toBlueprint) --  (bootstrap := true))\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "Nauvis150.lean",
    "content": "import Functorio\n\ninstance : Config where\n  generateBigPoles := true\n  generateRoboports := true\n  providerChestCapacity := 3\n  adapterMinHeight := 3\n\ndef ironFullBeltFactory : IronOre 2700 -> Bus (Iron 2700) :=\n  busAssemblyLine (recipe .ironPlate) 72\n\ndef ironFactory (ore:Vector (IronOre 2700 ) 8) : Bus (Vector (Iron 2700) 8) := do\n  let iron0 <- ironFullBeltFactory ore[0]\n  let iron1 <- ironFullBeltFactory ore[1]\n  let iron2 <- ironFullBeltFactory ore[2]\n  let iron3 <- ironFullBeltFactory ore[3]\n  let iron4 <- ironFullBeltFactory ore[4]\n  let iron5 <- ironFullBeltFactory ore[5]\n  let iron6 <- ironFullBeltFactory ore[6]\n  let iron7 <- ironFullBeltFactory ore[7]\n  return Array.toVector #[iron0, iron1, iron2, iron3, iron4, iron5, iron6, iron7]\n\ndef copperFullBeltFactory : CopperOre 2700 -> Bus (Copper 2700) :=\n  busAssemblyLine (recipe .copperPlate) 72\n\ndef copperFactory (ore:Vector (CopperOre 2700 ) 6) : Bus (Vector (Copper 2700) 6) := do\n  let copper0 <- copperFullBeltFactory ore[0]\n  let copper1 <- copperFullBeltFactory ore[1]\n  let copper2 <- copperFullBeltFactory ore[2]\n  let copper3 <- copperFullBeltFactory ore[3]\n  let copper4 <- copperFullBeltFactory ore[4]\n  let copper5 <- copperFullBeltFactory ore[5]\n  return Array.toVector #[copper0, copper1, copper2, copper3, copper4, copper5]\n\ndef steelPartialBeltFactory : Iron 2700 -> Bus (Steel 540) :=\n  busAssemblyLine (recipe .steelPlate) 72\n\ndef steelFactory (iron:Vector (Iron 2700) 4) : Bus (Steel 2160) := do\n  let steel0 <- steelPartialBeltFactory iron[0]\n  let steel1 <- steelPartialBeltFactory iron[1]\n  let steel2 <- steelPartialBeltFactory iron[2]\n  let steel3 <- steelPartialBeltFactory iron[3]\n\n  let steel01 <- merge steel0 steel1\n  let steel23 <- merge steel2 steel3\n  merge steel01 steel23\n\ndef brickFactory : Stone 2700 -> Bus (Brick 1350) :=\n  busAssemblyLine (recipe .stoneBrick) 36\n\ndef gearFactory : Iron 1500 -> Bus (Gear 750) :=\n  busAssemblyLine (recipe .ironGearWheel) 5\n\ndef redScienceFactory : Copper 150 -> Gear 150 -> Bus (RedScience 150) :=\n  busAssemblyLine (recipe .automationSciencePack) 10\n\ndef inserterFactory : GreenCircuit 150 -> Gear 150 -> Iron 150 -> Bus (Inserter 150) :=\n  busAssemblyLine (recipe .inserter) 1\n\ndef yellowBeltFactory : Iron 150 -> Gear 150 -> Bus (YellowBelt 300) :=\n  busAssemblyLine (recipe .transportBelt) 1\n\ndef greenScienceFactory : Inserter 150 -> YellowBelt 150 -> Bus (GreenScience 150) :=\n  busAssemblyLine (recipe .logisticSciencePack) 12\n\ndef yellowAmmoFactory : Iron 300 -> Bus (YellowAmmo 75) :=\n  busAssemblyLine (recipe .firearmMagazine) 1\n\ndef redAmmoFactory : YellowAmmo 75 -> Steel (75/2) -> Copper 75 -> Bus (RedAmmo 75) :=\n  busAssemblyLine (recipe .piercingRoundsMagazine) 3\n\ndef wallFactory : Brick 750 -> Bus (Wall 150) :=\n  busAssemblyLine (recipe .stoneWall) 1\n\ndef grenadeFactory : Iron 375 -> Coal 750 -> Bus (Grenade 75) :=\n  busAssemblyLine (recipe .grenade) 8\n\ndef blackScienceFactory : RedAmmo 75 -> Grenade 75 -> Wall 150 -> Bus (BlackScience 150) :=\n  busAssemblyLine (recipe .militarySciencePack) 10\n\ndef plasticFactory (petrol:Petrolium 28800) (coal:Coal 1440) : Bus (Plastic 2000 × Plastic 750) := do\n  let (petrol0, petrol1) <- split (left:=20400) petrol\n  let (coal0, coal1) <- split (left:=1020) coal\n  let plastic0 <- busAssemblyLine (recipe .plasticBar) 17 petrol0 coal0\n  let plastic1 <- busAssemblyLine (recipe .plasticBar) 7 petrol1 coal1\n  return (plastic0.less, plastic1.less)\n\ndef acidFactory : Water 6000 -> Sulfur 300 -> Iron 60 -> Bus (Acid 3000) :=\n  busAssemblyLine (recipe .sulfuricAcid) 1\n\ndef pipeFactory : Iron 450 -> Bus (Pipe 450) :=\n  busAssemblyLine (recipe .pipe) 3\n\ndef engineFactory : Steel 210 -> Gear 210 -> Pipe 420 -> Bus (Engine 210) :=\n  busAssemblyLine (recipe .engineUnit) 28\n\ndef sulfurFactory : Water 7200 -> Petrolium 7200 -> Bus (Sulfur 480) :=\n  busAssemblyLine (recipe .sulfur) 4\n\ndef blueScienceAssemblyLine : Engine 150 -> RedCircuit 225 -> Sulfur 75 -> Bus (BlueScience 150) :=\n  busAssemblyLine (recipe .chemicalSciencePack) 24\n\ndef furnaceFactory : Steel 600 -> RedCircuit 300 -> Brick 600 -> Bus (Furnace 60) :=\n  busAssemblyLine (recipe .electricFurnace) 4\n\ndef prodModuleFactory : RedCircuit 250 -> GreenCircuit 250 -> Bus (ProdModule 50) :=\n  busAssemblyLine (recipe .productivityModule) 10\n\ndef ironStickFactory : Iron 450 -> Bus (IronStick 900) :=\n  busAssemblyLine (recipe .ironStick) 3\n\ndef railFactory : Stone 750 -> IronStick 750-> Steel 750 -> Bus (Rail 1500) :=\n  busAssemblyLine (recipe .rail) 5\n\ndef purpleScienceFactory : Furnace 50 -> ProdModule 50 -> Rail 1500 ->  Bus (PurpleScience 150) :=\n  busAssemblyLine (recipe .productionSciencePack) 14\n\ndef batteryFactory : Acid 2400 -> Iron 120 -> Copper 120 -> Bus (Battery 120) :=\n  busAssemblyLine (recipe .battery) 8\n\ndef electricEngineFactory : Lubricant 900 -> Engine 60 -> GreenCircuit 120 -> Bus (ElectricEngine 60) :=\n  busAssemblyLine (recipe .electricEngineUnit) 8\n\ndef robotFrameFactory : ElectricEngine 60 -> Battery 120 -> Steel 60 ->GreenCircuit 180 -> Bus (RobotFrame 60) :=\n  busAssemblyLine (recipe .flyingRobotFrame) 16\n\ndef lowDensityStructureFactory (copperA:Copper 300) (copperB:Copper 2700) (steel:Steel 300) (plastic:Plastic 750) : Bus (LowDensityStructure 150) := do\n  let (steelA, steelB) <- split steel\n  let (plasticA, plasticB) <- split plastic\n  let structA <- busAssemblyLine (recipe .lowDensityStructure) 3 steelA copperA plasticA\n  let structB <- busAssemblyLine (recipe .lowDensityStructure) 27 steelB copperB plasticB\n  merge structA structB\n\ndef yellowScienceFactory : LowDensityStructure 150 -> BlueCircuit 100 -> RobotFrame 50 -> Bus (YellowScience 150) :=\n  busAssemblyLine (recipe .utilitySciencePack) 14\n\ndef greenCircuitFactory (copper:Copper 1350) (iron:Iron 900) : Bus (GreenCircuit 900) := do\n  let cable : Cable 2700 <- busAssemblyLine (recipe .copperCable) 9 copper\n  busAssemblyLine (recipe .electronicCircuit) 6 iron cable\n\ndef greenCircuitFullBeltFactory (copper0:Copper 1350) (copper1:Copper 2700) (iron:Iron 2700) : Bus (GreenCircuit 2700) := do\n  let (copper1, copper2) <- split copper1\n  let (iron0, iron) <- split iron\n  let (iron1, iron2) <- split iron\n\n  let green0 <- greenCircuitFactory copper0 iron0\n  let green1 <- greenCircuitFactory copper1 iron1\n  let green2 <- greenCircuitFactory copper2 iron2\n\n  let green <- merge green0 green1\n  let green <- merge green green2\n  return green\n\ndef redCircuitFactory (copper:Copper 1050) (greenCircuit:GreenCircuit 1000) (plastic:Plastic 1000) : Bus (RedCircuit 500) := do\n  let cable : Cable 2100 <- busAssemblyLine (recipe .copperCable) 7 copper\n  busAssemblyLine (recipe .advancedCircuit) 40 greenCircuit plastic cable.less\n\ndef blueCircuitFactory : Acid 525 -> GreenCircuit 2100 -> RedCircuit 210 -> Bus (BlueCircuit 105) :=\n  busAssemblyLine (recipe .processingUnit) 14\n\ndef circuitFactory (copper: Vector (Copper 2700) 4) (iron: Vector (Iron 2700) 2) (plastic:Plastic 2000) (acid:Acid 525)\n: Bus (GreenCircuit 700 × RedCircuit 775 × BlueCircuit 100)\n:= do\n  -- green\n  let (copper0, copper1) <- split copper[0]\n  let greenForRed <- greenCircuitFullBeltFactory copper0 copper[1] iron[0]\n  let greenForBlue <- greenCircuitFullBeltFactory copper1 copper[2] iron[1]\n\n  pipePumps   -- Right around here, the pipes on the bus are so long that they need pumps.\n\n  -- red\n  let (greenForRed0, greenForRed) <- split greenForRed\n  let (greenForRed1, greenOut) <- split greenForRed\n  let (copper0, copper1) <- split copper[3]\n  let (plastic0, plastic1) <- split plastic\n  let red0 <- redCircuitFactory copper0 greenForRed0 plastic0\n  let red1 <- redCircuitFactory copper1.less greenForRed1 plastic1\n  let red <- merge red0 red1\n  let (redForBlue, redOut) <- split red\n\n  -- blue\n  let blueOut <- blueCircuitFactory acid greenForBlue.less redForBlue\n\n  return (greenOut, redOut.less, blueOut.less)\n\ndef advancedOilProcessingFactory : Water 19200 -> CrudeOil 38400 -> Bus (HeavyOil 9600 × LightOil 17280 × Petrolium 21120) :=\n  busAssemblyLine (recipe .advancedOilProcessing) 32\n\ndef heavyOilCrackingFactory : Water 6300 -> HeavyOil 8400 ->Bus (LightOil 6300) :=\n  busAssemblyLine (recipe .heavyOilCracking) 7\n\ndef lightOilCrackingFactory :  Water 23400 -> LightOil 23400 ->Bus (Petrolium 15600) :=\n  busAssemblyLine (recipe .lightOilCracking) 26\n\ndef lubricantFactory : HeavyOil 1200 -> Bus (Lubricant 1200) :=\n  busAssemblyLine (recipe .lubricant) 2\n\ndef oilFactory (water:Water 48900) (crude:CrudeOil 38400) : Bus (Petrolium 36000 × Lubricant 900) := do\n  let (water0, rest) <- split (left:=19200) water\n  let (water1, water2) <- split (left:=6300) rest\n\n  let (heavy, light0, petrol0) <- advancedOilProcessingFactory water0 crude\n  let (heavy0, heavy1) <- split (left:=1200) heavy\n\n  let light1 <- heavyOilCrackingFactory water1 heavy1\n  let light <- merge light0 light1\n\n  let petrol1 <- lightOilCrackingFactory water2 light.less\n  let petrol <- merge petrol0 petrol1\n\n  let lube <- lubricantFactory heavy0\n\n  return (petrol.less, lube.less)\n\ndef nauvisFactory := bus do\n  let copperOre <- inputs 6 .copperOre 2700\n  let ironOre <- inputs 8 .ironOre 2700\n  let stone0 <- input .stone 2700\n  let stone1 <- input .stone 750\n  let coal <- input .coal 2190\n  let water <- input .water 62100\n  let crudeOil <- input .crudeOil 38400\n\n  let (water0, water) <- split (left:=48900) water\n  let (water1, water2) <- split (left:=7200) water\n\n  let (petrol, lubricant) <- oilFactory water0 crudeOil\n  let (petrol0, petrol1) <- split (left:=7200) petrol\n\n  let copper <- copperFactory copperOre\n  let (copper0, rest) <- split (left:=150) copper[0]\n  let (copper1, rest) <- split (left:=75) rest\n  let (copper2, rest) <- split (left:=120) rest\n  let copper3 : Copper 300 := rest.less\n\n  let iron <- ironFactory ironOre\n  let (iron0, rest) <- split (left:=60) iron[0]\n  let (iron1, rest) <- split (left:=1500) rest\n  let (iron2, rest) <- split (left:=150) rest\n  let (iron3, rest) <- split (left:=150) rest\n  let (iron4, rest) <- split (left:=300) rest\n  let iron5 : Iron 375 := rest.less\n  let (iron6, rest) <- split (left:=450) iron[1]\n  let (iron7, repr) <- split (left:=450) rest\n  let iron8 : Iron 120 := rest.less\n\n  let brick <- brickFactory stone0\n  let (brick0, brick1) <- split (left:=750) brick\n\n  let steel <- steelFactory (iron.extract 2 6)\n  let (steel0, rest) <- split (left:=75/2) steel\n  let (steel1, rest) <- split (left:=210) rest\n  let (steel2, rest) <- split (left:=600) rest\n  let (steel3, rest) <- split (left:=750) rest\n  let (steel4, rest) <- split (left:=60) rest\n  let steel5 : Steel 300 := rest.less\n\n  let (coal0, coal1) <- split (left:=1440) coal\n\n  let sulfur <- sulfurFactory water1 petrol0\n  let (sulfur0, sulfur1) <- split sulfur\n  let acid <- acidFactory water2 sulfur0 iron0\n  let (acid0, acid1) <- split (left:=525) acid\n\n  let (plastic0, plastic1) <- plasticFactory petrol1 coal0\n\n--  pipePumps   -- Right around here, the pipes on the bus are so long that they need pumps.\n\n  let (greenCircuit, redCircuit, blueCircuit) <-\n    circuitFactory (copper.extract 1 5) (iron.extract 6 8) plastic0 acid0\n  let (greenCircuit0, rest) <- split (left:=150) greenCircuit\n  let (greenCircuit1, rest) <- split (left:=250) rest\n  let (greenCircuit2, greenCircuit3) <- split (left:=120) rest\n  let (redCircuit0, rest) <- split (left:=225) redCircuit\n  let (redCircuit1, redCircuit2) <- split (left:=300) rest\n\n  let gear <- gearFactory iron1\n  let (gear0, rest) <- split (left:=150) gear\n  let (gear1, rest) <- split (left:=150) rest\n  let (gear2, rest) <- split (left:=150) rest\n  let gear3 : Gear 210 := rest.less\n\n  let _ <- redScienceFactory copper0 gear0\n\n  let inserter <- inserterFactory greenCircuit0 gear1 iron2\n  let belt <- yellowBeltFactory iron3 gear2\n  let _ <- greenScienceFactory inserter belt.less\n\n  let yellowAmmo <- yellowAmmoFactory iron4\n  let redAmmo <- redAmmoFactory yellowAmmo steel0 copper1\n  let wall <- wallFactory brick0\n  let grenade <- grenadeFactory iron5 coal1\n  let _ <- blackScienceFactory redAmmo grenade wall\n\n  let pipe <- pipeFactory iron6\n  let engine <- engineFactory steel1 gear3 pipe.less\n  let (engine0, engine1) <- split engine\n  let _ <- blueScienceAssemblyLine engine0 redCircuit0 sulfur1.less\n\n  let furnace <- furnaceFactory steel2 redCircuit1 brick1\n  let prodModule <- prodModuleFactory redCircuit2 greenCircuit1\n  let stick <- ironStickFactory iron7\n  let rail <- railFactory stone1 stick.less steel3\n  let _ <- purpleScienceFactory furnace.less prodModule rail\n\n  let battery <- batteryFactory acid1.less iron8 copper2\n  let electricEngine <- electricEngineFactory lubricant engine1 greenCircuit2\n  let robotFrame <- robotFrameFactory electricEngine battery steel4 greenCircuit3\n  let lowDensityStruct <- lowDensityStructureFactory copper3 copper[5] steel5 plastic1\n  let _ <- yellowScienceFactory lowDensityStruct blueCircuit.less robotFrame.less\n\ndef main : IO Unit :=\n  IO.println (nauvisFactory.toBlueprint) --  (bootstrap := true))\n"
  },
  {
    "path": "README.md",
    "content": "# Functorio\n\nFunctorio lets you build your Factorio factories in the Lean programming language; giving you conveniences like type safety, functions, recursion, version history, libraries, etc.\n\nFor example, here's a simple factory that generates 150 red science per minute.\n\n```lean\ndef redScience := bus do\n  let ironOre <- input .ironOre 300\n  let copperOre <- input .copperOre 150\n\n  let iron : Iron 300 <- makeIron ironOre\n  let copper : Copper 150 <- makeCopper copperOre\n  let gear : Gear 150 <- makeGear iron\n  let science : RedScience 150 <- makeRedScience copper gear\n```\n\nFunctorio let's you export your factories as blueprints, ready to be imported into Factorio:\n\n![](figures/red-science.png)\n\nIn addition to making it easy to write factories, Functiorio also makes it safe! Every resource that is passed around (like `iron`, `gear`, `science`, ...) has a type indicating the kind of item and the number of items provided per minute. In addition, every factory (like `makeIron`, `makeGear`, `makeRedScience`, ...) indicates how many resources it takes and returns. Thus, when your factory type checks, it often just works. And here are the stats to prove it:\n\n![](figures/red-science-stats.png)\n\nTo get started download the [github repo](https://github.com/konne88/functorio) and open it in\nVSCode. The github repo contains a devcontainer so VSCode will automatically install all necessary dependencies and you can just run to get the blueprint:\n\n```bash\n./red-science-150.sh\n```\n\nHere's a video to help you get started:\n\n[![](figures/quickstart-play.png)](https://youtu.be/NCp2bw8X4V8)\n\nOnce you're comfortable playing with Functorio, [here](Nauvis150.lean)'s a factory that is a bit more complicated, producing 150 science per minute for all the sciences on Nauvis, and here's a video walkthrough of the factory:\n\n[![](figures/nauvis150-play.png)](https://youtu.be/HK3KwjN-Dtc)\n\nThis will print a blueprint that you can import directly into Factorio.\n\nThis library is still in early alpha. Let's make it better together! Join the [discord](https://discord.gg/UGEcqxpSMn), send me\npull requests, file github issues, email me, etc. Anything you think is interesting is probably also interesting to me.\n\nI'm not the first person to have looked into the connection between Factorio and programming languages. Bartosz Milewski gave a [talk](https://www.youtube.com/watch?v=A46KQtriYuM) and wrote a great [blog post](https://bartoszmilewski.com/2021/02/16/functorio/) about the connection; and also came up with the name Functorio in his post.\n\nThe following sections describe Functorio's features in more detail. At the end of this post you'll see how to build\na factory that generates 150 of all the Nauvis sciences (red, green, blue, yellow, purple, black) per minute.\n\n# Buses\n\nA `bus` lets you connect multiple factories in a conventient a safe way. Here is an example bus that makes gears from iron ore. You just specify how the resources should flow,\nand the `bus` function creates the required bus entities (belts, pipes, etc) to realize that intention. The way factories get connected resembles the [main bus](https://wiki.factorio.com/tutorial:main_bus) pattern, hence the name.\n\nHere's a simple gear bus example:\n\n```lean\ndef gearFactory := bus do\n  let ironOre : IronOre 300 <- input .ironOre 300\n  let iron : Iron 300 <- makeIron ironOre\n  let gear <- makeGear iron\n```\n\n![](figures/gear-bus.png)\n\nThe variables on a bus (e.g. `ironOre`, `iron`, `gear`) represent the lanes of the bus, and have the type `BusLane ingredient throughput`.\nFor example `ironOre` has the type `BusLane .ironOre 100` which means this lane carries 100 iron ore per minute. The library also\nprovides shorter name for the types, e.g. `IronOre 100` is a synonym for `BusLane .ironOre 100`.\nA bus lane can be consumed by passing it into a factory, e.g. `makeIron ironOre`.\nNew lanes can be returned by factories, for example the `makeGear` factory creates the new `gear` lane.\n\nThe `input` command creates a new bus lane that is expected to be filled outside the factory. This is useful to provide the raw\ningredients for your factory. All inputs must be declared at the beginning of the bus, before any factories are used.\n\nBus lanes must only ever be consumed once. It is invalid to pass the same lane into two factories. If this is desired, the lane\nmust first be split into two lanes, using the `split (left:=x) (right:=y)` function. Here is an example:\n\n```lean\ndef splitIron := bus do\n  let iron : Iron 450 <- input .ironPlate 450\n  let (iron0, iron1) <- split (left:=300) (right:=150) iron\n  let gear <- makeGear iron0\n  let belt <- makeBelt iron1 gear\n```\n\n![](figures/split.png)\n\nSimilarly, lanes can be merged with the `merge` function. Here is an example:\n\n```lean\ndef mergeSteel := bus do\n  let iron0 <- input .ironPlate 2700\n  let iron1 <- input .ironPlate 2700\n  let steel0 <- makeSteel iron0\n  let steel1 <- makeSteel iron1\n  let steel <- merge steel0 steel1\n```\n\n![](figures/merge.png)\n\nThe bus also supports liquids. Those are used just like solid ingredients, but the bus will generate\npipes instead of belts to connect them. e.g.\n\n```lean\ndef acidFactory := bus do\n  let iron <- input .ironPlate 60\n  let sulfur <- input .sulfur 300\n  let water <- input .water 6000\n  let acid <- makeAcid water iron sulfur\n```\n\n![](figures/liquid-bus.png)\n\n# Bus Taps\n\nWe've talked about passing resources around the bus, but we still haven't talked about how `makeBelt` etc is actually implemented.\nTo connect a factory to the bus, you need to create a `busTap`, which takes a list of input belt lanes from the bus, connects to the given `factory` and returns the output of the factory. For example, `makeBelt` can be implemented as follows\n\n```lean\ndef makeBelt iron gear :=\n   busTap [iron, gear] factory\n```\n\n# Factories\n\nA `Factory` is a collection of entities (assemblers, belts, inserters, etc) placed within a rectangle,\nand has interfaces on all sides of that rectangle (north, east, south and west).\nFor the below example, the north and south interface are the same: two belts, one iron belt facing north and one gear belt facing south.\nThe east and west interfaces are empty.\n\n![](figures/factory-type.png)\n\nThe type of a factory is `Factory n e s w` where the letters stand for the north, east, south, and\nwest interfaces. Each interface specifies the resource type and travel direction of that resource.\nThus the above factory's type is:\n\n```lean\ngearFactory : Factory\n  [(.iron, .N), (.ironGearWheel, .S)]  -- north interfaces\n  []                                   -- east interfaces\n  [(.iron, .N), (.ironGearWheel, .S)]  -- south interfaces\n  []                                   -- west interfaces\n```\n\nThe `Factory` type is designed so that you should only have to worry about a factory's interfaces,\nbut not its size, entities, or the position of the interfaces. The interfaces are reflected in\nthe `Factory`'s type, so when your factory type checks, it should ideally just work.\n\n# Columns / Rows\n\nTwo factories with matching north/south interfaces can be combined with the `column` command. Here's an example:\n\n```lean\ncolumn gearFactory gearFactory\n```\n\n![](figures/two-column.png)\n\nThere is also a `columnList` command, which connects all the factories from the given list.\nTo create a column of 10 gear factories, you would write:\n\n```lean\ncolumnList (List.replicate 10 gearFactory)\n```\n\nSimilarly, there is also a `row`/`rowList` command, which combines factories with matching east/west interfaces.\n\n# Expanding\n\nSometimes the factories that you want to combine don't have the same dimensions. Expanding a\nfactory increases the factory's width/height without changing its interfaces.\nFor example, here we're expanding a factory's south by 5 tiles:\n\n```lean\nexpand .S 5 inserterFactory\n```\n\nExpansions are automatically generated when you combine factories with `column`/`row`, so you\ngenerally don't have to worry about the dimensions of factories.\n\n![](figures/expand.png)\n\n# Adapters\n\nSometimes the factories that you want to combine have the sames interfaces,\nbut the belts/pipes are in different positions. That's where adapters come in. For example, here we're trying\nto connect some inserter assemblers to a bus.\n\n![](figures/adapter.png)\n\nAdapters are automatically generated when you combine factories with `column`/`row`, so\nyou generally don't have to worry about the position of interfaces (but the order of interfaces matters).\n\n# Caps\n\nSometimes you want to remove an interface from a factory. The `capN`, `capE`, `capS`, `capW` commands do just that.\n\n```lean\ncapS inserterFactory\n```\n\n![](figures/cap.png)\n\n# Assembly Lines\n\nFunctorio comes with a builtin set of factories to assemble all the recipes required for red, green, blue, purple, yellow, and black science (contributions for more recipes are very welcome!).\nThese factories are designed so that they can be replicated many times into an assembly line.\n\nTo get just a single assembler for a certain recipe, you can use the `station` function (because it's one station of a larger assembly line):\n\n```lean\nstation .ironGearWheel\n```\n\n![](figures/gear-station.png)\n\nIf you want to have multiple stations connected, you can call:\n\n```lean\nassemblyLine .ironGearWheel 3\n```\n\n![](figures/gear-assembly-line.png)\n\nCalling `assemblyLine` is similar to just replicating the station multiple times, but the `assemblyLine` command\nwill also automatically insert output load balancers, roboports, passive provider chests, and big electric poles if desired.\n\nTo get a factory that can be attached to a bus, call:\n\n```lean\nbusAssemblyLine .ironGearWheel 3\n```\n\nCheckout the file `NauvisScience150.lean` for a full factory that uses the assembly line library for all its needs.\nYou can generate a blueprint as follows:\n\n```bash\n./nauvis-150.sh > blueprint.txt\n```\n\nWhich looks like this:\n\n![](figures/nauvis150.png)\n\nAnd as promised, produces 150 of all the Nauvis sciences per minutes.\n\n![](figures/nauvis150-stats.png)\n\n# Configuration\n\nYou can configure how Functorio generates certain aspects of your factory, by providing a `Config` instance.\n\n```lean\ninstance : Config where\n  generateRoboports := true   -- assembly lines add roboports\n  generateBigPoles := true    -- assembly lines add big poles\n  providerChestCapacity := 5  -- assembly lines add provider chests for outputs\n  adapterMinHeight := 3       -- minimum height of adapters\n```\n\n# Debugging\n\nSometimes you can't convince Lean that two factories have the same interface, even though you know that they do.\nYou can use the `factory.unsafeCastFactory` to change the interface of your factory to whatever you want.\n\nLean isn't all that great a printing types. For example, Lean will print the type of\n`busAssemblyLine .processingUnit 14` as `BusAssemblyLineType (getRecipe .processingUnit) 14`.\nThis is really quite useless. If you want to know what that type expands to, you can run\nthe following code, and see the simplified type in Lean's InfoView.\n\n```lean\ndef x : BusAssemblyLineType RecipeName.processingUnit 14 := by\n  simp!\n```\n\nWhich is:\n\n```lean\nBusLane Ingredient.sulfuricAcid (525, 1) →\nBusLane Ingredient.electronicCircuit (2100, 1) →\nBusLane Ingredient.advancedCircuit (210, 1) →\nBus (BusLane Ingredient.processingUnit (105, 1))\n```\n\nIf a test `#guard TEST.toAscii = OUTPUT` fails you can run this command to see by the new output:\n\n```\n#eval IO.print (TEST).toAscii\n```\n\n# Future Work\n\nThis library is still in early alpha. Let's make it better together! Expect bugs!\nFor any problems, please reach out on [discord](https://discord.gg/UGEcqxpSMn), file a ticket on github or contact me via email.\nIf you have a Lean problem, there are many really helpful people in this [chat](https://leanprover.zulipchat.com/).\n\nThere are still a lot of things that need modeling: e.g. other kinds of belts and inserters,\nmodules, beaconized factories, quality, rails, etc. I'm happy to take contributions!\n"
  },
  {
    "path": "RedScience150.lean",
    "content": "import Functorio\n\ndef makeIron : IronOre 300 -> Bus (Iron 300) :=\n  busAssemblyLine (recipe .ironPlate) 8\n\ndef makeCopper : CopperOre 150 -> Bus (Copper 150) :=\n  busAssemblyLine (recipe .copperPlate) 4\n\ndef makeGear : Iron 300 -> Bus (Gear 150) :=\n  busAssemblyLine (recipe .ironGearWheel) 1\n\ndef makeRedScience : Copper 150 -> Gear 150 -> Bus (RedScience 150) :=\n  busAssemblyLine (recipe .automationSciencePack) 10\n\ndef redScience := bus do\n  let ironOre <- input .ironOre 300\n  let copperOre <- input .copperOre 150\n\n  let iron : Iron 300 <- makeIron ironOre\n  let copper : Copper 150 <- makeCopper copperOre\n  let gear : Gear 150 <- makeGear iron\n  let _science : RedScience 150 <- makeRedScience copper gear\n\ndef main : IO Unit :=\n  IO.println (redScience.toBlueprint) --  (bootstrap := true))\n"
  },
  {
    "path": "Rocket.lean",
    "content": "import Functorio\n\ninstance : Config where\n  generateBigPoles := true\n  generateRoboports := true\n  providerChestCapacity := 0\n  adapterMinHeight := 3\n\ndef makeLowDensityStructure : Steel 40 -> Copper 400 -> Plastic 100 -> Bus (LowDensityStructure 20) :=\n  busAssemblyLine (recipe .lowDensityStructure) 4\n\ndef makeAcid : Water 6000 -> Sulfur 300 -> Iron 60 -> Bus (Acid 3000) :=\n  busAssemblyLine (recipe RecipeName.sulfuricAcid) 1\n\ndef makeCopper : CopperOre 1500 -> Bus (Copper 1500) :=\n  busAssemblyLine (recipe .copperPlate) 40\n\ndef makeIron : IronOre 900 -> Bus (Iron 900) :=\n  busAssemblyLine (recipe .ironPlate) 24\n\ndef makeSteel : Iron 225 -> Bus (Steel 45) :=\n  busAssemblyLine (recipe .steelPlate) 6\n\ndef makeSulfur : Water 7200 -> Petrolium 7200 -> Bus (Sulfur 480) :=\n  busAssemblyLine (recipe .sulfur) 4\n\ndef makePlastic : Petrolium 2400 -> Coal 120 -> Bus (Plastic 240) :=\n  busAssemblyLine (recipe .plasticBar) 2\n\ndef makeCable : Copper 1050 -> Bus (Cable 2100) :=\n  busAssemblyLine (recipe .copperCable) 7\n\ndef makeGreenCircuit : Iron 600 -> Cable 1800 -> Bus (GreenCircuit 600) :=\n  busAssemblyLine (recipe .electronicCircuit) 4\n\ndef makeRedCircuit : GreenCircuit 100 -> Plastic 100 -> Cable 200 -> Bus (RedCircuit 50) :=\n  busAssemblyLine (recipe .advancedCircuit) 4\n\ndef makeBlueCircuit : Acid (225/2) -> GreenCircuit 450 -> RedCircuit 45 -> Bus (BlueCircuit (45/2)) :=\n  busAssemblyLine (recipe .processingUnit) 3\n\ndef makeSolidFuel : LightOil 2400 -> Bus (SolidFuel 240) :=\n  busAssemblyLine (recipe .solidFuelFromLightOil) 4\n\ndef makeRocketFuel : LightOil 200 -> SolidFuel 200 -> Bus (RocketFuel 20) :=\n  busAssemblyLine (recipe .rocketFuel) 4\n\ndef makeRocket : BlueCircuit 20 -> LowDensityStructure 20 -> RocketFuel 20 -> Bus Unit :=\n  busAssemblyLine (recipe .rocketPart) 1\n\ndef rocketFactory := bus do\n  let copper <- input .copperPlate 1500\n  let iron <- input .ironPlate 900\n  let coal <- input .coal 120\n  let water <- input .water 13200\n  let petrol <- input .petroleumGas 9600\n  let lightOil <- input .lightOil 2600\n\n  let (water0, water1) <- split (left:=7200) water\n  let (petrol0, petrol1) <- split (left:=7200) petrol\n  let (lightOil0, lightOil1) <- split lightOil\n\n  let (copper0, copper1) <- split copper\n\n  let (iron0, iron) <- split iron\n  let (iron1, iron2) <- split iron\n  let steel <- makeSteel iron0\n\n  let sulfur <- makeSulfur water0 petrol0\n  let acid <- makeAcid water1 sulfur.less iron1\n  let plastic <- makePlastic petrol1 coal\n  let (plastic0, plastic1) <- split plastic\n\n  let lowDensityStruct <- makeLowDensityStructure steel.less copper0 plastic0\n\n  let solidFuel <- makeSolidFuel lightOil0\n  let rocketFuel <- makeRocketFuel lightOil1 solidFuel.less\n\n  let cable <- makeCable copper1.less\n  let (cable0, cable1) <- split cable\n  let greenCircuit <- makeGreenCircuit iron2.less cable0\n  let (greenCircuit0, greenCircuit1) <- split greenCircuit\n  let redCircuit <- makeRedCircuit greenCircuit0 plastic1.less cable1.less\n  let blueCircuit <- makeBlueCircuit acid.less greenCircuit1.less redCircuit.less\n\n  makeRocket blueCircuit.less lowDensityStruct rocketFuel\n\ndef main : IO Unit :=\n  IO.println (rocketFactory.toBlueprint) --  (bootstrap := true))\n"
  },
  {
    "path": "Spaceship.lean",
    "content": "import Functorio\n\ninstance : Config where\n  adapterMinHeight := 3\n\ndef makeWater : Ice 240 -> Bus (Water 4800) :=\n  busAssemblyLine (recipe .iceMelting) 4\n\ndef makeFuel : Water 1200 -> Carbon 24 -> Calcite 12 -> Bus (ThrusterFuel 18000) :=\n  busAssemblyLine (recipe .advancedThrusterFuel) 2\n\ndef makeOxidizer : Water 1200 -> IronOre 24 -> Calcite 12 -> Bus (ThrusterOxidizer 18000) :=\n  busAssemblyLine (recipe .advancedThrusterOxidizer) 2\n\ndef meltIronOre : IronOre 750 -> Calcite 15 -> Bus (MoltenIron 11250) :=\n  busAssemblyLine (recipe .moltenIron) 2\n\ndef meltCopperOre : CopperOre 750 -> Calcite 15 -> Bus (MoltenCopper 11250) :=\n  busAssemblyLine (recipe .moltenCopper) 2\n\ndef castIronPlate : MoltenIron 3000 -> Bus (Iron 450) :=\n  busAssemblyLine (recipe .castingIron) 2\n\ndef castCable : MoltenCopper 2400 -> Bus (Cable 1440) :=\n  busAssemblyLine (recipe .castingCopperCable) 2\n\ndef castSteel : MoltenIron 4500 -> Bus (Steel 225) :=\n  busAssemblyLine (recipe .castingSteel) 2\n\ndef makeYellowAmmo : Iron 300 -> Bus (YellowAmmo 75) :=\n  busAssemblyLine (recipe .firearmMagazine) 1\n\ndef makeCoal : Water 600 -> Carbon 300 -> Sulfur 60 -> Bus (Coal 60) :=\n  busAssemblyLine (recipe .coalSynthesis) 2\n\ndef makeExplosives : Water 300 -> Sulfur 30 -> Coal 30 -> Bus (Explosives 60) :=\n  busAssemblyLine (recipe .explosives) 2\n\ndef makeRockets : Explosives (75/2) -> Iron 75 -> Bus (Rocket (75/2)) :=\n    busAssemblyLine (recipe .rocket) 2\n\ndef makeRailgunAmmo : Steel 30 -> Cable 60 -> Explosives 12 -> Bus (RailgunAmmo 6) :=\n    busAssemblyLine (recipe .railgunAmmo) 2\n\ndef spaceship := bus do\n  let ice <- input .ice 240\n  let sulfur <- input .sulfur 90\n  let calcite <- input .calcite 54\n  let ironOre <- input .ironOre 774\n  let copperOre <- input .copperOre 750\n  let carbon <- input .carbon 324\n\n  let (calcite0, calcite) <- split calcite\n  let (calcite1, calcite) <- split calcite\n  let (calcite2, calcite3) <- split calcite\n\n  let (ironOre0, ironOre1) <- split ironOre\n  let (carbon0, carbon1) <- split carbon\n  let (sulfur0, sulfur1) <- split sulfur\n\n  let water <- makeWater ice\n  let (water0, water) <- split water\n  let (water1, water) <- split water\n  let (water2, water3) <- split water\n\n  let coal <- makeCoal water0 carbon0 sulfur0\n  let explosives <- makeExplosives water1 sulfur1 coal.less\n  let (explosives0, explosives1) <- split explosives\n\n  let moltenCopper <- meltCopperOre copperOre calcite0\n  let cable <- castCable moltenCopper.less\n\n  let moltenIron <- meltIronOre ironOre0 calcite1\n  let (moltenIron0, moltenIron1) <- split moltenIron\n  let ironPlate <- castIronPlate moltenIron0\n  let (ironPlate0, ironPlate1) <- split ironPlate\n  let steel <- castSteel moltenIron1.less\n\n  let railgunAmmo <- makeRailgunAmmo steel.less cable.less explosives0\n  let rockets <- makeRockets explosives1.less ironPlate0\n  let bullets <- makeYellowAmmo ironPlate1.less\n\n  let fuel <- makeFuel water2 carbon1 calcite2\n  let oxidizer <- makeOxidizer water3.less ironOre1.less calcite3\n\ndef main : IO Unit :=\n  IO.println (spaceship.toBlueprint)\n"
  },
  {
    "path": "fulgora-150.sh",
    "content": "#!/bin/bash\n\nlake build fulgora-150 > /dev/null\necho -n 0; .lake/build/bin/fulgora-150 | pigz -zc | base64 -w0; echo\n"
  },
  {
    "path": "generate-recipe.py",
    "content": "#!/usr/bin/env python3\n\n# Generates the Recipe.lean file from a factorio data dump, get it by passing --dump-data to factorio\n\nfrom fractions import Fraction\nimport json\n\ndef to_camel_case(kebab_case_str: str) -> str:\n    parts = kebab_case_str.split('-')\n    return parts[0] + ''.join(x.capitalize() for x in parts[1:])\n\ndef float_to_fraction(f: float) -> str:\n    return str(Fraction(f).limit_denominator(1000)) \n\nwith open('data-raw-dump.json', 'r') as f:\n    data = json.load(f)\n\nbuildings_data = data['assembling-machine'] | data['furnace'] | data['rocket-silo']\nrecipes_data = data['recipe']\ningredients_data = data['item'] | data['ammo'] | data['fluid'] | data['item-with-entity-data'] | data['tool'] | data['capsule'] | data['gun'] | data['module'] | data['armor']\n\nrecipe_names = [key for key in recipes_data.keys() if key != \"recipe-unknown\"]\nbuilding_names = list(buildings_data.keys())\ningredient_names = set()\ncategories = set() \n\nfor name in recipe_names:\n    recipe = recipes_data[name]\n    categories.add(recipe.get('category', 'crafting'))\n\n    for ingredient in recipe.get('ingredients', []):\n        ingredient_names.add(ingredient['name'])\n\n    for ingredient in recipe.get('results', []):\n        ingredient_names.add(ingredient['name'])\n  \nfor building in buildings_data.values():\n    for category in building[\"crafting_categories\"]:\n        categories.add(category)\n\n# Build the Lean file content as a list of strings\nlean_code = []\n\n# Header\nlean_code.append(\"-- Generated by generate-recipe.py. Do not modify.\\n\")\nlean_code.append(\"import Functorio.Fraction\\n\")\nlean_code.append(\"import Functorio.Direction\\n\")\n\n# Ingredient inductive type\nlean_code.append(\"inductive Ingredient\")\nfor ingredient in sorted(ingredient_names):\n    lean_code.append(f\"  | {to_camel_case(ingredient)}\")\nlean_code.append(\"  deriving DecidableEq, Repr, Inhabited\\n\")\n\n# Categories inductive type\nlean_code.append(\"inductive RecipeCategory\")\nfor category in sorted(list(categories)):\n    lean_code.append(f\"  | {to_camel_case(category)}\")\nlean_code.append(\"  deriving DecidableEq, Repr, Inhabited\\n\")\n\n# isLiquid function\nlean_code.append(\"namespace Ingredient\\n\")\nlean_code.append(\"def isLiquid : Ingredient -> Bool\")\nfor name in sorted(list(ingredient_names)):\n    if ingredients_data.get(name, {}).get('type') == 'fluid':\n        lean_code.append(f\"| .{to_camel_case(name)} => true\")\nlean_code.append(f\"| _ => false\\n\")\n\nlean_code.append(\"def spoilResult : Ingredient -> Option Ingredient\")\nfor name in sorted(list(ingredient_names)):\n    try:\n        spoil_result = ingredients_data[name]['spoil_result']\n        lean_code.append(f\"| .{to_camel_case(name)} => .some {to_camel_case(spoil_result)}\")\n    except Exception:\n        continue\nlean_code.append(f\"| _ => .none\\n\")\n\nlean_code.append(\"def name : Ingredient -> String\")\nfor name in sorted(ingredient_names):\n    lean_code.append(f'| .{to_camel_case(name)} => \"{name}\"')\n\nlean_code.append(\"\\nend Ingredient\")\n\n# Static Recipe structure\nlean_code.append(\"\"\"\nstructure Recipe where\n  name: String\n  -- The `Fraction` indicates how many items are needed to execute the recipe.\n  inputs : List (Fraction × Ingredient)\n  -- The `Fraction` indicates how many output items are generated by executing the recipe.\n  outputs : List (Fraction × Ingredient)\n  category : RecipeCategory\n  -- Number of seconds that it takes the user to execute the recipe.\n  time : Fraction\n  deriving DecidableEq, Repr, Inhabited\n\ninductive FluidBoxType where\n  | input\n  | output\n  | inputOutput\n  deriving DecidableEq, Repr, Inhabited\n\nstructure FluidBox where\n  side: Direction\n  offset: Nat\n  type: FluidBoxType\n  deriving DecidableEq, Repr, Inhabited\n\nstructure FabricatorDetails where\n  name : String\n  width : Nat\n  height : Nat\n  speedup : Fraction\n  productivity : Fraction\n  moduleSlots : Nat\n  fluidBoxes : List FluidBox\n  categories : List RecipeCategory\n  deriving DecidableEq, Repr, Inhabited\n\"\"\")\n\n# RecipeName inductive type\nlean_code.append(\"inductive RecipeName\")\nfor name in sorted(recipe_names):\n    lean_code.append(f\"  | {to_camel_case(name)}\")\nlean_code.append(\"  deriving DecidableEq, Repr, Inhabited\\n\")\n\n# getRecipe function definition\nlean_code.append(\"namespace RecipeName\\n\")\nlean_code.append(\"def getRecipe : RecipeName -> Recipe\")\nfor name in sorted(recipe_names):\n    recipe = recipes_data[name]\n    camel_case_name = to_camel_case(name)\n    try:\n        inputs = recipe.get('ingredients', [])\n        outputs = recipe.get('results', [])\n        category = recipe.get('category', 'crafting')\n        time = recipe.get('energy_required', 0.5)\n    except Exception as e:\n        print(f\"{name} not supported because of {e}\")\n\n    # sort fluids before solids    \n    inputs = [i for i in inputs if i['type'] == 'fluid'] + \\\n             [i for i in inputs if i['type'] != 'fluid'] \n\n    # large solid outputs come first\n    solidOutputs = [i for i in outputs if i['type'] != 'fluid']\n    solidOutputs.sort(reverse=True, key=lambda a: a['amount'])\n    liquidOutputs = [i for i in outputs if i['type'] == 'fluid']\n\n    outputs = solidOutputs + liquidOutputs\n\n    # Rocket parts cannot be removed from the rocket silo.\n    if name == \"rocket-part\":\n        outputs = []\n\n    lean_code.append(f\"| .{camel_case_name} => {{\")\n    lean_code.append(f'  name := \"{name}\",')\n\n    inputs_list = [\n        f\"({float_to_fraction(ingredient['amount'])}, .{to_camel_case(ingredient['name'])})\"\n        for ingredient in inputs\n    ]\n    lean_code.append(f\"  inputs := [{', '.join(inputs_list)}],\")\n\n    outputs_list = [\n        f\"({float_to_fraction(product['amount'] * product.get('probability', 1))}, .{to_camel_case(product['name'])})\"\n        for product in outputs\n    ]\n    lean_code.append(f\"  outputs := [{', '.join(outputs_list)}],\")\n    \n    lean_code.append(f\"  category := .{to_camel_case(category)}\")\n    lean_code.append(f\"  time := {float_to_fraction(time)}\")\n    \n    lean_code.append(\"}\")\n    \nlean_code.append(\"\\nend RecipeName\\n\")\n\n# Fabricators\nlean_code.append(\"inductive Fabricator\")\nfor building_name in sorted(building_names):\n    building = buildings_data[building_name]\n    lean_code.append(f\"  | {to_camel_case(building['name'])}\")\nlean_code.append(\"  deriving DecidableEq, Repr, Inhabited\\n\")\n\n# Fabricator details\nlean_code.append(\"namespace Fabricator\\n\")\n\nlean_code.append(\"@[simp]\")\nlean_code.append(\"def details : Fabricator -> FabricatorDetails\")\nfor building_name in sorted(building_names):\n    building = buildings_data[building_name]\n    ((x0,y0),(x1,y1)) = building[\"selection_box\"]\n    productivity = building.get('effect_receiver', {}).get(\"base_effect\", {}).get(\"productivity\", 0)\n\n    lean_code.append(f\"| .{to_camel_case(building['name'])} => {{\")\n    lean_code.append(f'  name := \"{building[\"name\"]}\"')\n    lean_code.append(f\"  speedup := {float_to_fraction(building['crafting_speed'])}\")\n    lean_code.append(f\"  productivity := {float_to_fraction(productivity)}\")\n    lean_code.append(f\"  moduleSlots := {building.get('module_slots', 0)}\")\n    lean_code.append(f\"  width := {int(x1 - x0)}\")\n    lean_code.append(f\"  height := {int(y1 - y0)}\")    \n\n    lean_code.append(f\"  fluidBoxes := [\")\n    for box in building.get(\"fluid_boxes\", []):\n        info = box[\"pipe_connections\"][0]\n        dir = {0: 'N', 4: 'E', 8: 'S', 12: 'W'}[info['direction']]\n        (x,y) = info[\"position\"]\n        offset = int({\n            'N': x - x0 - 0.5,\n            'E': y - y0 - 0.5,\n            'S': x - x0 - 0.5,\n            'W': y - y0 - 0.5,\n        }[dir])\n        lean_code.append(\"    {\")\n        lean_code.append(f\"      offset := {offset}\")\n        lean_code.append(f\"      side := .{dir}\")\n        lean_code.append(f\"      type := .{to_camel_case(info['flow_direction'])}\")\n        lean_code.append(\"    },\")\n    lean_code.append(\"  ]\")\n    \n    lean_code.append(f\"  categories := [\")\n    for category in sorted(building[\"crafting_categories\"]):\n        lean_code.append(f\"    .{to_camel_case(category)},\")\n    lean_code.append(\"  ]\")\n    lean_code.append(\"}\")\n\nlean_code.append(\"\"\"\ndef name (f:Fabricator) := f.details.name\n\ndef width (f:Fabricator) := f.details.width\n\ndef height (f:Fabricator) := f.details.height\n\n@[simp]\ndef speedup (f:Fabricator) := f.details.speedup\n\n@[simp]\ndef productivity (f:Fabricator) := f.details.productivity\n\n@[simp]\ndef moduleSlots (f:Fabricator) := f.details.moduleSlots\n\ndef fluidBoxes (f:Fabricator) := f.details.fluidBoxes\n\ndef handlesCategory (f:Fabricator) (c:RecipeCategory) :=\n  f.details.categories.contains c\n  \nend Fabricator\n\"\"\")\n\n# Write the content to the output file\nfile = \"Functorio/Recipe.lean\"\nwith open(file, 'w') as f:\n    f.write('\\n'.join(lean_code))\n\nprint(f\"✅ Successfully generated Lean file at '{file}'\")\n"
  },
  {
    "path": "gleba-300.sh",
    "content": "#!/bin/bash\n\nlake build gleba-300 > /dev/null\necho -n 0; .lake/build/bin/gleba-300 | pigz -zc | base64 -w0; echo\n\n\n"
  },
  {
    "path": "lake-manifest.json",
    "content": "{\"version\": \"1.1.0\",\n \"packagesDir\": \".lake/packages\",\n \"packages\": [],\n \"name\": \"functorio\",\n \"lakeDir\": \".lake\"}\n"
  },
  {
    "path": "lakefile.toml",
    "content": "name = \"functorio\"\nversion = \"0.1.0\"\ndefaultTargets = [\"nauvis-150\"]\n\n[leanOptions]\nautoImplicit = false\n\n[[lean_lib]]\nname = \"Functorio\"\n\n[[lean_exe]]\nname = \"nauvis-150\"\nroot = \"Nauvis150\"\n\n[[lean_exe]]\nname = \"red-science-150\"\nroot = \"RedScience150\"\n\n[[lean_exe]]\nname = \"rocket\"\nroot = \"Rocket\"\n\n[[lean_exe]]\nname = \"fulgora-150\"\nroot = \"Fulgora150\"\n\n[[lean_exe]]\nname = \"gleba-300\"\nroot = \"Gleba300\"\n\n[[lean_exe]]\nname = \"spaceship\"\nroot = \"Spaceship\""
  },
  {
    "path": "lean-toolchain",
    "content": "leanprover/lean4:v4.20.1\n"
  },
  {
    "path": "nauvis-150.sh",
    "content": "#!/bin/bash\n\nlake build nauvis-150 > /dev/null\necho -n 0; .lake/build/bin/nauvis-150 | pigz -zc | base64 -w0; echo\n"
  },
  {
    "path": "red-science-150.sh",
    "content": "#!/bin/bash\n\nlake build red-science-150 > /dev/null\necho -n 0; .lake/build/bin/red-science-150 | pigz -zc | base64 -w0; echo\n"
  },
  {
    "path": "rocket.sh",
    "content": "#!/bin/bash\n\nlake build rocket > /dev/null\necho -n 0; .lake/build/bin/rocket | pigz -zc | base64 -w0; echo\n"
  },
  {
    "path": "spaceship.sh",
    "content": "#!/bin/bash\n\nlake build spaceship > /dev/null\necho -n 0; .lake/build/bin/spaceship | pigz -zc | base64 -w0; echo\n"
  }
]