<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.logic.world/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Felipe</id>
	<title>Logic World Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.logic.world/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Felipe"/>
	<link rel="alternate" type="text/html" href="https://wiki.logic.world/wiki/Special:Contributions/Felipe"/>
	<updated>2026-05-15T13:38:46Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Blotter_File_Format/v7&amp;diff=779</id>
		<title>Blotter File Format/v7</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Blotter_File_Format/v7&amp;diff=779"/>
		<updated>2025-10-13T00:57:01Z</updated>

		<summary type="html">&lt;p&gt;Felipe: add choices section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Blotter File Format, successor to the format known as &amp;quot;.tung files&amp;quot;, is a system for storing the components and wires of a Logic World world. It is used for both full world saves as well as partial worlds (the data structure used by Subassemblies), but there are slight differences between the two.&lt;br /&gt;
&lt;br /&gt;
= File types =&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;World files:&#039;&#039;&#039;&lt;br /&gt;
** Represent a full, playable world space&lt;br /&gt;
** May have zero or more root components at different positions/rotations in the world&lt;br /&gt;
** Use &amp;lt;code&amp;gt;.logicworld&amp;lt;/code&amp;gt; file extension&lt;br /&gt;
** All circuit states are stored contiguously, from circuit state 0 through the highest state.&lt;br /&gt;
* &#039;&#039;&#039;PartialWorld files:&#039;&#039;&#039;&lt;br /&gt;
** Represent a substructure within a playable world space; can be loaded and added to a world with each root at an arbitrary position/rotation&lt;br /&gt;
** Must have at least one root component&lt;br /&gt;
** Use &amp;lt;code&amp;gt;.partialworld&amp;lt;/code&amp;gt; file extension&lt;br /&gt;
** Has a list of circuit state indexes that are On. All indexes not listed here are inferred as being Off.&lt;br /&gt;
&lt;br /&gt;
== File structure ==&lt;br /&gt;
&lt;br /&gt;
* [[#Header|Header]] (16 bytes)&lt;br /&gt;
* &#039;&#039;&#039;Save info&#039;&#039;&#039;&lt;br /&gt;
** [[#Save_Format_Version|Save Format Version]] (1 byte)&lt;br /&gt;
** [[#Game_version|Game version]] (16 bytes)&lt;br /&gt;
** [[#Save_type|Save type]] (1 byte)&lt;br /&gt;
** [[#Component_and_wire_counts|Number of components and wires]] (8 bytes)&lt;br /&gt;
** [[#Mod_versions|Mod versions]] (4+ bytes, dependent on the number of mods in the save)&lt;br /&gt;
** [[#Component_IDs_Map|Component IDs Map]] (4+ bytes, dependent on the number of loaded component IDs)&lt;br /&gt;
* &#039;&#039;&#039;Object data&#039;&#039;&#039;&lt;br /&gt;
** [[#Components_data|Components data]] (0+ bytes, dependent on the number of components)&lt;br /&gt;
** [[#Wires_data|Wires data]] (0+ bytes, dependent on the number of wires)&lt;br /&gt;
** [[#Circuit_states|Circuit states]] (4+ bytes, variable, has a different format for Worlds and PartialWorlds)&lt;br /&gt;
* [[#Footer|Footer]] (16 bytes)&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Save Info&amp;quot; data is at the beginning of the file so that it is easy to extract as metadata. This is done by the game itself in a few places, as well as logicworld.net when uploading a save there.&lt;br /&gt;
&lt;br /&gt;
== Header and footer ==&lt;br /&gt;
&lt;br /&gt;
=== Header ===&lt;br /&gt;
The first 16 bytes of every Blotter file are &amp;lt;code&amp;gt;4C-6F-67-69-63-20-57-6F-72-6C-64-20-73-61-76-65&amp;lt;/code&amp;gt;, which is UTF-8 for “Logic World save”.&lt;br /&gt;
&lt;br /&gt;
=== Footer ===&lt;br /&gt;
The final 16 bytes of every Blotter file are &amp;lt;code&amp;gt;72-65-64-73-74-6F-6E-65-20-73-75-78-20-6C-6F-6C&amp;lt;/code&amp;gt; , which is UTF-8 for “redstone sux lol”.&lt;br /&gt;
&lt;br /&gt;
The header and footer exist to protect against save corruption. If the file does not have them, we know that something is wrong with the file.&lt;br /&gt;
&lt;br /&gt;
== Save Format Version ==&lt;br /&gt;
One byte specifies the save format version. Currently this is &amp;lt;code&amp;gt;07&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Game version ==&lt;br /&gt;
16 bytes store the game [[#version|version]] that this save was created in.&lt;br /&gt;
&lt;br /&gt;
== Save type ==&lt;br /&gt;
There are two types of file that use the Blotter format, Worlds and PartialWorlds. One byte specifies which type of file this is.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Byte&lt;br /&gt;
!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt;&lt;br /&gt;
|Unknown/error&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt;&lt;br /&gt;
|World&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt;&lt;br /&gt;
|PartialWorld&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;FF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Reserved for future use&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Component and wire counts ==&lt;br /&gt;
Here there are two [[#int|ints]]. The first specifies the number of components in the save, the second specifies the number of wires.&lt;br /&gt;
&lt;br /&gt;
== Mod versions ==&lt;br /&gt;
Mods will be included if components they provide are used in this save. This is so that if a later mod version has changes, older save files can be converted.&lt;br /&gt;
&lt;br /&gt;
* An [[#int|int]] for the number of mod versions stored in this file&lt;br /&gt;
* For each mod version:&lt;br /&gt;
** A [[#string|string]] for the text ID of the mod&lt;br /&gt;
** A [[#version|version]] for the version of the mod that this save was created with&lt;br /&gt;
&lt;br /&gt;
== Component IDs Map ==&lt;br /&gt;
When humans add component types to Logic World, they use text IDs like &amp;lt;code&amp;gt;MHG.CircuitBoard&amp;lt;/code&amp;gt;. To make save files and network packets small in size, the game internally uses numeric IDs to reference component types. The relationship between text IDs and numeric IDs is not deterministic or consistent between different save files, so the map from text IDs to numeric IDs must be stored with the save.&lt;br /&gt;
&lt;br /&gt;
First we write an [[#int|int]] for the number of component IDs in this file.&lt;br /&gt;
&lt;br /&gt;
One by one the mappings are written to the file:&lt;br /&gt;
&lt;br /&gt;
* A 2-byte unsigned integer for the numeric ID of the component&lt;br /&gt;
* A [[#string|string]] for the text ID of the component&lt;br /&gt;
&lt;br /&gt;
== Components data ==&lt;br /&gt;
One by one, every component is listed in the file. The first component must be a root component -- i.e. have an address of &amp;lt;code&amp;gt;C-0&amp;lt;/code&amp;gt; for its parent -- and every subsequent component must have a parent that was listed before it.&lt;br /&gt;
&lt;br /&gt;
It is explicitly okay to have a World file with no components in it (i.e. an empty world). However, a PartialWorld file must have at least one component.&lt;br /&gt;
&lt;br /&gt;
Each component is written like so:&lt;br /&gt;
&lt;br /&gt;
* The [[#component_address|component address]] of this component&lt;br /&gt;
* The [[#component_address|component address]] of the component&#039;s parent&lt;br /&gt;
* 2 bytes for the numeric ID of the component&#039;s type (see [[#Component_IDs_Map|Component IDs Map]])&lt;br /&gt;
* Three [[#int|ints]] for the local fixed position of the component; &amp;lt;code&amp;gt;x y z&amp;lt;/code&amp;gt;&lt;br /&gt;
** One unit in the fixed position corresponds to a distance of 0.001 game units, or 1mm&lt;br /&gt;
* Four [[#float|floats]] for the local rotation of the component as a quaternion; &amp;lt;code&amp;gt;x y z w&amp;lt;/code&amp;gt;&lt;br /&gt;
* Information about the component&#039;s inputs:&lt;br /&gt;
** An [[#int|int]] for the number of inputs the component has&lt;br /&gt;
** For each input:&lt;br /&gt;
*** An [[#int|int]] for the input&#039;s circuit state ID&lt;br /&gt;
* Information about the component&#039;s outputs:&lt;br /&gt;
** An [[#int|int]] for the number of outputs the component has&lt;br /&gt;
** For each output:&lt;br /&gt;
*** An [[#int|int]] for the output&#039;s circuit state ID&lt;br /&gt;
* The component&#039;s custom data:&lt;br /&gt;
** An [[#int|int]] for the number of bytes in the custom data&lt;br /&gt;
*** If the component has no custom data, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; may be written instead of &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;&lt;br /&gt;
** All the bytes of the custom data, in order&lt;br /&gt;
&lt;br /&gt;
== Wires data ==&lt;br /&gt;
One by one, every wire is listed in the file.&lt;br /&gt;
&lt;br /&gt;
* The [[#peg_address|peg address]] of the wire&#039;s first point&lt;br /&gt;
* The [[#peg_address|peg address]] of the wire&#039;s second point&lt;br /&gt;
* An [[#int|int]] for the wire&#039;s circuit state ID&lt;br /&gt;
* A [[#float|float]] for the wire&#039;s rotation, relative to the default rotation it would have&lt;br /&gt;
&lt;br /&gt;
We do not need to save wire addresses; nothing in the save file needs to reference them, and there is no need for them to be consistent between save loads. Instead, wire addresses are dynamically assigned when the save is loaded.&lt;br /&gt;
&lt;br /&gt;
== Circuit states ==&lt;br /&gt;
We&#039;ve saved the circuit state IDs of all the pegs and wires in the save. Now we need to save the values of those state IDs, so states can be looked up.&lt;br /&gt;
&lt;br /&gt;
A state is either on or off. At runtime, the game stores all circuit states from zero through the highest state in a contiguous block of memory.&lt;br /&gt;
&lt;br /&gt;
=== World files ===&lt;br /&gt;
Worlds are large, and generally use most circuit states, starting from state 0. All circuit states are listed.&lt;br /&gt;
&lt;br /&gt;
* First an [[#int|int]] for the number of bytes in this sequence&lt;br /&gt;
* Then, that number of bytes. Each bit represents the value of the next circuit state; each byte contains 8 states.&lt;br /&gt;
** If, when saving the game, the number of circuit states is not divisible by 8, the last byte will be padded.&lt;br /&gt;
&lt;br /&gt;
=== PartialWorld files ===&lt;br /&gt;
PartialWorlds represent a subset of a World. Most of the circuit states in a world will be irrelevant to a particular PartialWorld, so it would be silly and wasteful to save all of them with every PartialWorld.&lt;br /&gt;
Instead, we save a list of circuit state IDs which are both used in this PartialWorld and have a state of on. All unlisted state IDs are assumed to be in the off state.&lt;br /&gt;
&lt;br /&gt;
* First an [[#int|int]] for the number of ints in this sequence&lt;br /&gt;
* Then, that number of ints. Each int signifies a state ID with a value of on.&lt;br /&gt;
&lt;br /&gt;
== Sub-standards ==&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Int&#039;&#039;&#039; (4 bytes) &amp;lt;span id=&amp;quot;int&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
32 bit signed integers, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Float&#039;&#039;&#039; (4 bytes) &amp;lt;span id=&amp;quot;float&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
32 bit floating point values, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Boolean&#039;&#039;&#039; (1 byte) &amp;lt;span id=&amp;quot;boolean&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
An entire byte that is either &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; for false or &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; for true.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;String&#039;&#039;&#039; (4+ bytes) &amp;lt;span id=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
An int for the number of bytes in the string, followed by that number of bytes, representing that string in UTF-8.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Version&#039;&#039;&#039; (16 bytes) &amp;lt;span id=&amp;quot;version&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
Versions contain four numbers (i.e. 1.0.3.1069). These four numbers are written to the file in order, each as a 4-byte int. Thus, 4x4 bytes = 16 bytes for this. Note: if any of these four numbers are missing, they will be saved as &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;. So if your mod defines its version as &amp;lt;code&amp;gt;1.2&amp;lt;/code&amp;gt;, the four numbers will be &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;. This is not our fault, it&#039;s just how System.Version works.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Component Address&#039;&#039;&#039; &amp;lt;span id=&amp;quot;component_address&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; (4 bytes) ====&lt;br /&gt;
Component Addresses are written as 32 bit unsigned integers, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Peg Address&#039;&#039;&#039; (9 bytes) &amp;lt;span id=&amp;quot;peg_address&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
* One byte to indicate the peg type:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Byte&lt;br /&gt;
!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt;&lt;br /&gt;
|Undefined&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt;&lt;br /&gt;
|Input peg&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt;&lt;br /&gt;
|Output peg&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;FF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Reserved for future use&lt;br /&gt;
|}&lt;br /&gt;
Any value other than &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt; is invalid.&lt;br /&gt;
&lt;br /&gt;
* A component address for the component of the peg&lt;br /&gt;
* An int for the zero-based index of the peg (i.e. is it the 0th input, the 1st input, etc)&lt;br /&gt;
&lt;br /&gt;
== A note on seemingly-bizarre design choices ==&lt;br /&gt;
This file structure is not the most efficient, most beautiful, or most logical way to lay out the data. The primary purpose of the design of the Blotter format is that it is easy to integrate them with the game code. Therefore, the format uses the exact same data structures used by the game. This makes it fast and simple to convert the game state to a save file and vice versa, but since these data structures are optimized for runtime, the files do look a little weird.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;quot;Why are circuit states stored so weirdly? Why not directly save the state of an input/output, instead of a reference to a state index?&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
** This is how the game stores circuit states at runtime, for performance and for ease of synchronization between the client and server. Storing it this way in the file makes for easy interoperability with the runtime game code, and allows us to use the same component/wire data structures in the saves and in the game.&lt;br /&gt;
* &#039;&#039;&#039;&amp;quot;Must all the circuit states be stored? Surely you only need the states of the inputs or outputs, and everything else (including the wires) can be inferred from those.&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
** This is true for the server, but the client has no ability to run a simulation and infer other states. However, the client needs to know all the states so that it can render a PartialWorld correctly before it is placed and added to the server&#039;s world/simulation. Storing all the states is also just simpler, and requires fewer steps when loading a save.&lt;br /&gt;
* &#039;&#039;&#039;&amp;quot;Why in god&#039;s name are all the integers signed when come on they really should be unsigned?&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
** Logic World is written in C#. The .NET runtime has a few weird quirks, and one of those quirks is that most integers are signed, even things that can never be below zero like array lengths and indexes. Since this file format is designed for maximum interoperability with the code, this quirk is copied, and we use signed ints a lot.&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Blotter_File_Format/v7&amp;diff=778</id>
		<title>Blotter File Format/v7</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Blotter_File_Format/v7&amp;diff=778"/>
		<updated>2025-10-13T00:54:49Z</updated>

		<summary type="html">&lt;p&gt;Felipe: /* Components data */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Blotter File Format, successor to the format known as &amp;quot;.tung files&amp;quot;, is a system for storing the components and wires of a Logic World world. It is used for both full world saves as well as partial worlds (the data structure used by Subassemblies), but there are slight differences between the two.&lt;br /&gt;
&lt;br /&gt;
= File types =&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;World files:&#039;&#039;&#039;&lt;br /&gt;
** Represent a full, playable world space&lt;br /&gt;
** May have zero or more root components at different positions/rotations in the world&lt;br /&gt;
** Use &amp;lt;code&amp;gt;.logicworld&amp;lt;/code&amp;gt; file extension&lt;br /&gt;
** All circuit states are stored contiguously, from circuit state 0 through the highest state.&lt;br /&gt;
* &#039;&#039;&#039;PartialWorld files:&#039;&#039;&#039;&lt;br /&gt;
** Represent a substructure within a playable world space; can be loaded and added to a world with each root at an arbitrary position/rotation&lt;br /&gt;
** Must have at least one root component&lt;br /&gt;
** Use &amp;lt;code&amp;gt;.partialworld&amp;lt;/code&amp;gt; file extension&lt;br /&gt;
** Has a list of circuit state indexes that are On. All indexes not listed here are inferred as being Off.&lt;br /&gt;
&lt;br /&gt;
== File structure ==&lt;br /&gt;
&lt;br /&gt;
* [[#Header|Header]] (16 bytes)&lt;br /&gt;
* &#039;&#039;&#039;Save info&#039;&#039;&#039;&lt;br /&gt;
** [[#Save_Format_Version|Save Format Version]] (1 byte)&lt;br /&gt;
** [[#Game_version|Game version]] (16 bytes)&lt;br /&gt;
** [[#Save_type|Save type]] (1 byte)&lt;br /&gt;
** [[#Component_and_wire_counts|Number of components and wires]] (8 bytes)&lt;br /&gt;
** [[#Mod_versions|Mod versions]] (4+ bytes, dependent on the number of mods in the save)&lt;br /&gt;
** [[#Component_IDs_Map|Component IDs Map]] (4+ bytes, dependent on the number of loaded component IDs)&lt;br /&gt;
* &#039;&#039;&#039;Object data&#039;&#039;&#039;&lt;br /&gt;
** [[#Components_data|Components data]] (0+ bytes, dependent on the number of components)&lt;br /&gt;
** [[#Wires_data|Wires data]] (0+ bytes, dependent on the number of wires)&lt;br /&gt;
** [[#Circuit_states|Circuit states]] (4+ bytes, variable, has a different format for Worlds and PartialWorlds)&lt;br /&gt;
* [[#Footer|Footer]] (16 bytes)&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Save Info&amp;quot; data is at the beginning of the file so that it is easy to extract as metadata. This is done by the game itself in a few places, as well as logicworld.net when uploading a save there.&lt;br /&gt;
&lt;br /&gt;
== Header and footer ==&lt;br /&gt;
&lt;br /&gt;
=== Header ===&lt;br /&gt;
The first 16 bytes of every Blotter file are &amp;lt;code&amp;gt;4C-6F-67-69-63-20-57-6F-72-6C-64-20-73-61-76-65&amp;lt;/code&amp;gt;, which is UTF-8 for “Logic World save”.&lt;br /&gt;
&lt;br /&gt;
=== Footer ===&lt;br /&gt;
The final 16 bytes of every Blotter file are &amp;lt;code&amp;gt;72-65-64-73-74-6F-6E-65-20-73-75-78-20-6C-6F-6C&amp;lt;/code&amp;gt; , which is UTF-8 for “redstone sux lol”.&lt;br /&gt;
&lt;br /&gt;
The header and footer exist to protect against save corruption. If the file does not have them, we know that something is wrong with the file.&lt;br /&gt;
&lt;br /&gt;
== Save Format Version ==&lt;br /&gt;
One byte specifies the save format version. Currently this is &amp;lt;code&amp;gt;07&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Game version ==&lt;br /&gt;
16 bytes store the game [[#version|version]] that this save was created in.&lt;br /&gt;
&lt;br /&gt;
== Save type ==&lt;br /&gt;
There are two types of file that use the Blotter format, Worlds and PartialWorlds. One byte specifies which type of file this is.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Byte&lt;br /&gt;
!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt;&lt;br /&gt;
|Unknown/error&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt;&lt;br /&gt;
|World&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt;&lt;br /&gt;
|PartialWorld&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;FF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Reserved for future use&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Component and wire counts ==&lt;br /&gt;
Here there are two [[#int|ints]]. The first specifies the number of components in the save, the second specifies the number of wires.&lt;br /&gt;
&lt;br /&gt;
== Mod versions ==&lt;br /&gt;
Mods will be included if components they provide are used in this save. This is so that if a later mod version has changes, older save files can be converted.&lt;br /&gt;
&lt;br /&gt;
* An [[#int|int]] for the number of mod versions stored in this file&lt;br /&gt;
* For each mod version:&lt;br /&gt;
** A [[#string|string]] for the text ID of the mod&lt;br /&gt;
** A [[#version|version]] for the version of the mod that this save was created with&lt;br /&gt;
&lt;br /&gt;
== Component IDs Map ==&lt;br /&gt;
When humans add component types to Logic World, they use text IDs like &amp;lt;code&amp;gt;MHG.CircuitBoard&amp;lt;/code&amp;gt;. To make save files and network packets small in size, the game internally uses numeric IDs to reference component types. The relationship between text IDs and numeric IDs is not deterministic or consistent between different save files, so the map from text IDs to numeric IDs must be stored with the save.&lt;br /&gt;
&lt;br /&gt;
First we write an [[#int|int]] for the number of component IDs in this file.&lt;br /&gt;
&lt;br /&gt;
One by one the mappings are written to the file:&lt;br /&gt;
&lt;br /&gt;
* A 2-byte unsigned integer for the numeric ID of the component&lt;br /&gt;
* A [[#string|string]] for the text ID of the component&lt;br /&gt;
&lt;br /&gt;
== Components data ==&lt;br /&gt;
One by one, every component is listed in the file. The first component must be a root component -- i.e. have an address of &amp;lt;code&amp;gt;C-0&amp;lt;/code&amp;gt; for its parent -- and every subsequent component must have a parent that was listed before it.&lt;br /&gt;
&lt;br /&gt;
It is explicitly okay to have a World file with no components in it (i.e. an empty world). However, a PartialWorld file must have at least one component.&lt;br /&gt;
&lt;br /&gt;
Each component is written like so:&lt;br /&gt;
&lt;br /&gt;
* The [[#component_address|component address]] of this component&lt;br /&gt;
* The [[#component_address|component address]] of the component&#039;s parent&lt;br /&gt;
* 2 bytes for the numeric ID of the component&#039;s type (see [[#Component_IDs_Map|Component IDs Map]])&lt;br /&gt;
* Three [[#int|ints]] for the local fixed position of the component; &amp;lt;code&amp;gt;x y z&amp;lt;/code&amp;gt;&lt;br /&gt;
** One unit in the fixed position corresponds to a distance of 0.001 game units, or 1mm&lt;br /&gt;
* Four [[#float|floats]] for the local rotation of the component as a quaternion; &amp;lt;code&amp;gt;x y z w&amp;lt;/code&amp;gt;&lt;br /&gt;
* Information about the component&#039;s inputs:&lt;br /&gt;
** An [[#int|int]] for the number of inputs the component has&lt;br /&gt;
** For each input:&lt;br /&gt;
*** An [[#int|int]] for the input&#039;s circuit state ID&lt;br /&gt;
* Information about the component&#039;s outputs:&lt;br /&gt;
** An [[#int|int]] for the number of outputs the component has&lt;br /&gt;
** For each output:&lt;br /&gt;
*** An [[#int|int]] for the output&#039;s circuit state ID&lt;br /&gt;
* The component&#039;s custom data:&lt;br /&gt;
** An [[#int|int]] for the number of bytes in the custom data&lt;br /&gt;
*** If the component has no custom data, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; may be written instead of &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;&lt;br /&gt;
** All the bytes of the custom data, in order&lt;br /&gt;
&lt;br /&gt;
== Wires data ==&lt;br /&gt;
One by one, every wire is listed in the file.&lt;br /&gt;
&lt;br /&gt;
* The [[#peg_address|peg address]] of the wire&#039;s first point&lt;br /&gt;
* The [[#peg_address|peg address]] of the wire&#039;s second point&lt;br /&gt;
* An [[#int|int]] for the wire&#039;s circuit state ID&lt;br /&gt;
* A [[#float|float]] for the wire&#039;s rotation, relative to the default rotation it would have&lt;br /&gt;
&lt;br /&gt;
We do not need to save wire addresses; nothing in the save file needs to reference them, and there is no need for them to be consistent between save loads. Instead, wire addresses are dynamically assigned when the save is loaded.&lt;br /&gt;
&lt;br /&gt;
== Circuit states ==&lt;br /&gt;
We&#039;ve saved the circuit state IDs of all the pegs and wires in the save. Now we need to save the values of those state IDs, so states can be looked up.&lt;br /&gt;
&lt;br /&gt;
A state is either on or off. At runtime, the game stores all circuit states from zero through the highest state in a contiguous block of memory.&lt;br /&gt;
&lt;br /&gt;
=== World files ===&lt;br /&gt;
Worlds are large, and generally use most circuit states, starting from state 0. All circuit states are listed.&lt;br /&gt;
&lt;br /&gt;
* First an [[#int|int]] for the number of bytes in this sequence&lt;br /&gt;
* Then, that number of bytes. Each bit represents the value of the next circuit state; each byte contains 8 states.&lt;br /&gt;
** If, when saving the game, the number of circuit states is not divisible by 8, the last byte will be padded.&lt;br /&gt;
&lt;br /&gt;
=== PartialWorld files ===&lt;br /&gt;
PartialWorlds represent a subset of a World. Most of the circuit states in a world will be irrelevant to a particular PartialWorld, so it would be silly and wasteful to save all of them with every PartialWorld.&lt;br /&gt;
Instead, we save a list of circuit state IDs which are both used in this PartialWorld and have a state of on. All unlisted state IDs are assumed to be in the off state.&lt;br /&gt;
&lt;br /&gt;
* First an [[#int|int]] for the number of ints in this sequence&lt;br /&gt;
* Then, that number of ints. Each int signifies a state ID with a value of on.&lt;br /&gt;
&lt;br /&gt;
== Sub-standards ==&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Int&#039;&#039;&#039; (4 bytes) &amp;lt;span id=&amp;quot;int&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
32 bit signed integers, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Float&#039;&#039;&#039; (4 bytes) &amp;lt;span id=&amp;quot;float&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
32 bit floating point values, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Boolean&#039;&#039;&#039; (1 byte) &amp;lt;span id=&amp;quot;boolean&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
An entire byte that is either &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; for false or &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; for true.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;String&#039;&#039;&#039; (4+ bytes) &amp;lt;span id=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
An int for the number of bytes in the string, followed by that number of bytes, representing that string in UTF-8.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Version&#039;&#039;&#039; (16 bytes) &amp;lt;span id=&amp;quot;version&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
Versions contain four numbers (i.e. 1.0.3.1069). These four numbers are written to the file in order, each as a 4-byte int. Thus, 4x4 bytes = 16 bytes for this. Note: if any of these four numbers are missing, they will be saved as &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;. So if your mod defines its version as &amp;lt;code&amp;gt;1.2&amp;lt;/code&amp;gt;, the four numbers will be &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;. This is not our fault, it&#039;s just how System.Version works.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Component Address&#039;&#039;&#039; &amp;lt;span id=&amp;quot;component_address&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; (4 bytes) ====&lt;br /&gt;
Component Addresses are written as 32 bit unsigned integers, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Peg Address&#039;&#039;&#039; (9 bytes) &amp;lt;span id=&amp;quot;peg_address&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
* One byte to indicate the peg type:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Byte&lt;br /&gt;
!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt;&lt;br /&gt;
|Undefined&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt;&lt;br /&gt;
|Input peg&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt;&lt;br /&gt;
|Output peg&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;FF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Reserved for future use&lt;br /&gt;
|}&lt;br /&gt;
Any value other than &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt; is invalid.&lt;br /&gt;
&lt;br /&gt;
* A component address for the component of the peg&lt;br /&gt;
* An int for the zero-based index of the peg (i.e. is it the 0th input, the 1st input, etc)&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Blotter_File_Format/v7&amp;diff=777</id>
		<title>Blotter File Format/v7</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Blotter_File_Format/v7&amp;diff=777"/>
		<updated>2025-10-13T00:52:39Z</updated>

		<summary type="html">&lt;p&gt;Felipe: add anchors and links&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Blotter File Format, successor to the format known as &amp;quot;.tung files&amp;quot;, is a system for storing the components and wires of a Logic World world. It is used for both full world saves as well as partial worlds (the data structure used by Subassemblies), but there are slight differences between the two.&lt;br /&gt;
&lt;br /&gt;
= File types =&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;World files:&#039;&#039;&#039;&lt;br /&gt;
** Represent a full, playable world space&lt;br /&gt;
** May have zero or more root components at different positions/rotations in the world&lt;br /&gt;
** Use &amp;lt;code&amp;gt;.logicworld&amp;lt;/code&amp;gt; file extension&lt;br /&gt;
** All circuit states are stored contiguously, from circuit state 0 through the highest state.&lt;br /&gt;
* &#039;&#039;&#039;PartialWorld files:&#039;&#039;&#039;&lt;br /&gt;
** Represent a substructure within a playable world space; can be loaded and added to a world with each root at an arbitrary position/rotation&lt;br /&gt;
** Must have at least one root component&lt;br /&gt;
** Use &amp;lt;code&amp;gt;.partialworld&amp;lt;/code&amp;gt; file extension&lt;br /&gt;
** Has a list of circuit state indexes that are On. All indexes not listed here are inferred as being Off.&lt;br /&gt;
&lt;br /&gt;
== File structure ==&lt;br /&gt;
&lt;br /&gt;
* [[#Header|Header]] (16 bytes)&lt;br /&gt;
* &#039;&#039;&#039;Save info&#039;&#039;&#039;&lt;br /&gt;
** [[#Save_Format_Version|Save Format Version]] (1 byte)&lt;br /&gt;
** [[#Game_version|Game version]] (16 bytes)&lt;br /&gt;
** [[#Save_type|Save type]] (1 byte)&lt;br /&gt;
** [[#Component_and_wire_counts|Number of components and wires]] (8 bytes)&lt;br /&gt;
** [[#Mod_versions|Mod versions]] (4+ bytes, dependent on the number of mods in the save)&lt;br /&gt;
** [[#Component_IDs_Map|Component IDs Map]] (4+ bytes, dependent on the number of loaded component IDs)&lt;br /&gt;
* &#039;&#039;&#039;Object data&#039;&#039;&#039;&lt;br /&gt;
** [[#Components_data|Components data]] (0+ bytes, dependent on the number of components)&lt;br /&gt;
** [[#Wires_data|Wires data]] (0+ bytes, dependent on the number of wires)&lt;br /&gt;
** [[#Circuit_states|Circuit states]] (4+ bytes, variable, has a different format for Worlds and PartialWorlds)&lt;br /&gt;
* [[#Footer|Footer]] (16 bytes)&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Save Info&amp;quot; data is at the beginning of the file so that it is easy to extract as metadata. This is done by the game itself in a few places, as well as logicworld.net when uploading a save there.&lt;br /&gt;
&lt;br /&gt;
== Header and footer ==&lt;br /&gt;
&lt;br /&gt;
=== Header ===&lt;br /&gt;
The first 16 bytes of every Blotter file are &amp;lt;code&amp;gt;4C-6F-67-69-63-20-57-6F-72-6C-64-20-73-61-76-65&amp;lt;/code&amp;gt;, which is UTF-8 for “Logic World save”.&lt;br /&gt;
&lt;br /&gt;
=== Footer ===&lt;br /&gt;
The final 16 bytes of every Blotter file are &amp;lt;code&amp;gt;72-65-64-73-74-6F-6E-65-20-73-75-78-20-6C-6F-6C&amp;lt;/code&amp;gt; , which is UTF-8 for “redstone sux lol”.&lt;br /&gt;
&lt;br /&gt;
The header and footer exist to protect against save corruption. If the file does not have them, we know that something is wrong with the file.&lt;br /&gt;
&lt;br /&gt;
== Save Format Version ==&lt;br /&gt;
One byte specifies the save format version. Currently this is &amp;lt;code&amp;gt;07&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Game version ==&lt;br /&gt;
16 bytes store the game [[#version|version]] that this save was created in.&lt;br /&gt;
&lt;br /&gt;
== Save type ==&lt;br /&gt;
There are two types of file that use the Blotter format, Worlds and PartialWorlds. One byte specifies which type of file this is.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Byte&lt;br /&gt;
!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt;&lt;br /&gt;
|Unknown/error&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt;&lt;br /&gt;
|World&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt;&lt;br /&gt;
|PartialWorld&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;FF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Reserved for future use&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Component and wire counts ==&lt;br /&gt;
Here there are two [[#int|ints]]. The first specifies the number of components in the save, the second specifies the number of wires.&lt;br /&gt;
&lt;br /&gt;
== Mod versions ==&lt;br /&gt;
Mods will be included if components they provide are used in this save. This is so that if a later mod version has changes, older save files can be converted.&lt;br /&gt;
&lt;br /&gt;
* An [[#int|int]] for the number of mod versions stored in this file&lt;br /&gt;
* For each mod version:&lt;br /&gt;
** A [[#string|string]] for the text ID of the mod&lt;br /&gt;
** A [[#version|version]] for the version of the mod that this save was created with&lt;br /&gt;
&lt;br /&gt;
== Component IDs Map ==&lt;br /&gt;
When humans add component types to Logic World, they use text IDs like &amp;lt;code&amp;gt;MHG.CircuitBoard&amp;lt;/code&amp;gt;. To make save files and network packets small in size, the game internally uses numeric IDs to reference component types. The relationship between text IDs and numeric IDs is not deterministic or consistent between different save files, so the map from text IDs to numeric IDs must be stored with the save.&lt;br /&gt;
&lt;br /&gt;
First we write an [[#int|int]] for the number of component IDs in this file.&lt;br /&gt;
&lt;br /&gt;
One by one the mappings are written to the file:&lt;br /&gt;
&lt;br /&gt;
* A 2-byte unsigned integer for the numeric ID of the component&lt;br /&gt;
* A [[#string|string]] for the text ID of the component&lt;br /&gt;
&lt;br /&gt;
== Components data ==&lt;br /&gt;
One by one, every component is listed in the file. The first component must be a root component -- i.e. have an address of &amp;lt;code&amp;gt;C-0&amp;lt;/code&amp;gt; for its parent -- and every subsequent component must have a parent that was listed before it.&lt;br /&gt;
&lt;br /&gt;
It is explicitly okay to have a World file with no components in it (i.e. an empty world). However, a PartialWorld file must have at least one component.&lt;br /&gt;
&lt;br /&gt;
Each component is written like so:&lt;br /&gt;
&lt;br /&gt;
* The [[#component_address|component address]] of this component&lt;br /&gt;
* The [[#component_address|component address]] of the component&#039;s parent&lt;br /&gt;
* 2 bytes for the numeric ID of the component&#039;s type (see [[#Component_IDs_Map|Component IDs Map]])&lt;br /&gt;
* Three [[#int|ints]] for the local fixed position of the component; &amp;lt;code&amp;gt;x y z&amp;lt;/code&amp;gt;&lt;br /&gt;
** One unit in the fixed position corresponds to a distance 0.001 game units, or 1mm&lt;br /&gt;
* Four [[#float|floats]] for the local rotation of the component as a quaternion; &amp;lt;code&amp;gt;x y z w&amp;lt;/code&amp;gt;&lt;br /&gt;
* Information about the component&#039;s inputs:&lt;br /&gt;
** An [[#int|int]] for the number of inputs the component has&lt;br /&gt;
** For each input:&lt;br /&gt;
*** An [[#int|int]] for the input&#039;s circuit state ID&lt;br /&gt;
* Information about the component&#039;s outputs:&lt;br /&gt;
** An [[#int|int]] for the number of outputs the component has&lt;br /&gt;
** For each output:&lt;br /&gt;
*** An [[#int|int]] for the output&#039;s circuit state ID&lt;br /&gt;
* The component&#039;s custom data:&lt;br /&gt;
** An [[#int|int]] for the number of bytes in the custom data&lt;br /&gt;
*** If the component has no custom data, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; may be written instead of &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;&lt;br /&gt;
** All the bytes of the custom data, in order&lt;br /&gt;
&lt;br /&gt;
== Wires data ==&lt;br /&gt;
One by one, every wire is listed in the file.&lt;br /&gt;
&lt;br /&gt;
* The [[#peg_address|peg address]] of the wire&#039;s first point&lt;br /&gt;
* The [[#peg_address|peg address]] of the wire&#039;s second point&lt;br /&gt;
* An [[#int|int]] for the wire&#039;s circuit state ID&lt;br /&gt;
* A [[#float|float]] for the wire&#039;s rotation, relative to the default rotation it would have&lt;br /&gt;
&lt;br /&gt;
We do not need to save wire addresses; nothing in the save file needs to reference them, and there is no need for them to be consistent between save loads. Instead, wire addresses are dynamically assigned when the save is loaded.&lt;br /&gt;
&lt;br /&gt;
== Circuit states ==&lt;br /&gt;
We&#039;ve saved the circuit state IDs of all the pegs and wires in the save. Now we need to save the values of those state IDs, so states can be looked up.&lt;br /&gt;
&lt;br /&gt;
A state is either on or off. At runtime, the game stores all circuit states from zero through the highest state in a contiguous block of memory.&lt;br /&gt;
&lt;br /&gt;
=== World files ===&lt;br /&gt;
Worlds are large, and generally use most circuit states, starting from state 0. All circuit states are listed.&lt;br /&gt;
&lt;br /&gt;
* First an [[#int|int]] for the number of bytes in this sequence&lt;br /&gt;
* Then, that number of bytes. Each bit represents the value of the next circuit state; each byte contains 8 states.&lt;br /&gt;
** If, when saving the game, the number of circuit states is not divisible by 8, the last byte will be padded.&lt;br /&gt;
&lt;br /&gt;
=== PartialWorld files ===&lt;br /&gt;
PartialWorlds represent a subset of a World. Most of the circuit states in a world will be irrelevant to a particular PartialWorld, so it would be silly and wasteful to save all of them with every PartialWorld.&lt;br /&gt;
Instead, we save a list of circuit state IDs which are both used in this PartialWorld and have a state of on. All unlisted state IDs are assumed to be in the off state.&lt;br /&gt;
&lt;br /&gt;
* First an [[#int|int]] for the number of ints in this sequence&lt;br /&gt;
* Then, that number of ints. Each int signifies a state ID with a value of on.&lt;br /&gt;
&lt;br /&gt;
== Sub-standards ==&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Int&#039;&#039;&#039; (4 bytes) &amp;lt;span id=&amp;quot;int&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
32 bit signed integers, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Float&#039;&#039;&#039; (4 bytes) &amp;lt;span id=&amp;quot;float&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
32 bit floating point values, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Boolean&#039;&#039;&#039; (1 byte) &amp;lt;span id=&amp;quot;boolean&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
An entire byte that is either &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; for false or &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; for true.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;String&#039;&#039;&#039; (4+ bytes) &amp;lt;span id=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
An int for the number of bytes in the string, followed by that number of bytes, representing that string in UTF-8.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Version&#039;&#039;&#039; (16 bytes) &amp;lt;span id=&amp;quot;version&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
Versions contain four numbers (i.e. 1.0.3.1069). These four numbers are written to the file in order, each as a 4-byte int. Thus, 4x4 bytes = 16 bytes for this. Note: if any of these four numbers are missing, they will be saved as &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;. So if your mod defines its version as &amp;lt;code&amp;gt;1.2&amp;lt;/code&amp;gt;, the four numbers will be &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;. This is not our fault, it&#039;s just how System.Version works.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Component Address&#039;&#039;&#039; &amp;lt;span id=&amp;quot;component_address&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; (4 bytes) ====&lt;br /&gt;
Component Addresses are written as 32 bit unsigned integers, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Peg Address&#039;&#039;&#039; (9 bytes) &amp;lt;span id=&amp;quot;peg_address&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
* One byte to indicate the peg type:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Byte&lt;br /&gt;
!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt;&lt;br /&gt;
|Undefined&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt;&lt;br /&gt;
|Input peg&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt;&lt;br /&gt;
|Output peg&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;FF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Reserved for future use&lt;br /&gt;
|}&lt;br /&gt;
Any value other than &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt; is invalid.&lt;br /&gt;
&lt;br /&gt;
* A component address for the component of the peg&lt;br /&gt;
* An int for the zero-based index of the peg (i.e. is it the 0th input, the 1st input, etc)&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Blotter_File_Format/v7&amp;diff=776</id>
		<title>Blotter File Format/v7</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Blotter_File_Format/v7&amp;diff=776"/>
		<updated>2025-10-13T00:39:39Z</updated>

		<summary type="html">&lt;p&gt;Felipe: Protected &amp;quot;Blotter File Format/v7&amp;quot; ([Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite))&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Blotter File Format, successor to the format known as &amp;quot;.tung files&amp;quot;, is a system for storing the components and wires of a Logic World world. It is used for both full world saves as well as partial worlds (the data structure used by Subassemblies), but there are slight differences between the two.&lt;br /&gt;
&lt;br /&gt;
= File types =&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;World files:&#039;&#039;&#039;&lt;br /&gt;
** Represent a full, playable world space&lt;br /&gt;
** May have zero or more root components at different positions/rotations in the world&lt;br /&gt;
** Use &amp;lt;code&amp;gt;.logicworld&amp;lt;/code&amp;gt; file extension&lt;br /&gt;
** All circuit states are stored contiguously, from circuit state 0 through the highest state.&lt;br /&gt;
* &#039;&#039;&#039;PartialWorld files:&#039;&#039;&#039;&lt;br /&gt;
** Represent a substructure within a playable world space; can be loaded and added to a world with each root at an arbitrary position/rotation&lt;br /&gt;
** Must have at least one root component&lt;br /&gt;
** Use &amp;lt;code&amp;gt;.partialworld&amp;lt;/code&amp;gt; file extension&lt;br /&gt;
** Has a list of circuit state indexes that are On. All indexes not listed here are inferred as being Off.&lt;br /&gt;
&lt;br /&gt;
== File structure ==&lt;br /&gt;
&lt;br /&gt;
* Header (16 bytes)&lt;br /&gt;
* &#039;&#039;&#039;Save info&#039;&#039;&#039;&lt;br /&gt;
** Save Format Version (1 byte)&lt;br /&gt;
** Game version (16 bytes)&lt;br /&gt;
** Save type (1 byte)&lt;br /&gt;
** Number of components and wires (8 bytes)&lt;br /&gt;
** Mod versions (4+ bytes, dependent on the number of mods in the save)&lt;br /&gt;
** Component IDs Map (4+ bytes, dependent on the number of loaded component IDs)&lt;br /&gt;
* &#039;&#039;&#039;Object data&#039;&#039;&#039;&lt;br /&gt;
** Components data (0+ bytes, dependent on the number of components)&lt;br /&gt;
** Wires data (0+ bytes, dependent on the number of wires)&lt;br /&gt;
** Circuit states (4+ bytes, variable, has a different format for Worlds and PartialWorlds)&lt;br /&gt;
* Footer (16 bytes)&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Save Info&amp;quot; data is at the beginning of the file so that it is easy to extract as metadata. This is done by the game itself in a few places, as well as logicworld.net when uploading a save there.&lt;br /&gt;
&lt;br /&gt;
== Header and footer ==&lt;br /&gt;
&lt;br /&gt;
=== Header ===&lt;br /&gt;
The first 16 bytes of every Blotter file are &amp;lt;code&amp;gt;4C-6F-67-69-63-20-57-6F-72-6C-64-20-73-61-76-65&amp;lt;/code&amp;gt;, which is UTF-8 for “Logic World save”.&lt;br /&gt;
&lt;br /&gt;
=== Footer ===&lt;br /&gt;
The final 16 bytes of every Blotter file are &amp;lt;code&amp;gt;72-65-64-73-74-6F-6E-65-20-73-75-78-20-6C-6F-6C&amp;lt;/code&amp;gt; , which is UTF-8 for “redstone sux lol”.&lt;br /&gt;
&lt;br /&gt;
The header and footer exist to protect against save corruption. If the file does not have them, we know that something is wrong with the file.&lt;br /&gt;
&lt;br /&gt;
== Save Format Version ==&lt;br /&gt;
One byte specifies the save format version. Currently this is &amp;lt;code&amp;gt;07&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Game version ==&lt;br /&gt;
16 bytes store the game version that this save was created in.&lt;br /&gt;
&lt;br /&gt;
== Save type ==&lt;br /&gt;
There are two types of file that use the Blotter format, Worlds and PartialWorlds. One byte specifies which type of file this is.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Byte&lt;br /&gt;
!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt;&lt;br /&gt;
|Unknown/error&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt;&lt;br /&gt;
|World&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt;&lt;br /&gt;
|PartialWorld&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;FF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Reserved for future use&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Component and wire counts ==&lt;br /&gt;
Here there are two ints. The first specifies the number of components in the save, the second specifies the number of wires.&lt;br /&gt;
&lt;br /&gt;
== Mod versions ==&lt;br /&gt;
Mods will be included if components they provide are used in this save. This is so that if a later mod version has changes, older save files can be converted.&lt;br /&gt;
&lt;br /&gt;
* An int for the number of mod versions stored in this file&lt;br /&gt;
* For each mod version:&lt;br /&gt;
** A string for the text ID of the mod&lt;br /&gt;
** A version for the version of the mod that this save was created with&lt;br /&gt;
&lt;br /&gt;
== Component IDs Map ==&lt;br /&gt;
When humans add component types to Logic World, they use text IDs like &amp;lt;code&amp;gt;MHG.CircuitBoard&amp;lt;/code&amp;gt;. To make save files and network packets small in size, the game internally uses numeric IDs to reference component types. The relationship between text IDs and numeric IDs is not deterministic or consistent between different save files, so the map from text IDs to numeric IDs must be stored with the save.&lt;br /&gt;
&lt;br /&gt;
First we write an int for the number of component IDs in this file.&lt;br /&gt;
&lt;br /&gt;
One by one the mappings are written to the file:&lt;br /&gt;
&lt;br /&gt;
* A 2-byte unsigned integer for the numeric ID of the component&lt;br /&gt;
* A string for the text ID of the component&lt;br /&gt;
&lt;br /&gt;
== Components data ==&lt;br /&gt;
One by one, every component is listed in the file. The first component must be a root component -- i.e. have an address of &amp;lt;code&amp;gt;C-0&amp;lt;/code&amp;gt; for its parent -- and every subsequent component must have a parent that was listed before it.&lt;br /&gt;
&lt;br /&gt;
It is explicitly okay to have a World file with no components in it (i.e. an empty world). However, a PartialWorld file must have at least one component.&lt;br /&gt;
&lt;br /&gt;
Each component is written like so:&lt;br /&gt;
&lt;br /&gt;
* The component address of this component&lt;br /&gt;
* The component address of the component&#039;s parent&lt;br /&gt;
* 2 bytes for the numeric ID of the component&#039;s type (see Component IDs Map)&lt;br /&gt;
* Three ints for the local fixed position of the component; &amp;lt;code&amp;gt;x y z&amp;lt;/code&amp;gt;&lt;br /&gt;
** One unit in the fixed position corresponds to a distance 0.001 game units, or 1mm&lt;br /&gt;
* Four floats for the local rotation of the component as a quaternion; &amp;lt;code&amp;gt;x y z w&amp;lt;/code&amp;gt;&lt;br /&gt;
* Information about the component&#039;s inputs:&lt;br /&gt;
** An int for the number of inputs the component has&lt;br /&gt;
** For each input:&lt;br /&gt;
*** An int for the input&#039;s circuit state ID&lt;br /&gt;
* Information about the component&#039;s outputs:&lt;br /&gt;
** An int for the number of outputs the component has&lt;br /&gt;
** For each output:&lt;br /&gt;
*** An int for the output&#039;s circuit state ID&lt;br /&gt;
* The component&#039;s custom data:&lt;br /&gt;
** An int for the number of bytes in the custom data&lt;br /&gt;
*** If the component has no custom data, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; may be written instead of &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;&lt;br /&gt;
** All the bytes of the custom data, in order&lt;br /&gt;
&lt;br /&gt;
== Wires data ==&lt;br /&gt;
One by one, every wire is listed in the file.&lt;br /&gt;
&lt;br /&gt;
* The peg address of the wire&#039;s first point&lt;br /&gt;
* The peg address of the wire&#039;s second point&lt;br /&gt;
* An int for the wire&#039;s circuit state ID&lt;br /&gt;
* A float for the wire&#039;s rotation, relative to the default rotation it would have&lt;br /&gt;
&lt;br /&gt;
We do not need to save wire addresses; nothing in the save file needs to reference them, and there is no need for them to be consistent between save loads. Instead, wire addresses are dynamically assigned when the save is loaded.&lt;br /&gt;
&lt;br /&gt;
== Circuit states ==&lt;br /&gt;
We&#039;ve saved the circuit state IDs of all the pegs and wires in the save. Now we need to save the values of those state IDs, so states can be looked up.&lt;br /&gt;
&lt;br /&gt;
A state is either on or off. At runtime, the game stores all circuit states from zero through the highest state in a contiguous block of memory.&lt;br /&gt;
&lt;br /&gt;
=== World files ===&lt;br /&gt;
Worlds are large, and generally use most circuit states, starting from state 0. All circuit states are listed.&lt;br /&gt;
&lt;br /&gt;
* First an int for the number of bytes in this sequence&lt;br /&gt;
* Then, that number of bytes. Each bit represents the value of the next circuit state; each byte contains 8 states.&lt;br /&gt;
** If, when saving the game, the number of circuit states is not divisible by 8, the last byte will be padded.&lt;br /&gt;
&lt;br /&gt;
=== PartialWorld files ===&lt;br /&gt;
PartialWorlds represent a subset of a World. Most of the circuit states in a world will be irrelevant to a particular PartialWorld, so it would be silly and wasteful to save all of them with every PartialWorld.&lt;br /&gt;
Instead, we save a list of circuit state IDs which are both used in this PartialWorld and have a state of on. All unlisted state IDs are assumed to be in the off state.&lt;br /&gt;
&lt;br /&gt;
* First an int for the number of ints in this sequence&lt;br /&gt;
* Then, that number of ints. Each int signifies a state ID with a value of on.&lt;br /&gt;
&lt;br /&gt;
== Sub-standards ==&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Int&#039;&#039;&#039; (4 bytes) ====&lt;br /&gt;
32 bit signed integers, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Float&#039;&#039;&#039; (4 bytes) ====&lt;br /&gt;
32 bit floating point values, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Boolean&#039;&#039;&#039; (1 byte) ====&lt;br /&gt;
An entire byte that is either &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; for false or &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; for true.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;String&#039;&#039;&#039; (4+ bytes) ====&lt;br /&gt;
An int for the number of bytes in the string, followed by that number of bytes, representing that string in UTF-8.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Version&#039;&#039;&#039; (16 bytes) ====&lt;br /&gt;
Versions contain four numbers (i.e. 1.0.3.1069). These four numbers are written to the file in order, each as a 4-byte int. Thus, 4x4 bytes = 16 bytes for this. Note: if any of these four numbers are missing, they will be saved as &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;. So if your mod defines its version as &amp;lt;code&amp;gt;1.2&amp;lt;/code&amp;gt;, the four numbers will be &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;. This is not our fault, it&#039;s just how System.Version works.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Component Address&#039;&#039;&#039; (4 bytes) ====&lt;br /&gt;
Component Addresses are written as 32 bit unsigned integers, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Peg Address&#039;&#039;&#039; (9 bytes) ====&lt;br /&gt;
&lt;br /&gt;
* One byte to indicate the peg type:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Byte&lt;br /&gt;
!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt;&lt;br /&gt;
|Undefined&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt;&lt;br /&gt;
|Input peg&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt;&lt;br /&gt;
|Output peg&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;FF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Reserved for future use&lt;br /&gt;
|}&lt;br /&gt;
Any value other than &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt; is invalid.&lt;br /&gt;
&lt;br /&gt;
* A component address for the component of the peg&lt;br /&gt;
* An int for the zero-based index of the peg (i.e. is it the 0th input, the 1st input, etc)&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Blotter_File_Format/v7&amp;diff=775</id>
		<title>Blotter File Format/v7</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Blotter_File_Format/v7&amp;diff=775"/>
		<updated>2025-10-13T00:39:30Z</updated>

		<summary type="html">&lt;p&gt;Felipe: Created page with &amp;quot;The Blotter File Format, successor to the format known as &amp;quot;.tung files&amp;quot;, is a system for storing the components and wires of a Logic World world. It is used for both full world saves as well as partial worlds (the data structure used by Subassemblies), but there are slight differences between the two.  = File types =  * &amp;#039;&amp;#039;&amp;#039;World files:&amp;#039;&amp;#039;&amp;#039; ** Represent a full, playable world space ** May have zero or more root components at different positions/rotations in the world ** Us...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Blotter File Format, successor to the format known as &amp;quot;.tung files&amp;quot;, is a system for storing the components and wires of a Logic World world. It is used for both full world saves as well as partial worlds (the data structure used by Subassemblies), but there are slight differences between the two.&lt;br /&gt;
&lt;br /&gt;
= File types =&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;World files:&#039;&#039;&#039;&lt;br /&gt;
** Represent a full, playable world space&lt;br /&gt;
** May have zero or more root components at different positions/rotations in the world&lt;br /&gt;
** Use &amp;lt;code&amp;gt;.logicworld&amp;lt;/code&amp;gt; file extension&lt;br /&gt;
** All circuit states are stored contiguously, from circuit state 0 through the highest state.&lt;br /&gt;
* &#039;&#039;&#039;PartialWorld files:&#039;&#039;&#039;&lt;br /&gt;
** Represent a substructure within a playable world space; can be loaded and added to a world with each root at an arbitrary position/rotation&lt;br /&gt;
** Must have at least one root component&lt;br /&gt;
** Use &amp;lt;code&amp;gt;.partialworld&amp;lt;/code&amp;gt; file extension&lt;br /&gt;
** Has a list of circuit state indexes that are On. All indexes not listed here are inferred as being Off.&lt;br /&gt;
&lt;br /&gt;
== File structure ==&lt;br /&gt;
&lt;br /&gt;
* Header (16 bytes)&lt;br /&gt;
* &#039;&#039;&#039;Save info&#039;&#039;&#039;&lt;br /&gt;
** Save Format Version (1 byte)&lt;br /&gt;
** Game version (16 bytes)&lt;br /&gt;
** Save type (1 byte)&lt;br /&gt;
** Number of components and wires (8 bytes)&lt;br /&gt;
** Mod versions (4+ bytes, dependent on the number of mods in the save)&lt;br /&gt;
** Component IDs Map (4+ bytes, dependent on the number of loaded component IDs)&lt;br /&gt;
* &#039;&#039;&#039;Object data&#039;&#039;&#039;&lt;br /&gt;
** Components data (0+ bytes, dependent on the number of components)&lt;br /&gt;
** Wires data (0+ bytes, dependent on the number of wires)&lt;br /&gt;
** Circuit states (4+ bytes, variable, has a different format for Worlds and PartialWorlds)&lt;br /&gt;
* Footer (16 bytes)&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Save Info&amp;quot; data is at the beginning of the file so that it is easy to extract as metadata. This is done by the game itself in a few places, as well as logicworld.net when uploading a save there.&lt;br /&gt;
&lt;br /&gt;
== Header and footer ==&lt;br /&gt;
&lt;br /&gt;
=== Header ===&lt;br /&gt;
The first 16 bytes of every Blotter file are &amp;lt;code&amp;gt;4C-6F-67-69-63-20-57-6F-72-6C-64-20-73-61-76-65&amp;lt;/code&amp;gt;, which is UTF-8 for “Logic World save”.&lt;br /&gt;
&lt;br /&gt;
=== Footer ===&lt;br /&gt;
The final 16 bytes of every Blotter file are &amp;lt;code&amp;gt;72-65-64-73-74-6F-6E-65-20-73-75-78-20-6C-6F-6C&amp;lt;/code&amp;gt; , which is UTF-8 for “redstone sux lol”.&lt;br /&gt;
&lt;br /&gt;
The header and footer exist to protect against save corruption. If the file does not have them, we know that something is wrong with the file.&lt;br /&gt;
&lt;br /&gt;
== Save Format Version ==&lt;br /&gt;
One byte specifies the save format version. Currently this is &amp;lt;code&amp;gt;07&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Game version ==&lt;br /&gt;
16 bytes store the game version that this save was created in.&lt;br /&gt;
&lt;br /&gt;
== Save type ==&lt;br /&gt;
There are two types of file that use the Blotter format, Worlds and PartialWorlds. One byte specifies which type of file this is.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Byte&lt;br /&gt;
!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt;&lt;br /&gt;
|Unknown/error&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt;&lt;br /&gt;
|World&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt;&lt;br /&gt;
|PartialWorld&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;FF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Reserved for future use&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Component and wire counts ==&lt;br /&gt;
Here there are two ints. The first specifies the number of components in the save, the second specifies the number of wires.&lt;br /&gt;
&lt;br /&gt;
== Mod versions ==&lt;br /&gt;
Mods will be included if components they provide are used in this save. This is so that if a later mod version has changes, older save files can be converted.&lt;br /&gt;
&lt;br /&gt;
* An int for the number of mod versions stored in this file&lt;br /&gt;
* For each mod version:&lt;br /&gt;
** A string for the text ID of the mod&lt;br /&gt;
** A version for the version of the mod that this save was created with&lt;br /&gt;
&lt;br /&gt;
== Component IDs Map ==&lt;br /&gt;
When humans add component types to Logic World, they use text IDs like &amp;lt;code&amp;gt;MHG.CircuitBoard&amp;lt;/code&amp;gt;. To make save files and network packets small in size, the game internally uses numeric IDs to reference component types. The relationship between text IDs and numeric IDs is not deterministic or consistent between different save files, so the map from text IDs to numeric IDs must be stored with the save.&lt;br /&gt;
&lt;br /&gt;
First we write an int for the number of component IDs in this file.&lt;br /&gt;
&lt;br /&gt;
One by one the mappings are written to the file:&lt;br /&gt;
&lt;br /&gt;
* A 2-byte unsigned integer for the numeric ID of the component&lt;br /&gt;
* A string for the text ID of the component&lt;br /&gt;
&lt;br /&gt;
== Components data ==&lt;br /&gt;
One by one, every component is listed in the file. The first component must be a root component -- i.e. have an address of &amp;lt;code&amp;gt;C-0&amp;lt;/code&amp;gt; for its parent -- and every subsequent component must have a parent that was listed before it.&lt;br /&gt;
&lt;br /&gt;
It is explicitly okay to have a World file with no components in it (i.e. an empty world). However, a PartialWorld file must have at least one component.&lt;br /&gt;
&lt;br /&gt;
Each component is written like so:&lt;br /&gt;
&lt;br /&gt;
* The component address of this component&lt;br /&gt;
* The component address of the component&#039;s parent&lt;br /&gt;
* 2 bytes for the numeric ID of the component&#039;s type (see Component IDs Map)&lt;br /&gt;
* Three ints for the local fixed position of the component; &amp;lt;code&amp;gt;x y z&amp;lt;/code&amp;gt;&lt;br /&gt;
** One unit in the fixed position corresponds to a distance 0.001 game units, or 1mm&lt;br /&gt;
* Four floats for the local rotation of the component as a quaternion; &amp;lt;code&amp;gt;x y z w&amp;lt;/code&amp;gt;&lt;br /&gt;
* Information about the component&#039;s inputs:&lt;br /&gt;
** An int for the number of inputs the component has&lt;br /&gt;
** For each input:&lt;br /&gt;
*** An int for the input&#039;s circuit state ID&lt;br /&gt;
* Information about the component&#039;s outputs:&lt;br /&gt;
** An int for the number of outputs the component has&lt;br /&gt;
** For each output:&lt;br /&gt;
*** An int for the output&#039;s circuit state ID&lt;br /&gt;
* The component&#039;s custom data:&lt;br /&gt;
** An int for the number of bytes in the custom data&lt;br /&gt;
*** If the component has no custom data, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; may be written instead of &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;&lt;br /&gt;
** All the bytes of the custom data, in order&lt;br /&gt;
&lt;br /&gt;
== Wires data ==&lt;br /&gt;
One by one, every wire is listed in the file.&lt;br /&gt;
&lt;br /&gt;
* The peg address of the wire&#039;s first point&lt;br /&gt;
* The peg address of the wire&#039;s second point&lt;br /&gt;
* An int for the wire&#039;s circuit state ID&lt;br /&gt;
* A float for the wire&#039;s rotation, relative to the default rotation it would have&lt;br /&gt;
&lt;br /&gt;
We do not need to save wire addresses; nothing in the save file needs to reference them, and there is no need for them to be consistent between save loads. Instead, wire addresses are dynamically assigned when the save is loaded.&lt;br /&gt;
&lt;br /&gt;
== Circuit states ==&lt;br /&gt;
We&#039;ve saved the circuit state IDs of all the pegs and wires in the save. Now we need to save the values of those state IDs, so states can be looked up.&lt;br /&gt;
&lt;br /&gt;
A state is either on or off. At runtime, the game stores all circuit states from zero through the highest state in a contiguous block of memory.&lt;br /&gt;
&lt;br /&gt;
=== World files ===&lt;br /&gt;
Worlds are large, and generally use most circuit states, starting from state 0. All circuit states are listed.&lt;br /&gt;
&lt;br /&gt;
* First an int for the number of bytes in this sequence&lt;br /&gt;
* Then, that number of bytes. Each bit represents the value of the next circuit state; each byte contains 8 states.&lt;br /&gt;
** If, when saving the game, the number of circuit states is not divisible by 8, the last byte will be padded.&lt;br /&gt;
&lt;br /&gt;
=== PartialWorld files ===&lt;br /&gt;
PartialWorlds represent a subset of a World. Most of the circuit states in a world will be irrelevant to a particular PartialWorld, so it would be silly and wasteful to save all of them with every PartialWorld.&lt;br /&gt;
Instead, we save a list of circuit state IDs which are both used in this PartialWorld and have a state of on. All unlisted state IDs are assumed to be in the off state.&lt;br /&gt;
&lt;br /&gt;
* First an int for the number of ints in this sequence&lt;br /&gt;
* Then, that number of ints. Each int signifies a state ID with a value of on.&lt;br /&gt;
&lt;br /&gt;
== Sub-standards ==&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Int&#039;&#039;&#039; (4 bytes) ====&lt;br /&gt;
32 bit signed integers, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Float&#039;&#039;&#039; (4 bytes) ====&lt;br /&gt;
32 bit floating point values, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Boolean&#039;&#039;&#039; (1 byte) ====&lt;br /&gt;
An entire byte that is either &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; for false or &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; for true.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;String&#039;&#039;&#039; (4+ bytes) ====&lt;br /&gt;
An int for the number of bytes in the string, followed by that number of bytes, representing that string in UTF-8.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Version&#039;&#039;&#039; (16 bytes) ====&lt;br /&gt;
Versions contain four numbers (i.e. 1.0.3.1069). These four numbers are written to the file in order, each as a 4-byte int. Thus, 4x4 bytes = 16 bytes for this. Note: if any of these four numbers are missing, they will be saved as &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;. So if your mod defines its version as &amp;lt;code&amp;gt;1.2&amp;lt;/code&amp;gt;, the four numbers will be &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;. This is not our fault, it&#039;s just how System.Version works.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Component Address&#039;&#039;&#039; (4 bytes) ====&lt;br /&gt;
Component Addresses are written as 32 bit unsigned integers, little endian.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Peg Address&#039;&#039;&#039; (9 bytes) ====&lt;br /&gt;
&lt;br /&gt;
* One byte to indicate the peg type:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Byte&lt;br /&gt;
!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt;&lt;br /&gt;
|Undefined&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt;&lt;br /&gt;
|Input peg&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt;&lt;br /&gt;
|Output peg&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;FF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Reserved for future use&lt;br /&gt;
|}&lt;br /&gt;
Any value other than &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;02&amp;lt;/code&amp;gt; is invalid.&lt;br /&gt;
&lt;br /&gt;
* A component address for the component of the peg&lt;br /&gt;
* An int for the zero-based index of the peg (i.e. is it the 0th input, the 1st input, etc)&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=MediaWiki:Group-module-editor&amp;diff=681</id>
		<title>MediaWiki:Group-module-editor</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=MediaWiki:Group-module-editor&amp;diff=681"/>
		<updated>2025-09-12T12:25:21Z</updated>

		<summary type="html">&lt;p&gt;Felipe: Created page with &amp;quot;Module editors&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Module editors&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Building_mechanic&amp;diff=679</id>
		<title>Building mechanic</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Building_mechanic&amp;diff=679"/>
		<updated>2025-09-11T22:52:49Z</updated>

		<summary type="html">&lt;p&gt;Felipe: use singular links&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Building mechanics&#039;&#039;&#039; are tools the player can use to build, i.e. to manipulate [[component]]s and [[wire]]s.&lt;br /&gt;
&lt;br /&gt;
== List of building mechanics ==&lt;br /&gt;
&lt;br /&gt;
* [[Editing]]&lt;br /&gt;
* [[Resizing]]&lt;br /&gt;
&lt;br /&gt;
{{todo|complete this list}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=MediaWiki:Common.css&amp;diff=663</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=MediaWiki:Common.css&amp;diff=663"/>
		<updated>2025-09-10T21:02:35Z</updated>

		<summary type="html">&lt;p&gt;Felipe: add nowrap class&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lw-infobox {&lt;br /&gt;
	background-color: var(--background-color-interactive-subtle,#f8f9fa);&lt;br /&gt;
	color: var(--color-base,#202122);&lt;br /&gt;
	border: 1px solid var(--border-color-subtle,#c8ccd1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.tabber .wikitable {&lt;br /&gt;
	margin-top: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* CSS variables for the binary signal graph */&lt;br /&gt;
body {&lt;br /&gt;
    --signal-border: 3px solid rgb(239, 53, 53);&lt;br /&gt;
    --tick-marker-border: 1px solid var(--border-color-base);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* === Navbox styles === */&lt;br /&gt;
.mw-parser-output .navbox {&lt;br /&gt;
  border:1px solid #aaa;&lt;br /&gt;
  box-sizing:border-box;&lt;br /&gt;
  width:100%;&lt;br /&gt;
  margin:auto;&lt;br /&gt;
  clear:both;&lt;br /&gt;
  font-size:88%;&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  padding:1px&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-inner,&lt;br /&gt;
.mw-parser-output .navbox-subgroup {&lt;br /&gt;
  width:100%&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox+.navbox-styles+.navbox {&lt;br /&gt;
  margin-top:-1px&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox th,&lt;br /&gt;
.mw-parser-output .navbox-title,&lt;br /&gt;
.mw-parser-output .navbox-abovebelow {&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  padding-left:1em;&lt;br /&gt;
  padding-right:1em&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output th.navbox-group {&lt;br /&gt;
  white-space:nowrap;&lt;br /&gt;
  text-align:right&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox,&lt;br /&gt;
.mw-parser-output .navbox-subgroup {&lt;br /&gt;
  background:#fdfdfd&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-list {&lt;br /&gt;
  border-color:#fdfdfd&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox th,&lt;br /&gt;
.mw-parser-output .navbox-title {&lt;br /&gt;
  background:#eaeeff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-abovebelow,&lt;br /&gt;
.mw-parser-output th.navbox-group,&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-title {&lt;br /&gt;
  background:#ddddff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-group,&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-abovebelow {&lt;br /&gt;
  background:#e6e6ff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-even {&lt;br /&gt;
  background:#f7f7f7&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-odd {&lt;br /&gt;
  background:transparent&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output th.navbox-title1 {&lt;br /&gt;
  border-left:2px solid #fdfdfd;&lt;br /&gt;
  width:100%&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output td.navbox-list1 {&lt;br /&gt;
  text-align:left;&lt;br /&gt;
  border-left-width:2px;&lt;br /&gt;
  border-left-style:solid&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist td dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist td ol,&lt;br /&gt;
.mw-parser-output .navbox .hlist td ul,&lt;br /&gt;
.mw-parser-output .navbox td.hlist dl,&lt;br /&gt;
.mw-parser-output .navbox td.hlist ol,&lt;br /&gt;
.mw-parser-output .navbox td.hlist ul {&lt;br /&gt;
  padding:0.125em 0&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist dd,&lt;br /&gt;
.mw-parser-output .navbox .hlist dt,&lt;br /&gt;
.mw-parser-output .navbox .hlist li {&lt;br /&gt;
  white-space:nowrap&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist dd dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist dt dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist li ol,&lt;br /&gt;
.mw-parser-output .navbox .hlist li ul {&lt;br /&gt;
  white-space:normal&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output ol+.navbox-styles+.navbox,&lt;br /&gt;
.mw-parser-output ul+.navbox-styles+.navbox {&lt;br /&gt;
  margin-top:0.5em&lt;br /&gt;
}&lt;br /&gt;
@media screen {&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-title {&lt;br /&gt;
    background-color:#345&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-group {&lt;br /&gt;
    background-color:#567&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-subgroup td.navbox-title {&lt;br /&gt;
    background-color:#456&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-even {&lt;br /&gt;
    background-color:#000&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-odd {&lt;br /&gt;
    background-color:#123&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox-subgroup,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox-list {&lt;br /&gt;
    background-color:#111111&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-image,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-list {&lt;br /&gt;
    border-top:2px solid #111111&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
@media screen and (prefers-color-scheme:dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-title {&lt;br /&gt;
    background-color:#345&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-group {&lt;br /&gt;
    background-color:#567&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-subgroup td.navbox-title {&lt;br /&gt;
    background-color:#456&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-even {&lt;br /&gt;
    background-color:#000&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-odd {&lt;br /&gt;
    background-color:#123&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox-subgroup,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox-list {&lt;br /&gt;
    background-color:#111111&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-image,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-list {&lt;br /&gt;
    border-top:2px solid #111111&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
/* === End of Navbox styles === */&lt;br /&gt;
&lt;br /&gt;
@media screen {&lt;br /&gt;
	kbd.key {&lt;br /&gt;
		background-color: var(--background-color-neutral-subtle,#f8f9fa);&lt;br /&gt;
	    color: var(--color-emphasized,#101418);&lt;br /&gt;
	    border: 2.5px outset var(--border-color-muted,#dadde3);&lt;br /&gt;
	    border-radius: 2px;&lt;br /&gt;
    	padding: 1px 4px;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.nowrap {&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=MediaWiki:Common.css&amp;diff=662</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=MediaWiki:Common.css&amp;diff=662"/>
		<updated>2025-09-10T21:00:39Z</updated>

		<summary type="html">&lt;p&gt;Felipe: add key styles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lw-infobox {&lt;br /&gt;
	background-color: var(--background-color-interactive-subtle,#f8f9fa);&lt;br /&gt;
	color: var(--color-base,#202122);&lt;br /&gt;
	border: 1px solid var(--border-color-subtle,#c8ccd1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.tabber .wikitable {&lt;br /&gt;
	margin-top: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* CSS variables for the binary signal graph */&lt;br /&gt;
body {&lt;br /&gt;
    --signal-border: 3px solid rgb(239, 53, 53);&lt;br /&gt;
    --tick-marker-border: 1px solid var(--border-color-base);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* === Navbox styles === */&lt;br /&gt;
.mw-parser-output .navbox {&lt;br /&gt;
  border:1px solid #aaa;&lt;br /&gt;
  box-sizing:border-box;&lt;br /&gt;
  width:100%;&lt;br /&gt;
  margin:auto;&lt;br /&gt;
  clear:both;&lt;br /&gt;
  font-size:88%;&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  padding:1px&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-inner,&lt;br /&gt;
.mw-parser-output .navbox-subgroup {&lt;br /&gt;
  width:100%&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox+.navbox-styles+.navbox {&lt;br /&gt;
  margin-top:-1px&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox th,&lt;br /&gt;
.mw-parser-output .navbox-title,&lt;br /&gt;
.mw-parser-output .navbox-abovebelow {&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  padding-left:1em;&lt;br /&gt;
  padding-right:1em&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output th.navbox-group {&lt;br /&gt;
  white-space:nowrap;&lt;br /&gt;
  text-align:right&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox,&lt;br /&gt;
.mw-parser-output .navbox-subgroup {&lt;br /&gt;
  background:#fdfdfd&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-list {&lt;br /&gt;
  border-color:#fdfdfd&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox th,&lt;br /&gt;
.mw-parser-output .navbox-title {&lt;br /&gt;
  background:#eaeeff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-abovebelow,&lt;br /&gt;
.mw-parser-output th.navbox-group,&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-title {&lt;br /&gt;
  background:#ddddff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-group,&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-abovebelow {&lt;br /&gt;
  background:#e6e6ff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-even {&lt;br /&gt;
  background:#f7f7f7&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-odd {&lt;br /&gt;
  background:transparent&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output th.navbox-title1 {&lt;br /&gt;
  border-left:2px solid #fdfdfd;&lt;br /&gt;
  width:100%&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output td.navbox-list1 {&lt;br /&gt;
  text-align:left;&lt;br /&gt;
  border-left-width:2px;&lt;br /&gt;
  border-left-style:solid&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist td dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist td ol,&lt;br /&gt;
.mw-parser-output .navbox .hlist td ul,&lt;br /&gt;
.mw-parser-output .navbox td.hlist dl,&lt;br /&gt;
.mw-parser-output .navbox td.hlist ol,&lt;br /&gt;
.mw-parser-output .navbox td.hlist ul {&lt;br /&gt;
  padding:0.125em 0&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist dd,&lt;br /&gt;
.mw-parser-output .navbox .hlist dt,&lt;br /&gt;
.mw-parser-output .navbox .hlist li {&lt;br /&gt;
  white-space:nowrap&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist dd dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist dt dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist li ol,&lt;br /&gt;
.mw-parser-output .navbox .hlist li ul {&lt;br /&gt;
  white-space:normal&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output ol+.navbox-styles+.navbox,&lt;br /&gt;
.mw-parser-output ul+.navbox-styles+.navbox {&lt;br /&gt;
  margin-top:0.5em&lt;br /&gt;
}&lt;br /&gt;
@media screen {&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-title {&lt;br /&gt;
    background-color:#345&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-group {&lt;br /&gt;
    background-color:#567&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-subgroup td.navbox-title {&lt;br /&gt;
    background-color:#456&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-even {&lt;br /&gt;
    background-color:#000&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-odd {&lt;br /&gt;
    background-color:#123&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox-subgroup,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox-list {&lt;br /&gt;
    background-color:#111111&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-image,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-list {&lt;br /&gt;
    border-top:2px solid #111111&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
@media screen and (prefers-color-scheme:dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-title {&lt;br /&gt;
    background-color:#345&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-group {&lt;br /&gt;
    background-color:#567&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-subgroup td.navbox-title {&lt;br /&gt;
    background-color:#456&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-even {&lt;br /&gt;
    background-color:#000&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-odd {&lt;br /&gt;
    background-color:#123&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox-subgroup,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox-list {&lt;br /&gt;
    background-color:#111111&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-image,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-list {&lt;br /&gt;
    border-top:2px solid #111111&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
/* === End of Navbox styles === */&lt;br /&gt;
&lt;br /&gt;
@media screen {&lt;br /&gt;
	kbd.key {&lt;br /&gt;
		background-color: var(--background-color-neutral-subtle,#f8f9fa);&lt;br /&gt;
	    color: var(--color-emphasized,#101418);&lt;br /&gt;
	    border: 2.5px outset var(--border-color-muted,#dadde3);&lt;br /&gt;
	    border-radius: 2px;&lt;br /&gt;
    	padding: 1px 4px;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Tick&amp;diff=661</id>
		<title>Tick</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Tick&amp;diff=661"/>
		<updated>2025-09-10T20:51:22Z</updated>

		<summary type="html">&lt;p&gt;Felipe: use keys template&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;tick&#039;&#039;&#039; refers to one step of the game&#039;s logic simulation.&lt;br /&gt;
&lt;br /&gt;
Most [[component|components]] take one tick to update the state of their output after their input changes state. Thus, on some ticks, the state of a component&#039;s outputs will be &amp;quot;wrong&amp;quot; compared to the state of its inputs.&lt;br /&gt;
{{Todo|Add more general information about timing}}[[File:And gate activation delay.png|thumb|A demonstration of two AND gates being activated. The top AND gate has both its inputs active, but its output is not yet active because a delay of 1 tick has not yet passed. The bottom AND gate has had both its inputs active for at least 1 tick, and its output is therefore active.]]&lt;br /&gt;
&lt;br /&gt;
[[File:XOR gate activation delay.png|thumb|A demonstration of two XOR gates being activated. The top XOR gate has one of its inputs active, but its output is not yet active because a delay of 1 tick has not yet passed. The bottom XOR gate has had one of its inputs active for at least 1 tick, and its output is therefore active.]]&lt;br /&gt;
&lt;br /&gt;
== Simulation Speed ==&lt;br /&gt;
While every components state is calculated and updated at each tick, the number of ticks that occur in a second can be adjusted from as low as 1 tick per second (tps) all the way up to 10,000 tps.&lt;br /&gt;
&lt;br /&gt;
Adjustments can be made by opening up the simulation controls menu. By default, this is the {{keys|F9}} button.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Additionally, the simulation can be paused entirely, and stepped 1 tick at a time from the control menu. Useful for debugging circuits that don&#039;t behave like you&#039;d expect.&lt;br /&gt;
&lt;br /&gt;
You can also pause/resume the simulation or step the simulation using hotkeys on your keyboard.&lt;br /&gt;
&lt;br /&gt;
By default, this is {{keys|F10}} to pause/resume the simulation, and {{keys|shift+F10}} to step the simulation when paused.&lt;br /&gt;
{{Todo|Add screenshots of the simulation controls menu}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Keys&amp;diff=660</id>
		<title>Template:Keys</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Keys&amp;diff=660"/>
		<updated>2025-09-10T20:49:51Z</updated>

		<summary type="html">&lt;p&gt;Felipe: Created page with &amp;quot;&amp;lt;includeonly&amp;gt;{{#invoke: keys | keys }}&amp;lt;/includeonly&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;{{#invoke: keys | keys }}&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Module:Keys/Symbols&amp;diff=659</id>
		<title>Module:Keys/Symbols</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Module:Keys/Symbols&amp;diff=659"/>
		<updated>2025-09-10T20:48:48Z</updated>

		<summary type="html">&lt;p&gt;Felipe: Created page with &amp;quot;-- taken from https://minecraft.wiki/w/Module:Keys/Symbols  return { 	[&amp;#039;caps lock&amp;#039;] = &amp;#039;⮸ Caps Lock&amp;#039;, 	[&amp;#039;control&amp;#039;] = &amp;#039;Ctrl&amp;#039;, 	[&amp;#039;shift&amp;#039;] = &amp;#039;⇧ Shift&amp;#039;, 	[&amp;#039;lshift&amp;#039;] = &amp;#039;⇧ Left Shift&amp;#039;, 	[&amp;#039;rshift&amp;#039;] = &amp;#039;⇧ Right Shift&amp;#039;, 	[&amp;#039;enter&amp;#039;] = &amp;#039;↵ Enter&amp;#039;, 	[&amp;#039;return&amp;#039;] = &amp;#039;↵ Return&amp;#039;, 	[&amp;#039;cmd&amp;#039;] = &amp;#039;⌘ Cmd&amp;#039;, 	[&amp;#039;command&amp;#039;] = &amp;#039;⌘ Command&amp;#039;, 	[&amp;#039;opt&amp;#039;] = &amp;#039;⌥ Opt&amp;#039;, 	[&amp;#039;option&amp;#039;] = &amp;#039;⌥ Option&amp;#039;, 	[&amp;#039;tab&amp;#039;] = &amp;#039;Tab ↹&amp;#039;, 	[&amp;#039;backspace&amp;#039;] = &amp;#039;← Backspace&amp;#039;, 	[&amp;#039;win&amp;#039;] = &amp;#039;⊞ Win&amp;#039;, 	[&amp;#039;super&amp;#039;] = &amp;#039;⊞ Super&amp;#039;...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- taken from https://minecraft.wiki/w/Module:Keys/Symbols&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
	[&#039;caps lock&#039;] = &#039;⮸ Caps Lock&#039;,&lt;br /&gt;
	[&#039;control&#039;] = &#039;Ctrl&#039;,&lt;br /&gt;
	[&#039;shift&#039;] = &#039;⇧ Shift&#039;,&lt;br /&gt;
	[&#039;lshift&#039;] = &#039;⇧ Left Shift&#039;,&lt;br /&gt;
	[&#039;rshift&#039;] = &#039;⇧ Right Shift&#039;,&lt;br /&gt;
	[&#039;enter&#039;] = &#039;↵ Enter&#039;,&lt;br /&gt;
	[&#039;return&#039;] = &#039;↵ Return&#039;,&lt;br /&gt;
	[&#039;cmd&#039;] = &#039;⌘ Cmd&#039;,&lt;br /&gt;
	[&#039;command&#039;] = &#039;⌘ Command&#039;,&lt;br /&gt;
	[&#039;opt&#039;] = &#039;⌥ Opt&#039;,&lt;br /&gt;
	[&#039;option&#039;] = &#039;⌥ Option&#039;,&lt;br /&gt;
	[&#039;tab&#039;] = &#039;Tab ↹&#039;,&lt;br /&gt;
	[&#039;backspace&#039;] = &#039;← Backspace&#039;,&lt;br /&gt;
	[&#039;win&#039;] = &#039;⊞ Win&#039;,&lt;br /&gt;
	[&#039;super&#039;] = &#039;⊞ Super&#039;,&lt;br /&gt;
	[&#039;menu&#039;] = &#039;≣ Menu&#039;,&lt;br /&gt;
	[&#039;windows&#039;] = &#039;⊞ Windows&#039;,&lt;br /&gt;
	[&#039;up&#039;] = &#039;↑&#039;,&lt;br /&gt;
	[&#039;down&#039;] = &#039;↓&#039;,&lt;br /&gt;
	[&#039;left&#039;] = &#039;←&#039;,&lt;br /&gt;
	[&#039;right&#039;] = &#039;→&#039;,&lt;br /&gt;
	[&#039;page up&#039;] = &#039;⇞ Page Up&#039;,&lt;br /&gt;
	[&#039;page down&#039;] = &#039;⇟ Page Down&#039;,&lt;br /&gt;
	&lt;br /&gt;
	-- Left &amp;amp; right analog sticks&lt;br /&gt;
	[&#039;l-up&#039;] = &#039;L↑&#039;,&lt;br /&gt;
	[&#039;l up&#039;] = &#039;L↑&#039;,&lt;br /&gt;
	[&#039;l-down&#039;] = &#039;L↓&#039;,&lt;br /&gt;
	[&#039;l down&#039;] = &#039;L↓&#039;,&lt;br /&gt;
	[&#039;l-left&#039;] = &#039;L←&#039;,&lt;br /&gt;
	[&#039;l left&#039;] = &#039;L←&#039;,&lt;br /&gt;
	[&#039;l-right&#039;] = &#039;L→&#039;,&lt;br /&gt;
	[&#039;l right&#039;] = &#039;L→&#039;,&lt;br /&gt;
	[&#039;l-ne&#039;] = &#039;L↗&#039;,&lt;br /&gt;
	[&#039;l ne&#039;] = &#039;L↗&#039;,&lt;br /&gt;
	[&#039;l-se&#039;] = &#039;L↘&#039;,&lt;br /&gt;
	[&#039;l se&#039;] = &#039;L↘&#039;,&lt;br /&gt;
	[&#039;l-nw&#039;] = &#039;L↖&#039;,&lt;br /&gt;
	[&#039;l nw&#039;] = &#039;L↖&#039;,&lt;br /&gt;
	[&#039;l-sw&#039;] = &#039;L↙&#039;,&lt;br /&gt;
	[&#039;l sw&#039;] = &#039;L↙&#039;,&lt;br /&gt;
	&lt;br /&gt;
	[&#039;r-up&#039;] = &#039;R↑&#039;,&lt;br /&gt;
	[&#039;r up&#039;] = &#039;R↑&#039;,&lt;br /&gt;
	[&#039;r-down&#039;] = &#039;R↓&#039;,&lt;br /&gt;
	[&#039;r down&#039;] = &#039;R↓&#039;,&lt;br /&gt;
	[&#039;r-left&#039;] = &#039;R←&#039;,&lt;br /&gt;
	[&#039;r left&#039;] = &#039;R←&#039;,&lt;br /&gt;
	[&#039;r-right&#039;] = &#039;R→&#039;,&lt;br /&gt;
	[&#039;r right&#039;] = &#039;R→&#039;,&lt;br /&gt;
	[&#039;r-ne&#039;] = &#039;R↗&#039;,&lt;br /&gt;
	[&#039;r ne&#039;] = &#039;R↗&#039;,&lt;br /&gt;
	[&#039;r-se&#039;] = &#039;R↘&#039;,&lt;br /&gt;
	[&#039;r se&#039;] = &#039;R↘&#039;,&lt;br /&gt;
	[&#039;r-nw&#039;] = &#039;R↖&#039;,&lt;br /&gt;
	[&#039;r nw&#039;] = &#039;R↖&#039;,&lt;br /&gt;
	[&#039;r-sw&#039;] = &#039;R↙&#039;,&lt;br /&gt;
	[&#039;r sw&#039;] = &#039;R↙&#039;,&lt;br /&gt;
	&lt;br /&gt;
	-- PlayStation&lt;br /&gt;
	[&#039;ps x&#039;] = &#039;×&#039;,&lt;br /&gt;
	[&#039;ex&#039;] = &#039;×&#039;,&lt;br /&gt;
	[&#039;ps c&#039;] = &#039;○&#039;,&lt;br /&gt;
	[&#039;circle&#039;] = &#039;○&#039;,&lt;br /&gt;
	[&#039;ps s&#039;] = &#039;□&#039;,&lt;br /&gt;
	[&#039;square&#039;] = &#039;□&#039;,&lt;br /&gt;
	[&#039;ps t&#039;] = &#039;△&#039;,&lt;br /&gt;
	[&#039;triangle&#039;] = &#039;△&#039;,&lt;br /&gt;
	&lt;br /&gt;
	-- Nintendo 64 &amp;amp; GameCube&lt;br /&gt;
	[&#039;c-up&#039;] = &#039;C↑&#039;,&lt;br /&gt;
	[&#039;c up&#039;] = &#039;C↑&#039;,&lt;br /&gt;
	[&#039;c-down&#039;] = &#039;C↓&#039;,&lt;br /&gt;
	[&#039;c down&#039;] = &#039;C↓&#039;,&lt;br /&gt;
	[&#039;c-left&#039;] = &#039;C←&#039;,&lt;br /&gt;
	[&#039;c left&#039;] = &#039;C←&#039;,&lt;br /&gt;
	[&#039;c-right&#039;] = &#039;C→&#039;,&lt;br /&gt;
	[&#039;c right&#039;] = &#039;C→&#039;,&lt;br /&gt;
	[&#039;c-ne&#039;] = &#039;C↗&#039;,&lt;br /&gt;
	[&#039;c ne&#039;] = &#039;C↗&#039;,&lt;br /&gt;
	[&#039;c-se&#039;] = &#039;C↘&#039;,&lt;br /&gt;
	[&#039;c se&#039;] = &#039;C↘&#039;,&lt;br /&gt;
	[&#039;c-nw&#039;] = &#039;C↖&#039;,&lt;br /&gt;
	[&#039;c nw&#039;] = &#039;C↖&#039;,&lt;br /&gt;
	[&#039;c-sw&#039;] = &#039;C↙&#039;,&lt;br /&gt;
	[&#039;c sw&#039;] = &#039;C↙&#039;,&lt;br /&gt;
	&lt;br /&gt;
	-- Xbox Controller&lt;br /&gt;
	[&#039;xb-a&#039;] = &#039;Ⓐ&#039;,&lt;br /&gt;
	[&#039;xb-b&#039;] = &#039;Ⓑ&#039;,&lt;br /&gt;
	[&#039;xb-x&#039;] = &#039;Ⓧ&#039;,&lt;br /&gt;
	[&#039;xb-y&#039;] = &#039;Ⓨ&#039;,&lt;br /&gt;
	[&#039;xb-lb&#039;] = &#039;╼&#039;,&lt;br /&gt;
	[&#039;xb-rb&#039;] = &#039;╾&#039;,&lt;br /&gt;
	[&#039;xb-lt&#039;] = &#039;⟅&#039;,&lt;br /&gt;
	[&#039;xb-rt&#039;] = &#039;⟆&#039;,&lt;br /&gt;
	[&#039;xb-back&#039;] = &#039;◀&#039;,&lt;br /&gt;
	[&#039;xb-start&#039;] = &#039;▶&#039;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Module:Keys&amp;diff=658</id>
		<title>Module:Keys</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Module:Keys&amp;diff=658"/>
		<updated>2025-09-10T20:48:22Z</updated>

		<summary type="html">&lt;p&gt;Felipe: Created page with &amp;quot;-- taken from https://minecraft.wiki/w/Module:Keys  local p = {} p.keys = function( f ) 	local args = f 	if f == mw.getCurrentFrame() then 		args = f:getParent().args 	end 	local keys = {} 	 	for _, key in ipairs( args ) do 		key = mw.text.trim( key ) 		if key ~= &amp;#039;+&amp;#039; and key:find( &amp;#039;%+&amp;#039; ) then 			local comboKeys = {} 			for comboKey in mw.text.gsplit( key, &amp;#039;%s*%+%s*&amp;#039; ) do 				table.insert( comboKeys, p.key( comboKey ) ) 			end 			table.insert( keys, table.concat( comboKey...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- taken from https://minecraft.wiki/w/Module:Keys&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
p.keys = function( f )&lt;br /&gt;
	local args = f&lt;br /&gt;
	if f == mw.getCurrentFrame() then&lt;br /&gt;
		args = f:getParent().args&lt;br /&gt;
	end&lt;br /&gt;
	local keys = {}&lt;br /&gt;
	&lt;br /&gt;
	for _, key in ipairs( args ) do&lt;br /&gt;
		key = mw.text.trim( key )&lt;br /&gt;
		if key ~= &#039;+&#039; and key:find( &#039;%+&#039; ) then&lt;br /&gt;
			local comboKeys = {}&lt;br /&gt;
			for comboKey in mw.text.gsplit( key, &#039;%s*%+%s*&#039; ) do&lt;br /&gt;
				table.insert( comboKeys, p.key( comboKey ) )&lt;br /&gt;
			end&lt;br /&gt;
			table.insert( keys, table.concat( comboKeys, &#039;&amp;amp;#8239;+&amp;amp;#8239;&#039; ) )&lt;br /&gt;
		else&lt;br /&gt;
			table.insert( keys, p.key( key ) )&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return table.concat( keys )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.key = function( key )&lt;br /&gt;
	if key == &#039;&#039; then&lt;br /&gt;
		return &#039;&#039;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local symbols = mw.loadData( &#039;Module:Keys/Symbols&#039; )&lt;br /&gt;
	return &#039;&amp;lt;kbd class=&amp;quot;key nowrap&amp;quot;&amp;gt;&#039; .. ( symbols[key:lower()] or key ) .. &#039;&amp;lt;/kbd&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Buffer&amp;diff=644</id>
		<title>Buffer</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Buffer&amp;diff=644"/>
		<updated>2025-09-10T10:07:48Z</updated>

		<summary type="html">&lt;p&gt;Felipe: add fast buffer on see also section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox component&lt;br /&gt;
| title       = Buffer&lt;br /&gt;
| id          = MHG.BufferWithOutput&lt;br /&gt;
| caption     = Buffer&lt;br /&gt;
| configurable = 0&lt;br /&gt;
| io.minInputs        = 1&lt;br /&gt;
| io.maxInputs        = 1&lt;br /&gt;
| io.outputs          = 1&lt;br /&gt;
| io.propagationDelay = 1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Buffer&#039;&#039;&#039; is a [[component]] used for [[digital logic]]. It has one input and one output, and the output is updated to propagate the same state of the input. Buffers are useful for preventing back-propagation of signals, because signals can only propagate from the input to the output, not in the other direction.&lt;br /&gt;
&lt;br /&gt;
== Behavior ==&lt;br /&gt;
The Buffer simply passes on the state of its input to its output.&lt;br /&gt;
&lt;br /&gt;
{{Truth table auto&lt;br /&gt;
| inputs=1&lt;br /&gt;
| outputs=1&lt;br /&gt;
| caption=Buffer Truth Table&lt;br /&gt;
| expr1=a&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The Buffer takes one [[tick]] to update the state of its output after a change to the state of its input.&lt;br /&gt;
&lt;br /&gt;
== Placement ==&lt;br /&gt;
&lt;br /&gt;
The Buffer can be placed in the center of a [[Circuit Board]] square or on top of a [[Mount]], and can be fine-rotated.&lt;br /&gt;
&lt;br /&gt;
{{todo|image of different ways Buffer can be placed}}&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Fast Buffer]]&lt;br /&gt;
&lt;br /&gt;
{{Navbox components}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Vanilla components]]&lt;br /&gt;
[[Category:Circuitry components]]&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Delayer&amp;diff=643</id>
		<title>Delayer</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Delayer&amp;diff=643"/>
		<updated>2025-09-10T10:06:44Z</updated>

		<summary type="html">&lt;p&gt;Felipe: fix typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox component&lt;br /&gt;
| title       = Delayer&lt;br /&gt;
| id          = MHG.Delayer&lt;br /&gt;
| caption     = Delayer set to 10 ticks of delay&lt;br /&gt;
| configurable = 1&lt;br /&gt;
| io.minInputs        = 1&lt;br /&gt;
| io.maxInputs        = 1&lt;br /&gt;
| io.outputs          = 1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Delayer&#039;&#039;&#039; is a [[component]] used for [[digital logic]]. It propagates signals from its input to its output, similar to a [[Buffer]], but the signal takes more time to propagate. The signal is &#039;&#039;delayed&#039;&#039;, if you will.&lt;br /&gt;
&lt;br /&gt;
Delayers are useful for building [[clock]] circuits.&lt;br /&gt;
&lt;br /&gt;
== Behavior ==&lt;br /&gt;
&lt;br /&gt;
The output of a Delayer will change to reflect the state of the input, as long as the input remains stable at that state for the configured delay length.&lt;br /&gt;
&lt;br /&gt;
{{Truth table&lt;br /&gt;
| inputs=Input&lt;br /&gt;
| outputs=Output (eventually)&lt;br /&gt;
| caption=Fast Buffer Truth Table&lt;br /&gt;
| 0 0&lt;br /&gt;
| 1 1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{todo|add more details about the tick-by-tick behavior when the input changes state faster than the delay length}}&lt;br /&gt;
&lt;br /&gt;
== Configurability ==&lt;br /&gt;
&lt;br /&gt;
The length of the delay can be configured by [[editing]] the component. The default delay is 10 ticks, the minimum is 2 ticks and the maximum is 30 ticks.&lt;br /&gt;
&lt;br /&gt;
== Placement ==&lt;br /&gt;
&lt;br /&gt;
The Delayer can be placed in the center of a [[Circuit Board]] square or on top of a [[Mount]], and can be fine-rotated.&lt;br /&gt;
&lt;br /&gt;
== Tips ==&lt;br /&gt;
&lt;br /&gt;
* If you need to delay for longer than 30 ticks, you can chain multiple Delayers together.&lt;br /&gt;
* If you need to delay for only 1 tick, you can use a [[Buffer]] instead.&lt;br /&gt;
&lt;br /&gt;
{{Navbox components}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Vanilla components]]&lt;br /&gt;
[[Category:Circuitry components]]&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Truth_table_auto&amp;diff=640</id>
		<title>Template:Truth table auto</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Truth_table_auto&amp;diff=640"/>
		<updated>2025-09-09T19:00:33Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&amp;lt;templatedata&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;params&amp;quot;: {&lt;br /&gt;
		&amp;quot;caption&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Title to show above the table&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;inputs&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Number of inputs or list of input names&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;\&amp;quot;3\&amp;quot;, \&amp;quot;A B C\&amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;outputs&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Number of outputs or list of output names&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;\&amp;quot;2\&amp;quot;, \&amp;quot;X Y\&amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr1&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #1&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr2&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #2&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr3&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #3&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr4&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #4&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr5&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #5&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr6&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #6&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;description&amp;quot;: &amp;quot;Renders a truth table with any number of inputs and outputs.\n\nIMPORTANT: Make sure to use \&amp;quot;+\&amp;quot; instead of \&amp;quot;|\&amp;quot; as the OR operator. Wikitest parses the \&amp;quot;|\&amp;quot; as a property separator unless you instead use \&amp;quot;{{!}}\&amp;quot;, but the plus sign is better for readability.&amp;quot;,&lt;br /&gt;
	&amp;quot;paramOrder&amp;quot;: [&lt;br /&gt;
		&amp;quot;caption&amp;quot;,&lt;br /&gt;
		&amp;quot;inputs&amp;quot;,&lt;br /&gt;
		&amp;quot;outputs&amp;quot;,&lt;br /&gt;
		&amp;quot;expr1&amp;quot;,&lt;br /&gt;
		&amp;quot;expr2&amp;quot;,&lt;br /&gt;
		&amp;quot;expr3&amp;quot;,&lt;br /&gt;
		&amp;quot;expr4&amp;quot;,&lt;br /&gt;
		&amp;quot;expr5&amp;quot;,&lt;br /&gt;
		&amp;quot;expr6&amp;quot;&lt;br /&gt;
	],&lt;br /&gt;
	&amp;quot;format&amp;quot;: &amp;quot;block&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/templatedata&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&amp;lt;includeonly&amp;gt;{{#invoke:LogicUtils|truth_table_auto}}&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Truth_table_auto&amp;diff=639</id>
		<title>Template:Truth table auto</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Truth_table_auto&amp;diff=639"/>
		<updated>2025-09-09T18:59:16Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&amp;lt;templatedata&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;params&amp;quot;: {&lt;br /&gt;
		&amp;quot;caption&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Title to show above the table&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;inputs&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Number of inputs or list of input names&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;\&amp;quot;3\&amp;quot;, \&amp;quot;A B C\&amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;outputs&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Number of outputs or list of output names&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;\&amp;quot;2\&amp;quot;, \&amp;quot;X Y\&amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr1&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #1&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr2&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #2&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr3&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #3&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr4&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #4&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr5&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #5&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr6&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #6&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) + c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;description&amp;quot;: &amp;quot;Renders a truth table with any number of inputs and outputs&amp;quot;,&lt;br /&gt;
	&amp;quot;paramOrder&amp;quot;: [&lt;br /&gt;
		&amp;quot;caption&amp;quot;,&lt;br /&gt;
		&amp;quot;inputs&amp;quot;,&lt;br /&gt;
		&amp;quot;outputs&amp;quot;,&lt;br /&gt;
		&amp;quot;expr1&amp;quot;,&lt;br /&gt;
		&amp;quot;expr2&amp;quot;,&lt;br /&gt;
		&amp;quot;expr3&amp;quot;,&lt;br /&gt;
		&amp;quot;expr4&amp;quot;,&lt;br /&gt;
		&amp;quot;expr5&amp;quot;,&lt;br /&gt;
		&amp;quot;expr6&amp;quot;&lt;br /&gt;
	],&lt;br /&gt;
	&amp;quot;format&amp;quot;: &amp;quot;block&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/templatedata&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&amp;lt;includeonly&amp;gt;{{#invoke:LogicUtils|truth_table_auto}}&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=638</id>
		<title>User:Felipe/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=638"/>
		<updated>2025-09-09T18:58:22Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Binary signal&lt;br /&gt;
| signals=Input A, Input B, Output&lt;br /&gt;
| signal1=0111?0011100011100011100011100011100011100011100011100&lt;br /&gt;
| signal2=00?100001100001100001100001100001100001100001100001100&lt;br /&gt;
| signal3=0001100??110000110000110000110000110000110000110000110&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Truth table auto&lt;br /&gt;
| inputs=3&lt;br /&gt;
| outputs=1&lt;br /&gt;
| expr1=a + b + c&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=637</id>
		<title>Module:LogicUtils</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=637"/>
		<updated>2025-09-09T18:57:45Z</updated>

		<summary type="html">&lt;p&gt;Felipe: support math style binary operators&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
local bit32 = require( &#039;bit32&#039; )&lt;br /&gt;
&lt;br /&gt;
function parse_io(kind, row, str)&lt;br /&gt;
    local count = tonumber(str)&lt;br /&gt;
    local names = {}&lt;br /&gt;
&lt;br /&gt;
    if count == nil then&lt;br /&gt;
        -- str is a list of IO names&lt;br /&gt;
        names = mw.text.split(str, &amp;quot;,&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        -- str is a number&lt;br /&gt;
        if count == 1 then&lt;br /&gt;
            names[1] = kind&lt;br /&gt;
        else&lt;br /&gt;
            for i=1,count do&lt;br /&gt;
                names[i] = string.format(&amp;quot;%s %i&amp;quot;, kind, i)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for i, v in ipairs(names) do&lt;br /&gt;
        row:tag(&#039;th&#039;)&lt;br /&gt;
            :wikitext(v)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return #names&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isBinOp(c)&lt;br /&gt;
    return c == &amp;quot;&amp;amp;&amp;quot; or c == &amp;quot;|&amp;quot; or c == &amp;quot;^&amp;quot; or c == &amp;quot;+&amp;quot; or c == &amp;quot;*&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function letterToCell(c)&lt;br /&gt;
    return c:lower():byte() - 96&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function eval(expr, values)&lt;br /&gt;
    local stack = {}&lt;br /&gt;
&lt;br /&gt;
    for _, c in pairs(expr) do&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
            local v2 = table.remove(stack)&lt;br /&gt;
            if v2 == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
            if c == &amp;quot;&amp;amp;&amp;quot; or c == &amp;quot;*&amp;quot; then&lt;br /&gt;
                table.insert(stack, v and v2)&lt;br /&gt;
            elseif c == &amp;quot;|&amp;quot; or c == &amp;quot;+&amp;quot; then&lt;br /&gt;
                table.insert(stack, v or v2)&lt;br /&gt;
            elseif c == &amp;quot;^&amp;quot; then&lt;br /&gt;
                table.insert(stack, v ~= v2)&lt;br /&gt;
            end&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand after &#039;!&#039;&amp;quot;) end&lt;br /&gt;
            table.insert(stack, not v)&lt;br /&gt;
        else&lt;br /&gt;
            local index = letterToCell(c) -- &#039;a&#039; = 1, &#039;b&#039; = 2, etc.&lt;br /&gt;
            if index then&lt;br /&gt;
                if values[index] == nil and not ignoreMissing then error(&amp;quot;Value for variable &#039;&amp;quot; .. c .. &amp;quot;&#039; not provided&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
                table.insert(stack, values[index] or false)&lt;br /&gt;
            else&lt;br /&gt;
                error(&amp;quot;Invalid character in expression: &amp;quot; .. c)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return stack[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function evalAll(expr, num_vars, callback)&lt;br /&gt;
    local totalCombinations = 2 ^ num_vars&lt;br /&gt;
&lt;br /&gt;
    for i = 0, totalCombinations - 1 do&lt;br /&gt;
        local values = {}&lt;br /&gt;
        for j = 1, num_vars do&lt;br /&gt;
            values[j] = bit32.band(i, bit32.lshift(1, num_vars - j)) ~= 0&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        local result = eval(expr, values)&lt;br /&gt;
        callback(values, result)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function algebraicToRPN(input)&lt;br /&gt;
    local output = {}&lt;br /&gt;
    local opstack = {}&lt;br /&gt;
&lt;br /&gt;
    input:gsub(&amp;quot;[^%s]&amp;quot;, function(c)&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            while opstack[#opstack] == &amp;quot;!&amp;quot; do&lt;br /&gt;
                table.insert(output, table.remove(opstack))&lt;br /&gt;
            end&lt;br /&gt;
&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; or c == &amp;quot;(&amp;quot; then&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;)&amp;quot; then&lt;br /&gt;
            while #opstack &amp;gt; 0 do&lt;br /&gt;
                local op = table.remove(opstack)&lt;br /&gt;
                if op == &amp;quot;(&amp;quot; then&lt;br /&gt;
                    break&lt;br /&gt;
                end&lt;br /&gt;
                table.insert(output, op)&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
            table.insert(output, c)&lt;br /&gt;
        end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    -- Drain the operator stack to the output&lt;br /&gt;
    while #opstack &amp;gt; 0 do table.insert(output, table.remove(opstack)) end&lt;br /&gt;
&lt;br /&gt;
    return output&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderCell(row, value)&lt;br /&gt;
    local style = &amp;quot;color:white; text-align:center;&amp;quot;&lt;br /&gt;
    if value == false or value == &amp;quot;0&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#1f1e1e;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;0&amp;quot;)&lt;br /&gt;
    elseif value == true or value == &amp;quot;1&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#fd140f;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;1&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :cssText(&amp;quot;text-align:center&amp;quot;)&lt;br /&gt;
            :wikitext(value)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    local i = 1&lt;br /&gt;
    while args[i] ~= nil do&lt;br /&gt;
        local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(args[i], &amp;quot;[^%s]+&amp;quot;) do&lt;br /&gt;
            renderCell(row, token)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table_auto = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    local exprs = {}&lt;br /&gt;
    for i = 1,nOutputs do&lt;br /&gt;
        local exprStr = args[&amp;quot;expr&amp;quot;..i]&lt;br /&gt;
        if exprStr == nil then error(&amp;quot;Missing expression for output &amp;quot; .. i) end&lt;br /&gt;
        table.insert(exprs, algebraicToRPN(exprStr))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local totalCombinations = 2 ^ nInputs&lt;br /&gt;
    local values = {}&lt;br /&gt;
&lt;br /&gt;
    for i = 0,totalCombinations - 1 do&lt;br /&gt;
        local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
&lt;br /&gt;
        for j = 1, nInputs do&lt;br /&gt;
            values[j] = bit32.band(i, bit32.lshift(1, nInputs - j)) ~= 0&lt;br /&gt;
            renderCell(row, values[j])&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        for _, expr in pairs(exprs) do&lt;br /&gt;
            renderCell(row, eval(expr, values))&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 0, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-header-cell&#039;)&lt;br /&gt;
            :wikitext(tostring(i))&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer-row&amp;quot;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-spacer-row-cell&#039;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-row&amp;quot;)&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :wikitext(signal.name)&lt;br /&gt;
&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    local prevValue = nil&lt;br /&gt;
    for _, v in pairs(signal.values) do&lt;br /&gt;
        if v == &amp;quot;nil&amp;quot; then v = nil end&lt;br /&gt;
&lt;br /&gt;
        local cell = row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;signal-value-cell&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        if v == 1 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-top&amp;quot;)&lt;br /&gt;
        elseif v == 0 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-bottom&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if prevValue ~= nil and v ~= nil and v ~= prevValue then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-left&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        prevValue = v&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraph(parent, signals)&lt;br /&gt;
    local tickCount = #signals[1].values&lt;br /&gt;
&lt;br /&gt;
    local tbl = parent:tag(&#039;table&#039;)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    for _, signal in pairs(signals) do&lt;br /&gt;
        renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
        renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.binary_signal_graph = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local signalNames = mw.text.split(args.signals, &amp;quot;,&amp;quot;)&lt;br /&gt;
    local signals = {}&lt;br /&gt;
&lt;br /&gt;
    local container = mw.html.create(&#039;div&#039;)&lt;br /&gt;
        :addClass(&#039;signal-graph-table&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1,#signalNames do&lt;br /&gt;
        local str = args[&amp;quot;signal&amp;quot;..i]&lt;br /&gt;
        local values = {}&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(str, &amp;quot;[01?]&amp;quot;) do&lt;br /&gt;
            if token == &amp;quot;?&amp;quot; then&lt;br /&gt;
                table.insert(values, &amp;quot;nil&amp;quot;) -- lua tables can&#039;t store nil&lt;br /&gt;
            else&lt;br /&gt;
                table.insert(values, tonumber(token))&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        table.insert(signals, { name = mw.text.trim(signalNames[i]), values = values })&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraph(container, signals)&lt;br /&gt;
&lt;br /&gt;
    return tostring(container)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=636</id>
		<title>User:Felipe/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=636"/>
		<updated>2025-09-09T18:52:49Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Binary signal&lt;br /&gt;
| signals=Input A, Input B, Output&lt;br /&gt;
| signal1=0111?0011100011100011100011100011100011100011100011100&lt;br /&gt;
| signal2=00?100001100001100001100001100001100001100001100001100&lt;br /&gt;
| signal3=0001100??110000110000110000110000110000110000110000110&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Truth table auto&lt;br /&gt;
| inputs=3&lt;br /&gt;
| outputs=1&lt;br /&gt;
| expr1=a | b | c&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=635</id>
		<title>Module:LogicUtils</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=635"/>
		<updated>2025-09-09T18:51:25Z</updated>

		<summary type="html">&lt;p&gt;Felipe: properly support multiple outputs&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
local bit32 = require( &#039;bit32&#039; )&lt;br /&gt;
&lt;br /&gt;
function parse_io(kind, row, str)&lt;br /&gt;
    local count = tonumber(str)&lt;br /&gt;
    local names = {}&lt;br /&gt;
&lt;br /&gt;
    if count == nil then&lt;br /&gt;
        -- str is a list of IO names&lt;br /&gt;
        names = mw.text.split(str, &amp;quot;,&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        -- str is a number&lt;br /&gt;
        if count == 1 then&lt;br /&gt;
            names[1] = kind&lt;br /&gt;
        else&lt;br /&gt;
            for i=1,count do&lt;br /&gt;
                names[i] = string.format(&amp;quot;%s %i&amp;quot;, kind, i)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for i, v in ipairs(names) do&lt;br /&gt;
        row:tag(&#039;th&#039;)&lt;br /&gt;
            :wikitext(v)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return #names&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isBinOp(c)&lt;br /&gt;
    return c == &amp;quot;&amp;amp;&amp;quot; or c == &amp;quot;|&amp;quot; or c == &amp;quot;^&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function letterToCell(c)&lt;br /&gt;
    return c:lower():byte() - 96&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function eval(expr, values)&lt;br /&gt;
    local stack = {}&lt;br /&gt;
&lt;br /&gt;
    for _, c in pairs(expr) do&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
            local v2 = table.remove(stack)&lt;br /&gt;
            if v2 == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
            if c == &amp;quot;&amp;amp;&amp;quot; then&lt;br /&gt;
                table.insert(stack, v and v2)&lt;br /&gt;
            elseif c == &amp;quot;|&amp;quot; then&lt;br /&gt;
                table.insert(stack, v or v2)&lt;br /&gt;
            elseif c == &amp;quot;^&amp;quot; then&lt;br /&gt;
                table.insert(stack, v ~= v2)&lt;br /&gt;
            end&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand after &#039;!&#039;&amp;quot;) end&lt;br /&gt;
            table.insert(stack, not v)&lt;br /&gt;
        else&lt;br /&gt;
            local index = letterToCell(c) -- &#039;a&#039; = 1, &#039;b&#039; = 2, etc.&lt;br /&gt;
            if index then&lt;br /&gt;
                if values[index] == nil and not ignoreMissing then error(&amp;quot;Value for variable &#039;&amp;quot; .. c .. &amp;quot;&#039; not provided&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
                table.insert(stack, values[index] or false)&lt;br /&gt;
            else&lt;br /&gt;
                error(&amp;quot;Invalid character in expression: &amp;quot; .. c)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return stack[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function evalAll(expr, num_vars, callback)&lt;br /&gt;
    local totalCombinations = 2 ^ num_vars&lt;br /&gt;
&lt;br /&gt;
    for i = 0, totalCombinations - 1 do&lt;br /&gt;
        local values = {}&lt;br /&gt;
        for j = 1, num_vars do&lt;br /&gt;
            values[j] = bit32.band(i, bit32.lshift(1, num_vars - j)) ~= 0&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        local result = eval(expr, values)&lt;br /&gt;
        callback(values, result)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function algebraicToRPN(input)&lt;br /&gt;
    local output = {}&lt;br /&gt;
    local opstack = {}&lt;br /&gt;
&lt;br /&gt;
    input:gsub(&amp;quot;[^%s]&amp;quot;, function(c)&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            while opstack[#opstack] == &amp;quot;!&amp;quot; do&lt;br /&gt;
                table.insert(output, table.remove(opstack))&lt;br /&gt;
            end&lt;br /&gt;
&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; or c == &amp;quot;(&amp;quot; then&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;)&amp;quot; then&lt;br /&gt;
            while #opstack &amp;gt; 0 do&lt;br /&gt;
                local op = table.remove(opstack)&lt;br /&gt;
                if op == &amp;quot;(&amp;quot; then&lt;br /&gt;
                    break&lt;br /&gt;
                end&lt;br /&gt;
                table.insert(output, op)&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
            table.insert(output, c)&lt;br /&gt;
        end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    -- Drain the operator stack to the output&lt;br /&gt;
    while #opstack &amp;gt; 0 do table.insert(output, table.remove(opstack)) end&lt;br /&gt;
&lt;br /&gt;
    return output&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderCell(row, value)&lt;br /&gt;
    local style = &amp;quot;color:white; text-align:center;&amp;quot;&lt;br /&gt;
    if value == false or value == &amp;quot;0&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#1f1e1e;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;0&amp;quot;)&lt;br /&gt;
    elseif value == true or value == &amp;quot;1&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#fd140f;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;1&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :cssText(&amp;quot;text-align:center&amp;quot;)&lt;br /&gt;
            :wikitext(value)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    local i = 1&lt;br /&gt;
    while args[i] ~= nil do&lt;br /&gt;
        local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(args[i], &amp;quot;[^%s]+&amp;quot;) do&lt;br /&gt;
            renderCell(row, token)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table_auto = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    local exprs = {}&lt;br /&gt;
    for i = 1,nOutputs do&lt;br /&gt;
        local exprStr = args[&amp;quot;expr&amp;quot;..i]&lt;br /&gt;
        if exprStr == nil then error(&amp;quot;Missing expression for output &amp;quot; .. i) end&lt;br /&gt;
        table.insert(exprs, algebraicToRPN(exprStr))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local totalCombinations = 2 ^ nInputs&lt;br /&gt;
    local values = {}&lt;br /&gt;
&lt;br /&gt;
    for i = 0,totalCombinations - 1 do&lt;br /&gt;
        local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
&lt;br /&gt;
        for j = 1, nInputs do&lt;br /&gt;
            values[j] = bit32.band(i, bit32.lshift(1, nInputs - j)) ~= 0&lt;br /&gt;
            renderCell(row, values[j])&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        for _, expr in pairs(exprs) do&lt;br /&gt;
            renderCell(row, eval(expr, values))&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 0, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-header-cell&#039;)&lt;br /&gt;
            :wikitext(tostring(i))&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer-row&amp;quot;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-spacer-row-cell&#039;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-row&amp;quot;)&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :wikitext(signal.name)&lt;br /&gt;
&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    mw.log(mw.dumpObject(signal))&lt;br /&gt;
&lt;br /&gt;
    local prevValue = nil&lt;br /&gt;
    for _, v in pairs(signal.values) do&lt;br /&gt;
        if v == &amp;quot;nil&amp;quot; then v = nil end&lt;br /&gt;
&lt;br /&gt;
        local cell = row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;signal-value-cell&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        if v == 1 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-top&amp;quot;)&lt;br /&gt;
        elseif v == 0 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-bottom&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if prevValue ~= nil and v ~= nil and v ~= prevValue then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-left&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        prevValue = v&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraph(parent, signals)&lt;br /&gt;
    local tickCount = #signals[1].values&lt;br /&gt;
&lt;br /&gt;
    local tbl = parent:tag(&#039;table&#039;)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    for _, signal in pairs(signals) do&lt;br /&gt;
        renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
        renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.binary_signal_graph = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local signalNames = mw.text.split(args.signals, &amp;quot;,&amp;quot;)&lt;br /&gt;
    local signals = {}&lt;br /&gt;
&lt;br /&gt;
    local container = mw.html.create(&#039;div&#039;)&lt;br /&gt;
        :addClass(&#039;signal-graph-table&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1,#signalNames do&lt;br /&gt;
        local str = args[&amp;quot;signal&amp;quot;..i]&lt;br /&gt;
        local values = {}&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(str, &amp;quot;[01?]&amp;quot;) do&lt;br /&gt;
            if token == &amp;quot;?&amp;quot; then&lt;br /&gt;
                table.insert(values, &amp;quot;nil&amp;quot;) -- lua tables can&#039;t store nil&lt;br /&gt;
            else&lt;br /&gt;
                table.insert(values, tonumber(token))&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        table.insert(signals, { name = mw.text.trim(signalNames[i]), values = values })&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraph(container, signals)&lt;br /&gt;
&lt;br /&gt;
    return tostring(container)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=631</id>
		<title>User:Felipe/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=631"/>
		<updated>2025-09-09T18:41:18Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Binary signal&lt;br /&gt;
| signals=Input A, Input B, Output&lt;br /&gt;
| signal1=0111?0011100011100011100011100011100011100011100011100&lt;br /&gt;
| signal2=00?100001100001100001100001100001100001100001100001100&lt;br /&gt;
| signal3=0001100??110000110000110000110000110000110000110000110&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Truth table auto&lt;br /&gt;
| inputs=3&lt;br /&gt;
| outputs=2&lt;br /&gt;
| expr1=a | b | c&lt;br /&gt;
| expr2=a ^ b &amp;amp; c&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Talk:Decoder&amp;diff=630</id>
		<title>Talk:Decoder</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Talk:Decoder&amp;diff=630"/>
		<updated>2025-09-09T18:40:06Z</updated>

		<summary type="html">&lt;p&gt;Felipe: /* Spacing */ Reply&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Spacing ==&lt;br /&gt;
&lt;br /&gt;
I&#039;m not a fan of the spacing after the Behavior section, it doesn&#039;t look great on mobile. Is there another way of doing it? [[User:Felipe|Felipe]] ([[User talk:Felipe|talk]]) 18:23, 9 September 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
:i changed it, hopefully it&#039;s better [[User:DjSapsan|DjSapsan]] ([[User talk:DjSapsan|talk]]) 18:38, 9 September 2025 (UTC)&lt;br /&gt;
::Yup, looks good on mobile now [[User:Felipe|Felipe]] ([[User talk:Felipe|talk]]) 18:40, 9 September 2025 (UTC)&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Talk:Decoder&amp;diff=627</id>
		<title>Talk:Decoder</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Talk:Decoder&amp;diff=627"/>
		<updated>2025-09-09T18:23:12Z</updated>

		<summary type="html">&lt;p&gt;Felipe: /* Spacing */ new section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Spacing ==&lt;br /&gt;
&lt;br /&gt;
I&#039;m not a fan of the spacing after the Behavior section, it doesn&#039;t look great on mobile. Is there another way of doing it? [[User:Felipe|Felipe]] ([[User talk:Felipe|talk]]) 18:23, 9 September 2025 (UTC)&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Decoder&amp;diff=626</id>
		<title>Decoder</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Decoder&amp;diff=626"/>
		<updated>2025-09-09T18:21:36Z</updated>

		<summary type="html">&lt;p&gt;Felipe: improve &amp;quot;see also&amp;quot; formatting&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;; &#039;&#039;&#039;Decoder&#039;&#039;&#039;&lt;br /&gt;
: In &#039;&#039;Logic World&#039;&#039;, a &#039;&#039;&#039;Decoder&#039;&#039;&#039; is a circuit that takes a binary input and activates one specific output bit corresponding to that input value.&lt;br /&gt;
: It is commonly used to select one element out of many, for example a cell in memory.&lt;br /&gt;
: See also [[Encoder]], which performs the reverse operation.&lt;br /&gt;
:[[File:4bit-decoder.png|thumb|right|4-bit decoder. Input on the left is 1110. The 14th output bit on the right is activated.]]&lt;br /&gt;
&lt;br /&gt;
== Behavior ==&lt;br /&gt;
&lt;br /&gt;
The Decoder&#039;s output is always a single bit that corresponds to the input value, effectively translating a binary value into a [[wikipedia:Unary_numeral_system|unary]] value.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Construction ==&lt;br /&gt;
&lt;br /&gt;
A Decoder is one of the simplest circuits to build.  &lt;br /&gt;
First, determine how many bits you need.  &lt;br /&gt;
The number of output bits from input bits is given by:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;o = 2^i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And vice versa:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;i = \log_2(o)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, if you want 256 outputs, you will need a decoder with 8 input bits.  &lt;br /&gt;
Each output has a specific pattern of {{on}} and {{off}} bits coming from the input. Here are the steps to build it:&lt;br /&gt;
&lt;br /&gt;
* Place a row of input items.  &lt;br /&gt;
* Connect each input bit to a NOT gate to get its inverted value. And add a Buffer to pass the non-inverted value and match the delay with the NOT gate.  &lt;br /&gt;
* Place a row of NOT gates to serve as the outputs.  &lt;br /&gt;
* Go through each output NOT gate and connect them to the corresponding inputs (normal or inverted) to match its binary value. For example, the output corresponding to number 12 (binary 1100) should receive the following binary values:&lt;br /&gt;
** 1st bit – normal  &lt;br /&gt;
** 2nd bit – normal  &lt;br /&gt;
** 3rd bit – inverted  &lt;br /&gt;
** 4th bit – inverted  &lt;br /&gt;
:and so on for each value.&lt;br /&gt;
&lt;br /&gt;
You can design a decoder in different ways. If you are building one for the first time, you may want to explicitly place each wire and gate to match the real electronic analog or to be visually understandable.&lt;br /&gt;
&lt;br /&gt;
When you become more experienced, you can skip unnecessary details and make it as simple as the screenshot above demonstrates. This design, optimized for speed and size, uses the trick of connecting wires from &#039;&#039;&#039;output&#039;&#039;&#039; pins directly to the required items, avoiding additional buffers and wire routing. In the screenshot below you can see the side view how to make it compact.  &lt;br /&gt;
&lt;br /&gt;
[[File:Decoder-side.png|thumb|center|alt=Side view of the decoder|Side view of the decoder]]&lt;br /&gt;
&lt;br /&gt;
== Speed ==&lt;br /&gt;
&lt;br /&gt;
The compact design above computes the output in 2 ticks.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Encoder]]&lt;br /&gt;
* [[Lookup Table]]&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Component&amp;diff=619</id>
		<title>Component</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Component&amp;diff=619"/>
		<updated>2025-09-09T11:47:49Z</updated>

		<summary type="html">&lt;p&gt;Felipe: add components category in see also section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Components&#039;&#039;&#039; are the items used to build things in Logic World.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[:Category:Components]]&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Circuit&amp;diff=618</id>
		<title>Circuit</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Circuit&amp;diff=618"/>
		<updated>2025-09-09T11:46:39Z</updated>

		<summary type="html">&lt;p&gt;Felipe: use singular link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;circuit&#039;&#039;&#039; is a collection of multiple [[component]]s and wires. There are many kinds of useful circuits, which this page will tell you about once it&#039;s been edited a bunch more.&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Logic_World&amp;diff=617</id>
		<title>Logic World</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Logic_World&amp;diff=617"/>
		<updated>2025-09-09T11:46:22Z</updated>

		<summary type="html">&lt;p&gt;Felipe: use singular link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
[[File:Logic World Steam banner.jpg|thumb|Logic World]]&lt;br /&gt;
&#039;&#039;&#039;Logic World&#039;&#039;&#039; is a first-person sandbox simulation video game developed by [[Mouse Hat Games]] for Linux, MacOS, and Windows. The game enables players to build [[digital logic]] machines out of various [[component]]s. Logic World can be played in singleplayer or [[multiplayer]].&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Category:Miscellaneous_components&amp;diff=615</id>
		<title>Category:Miscellaneous components</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Category:Miscellaneous_components&amp;diff=615"/>
		<updated>2025-09-09T08:04:44Z</updated>

		<summary type="html">&lt;p&gt;Felipe: add to the components category&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Category for components that don&#039;t fit into the other categories.&lt;br /&gt;
&lt;br /&gt;
[[Category:Components]]&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Truth_table_auto&amp;diff=519</id>
		<title>Template:Truth table auto</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Truth_table_auto&amp;diff=519"/>
		<updated>2025-09-08T22:56:08Z</updated>

		<summary type="html">&lt;p&gt;Felipe: fix empty space above table&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&amp;lt;templatedata&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;params&amp;quot;: {&lt;br /&gt;
		&amp;quot;caption&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Title to show above the table&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;inputs&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Number of inputs or list of input names&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;\&amp;quot;3\&amp;quot;, \&amp;quot;A B C\&amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;outputs&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Number of outputs or list of output names&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;\&amp;quot;2\&amp;quot;, \&amp;quot;X Y\&amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr1&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #1&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) | c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr2&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #2&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) | c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr3&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #3&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) | c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr4&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #4&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) | c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr5&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #5&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) | c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;expr6&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Expression for output #6&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;(a &amp;amp; b) | c ^ d&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;description&amp;quot;: &amp;quot;Renders a truth table with any number of inputs and outputs&amp;quot;,&lt;br /&gt;
	&amp;quot;paramOrder&amp;quot;: [&lt;br /&gt;
		&amp;quot;caption&amp;quot;,&lt;br /&gt;
		&amp;quot;inputs&amp;quot;,&lt;br /&gt;
		&amp;quot;outputs&amp;quot;,&lt;br /&gt;
		&amp;quot;expr1&amp;quot;,&lt;br /&gt;
		&amp;quot;expr2&amp;quot;,&lt;br /&gt;
		&amp;quot;expr3&amp;quot;,&lt;br /&gt;
		&amp;quot;expr4&amp;quot;,&lt;br /&gt;
		&amp;quot;expr5&amp;quot;,&lt;br /&gt;
		&amp;quot;expr6&amp;quot;&lt;br /&gt;
	],&lt;br /&gt;
	&amp;quot;format&amp;quot;: &amp;quot;block&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/templatedata&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&amp;lt;includeonly&amp;gt;{{#invoke:LogicUtils|truth_table_auto}}&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Navbox&amp;diff=518</id>
		<title>Template:Navbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Navbox&amp;diff=518"/>
		<updated>2025-09-08T22:54:29Z</updated>

		<summary type="html">&lt;p&gt;Felipe: add &amp;quot;navigation&amp;quot; header&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Navigation ==&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Navbox|navbox}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Oracle&amp;diff=503</id>
		<title>Oracle</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Oracle&amp;diff=503"/>
		<updated>2025-09-08T22:08:06Z</updated>

		<summary type="html">&lt;p&gt;Felipe: use on and off templates&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox component&lt;br /&gt;
| title=Oracle&lt;br /&gt;
| caption=Oracle&lt;br /&gt;
| id=MHG.Randomizer&lt;br /&gt;
| io.propagationDelay=1&lt;br /&gt;
| io.maxInputs=1&lt;br /&gt;
| io.minInputs=1&lt;br /&gt;
| io.outputs=1&lt;br /&gt;
}}&lt;br /&gt;
The Oracle is a digital component which will output a random state ({{on}} or {{off}}) on the rising edge of the input.&lt;br /&gt;
&lt;br /&gt;
The Oracle will demonstrate the following behavior:&lt;br /&gt;
{{Truth table&lt;br /&gt;
| inputs=Enable&lt;br /&gt;
| outputs=1&lt;br /&gt;
| caption=Oracle Truth Table&lt;br /&gt;
| 0 0&lt;br /&gt;
| 1 1/0&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Timing ==&lt;br /&gt;
The Oracle has a latency of 1 tick, so in the instance of the enable pin goes {{on}}, the output will be in a random state of either {{off}} or {{on}}. The same is for when the enable pin goes {{off}}, the output will be set to {{off}} one tick later.&lt;br /&gt;
&lt;br /&gt;
== Configurability ==&lt;br /&gt;
This component cannot be configured in the edit menu.&lt;br /&gt;
&lt;br /&gt;
[[Category:Vanilla components]]&lt;br /&gt;
[[Category:Circuitry components]]&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=493</id>
		<title>User:Felipe/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=493"/>
		<updated>2025-09-08T18:06:52Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{Binary signal&lt;br /&gt;
| signals=Input A, Input B, Output&lt;br /&gt;
| signal1=0111?0011100011100011100011100011100011100011100011100&lt;br /&gt;
| signal2=00?100001100001100001100001100001100001100001100001100&lt;br /&gt;
| signal3=0001100??110000110000110000110000110000110000110000110&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Binary_signal&amp;diff=492</id>
		<title>Template:Binary signal</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Binary_signal&amp;diff=492"/>
		<updated>2025-09-08T18:00:52Z</updated>

		<summary type="html">&lt;p&gt;Felipe: add templatedata&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;templatestyles src=&amp;quot;Binary signal/styles.css&amp;quot; /&amp;gt;{{#invoke:LogicUtils|binary_signal_graph}}&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&amp;lt;templatedata&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;params&amp;quot;: {&lt;br /&gt;
		&amp;quot;signals&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Comma-separated list of signal names&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;Input 1, Input 2, Output&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;signal1&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Binary data for signal 1&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;00110101&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;signal2&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Binary data for signal 2&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;00110101&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;signal3&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Binary data for signal 3&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;00110101&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;signal4&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Binary data for signal 4&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;00110101&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;signal5&amp;quot;: {&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Binary data for signal 5&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;line&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;00110101&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;description&amp;quot;: &amp;quot;Renders a graph showing one or multiple binary signals over time. Parameters are only documented until signal5, but there is no hard limit.&amp;quot;,&lt;br /&gt;
	&amp;quot;format&amp;quot;: &amp;quot;block&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/templatedata&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=491</id>
		<title>User:Felipe/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=491"/>
		<updated>2025-09-08T17:53:59Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{Binary signal&lt;br /&gt;
| signals=Input A, Input B, Output&lt;br /&gt;
| signal1=011100011100011100011100011100011100011100011100011100&lt;br /&gt;
| signal2=00????001100001100001100001100001100001100001100001100&lt;br /&gt;
| signal3=000110000110000110000110000110000110000110000110000110&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=490</id>
		<title>Module:LogicUtils</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=490"/>
		<updated>2025-09-08T17:53:51Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
function parse_io(kind, row, str)&lt;br /&gt;
    local count = tonumber(str)&lt;br /&gt;
    local names = {}&lt;br /&gt;
&lt;br /&gt;
    if count == nil then&lt;br /&gt;
        -- str is a list of IO names&lt;br /&gt;
        names = mw.text.split(str, &amp;quot;,&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        -- str is a number&lt;br /&gt;
        if count == 1 then&lt;br /&gt;
            names[1] = kind&lt;br /&gt;
        else&lt;br /&gt;
            for i=1,count do&lt;br /&gt;
                names[i] = string.format(&amp;quot;%s %i&amp;quot;, kind, i)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for i, v in ipairs(names) do&lt;br /&gt;
        row:tag(&#039;th&#039;)&lt;br /&gt;
            :wikitext(v)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return #names&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isBinOp(c)&lt;br /&gt;
    return c == &amp;quot;&amp;amp;&amp;quot; or c == &amp;quot;|&amp;quot; or c == &amp;quot;^&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function letterToCell(c)&lt;br /&gt;
    return c:lower():byte() - 96&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function eval(expr, values)&lt;br /&gt;
    local stack = {}&lt;br /&gt;
&lt;br /&gt;
    for _, c in pairs(expr) do&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
            local v2 = table.remove(stack)&lt;br /&gt;
            if v2 == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
            if c == &amp;quot;&amp;amp;&amp;quot; then&lt;br /&gt;
                table.insert(stack, v and v2)&lt;br /&gt;
            elseif c == &amp;quot;|&amp;quot; then&lt;br /&gt;
                table.insert(stack, v or v2)&lt;br /&gt;
            elseif c == &amp;quot;^&amp;quot; then&lt;br /&gt;
                table.insert(stack, v ~= v2)&lt;br /&gt;
            end&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand after &#039;!&#039;&amp;quot;) end&lt;br /&gt;
            table.insert(stack, not v)&lt;br /&gt;
        else&lt;br /&gt;
            local index = letterToCell(c) -- &#039;a&#039; = 1, &#039;b&#039; = 2, etc.&lt;br /&gt;
            if index then&lt;br /&gt;
                if values[index] == nil and not ignoreMissing then error(&amp;quot;Value for variable &#039;&amp;quot; .. c .. &amp;quot;&#039; not provided&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
                table.insert(stack, values[index] or false)&lt;br /&gt;
            else&lt;br /&gt;
                error(&amp;quot;Invalid character in expression: &amp;quot; .. c)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return stack[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function evalAll(expr, num_vars, callback)&lt;br /&gt;
    local totalCombinations = 2 ^ num_vars&lt;br /&gt;
    local bit32 = require( &#039;bit32&#039; )&lt;br /&gt;
&lt;br /&gt;
    for i = 0, totalCombinations - 1 do&lt;br /&gt;
        local values = {}&lt;br /&gt;
        for j = 1, num_vars do&lt;br /&gt;
            values[j] = bit32.band(i, bit32.lshift(1, num_vars - j)) ~= 0&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        local result = eval(expr, values)&lt;br /&gt;
        callback(values, result)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function algebraicToRPN(input)&lt;br /&gt;
    local output = {}&lt;br /&gt;
    local opstack = {}&lt;br /&gt;
&lt;br /&gt;
    input:gsub(&amp;quot;[^%s]&amp;quot;, function(c)&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            while opstack[#opstack] == &amp;quot;!&amp;quot; do&lt;br /&gt;
                table.insert(output, table.remove(opstack))&lt;br /&gt;
            end&lt;br /&gt;
&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; or c == &amp;quot;(&amp;quot; then&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;)&amp;quot; then&lt;br /&gt;
            while #opstack &amp;gt; 0 do&lt;br /&gt;
                local op = table.remove(opstack)&lt;br /&gt;
                if op == &amp;quot;(&amp;quot; then&lt;br /&gt;
                    break&lt;br /&gt;
                end&lt;br /&gt;
                table.insert(output, op)&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
            table.insert(output, c)&lt;br /&gt;
        end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    -- Drain the operator stack to the output&lt;br /&gt;
    while #opstack &amp;gt; 0 do table.insert(output, table.remove(opstack)) end&lt;br /&gt;
&lt;br /&gt;
    return output&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderCell(row, value)&lt;br /&gt;
    local style = &amp;quot;color:white; text-align:center;&amp;quot;&lt;br /&gt;
    if value == false or value == &amp;quot;0&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#1f1e1e;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;0&amp;quot;)&lt;br /&gt;
    elseif value == true or value == &amp;quot;1&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#fd140f;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;1&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :cssText(&amp;quot;text-align:center&amp;quot;)&lt;br /&gt;
            :wikitext(value)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    local i = 1&lt;br /&gt;
    while args[i] ~= nil do&lt;br /&gt;
        local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(args[i], &amp;quot;[^%s]+&amp;quot;) do&lt;br /&gt;
            renderCell(row, token)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table_auto = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    for i = 1,nOutputs do&lt;br /&gt;
        local exprStr = args[&amp;quot;expr&amp;quot;..i]&lt;br /&gt;
        if exprStr == nil then&lt;br /&gt;
            error(&amp;quot;Missing expression for output &amp;quot; .. i)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        evalAll(algebraicToRPN(exprStr), nInputs, function(values, result)&lt;br /&gt;
            local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
            for j = 1,nInputs do&lt;br /&gt;
                renderCell(row, values[j])&lt;br /&gt;
            end&lt;br /&gt;
            renderCell(row, result)&lt;br /&gt;
        end)&lt;br /&gt;
&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 0, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-header-cell&#039;)&lt;br /&gt;
            :wikitext(tostring(i))&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer-row&amp;quot;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-spacer-row-cell&#039;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-row&amp;quot;)&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :wikitext(signal.name)&lt;br /&gt;
&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    mw.log(mw.dumpObject(signal))&lt;br /&gt;
&lt;br /&gt;
    local prevValue = nil&lt;br /&gt;
    for _, v in pairs(signal.values) do&lt;br /&gt;
        if v == &amp;quot;nil&amp;quot; then v = nil end&lt;br /&gt;
&lt;br /&gt;
        local cell = row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;signal-value-cell&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        if v == 1 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-top&amp;quot;)&lt;br /&gt;
        elseif v == 0 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-bottom&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if prevValue ~= nil and v ~= nil and v ~= prevValue then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-left&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        prevValue = v&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraph(parent, signals)&lt;br /&gt;
    local tickCount = #signals[1].values&lt;br /&gt;
&lt;br /&gt;
    local tbl = parent:tag(&#039;table&#039;)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    for _, signal in pairs(signals) do&lt;br /&gt;
        renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
        renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.binary_signal_graph = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local signalNames = mw.text.split(args.signals, &amp;quot;,&amp;quot;)&lt;br /&gt;
    local signals = {}&lt;br /&gt;
&lt;br /&gt;
    local container = mw.html.create(&#039;div&#039;)&lt;br /&gt;
        :addClass(&#039;signal-graph-table&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1,#signalNames do&lt;br /&gt;
        local str = args[&amp;quot;signal&amp;quot;..i]&lt;br /&gt;
        local values = {}&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(str, &amp;quot;[01?]&amp;quot;) do&lt;br /&gt;
            if token == &amp;quot;?&amp;quot; then&lt;br /&gt;
                table.insert(values, &amp;quot;nil&amp;quot;) -- lua tables can&#039;t store nil&lt;br /&gt;
            else&lt;br /&gt;
                table.insert(values, tonumber(token))&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        table.insert(signals, { name = mw.text.trim(signalNames[i]), values = values })&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraph(container, signals)&lt;br /&gt;
&lt;br /&gt;
    return tostring(container)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=489</id>
		<title>Module:LogicUtils</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=489"/>
		<updated>2025-09-08T17:48:05Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
function parse_io(kind, row, str)&lt;br /&gt;
    local count = tonumber(str)&lt;br /&gt;
    local names = {}&lt;br /&gt;
&lt;br /&gt;
    if count == nil then&lt;br /&gt;
        -- str is a list of IO names&lt;br /&gt;
        names = mw.text.split(str, &amp;quot;,&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        -- str is a number&lt;br /&gt;
        if count == 1 then&lt;br /&gt;
            names[1] = kind&lt;br /&gt;
        else&lt;br /&gt;
            for i=1,count do&lt;br /&gt;
                names[i] = string.format(&amp;quot;%s %i&amp;quot;, kind, i)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for i, v in ipairs(names) do&lt;br /&gt;
        row:tag(&#039;th&#039;)&lt;br /&gt;
            :wikitext(v)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return #names&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isBinOp(c)&lt;br /&gt;
    return c == &amp;quot;&amp;amp;&amp;quot; or c == &amp;quot;|&amp;quot; or c == &amp;quot;^&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function letterToCell(c)&lt;br /&gt;
    return c:lower():byte() - 96&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function eval(expr, values)&lt;br /&gt;
    local stack = {}&lt;br /&gt;
&lt;br /&gt;
    for _, c in pairs(expr) do&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
            local v2 = table.remove(stack)&lt;br /&gt;
            if v2 == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
            if c == &amp;quot;&amp;amp;&amp;quot; then&lt;br /&gt;
                table.insert(stack, v and v2)&lt;br /&gt;
            elseif c == &amp;quot;|&amp;quot; then&lt;br /&gt;
                table.insert(stack, v or v2)&lt;br /&gt;
            elseif c == &amp;quot;^&amp;quot; then&lt;br /&gt;
                table.insert(stack, v ~= v2)&lt;br /&gt;
            end&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand after &#039;!&#039;&amp;quot;) end&lt;br /&gt;
            table.insert(stack, not v)&lt;br /&gt;
        else&lt;br /&gt;
            local index = letterToCell(c) -- &#039;a&#039; = 1, &#039;b&#039; = 2, etc.&lt;br /&gt;
            if index then&lt;br /&gt;
                if values[index] == nil and not ignoreMissing then error(&amp;quot;Value for variable &#039;&amp;quot; .. c .. &amp;quot;&#039; not provided&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
                table.insert(stack, values[index] or false)&lt;br /&gt;
            else&lt;br /&gt;
                error(&amp;quot;Invalid character in expression: &amp;quot; .. c)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return stack[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function evalAll(expr, num_vars, callback)&lt;br /&gt;
    local totalCombinations = 2 ^ num_vars&lt;br /&gt;
    local bit32 = require( &#039;bit32&#039; )&lt;br /&gt;
&lt;br /&gt;
    for i = 0, totalCombinations - 1 do&lt;br /&gt;
        local values = {}&lt;br /&gt;
        for j = 1, num_vars do&lt;br /&gt;
            values[j] = bit32.band(i, bit32.lshift(1, num_vars - j)) ~= 0&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        local result = eval(expr, values)&lt;br /&gt;
        callback(values, result)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function algebraicToRPN(input)&lt;br /&gt;
    local output = {}&lt;br /&gt;
    local opstack = {}&lt;br /&gt;
&lt;br /&gt;
    input:gsub(&amp;quot;[^%s]&amp;quot;, function(c)&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            while opstack[#opstack] == &amp;quot;!&amp;quot; do&lt;br /&gt;
                table.insert(output, table.remove(opstack))&lt;br /&gt;
            end&lt;br /&gt;
&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; or c == &amp;quot;(&amp;quot; then&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;)&amp;quot; then&lt;br /&gt;
            while #opstack &amp;gt; 0 do&lt;br /&gt;
                local op = table.remove(opstack)&lt;br /&gt;
                if op == &amp;quot;(&amp;quot; then&lt;br /&gt;
                    break&lt;br /&gt;
                end&lt;br /&gt;
                table.insert(output, op)&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
            table.insert(output, c)&lt;br /&gt;
        end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    -- Drain the operator stack to the output&lt;br /&gt;
    while #opstack &amp;gt; 0 do table.insert(output, table.remove(opstack)) end&lt;br /&gt;
&lt;br /&gt;
    return output&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderCell(row, value)&lt;br /&gt;
    local style = &amp;quot;color:white; text-align:center;&amp;quot;&lt;br /&gt;
    if value == false or value == &amp;quot;0&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#1f1e1e;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;0&amp;quot;)&lt;br /&gt;
    elseif value == true or value == &amp;quot;1&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#fd140f;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;1&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :cssText(&amp;quot;text-align:center&amp;quot;)&lt;br /&gt;
            :wikitext(value)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    local i = 1&lt;br /&gt;
    while args[i] ~= nil do&lt;br /&gt;
        local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(args[i], &amp;quot;[^%s]+&amp;quot;) do&lt;br /&gt;
            renderCell(row, token)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table_auto = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    for i = 1,nOutputs do&lt;br /&gt;
        local exprStr = args[&amp;quot;expr&amp;quot;..i]&lt;br /&gt;
        if exprStr == nil then&lt;br /&gt;
            error(&amp;quot;Missing expression for output &amp;quot; .. i)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        evalAll(algebraicToRPN(exprStr), nInputs, function(values, result)&lt;br /&gt;
            local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
            for j = 1,nInputs do&lt;br /&gt;
                renderCell(row, values[j])&lt;br /&gt;
            end&lt;br /&gt;
            renderCell(row, result)&lt;br /&gt;
        end)&lt;br /&gt;
&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 0, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-header-cell&#039;)&lt;br /&gt;
            :wikitext(tostring(i))&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer-row&amp;quot;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-spacer-row-cell&#039;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-row&amp;quot;)&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :wikitext(signal.name)&lt;br /&gt;
&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    local prevValue = nil&lt;br /&gt;
    for _, v in pairs(signal.values) do&lt;br /&gt;
        local cell = row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;signal-value-cell&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        if v == 1 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-top&amp;quot;)&lt;br /&gt;
        elseif v == 0 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-bottom&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if prevValue ~= nil and v ~= nil and v ~= prevValue then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-left&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        prevValue = v&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraph(parent, signals)&lt;br /&gt;
    local tickCount = #signals[1].values&lt;br /&gt;
&lt;br /&gt;
    local tbl = parent:tag(&#039;table&#039;)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    for _, signal in pairs(signals) do&lt;br /&gt;
        renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
        renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.binary_signal_graph = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local signalNames = mw.text.split(args.signals, &amp;quot;,&amp;quot;)&lt;br /&gt;
    local signals = {}&lt;br /&gt;
&lt;br /&gt;
    local container = mw.html.create(&#039;div&#039;)&lt;br /&gt;
        :addClass(&#039;signal-graph-table&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1,#signalNames do&lt;br /&gt;
        local str = args[&amp;quot;signal&amp;quot;..i]&lt;br /&gt;
        local values = {}&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(str, &amp;quot;[01x]&amp;quot;) do&lt;br /&gt;
            table.insert(values, token ~= &amp;quot;x&amp;quot; and tonumber(token) or nil)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        table.insert(signals, { name = mw.text.trim(signalNames[i]), values = values })&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraph(container, signals)&lt;br /&gt;
&lt;br /&gt;
    return tostring(container)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=488</id>
		<title>User:Felipe/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=488"/>
		<updated>2025-09-08T17:47:53Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{Binary signal&lt;br /&gt;
| signals=Input A, Input B, Output&lt;br /&gt;
| signal1=011100011100011100011100011100011100011100011100011100&lt;br /&gt;
| signal2=00xxxx001100001100001100001100001100001100001100001100&lt;br /&gt;
| signal3=000110000110000110000110000110000110000110000110000110&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=487</id>
		<title>Module:LogicUtils</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=487"/>
		<updated>2025-09-08T17:47:17Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
function parse_io(kind, row, str)&lt;br /&gt;
    local count = tonumber(str)&lt;br /&gt;
    local names = {}&lt;br /&gt;
&lt;br /&gt;
    if count == nil then&lt;br /&gt;
        -- str is a list of IO names&lt;br /&gt;
        names = mw.text.split(str, &amp;quot;,&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        -- str is a number&lt;br /&gt;
        if count == 1 then&lt;br /&gt;
            names[1] = kind&lt;br /&gt;
        else&lt;br /&gt;
            for i=1,count do&lt;br /&gt;
                names[i] = string.format(&amp;quot;%s %i&amp;quot;, kind, i)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for i, v in ipairs(names) do&lt;br /&gt;
        row:tag(&#039;th&#039;)&lt;br /&gt;
            :wikitext(v)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return #names&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isBinOp(c)&lt;br /&gt;
    return c == &amp;quot;&amp;amp;&amp;quot; or c == &amp;quot;|&amp;quot; or c == &amp;quot;^&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function letterToCell(c)&lt;br /&gt;
    return c:lower():byte() - 96&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function eval(expr, values)&lt;br /&gt;
    local stack = {}&lt;br /&gt;
&lt;br /&gt;
    for _, c in pairs(expr) do&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
            local v2 = table.remove(stack)&lt;br /&gt;
            if v2 == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
            if c == &amp;quot;&amp;amp;&amp;quot; then&lt;br /&gt;
                table.insert(stack, v and v2)&lt;br /&gt;
            elseif c == &amp;quot;|&amp;quot; then&lt;br /&gt;
                table.insert(stack, v or v2)&lt;br /&gt;
            elseif c == &amp;quot;^&amp;quot; then&lt;br /&gt;
                table.insert(stack, v ~= v2)&lt;br /&gt;
            end&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand after &#039;!&#039;&amp;quot;) end&lt;br /&gt;
            table.insert(stack, not v)&lt;br /&gt;
        else&lt;br /&gt;
            local index = letterToCell(c) -- &#039;a&#039; = 1, &#039;b&#039; = 2, etc.&lt;br /&gt;
            if index then&lt;br /&gt;
                if values[index] == nil and not ignoreMissing then error(&amp;quot;Value for variable &#039;&amp;quot; .. c .. &amp;quot;&#039; not provided&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
                table.insert(stack, values[index] or false)&lt;br /&gt;
            else&lt;br /&gt;
                error(&amp;quot;Invalid character in expression: &amp;quot; .. c)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return stack[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function evalAll(expr, num_vars, callback)&lt;br /&gt;
    local totalCombinations = 2 ^ num_vars&lt;br /&gt;
    local bit32 = require( &#039;bit32&#039; )&lt;br /&gt;
&lt;br /&gt;
    for i = 0, totalCombinations - 1 do&lt;br /&gt;
        local values = {}&lt;br /&gt;
        for j = 1, num_vars do&lt;br /&gt;
            values[j] = bit32.band(i, bit32.lshift(1, num_vars - j)) ~= 0&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        local result = eval(expr, values)&lt;br /&gt;
        callback(values, result)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function algebraicToRPN(input)&lt;br /&gt;
    local output = {}&lt;br /&gt;
    local opstack = {}&lt;br /&gt;
&lt;br /&gt;
    input:gsub(&amp;quot;[^%s]&amp;quot;, function(c)&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            while opstack[#opstack] == &amp;quot;!&amp;quot; do&lt;br /&gt;
                table.insert(output, table.remove(opstack))&lt;br /&gt;
            end&lt;br /&gt;
&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; or c == &amp;quot;(&amp;quot; then&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;)&amp;quot; then&lt;br /&gt;
            while #opstack &amp;gt; 0 do&lt;br /&gt;
                local op = table.remove(opstack)&lt;br /&gt;
                if op == &amp;quot;(&amp;quot; then&lt;br /&gt;
                    break&lt;br /&gt;
                end&lt;br /&gt;
                table.insert(output, op)&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
            table.insert(output, c)&lt;br /&gt;
        end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    -- Drain the operator stack to the output&lt;br /&gt;
    while #opstack &amp;gt; 0 do table.insert(output, table.remove(opstack)) end&lt;br /&gt;
&lt;br /&gt;
    return output&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderCell(row, value)&lt;br /&gt;
    local style = &amp;quot;color:white; text-align:center;&amp;quot;&lt;br /&gt;
    if value == false or value == &amp;quot;0&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#1f1e1e;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;0&amp;quot;)&lt;br /&gt;
    elseif value == true or value == &amp;quot;1&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#fd140f;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;1&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :cssText(&amp;quot;text-align:center&amp;quot;)&lt;br /&gt;
            :wikitext(value)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    local i = 1&lt;br /&gt;
    while args[i] ~= nil do&lt;br /&gt;
        local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(args[i], &amp;quot;[^%s]+&amp;quot;) do&lt;br /&gt;
            renderCell(row, token)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table_auto = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    for i = 1,nOutputs do&lt;br /&gt;
        local exprStr = args[&amp;quot;expr&amp;quot;..i]&lt;br /&gt;
        if exprStr == nil then&lt;br /&gt;
            error(&amp;quot;Missing expression for output &amp;quot; .. i)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        evalAll(algebraicToRPN(exprStr), nInputs, function(values, result)&lt;br /&gt;
            local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
            for j = 1,nInputs do&lt;br /&gt;
                renderCell(row, values[j])&lt;br /&gt;
            end&lt;br /&gt;
            renderCell(row, result)&lt;br /&gt;
        end)&lt;br /&gt;
&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 0, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-header-cell&#039;)&lt;br /&gt;
            :wikitext(tostring(i))&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer-row&amp;quot;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-spacer-row-cell&#039;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-row&amp;quot;)&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :wikitext(signal.name)&lt;br /&gt;
&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    local prevValue = nil&lt;br /&gt;
    for _, v in pairs(signal.values) do&lt;br /&gt;
        local cell = row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;signal-value-cell&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        if v == 1 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-top&amp;quot;)&lt;br /&gt;
        elseif v == 0 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-bottom&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if prevValue ~= nil and v ~= nil and v ~= prevValue then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-left&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        prevValue = v&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraph(parent, signals)&lt;br /&gt;
    local tickCount = #signals[1].values&lt;br /&gt;
&lt;br /&gt;
    local tbl = parent:tag(&#039;table&#039;)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    for _, signal in pairs(signals) do&lt;br /&gt;
        renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
        renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.binary_signal_graph = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local signalNames = mw.text.split(args.signals, &amp;quot;,&amp;quot;)&lt;br /&gt;
    local signals = {}&lt;br /&gt;
&lt;br /&gt;
    local container = mw.html.create(&#039;div&#039;)&lt;br /&gt;
        :addClass(&#039;signal-graph-table&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1,#signalNames do&lt;br /&gt;
        local str = args[&amp;quot;signal&amp;quot;..i]&lt;br /&gt;
        local values = {}&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(str, &amp;quot;[01x]&amp;quot;) do&lt;br /&gt;
            table.insert(values, token == &amp;quot;x&amp;quot; and nil or tonumber(token))&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        table.insert(signals, { name = mw.text.trim(signalNames[i]), values = values })&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraph(container, signals)&lt;br /&gt;
&lt;br /&gt;
    return tostring(container)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Binary_signal/styles.css&amp;diff=486</id>
		<title>Template:Binary signal/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Binary_signal/styles.css&amp;diff=486"/>
		<updated>2025-09-08T17:15:23Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.signal-graph-table {&lt;br /&gt;
    padding: .6rem;&lt;br /&gt;
    background-color: var(--background-color-neutral-subtle,#f8f9fa);&lt;br /&gt;
    border: 1px solid var(--border-color-base,#a2a9b1);&lt;br /&gt;
&lt;br /&gt;
    max-width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    overflow-y: auto;&lt;br /&gt;
&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table table {&lt;br /&gt;
    border-collapse: collapse;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-header-cell {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    transform: translateX(-50%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table tr.signal-spacer-row {&lt;br /&gt;
    height: 1rem;&lt;br /&gt;
    border: none;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-spacer-row td.signal-spacer-row-cell {&lt;br /&gt;
    border-left: var(--tick-marker-border);&lt;br /&gt;
    border-right: var(--tick-marker-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table tr.signal-row {&lt;br /&gt;
    height: 2rem;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-row td:first-of-type {&lt;br /&gt;
    width: auto;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    padding: 0 0.5rem;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-row td.signal-spacer {&lt;br /&gt;
    border: none;&lt;br /&gt;
    width: .5rem;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-value-cell {&lt;br /&gt;
    width: 3rem;&lt;br /&gt;
    min-width: 3rem;&lt;br /&gt;
    border-left: 1px solid;&lt;br /&gt;
    border-right: 1px solid;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-top {&lt;br /&gt;
    border-top: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-bottom {&lt;br /&gt;
    border-bottom: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-left {&lt;br /&gt;
    border-left: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-value-cell {&lt;br /&gt;
    border-right: var(--tick-marker-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell:not(.signal-on-left) {&lt;br /&gt;
    border-left: var(--tick-marker-border);&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=485</id>
		<title>User:Felipe/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=485"/>
		<updated>2025-09-08T17:13:33Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{Binary signal&lt;br /&gt;
| signals=Input A, Input B, Output&lt;br /&gt;
| signal1=011100011100011100011100011100011100011100011100011100&lt;br /&gt;
| signal2=001100001100001100001100001100001100001100001100001100&lt;br /&gt;
| signal3=000110000110000110000110000110000110000110000110000110&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Binary_signal/styles.css&amp;diff=484</id>
		<title>Template:Binary signal/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Binary_signal/styles.css&amp;diff=484"/>
		<updated>2025-09-08T17:12:18Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.signal-graph-table {&lt;br /&gt;
    padding: .6rem;&lt;br /&gt;
    background-color: var(--background-color-neutral-subtle,#f8f9fa);&lt;br /&gt;
    border: 1px solid var(--border-color-base,#a2a9b1);&lt;br /&gt;
&lt;br /&gt;
    max-width: 500px;&lt;br /&gt;
    overflow-y: auto;&lt;br /&gt;
&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table table {&lt;br /&gt;
    border-collapse: collapse;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-header-cell {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    transform: translateX(-50%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table tr.signal-spacer-row {&lt;br /&gt;
    height: 1rem;&lt;br /&gt;
    border: none;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-spacer-row td.signal-spacer-row-cell {&lt;br /&gt;
    border-left: var(--tick-marker-border);&lt;br /&gt;
    border-right: var(--tick-marker-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table tr.signal-row {&lt;br /&gt;
    height: 2rem;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-row td:first-of-type {&lt;br /&gt;
    width: auto;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    padding: 0 0.5rem;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-row td.signal-spacer {&lt;br /&gt;
    border: none;&lt;br /&gt;
    width: .5rem;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-value-cell {&lt;br /&gt;
    width: 3rem;&lt;br /&gt;
    min-width: 3rem;&lt;br /&gt;
    border-left: 1px solid;&lt;br /&gt;
    border-right: 1px solid;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-top {&lt;br /&gt;
    border-top: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-bottom {&lt;br /&gt;
    border-bottom: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-left {&lt;br /&gt;
    border-left: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-value-cell {&lt;br /&gt;
    border-right: var(--tick-marker-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell:not(.signal-on-left) {&lt;br /&gt;
    border-left: var(--tick-marker-border);&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=483</id>
		<title>Module:LogicUtils</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Module:LogicUtils&amp;diff=483"/>
		<updated>2025-09-08T17:10:21Z</updated>

		<summary type="html">&lt;p&gt;Felipe: better styling&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
function parse_io(kind, row, str)&lt;br /&gt;
    local count = tonumber(str)&lt;br /&gt;
    local names = {}&lt;br /&gt;
&lt;br /&gt;
    if count == nil then&lt;br /&gt;
        -- str is a list of IO names&lt;br /&gt;
        names = mw.text.split(str, &amp;quot;,&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        -- str is a number&lt;br /&gt;
        if count == 1 then&lt;br /&gt;
            names[1] = kind&lt;br /&gt;
        else&lt;br /&gt;
            for i=1,count do&lt;br /&gt;
                names[i] = string.format(&amp;quot;%s %i&amp;quot;, kind, i)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for i, v in ipairs(names) do&lt;br /&gt;
        row:tag(&#039;th&#039;)&lt;br /&gt;
            :wikitext(v)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return #names&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isBinOp(c)&lt;br /&gt;
    return c == &amp;quot;&amp;amp;&amp;quot; or c == &amp;quot;|&amp;quot; or c == &amp;quot;^&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function eval(expr, values)&lt;br /&gt;
    local stack = {}&lt;br /&gt;
&lt;br /&gt;
    for _, c in pairs(expr) do&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
            local v2 = table.remove(stack)&lt;br /&gt;
            if v2 == nil then error(&amp;quot;Invalid expression: missing operand&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
            if c == &amp;quot;&amp;amp;&amp;quot; then&lt;br /&gt;
                table.insert(stack, v and v2)&lt;br /&gt;
            elseif c == &amp;quot;|&amp;quot; then&lt;br /&gt;
                table.insert(stack, v or v2)&lt;br /&gt;
            elseif c == &amp;quot;^&amp;quot; then&lt;br /&gt;
                table.insert(stack, v ~= v2)&lt;br /&gt;
            end&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; then&lt;br /&gt;
            local v = table.remove(stack)&lt;br /&gt;
            if v == nil then error(&amp;quot;Invalid expression: missing operand after &#039;!&#039;&amp;quot;) end&lt;br /&gt;
            table.insert(stack, not v)&lt;br /&gt;
        else&lt;br /&gt;
            local index = c:lower():byte() - 96 -- &#039;a&#039; = 1, &#039;b&#039; = 2, etc.&lt;br /&gt;
            if index then&lt;br /&gt;
                if values[index] == nil and not ignoreMissing then error(&amp;quot;Value for variable &#039;&amp;quot; .. c .. &amp;quot;&#039; not provided&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
                table.insert(stack, values[index] or false)&lt;br /&gt;
            else&lt;br /&gt;
                error(&amp;quot;Invalid character in expression: &amp;quot; .. c)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return stack[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function evalAll(expr, num_vars, callback)&lt;br /&gt;
    local totalCombinations = 2 ^ num_vars&lt;br /&gt;
    local bit32 = require( &#039;bit32&#039; )&lt;br /&gt;
&lt;br /&gt;
    for i = 0, totalCombinations - 1 do&lt;br /&gt;
        local values = {}&lt;br /&gt;
        for j = 1, num_vars do&lt;br /&gt;
            values[j] = bit32.band(i, bit32.lshift(1, num_vars - j)) ~= 0&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        local result = eval(expr, values)&lt;br /&gt;
        callback(values, result)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function algebraicToRPN(input)&lt;br /&gt;
    local output = {}&lt;br /&gt;
    local opstack = {}&lt;br /&gt;
&lt;br /&gt;
    input:gsub(&amp;quot;[^%s]&amp;quot;, function(c)&lt;br /&gt;
        if isBinOp(c) then&lt;br /&gt;
            while opstack[#opstack] == &amp;quot;!&amp;quot; do&lt;br /&gt;
                table.insert(output, table.remove(opstack))&lt;br /&gt;
            end&lt;br /&gt;
&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;!&amp;quot; or c == &amp;quot;(&amp;quot; then&lt;br /&gt;
            table.insert(opstack, c)&lt;br /&gt;
        elseif c == &amp;quot;)&amp;quot; then&lt;br /&gt;
            while #opstack &amp;gt; 0 do&lt;br /&gt;
                local op = table.remove(opstack)&lt;br /&gt;
                if op == &amp;quot;(&amp;quot; then&lt;br /&gt;
                    break&lt;br /&gt;
                end&lt;br /&gt;
                table.insert(output, op)&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
            table.insert(output, c)&lt;br /&gt;
        end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    -- Drain the operator stack to the output&lt;br /&gt;
    while #opstack &amp;gt; 0 do table.insert(output, table.remove(opstack)) end&lt;br /&gt;
&lt;br /&gt;
    return output&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderCell(row, value)&lt;br /&gt;
    local style = &amp;quot;color:white; text-align:center;&amp;quot;&lt;br /&gt;
    if value == false or value == &amp;quot;0&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#1f1e1e;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;0&amp;quot;)&lt;br /&gt;
    elseif value == true or value == &amp;quot;1&amp;quot; then&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :attr(&#039;style&#039;, &#039;background-color:#fd140f;&#039;..style)&lt;br /&gt;
            :wikitext(&amp;quot;1&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :cssText(&amp;quot;text-align:center&amp;quot;)&lt;br /&gt;
            :wikitext(value)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    local i = 1&lt;br /&gt;
    while args[i] ~= nil do&lt;br /&gt;
        local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(args[i], &amp;quot;[^%s]+&amp;quot;) do&lt;br /&gt;
            renderCell(row, token)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.truth_table_auto = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&#039;table&#039;)&lt;br /&gt;
        :addClass(&#039;wikitable&#039;)&lt;br /&gt;
&lt;br /&gt;
    if args.caption ~= nil then&lt;br /&gt;
        tbl:tag(&#039;caption&#039;)&lt;br /&gt;
            :wikitext(args.caption)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    local nInputs = parse_io(&amp;quot;Input&amp;quot;, header, args.inputs)&lt;br /&gt;
    local nOutputs = parse_io(&amp;quot;Output&amp;quot;, header, args.outputs)&lt;br /&gt;
&lt;br /&gt;
    for i = 1,nOutputs do&lt;br /&gt;
        local exprStr = args[&amp;quot;expr&amp;quot;..i]&lt;br /&gt;
        if exprStr == nil then&lt;br /&gt;
            error(&amp;quot;Missing expression for output &amp;quot; .. i)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        evalAll(algebraicToRPN(exprStr), nInputs, function(values, result)&lt;br /&gt;
            local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
            for j = 1,nInputs do&lt;br /&gt;
                renderCell(row, values[j])&lt;br /&gt;
            end&lt;br /&gt;
            renderCell(row, result)&lt;br /&gt;
        end)&lt;br /&gt;
&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 0, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-header-cell&#039;)&lt;br /&gt;
            :wikitext(tostring(i))&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer-row&amp;quot;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
    row:tag(&#039;td&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1, tickCount do&lt;br /&gt;
        row:tag(&#039;td&#039;)&lt;br /&gt;
            :addClass(&#039;signal-spacer-row-cell&#039;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
    local row = tbl:tag(&#039;tr&#039;)&lt;br /&gt;
        :addClass(&amp;quot;signal-row&amp;quot;)&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :wikitext(signal.name)&lt;br /&gt;
&lt;br /&gt;
    row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
        :addClass(&amp;quot;signal-spacer&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    local prevValue = nil&lt;br /&gt;
    for _, v in pairs(signal.values) do&lt;br /&gt;
        local cell = row:tag(&amp;quot;td&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;signal-value-cell&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        if v == 1 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-top&amp;quot;)&lt;br /&gt;
        elseif v == 0 then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-bottom&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if prevValue ~= nil and v ~= nil and v ~= prevValue then&lt;br /&gt;
            cell:addClass(&amp;quot;signal-on-left&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        prevValue = v&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBinaryGraph(parent, signals)&lt;br /&gt;
    local tickCount = #signals[1].values&lt;br /&gt;
&lt;br /&gt;
    local tbl = parent:tag(&#039;table&#039;)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphHeader(tbl, tickCount)&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    for _, signal in pairs(signals) do&lt;br /&gt;
        renderBinaryGraphSignalRow(tbl, signal)&lt;br /&gt;
        renderBinaryGraphSpacerRow(tbl, tickCount)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.binary_signal_graph = function(frame, args)&lt;br /&gt;
    local args = args or frame:getParent().args&lt;br /&gt;
&lt;br /&gt;
    local signalNames = mw.text.split(args.signals, &amp;quot;,&amp;quot;)&lt;br /&gt;
    local signals = {}&lt;br /&gt;
&lt;br /&gt;
    local container = mw.html.create(&#039;div&#039;)&lt;br /&gt;
        :addClass(&#039;signal-graph-table&#039;)&lt;br /&gt;
&lt;br /&gt;
    for i = 1,#signalNames do&lt;br /&gt;
        local str = args[&amp;quot;signal&amp;quot;..i]&lt;br /&gt;
        local values = {}&lt;br /&gt;
&lt;br /&gt;
        for token in string.gmatch(str, &amp;quot;[01]&amp;quot;) do&lt;br /&gt;
            table.insert(values, tonumber(token))&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        table.insert(signals, { name = mw.text.trim(signalNames[i]), values = values })&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    renderBinaryGraph(container, signals)&lt;br /&gt;
&lt;br /&gt;
    return tostring(container)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Binary_signal/styles.css&amp;diff=482</id>
		<title>Template:Binary signal/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Binary_signal/styles.css&amp;diff=482"/>
		<updated>2025-09-08T17:09:05Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.signal-graph-table {&lt;br /&gt;
    padding: .6rem;&lt;br /&gt;
    background-color: var(--background-color-neutral-subtle,#f8f9fa);&lt;br /&gt;
    border: 1px solid var(--border-color-base,#a2a9b1);&lt;br /&gt;
&lt;br /&gt;
    max-width: 500px;&lt;br /&gt;
    overflow-y: auto;&lt;br /&gt;
&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table table {&lt;br /&gt;
    border-collapse: collapse;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-header-cell {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    transform: translateX(-50%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table tr.signal-spacer-row {&lt;br /&gt;
    height: 1rem;&lt;br /&gt;
    border: none;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-spacer-row td.signal-spacer-row-cell {&lt;br /&gt;
    border-left: var(--tick-marker-border);&lt;br /&gt;
    border-right: var(--tick-marker-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table tr.signal-row {&lt;br /&gt;
    height: 2rem;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-row td:first-of-type {&lt;br /&gt;
    width: auto;&lt;br /&gt;
    padding: 0 0.5rem;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-row td.signal-spacer {&lt;br /&gt;
    border: none;&lt;br /&gt;
    width: .5rem;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-value-cell {&lt;br /&gt;
    width: 3rem;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-top {&lt;br /&gt;
    border-top: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-bottom {&lt;br /&gt;
    border-bottom: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-left {&lt;br /&gt;
    border-left: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-value-cell {&lt;br /&gt;
    border-right: var(--tick-marker-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell:not(.signal-on-left) {&lt;br /&gt;
    border-left: var(--tick-marker-border);&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=481</id>
		<title>User:Felipe/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=User:Felipe/sandbox&amp;diff=481"/>
		<updated>2025-09-08T16:57:17Z</updated>

		<summary type="html">&lt;p&gt;Felipe: Created page with &amp;quot;{{Binary signal | signals=Input A, Input B, Output | signal1=011100 | signal2=001100 | signal3=000110 }}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Binary signal&lt;br /&gt;
| signals=Input A, Input B, Output&lt;br /&gt;
| signal1=011100&lt;br /&gt;
| signal2=001100&lt;br /&gt;
| signal3=000110&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=MediaWiki:Common.css&amp;diff=480</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=MediaWiki:Common.css&amp;diff=480"/>
		<updated>2025-09-08T16:55:57Z</updated>

		<summary type="html">&lt;p&gt;Felipe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lw-infobox {&lt;br /&gt;
	background-color: var(--background-color-interactive-subtle,#f8f9fa);&lt;br /&gt;
	color: var(--color-base,#202122);&lt;br /&gt;
	border: 1px solid var(--border-color-subtle,#c8ccd1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.tabber .wikitable {&lt;br /&gt;
	margin-top: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* CSS variables for the binary signal graph */&lt;br /&gt;
body {&lt;br /&gt;
    --signal-border: 3px solid rgb(239, 53, 53);&lt;br /&gt;
    --tick-marker-border: 1px solid var(--border-color-base);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* === Navbox styles === */&lt;br /&gt;
.mw-parser-output .navbox {&lt;br /&gt;
  border:1px solid #aaa;&lt;br /&gt;
  box-sizing:border-box;&lt;br /&gt;
  width:100%;&lt;br /&gt;
  margin:auto;&lt;br /&gt;
  clear:both;&lt;br /&gt;
  font-size:88%;&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  padding:1px&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-inner,&lt;br /&gt;
.mw-parser-output .navbox-subgroup {&lt;br /&gt;
  width:100%&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox+.navbox-styles+.navbox {&lt;br /&gt;
  margin-top:-1px&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox th,&lt;br /&gt;
.mw-parser-output .navbox-title,&lt;br /&gt;
.mw-parser-output .navbox-abovebelow {&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  padding-left:1em;&lt;br /&gt;
  padding-right:1em&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output th.navbox-group {&lt;br /&gt;
  white-space:nowrap;&lt;br /&gt;
  text-align:right&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox,&lt;br /&gt;
.mw-parser-output .navbox-subgroup {&lt;br /&gt;
  background:#fdfdfd&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-list {&lt;br /&gt;
  border-color:#fdfdfd&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox th,&lt;br /&gt;
.mw-parser-output .navbox-title {&lt;br /&gt;
  background:#eaeeff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-abovebelow,&lt;br /&gt;
.mw-parser-output th.navbox-group,&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-title {&lt;br /&gt;
  background:#ddddff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-group,&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-abovebelow {&lt;br /&gt;
  background:#e6e6ff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-even {&lt;br /&gt;
  background:#f7f7f7&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-odd {&lt;br /&gt;
  background:transparent&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output th.navbox-title1 {&lt;br /&gt;
  border-left:2px solid #fdfdfd;&lt;br /&gt;
  width:100%&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output td.navbox-list1 {&lt;br /&gt;
  text-align:left;&lt;br /&gt;
  border-left-width:2px;&lt;br /&gt;
  border-left-style:solid&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist td dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist td ol,&lt;br /&gt;
.mw-parser-output .navbox .hlist td ul,&lt;br /&gt;
.mw-parser-output .navbox td.hlist dl,&lt;br /&gt;
.mw-parser-output .navbox td.hlist ol,&lt;br /&gt;
.mw-parser-output .navbox td.hlist ul {&lt;br /&gt;
  padding:0.125em 0&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist dd,&lt;br /&gt;
.mw-parser-output .navbox .hlist dt,&lt;br /&gt;
.mw-parser-output .navbox .hlist li {&lt;br /&gt;
  white-space:nowrap&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist dd dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist dt dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist li ol,&lt;br /&gt;
.mw-parser-output .navbox .hlist li ul {&lt;br /&gt;
  white-space:normal&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output ol+.navbox-styles+.navbox,&lt;br /&gt;
.mw-parser-output ul+.navbox-styles+.navbox {&lt;br /&gt;
  margin-top:0.5em&lt;br /&gt;
}&lt;br /&gt;
@media screen {&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-title {&lt;br /&gt;
    background-color:#345&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-group {&lt;br /&gt;
    background-color:#567&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-subgroup td.navbox-title {&lt;br /&gt;
    background-color:#456&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-even {&lt;br /&gt;
    background-color:#000&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-odd {&lt;br /&gt;
    background-color:#123&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox-subgroup,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox-list {&lt;br /&gt;
    background-color:#111111&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-image,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-list {&lt;br /&gt;
    border-top:2px solid #111111&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
@media screen and (prefers-color-scheme:dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-title {&lt;br /&gt;
    background-color:#345&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-group {&lt;br /&gt;
    background-color:#567&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-subgroup td.navbox-title {&lt;br /&gt;
    background-color:#456&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-even {&lt;br /&gt;
    background-color:#000&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-odd {&lt;br /&gt;
    background-color:#123&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox-subgroup,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox-list {&lt;br /&gt;
    background-color:#111111&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-image,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-list {&lt;br /&gt;
    border-top:2px solid #111111&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
/* === End of Navbox styles === */&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Binary_signal/styles.css&amp;diff=479</id>
		<title>Template:Binary signal/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Binary_signal/styles.css&amp;diff=479"/>
		<updated>2025-09-08T16:55:30Z</updated>

		<summary type="html">&lt;p&gt;Felipe: Undo revision 478 by Felipe (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.signal-graph-table {&lt;br /&gt;
    border-collapse: collapse;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-header-cell {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    transform: translateX(-50%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table tr.signal-spacer-row {&lt;br /&gt;
    height: 1rem;&lt;br /&gt;
    border: none;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-spacer-row td.signal-spacer-row-cell {&lt;br /&gt;
    border-left: var(--tick-marker-border);&lt;br /&gt;
    border-right: var(--tick-marker-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table tr.signal-row {&lt;br /&gt;
    height: 2rem;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-row td:first-of-type {&lt;br /&gt;
    width: auto;&lt;br /&gt;
    padding: 0 0.5rem;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-row td.signal-spacer {&lt;br /&gt;
    border: none;&lt;br /&gt;
    width: .5rem;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-value-cell {&lt;br /&gt;
    width: 3rem;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-top {&lt;br /&gt;
    border-top: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-bottom {&lt;br /&gt;
    border-bottom: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-left {&lt;br /&gt;
    border-left: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-value-cell {&lt;br /&gt;
    border-right: var(--tick-marker-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell:not(.signal-on-left) {&lt;br /&gt;
    border-left: var(--tick-marker-border);&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Binary_signal/styles.css&amp;diff=478</id>
		<title>Template:Binary signal/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Binary_signal/styles.css&amp;diff=478"/>
		<updated>2025-09-08T16:51:02Z</updated>

		<summary type="html">&lt;p&gt;Felipe: use border color for tick markers&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.signal-graph-table {&lt;br /&gt;
    border-collapse: collapse;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-header-cell {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    transform: translateX(-50%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table tr.signal-spacer-row {&lt;br /&gt;
    height: 1rem;&lt;br /&gt;
    border: none;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-spacer-row td.signal-spacer-row-cell {&lt;br /&gt;
    border-left: var(--border-color-base);&lt;br /&gt;
    border-right: var(--border-color-base);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table tr.signal-row {&lt;br /&gt;
    height: 2rem;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-row td:first-of-type {&lt;br /&gt;
    width: auto;&lt;br /&gt;
    padding: 0 0.5rem;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table tr.signal-row td.signal-spacer {&lt;br /&gt;
    border: none;&lt;br /&gt;
    width: .5rem;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-value-cell {&lt;br /&gt;
    width: 3rem;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-top {&lt;br /&gt;
    border-top: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-bottom {&lt;br /&gt;
    border-bottom: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell.signal-on-left {&lt;br /&gt;
    border-left: var(--signal-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.signal-graph-table td.signal-value-cell {&lt;br /&gt;
    border-right: var(--border-color-base);&lt;br /&gt;
}&lt;br /&gt;
.signal-graph-table td.signal-value-cell:not(.signal-on-left) {&lt;br /&gt;
    border-left: var(--border-color-base);&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=Template:Binary_signal&amp;diff=477</id>
		<title>Template:Binary signal</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=Template:Binary_signal&amp;diff=477"/>
		<updated>2025-09-08T16:49:40Z</updated>

		<summary type="html">&lt;p&gt;Felipe: load styles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;templatestyles src=&amp;quot;Binary signal/styles.css&amp;quot; /&amp;gt;{{#invoke:LogicUtils|binary_signal_graph}}&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
	<entry>
		<id>https://wiki.logic.world/index.php?title=MediaWiki:Common.css&amp;diff=476</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.logic.world/index.php?title=MediaWiki:Common.css&amp;diff=476"/>
		<updated>2025-09-08T16:48:59Z</updated>

		<summary type="html">&lt;p&gt;Felipe: add binary signal graph variables&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lw-infobox {&lt;br /&gt;
	background-color: var(--background-color-interactive-subtle,#f8f9fa);&lt;br /&gt;
	color: var(--color-base,#202122);&lt;br /&gt;
	border: 1px solid var(--border-color-subtle,#c8ccd1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.tabber .wikitable {&lt;br /&gt;
	margin-top: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* CSS variables for the binary signal graph */&lt;br /&gt;
body {&lt;br /&gt;
    --signal-border: 3px solid rgb(239, 53, 53);&lt;br /&gt;
    --tick-marker-border: 1px solid rgba(0, 0, 0, 0.59);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* === Navbox styles === */&lt;br /&gt;
.mw-parser-output .navbox {&lt;br /&gt;
  border:1px solid #aaa;&lt;br /&gt;
  box-sizing:border-box;&lt;br /&gt;
  width:100%;&lt;br /&gt;
  margin:auto;&lt;br /&gt;
  clear:both;&lt;br /&gt;
  font-size:88%;&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  padding:1px&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-inner,&lt;br /&gt;
.mw-parser-output .navbox-subgroup {&lt;br /&gt;
  width:100%&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox+.navbox-styles+.navbox {&lt;br /&gt;
  margin-top:-1px&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox th,&lt;br /&gt;
.mw-parser-output .navbox-title,&lt;br /&gt;
.mw-parser-output .navbox-abovebelow {&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  padding-left:1em;&lt;br /&gt;
  padding-right:1em&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output th.navbox-group {&lt;br /&gt;
  white-space:nowrap;&lt;br /&gt;
  text-align:right&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox,&lt;br /&gt;
.mw-parser-output .navbox-subgroup {&lt;br /&gt;
  background:#fdfdfd&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-list {&lt;br /&gt;
  border-color:#fdfdfd&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox th,&lt;br /&gt;
.mw-parser-output .navbox-title {&lt;br /&gt;
  background:#eaeeff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-abovebelow,&lt;br /&gt;
.mw-parser-output th.navbox-group,&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-title {&lt;br /&gt;
  background:#ddddff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-group,&lt;br /&gt;
.mw-parser-output .navbox-subgroup .navbox-abovebelow {&lt;br /&gt;
  background:#e6e6ff&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-even {&lt;br /&gt;
  background:#f7f7f7&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-odd {&lt;br /&gt;
  background:transparent&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output th.navbox-title1 {&lt;br /&gt;
  border-left:2px solid #fdfdfd;&lt;br /&gt;
  width:100%&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output td.navbox-list1 {&lt;br /&gt;
  text-align:left;&lt;br /&gt;
  border-left-width:2px;&lt;br /&gt;
  border-left-style:solid&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist td dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist td ol,&lt;br /&gt;
.mw-parser-output .navbox .hlist td ul,&lt;br /&gt;
.mw-parser-output .navbox td.hlist dl,&lt;br /&gt;
.mw-parser-output .navbox td.hlist ol,&lt;br /&gt;
.mw-parser-output .navbox td.hlist ul {&lt;br /&gt;
  padding:0.125em 0&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist dd,&lt;br /&gt;
.mw-parser-output .navbox .hlist dt,&lt;br /&gt;
.mw-parser-output .navbox .hlist li {&lt;br /&gt;
  white-space:nowrap&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox .hlist dd dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist dt dl,&lt;br /&gt;
.mw-parser-output .navbox .hlist li ol,&lt;br /&gt;
.mw-parser-output .navbox .hlist li ul {&lt;br /&gt;
  white-space:normal&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output ol+.navbox-styles+.navbox,&lt;br /&gt;
.mw-parser-output ul+.navbox-styles+.navbox {&lt;br /&gt;
  margin-top:0.5em&lt;br /&gt;
}&lt;br /&gt;
@media screen {&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-title {&lt;br /&gt;
    background-color:#345&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-group {&lt;br /&gt;
    background-color:#567&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output th.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-subgroup td.navbox-title {&lt;br /&gt;
    background-color:#456&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-even {&lt;br /&gt;
    background-color:#000&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output td.navbox-odd {&lt;br /&gt;
    background-color:#123&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox-subgroup,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output .navbox-list {&lt;br /&gt;
    background-color:#111111&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-image,&lt;br /&gt;
  html.skin-theme-clientpref-night .mw-parser-output tr+tr&amp;gt;.navbox-list {&lt;br /&gt;
    border-top:2px solid #111111&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
@media screen and (prefers-color-scheme:dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-title {&lt;br /&gt;
    background-color:#345&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-group {&lt;br /&gt;
    background-color:#567&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output th.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-subgroup td.navbox-title {&lt;br /&gt;
    background-color:#456&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-even {&lt;br /&gt;
    background-color:#000&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output td.navbox-odd {&lt;br /&gt;
    background-color:#123&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox-subgroup,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output .navbox-list {&lt;br /&gt;
    background-color:#111111&lt;br /&gt;
  }&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-abovebelow,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-group,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-image,&lt;br /&gt;
  html.skin-theme-clientpref-os .mw-parser-output tr+tr&amp;gt;.navbox-list {&lt;br /&gt;
    border-top:2px solid #111111&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
/* === End of Navbox styles === */&lt;/div&gt;</summary>
		<author><name>Felipe</name></author>
	</entry>
</feed>