# You may have to edit this file to delete header lines produced by # mailers or news systems from this file (all lines before these shell # comments you are currently reading). # Shell archive made by dwjones on Thu Apr 20 16:17:19 CDT 2017 # To install this software on a UNIX system: # 1) create a directory (e.g. with the shell command mkdir stuff) # 2) change to that directory (e.g. with the command cd stuff), # 3) direct the remainder of this text to sh (e.g. sh < ../thisfile). # This will make sh create files in the new directory; it will do # nothing else (if you're paranoid, you should scan the following text # to verify this before you follow these directions). Then read README # in the new directory for additional instructions. cat > README <<\xxxxxxxxxx README a ternary logic simulator with graphical output in the style required by MP5. Note that only the file Gate.java is specifically a solution to MP5; the rest of this distribution is merely scaffolding to permit that code to run. Contents: README -- this file classes -- a list of all the class.java files in this program *.java -- the source code testfile -- a file of input formatted for this code To build this project, use the following command line: javac @classes To run this project on the test file use this command line: java TernaryLogic testfile 1 16 xxxxxxxxxx cat > classes <<\xxxxxxxxxx Errors.java ScanSupport.java Simulation.java Wire.java Gate.java MaxGate.java MinGate.java NegGate.java IsFGate.java IsTGate.java IsUGate.java TernaryLogic.java xxxxxxxxxx cat > Errors.java <<\xxxxxxxxxx /* Errors.java -- error reporting support package */ /** Utility package for error handling. * * General purpose error reporting package for command-line applications. * It allows reporting fatal errors and warnings to the user. * * @author Douglas Jones * @version 2017-04-05 * this code is ripped from RoadNetwork.java version 2017-03-31. */ public class Errors { private Errors(){}; // you may never instantiate this class private static int count = 0; // warning count, really public read only /** Provide public read only access to the count of warnings. * @return the count of the non-fatal warnings */ public static int count() { return count; } /** Warn of non fatal errors with a message on system.err * @param message the string to output as an error message. */ public static void warn( String message ) { System.err.println( "Warning: " + message ); count = count + 1; } /** Report fatal errors with a message on system.err * and then exit the application. * @param message the string to output as an error message. */ public static void fatal( String message ) { System.err.println( "Fatal error: " + message ); System.exit( -1 ); } } xxxxxxxxxx cat > Gate.java <<\xxxxxxxxxx /* Gate.java -- abstract class defining generic properties of logic gates */ import java.util.LinkedList; import java.util.Scanner; /** Gates are linked by wires. * Each specific type of gate must extend this abstract class. * * @author Douglas Jones * @version 2017-04-17 * The logic used to simulate a gate is largely lifted from the * posted solution to Homework 8. * * @see Wire * @see MinGate * @see MaxGate * @see NegGate * @see IsTGate * @see IsFGate * @see IsUGate * @see TernaryLogic#findGate(String) */ public abstract class Gate { private final LinkedList outgoing = new LinkedList (); /** setter method to add outgoing wires to this gate * @param w the wire that connects from this gate */ public void addOutgoing( Wire w ) { outgoing.add( w ); } private int incount = 0; // how many inputs are connected? /** setter method to add incoming wires to this gate * @param w the wire that connects to this gate */ public void addIncoming( Wire w ) { // actually, we don't need w, but the public doesn't know that incount = incount + 1; } public final String name; // the name of the gate // Gripe: We'd like to declare the following as final, but can't // because they're set by the subclass constructor public int inputs; // the type of the gate public float delay; // the type of the gate /** constructor needed by subclasses to set the final fields of gate * @param n the name of the new gate */ protected Gate( String n ) { name = n; } /** factory method scans and processes one gate definition * @param sc The scanner from which input is read to build the gate. * @return the newly constructed gate. */ public static Gate newGate( Scanner sc ) { String myName = ScanSupport.nextName( sc ); if ("".equals( myName )) { Errors.warn( "gate has no name" ); sc.nextLine(); return null; } if (TernaryLogic.findGate( myName ) != null) { Errors.warn( "Gate '" + myName + "' redefined." ); sc.nextLine(); return null; } String myType = ScanSupport.nextName( sc ); if ("min".equals( myType )) { return new MinGate( sc, myName ); } else if ("max".equals( myType )) { return new MaxGate( sc, myName ); } else if ("neg".equals( myType )) { return new NegGate( sc, myName ); } else if ("isfalse".equals( myType )) { return new IsFGate( sc, myName ); } else if ("istrue".equals( myType )) { return new IsTGate( sc, myName ); } else if ("isunknown".equals( myType )) { return new IsUGate( sc, myName ); } else { Errors.warn( "Gate '" + myName + "' '" + myType + "' has an illegal type." ); sc.nextLine(); return null; } } /** Scan gate's delay and line end to finish initialization; * this is always called at the end of the subclass constructor. * @param sc The scanner from which input is read to build the gate. */ protected final void finishGate( Scanner sc ) { delay = sc.nextFloat(); if (delay != delay) { // really asks if delay == NaN Errors.warn( this.myString() + " -- has no delay" ); } else if (delay < 0.0f) { Errors.warn( this.myString() + " -- has negative delay." ); } ScanSupport.lineEnd( sc, () -> this.myString() ); } /** Get the representation of this Gate in the format of the input file. * It would have been nice to use toString here, * but Java does not permit that to be protected. * @return the string representation of this gate. */ protected String myString() { return( "gate " + name ); } // ***** Logic Simulation ***** // for logic values, inputCount[v] shows how many inputs have that value int inputCounts[] = new int[3]; // all of the following are initially set to unknown int output = 1; // this gate's most recently computed output value int current = 1; // this gate's current output for printing int previous = 1; // this gate's previously printed output /** Sanity check for gates */ public void check() { if (incount < inputs) { Errors.warn( this.myString() + " -- has missing inputs." ); } else if (incount > inputs) { Errors.warn( this.myString() + " -- has too many inputs." ); } // initially, all the inputs are unknown inputCounts[0] = 0; inputCounts[1] = inputs; inputCounts[2] = 0; // and initially, the output is unknown output = 1; // some subclasses will add to this behavior } /** The textual print value for this gate as required by MP5. * The first array index is the old value, * the second array index is the new value. */ private static final String[][] printValues = { { "| ", "|_ ", "|___ " }, { " _| ", " | ", " |_ " }, { " ___|", " _|", " |" } }; /** Get the graphical display of this gate's output since the last * time this gate was checked. * @return The string to be displayed. */ public String printValue() { String r = printValues[previous][current]; previous = current; return r; } /** Every subclass must define this function; * @return the new logic value, a function of inputCounts; */ protected abstract int logicValue(); /** Event service routine called when the input to a gate changes * @param time the time at which the input changes * @param oldv the previous logic value carried over this input * @param newv the new logic value carried over this input */ public void inputChangeEvent( float time, int oldv, int newv ) { inputCounts[oldv]--; inputCounts[newv]++; final int newOut = logicValue(); if (output != newOut) { final int old = output; Simulation.schedule( time + delay, (float t) -> outputChangeEvent( t, old, newOut ) ); output = newOut; } }; /** Event service routine called when the output of a gate changes * @param time the time at which the output changes * @param oldv the previous logic value of this gate's output * @param newv the new logic value of this gate's output */ public void outputChangeEvent( float time, int oldv, int newv ) { // send the new value out to all the outgoing wires for ( Wire w: outgoing ) { // this is optimized, we could have scheduled an event w.inputChangeEvent( time, oldv, newv ); } // record the new output value in case it needs to be printed current = newv; }; } xxxxxxxxxx cat > IsFGate.java <<\xxxxxxxxxx /* IsFGate.java -- implements the is-false subclass of gates */ import java.util.Scanner; /** the is-false gate, a kind of gate. * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class IsFGate extends Gate { /** initializer scans and processes one is-false gate * @param sc the Scanner from which gate description is read * @param myName the value to be put in the name field */ IsFGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); inputs = 1; // it is a one-input gate this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " isfalse " + delay ); } // ***** Logic Simulation for IsFGate ***** /** Sanity check for IsFGate */ public void check() { super.check(); // now change the output from unknown to false Simulation.schedule( delay, (float t) -> this.outputChangeEvent( t, 1, 0 ) ); output = 0; } /** Return the new logic value, false unless the input is false. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { int newOutput = 0; if (inputCounts[0] != 0) newOutput = 2; return newOutput; } } xxxxxxxxxx cat > IsTGate.java <<\xxxxxxxxxx /* IsTGate.java -- implements the is-true subclass of gates */ import java.util.Scanner; /** the is-true gate, a kind of gate * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class IsTGate extends Gate { /** initializer scans and processes one is-true gate * @parame sc Scanner from which gate description is read * @param myName the value to be put in the name field */ IsTGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); inputs = 1; // it is a one-input gate this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " istrue " + delay ); } // ***** Logic Simulation for IsTGate ***** /** Sanity check for IsTGate */ public void check() { super.check(); // now change the output from unknown to false Simulation.schedule( delay, (float t) -> this.outputChangeEvent( t, 1, 0 ) ); output = 0; } /** Return the new logic value, false unless the input is true. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { int newOutput = 0; if (inputCounts[2] != 0) newOutput = 2; return newOutput; } } xxxxxxxxxx cat > IsUGate.java <<\xxxxxxxxxx /* IsUGate.java -- implements the is-undefined subclass of gates */ import java.util.Scanner; /** the is-undefined gate, a kind of gate. * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class IsUGate extends Gate { /** initializer scans and processes one is-undefined gate * @param sc Scanner from which gate description is read * @param myName the value to be put in the name field */ IsUGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); inputs = 1; // it is a one-input gate this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " isundefined " + delay ); } // ***** Logic Simulation for IsUGate ***** /** Sanity check for IsUGate */ public void check() { super.check(); // now change the output from unknown to true Simulation.schedule( delay, (float t) -> this.outputChangeEvent( t, 1, 2 ) ); output = 2; } /** Return the new logic value, false unless the input is undefined. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { int newOutput = 0; if (inputCounts[1] != 0) newOutput = 2; return newOutput; } } xxxxxxxxxx cat > MaxGate.java <<\xxxxxxxxxx /* MaxGate.java -- a subclass of gates that computes the max of its inputs */ import java.util.Scanner; /** the max operator, a fundamental kind of ternary logic gate. * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class MaxGate extends Gate { /** initializer scans and processes one max gate * @param sc Scanner from which gate description is read * @param myName the value to be put in the name field */ MaxGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); // get inputs if (sc.hasNextInt()) { inputs = sc.nextInt(); } else { Errors.warn( this.myString() + " max -- has no input count" ); } this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " max " + inputs + " " + delay ); } // ***** Logic Simulation for MaxGate ***** /** Return the new logic value, the max of the input values. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { // find the maximum of all the inputs int newOutput = 2; while (inputCounts[newOutput] == 0) newOutput--; return newOutput; } } xxxxxxxxxx cat > MinGate.java <<\xxxxxxxxxx /* MinGate.java -- a subclass of gates that computes the min of its inputs */ import java.util.Scanner; /** the min operator, a fundamental kind of ternary logic gate. * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class MinGate extends Gate { /** initializer scans and processes one min gate * @param sc Scanner from which gate description is read * @param myName the value to be put in the name field */ MinGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); // get inputs if (sc.hasNextInt()) { inputs = sc.nextInt(); } else { Errors.warn( this.myString() + " min -- has no input count" ); } this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " min " + inputs + " " + delay ); } // ***** Logic Simulation for MinGate ***** /** Return the new logic value, the min of the input values. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { // find the minimum of all the inputs int newOutput = 0; while (inputCounts[newOutput] == 0) newOutput++; return newOutput; } } xxxxxxxxxx cat > NegGate.java <<\xxxxxxxxxx /* NegGate.java -- implements logical negation gates, a ternary operator */ import java.util.Scanner; /** the negate gate, a fundamental ternary logic gate. * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class NegGate extends Gate { /** initializer scans and processes one neg gate * @param sc Scanner from which gate description is read * @param myName the value to be put in the name field */ NegGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); inputs = 1; // it is a one-input gate this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " neg " + " " + delay ); } // ***** Logic Simulation for NegGate ***** /** Return the new logic value, 2 minus the input value. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { // Warning this is mildly tricky code int newOutput = 2; while (inputCounts[2 - newOutput] == 0) newOutput--; return newOutput; } } xxxxxxxxxx cat > ScanSupport.java <<\xxxxxxxxxx /* ScanSupport.java -- package of support methods for scanning */ import java.util.Scanner; import java.util.regex.Pattern; /** Support methods for scanning input files. * @author Douglas Jones * @version 2017-04-05 * This is ripped from RoadNetwork.java version 2017-03-31 with a change: * nextFloat method is added based on the homework and the comments are * somewhat improved. * @see Errors */ public class ScanSupport { /** Pattern for recognizing identifers */ public static final Pattern name // letter followed by alphanumeric = Pattern.compile( "[a-zA-Z][a-zA-Z0-9_]*|" ); /** Pattern for recognizing floating point numbers */ public static final Pattern numb // Digits.Digits or .Digits or nothing = Pattern.compile( "[0-9]+\\.?[0-9]*|\\.[0-9]+|" ); /** Pattern for recognzing whitespace excluding newlines */ public static final Pattern whitespace = Pattern.compile( "[ \t]*" ); /** Get next name without skipping to next line (unlike sc.Next()). * @param sc the scanner from which end of line is scanned. * @return the name, if there was one, or an empty string. */ public static String nextName( Scanner sc ) { sc.skip( whitespace ); // the following is weird code, it skips the name // and then returns the string that matched what was skipped sc.skip( name ); return sc.match().group(); } /** Get next float without skipping lines (unlike sc.nextFloat()). * @param sc the scanner from which end of line is scanned. * @return the name, if there was one, or NaN if not. */ public static Float nextFloat( Scanner sc ) { sc.skip( whitespace ); // the following is weird code, it skips the name // and then returns the string that matched what was skipped sc.skip( numb ); String f = sc.match().group(); // now convert what we can or return NaN if (!"".equals( f )) { return Float.parseFloat( f ); } else { return Float.NaN; } } /** Class used only for deferred evaluation of lambda expressions * passed to lineEnd. */ public interface EndMessage { /** Method to compute the error message text * @return the text the error message */ public abstract String myString(); } /** Advance to next line and complain if there is junk at the line end; * call this when all useful content has been consumed from the line * it skips optional line-end comments and complains about anything * it finds while advancing to the next line. * @see Errors * @see EndMessage * @param sc the scanner from which end of line is scanned. * @param message will be evaluated only when there is an error; * it is typically passed as a lambda expression, for example, * {@code ScanSupport.lineEnd( sc, () -> "this " + x + " that" );} */ public static void lineEnd( Scanner sc, EndMessage message ) { sc.skip( whitespace ); String lineEnd = sc.nextLine(); if ( (!lineEnd.equals( "" )) && (!lineEnd.startsWith( "--" )) ) { Errors.warn( "" + message.myString() + " followed unexpected by '" + lineEnd + "'" ); } } } xxxxxxxxxx cat > Simulation.java <<\xxxxxxxxxx /* Simulation.java -- discrete event simulation framework */ import java.util.PriorityQueue; /** Discrete event simulation support framework * @author Douglas Jones * @version 2017-04-10 * this code is ripped from RoadNetwork.java version 2017-03-31 */ class Simulation { /** Interface allowing actions to be passed as lambda expressions. */ public interface Action { void trigger( float time ); } /** Events are the core of the control structure of the simulation. */ private static class Event { /** Each event has a time */ float time; /** Each event has an action */ Action act; /** Construct and initialize a new Event. * @param t the time of the event. * @param a the act to be triggered then. */ Event( float t, Action a ) { time = t; act = a; }; /** Trigger the event's act at the indicated * time. */ void trigger() { act.trigger( time ); } } /** Events are queued for {@code run} retrieve in chronological order. */ private static PriorityQueue eventSet = new PriorityQueue ( (Event e1, Event e2)->Float.compare( e1.time, e2.time ) ); /** Users call schedule to schedule one action at some time, * usually a later time but possibly the current time. * * @param time specifies when the event should occur. * @param act specifies what to do at that time. * Typically, {@code act} is a lambda expression, * so a call to schedule could look like this: * * {@code * Simulation.schedule( t, (float t)->object.method( params, t ) ) * } */ public static void schedule( float time, Action act ) { eventSet.add( new Event( time, act ) ); } /** the main program should build the model, * this inolves scheduling some initial events * and then, just once, it should call {@code run}. */ public static void run() { while (!eventSet.isEmpty()) { Event e = eventSet.remove(); e.trigger(); } } } xxxxxxxxxx cat > TernaryLogic.java <<\xxxxxxxxxx /* TernaryLogic.java -- main class of a ternary logic simulator */ import java.util.LinkedList; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; /** TernaryLogic -- The main class of a ternary logic simulator. * * This reads a description of a ternary logic circuit, builds a model, * and if that model passes sanity checks, runs a simulation of that * circuit. * * It runs from the command line, with three command line arguments, * the input file name, the interval between successive outputs, and the * total duration of the simulation. * * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * * @see Wire * @see Gate * @see Errors * @see Simulation * @see ScanSupport * @see #main */ public class TernaryLogic { // lists of roads and intersectins static LinkedList wires = new LinkedList (); static LinkedList gates = new LinkedList (); /** utility method to look up an gate by name * @param s is the name of the gate, a string * @return is the Gate object with that name */ public static Gate findGate( String s ) { for ( Gate g: gates ) { if (g.name.equals( s )) return g; } return null; } /** read a ternary logic system. * @param sc the scanner from which the system is read. */ public static void initializeTernary( Scanner sc ) { while (sc.hasNext()) { // until we hit the end of the file String command = ScanSupport.nextName( sc ); if ("gate".equals( command )) { Gate g = Gate.newGate( sc ); if (g != null) gates.add( g ); } else if ("wire".equals( command )) { wires.add( new Wire( sc ) ); } else if ("".equals( command )) { // blank or comment // line holding -- ends up here! ScanSupport.lineEnd( sc, () -> "Line" ); } else { Errors.warn( "Command '" + command + "' is not gate or wire" ); sc.nextLine(); // skip the rest of the error } } } /** Check the sanity of the network. * @see Gate#check */ public static void checkNetwork() { for ( Gate g: gates ) { g.check(); } // we could also go through the wires, // but there's nothing to check there. } /** write out a ternary logic system */ public static void writeTernary() { for ( Gate g: gates ) { System.out.println( g.toString() ); } for ( Wire w: wires ) { System.out.println( w.toString() ); } } /** logic output interval */ private static float printInterval; /** output headline for logic output * @param i the interval between successive outputs */ public static void initPrint( float i ) { printInterval = i; Simulation.schedule( 0.0f, (float t) -> printGates( t ) ); for( Gate g: gates ) { System.out.print( " " + g.name ); } System.out.println(); } /** output event service routine * @see printInterval */ private static void printGates( float time ) { for( Gate g: gates ) { System.out.print( " " + g.printValue() ); } System.out.println(); Simulation.schedule( time + printInterval, (float t) -> printGates( t ) ); } /** main program that reads and writes a road network * @param args the command line arguments must hold one file name */ public static void main( String[] args ) { // verify that the argument exists. if (args.length < 1) { Errors.fatal( "Missing file name on command line" ); } else if (args.length < 2) { Errors.fatal( "Missing interval on command line" ); } else if (args.length < 3) { Errors.fatal( "Missing time limit on command line" ); } else if (args.length > 3) { Errors.fatal( "Unexpected command line args" ); } else try { initializeTernary( new Scanner( new File( args[0] ) ) ); checkNetwork(); if (Errors.count() > 0) { writeTernary(); } else try { initPrint( Float.parseFloat( args[1] ) ); Simulation.schedule( Float.parseFloat( args[2] ), (float t) -> System.exit( 0 ) ); Simulation.run(); } catch (NumberFormatException e) { // Bug: The error message is wrong for args[2] Errors.fatal( "'" + args[1] + "' is not an floating print interval" ); } } catch (FileNotFoundException e) { Errors.fatal( "Could not read '" + args[0] + "'" ); } } } xxxxxxxxxx cat > Wire.java <<\xxxxxxxxxx /* Wire.java -- class defining the properties of wires */ import java.util.Scanner; /** Wires are link by gates. * * @author Douglas Jones * @version 2017-04-17 * * @see Gate * @see Errors * @see TernaryLogic#findGate(String) */ class Wire { private final float delay; // time delay of this wire private final Gate destination; // where wire goes, or null private final Gate source; // source of wire, or null // Wire name is the source-destination names /** Initializer scans and processes one wire definition. * @param sc The scanner from which the definition is read. */ public Wire( Scanner sc ) { // textual names of source and dest String srcName = ScanSupport.nextName( sc ); String dstName = ScanSupport.nextName( sc ); // if there are no next names on this line, these are "" // therefore, the findGate calls below will fail // lookup names of source and dest source = TernaryLogic.findGate( srcName ); if (source == null) { Errors.warn( "Wire '" + srcName + "' '" + dstName + "' source undefined." ); } destination = TernaryLogic.findGate( dstName ); if (destination == null) { Errors.warn( "Wire '" + srcName + "' '" + dstName + "' destination undefined." ); } delay = ScanSupport.nextFloat( sc ); if (delay != delay) { // really asks if delay == NaN Errors.warn( "Wire '" + srcName + "' '" + dstName + "' has no delay." ); } else if (delay < 0.0f) { Errors.warn( "Wire '" + srcName + "' '" + dstName + "' '" + delay + "' has negative delay." ); } ScanSupport.lineEnd( sc, () -> this.toString() ); // Now, tell the gates that they've been wired together if (destination != null) destination.addIncoming( this ); if (source != null) source.addOutgoing( this ); } /** Convert this wire to a format like that used for input * @return The textual description of the wire */ public String toString() { String srcName; String dstName; if (source == null) { srcName = "???"; } else { srcName = source.name; } if (destination == null) { dstName = "???"; } else { dstName = destination.name; } return( "wire " + srcName + " " + dstName + " " + delay ); } // ***** Logic Simulation ***** /** Event service routine called when the input to a wire changes * @param time the time at which the input changes * @param old the previous logic value carried over this wire * @param new the new logic value carried over this wire */ public void inputChangeEvent( float time, int oldv, int newv ) { Simulation.schedule( time + delay, (float t) -> outputChangeEvent( t, oldv, newv ) ); }; /** Event service routine called when the output of a wire changes * @param time the time at which the output changes * @param old the previous logic value carried over this wire * @param new the new logic value carried over this wire */ public void outputChangeEvent( float time, int oldv, int newv ) { // this version is optimized, we could have scheduled an event destination.inputChangeEvent( time, oldv, newv ); }; } xxxxxxxxxx cat > testfile <<\xxxxxxxxxx gate TIMER istrue 9.0 wire TIMER TIMER 1.0 gate ENABLE isunknown 1.0 wire TIMER ENABLE 1.0 gate CLOCK isfalse 0.5 gate CONTROL min 2 0.5 wire CLOCK CONTROL 0.5 wire CONTROL CLOCK 0.5 wire ENABLE CONTROL 0.5 gate FLIP neg 0.5 gate MIX max 2 0.5 wire TIMER FLIP 1.0 wire FLIP MIX 1.0 wire MIX MIX 1.0 xxxxxxxxxx