Merged logicui into master
authorDaniel Kirschten <daniel.kirschten@gmx.de>
Mon, 20 May 2019 17:31:07 +0000 (19:31 +0200)
committerDaniel Kirschten <daniel.kirschten@gmx.de>
Mon, 20 May 2019 17:31:07 +0000 (19:31 +0200)
39 files changed:
.gitmodules
README.md [new file with mode: 0644]
REQUIREMENTS.MD [new file with mode: 0644]
SampleERCP [new submodule]
era.mi/.classpath [new file with mode: 0644]
era.mi/.gitignore [new file with mode: 0644]
era.mi/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
era.mi/.settings/org.eclipse.jdt.ui.prefs [new file with mode: 0644]
era.mi/src/era/mi/logic/Simulation.java [new file with mode: 0644]
era.mi/src/era/mi/logic/Util.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/BasicComponent.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/BitDisplay.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/Clock.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/Component.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/Connector.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/Demux.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/ManualSwitch.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/Merger.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/Mux.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/Splitter.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/TriStateBuffer.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/gates/AndGate.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/gates/MultiInputGate.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/gates/NotGate.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/gates/OrGate.java [new file with mode: 0644]
era.mi/src/era/mi/logic/components/gates/XorGate.java [new file with mode: 0644]
era.mi/src/era/mi/logic/tests/ComponentTest.java [new file with mode: 0644]
era.mi/src/era/mi/logic/tests/GUITest.java [new file with mode: 0644]
era.mi/src/era/mi/logic/tests/TestBitDisplay.java [new file with mode: 0644]
era.mi/src/era/mi/logic/timeline/Timeline.java [new file with mode: 0644]
era.mi/src/era/mi/logic/timeline/TimelineEvent.java [new file with mode: 0644]
era.mi/src/era/mi/logic/timeline/TimelineEventHandler.java [new file with mode: 0644]
era.mi/src/era/mi/logic/types/Bit.java [new file with mode: 0644]
era.mi/src/era/mi/logic/types/BitVector.java [new file with mode: 0644]
era.mi/src/era/mi/logic/types/LogicType.java [new file with mode: 0644]
era.mi/src/era/mi/logic/types/MutationOperation.java [new file with mode: 0644]
era.mi/src/era/mi/logic/types/StrictLogicType.java [new file with mode: 0644]
era.mi/src/era/mi/logic/wires/Wire.java [new file with mode: 0644]
era.mi/src/era/mi/logic/wires/WireObserver.java [new file with mode: 0644]

index 7472573..c6d7205 100644 (file)
@@ -1,3 +1,7 @@
+[submodule "SampleERCP"]
+       path = SampleERCP
+       url = <address of this repo>
+       branch = SampleERCP
 [submodule "SWTHelper"]
        path = SWTHelper
        url = https://github.com/Haspamelodica/SWTHelper
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/REQUIREMENTS.MD b/REQUIREMENTS.MD
new file mode 100644 (file)
index 0000000..3a7a8d7
--- /dev/null
@@ -0,0 +1,165 @@
+# Rechnerarchitektur GP 2019
+#### Modularer Grafischer Schaltkreis Simulator (für die MI-Maschine)
+
+*Ihr könnt hier gerne nach belieben Dinge ändern, haltet euch nicht zurück^^*
+
+## Anforderungen
+
+### Pflichtfeatures
+1.  Emulation der MI-Maschine (siehe Am2900ME)
+2.  Visualisierung der MI-Maschine sehr ähnlich dem Bild der VL
+3.  sehr gute Website, Dokumentation, Hilfe aus dem Kontextmenü heraus
+4.  Hilfreiche Fehlermeldungen, Warnungen bei gefährlichen Operationen  
+    für uns/Entwickler: gutes Loggen, Absturzberichte
+5.  Immer hilfreiche Tooltips zu fast allem.
+    
+
+### Sollte möglich sein
+1.  Export der Ansicht
+2.  Sicht des Assemblerprogrammierers, spezifizieren einer ISA und Nutzung
+3.  Farben, Schriftgröße & Co. einstellbar (für Präsentation, Hell/Dunkel, Farbenblind/…)
+4.  Entwurf eines Schaltkreises mit einem Editor
+5.  Sprache ändern (für nicht Muttersprachler, internat. Potenzial, Erweiterungsmögl.)
+6.  Sehr einfache Installation (Eclipse Marketplace)
+7.  Editor sollte tooltip Einstellung erlauben
+8.  Rückgängig
+    
+
+### Wunschfeatures
+1.  Debuggen im Sinne von Breakpoints & Co.
+2.  interaktives Tutorial in der GUI
+3.  Zurückspulen der Simulation / Zeitleiste
+4.  Wiederherstellungspunkt (Status)
+
+### Weiterführende Ideen
+1.  x86 Modell
+2.  ARM Modell
+3.  Signalgraphen
+
+### Use Cases
+<details><summary><strong>Alle Use Cases anzeigen</strong></summary><p>
+
+#### 01. Installation
+Das Programm lässt sich entweder über den Eclipse Marketplace einfach installieren (oder als Eclipse Projekt) oder als eigenes Eclipse RPC Projekt direkt herunterladen (ggf. entpacken) und starten.<sup>[A]</sup> Das Programm / Plugin lässt sich auf Windows (7+), MacOS und Linux ausführen; alle verbreitete Hardware nach 2010 mit 64 Bit OS sollte funktionieren.<sup>[B]</sup> Es muss auf den Rechnern in den TUM Rechnerhallen funktionieren.<sup>[C]</sup>
+
+#### 02. Deinstallation
+Das Programm muss sich ohne nennenswerten Aufwand oder Kentnisse vom Benutzer rückstandslos entfernen lassen.<sup>[A]</sup>
+
+#### 03. Programmstart
+Das Programm bzw. Plugin startet schnell (unter 10 sec.; besser unter 5 sec).<sup>[A]</sup> Bei dem ersten Programmstart ist ein Willkommensfenster zu sehen, das auf erklärende Resourcen verweist, den Nutzer grüßt und ggf. auf die (Sprach-)Einstellungen hinweist.<sup>[B]</sup> Es wird bestenfalls auf ein Beispielprojekt verwiesen (kann erstellt werden?) um dem Nutzer zu ermöglichen, sich damit vertraut zu machen.<sup>[C]</sup> Ein schnell Link zum Erstellen eines neuen Projekts/Simulation/... wird angezeigt.<sup>[D]</sup>
+
+#### 04. Programmende
+Sollten bei Beenden des Programms oder Programmabsturz nicht persistierte Daten vorliegen, muss dafür gesorgt werden, dass diese nicht verloren gehen.<sup>[A]</sup> Enweder durch einen Dialog<sup>[B]</sup>, oder durch erzeugte Recovery-Dateien<sup>[C]</sup>. Bei Absturz des Programms wird eine passende und aufschlussreiche Fehlermeldung angezeigt, bestenfalls mit Link zu einer Report-Möglichkeit.<sup>[D]</sup>
+
+#### 05. Einstellungen
+Die Hauptextfarbe und Schriftgröße muss einstellbar sein<sup>[A]</sup>, besser noch Hintergrund- und Markierungsfarben und Schriftstil.<sup>[B]</sup> Die Einstellung der Sprache muss möglich sein.<sup>[C]</sup>
+
+#### 06. Einstieg
+Das Einstiegsprojekt zeigt zu Beginn die passende Ansicht an, die Mikroprogrammierung ist wie bei dem Am2900ME ungehindert möglich.<sup>[A]</sup> Bestenfalls ist dieses beim (erstmaligen) Start direkt geöffnet.<sup>[B]</sup> Wichtige Fenster (v.a. die grafische Darstellung) sind direkt sichtbar, oder zumindest in einem Tab geöffnet (müssen nicht gesucht werden).<sup>[C]</sup>
+
+#### 07. Nutzung der Visualisierung
+Die Visualisierung soll möglichst der Vorlesung entsprechen.<sup>[A]</sup> Sie lässt sich transformieren (z.B. Zoom mit dem Mausrad, Verschieben mit Mausklick oder Mausklick und Alt) und wieder zurücksetzen.<sup>[B]</sup>
+
+#### 08. Hilfestellung
+Mit dem Produkt wird eine offline-Dokumentation mitgeliefert. (bestenfalls DE und EN)<sup>[A]</sup> Mit Rechtsklick lässt sich über ein Kontextmenü der Teil der Dokumentation öffnen, der das angeklickte und seine Funktion beschreibt (Vorbild Cinema4D). Dies sollte mit fast allem möglich sein.<sup>[B]</sup> Bleibt man mit der Maus länger über einem GUI-Bestandteil, wird eine kurze Beschreibung/Hilfe dazu angezeigt (Tooltip).<sup>[C]</sup>
+
+#### 09. Dateihandhabung
+Die Dateien sollten möglichst menschenlesbar sein, z.B. in CSV-Format, um Kommunikation mit anderer Software und Änderungen durch den Nutzer zu ermöglichen.<sup>[A]</sup> Die Dateien enthalten eine Versionsnummer, um Abwärtskompatibilität und hilfreiche Fehlermeldungen zu ermöglichen.<sup>[B]</sup>
+
+#### 10. Entwicklerunterstützung
+Das Projekt soll sich einfach bauen/kompilieren/exportieren lassen, es muss beschrieben werden wie das möglich ist.<sup>[A]</sup> Die Verbreitung zur Anwendung soll ebenfalls unproblematisch sein.<sup>[B]</sup>
+
+#### Fügt hier eure eigenen dazu, fortlaufend nummeriert und ggf. mit <sup>[A,B,C,..]</sup> genauer spezifiziert.
+
+</p></details>
+
+### UML und Ähnliches
+#### Grobe Struktur
+![image](https://user-images.githubusercontent.com/11130248/57186855-aae24600-6ee6-11e9-88b4-371622d43adc.png)
+
+## Technologie-Stack
+*   Git (anscheinend über Gitlab)
+*   Eclipse RCP
+    -  (noch zu testen)
+*   Java
+    -   Version 8?
+    -   Version 10?
+    -   Version 12?
+*   Bibliotheken
+    -   JUnit 5.4.x ? AssertJ? [http://joel-costigliola.github.io/assertj/index.html](http://joel-costigliola.github.io/assertj/index.html)
+    - (Apache Commons?)
+    Sehr weit verbreitete und gut designte & getestete Lib;  
+    aber zusätzliche Dep.
+    - Sonstiges? Logging? Log4j
+-   Automatischer Build mit Maven? Wohl eher nicht, wegen ERPC Build
+
+## Meilensteine
+
+## Außenwirkung
+### Name, Abkürzung
+### Signet, Logo
+### Beschreibung
+
+## Sonstiges
+### Nützliche Links
+- [https://wiki.eclipse.org/Rich_Client_Platform](https://wiki.eclipse.org/Rich_Client_Platform) -Links
+- [https://wiki.eclipse.org/User_Interface_Guidelines](https://wiki.eclipse.org/User_Interface_Guidelines) - etwas alt, aber nützlich
+- [https://download.eclipse.org/eclipse/downloads/](https://download.eclipse.org/eclipse/downloads/) - Eclipse Project
+- [https://www.vogella.com/tutorials/EclipseRCP/article.html](https://www.vogella.com/tutorials/EclipseRCP/article.html) - **Eclipse RPC Tutorial**
+- [https://github.com/eclipse/gef/wiki](https://github.com/eclipse/gef/wiki) - **Wiki für das Grafikframework**
+- [https://github.com/eclipse/MaisiKoleni/Am2900ME](https://github.com/eclipse/MaisiKoleni/Am2900ME) - der geistige Vorgänger
+- [https://github.com/Haspamelodica/Am2900ME_swt](https://github.com/Haspamelodica/Am2900ME_swt) -  ... auch mit SWT
+
+### Rechtliches
+<details><summary>
+MIT Lizenz? - https://choosealicense.com/licenses/mit/
+
+</summary><p>
+
+>MIT License
+>
+>Copyright (c) 2019 Christian Femers, Daniel Kirschten and Fabian Stemmler
+>
+> 
+>
+>Permission is hereby granted, free of charge, to any person obtaining a copy
+>of this software and associated documentation files (the "Software"), to deal
+>in the Software without restriction, including without limitation the rights
+>to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+>copies of the Software, and to permit persons to whom the Software is
+>furnished to do so, subject to the following conditions:
+>
+>The above copyright notice and this permission notice shall be included in all
+>copies or substantial portions of the Software.
+>
+>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+>IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+>FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+>AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+>LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+>OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+>SOFTWARE.
+
+</p></details>
+
+Muss ggf. Eclipse Public License verwendet werden wegen Copyleft?
+<details><summary>
+Eclipse Public License 2.0? - https://www.eclipse.org/legal/epl-2.0/
+
+</summary><p>
+
+>Copyright (c) 2019 Christian Femers, Daniel Kirschten and Fabian Stemmler
+>
+>This program and the accompanying materials are made
+>available under the terms of the Eclipse Public License 2.0
+>which is available at https://www.eclipse.org/legal/epl-2.0/
+>
+>This Source Code may also be made available under the following Secondary
+>Licenses when the conditions for such availability set forth in the Eclipse
+>Public License, v. 2.0 are satisfied: GNU General Public License, version 2
+>with the GNU Classpath Exception which is
+>available at https://www.gnu.org/software/classpath/license.html.
+>
+>SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+</p></details>
diff --git a/SampleERCP b/SampleERCP
new file mode 160000 (submodule)
index 0000000..10e8749
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 10e87495184e49c5cab906921f0afaf2473d9aed
diff --git a/era.mi/.classpath b/era.mi/.classpath
new file mode 100644 (file)
index 0000000..be85523
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src" />
+       <classpathentry kind="con"
+               path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5" />
+       <classpathentry kind="con"
+               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-10">
+               <attributes>
+                       <attribute name="module" value="true" />
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="output" path="bin" />
+</classpath>
diff --git a/era.mi/.gitignore b/era.mi/.gitignore
new file mode 100644 (file)
index 0000000..f59a3a7
--- /dev/null
@@ -0,0 +1,15 @@
+/bin/
+*.class
+*.log
+
+# package files
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# vm crash logs
+hs_err_pid*
\ No newline at end of file
diff --git a/era.mi/.settings/org.eclipse.jdt.core.prefs b/era.mi/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..82c6671
--- /dev/null
@@ -0,0 +1,367 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=10
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=10
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=10
+org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
+org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false
+org.eclipse.jdt.core.formatter.align_with_spaces=false
+org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
+org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
+org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line
+org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=true
+org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
+org.eclipse.jdt.core.formatter.comment.indent_tag_description=false
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=140
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.lineSplit=140
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/era.mi/.settings/org.eclipse.jdt.ui.prefs b/era.mi/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644 (file)
index 0000000..07b37c1
--- /dev/null
@@ -0,0 +1,63 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_ERA-MI
+formatter_settings_version=16
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=false
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=true
+sp_cleanup.convert_to_enhanced_for_loop=true
+sp_cleanup.correct_indentation=true
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=true
+sp_cleanup.never_use_blocks=true
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=true
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_modifiers=true
+sp_cleanup.remove_redundant_semicolons=true
+sp_cleanup.remove_redundant_type_arguments=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=true
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=true
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=true
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=true
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/era.mi/src/era/mi/logic/Simulation.java b/era.mi/src/era/mi/logic/Simulation.java
new file mode 100644 (file)
index 0000000..0468ec1
--- /dev/null
@@ -0,0 +1,9 @@
+package era.mi.logic;
+
+import era.mi.logic.timeline.Timeline;
+
+public class Simulation
+{
+       public final static Timeline TIMELINE = new Timeline(11);
+
+}
\ No newline at end of file
diff --git a/era.mi/src/era/mi/logic/Util.java b/era.mi/src/era/mi/logic/Util.java
new file mode 100644 (file)
index 0000000..d621a44
--- /dev/null
@@ -0,0 +1,110 @@
+package era.mi.logic;
+
+import java.util.Arrays;
+
+import era.mi.logic.types.Bit;
+
+public final class Util
+{
+
+       @SuppressWarnings("unchecked")
+       public static <T> T[] concat(T[]... arrays)
+       {
+               if (arrays.length == 0)
+                       throw new IllegalArgumentException("Cannot concatenate 0 arrays.");
+
+               int length = 0;
+               for (T[] array : arrays)
+                       length += array.length;
+
+               T[] newArray = Arrays.copyOf(arrays[0], length);
+               int appendIndex = arrays[0].length;
+               for (int i = 1; i < arrays.length; i++)
+               {
+                       System.arraycopy(arrays[i], 0, newArray, appendIndex, arrays[i].length);
+                       appendIndex += arrays[i].length;
+               }
+
+               return newArray;
+       }
+
+//     @SuppressWarnings("unchecked")
+//     public static <T> T[][] split(T[] array, int... lengths)
+//     {
+//             //TODO: implement array split again; This version contains an illegal cast
+//             int totalLength = 0;
+//             for(int length : lengths)
+//                     totalLength += length;
+//             
+//             if(totalLength != array.length)
+//                     throw new IllegalArgumentException(); //TODO: add proper error message
+//             
+//             Object[][] newArray = new Object[lengths.length][];
+//             int splitIndex = 0;
+//             for(int i = 0; i < lengths.length; i++)
+//             {
+//                     System.arraycopy(array, splitIndex, newArray, 0, lengths[i]);
+//                     splitIndex += lengths[i];
+//             }
+//             
+//             return (T[][]) newArray;
+//     }
+
+       public static Bit[] and(Bit[] a, Bit[] b)
+       {
+               return binBitOp(a, b, Bit::and);
+       }
+
+       public static Bit[] or(Bit[] a, Bit[] b)
+       {
+               return binBitOp(a, b, Bit::or);
+       }
+
+       public static Bit[] xor(Bit[] a, Bit[] b)
+       {
+               return binBitOp(a, b, Bit::xor);
+       }
+
+       private static Bit[] binBitOp(Bit[] a, Bit[] b, BitOp op)
+       {
+               if (a.length != b.length)
+                       throw new IllegalArgumentException("Bit Arrays were not of equal length.");
+               Bit[] out = new Bit[a.length];
+               for (int i = 0; i < a.length; i++)
+               {
+                       out[i] = op.execute(a[i], b[i]);
+               }
+               return out;
+       }
+
+       public static Bit[] not(Bit[] a)
+       {
+               Bit[] out = new Bit[a.length];
+               for (int i = 0; i < a.length; i++)
+               {
+                       out[i] = a[i].not();
+               }
+               return out;
+       }
+
+       /**
+        * uses the {@link Bit#combineWith(Bit)} method, does not create a new array, the result is stored in the first array.
+        * 
+        * @author Christian Femers
+        */
+       public static Bit[] combineInto(Bit[] dest, Bit[] addition)
+       {
+               if (dest.length != addition.length)
+                       throw new IllegalArgumentException("Bit Arrays were not of equal length.");
+               for (int i = 0; i < addition.length; i++)
+               {
+                       dest[i] = dest[i].join(addition[i]);
+               }
+               return dest;
+       }
+
+       interface BitOp
+       {
+               Bit execute(Bit a, Bit b);
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/BasicComponent.java b/era.mi/src/era/mi/logic/components/BasicComponent.java
new file mode 100644 (file)
index 0000000..e695283
--- /dev/null
@@ -0,0 +1,35 @@
+package era.mi.logic.components;
+
+import era.mi.logic.Simulation;
+import era.mi.logic.types.BitVector;
+import era.mi.logic.wires.Wire;
+import era.mi.logic.wires.WireObserver;
+
+/**
+ * A basic component that recomputes all outputs (with a delay), when it is updated.
+ * 
+ * @author Fabian Stemmler
+ */
+public abstract class BasicComponent implements WireObserver, Component
+{
+       private int processTime;
+
+       /**
+        * 
+        * @param processTime Amount of time this component takes to update its outputs. Must be more than 0, otherwise 1 is assumed.
+        * 
+        * @author Fabian Stemmler
+        */
+       public BasicComponent(int processTime)
+       {
+               this.processTime = processTime > 0 ? processTime : 1;
+       }
+
+       @Override
+       public void update(Wire initiator, BitVector oldValues)
+       {
+               Simulation.TIMELINE.addEvent(e -> compute(), processTime);
+       }
+
+       protected abstract void compute();
+}
diff --git a/era.mi/src/era/mi/logic/components/BitDisplay.java b/era.mi/src/era/mi/logic/components/BitDisplay.java
new file mode 100644 (file)
index 0000000..971eb7a
--- /dev/null
@@ -0,0 +1,49 @@
+package era.mi.logic.components;
+
+import java.util.List;
+
+import era.mi.logic.types.Bit;
+import era.mi.logic.types.BitVector;
+import era.mi.logic.wires.Wire.WireEnd;
+
+public class BitDisplay extends BasicComponent
+{
+       private final WireEnd in;
+       private BitVector displayedValue;
+
+       public BitDisplay(WireEnd in)
+       {
+               super(1);
+               this.in = in;
+               in.addObserver(this);
+               compute();
+       }
+
+       @Override
+       protected void compute()
+       {
+               displayedValue = in.getValues();
+       }
+
+       public BitVector getDisplayedValue()
+       {
+               return displayedValue;
+       }
+
+       public boolean isDisplaying(Bit... values)
+       {
+               return displayedValue.equals(BitVector.of(values));
+       }
+
+       @Override
+       public List<WireEnd> getAllInputs()
+       {
+               return List.of(in);
+       }
+
+       @Override
+       public List<WireEnd> getAllOutputs()
+       {
+               return List.of();
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/Clock.java b/era.mi/src/era/mi/logic/components/Clock.java
new file mode 100644 (file)
index 0000000..07419b4
--- /dev/null
@@ -0,0 +1,59 @@
+package era.mi.logic.components;
+
+import java.util.List;
+
+import era.mi.logic.Simulation;
+import era.mi.logic.timeline.TimelineEvent;
+import era.mi.logic.timeline.TimelineEventHandler;
+import era.mi.logic.types.Bit;
+import era.mi.logic.wires.Wire;
+import era.mi.logic.wires.Wire.WireEnd;
+
+public class Clock implements TimelineEventHandler, Component
+{
+       private boolean toggle = false;
+       private WireEnd out;
+       private int delta;
+
+       /**
+        * 
+        * @param out   {@link Wire} the clock's impulses are fed into
+        * @param delta ticks between rising and falling edge
+        */
+       public Clock(WireEnd out, int delta)
+       {
+               this.delta = delta;
+               this.out = out;
+               addToTimeline();
+       }
+
+       @Override
+       public void handle(TimelineEvent e)
+       {
+               addToTimeline();
+               out.feedSignals(toggle ? Bit.ONE : Bit.ZERO);
+               toggle = !toggle;
+       }
+
+       public WireEnd getOut()
+       {
+               return out;
+       }
+
+       private void addToTimeline()
+       {
+               Simulation.TIMELINE.addEvent(this, delta);
+       }
+
+       @Override
+       public List<WireEnd> getAllInputs()
+       {
+               return List.of();
+       }
+
+       @Override
+       public List<WireEnd> getAllOutputs()
+       {
+               return List.of(out);
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/Component.java b/era.mi/src/era/mi/logic/components/Component.java
new file mode 100644 (file)
index 0000000..522a58d
--- /dev/null
@@ -0,0 +1,20 @@
+package era.mi.logic.components;
+
+import java.util.List;
+
+import era.mi.logic.wires.Wire.WireEnd;
+
+public interface Component
+{
+
+       /**
+        * Returns immutable list of all inputs to the {@link Component} (including e.g. the select bits to a MUX). Intended for visualization
+        * in the UI.
+        */
+       public List<WireEnd> getAllInputs();
+
+       /**
+        * Returns immutable list of all outputs to the {@link Component}. Intended for visualization in the UI.
+        */
+       public List<WireEnd> getAllOutputs();
+}
diff --git a/era.mi/src/era/mi/logic/components/Connector.java b/era.mi/src/era/mi/logic/components/Connector.java
new file mode 100644 (file)
index 0000000..89fb96e
--- /dev/null
@@ -0,0 +1,75 @@
+package era.mi.logic.components;
+
+import java.util.List;
+
+import era.mi.logic.Simulation;
+import era.mi.logic.types.BitVector;
+import era.mi.logic.wires.Wire;
+import era.mi.logic.wires.Wire.WireEnd;
+import era.mi.logic.wires.WireObserver;
+
+public class Connector implements WireObserver, Component
+{
+       private boolean connected;
+       private final WireEnd a;
+       private final WireEnd b;
+
+       public Connector(WireEnd a, WireEnd b)
+       {
+               if (a.length() != b.length())
+                       throw new IllegalArgumentException(String.format("WireArray width does not match: %d, %d", a.length(), b.length()));
+               this.a = a;
+               this.b = b;
+               a.addObserver(this);
+               b.addObserver(this);
+       }
+
+       public void connect()
+       {
+               connected = true;
+               update(a.getWire());
+               update(b.getWire());
+       }
+
+       public void disconnect()
+       {
+               connected = false;
+               a.clearSignals();
+               b.clearSignals();
+       }
+
+       public void setConnection(boolean connected)
+       {
+               if (connected)
+                       connect();
+               else
+                       disconnect();
+       }
+
+       @Override
+       public void update(Wire initiator, BitVector oldValues)
+       {
+               if (connected)
+                       Simulation.TIMELINE.addEvent(e -> update(initiator), 1);
+       }
+
+       private void update(Wire initiator)
+       {
+               if (initiator == a.getWire())
+                       b.feedSignals(a.wireValuesExcludingMe());
+               else
+                       a.feedSignals(b.wireValuesExcludingMe());
+       }
+
+       @Override
+       public List<WireEnd> getAllInputs()
+       {
+               return List.of(a, b);
+       }
+
+       @Override
+       public List<WireEnd> getAllOutputs()
+       {
+               return List.of(a, b);
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/Demux.java b/era.mi/src/era/mi/logic/components/Demux.java
new file mode 100644 (file)
index 0000000..147dd7f
--- /dev/null
@@ -0,0 +1,80 @@
+package era.mi.logic.components;
+
+import java.util.List;
+
+import era.mi.logic.wires.Wire;
+import era.mi.logic.wires.Wire.WireEnd;
+
+/**
+ * Models a multiplexer. Takes an arbitrary amount of input {@link Wire}s, one of which, as determined by select, is put through to the
+ * output.
+ * 
+ * @author Fabian Stemmler
+ *
+ */
+public class Demux extends BasicComponent
+{
+       private final WireEnd select, in;
+       private final WireEnd[] outputs;
+       private final int outputSize;
+       private int selected = -1;
+
+       /**
+        * Output {@link Wire}s and in must be of uniform length
+        * 
+        * @param in      Must be of uniform length with all outputs.
+        * @param select  Indexes the output array to which the input is mapped. Must have enough bits to index all outputs.
+        * @param outputs One of these outputs receives the input signal, depending on the select bits
+        */
+       public Demux(int processTime, WireEnd in, WireEnd select, WireEnd... outputs)
+       {
+               super(processTime);
+               outputSize = in.length();
+
+               this.in = in;
+               this.outputs = outputs;
+               for (int i = 0; i < this.outputs.length; i++)
+               {
+                       if (outputs[i].length() != outputSize)
+                               throw new IllegalArgumentException("All DEMUX wire arrays must be of uniform length!");
+                       this.outputs[i] = outputs[i];
+               }
+
+               this.select = select;
+               select.addObserver(this);
+
+               int maxInputs = 1 << select.length();
+               if (this.outputs.length > maxInputs)
+                       throw new IllegalArgumentException("There are more outputs (" + this.outputs.length + ") to the DEMUX than supported by "
+                                       + select.length() + " select bits (" + maxInputs + ").");
+               in.addObserver(this);
+       }
+
+       @Override
+       public void compute()
+       {
+               int selectValue = select.hasNumericValue() ? (int) select.getUnsignedValue() : -1;
+               if (selectValue >= outputs.length)
+                       selectValue = -1;
+
+               if (selected != selectValue && selected != -1)
+                       outputs[selected].clearSignals();
+
+               selected = selectValue;
+
+               if (selectValue != -1)
+                       outputs[selectValue].feedSignals(in.getValues());
+       }
+
+       @Override
+       public List<WireEnd> getAllInputs()
+       {
+               return List.of(in, select);
+       }
+
+       @Override
+       public List<WireEnd> getAllOutputs()
+       {
+               return List.of(outputs);
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/ManualSwitch.java b/era.mi/src/era/mi/logic/components/ManualSwitch.java
new file mode 100644 (file)
index 0000000..0ad4a76
--- /dev/null
@@ -0,0 +1,71 @@
+package era.mi.logic.components;
+
+import java.util.List;
+
+import era.mi.logic.types.Bit;
+import era.mi.logic.wires.Wire.WireEnd;
+
+/**
+ * This class models a simple on/off (ONE/ZERO) switch for user interaction.
+ *
+ * @author Christian Femers
+ *
+ */
+public class ManualSwitch implements Component
+{
+       private WireEnd output;
+       private boolean isOn;
+
+       public ManualSwitch(WireEnd output)
+       {
+               if (output.length() != 1)
+                       throw new IllegalArgumentException("Switch output can be only a single wire");
+               this.output = output;
+       }
+
+       public void switchOn()
+       {
+               setState(true);
+       }
+
+       public void switchOff()
+       {
+               setState(false);
+       }
+
+       public void toggle()
+       {
+               setState(!isOn);
+       }
+
+       public void setState(boolean isOn)
+       {
+               if (this.isOn == isOn)
+                       return;
+               this.isOn = isOn;
+               output.feedSignals(getValue());
+       }
+
+       public boolean isOn()
+       {
+               return isOn;
+       }
+
+       public Bit getValue()
+       {
+               return isOn ? Bit.ONE : Bit.ZERO;
+       }
+
+       @Override
+       public List<WireEnd> getAllInputs()
+       {
+               return List.of();
+       }
+
+       @Override
+       public List<WireEnd> getAllOutputs()
+       {
+               return List.of(output);
+       }
+
+}
diff --git a/era.mi/src/era/mi/logic/components/Merger.java b/era.mi/src/era/mi/logic/components/Merger.java
new file mode 100644 (file)
index 0000000..34ba217
--- /dev/null
@@ -0,0 +1,82 @@
+package era.mi.logic.components;
+
+import java.util.List;
+
+import era.mi.logic.types.BitVector;
+import era.mi.logic.wires.Wire;
+import era.mi.logic.wires.Wire.WireEnd;
+import era.mi.logic.wires.WireObserver;
+
+public class Merger implements WireObserver, Component
+{
+       private WireEnd out;
+       private WireEnd[] inputs;
+       private int[] beginningIndex;
+
+       /**
+        * 
+        * @param union  The output of merging n {@link Wire}s into one. Must have length = a1.length() + a2.length() + ... + an.length().
+        * @param inputs The inputs to be merged into the union
+        */
+       public Merger(WireEnd union, WireEnd... inputs)
+       {
+               this.inputs = inputs;
+               this.out = union;
+               this.beginningIndex = new int[inputs.length];
+
+               int length = 0;
+               for (int i = 0; i < inputs.length; i++)
+               {
+                       beginningIndex[i] = length;
+                       length += inputs[i].length();
+                       inputs[i].addObserver(this);
+               }
+
+               if (length != union.length())
+                       throw new IllegalArgumentException(
+                                       "The output of merging n WireArrays into one must have length = a1.length() + a2.length() + ... + an.length().");
+       }
+
+       public WireEnd getInput(int index)
+       {
+               return inputs[index];
+       }
+
+       public WireEnd getUnion()
+       {
+               return out;
+       }
+
+       @Override
+       public void update(Wire initiator, BitVector oldValues)
+       {
+               int index = find(initiator);
+               int beginning = beginningIndex[index];
+               out.feedSignals(beginning, inputs[index].getValues());
+       }
+
+       private int find(Wire w)
+       {
+               for (int i = 0; i < inputs.length; i++)
+                       if (inputs[i].getWire() == w)
+                               return i;
+               return -1;
+       }
+
+       public WireEnd[] getInputs()
+       {
+               return inputs.clone();
+       }
+
+       @Override
+       public List<WireEnd> getAllInputs()
+       {
+               return List.of(inputs);
+       }
+
+       @Override
+       public List<WireEnd> getAllOutputs()
+       {
+               return List.of(out);
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/Mux.java b/era.mi/src/era/mi/logic/components/Mux.java
new file mode 100644 (file)
index 0000000..aea7116
--- /dev/null
@@ -0,0 +1,93 @@
+package era.mi.logic.components;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import era.mi.logic.wires.Wire;
+import era.mi.logic.wires.Wire.WireEnd;
+
+/**
+ * Models a multiplexer. Takes an arbitrary amount of input {@link Wire}s, one of which, as determined by select, is put through to the
+ * output.
+ * 
+ * @author Fabian Stemmler
+ *
+ */
+public class Mux extends BasicComponent
+{
+       private WireEnd select;
+       private WireEnd out;
+       private WireEnd[] inputs;
+       private final int outputSize;
+
+       /**
+        * Input {@link Wire}s and out must be of uniform length
+        * 
+        * @param out    Must be of uniform length with all inputs.
+        * @param select Indexes the input array which is to be mapped to the output. Must have enough bits to index all inputs.
+        * @param inputs One of these inputs is mapped to the output, depending on the select bits
+        */
+       public Mux(int processTime, WireEnd out, WireEnd select, WireEnd... inputs)
+       {
+               super(processTime);
+               outputSize = out.length();
+
+               this.inputs = inputs.clone();
+               for (int i = 0; i < this.inputs.length; i++)
+               {
+                       if (inputs[i].length() != outputSize)
+                               throw new IllegalArgumentException("All MUX wire arrays must be of uniform length!");
+                       inputs[i].addObserver(this);
+               }
+
+               this.select = select;
+               select.addObserver(this);
+
+               int maxInputs = 1 << select.length();
+               if (this.inputs.length > maxInputs)
+                       throw new IllegalArgumentException("There are more inputs (" + this.inputs.length + ") to the MUX than supported by "
+                                       + select.length() + " select bits (" + maxInputs + ").");
+
+               this.out = out;
+       }
+
+       public WireEnd getOut()
+       {
+               return out;
+       }
+
+       public WireEnd getSelect()
+       {
+               return select;
+       }
+
+       @Override
+       public void compute()
+       {
+               int selectValue;
+               if (!select.hasNumericValue() || (selectValue = (int) select.getUnsignedValue()) >= inputs.length)
+               {
+                       out.clearSignals();
+                       return;
+               }
+
+               WireEnd active = inputs[selectValue];
+               out.feedSignals(active.getValues());
+       }
+
+       @Override
+       public List<WireEnd> getAllInputs()
+       {
+               ArrayList<WireEnd> wires = new ArrayList<WireEnd>(Arrays.asList(inputs));
+               wires.add(select);
+               return Collections.unmodifiableList(wires);
+       }
+
+       @Override
+       public List<WireEnd> getAllOutputs()
+       {
+               return List.of(out);
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/Splitter.java b/era.mi/src/era/mi/logic/components/Splitter.java
new file mode 100644 (file)
index 0000000..4764c27
--- /dev/null
@@ -0,0 +1,43 @@
+package era.mi.logic.components;
+
+import era.mi.logic.types.BitVector;
+import era.mi.logic.wires.Wire;
+import era.mi.logic.wires.Wire.WireEnd;
+import era.mi.logic.wires.WireObserver;
+
+public class Splitter implements WireObserver
+{
+       private WireEnd input;
+       private WireEnd[] outputs;
+
+       public Splitter(WireEnd input, WireEnd... outputs)
+       {
+               this.input = input;
+               this.outputs = outputs;
+               input.addObserver(this);
+               int length = 0;
+               for (WireEnd out : outputs)
+                       length += out.length();
+
+               if (input.length() != length)
+                       throw new IllegalArgumentException(
+                                       "The input of splitting one into n WireArrays must have length = a1.length() + a2.length() + ... + an.length().");
+       }
+
+       protected void compute()
+       {
+               BitVector inputBits = input.getValues();
+               int startIndex = 0;
+               for (int i = 0; i < outputs.length; i++)
+               {
+                       outputs[i].feedSignals(inputBits.subVector(startIndex, startIndex + outputs[i].length()));
+                       startIndex += outputs[i].length();
+               }
+       }
+
+       @Override
+       public void update(Wire initiator, BitVector oldValues)
+       {
+               compute();
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/TriStateBuffer.java b/era.mi/src/era/mi/logic/components/TriStateBuffer.java
new file mode 100644 (file)
index 0000000..c0dd76e
--- /dev/null
@@ -0,0 +1,49 @@
+package era.mi.logic.components;
+
+import java.util.List;
+
+import era.mi.logic.types.Bit;
+import era.mi.logic.wires.Wire.WireEnd;
+
+public class TriStateBuffer extends BasicComponent
+{
+       WireEnd in, enable;
+       WireEnd out;
+
+       public TriStateBuffer(int processTime, WireEnd in, WireEnd out, WireEnd enable)
+       {
+               super(processTime);
+               if (in.length() != out.length())
+                       throw new IllegalArgumentException(
+                                       "Tri-state output must have the same amount of bits as the input. Input: " + in.length() + " Output: " + out.length());
+               if (enable.length() != 1)
+                       throw new IllegalArgumentException("Tri-state enable must have exactly one bit, not " + enable.length() + ".");
+               this.in = in;
+               in.addObserver(this);
+               this.enable = enable;
+               enable.addObserver(this);
+               this.out = out;
+       }
+
+       @Override
+       protected void compute()
+       {
+               if (enable.getValue() == Bit.ONE)
+                       out.feedSignals(in.getValues());
+               else
+                       out.clearSignals();
+       }
+
+       @Override
+       public List<WireEnd> getAllInputs()
+       {
+               return List.of(in, enable);
+       }
+
+       @Override
+       public List<WireEnd> getAllOutputs()
+       {
+               return List.of(out);
+       }
+
+}
diff --git a/era.mi/src/era/mi/logic/components/gates/AndGate.java b/era.mi/src/era/mi/logic/components/gates/AndGate.java
new file mode 100644 (file)
index 0000000..5da680e
--- /dev/null
@@ -0,0 +1,12 @@
+package era.mi.logic.components.gates;
+
+import era.mi.logic.types.BitVector.BitVectorMutator;
+import era.mi.logic.wires.Wire.WireEnd;
+
+public class AndGate extends MultiInputGate
+{
+       public AndGate(int processTime, WireEnd out, WireEnd... in)
+       {
+               super(processTime, BitVectorMutator::and, out, in);
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/gates/MultiInputGate.java b/era.mi/src/era/mi/logic/components/gates/MultiInputGate.java
new file mode 100644 (file)
index 0000000..0b4a5d0
--- /dev/null
@@ -0,0 +1,54 @@
+package era.mi.logic.components.gates;
+
+import java.util.List;
+
+import era.mi.logic.components.BasicComponent;
+import era.mi.logic.types.BitVector.BitVectorMutator;
+import era.mi.logic.types.MutationOperation;
+import era.mi.logic.wires.Wire.WireEnd;
+
+public abstract class MultiInputGate extends BasicComponent
+{
+       protected WireEnd[] in;
+       protected WireEnd out;
+       protected final int length;
+       protected MutationOperation op;
+
+       protected MultiInputGate(int processTime, MutationOperation op, WireEnd out, WireEnd... in)
+       {
+               super(processTime);
+               this.op = op;
+               length = out.length();
+               this.in = in.clone();
+               if (in.length < 1)
+                       throw new IllegalArgumentException(String.format("Cannot create gate with %d wires.", in.length));
+               for (WireEnd w : in)
+               {
+                       if (w.length() != length)
+                               throw new IllegalArgumentException("All wires connected to the gate must be of uniform length.");
+                       w.addObserver(this);
+               }
+               this.out = out;
+       }
+
+       @Override
+       public List<WireEnd> getAllInputs()
+       {
+               return List.of(in);
+       }
+
+       @Override
+       public List<WireEnd> getAllOutputs()
+       {
+               return List.of(out);
+       }
+
+       @Override
+       protected void compute()
+       {
+               BitVectorMutator mutator = BitVectorMutator.empty();
+               for (WireEnd w : in)
+                       op.apply(mutator, w.getValues());
+               out.feedSignals(mutator.get());
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/gates/NotGate.java b/era.mi/src/era/mi/logic/components/gates/NotGate.java
new file mode 100644 (file)
index 0000000..1c0d833
--- /dev/null
@@ -0,0 +1,48 @@
+package era.mi.logic.components.gates;
+
+import java.util.List;
+
+import era.mi.logic.components.BasicComponent;
+import era.mi.logic.wires.Wire.WireEnd;
+
+public class NotGate extends BasicComponent
+{
+       private WireEnd in;
+       private WireEnd out;
+
+       public NotGate(int processTime, WireEnd in, WireEnd out)
+       {
+               super(processTime);
+               this.in = in;
+               in.addObserver(this);
+               this.out = out;
+       }
+
+       @Override
+       protected void compute()
+       {
+               out.feedSignals(in.getValues().not());
+       }
+
+       public WireEnd getIn()
+       {
+               return in;
+       }
+
+       public WireEnd getOut()
+       {
+               return out;
+       }
+
+       @Override
+       public List<WireEnd> getAllInputs()
+       {
+               return List.of(in);
+       }
+
+       @Override
+       public List<WireEnd> getAllOutputs()
+       {
+               return List.of(out);
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/gates/OrGate.java b/era.mi/src/era/mi/logic/components/gates/OrGate.java
new file mode 100644 (file)
index 0000000..8c1775f
--- /dev/null
@@ -0,0 +1,12 @@
+package era.mi.logic.components.gates;
+
+import era.mi.logic.types.BitVector.BitVectorMutator;
+import era.mi.logic.wires.Wire.WireEnd;
+
+public class OrGate extends MultiInputGate
+{
+       public OrGate(int processTime, WireEnd out, WireEnd... in)
+       {
+               super(processTime, BitVectorMutator::or, out, in);
+       }
+}
diff --git a/era.mi/src/era/mi/logic/components/gates/XorGate.java b/era.mi/src/era/mi/logic/components/gates/XorGate.java
new file mode 100644 (file)
index 0000000..73a2556
--- /dev/null
@@ -0,0 +1,18 @@
+package era.mi.logic.components.gates;
+
+import era.mi.logic.types.BitVector.BitVectorMutator;
+import era.mi.logic.wires.Wire.WireEnd;
+
+/**
+ * Outputs 1 when the number of 1 inputs is odd.
+ * 
+ * @author Fabian Stemmler
+ */
+public class XorGate extends MultiInputGate
+{
+       public XorGate(int processTime, WireEnd out, WireEnd... in)
+       {
+               super(processTime, BitVectorMutator::xor, out, in);
+       }
+
+}
diff --git a/era.mi/src/era/mi/logic/tests/ComponentTest.java b/era.mi/src/era/mi/logic/tests/ComponentTest.java
new file mode 100644 (file)
index 0000000..8a9bc5a
--- /dev/null
@@ -0,0 +1,384 @@
+package era.mi.logic.tests;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.function.LongConsumer;
+
+import org.junit.jupiter.api.Test;
+
+import era.mi.logic.Simulation;
+import era.mi.logic.components.Connector;
+import era.mi.logic.components.Demux;
+import era.mi.logic.components.Merger;
+import era.mi.logic.components.Mux;
+import era.mi.logic.components.Splitter;
+import era.mi.logic.components.TriStateBuffer;
+import era.mi.logic.components.gates.AndGate;
+import era.mi.logic.components.gates.NotGate;
+import era.mi.logic.components.gates.OrGate;
+import era.mi.logic.components.gates.XorGate;
+import era.mi.logic.types.Bit;
+import era.mi.logic.types.BitVector;
+import era.mi.logic.wires.Wire;
+import era.mi.logic.wires.Wire.WireEnd;
+
+@SuppressWarnings("unused")
+class ComponentTest
+{
+
+       @Test
+       void circuitExampleTest()
+       {
+               Simulation.TIMELINE.reset();
+               Wire a = new Wire(1, 1), b = new Wire(1, 1), c = new Wire(1, 10), d = new Wire(2, 1), e = new Wire(1, 1), f = new Wire(1, 1),
+                               g = new Wire(1, 1), h = new Wire(2, 1), i = new Wire(2, 1), j = new Wire(1, 1), k = new Wire(1, 1);
+               new AndGate(1, f.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());
+               new NotGate(1, f.createReadOnlyEnd(), g.createEnd());
+               new Merger(h.createEnd(), c.createReadOnlyEnd(), g.createReadOnlyEnd());
+               new Mux(1, i.createEnd(), e.createReadOnlyEnd(), h.createReadOnlyEnd(), d.createReadOnlyEnd());
+               new Splitter(i.createReadOnlyEnd(), k.createEnd(), j.createEnd());
+
+               a.createEnd().feedSignals(Bit.ZERO);
+               b.createEnd().feedSignals(Bit.ONE);
+               c.createEnd().feedSignals(Bit.ZERO);
+               d.createEnd().feedSignals(Bit.ONE, Bit.ONE);
+               e.createEnd().feedSignals(Bit.ZERO);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertEquals(Bit.ONE, j.getValue());
+               assertEquals(Bit.ZERO, k.getValue());
+       }
+
+       @Test
+       void splitterTest()
+       {
+               Simulation.TIMELINE.reset();
+               Wire a = new Wire(3, 1), b = new Wire(2, 1), c = new Wire(3, 1), in = new Wire(8, 1);
+               in.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+               new Splitter(in.createReadOnlyEnd(), a.createEnd(), b.createEnd(), c.createEnd());
+
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(a.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO);
+               assertBitArrayEquals(b.getValues(), Bit.ONE, Bit.ZERO);
+               assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE);
+       }
+
+       @Test
+       void mergerTest()
+       {
+               Simulation.TIMELINE.reset();
+               Wire a = new Wire(3, 1), b = new Wire(2, 1), c = new Wire(3, 1), out = new Wire(8, 1);
+               a.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO);
+               b.createEnd().feedSignals(Bit.ONE, Bit.ZERO);
+               c.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE);
+
+               new Merger(out.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd());
+
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(out.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+       }
+
+       @Test
+       void triStateBufferTest()
+       {
+               Wire a = new Wire(1, 1), b = new Wire(1, 1), en = new Wire(1, 1), notEn = new Wire(1, 1);
+               new NotGate(1, en.createReadOnlyEnd(), notEn.createEnd());
+               new TriStateBuffer(1, a.createReadOnlyEnd(), b.createEnd(), en.createReadOnlyEnd());
+               new TriStateBuffer(1, b.createReadOnlyEnd(), a.createEnd(), notEn.createReadOnlyEnd());
+
+               WireEnd enI = en.createEnd(), aI = a.createEnd(), bI = b.createEnd();
+               enI.feedSignals(Bit.ONE);
+               aI.feedSignals(Bit.ONE);
+               bI.feedSignals(Bit.Z);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertEquals(Bit.ONE, b.getValue());
+
+               bI.feedSignals(Bit.ZERO);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertEquals(Bit.X, b.getValue());
+               assertEquals(Bit.ONE, a.getValue());
+
+               aI.clearSignals();
+               enI.feedSignals(Bit.ZERO);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertEquals(Bit.ZERO, a.getValue());
+
+       }
+
+       @Test
+       void muxTest()
+       {
+               Simulation.TIMELINE.reset();
+               Wire a = new Wire(4, 3), b = new Wire(4, 6), c = new Wire(4, 4), select = new Wire(2, 5), out = new Wire(4, 1);
+               WireEnd selectIn = select.createEnd();
+
+               selectIn.feedSignals(Bit.ZERO, Bit.ZERO);
+               a.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
+               c.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+
+               new Mux(1, out.createEnd(), select.createReadOnlyEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd());
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(out.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
+               selectIn.feedSignals(Bit.ZERO, Bit.ONE);
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(out.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+
+               selectIn.feedSignals(Bit.ONE, Bit.ONE);
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(out.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
+
+       }
+
+       @Test
+       void demuxTest()
+       {
+               Simulation.TIMELINE.reset();
+               Wire a = new Wire(4, 3), b = new Wire(4, 6), c = new Wire(4, 4), select = new Wire(2, 5), in = new Wire(4, 1);
+               WireEnd selectIn = select.createEnd();
+
+               selectIn.feedSignals(Bit.ZERO, Bit.ZERO);
+               in.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
+
+               new Demux(1, in.createReadOnlyEnd(), select.createReadOnlyEnd(), a.createEnd(), b.createEnd(), c.createEnd());
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(a.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
+               assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
+               assertBitArrayEquals(c.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
+               selectIn.feedSignals(Bit.ZERO, Bit.ONE);
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(a.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
+               assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
+               assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
+
+               selectIn.feedSignals(Bit.ONE, Bit.ONE);
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(a.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
+               assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
+               assertBitArrayEquals(c.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
+
+       }
+
+       @Test
+       void andTest()
+       {
+               Simulation.TIMELINE.reset();
+               Wire a = new Wire(4, 1), b = new Wire(4, 3), c = new Wire(4, 1);
+               new AndGate(1, c.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());
+               a.createEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO);
+               b.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(c.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ZERO);
+       }
+
+       @Test
+       void orTest()
+       {
+               Simulation.TIMELINE.reset();
+               Wire a = new Wire(4, 1), b = new Wire(4, 3), c = new Wire(4, 1);
+               new OrGate(1, c.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());
+               a.createEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO);
+               b.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ONE);
+       }
+
+       @Test
+       void xorTest()
+       {
+               Simulation.TIMELINE.reset();
+               Wire a = new Wire(3, 1), b = new Wire(3, 2), c = new Wire(3, 1), d = new Wire(3, 1);
+               new XorGate(1, d.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd());
+               a.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE);
+               b.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE);
+               c.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(d.getValues(), Bit.ZERO, Bit.ONE, Bit.ONE);
+       }
+
+       @Test
+       void notTest()
+       {
+               Simulation.TIMELINE.reset();
+               Wire a = new Wire(3, 1), b = new Wire(3, 2);
+               new NotGate(1, a.createReadOnlyEnd(), b.createEnd());
+               a.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertBitArrayEquals(b.getValues(), Bit.ONE, Bit.ZERO, Bit.ZERO);
+       }
+
+       @Test
+       void rsLatchCircuitTest()
+       {
+               Simulation.TIMELINE.reset();
+               Wire r = new Wire(1, 1), s = new Wire(1, 1), t1 = new Wire(1, 15), t2 = new Wire(1, 1), q = new Wire(1, 1), nq = new Wire(1, 1);
+
+               new OrGate(1, t2.createEnd(), r.createReadOnlyEnd(), nq.createReadOnlyEnd());
+               new OrGate(1, t1.createEnd(), s.createReadOnlyEnd(), q.createReadOnlyEnd());
+               new NotGate(1, t2.createReadOnlyEnd(), q.createEnd());
+               new NotGate(1, t1.createReadOnlyEnd(), nq.createEnd());
+
+               WireEnd sIn = s.createEnd(), rIn = r.createEnd();
+
+               sIn.feedSignals(Bit.ONE);
+               rIn.feedSignals(Bit.ZERO);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertEquals(Bit.ONE, q.getValue());
+               assertEquals(Bit.ZERO, nq.getValue());
+
+               sIn.feedSignals(Bit.ZERO);
+
+               Simulation.TIMELINE.executeAll();
+               assertEquals(Bit.ONE, q.getValue());
+               assertEquals(Bit.ZERO, nq.getValue());
+
+               rIn.feedSignals(Bit.ONE);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertEquals(Bit.ZERO, q.getValue());
+               assertEquals(Bit.ONE, nq.getValue());
+       }
+
+       @Test
+       void numericValueTest()
+       {
+               Simulation.TIMELINE.reset();
+
+               Wire a = new Wire(4, 1);
+               a.createEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ONE, Bit.ONE);
+
+               Simulation.TIMELINE.executeAll();
+
+               assertEquals(15, a.getUnsignedValue());
+               assertEquals(-1, a.getSignedValue());
+       }
+
+       @Test
+       void multipleInputs()
+       {
+               Simulation.TIMELINE.reset();
+               Wire w = new Wire(2, 1);
+               WireEnd wI1 = w.createEnd(), wI2 = w.createEnd();
+               wI1.feedSignals(Bit.ONE, Bit.Z);
+               wI2.feedSignals(Bit.Z, Bit.X);
+               Simulation.TIMELINE.executeAll();
+               assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.X);
+
+               wI2.feedSignals(Bit.ZERO, Bit.Z);
+               Simulation.TIMELINE.executeAll();
+               assertBitArrayEquals(w.getValues(), Bit.X, Bit.Z);
+
+               wI2.feedSignals(Bit.Z, Bit.Z);
+               Simulation.TIMELINE.executeAll();
+               assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.Z);
+
+               wI2.feedSignals(Bit.ONE, Bit.Z);
+               w.addObserver((i, oldValues) -> fail("WireArray notified observer, although value did not change."));
+               Simulation.TIMELINE.executeAll();
+               assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.Z);
+       }
+
+       @Test
+       void wireConnections()
+       {
+               // Nur ein Experiment, was über mehrere 'passive' Bausteine hinweg passieren würde
+
+               Simulation.TIMELINE.reset();
+
+               Wire a = new Wire(1, 2);
+               Wire b = new Wire(1, 2);
+               Wire c = new Wire(1, 2);
+               WireEnd aI = a.createEnd();
+               WireEnd bI = b.createEnd();
+               WireEnd cI = c.createEnd();
+
+               TestBitDisplay test = new TestBitDisplay(c.createReadOnlyEnd());
+               TestBitDisplay test2 = new TestBitDisplay(a.createReadOnlyEnd());
+               LongConsumer print = time -> System.out.format("Time %2d\n   a: %s\n   b: %s\n   c: %s\n", time, a, b, c);
+
+               cI.feedSignals(Bit.ONE);
+               test.assertAfterSimulationIs(print, Bit.ONE);
+
+               cI.feedSignals(Bit.X);
+               test.assertAfterSimulationIs(print, Bit.X);
+
+               cI.feedSignals(Bit.X);
+               cI.feedSignals(Bit.Z);
+               test.assertAfterSimulationIs(print, Bit.Z);
+
+               new Connector(b.createEnd(), c.createEnd()).connect();
+               test.assertAfterSimulationIs(print, Bit.Z);
+               System.err.println("ONE");
+               bI.feedSignals(Bit.ONE);
+               test.assertAfterSimulationIs(print, Bit.ONE);
+               System.err.println("ZERO");
+               bI.feedSignals(Bit.ZERO);
+               test.assertAfterSimulationIs(print, Bit.ZERO);
+               System.err.println("Z");
+               bI.feedSignals(Bit.Z);
+               test.assertAfterSimulationIs(print, Bit.Z);
+
+               new Connector(a.createEnd(), b.createEnd()).connect();
+               System.err.println("Z 2");
+               aI.feedSignals(Bit.Z);
+               test.assertAfterSimulationIs(print, Bit.Z);
+               test2.assertAfterSimulationIs(Bit.Z);
+               System.err.println("ONE 2");
+               aI.feedSignals(Bit.ONE);
+               test.assertAfterSimulationIs(print, Bit.ONE);
+               test2.assertAfterSimulationIs(Bit.ONE);
+               System.err.println("ZERO 2");
+               aI.feedSignals(Bit.ZERO);
+               test.assertAfterSimulationIs(print, Bit.ZERO);
+               test2.assertAfterSimulationIs(Bit.ZERO);
+               System.err.println("Z 2 II");
+               aI.feedSignals(Bit.Z);
+               test.assertAfterSimulationIs(print, Bit.Z);
+               test2.assertAfterSimulationIs(Bit.Z);
+
+               System.err.println("No Conflict yet");
+               bI.feedSignals(Bit.ONE);
+               test.assertAfterSimulationIs(print, Bit.ONE);
+               test2.assertAfterSimulationIs(Bit.ONE);
+               aI.feedSignals(Bit.ONE);
+               test.assertAfterSimulationIs(print, Bit.ONE);
+               test2.assertAfterSimulationIs(Bit.ONE);
+               System.err.println("Conflict");
+               aI.feedSignals(Bit.ZERO);
+               test.assertAfterSimulationIs(print, Bit.X);
+               test2.assertAfterSimulationIs(Bit.X);
+               aI.feedSignals(Bit.ONE);
+               test.assertAfterSimulationIs(print, Bit.ONE);
+               test2.assertAfterSimulationIs(Bit.ONE);
+       }
+
+       private static void assertBitArrayEquals(BitVector actual, Bit... expected)
+       {
+               assertArrayEquals(expected, actual.getBits());
+       }
+}
diff --git a/era.mi/src/era/mi/logic/tests/GUITest.java b/era.mi/src/era/mi/logic/tests/GUITest.java
new file mode 100644 (file)
index 0000000..b93d4b6
--- /dev/null
@@ -0,0 +1,294 @@
+package era.mi.logic.tests;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.WindowConstants;
+
+import era.mi.logic.Simulation;
+import era.mi.logic.components.ManualSwitch;
+import era.mi.logic.components.gates.NotGate;
+import era.mi.logic.components.gates.OrGate;
+import era.mi.logic.timeline.Timeline.ExecutionResult;
+import era.mi.logic.wires.Wire;
+
+public class GUITest extends JPanel
+{
+
+       private static final long serialVersionUID = 1L;
+
+       private static final int WIRE_DELAY = 40;
+       private static final int OR_DELAY = 100;
+       private static final int NOT_DELAY = 100;
+
+       Wire r = new Wire(1, WIRE_DELAY);
+       Wire s = new Wire(1, WIRE_DELAY);
+       Wire t1 = new Wire(1, WIRE_DELAY);
+       Wire t2 = new Wire(1, WIRE_DELAY);
+       Wire q = new Wire(1, WIRE_DELAY);
+       Wire nq = new Wire(1, WIRE_DELAY);
+
+       ManualSwitch rIn = new ManualSwitch(r.createEnd());
+       ManualSwitch sIn = new ManualSwitch(s.createEnd());
+
+       OrGate or1 = new OrGate(OR_DELAY, t2.createEnd(), r.createEnd(), nq.createEnd());
+       OrGate or2 = new OrGate(OR_DELAY, t1.createEnd(), s.createEnd(), q.createEnd());
+       NotGate not1 = new NotGate(NOT_DELAY, t2.createEnd(), q.createEnd());
+       NotGate not2 = new NotGate(NOT_DELAY, t1.createEnd(), nq.createEnd());
+
+       Map<ManualSwitch, Rectangle> switchMap = new HashMap<>();
+
+       int height;
+       int width;
+       boolean sizeChanged;
+
+       public GUITest()
+       {
+               addMouseListener(new MouseListener()
+               {
+
+                       @Override
+                       public void mouseReleased(MouseEvent e)
+                       {
+                               for (Entry<ManualSwitch, Rectangle> dim : switchMap.entrySet())
+                               {
+                                       if (dim.getValue().contains(e.getPoint()))
+                                       {
+                                               dim.getKey().switchOff();
+                                               repaint();
+                                       }
+                               }
+                       }
+
+                       @Override
+                       public void mousePressed(MouseEvent e)
+                       {
+                               for (Entry<ManualSwitch, Rectangle> dim : switchMap.entrySet())
+                               {
+                                       if (dim.getValue().contains(e.getPoint()))
+                                       {
+                                               dim.getKey().switchOn();
+                                               repaint();
+                                       }
+                               }
+                       }
+
+                       @Override
+                       public void mouseExited(MouseEvent e)
+                       {
+                               // none
+                       }
+
+                       @Override
+                       public void mouseEntered(MouseEvent e)
+                       {
+                               // none
+                       }
+
+                       @Override
+                       public void mouseClicked(MouseEvent e)
+                       {
+                               // If you want toggle buttons, use this code instead
+//                             for (Entry<ManualSwitch, Rectangle> dim : switchMap.entrySet()) {
+//                                     if (dim.getValue().contains(e.getPoint())) {
+//                                             dim.getKey().toggle();
+//                                             repaint();
+//                                     }
+//                             }
+                       }
+               });
+       }
+
+       @Override
+       public void paint(Graphics some_g)
+       {
+               super.paint(some_g);
+               Graphics2D g = ((Graphics2D) some_g);
+               g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
+               g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+               g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
+
+               checkSizeChange();
+               adaptFont(g);
+
+               drawWire(g, r, "r", 2, 9, 4, 9);
+
+               drawWire(g, s, "s", 2, 3, 4, 3);
+
+               drawWire(g, t2, "t2", 5, 8.5, 6, 8.5);
+
+               drawWire(g, t1, "t1", 5, 3.5, 6, 3.5);
+
+               drawWire(g, q, "q", 7, 8.5, 9, 8.5);
+
+               drawWire(g, nq, "nq", 7, 3.5, 9, 3.5);
+
+               drawWire(g, q, "", 7.5, 8.5, 7.5, 7.5);
+               drawWire(g, q, "", 7.5, 7.5, 3, 4.5);
+               drawWire(g, q, "", 3, 4.5, 3, 4);
+               drawWire(g, q, "q", 3, 4, 4, 4);
+
+               drawWire(g, nq, "", 7.5, 3.5, 7.5, 4.5);
+               drawWire(g, nq, "", 7.5, 4.5, 3, 7.5);
+               drawWire(g, nq, "", 3, 7.5, 3, 8);
+               drawWire(g, nq, "nq", 3, 8, 4, 8);
+
+               drawSquare(g, 4, 8, "OR");
+               drawSquare(g, 4, 3, "OR");
+
+               drawSquare(g, 6, 8, "NOT");
+               drawSquare(g, 6, 3, "NOT");
+
+               drawSwitch(g, rIn, "Switch R", 0.5, 8.25, 2, 9.75);
+               drawSwitch(g, sIn, "Switch S", 0.5, 2.25, 2, 3.75);
+
+               drawString(g, "Hint: drag the cursor out of the pressed switch to keep it's state", 5, 0, 0.0, 1.0);
+       }
+
+       private void checkSizeChange()
+       {
+               sizeChanged = height != getHeight() || width != getWidth();
+               if (sizeChanged)
+               {
+                       height = getHeight();
+                       width = getWidth();
+               }
+       }
+
+       private void adaptFont(Graphics g)
+       {
+               g.setFont(g.getFont().deriveFont(Math.min(height, width) / 40f));
+       }
+
+       private void drawString(Graphics g, String s, int x, int y, double anchorX, double anchorY)
+       {
+               int h = g.getFontMetrics().getAscent();
+               int w = g.getFontMetrics().stringWidth(s);
+               g.drawString(s, x - (int) (w * anchorX), y + (int) (h * anchorY));
+       }
+
+       private void drawWire(Graphics g, Wire wa, String name, double x1, double y1, double x2, double y2)
+       {
+               setTo(g, wa);
+               g.drawLine(gX(x1), gY(y1), gX(x2), gY(y2));
+               drawString(g, name, (gX(x1) + gX(x2)) / 2, (gY(y1) + gY(y2)) / 2 - 5, 0, 0);
+       }
+
+       private void drawSquare(Graphics g, int posX, int posY, String text)
+       {
+               int x1 = gX(posX) - 5;
+               int x2 = gX(posX + 1) + 5;
+               int y1 = gY(posY) - 5;
+               int y2 = gY(posY + 1) + 5;
+
+               g.setColor(Color.WHITE);
+               g.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+               setBlack(g);
+               g.drawRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+               drawString(g, text, (x1 + x2) / 2, (y1 + y2) / 2, 0.5, 0.5);
+
+       }
+
+       private void drawSwitch(Graphics g, ManualSwitch ms, String text, double posX1, double posY1, double posX2, double posY2)
+       {
+               int x1 = gX(posX1) - 5;
+               int x2 = gX(posX2) + 5;
+               int y1 = gY(posY1) - 5;
+               int y2 = gY(posY2) + 5;
+
+               if (sizeChanged)
+               {
+                       Rectangle r = new Rectangle(x1, y1, x2 - x1, y2 - y1);
+                       switchMap.put(ms, r);
+               }
+
+               g.setColor(ms.isOn() ? Color.getHSBColor(.3f, .5f, 1f) : Color.WHITE);
+               g.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+               setBlack(g);
+               g.drawRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+               drawString(g, text, (x1 + x2) / 2, (y1 + y2) / 2, 0.5, 0.5);
+       }
+
+       private static void setBlack(Graphics g)
+       {
+               g.setColor(Color.BLACK);
+       }
+
+       private static void setTo(Graphics g, Wire wa)
+       {
+               switch (wa.getValue())
+               {
+               case ONE:
+                       g.setColor(Color.GREEN);
+                       break;
+               case X:
+                       g.setColor(Color.RED);
+                       break;
+               case Z:
+                       g.setColor(Color.DARK_GRAY);
+                       break;
+               case ZERO:
+                       g.setColor(Color.BLACK);
+                       break;
+               case U:
+                       g.setColor(Color.MAGENTA);
+                       break;
+               default:
+                       throw new IllegalArgumentException();
+               }
+       }
+
+       private int gY(double pos)
+       {
+               return (int) (pos * height / 11);
+       }
+
+       private int gX(double pos)
+       {
+               return (int) (pos * width / 11) + 50;
+       }
+
+       public static void main(String[] args)
+       {
+               JFrame f = new JFrame("Test circuit 1.0.0");
+               GUITest gt = new GUITest();
+               f.add(gt);
+               f.setSize(800, 600);
+               f.setLocation(500, 400);
+               f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+               f.setVisible(true);
+
+               long begin = System.currentTimeMillis();
+
+               long lastFrame = begin;
+               long updateT = 16;
+
+               while (f.isVisible())
+               {
+                       ExecutionResult er = Simulation.TIMELINE.executeUpTo((lastFrame - begin) * 3, lastFrame + 14);
+//                             if (Simulation.TIMELINE.hasNext()) 
+//                             Simulation.TIMELINE.executeNext();
+                       if (er != ExecutionResult.NOTHING_DONE)
+                               gt.repaint(12);
+                       try
+                       {
+                               Thread.sleep(Math.max(updateT - System.currentTimeMillis() + lastFrame, 0));
+                       }
+                       catch (Exception e)
+                       {
+                               e.printStackTrace();
+                       }
+                       lastFrame = System.currentTimeMillis();
+               }
+       }
+}
diff --git a/era.mi/src/era/mi/logic/tests/TestBitDisplay.java b/era.mi/src/era/mi/logic/tests/TestBitDisplay.java
new file mode 100644 (file)
index 0000000..cb0494e
--- /dev/null
@@ -0,0 +1,47 @@
+package era.mi.logic.tests;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+import java.util.function.LongConsumer;
+
+import era.mi.logic.Simulation;
+import era.mi.logic.components.BitDisplay;
+import era.mi.logic.types.Bit;
+import era.mi.logic.wires.Wire.WireEnd;
+
+public final class TestBitDisplay extends BitDisplay
+{
+
+       public TestBitDisplay(WireEnd in)
+       {
+               super(in);
+       }
+
+       public void assertDisplays(Bit... expected)
+       {
+               assertArrayEquals(expected, getDisplayedValue().getBits());
+       }
+
+       public void assertAfterSimulationIs(Bit... expected)
+       {
+               Simulation.TIMELINE.executeAll();
+               assertDisplays(expected);
+       }
+
+       public void assertAfterSimulationIs(LongConsumer r, Bit... expected)
+       {
+               while (Simulation.TIMELINE.hasNext())
+               {
+                       Simulation.TIMELINE.executeNext();
+                       r.accept(Simulation.TIMELINE.getSimulationTime());
+               }
+               assertDisplays(expected);
+       }
+
+       @Override
+       protected void compute()
+       {
+               super.compute();
+               System.out.println("update: value is " + getDisplayedValue());
+       }
+}
diff --git a/era.mi/src/era/mi/logic/timeline/Timeline.java b/era.mi/src/era/mi/logic/timeline/Timeline.java
new file mode 100644 (file)
index 0000000..8107db9
--- /dev/null
@@ -0,0 +1,178 @@
+package era.mi.logic.timeline;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.function.Consumer;
+
+/**
+ * Orders Events by the time they are due to be executed. Can execute Events individually.
+ * 
+ * @author Fabian Stemmler
+ *
+ */
+public class Timeline
+{
+       private PriorityQueue<InnerEvent> events;
+       private long currentTime = 0;
+
+       private final List<Consumer<TimelineEvent>> eventAddedListener;
+
+       public Timeline(int initCapacity)
+       {
+               events = new PriorityQueue<InnerEvent>(initCapacity, (a, b) ->
+               {
+                       long difference = a.getTiming() - b.getTiming();
+                       if (difference == 0)
+                               return 0;
+                       return difference < 0 ? -1 : 1;
+               });
+
+               eventAddedListener = new ArrayList<>();
+       }
+
+       public boolean hasNext()
+       {
+               return !events.isEmpty();
+       }
+
+       public void executeNext()
+       {
+               InnerEvent first = events.poll();
+               currentTime = first.getTiming();
+               first.run();
+       }
+
+       public void executeAll()
+       {
+               while (hasNext())
+                       executeNext();
+       }
+
+       /**
+        * Executes all events up to a given simulation timestamp. The simulation process can be constrained by a real world timestamp.
+        * 
+        * @param timestamp  the simulation timestamp up to which the events will be processed
+        * @param stopMillis the System.currentTimeMillis() when simulation definitely needs to stop.
+        * @return if it was possible to fulfil the goal in the given real world time.
+        * @author Christian Femers
+        */
+       public ExecutionResult executeUpTo(long timestamp, long stopMillis)
+       {
+               if (events.isEmpty())
+               {
+                       currentTime = timestamp;
+                       return ExecutionResult.NOTHING_DONE;
+               }
+               int checkStop = 0;
+               InnerEvent first = events.peek();
+               while (first != null && first.getTiming() <= timestamp)
+               {
+                       events.remove();
+                       currentTime = first.getTiming();
+                       first.run();
+                       // Don't check after every run
+                       checkStop = (checkStop + 1) % 10;
+                       if (checkStop == 0 && System.currentTimeMillis() >= stopMillis)
+                               return ExecutionResult.RAN_OUT_OF_TIME;
+                       first = events.peek();
+               }
+               currentTime = timestamp;
+               return ExecutionResult.DONE_IN_TIME;
+       }
+
+       public long getSimulationTime()
+       {
+               return currentTime;
+       }
+
+       public long nextEventTime()
+       {
+               if (!hasNext())
+                       return -1;
+               return events.peek().timing;
+       }
+
+       public void reset()
+       {
+               events.clear();
+               currentTime = 0;
+       }
+
+       public void addEventAddedListener(Consumer<TimelineEvent> listener)
+       {
+               eventAddedListener.add(listener);
+       }
+
+       public void removeEventAddedListener(Consumer<TimelineEvent> listener)
+       {
+               eventAddedListener.remove(listener);
+       }
+
+       /**
+        * Adds an Event to the {@link Timeline}
+        * 
+        * @param function       The {@link TimelineEventHandler} that will be executed, when the {@link InnerEvent} occurs on the timeline.
+        * @param relativeTiming The amount of MI ticks in which the {@link InnerEvent} is called, starting from the current time.
+        */
+       public void addEvent(TimelineEventHandler function, int relativeTiming)
+       {
+               long timing = currentTime + relativeTiming;
+               TimelineEvent event = new TimelineEvent(timing);
+               events.add(new InnerEvent(function, event, timing));
+               eventAddedListener.forEach(l -> l.accept(event));
+       }
+
+       private class InnerEvent
+       {
+
+               final long timing;
+               private final TimelineEventHandler function;
+               private final TimelineEvent event;
+
+               /**
+                * Creates an {@link InnerEvent}
+                * 
+                * @param function {@link TimelineEventHandler} to be executed when the {@link InnerEvent} occurs
+                * @param timing   Point in the MI simulation {@link Timeline}, at which the {@link InnerEvent} is executed;
+                */
+               InnerEvent(TimelineEventHandler function, TimelineEvent event, long timing)
+               {
+                       this.function = function;
+                       this.event = event;
+                       this.timing = timing;
+               }
+
+               public long getTiming()
+               {
+                       return timing;
+               }
+
+               public void run()
+               {
+                       function.handle(event);
+               }
+
+               @Override
+               public String toString()
+               {
+                       return event.toString();
+               }
+       }
+
+       @Override
+       public String toString()
+       {
+               return "simulation time: " + currentTime + ", " + events.toString();
+       }
+
+       public static long toNanoseconds(long ticks)
+       {
+               return ticks; // TODO: Alter this when it has been determined how ticks should relate to real time.
+       }
+
+       public enum ExecutionResult
+       {
+               NOTHING_DONE, DONE_IN_TIME, RAN_OUT_OF_TIME
+       }
+}
\ No newline at end of file
diff --git a/era.mi/src/era/mi/logic/timeline/TimelineEvent.java b/era.mi/src/era/mi/logic/timeline/TimelineEvent.java
new file mode 100644 (file)
index 0000000..46decf5
--- /dev/null
@@ -0,0 +1,30 @@
+package era.mi.logic.timeline;
+
+/**
+ * A class that stores all relevant information about an event in the {@link Timeline}. Currently, there is not much relevant information to
+ * store.
+ * 
+ * @author Fabian Stemmler
+ *
+ */
+public class TimelineEvent
+{
+       private final long timing;
+
+       TimelineEvent(long timing)
+       {
+               super();
+               this.timing = timing;
+       }
+
+       public long getTiming()
+       {
+               return timing;
+       }
+
+       @Override
+       public String toString()
+       {
+               return "timestamp: " + timing;
+       }
+}
\ No newline at end of file
diff --git a/era.mi/src/era/mi/logic/timeline/TimelineEventHandler.java b/era.mi/src/era/mi/logic/timeline/TimelineEventHandler.java
new file mode 100644 (file)
index 0000000..59a91c9
--- /dev/null
@@ -0,0 +1,6 @@
+package era.mi.logic.timeline;
+
+public interface TimelineEventHandler
+{
+       public void handle(TimelineEvent e);
+}
\ No newline at end of file
diff --git a/era.mi/src/era/mi/logic/types/Bit.java b/era.mi/src/era/mi/logic/types/Bit.java
new file mode 100644 (file)
index 0000000..6674f9a
--- /dev/null
@@ -0,0 +1,132 @@
+package era.mi.logic.types;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * stdlogic according to IEEE 1164
+ */
+public enum Bit implements StrictLogicType<Bit>
+{
+       U("U"), X("X"), ZERO("0"), ONE("1"), Z("Z");
+
+       private final String symbol;
+
+       private Bit(String symbol)
+       {
+               this.symbol = symbol;
+       }
+
+       @Override
+       public Bit and(Bit other)
+       {
+               return fromTable(AND_TABLE, this, other);
+       }
+
+       @Override
+       public Bit or(Bit other)
+       {
+               return fromTable(OR_TABLE, this, other);
+       }
+
+       @Override
+       public Bit xor(Bit other)
+       {
+               return fromTable(XOR_TABLE, this, other);
+       }
+
+       @Override
+       public Bit not()
+       {
+               switch (this)
+               {
+               case U:
+                       return U;
+               case ONE:
+                       return ZERO;
+               case ZERO:
+                       return ONE;
+               default:
+                       return X;
+               }
+       }
+
+       public Bit[] makeArray(int length)
+       {
+               Bit[] bits = new Bit[length];
+               Arrays.fill(bits, this);
+               return bits;
+       }
+
+       public BitVector toVector(int length)
+       {
+               return BitVector.of(this, length);
+       }
+
+       @Override
+       public Bit join(Bit other)
+       {
+               return fromTable(JOIN_TABLE, this, other);
+       }
+
+       @Override
+       public String toString()
+       {
+               return getSymbol();
+       }
+
+       public String getSymbol()
+       {
+               return symbol;
+       }
+
+       public static Bit parse(String s)
+       {
+               Bit bit = SYMBOL_MAP.get(s);
+               Objects.requireNonNull(bit, "No Bit found for symbol " + s);
+               return bit;
+       }
+
+       public static Bit parse(String s, int symbolPosition)
+       {
+               return parse(s.substring(symbolPosition, symbolPosition + 1));
+       }
+
+       private static Bit fromTable(Bit[][] table, Bit a, Bit b)
+       {
+               return table[a.ordinal()][b.ordinal()];
+       }
+
+       static final Map<String, Bit> SYMBOL_MAP = Map.of(U.symbol, U, X.symbol, X, ZERO.symbol, ZERO, ONE.symbol, ONE, Z.symbol, Z);
+
+       // @formatter:off
+       private static final Bit[][] JOIN_TABLE = 
+               { { U, U, U,    U,   U    }, 
+                 { U, X, X,    X,   X    }, 
+                 { U, X, ZERO, X,   ZERO },
+                 { U, X, X,    ONE, ONE  }, 
+                 { U, X, ZERO, ONE, Z    } };
+
+       private static final Bit[][] AND_TABLE = 
+               { { U,    U,    ZERO, U,    U    }, 
+                 { U,    X,    ZERO, X,    X    },
+                 { ZERO, ZERO, ZERO, ZERO, ZERO }, 
+                 { U,    X,    ZERO, ONE,  X    }, 
+                 { U,    X,    ZERO, X,    X    } };
+
+       private static final Bit[][] OR_TABLE =
+       { { U,   U,   U,    ONE, U    },    
+         { U,   X,   X,    ONE, X    },    
+         { U,   X,   ZERO, ONE, X    },    
+         { ONE, ONE, ONE,  ONE, ONE  },    
+         { U,   X,   X,    ONE, X    } };
+       
+       private static final Bit[][] XOR_TABLE =
+       { { U, U, U,    U,    U },    
+         { U, X, X,    X,    X },    
+         { U, X, ZERO, ONE,  X },    
+         { U, X, ONE,  ZERO, X },    
+         { U, X, X,    X,    X } }; 
+       // @formatter:on
+}
\ No newline at end of file
diff --git a/era.mi/src/era/mi/logic/types/BitVector.java b/era.mi/src/era/mi/logic/types/BitVector.java
new file mode 100644 (file)
index 0000000..38d7d26
--- /dev/null
@@ -0,0 +1,316 @@
+package era.mi.logic.types;
+
+import static java.lang.String.format;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.RandomAccess;
+import java.util.function.BinaryOperator;
+import java.util.function.UnaryOperator;
+
+/**
+ * Immutable class representing a {@link Bit}Vector
+ *
+ *
+ * @author Christian Femers
+ *
+ */
+public final class BitVector implements StrictLogicType<BitVector>, Iterable<Bit>, RandomAccess
+{
+       private final Bit[] bits;
+
+       private BitVector(Bit[] bits)
+       {
+               this.bits = Objects.requireNonNull(bits);
+       }
+
+       public static BitVector of(Bit... bits)
+       {
+               return new BitVector(bits.clone());
+       }
+
+       public static BitVector of(Bit bit, int length)
+       {
+               return new BitVector(bit.makeArray(length));
+       }
+
+       public BitVectorMutator mutator()
+       {
+               return BitVectorMutator.of(this);
+       }
+
+       public Bit getBit(int bitIndex)
+       {
+               return bits[bitIndex];
+       }
+
+       public Bit[] getBits()
+       {
+               return bits.clone();
+       }
+
+       @Override
+       public BitVector join(BitVector t)
+       {
+               checkCompatibility(t);
+               return new BitVector(binOp(bits.clone(), t.bits, Bit::join));
+       }
+
+       @Override
+       public BitVector and(BitVector t)
+       {
+               checkCompatibility(t);
+               return new BitVector(binOp(bits.clone(), t.bits, Bit::and));
+       }
+
+       @Override
+       public BitVector or(BitVector t)
+       {
+               checkCompatibility(t);
+               return new BitVector(binOp(bits.clone(), t.bits, Bit::or));
+       }
+
+       @Override
+       public BitVector xor(BitVector t)
+       {
+               checkCompatibility(t);
+               return new BitVector(binOp(bits.clone(), t.bits, Bit::xor));
+       }
+
+       @Override
+       public BitVector not()
+       {
+               return new BitVector(unOp(bits.clone(), Bit::not));
+       }
+
+       public int length()
+       {
+               return bits.length;
+       }
+
+       public BitVector concat(BitVector other)
+       {
+               Bit[] newBits = Arrays.copyOf(bits, length() + other.length());
+               System.arraycopy(other.bits, 0, newBits, length(), other.length());
+               return new BitVector(newBits);
+       }
+
+       public BitVector subVector(int start)
+       {
+               return new BitVector(Arrays.copyOfRange(bits, start, length()));
+       }
+
+       public BitVector subVector(int start, int end)
+       {
+               return new BitVector(Arrays.copyOfRange(bits, start, end));
+       }
+
+       private void checkCompatibility(BitVector bv)
+       {
+               if (length() != bv.length())
+                       throw new IllegalArgumentException(format("BitVector length does not match: %d and %d", length(), bv.length()));
+       }
+
+       static Bit[] binOp(Bit[] dest, Bit[] second, BinaryOperator<Bit> op)
+       {
+               if (dest == null)
+                       return second.clone();
+               for (int i = 0; i < dest.length; i++)
+               {
+                       dest[i] = op.apply(dest[i], second[i]);
+               }
+               return dest;
+       }
+
+       static Bit[] unOp(Bit[] dest, UnaryOperator<Bit> op)
+       {
+               if (dest == null)
+                       return null;
+               for (int i = 0; i < dest.length; i++)
+               {
+                       dest[i] = op.apply(dest[i]);
+               }
+               return dest;
+       }
+
+       /**
+        * Class for comfortable and efficient manipulation of {@link BitVector}s, similar to {@link StringBuilder}
+        *
+        * @author Christian Femers
+        */
+       @SuppressWarnings("synthetic-access")
+       public static final class BitVectorMutator implements LogicType<BitVectorMutator, BitVector>
+       {
+               private Bit[] bits;
+
+               private BitVectorMutator(Bit[] bits)
+               {
+                       this.bits = bits;
+               }
+
+               static BitVectorMutator of(BitVector bv)
+               {
+                       return new BitVectorMutator(bv.getBits());
+               }
+
+               /**
+                * Returns an empty mutator which has no bits set and will simply copy the values from the first binary operation performed.
+                * 
+                */
+               public static BitVectorMutator empty()
+               {
+                       return new BitVectorMutator(null);
+               }
+
+               /**
+                * Produces the resulting, immutable {@link BitVector}<br>
+                * 
+                * @throws IllegalStateException if the mutator is (still) empty
+                */
+               public BitVector get()
+               {
+                       if (bits == null)
+                               throw new IllegalStateException("cannot create a BitVector from an empty mutator");
+                       return new BitVector(bits);
+               }
+
+               @Override
+               public BitVectorMutator join(BitVector t)
+               {
+                       checkCompatibility(t);
+                       bits = binOp(bits, t.bits, Bit::join);
+                       return this;
+               }
+
+               @Override
+               public BitVectorMutator and(BitVector t)
+               {
+                       checkCompatibility(t);
+                       bits = binOp(bits, t.bits, Bit::and);
+                       return this;
+               }
+
+               @Override
+               public BitVectorMutator or(BitVector t)
+               {
+                       checkCompatibility(t);
+                       bits = binOp(bits, t.bits, Bit::or);
+                       return this;
+               }
+
+               @Override
+               public BitVectorMutator xor(BitVector t)
+               {
+                       checkCompatibility(t);
+                       bits = binOp(bits, t.bits, Bit::xor);
+                       return this;
+               }
+
+               @Override
+               public BitVectorMutator not()
+               {
+                       unOp(bits, Bit::not);
+                       return this;
+               }
+
+               private void checkCompatibility(BitVector bv)
+               {
+                       if (bits != null && bits.length != bv.length())
+                               throw new IllegalArgumentException(format("BitVector length does not match: %d and %d", bits.length, bv.length()));
+               }
+       }
+
+       /**
+        * @see Arrays#hashCode(Object[])
+        */
+       @Override
+       public int hashCode()
+       {
+               return Arrays.hashCode(bits);
+       }
+
+       /**
+        * Does test for equality of values/content
+        * 
+        * @see Object#equals(Object)
+        */
+       @Override
+       public boolean equals(Object obj)
+       {
+               if (this == obj)
+                       return true;
+               if (!(obj instanceof BitVector))
+                       return false;
+               BitVector other = (BitVector) obj;
+               return Arrays.equals(bits, other.bits);
+       }
+
+       /**
+        * Does test for equality of values/content, shifting the other BitVector by <code>offset</code> to the right.<br>
+        * Therefore <code>offset + other.length() <= this.length()</code> needs to be true.
+        * 
+        * @throws ArrayIndexOutOfBoundsException if <code>offset + other.length() > this.length()</code>
+        * 
+        * @see Object#equals(Object)
+        */
+       public boolean equalsWithOffset(BitVector other, int offset)
+       {
+               if (other == null)
+                       return false;
+               return Arrays.equals(bits, offset, offset + other.length(), other.bits, 0, other.length());
+       }
+
+       /**
+        * All {@link Bit}s symbols concatenated together
+        * 
+        * @see #parse(String)
+        */
+       @Override
+       public String toString()
+       {
+               StringBuilder sb = new StringBuilder(bits.length);
+               for (Bit bit : bits)
+                       sb.append(bit);
+               return sb.toString();
+       }
+
+       /**
+        * Parses a String containing solely {@link Bit} symbols
+        * 
+        * @see #toString()
+        */
+       public static BitVector parse(String s)
+       {
+               Bit[] values = new Bit[s.length()];
+               for (int i = 0; i < s.length(); i++)
+               {
+                       values[i] = Bit.parse(s, i);
+               }
+               return new BitVector(values);
+       }
+
+       @Override
+       public Iterator<Bit> iterator()
+       {
+               return new Iterator<>()
+               {
+                       private int pos = 0;
+
+                       @Override
+                       public Bit next()
+                       {
+                               if (!hasNext())
+                                       throw new NoSuchElementException();
+                               return getBit(pos++);
+                       }
+
+                       @Override
+                       public boolean hasNext()
+                       {
+                               return pos != length();
+                       }
+               };
+       }
+}
diff --git a/era.mi/src/era/mi/logic/types/LogicType.java b/era.mi/src/era/mi/logic/types/LogicType.java
new file mode 100644 (file)
index 0000000..9a3180e
--- /dev/null
@@ -0,0 +1,130 @@
+package era.mi.logic.types;
+
+/**
+ * Interface for types that support the basic logic operations
+ *
+ * @author Christian Femers
+ *
+ * @param <T> the logic type itself, to make the operations closed in T
+ * @param <S> the operand type, may be the same as T, see {@link StrictLogicType}
+ */
+public interface LogicType<T extends LogicType<T, S>, S>
+{
+       /**
+        * Determines the result when two signals meet each other directly, also called resolution (IEEE 1164)
+        * 
+        * For example:
+        * 
+        * <pre>
+        * 0 joined 0 == 0
+        * 1 joined 0 == X
+        * 0 joined 1 == X
+        * 1 joined 1 == 1
+        * </pre>
+        * 
+        * @param t the second logic signal
+        * @return the resulting value
+        * @author Christian Femers
+        */
+       T join(S t);
+
+       /**
+        * Classical logic AND
+        * 
+        * For example:
+        * 
+        * <pre>
+        * 0 AND 0 == 0
+        * 1 AND 0 == 0
+        * 0 AND 1 == 0
+        * 1 AND 1 == 1
+        * </pre>
+        * 
+        * @param t the second logic signal
+        * @return the resulting value
+        * @author Christian Femers
+        */
+       T and(S t);
+
+       /**
+        * Classical logic OR
+        *
+        * For example:
+        * 
+        * <pre>
+        * 0 OR 0 == 0
+        * 1 OR 0 == 1
+        * 0 OR 1 == 1
+        * 1 OR 1 == 1
+        * </pre>
+        * 
+        * @param t the second logic signal
+        * @return the resulting value
+        * @author Christian Femers
+        */
+       T or(S t);
+
+       /**
+        * Classical logic XOR (exclusive OR)
+        * 
+        * For example:
+        * 
+        * <pre>
+        * 0 XOR 0 == 0
+        * 1 XOR 0 == 1
+        * 0 XOR 1 == 1
+        * 1 XOR 1 == 0
+        * </pre>
+        * 
+        * @param t the second logic signal
+        * @return the resulting value
+        * @author Christian Femers
+        */
+       T xor(S t);
+
+       /**
+        * Classical logic NOT (logical negation)
+        * 
+        * For example:
+        * 
+        * <pre>
+        * NOT 0 == 1
+        * NOT 1 == 0
+        * </pre>
+        * 
+        * @return the resulting value
+        * @author Christian Femers
+        */
+       T not();
+
+       /**
+        * {@link #and(Object) AND} and then {@link #not() NOT}
+        * 
+        * @author Christian Femers
+        */
+       default T nand(S t)
+       {
+               return and(t).not();
+       }
+
+       /**
+        * {@link #or(Object) OR} and then {@link #not() NOT}
+        * 
+        * @author Christian Femers
+        */
+       default T nor(S t)
+       {
+               return or(t).not();
+       }
+
+       /**
+        * {@link #xor(Object) XOR} and then {@link #not() NOT}<br>
+        * Used to determine equality (alias parity, consistency)
+        * 
+        * @author Christian Femers
+        */
+       default T xnor(S t)
+       {
+               return xor(t).not();
+       }
+}
diff --git a/era.mi/src/era/mi/logic/types/MutationOperation.java b/era.mi/src/era/mi/logic/types/MutationOperation.java
new file mode 100644 (file)
index 0000000..d7a16fc
--- /dev/null
@@ -0,0 +1,11 @@
+package era.mi.logic.types;
+
+import java.util.function.BiFunction;
+
+import era.mi.logic.types.BitVector.BitVectorMutator;
+
+@FunctionalInterface
+public interface MutationOperation extends BiFunction<BitVectorMutator, BitVector, BitVectorMutator>
+{
+
+}
diff --git a/era.mi/src/era/mi/logic/types/StrictLogicType.java b/era.mi/src/era/mi/logic/types/StrictLogicType.java
new file mode 100644 (file)
index 0000000..560bb96
--- /dev/null
@@ -0,0 +1,14 @@
+package era.mi.logic.types;
+
+/**
+ * Interface for types that support the basic logic operations only among themselves, making it mathematically closed
+ * 
+ * @author Christian Femers
+ * @see LogicType
+ *
+ * @param <T> the logic type itself to make the operations closed
+ */
+public interface StrictLogicType<T extends StrictLogicType<T>> extends LogicType<T, T>
+{
+       // this is just a matter of type parameters
+}
diff --git a/era.mi/src/era/mi/logic/wires/Wire.java b/era.mi/src/era/mi/logic/wires/Wire.java
new file mode 100644 (file)
index 0000000..a7b96ee
--- /dev/null
@@ -0,0 +1,463 @@
+package era.mi.logic.wires;
+
+import static era.mi.logic.types.Bit.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import era.mi.logic.Simulation;
+import era.mi.logic.types.Bit;
+import era.mi.logic.types.BitVector;
+import era.mi.logic.types.BitVector.BitVectorMutator;
+
+/**
+ * Represents an array of wires that can store n bits of information.
+ * 
+ * @author Fabian Stemmler
+ *
+ */
+public class Wire
+{
+       private BitVector values;
+       public final int travelTime;
+       private List<WireObserver> observers = new ArrayList<WireObserver>();
+       public final int length;
+       private List<WireEnd> inputs = new ArrayList<WireEnd>();
+
+       public Wire(int length, int travelTime)
+       {
+               if (length < 1)
+                       throw new IllegalArgumentException(
+                                       String.format("Tried to create an array of wires with length %d, but a length of less than 1 makes no sense.", length));
+               this.length = length;
+               this.travelTime = travelTime;
+               initValues();
+       }
+
+       private void initValues()
+       {
+               values = U.toVector(length);
+       }
+
+       private void recalculateSingleInput()
+       {
+               setNewValues(inputs.get(0).getInputValues());
+       }
+
+       private void recalculateMultipleInputs()
+       {
+               BitVectorMutator mutator = BitVectorMutator.empty();
+               for (WireEnd wireArrayEnd : inputs)
+                       mutator.join(wireArrayEnd.getInputValues());
+               setNewValues(mutator.get());
+       }
+
+       private void setNewValues(BitVector newValues)
+       {
+               if (values.equals(newValues))
+                       return;
+               BitVector oldValues = values;
+               values = newValues;
+               notifyObservers(oldValues);
+       }
+
+       private void recalculate()
+       {
+               switch (inputs.size())
+               {
+               case 0:
+                       return;
+               case 1:
+                       recalculateSingleInput();
+                       break;
+               default:
+                       recalculateMultipleInputs();
+               }
+       }
+
+       /**
+        * The {@link Wire} is interpreted as an unsigned integer with n bits.
+        * 
+        * @return <code>true</code> if all bits are either <code>Bit.ONE</code> or <code>Bit.ZERO</code> (they do not all have to have the same
+        *         value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.
+        * 
+        * @author Fabian Stemmler
+        */
+       public boolean hasNumericValue()
+       {
+               for (Bit b : values)
+               {
+                       if (b != Bit.ZERO && b != Bit.ONE)
+                               return false;
+               }
+               return true;
+       }
+
+       /**
+        * The {@link Wire} is interpreted as an unsigned integer with n bits.
+        * 
+        * @return The unsigned value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
+        * 
+        * @author Fabian Stemmler
+        */
+       public long getUnsignedValue()
+       {
+               long val = 0;
+               long mask = 1;
+               for (Bit bit : values)
+               {
+                       switch (bit)
+                       {
+                       default:
+                       case Z:
+                       case X:
+                               return 0; // TODO: Proper handling for getUnsignedValue(), if not all bits are 1 or 0;
+                       // Random number?
+                       case ONE:
+                               val |= mask;
+                               break;
+                       case ZERO:
+                       }
+                       mask = mask << 1;
+               }
+               return val;
+       }
+
+       /**
+        * The {@link Wire} is interpreted as a signed integer with n bits.
+        * 
+        * @return The signed value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
+        * 
+        * @author Fabian Stemmler
+        */
+       public long getSignedValue()
+       {
+               long val = getUnsignedValue();
+               long mask = 1 << (length - 1);
+               if ((mask & val) != 0)
+               {
+                       int shifts = 64 - length;
+                       return (val << shifts) >> shifts;
+               }
+               return val;
+       }
+
+       public Bit getValue()
+       {
+               return getValue(0);
+       }
+
+       public Bit getValue(int index)
+       {
+               return values.getBit(index);
+       }
+
+       public BitVector getValues(int start, int end)
+       {
+               return values.subVector(start, end);
+       }
+
+       public BitVector getValues()
+       {
+               return values;
+       }
+
+       /**
+        * Adds an {@link WireObserver}, who will be notified when the value of the {@link Wire} is updated.
+        * 
+        * @param ob The {@link WireObserver} to be notified of changes.
+        * @return true if the given {@link WireObserver} was not already registered, false otherwise
+        * 
+        * @author Fabian Stemmler
+        */
+       public boolean addObserver(WireObserver ob)
+       {
+               return observers.add(ob);
+       }
+
+       private void notifyObservers(BitVector oldValues)
+       {
+               for (WireObserver o : observers)
+                       o.update(this, oldValues);
+       }
+
+       /**
+        * Create and register a {@link WireEnd} object, which is tied to this {@link Wire}.
+        */
+       public WireEnd createEnd()
+       {
+               return new WireEnd(false);
+       }
+
+       /**
+        * Create a {@link WireEnd} object, which is tied to this {@link Wire}. This {@link WireEnd} cannot written to.
+        */
+       public WireEnd createReadOnlyEnd()
+       {
+               return new WireEnd(true);
+       }
+
+       private void registerInput(WireEnd toRegister)
+       {
+               inputs.add(toRegister);
+       }
+
+       /**
+        * A {@link WireEnd} feeds a constant signal into the {@link Wire} it is tied to. The combination of all inputs determines the
+        * {@link Wire}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than Z 0
+        * and 1 turn into X when they are mixed
+        * 
+        * @author Fabian Stemmler
+        */
+       public class WireEnd
+       {
+               private boolean open;
+               private BitVector inputValues;
+
+               private WireEnd(boolean readOnly)
+               {
+                       super();
+                       open = !readOnly; // TODO: that makes sense, doesn't it?
+                       initValues();
+                       if (!readOnly)
+                               registerInput(this);
+               }
+
+               private void initValues()
+               {
+                       inputValues = U.toVector(length);
+               }
+
+               /**
+                * Sets the wires values. This takes up time, as specified by the {@link Wire}s travel time.
+                * 
+                * @param newValues The new values the wires should take on.
+                * 
+                * @author Fabian Stemmler
+                */
+               public void feedSignals(Bit... newValues)
+               {
+                       feedSignals(BitVector.of(newValues));
+               }
+
+               public void feedSignals(BitVector newValues)
+               {
+                       if (newValues.length() != length)
+                               throw new IllegalArgumentException(
+                                               String.format("Attempted to input %d bits instead of %d bits.", newValues.length(), length));
+                       if (!open)
+                               throw new RuntimeException("Attempted to write to closed WireArrayEnd.");
+                       Simulation.TIMELINE.addEvent(e -> setValues(newValues), travelTime);
+               }
+
+               /**
+                * Sets values of a subarray of wires. This takes up time, as specified by the {@link Wire}s travel time.
+                * 
+                * @param bitVector   The new values the wires should take on.
+                * @param startingBit The first index of the subarray of wires.
+                * 
+                * @author Fabian Stemmler
+                */
+               public void feedSignals(int startingBit, BitVector bitVector)
+               {
+                       if (!open)
+                               throw new RuntimeException("Attempted to write to closed WireArrayEnd.");
+                       Simulation.TIMELINE.addEvent(e -> setValues(startingBit, bitVector), travelTime);
+               }
+
+               private void setValues(int startingBit, BitVector newValues)
+               {
+                       // index check covered in equals
+                       if (!inputValues.equalsWithOffset(newValues, startingBit))
+                       {
+                               Bit[] vals = inputValues.getBits();
+                               System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length());
+                               inputValues = BitVector.of(vals);
+                               Wire.this.recalculate();
+                       }
+               }
+
+               private void setValues(BitVector newValues)
+               {
+                       if (inputValues.equals(newValues))
+                               return;
+                       inputValues = newValues;
+                       Wire.this.recalculate();
+               }
+
+               /**
+                * @return The value (of bit 0) the {@link WireEnd} is currently feeding into the associated {@link Wire}.
+                */
+               public Bit getInputValue()
+               {
+                       return getInputValue(0);
+               }
+
+               /**
+                * @return The value which the {@link WireEnd} is currently feeding into the associated {@link Wire} at the indexed {@link Bit}.
+                */
+               public Bit getInputValue(int index)
+               {
+                       return inputValues.getBit(index);
+               }
+
+               /**
+                * @return A copy (safe to modify) of the values the {@link WireEnd} is currently feeding into the associated {@link Wire}.
+                */
+               public BitVector getInputValues()
+               {
+                       return getInputValues(0, length);
+               }
+
+               public BitVector getInputValues(int start, int end)
+               {
+                       return inputValues.subVector(start, end);
+               }
+
+               /**
+                * {@link WireEnd} now feeds Z into the associated {@link Wire}.
+                */
+               public void clearSignals()
+               {
+                       feedSignals(Z.toVector(length));
+               }
+
+               public BitVector wireValuesExcludingMe()
+               {
+                       BitVectorMutator mutator = BitVectorMutator.empty();
+                       for (WireEnd wireEnd : inputs)
+                       {
+                               if (wireEnd == this)
+                                       continue;
+                               mutator.join(wireEnd.inputValues);
+                       }
+                       return mutator.get();
+               }
+
+               /**
+                * Included for convenient use on {@link Wire}s of length 1.
+                * 
+                * @return The value of bit 0.
+                * 
+                * @author Fabian Stemmler
+                */
+               public Bit getValue()
+               {
+                       return Wire.this.getValue();
+               }
+
+               /**
+                * @param index Index of the requested bit.
+                * @return The value of the indexed bit.
+                * 
+                * @author Fabian Stemmler
+                */
+               public Bit getValue(int index)
+               {
+                       return Wire.this.getValue(index);
+               }
+
+               /**
+                * @param index Index of the requested bit.
+                * @return The value of the indexed bit.
+                * 
+                * @author Fabian Stemmler
+                */
+               public BitVector getValues()
+               {
+                       return Wire.this.getValues();
+               }
+
+               /**
+                * @param start Start of the wanted segment. (inclusive)
+                * @param end   End of the wanted segment. (exclusive)
+                * @return The values of the segment of {@link Bit}s indexed.
+                * 
+                * @author Fabian Stemmler
+                */
+               public BitVector getValues(int start, int end)
+               {
+                       return Wire.this.getValues(start, end);
+               }
+
+               /**
+                * The {@link Wire} is interpreted as an unsigned integer with n bits.
+                * 
+                * @return <code>true</code> if all bits are either <code>Bit.ONE</code> or <code>Bit.ZERO</code> (they do not all have to have the
+                *         same value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.
+                * 
+                * @author Fabian Stemmler
+                */
+               public boolean hasNumericValue()
+               {
+                       return Wire.this.hasNumericValue();
+               }
+
+               /**
+                * The {@link Wire} is interpreted as an unsigned integer with n bits.
+                * 
+                * @return The unsigned value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
+                * 
+                * @author Fabian Stemmler
+                */
+               public long getUnsignedValue()
+               {
+                       return Wire.this.getUnsignedValue();
+               }
+
+               /**
+                * The {@link Wire} is interpreted as a signed integer with n bits.
+                * 
+                * @return The signed value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
+                * 
+                * @author Fabian Stemmler
+                */
+               public long getSignedValue()
+               {
+                       return Wire.this.getSignedValue();
+               }
+
+               @Override
+               public String toString()
+               {
+                       return inputValues.toString();
+                       // return String.format("%s \nFeeding: %s", WireArray.this.toString(), Arrays.toString(inputValues));
+               }
+
+               public void close()
+               {
+                       inputs.remove(this);
+                       open = false;
+               }
+
+               public int length()
+               {
+                       return length;
+               }
+
+               public boolean addObserver(WireObserver ob)
+               {
+                       return Wire.this.addObserver(ob);
+               }
+
+               public Wire getWire()
+               {
+                       return Wire.this;
+               }
+       }
+
+       @Override
+       public String toString()
+       {
+               return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), values, inputs);
+               // Arrays.toString(values), inputs.stream().map(i -> Arrays.toString(i.inputValues)).reduce((s1, s2) -> s1 + s2)
+       }
+
+       public static WireEnd[] extractEnds(Wire[] w)
+       {
+               WireEnd[] inputs = new WireEnd[w.length];
+               for (int i = 0; i < w.length; i++)
+                       inputs[i] = w[i].createEnd();
+               return inputs;
+       }
+}
\ No newline at end of file
diff --git a/era.mi/src/era/mi/logic/wires/WireObserver.java b/era.mi/src/era/mi/logic/wires/WireObserver.java
new file mode 100644 (file)
index 0000000..9258e0a
--- /dev/null
@@ -0,0 +1,8 @@
+package era.mi.logic.wires;
+
+import era.mi.logic.types.BitVector;
+
+public interface WireObserver
+{
+       public void update(Wire initiator, BitVector oldValues);
+}