#!/usr/bin/perl -I/home/phil/perl/cpan/SvgSimple/lib/
#-------------------------------------------------------------------------------
# Design a silicon chip by combining gates and sub chips.
# Philip R Brenan at appaapps dot com, Appa Apps Ltd Inc., 2023
#-------------------------------------------------------------------------------
# Update sizes from sub chips
use v5.34;
package Silicon::Chip;
our $VERSION = 20231111;                                                        # Version
use warnings FATAL => qw(all);
use strict;
use Carp;
use Data::Dump qw(dump);
use Data::Table::Text qw(:all);
use Svg::Simple;

makeDieConfess;

my sub maxSimulationSteps {100}                                                 # Maximum simulation steps
my sub gateNotIO          {0}                                                   # Not an input or output gate
my sub gateInternalInput  {1}                                                   # Input gate on an internal chip
my sub gateInternalOutput {2}                                                   # Output gate on an internal chip
my sub gateExternalInput  {3}                                                   # Input gate on the external chip
my sub gateExternalOutput {4}                                                   # Output gate on the external chip
my sub gateOuterInput     {5}                                                   # Input gate on the external chip connecting to the outer world
my sub gateOuterOutput    {6}                                                   # Output gate on the external chip connecting to the outer world

my $possibleTypes = q(and|continue|gt|input|lt|nand|nor|not|nxor|one|or|output|xor|zero);#Substitute: possible gate types

#D1 Construct                                                                   # Construct a L<silicon> L<chip> using standard L<lgs>, components and sub chips combined via buses.

sub newChip(%)                                                                  # Create a new L<chip>.
 {my (%options) = @_;                                                           # Options
  !@_ or !ref($_[0]) or confess "Call as a sub not as a method";
  genHash(__PACKAGE__,                                                          # Chip description
    name => $options{name} // $options{title}  // "Unnamed chip: ".timeStamp,   # Name of chip
    gates     => $options{gates} // {},                                         # Gates in chip
    installs  => $options{installs} // [],                                      # Chips installed within the chip
    title     => $options{title},                                               # Title if known
    gateSeq   => 0,                                                             # Gate sequence number - this allows us to display the gates in the order they were defined ti simplify the understanding of drawn layouts
    sizeBits  => {},                                                            # Sizes of buses
    sizeWords => {},                                                            # Sizes of buses
   );
 }

my sub newGate($$$$)                                                            # Make a L<lg>.
 {my ($chip, $type, $output, $inputs) = @_;                                     # Chip, gate type, output name, input names to output from another gate

  my $g = genHash("Silicon::Chip::Gate",                                        # Gate
   type     => $type,                                                           # Gate type
   output   => $output,                                                         # Output name which is used as the name of the gate as well
   inputs   => $inputs,                                                         # Input names to driving outputs
   io       => gateNotIO,                                                       # Whether an input/output gate or not
   seq      => ++$chip->gateSeq,                                                # Sequence number for this gate
  );
 }

my sub validateName($$%)                                                        # Confirm that a component name looks like a variable name and has not already been used
 {my ($chip, $output, %options) = @_;                                           # Chip, name, options

  my $gates = $chip->gates;                                                     # Gates implementing the chip

  $output =~ m(\A[a-z][a-z0-9_.:]*\Z)i or confess <<"END";
Invalid gate name: '$output'
END

  $$gates{$output} and confess <<"END";
Gate: '$output' has already been specified
END
  1
 }

sub gate($$$;$$)                                                                # A L<lg> chosen from B<possibleTypes>.  The gate type can be used as a method name, so B<-E<gt>gate("and",> can be reduced to B<-E<gt>and(>.
 {my ($chip, $type, $output, $input1, $input2) = @_;                            # Chip, gate type, output name, input from another gate, input from another gate
  @_ >= 3 or confess "Three or more parameters";
  my $gates = $chip->gates;                                                     # Gates implementing the chip

  my $inputs;                                                                   # Input hash mapping used to accept outputs from other gates as inputs for this gate

  validateName $chip, $output;                                                  # Validate the name of the gate

  if ($type =~ m(\A(input)\Z)i)                                                 # Input gates input to themselves unless they have been connected to an output gate during sub chip expansion
   {@_> 3 and confess <<"END";
No input hash allowed for input gate: '$output'
END
    $inputs = {$output=>$output};                                               # Convert convenient scalar name to hash for consistency with gates in general
   }
  elsif ($type =~ m(\A(one|zero)\Z)i)                                           # Input gates input to themselves unless they have been connected to an output gate during sub chip expansion
   {@_> 3 and confess <<"END";
No input hash allowed for '$type' gate: '$output'
END
    $inputs = {};                                                               # Convert convenient scalar name to hash for consistency with gates in general
   }
  elsif ($type =~ m(\A(output)\Z)i)                                             # Output has one optional scalar value naming its input if known at this point
   {if (defined($input1))
     {ref($input1) and confess <<"END";
Scalar input name required for input on output gate: '$output'
END
      $inputs = {$output=>$input1};                                             # Convert convenient scalar name to hash for consistency with gates in general
     }
   }
  elsif ($type =~ m(\A(continue|not)\Z)i)                                       # These gates have one input expressed as a name rather than a hash
   {!defined($input1) and confess "Input name required for gate: '$output'\n";
    $type =~ m(\Anot\Z)i and ref($input1) =~ m(hash)i and confess <<"END";
Scalar input name required for: '$output'
END
    $inputs = {$output=>$input1};                                               # Convert convenient scalar name to hash for consistency with gates in general
   }
  elsif ($type =~ m(\A(nxor|xor|gt|ngt|lt|nlt)\Z)i)                             # These gates must have exactly two inputs expressed as a hash mapping input pin name to connection to a named gate.  These operations are associative.
   {!defined($input1) and confess <<"END" =~ s/\n/ /gsr;
Input one required for gate: '$output'
END
    !defined($input2) and confess <<"END" =~ s/\n/ /gsr;
Input two required for gate: '$output'
END
    ref($input1) and confess <<"END" =~ s/\n/ /gsr;
Input one must be the name of the connecting gate.
END
    ref($input2) and confess <<"END" =~ s/\n/ /gsr;
Input two must be the name of the connecting gate.
END
    $inputs = {1=>$input1, 2=>$input2};                                         # Construct the inputs hash expected in general for these two input gates
   }
  elsif ($type =~ m(\A(and|nand|nor|or)\Z)i)                                    # These gates must have two or more inputs expressed as a hash mapping input pin name to connection to a named gate.  These operations are associative.
   {!defined($input1) and confess <<"END" =~ s/\n/ /gsr;
Input hash required for gate: '$output'
END
    if (ref($input1) =~ m(hash)i)
     {$inputs = $input1;
     }
    elsif (ref($input1) =~ m(array)i)
     {$inputs = {map {$_=>$$input1[$_]} keys @$input1};
     }
    else
     {confess <<"END" =~ s/\n/ /gsr;
Inputs must be either a hash of input gate
names to output gate names or an array of input gate name for gate: '$output'
END
     }
   }
  else                                                                          # Unknown gate type
   {confess <<"END" =~ s/\n/ /gsr;
Unknown gate type: '$type' for gate: '$output',
possible types are: '$possibleTypes
END
   }

  $chip->gates->{$output} = newGate($chip, $type, $output, $inputs);            # Construct gate, save it and return it
 }

our $AUTOLOAD;                                                                  # The method to be autoloaded appears here. This allows us to have gate names like 'or' and 'and' without overwriting the existing Perl operators with these names.

sub AUTOLOAD($@)                                                                #P Autoload by L<lg> name to provide a more readable way to specify the L<lgs> on a L<chip>.
 {my ($chip, @options) = @_;                                                    # Chip, options
  my $type = $AUTOLOAD =~ s(\A.*::) ()r;
  if ($type !~ m(\A($possibleTypes|DESTROY)\Z))                                 # Select autoload requests we can process as gate names
   {confess <<"END" =~ s/\n/ /gsr;
Unknown method: '$type'
END
   }
  &gate($chip, $type, @options) if $type =~ m(\A($possibleTypes)\Z);
 }

my sub cloneGate($$)                                                            # Clone a L<lg> on a L<chip>.
 {my ($chip, $gate) = @_;                                                       # Chip, gate
  my %i = $gate->inputs ? $gate->inputs->%* : ();                               # Copy inputs
  newGate($chip, $gate->type, $gate->output, {%i})
 }

my sub getGate($$)                                                              # Details of a named gate or confess if no such gate
 {my ($chip, $name) = @_;                                                       # Chip, gate name
  my $g = $chip->gates->{$name};                                                # Gate details
  $g or confess "No such gate as $name\n";
  $g
 }

my sub renameGateInputs($$$)                                                    # Rename the inputs of a L<lg> on a L<chip>.
 {my ($chip, $gate, $name) = @_;                                                # Chip, gate, prefix name
  for my $p(qw(inputs))
   {my %i;
    my $i = $gate->inputs;
    for my $n(sort keys %$i)
     {$i{$n} = sprintf "(%s %s)", $name, $$i{$n};
     }
    $gate->inputs = \%i;
   }
  $gate
 }

my sub renameGate($$$)                                                          # Rename a L<lg> on a L<chip> by adding a prefix.
 {my ($chip, $gate, $name) = @_;                                                # Chip, gate, prefix name
  $gate->output = sprintf "(%s %s)", $name, $gate->output;
  $gate
 }

sub connectInput($$$%)                                                          # Connect a previously defined input gate to the output of another previously gate on the same chip. This allows us to define a set of gates on the chip without having to know, first, all the names of the gates that will provide input to these gates.
 {my ($chip, $in, $to, %options) = @_;                                          # Chip, input gate, gate to connect input gate to, options
  my $gates = $chip->gates;
  my $i = $$gates{$in};
  defined($i) or confess "No definition of input gate $in";
  $i->type =~ m(\Ainput\Z) or confess "No definition of input gate $in";
  $i->inputs = {1=>$to};
  $chip
 }

#D2 Buses                                                                       # A bus is an array of bits or an array of arrays of bits

#D3 Bits                                                                        # An array of bits that can be manipulated via one name.

my sub sizeBits($$%)                                                            # Size of a bits bus.
 {my ($chip, $name, %options) = @_;                                             # Chip, bits bus name, options
  return $options{bits} if defined($options{bits});
  my $s = $chip->sizeBits->{$name};
  defined($s) or confess "No bit bus named ".dump($name)."\n";
  $s
 }

sub setSizeBits($$$%)                                                           # Set the size of a bits bus.
 {my ($chip, $name, $bits, %options) = @_;                                      # Chip, bits bus name, options
  @_ >= 3 or confess "Three or more parameters";
  defined($chip->sizeBits->{$name}) and confess <<"END";
A bit bus with name: $name has already been defined
END
  $chip->sizeBits->{$name} = $bits;
  $chip
 }

sub bits($$$$%)                                                                 # Create a bus set to a specified number.
 {my ($chip, $name, $bits, $value, %options) = @_;                              # Chip, name of bus, width in bits of bus, value of bus, options
  @_ >= 4 or confess "Four or more parameters";
  my @b = reverse split //, sprintf "%0${bits}b", $value;                       # Bits needed
  for my $b(1..@b)                                                              # Generate constant
   {my $v = $b[$b-1];                                                           # Bit value
    $chip->one (n($name, $b)) if     $v;                                        # Set 1
    $chip->zero(n($name, $b)) unless $v;                                        # Set 0
   }
  setSizeBits($chip, $name, $bits);                                             # Record bus width
 }

sub inputBits($$$%)                                                             # Create an B<input> bus made of bits.
 {my ($chip, $name, $bits, %options) = @_;                                      # Chip, name of bus, width in bits of bus, options
  @_ >= 3 or confess "Three or more parameters";
  setSizeBits($chip, $name, $bits);                                             # Record bus width
  map {$chip->input(n($name, $_))} 1..$bits;                                    # Bus of input gates
 }

sub outputBits($$$%)                                                            # Create an B<output> bus made of bits.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, name of inputs, options
  @_ >= 3 or confess "Three or more parameters";
  my $bits = sizeBits($chip, $input, %options);
  map {$chip->output(n($name, $_), n($input, $_))} 1..$bits;                    # Bus of output gates
  setSizeBits($chip, $name, $bits);                                             # Record bus width
 }

sub notBits($$$%)                                                               # Create a B<not> bus made of bits.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, name of inputs, options
  @_ >= 3 or confess "Three or more parameters";
  my $bits = sizeBits($chip, $input, %options);
  map {$chip->not(n($name, $_), n($input, $_))} 1..$bits;                       # Bus of not gates
  setSizeBits($chip, $name, $bits);                                             # Record bus width
 }

sub andBits($$$%)                                                               # B<and> a bus made of bits.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, name of inputs, options
  @_ >= 3 or confess "Three or more parameters";
  my $bits = sizeBits($chip, $input, %options);
  $chip->and($name, {map {($_=>n($input, $_))} 1..$bits});                      # Combine inputs in one B<and> gate
  setSizeBits($chip, $name, $bits);                                             # Record bus width
 }

sub nandBits($$$%)                                                              # B<nand> a bus made of bits.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, name of inputs, options
  @_ >= 3 or confess "Three or more parameters";
  my $bits = sizeBits($chip, $input, %options);
  $chip->nand($name, {map {($_=>n($input, $_))} 1..$bits});                     # Combine inputs in one B<nand> gate
  setSizeBits($chip, $name, $bits);                                             # Record bus width
 }

sub orBits($$$%)                                                                # B<or> a bus made of bits.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, options
  @_ >= 3 or confess "Three or more parameters";
  my $bits = sizeBits($chip, $input, %options);
  $chip->or($name,  {map {($_=>n($input, $_))} 1..$bits});                      # Combine inputs in one B<or> gate
  setSizeBits($chip, $name, $bits);                                             # Record bus width
 }

sub norBits($$$%)                                                               # B<nor> a bus made of bits.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, options
  @_ >= 3 or confess "Three or more parameters";
  my $bits = sizeBits($chip, $input, %options);
  $chip->nor($name,  {map {($_=>n($input, $_))} 1..$bits});                     # Combine inputs in one B<nor> gate
  setSizeBits($chip, $name, $bits);                                             # Record bus width
 }

#D3 Words                                                                       # An array of arrays of bits that can be manipulated via one name.

my sub sizeWords($$%)                                                           # Size of a words bus.
 {my ($chip, $name, %options) = @_;                                             # Chip, word bus name, options
  my $s = $chip->sizeWords->{$name};
  my $w = $options{bits}  // $$s[0];
  my $b = $options{words} // $$s[1];
  defined($w) or confess "No words width specified or defaulted for $name";
  defined($b) or confess "No bits width specified or defaulted for $name";
  ($w, $b)
 }

sub setSizeWords($$$$%)                                                         # Set the size of a bits bus.
 {my ($chip, $name, $words, $bits, %options) = @_;                              # Chip, bits bus name, words, bits per word, options
  @_ >= 4 or confess "Four or more parameters";
  defined($chip->sizeWords->{$name}) and confess <<"END";
A word bus with name: $name has already been defined
END
  $chip->sizeWords->{$name} = [$words, $bits];                                  # Word bus size
  setSizeBits($chip, n($name, $_), $bits) for 1..$words;                        # Size of bit bus for each word in the word bus
  $chip
 }

sub words($$$@)                                                                 # Create a word bus set to specified numbers.
 {my ($chip, $name, $bits, @values) = @_;                                       # Chip, name of bus,  width in bits of each word, values of words
  @_ >= 3 or confess "Three or more parameters";
  for my $w(1..@values)                                                         # Each value to put on the bus
   {my $value = $values[$w-1];                                                  # Each value to put on the bus
    my @b = reverse split //, sprintf "%0${bits}b", $value;                     # Bits needed
    for my $b(1..@b)                                                            # Generate constant
     {my $v = $b[$b-1];                                                         # Bit value
      $chip->one (nn($name, $w, $b)) if     $v;                                 # Set 1
      $chip->zero(nn($name, $w, $b)) unless $v;                                 # Set 0
     }
   }
  setSizeWords($chip, $name, scalar(@values), $bits);                           # Record bus width
 }

sub inputWords($$$$%)                                                           # Create an B<input> bus made of words.
 {my ($chip, $name, $words, $bits, %options) = @_;                              # Chip, name of bus, width in words of bus, width in bits of each word on bus, options
  @_ >= 4 or confess "Four or more parameters";
  for my $w(1..$words)                                                          # Each word on the bus
   {map {$chip->input(nn($name, $w, $_))} 1..$bits;                             # Bus of input gates
   }
  setSizeWords($chip, $name, $words, $bits);                                    # Record bus size
 }

sub outputWords($$$%)                                                           # Create an B<output> bus made of words.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, name of inputs, options
  @_ >= 3 or confess "Three or more parameters";
  my ($words, $bits) = sizeWords($chip, $input);
  for my $w(1..$words)                                                          # Each word on the bus
   {map {$chip->output(nn($name, $w, $_), nn($input, $w, $_))} 1..$bits;        # Bus of output gates
   }
  setSizeWords($chip, $name, $words, $bits);                                    # Record bus size
 }

sub notWords($$$%)                                                              # Create a B<not> bus made of words.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, name of inputs, options
  @_ >= 3 or confess "Three or more parameters";
  my ($words, $bits) = sizeWords($chip, $input, %options);
  for my $w(1..$words)                                                          # Each word on the bus
   {map {$chip->not(nn($name, $w, $_), nn($input, $w, $_))} 1..$bits;           # Bus of not gates
   }
  setSizeWords($chip, $name, $words, $bits);                                    # Record bus size
 }

sub andWords($$$%)                                                              # B<and> a bus made of words to produce a single word.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, name of inputs, options
  @_ >= 3 or confess "Three or more parameters";
  my ($words, $bits) = sizeWords($chip, $input, %options);
  for my $w(1..$words)                                                          # Each word on the bus
   {$chip->andBits(n($name, $w), n($input, $w));                                # Combine inputs using B<and> gates
   }
  setSizeBits($chip, $name, $words);                                            # Record bus size
 }

sub andWordsX($$$%)                                                             # B<and> a bus made of words by and-ing the corresponding bits in each word to make a single word.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, name of inputs, options
  @_ >= 3 or confess "Three or more parameters";
  my ($words, $bits) = sizeWords($chip, $input, %options);
  for my $b(1..$bits)                                                           # Each word on the bus
   {$chip->and(n($name, $b), {map {($_=>nn($input, $_, $b))} 1..$words});       # Combine inputs using B<and> gates
   }
  setSizeBits($chip, $name, $bits);                                             # Record bus size
 }

sub orWords($$$%)                                                               # B<or> a bus made of words to produce a single word.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, name of inputs, options
  @_ >= 3 or confess "Three or more parameters";
  my ($words, $bits) = sizeWords($chip, $input, %options);
  for my $w(1..$words)                                                          # Each word on the bus
   {$chip->orBits(n($name, $w), n($input, $w));                                 # Combine inputs using B<or> gates
   }
  setSizeBits($chip, $name, $words);                                            # Record bus size
 }

sub orWordsX($$$%)                                                              # B<or> a bus made of words by or-ing the corresponding bits in each word to make a single word.
 {my ($chip, $name, $input, %options) = @_;                                     # Chip, name of bus, name of inputs, options
  @_ >= 3 or confess "Three or more parameters";
  my ($words, $bits) = sizeWords($chip, $input, %options);
  for my $b(1..$bits)                                                           # Each word on the bus
   {$chip->or(n($name, $b), {map {($_=>nn($input, $_, $b))} 1..$words});        # Combine inputs using B<or> gates
   }
  setSizeBits($chip, $name, $bits);                                             # Record bus size
 }

#D2 Install                                                                     # Install a chip within a chip as a sub chip.

sub install($$$$%)                                                              # Install a L<chip> within another L<chip> specifying the connections between the inner and outer L<chip>.  The same L<chip> can be installed multiple times as each L<chip> description is read only.
 {my ($chip, $subChip, $inputs, $outputs, %options) = @_;                       # Outer chip, inner chip, inputs of inner chip to to outputs of outer chip, outputs of inner chip to inputs of outer chip, options
  @_ >= 4 or confess "Four or more parameters";
  my $c = genHash("Silicon::Chip::Install",                                     # Installation of a chip within a chip
    chip    => $subChip,                                                        # Chip being installed
    inputs  => $inputs,                                                         # Outputs of outer chip to inputs of inner chip
    outputs => $outputs,                                                        # Outputs of inner chip to inputs of outer chip
   );
  push $chip->installs->@*, $c;                                                 # Install chip
  $c
 }

my sub getGates($%)                                                             # Get the L<lgs> of a L<chip> and all it installed sub chips.
 {my ($chip, %options) = @_;                                                    # Chip, options

  my %outerGates;
  for my $g(sort {$a->seq <=> $b->seq} values $chip->gates->%*)                 # Copy gates from outer chip
   {my $G = $outerGates{$g->output} = cloneGate($chip, $g);
    if    ($G->type =~ m(\Ainput\Z)i)  {$G->io = gateExternalInput}             # Input gate on outer chip
    elsif ($G->type =~ m(\Aoutput\Z)i) {$G->io = gateExternalOutput}            # Output gate on outer chip
   }

  my @installs = $chip->installs->@*;                                           # Each sub chip used in this chip

  for my $install(keys @installs)                                               # Each sub chip
   {my $s = $installs[$install];                                                # Sub chip installed in this chip
    my $n = $s->chip->name;                                                     # Name of sub chip
    my $innerGates = __SUB__->($s->chip);                                       # Gates in sub chip

    for my $G(sort {$$innerGates{$a}->seq <=> $$innerGates{$b}->seq}
              keys  %$innerGates)                                               # Each gate in sub chip on definition order
     {my $g = $$innerGates{$G};                                                 # Gate in sub chip
      my $o = $g->output;                                                       # Name of gate
      my $copy = cloneGate $chip, $g;                                           # Clone gate from chip description
      my $newGateName = sprintf "$n %d", $install+1;                            # Rename gates to prevent name collisions from the expansions of the definitions of the inner chips

      if ($copy->type =~ m(\Ainput\Z)i)                                         # Input gate on inner chip - connect to corresponding output gate on containing chip
       {my $in = $copy->output;                                                 # Name of input gate on inner chip
        my $o  = $s->inputs->{$in};
           $o or confess <<"END";
No connection specified to inner input gate: '$in' on sub chip: '$n'
END
        my $O  = $outerGates{$o};
           $O or confess <<"END" =~ s(\n) ( )gsr;
No outer output gate '$o' to connect to inner input gate: '$in'
on sub chip: '$n'
END
        my $ot = $O->type;
        my $on = $O->output;
           $ot =~ m(\Aoutput\Z)i or confess <<"END" =~ s(\n) ( )gsr;
Output gate required for connection to: '$in' on sub chip $n,
not: '$ot' gate: '$on'
END
        $copy->inputs = {1 => $o};                                              # Connect inner input gate to outer output gate
        renameGate $chip, $copy, $newGateName;                                  # Add chip name to gate to disambiguate it from any other gates
        $copy->io = gateInternalInput;                                          # Mark this as an internal input gate
       }

      elsif ($copy->type =~ m(\Aoutput\Z)i)                                     # Output gate on inner chip - connect to corresponding input gate on containing chip
       {my $on = $copy->output;                                                 # Name of output gate on outer chip
        my $i  = $s->outputs->{$on};
           $i or confess <<"END";
No connection specified to inner output gate: '$on' on sub chip: '$n'
END
        my $I  = $outerGates{$i};
           $I or confess <<"END";
No outer input gate: '$i' to connect to inner output gate: $on on sub chip: '$n'
END
        my $it = $I->type;
        my $in = $I->output;
           $it =~ m(\Ainput\Z)i or confess <<"END" =~ s(\n) ( )gsr;
Input gate required for connection to '$in' on sub chip '$n',
not gate '$in' of type '$it'
END
        renameGateInputs $chip, $copy, $newGateName;
        renameGate       $chip, $copy, $newGateName;
        $I->inputs = {11 => $copy->output};                                     # Connect inner output gate to outer input gate
        $copy->io  = gateInternalOutput;                                        # Mark this as an internal output gate
       }
      else                                                                      # Rename all other gate inputs
       {renameGateInputs $chip, $copy, $newGateName;
        renameGate       $chip, $copy, $newGateName;
       }

      $outerGates{$copy->output} = $copy;                                       # Install gate with new name now it has been connected up
     }
   }
  \%outerGates                                                                  # Return all the gates in the chip extended by its sub chips
 }

my sub checkIO($%)                                                              # Check that each input L<lg> is connected to one output  L<lg>.
 {my ($chip, %options) = @_;                                                    # Chip, options
  my $gates = $chip->gates;                                                     # Gates on chip

  my %o;
  for my $G(sort keys %$gates)                                                  # Find all inputs and outputs
   {my $g = $$gates{$G};                                                        # Address gate
    my $t = $g->type;                                                           # Type of gate
    my %i = $g->inputs->%*;                                                     # Inputs for gate
    for my $i(sort keys %i)                                                     # Each input
     {my $o = $i{$i};                                                           # Output driving input
      defined($o) or  confess <<"END";                                          # No driving output
No output driving input pin '$i' on '$t' gate '$G'
END
      my $O = $$gates{$o};
      defined($O) or  confess <<"END";                                          # No driving output
No output driving input '$o' on '$t' gate '$G'
END
      if ($g->io != gateOuterInput)                                             # The gate must inputs driven by the outputs of other gates
       {$o{$o}++;                                                               # Show that this output has been used
        my $T = $O->type;
        if ($g->type =~ m(\Ainput\Z)i)
         {$O->type =~ m(\Aoutput\Z)i or confess <<"END" =~ s(\n) ( )gsr;
Input gate: '$G' must connect to an output gate on pin: '$i'
not to '$T' gate: '$o'
END
         }
        elsif (!$g->io)                                                         # Not an io gate so it cannot have an input from an output gate
         {$O->type =~ m(\Aoutput\Z) and confess <<"END";
Cannot drive a '$t' gate: '$G' using output gate: '$o'
END
         }
       }
     }
   }

  for my $G(sort keys %$gates)                                                  # Check all inputs and outputs are being used
   {my $g = $$gates{$G};                                                        # Address gate
    next if $g->type =~ m(\Aoutput\Z)i;
    $o{$G} or confess <<"END" =~ s/\n/ /gsr;
Output from gate '$G' is never used
END
   }
 }

my sub setOuterGates($$%)                                                       # Set outer  L<lgs> on external chip that connect to the outer world.
 {my ($chip, $gates, %options) = @_;                                            # Chip, gates in chip plus all sub chips as supplied by L<getGates>.

  for my $G(sort keys %$gates)                                                  # Find all inputs and outputs
   {my $g = $$gates{$G};                                                        # Address gate
    next unless $g->io == gateExternalInput;                                    # Input on external chip
    my ($i) = values $g->inputs->%*;
    $g->io = gateOuterInput if $g->output eq $i;                                # Unconnected input gates reflect back on themselves - this is a short hand way of discovering such gates
   }

  gate: for my $G(sort keys %$gates)                                            # Find all inputs and outputs
   {my $g = $$gates{$G};                                                        # Address gate
    next unless $g->io == gateExternalOutput;                                   # Output on external chip
    for my $H(sort keys %$gates)                                                # Gates driven by this gate
     {next if $G eq $H;
      my %i = $$gates{$H}->inputs->%*;                                          # Inputs to this gate
      for my $I(sort keys %i)                                                   # Each input
       {next gate if $i{$I} eq $G;                                              # Found a gate that accepts input from this gate
       }
     }
    $g->io = gateOuterOutput;                                                   # Does not drive any other gate
   }
 }

my sub removeExcessIO($$%)                                                      # Remove unneeded IO L<lgs> .
 {my ($chip, $gates, %options) = @_;                                            # Chip, gates in chip plus all sub chips as supplied by L<getGates>.

  my %d;                                                                        # Names of gates to delete
  for(;;)                                                                       # Multiple passes until no more gates can be replaced
   {my $changes = 0;

    gate: for my $G(sort keys %$gates)                                          # Find all inputs and outputs
     {my $g = $$gates{$G};                                                      # Address gate
      next unless $g->io;                                                       # Skip non IO gates
      next if     $g->io == gateOuterInput or $g->io == gateOuterOutput;        # Cannot be collapsed
      my ($n) = values $g->inputs->%*;                                          # Name of the gate driving this gate

      for my $H(sort keys %$gates)                                              # Gates driven by this gate
       {next if $G eq $H;
        my $h = $$gates{$H};                                                    # Address gate
        my %i = $h->inputs->%*;                                                 # Inputs
        for my $i(sort keys %i)                                                 # Each input
         {if ($i{$i} eq $G)                                                     # Found a gate that accepts input from this gate
           {my $replace = $h->inputs->{$i};
            $h->inputs->{$i} = $n;                                              # Bypass io gate
            $d{$G}++;                                                           # Delete this gate
            ++$changes;                                                         # Count changes in this pass
           }
         }
       }
     }
    last unless $changes;
   }
  for my $d(sort keys %d)                                                       # Gates to delete
   {delete $$gates{$d};
   }
 }

#D1 Visualize                                                                   # Visualize the L<chip> in various ways.

my sub orderGates($%)                                                           # Order the L<lgs> on a L<chip> so that input L<lg> are first, the output L<lgs> are last and the non io L<lgs> are in between. All L<lgs> are first ordered alphabetically. The non io L<lgs> are then ordered by the step number at which they last changed during simulation of the L<chip>.
 {my ($chip, %options) = @_;                                                    # Chip, options

  my $gates = $chip->gates;                                                     # Gates on chip
  my @i; my @n; my @o;

  for my $G(sort {$$gates{$a}->seq <=> $$gates{$b}->seq} keys %$gates)          # Dump each gate one per line in definition order
   {my $g = $$gates{$G};
    push @i, $G if $g->type =~ m(\Ainput\Z)i;
    push @n, $G if $g->type !~ m(\A(in|out)put\Z)i;
    push @o, $G if $g->type =~ m(\Aoutput\Z)i;
   }

  if (my $c = $options{changed})                                                # Order non IO gates by last change time during simulation if possible
   {@n = sort {($$c{$a}//0) <=> ($$c{$b}//0)} @n;
   }

  (\@i, \@n, \@o)
 }

sub print($%)                                                                   # Dump the L<lgs> present on a L<chip>.
 {my ($chip, %options) = @_;                                                    # Chip, gates, options
  my $gates  = $chip->gates;                                                    # Gates on chip
  my $values = $options{values};                                                # Values of each gate if known
  my @s;
  my ($i, $n, $o) = orderGates $chip, %options;                                 # Gates by type
  for my $G(@$i, @$n, @$o)                                                      # Dump each gate one per line
   {my $g = $$gates{$G};
    my %i = $g->inputs ? $g->inputs->%* : ();

    my $p = sub                                                                 # Instruction name and type
     {my $v = $$values{$G};                                                     # Value if known for this gate
      my $o = $g->output;
      my $t = $g->type;
      return sprintf "%-32s: %3d %-32s", $o, $v, $t if defined($v);             # With value
      return sprintf "%-32s:     %-32s", $o,     $t;                            # Without value
     }->();

    if (my @i = map {$i{$_}} sort keys %i)                                      # Add actual inputs in same line sorted in input pin name
     {$p .= join " ", @i;
     }
    push @s, $p;
   }
  my $s = join "\n", @s, '';                                                    # Representation of gates as text
  owf fpe($options{print}, q(txt)), $s if $options{print};                      # Write representation of gates as text to the named file
  $s
 }

sub Silicon::Chip::Simulation::print($%)                                        # Print simulation results as text.
 {my ($sim, %options) = @_;                                                     # Simulation, options
  $sim->chip->print(%options, values=>$sim->values);
 }

my sub newGatePosition(%)                                                       # Specify the position of a L<lg> on a drawing of the containing L<chip>.
 {my (%options) = @_;                                                           # Options

  genHash("Silicon::Chip::Gate::Position",                                      # Gate position
    gate  => $options{gate}  // undef,                                          # Gate
    x     => $options{x}     // undef,                                          # X position of gate
    y     => $options{y}     // undef,                                          # Y position of gate
    width => $options{width} // undef,                                          # Width of gate
   )
 }

sub printSvg($%)                                                                # Dump the L<lgs> on a L<chip> as an L<svg> drawing to help visualize the structure of the L<chip>.
 {my ($chip, %options) = @_;                                                    # Chip, options
  my $gates   = $chip->gates;                                                   # Gates on chip
  my $title   = $chip->title;                                                   # Title of chip
  my $changed = $options{changed};                                              # Step at which gate last changed in simulation
  my $values  = $options{values};                                               # Values of each gate if known
  my $steps   = $options{steps};                                                # Number of steps to equilibrium

  my $fs = 0.2; my $fw = 0.02;                                                  # Font sizes
  my $Fs = 0.4; my $Fw = 0.04;
  my $op0 = q(transparent);

  my $s = Svg::Simple::new(defaults=>{stroke_width=>$fw, font_size=>$fs});      # Draw each gate via Svg

  my %p;                                                                        # Dimensions and drawing positions of gates
  my ($iG, $nG, $oG) = orderGates $chip, %options;                              # Gates by type

  for my $i(keys @$iG)                                                          # Index of each input gate
   {my $G = $$iG[$i];                                                           # Gate name
    my $g = $$gates{$G};                                                        # Gate
    $p{$G} = newGatePosition(gate=>$g, x=>0, y=>$i, width=>1);                  # Position input gate
   }

  my $W = 0;                                                                    # Number of inputs to all the non IO gates
  for my $i(keys @$nG)                                                          # Index of each non IO gate
   {my $G = $$nG[$i];                                                           # Gate name
    my $g = $$gates{$G};                                                        # Gate
    my $w = keys($g->inputs->%*) || 1;                                          # Width of gate has to be wide enough to accommodate all inputs
    $p{$G} = newGatePosition(gate=>$g, x=>$W+1, y=>@$iG+$i, width=>$w);         # Position non io gate
    $W   += $w;                                                                 # Width of area needed for non io gates
   }

  for my $i(keys @$oG)                                                          # Index of each output gate
   {my $G = $$oG[$i];                                                           # Gate name
    my $g = $$gates{$G};                                                        # Gate
    my %i = $g->inputs ? $g->inputs->%* : ();                                   # Inputs to gate
    my ($d) = values %i;                                                        # The one driver for this gate
    next unless defined $p{$d};
    my $y = $p{$d}->y;
    $p{$G} = newGatePosition(gate=>$g, x=>1+$W, y=>$y, width=>1);               # Position output gate
   }

  my $pageWidth = $W + 2;                                                       # Width of input, output and non io gates as laid out.

  if (defined($title))                                                          # Title if known
   {$s->text(x=>$pageWidth, y=>0.5, fill=>"darkGreen", text_anchor=>"end",
      stroke_width=>$Fw, font_size=>$Fs,
      cdata=>$title);
   }

  if (defined($steps))                                                          # Number of steps taken if known
   {$s->text(x=>$pageWidth, y=>1.5, fill=>"darkGreen", text_anchor=>"end",
      stroke_width=>$Fw, font_size=>$Fs,
      cdata=>"$steps steps");
   }

  for my $P(sort keys %p)                                                       # Each gate with text describing it
   {my $p = $p{$P};
    my $x = $p->x;
    my $y = $p->y;
    my $w = $p->width;
    my $g = $p->gate;

    my $color = sub
     {return "red"  if $g->io == gateOuterOutput;
      return "blue" if $g->io == gateOuterInput;
      "green"
     }->();

    if ($g->io)                                                                 # Circle for io pin
     {$s->circle(cx=>$x+1/2, cy=>$y+1/2, r=>1/2,   fill=>$op0, stroke=>$color);
     }
    else                                                                        # Rectangle for non io gate
     {$s->rect(x=>$x, y=>$y, width=>$w, height=>1, fill=>$op0, stroke=>$color);
     }

    if (defined(my $v = $$values{$g->output}))                                  # Value of gate if known
     {$s->text(
       x                 => $g->io != gateOuterOutput ? $x : $x + 1,
       y                 => $y,
       fill              =>"black",
       stroke_width      =>$Fw, font_size=>$Fs,
       text_anchor       => $g->io != gateOuterOutput ? "start": "end",
       dominant_baseline => "hanging",
       cdata             => $v ? "1" : "0");
     }

    my sub ot($$$$)                                                             # Output svg text
     {my ($dy, $fill, $pos, $text) = @_;
      $s->text(x                 => $x+$w/2,
               y                 => $y+$dy,
               fill              => $fill,
               text_anchor       => "middle",
               dominant_baseline => $pos,
               cdata             => $text);
      }

    ot(5/12, "red",      "auto",    $g->type);                                  # Type of gate
    ot(7/12, "darkblue", "hanging", $g->output);

    if ($g->io != gateOuterInput)                                               # Not an input pin
     {my %i = $g->inputs ? $g->inputs->%* : ();
      my @i = sort values %i;                                                   # Connections to each gate
      my $o = $g->output;

      for my $i(keys @i)                                                        # Connections to each gate
       {my $P = $p{$i[$i]};                                                     # Source gate
        defined($P) or confess <<"END";
No such gate as: '$i[$i]' on gate $o
END
        my $X = $P->x; my $Y = $P->y; my $W = $P->width; my $G = $P->gate;      # Position of gate
        my $dx = $i + 1/2;
        my $dy = $Y < $y ?  0 : 1;
        my $dX = $X < $x ? $W : 0;
        my $dY = $Y < $y ?  0 : 0;
        my $cx = $x+$dx;                                                        # Horizontal line corner x
        my $cy = $Y+$dY+1/2;                                                    # Horizontal line corner y

        my $xc = $X < $x ? q(black) : q(darkBlue);                              # Horizontal line color
        my $x2 = $g->io == gateOuterOutput ? $cx - 1/2 : $cx;
        $s->line(x1=>$X+$dX, x2=>$x2, y1=>$cy, y2=>$cy,    stroke=>$xc);        # Outgoing value along horizontal lines

        my $yc = $Y < $y ? q(purple) : q(darkRed);                              # Vertical lines

        if ($g->io != gateOuterOutput)                                          # Incoming value along vertical line - not needed for outer output gates
         {$s->line(x1=>$cx,   x2=>$cx, y1=>$cy, y2=>$y+$dy, stroke=>$yc);
          $s->circle(cx=>$cx, cy=>$cy,    r=>0.06, fill=>"red");                # Line corner
          $s->circle(cx=>$x2, cy=>$y+$dy, r=>0.04, fill=>"blue");               # Line entering gate
         }
        else                                                                    # External output gate
         {$s->circle(cx=>$x2,   cy=>$y+$dy-1/2, r=>0.04, fill=>"blue");         # Line entering output
         }

        $s->circle(cx=>$X+$W, cy=>$cy,    r=>0.04, fill=>"red");                # Line exiting gate

        if (defined(my $v = $$values{$G->output}) and $g->io != gateOuterOutput)# Value of gate if known except for output gates written else where
         {$s->text(
            x           => $cx,
            y           => $y+$dy+($X < $x ? 0.1 : -0.1),
            fill        => "black", stroke_width=>$fw, font_size=>$fs,
            text_anchor => "middle",
            $X < $x ? (dominant_baseline=>"hanging") : (),
            cdata       =>  $v ? "1" : "0");
         }
       }
     }
   }
  my $t = $s->print;
  return owf(fpe($options{svg}, q(svg)), $t) if $options{svg};
  $t
 }

sub Silicon::Chip::Simulation::printSvg($%)                                     # Print simulation results as svg.
 {my ($sim, %options) = @_;                                                     # Simulation, options
  printSvg($sim->chip, %options, values=>$sim->values);
 }

#D1 Basic Circuits                                                              # Some well known basic circuits.

sub n(*$)                                                                       # Gate name from single index.
 {my ($c, $i) = @_;                                                             # Gate name, bit number
  !@_ or !ref($_[0]) or confess <<"END";
Call as a sub not as a method
END
  "${c}_$i"
 }

sub nn(*$$)                                                                     # Gate name from double index.
 {my ($c, $i, $j) = @_;                                                         # Gate name, word number, bit number
  !@_ or !ref($_[0]) or confess confess <<"END";
Call as a sub not as a method
END
 "${c}_${i}_$j"
 }

#D2 Comparisons                                                                 # Compare unsigned binary integers of specified bit widths.

sub compareEq($$$$%)                                                            # Compare two unsigned binary integers of a specified width returning B<1> if they are equal else B<0>.
 {my ($chip, $output, $a, $b, %options) = @_;                                   # Chip, name of component also the output bus, first integer, second integer, options
  @_ >= 4 or confess "Four or more parameters";
  my $o  = $output;
  my $A = sizeBits($chip, $a);
  my $B = sizeBits($chip, $b);
  $A == $B or confess <<"END" =~ s/\n(.)/ $1/gsr;
Input $a has width $A but input $b has width $B
END
  $chip->nxor(n("$o.e", $_), n($a, $_), n($b, $_)) for 1..$B;                   # Test each bit pair for equality
  $chip->andBits($o, "$o.e", bits=>$B);                                         # All bits must be equal

  $chip
 }

sub compareGt($$$$%)                                                            # Compare two unsigned binary integers and return B<1> if the first integer is more than B<b> else B<0>.
 {my ($chip, $output, $a, $b, %options) = @_;                                   # Chip, name of component also the output bus, first integer, second integer, options
  @_ >= 4 or confess "Four or more parameters";
  my $o  = $output;
  my $A = sizeBits($chip, $a);
  my $B = sizeBits($chip, $b);
  $A == $B or confess <<"END" =~ s/\n(.)/ $1/gsr;
Input $a has width $A but input $b has width $B
END
  $chip->nxor (n("$o.e", $_), n($a, $_), n($b, $_)) for 2..$B;                  # Test all but the lowest bit pair for equality
  $chip->gt   (n("$o.g", $_), n($a, $_), n($b, $_)) for 1..$B;                  # Test each bit pair for more than

  for my $b(2..$B)                                                              # More than on one bit and all preceding bits are equal
   {$chip->and(n("$o.c", $b),
     {(map {$_=>n("$o.e", $_)} $b..$B), ($b-1)=>n("$o.g", $b-1)});
   }

  $chip->or   ($o, {$B=>n("$o.g", $B),  (map {($_-1)=>n("$o.c", $_)} 2..$B)});  # Any set bit indicates that B<a> is more than B<b>

  $chip
 }

sub compareLt($$$$%)                                                            # Compare two unsigned binary integers B<a>, B<b> of a specified width. Output B<out> is B<1> if B<a> is less than B<b> else B<0>.
 {my ($chip, $output, $a, $b, %options) = @_;                                   # Chip, name of component also the output bus, first integer, second integer, options
  @_ >= 4 or confess "Four or more parameters";

  my $A = sizeBits($chip, $a);
  my $B = sizeBits($chip, $b);
  $A == $B or confess <<"END" =~ s/\n(.)/ $1/gsr;
Input $a has width $A but input $b has width $B
END

  my $o = $output;

  $chip->nxor (n("$o.e", $_), n($a, $_), n($b, $_)) for 2..$B;                  # Test all but the lowest bit pair for equality
  $chip->lt   (n("$o.l", $_), n($a, $_), n($b, $_)) for 1..$B;                  # Test each bit pair for less than

  for my $b(2..$B)                                                              # More than on one bit and all preceding bits are equal
   {$chip->and(n("$o.c", $b),
     {(map {$_=>n("$o.e", $_)} $b..$B), ($b-1)=>n("$o.l", $b-1)});
   }

  $chip->or   ($o, {$B=>n("$o.l", $B),  (map {($_-1)=>n("$o.c", $_)} 2..$B)});  # Any set bit indicates that B<a> is less than B<b>

  $chip
 }

sub chooseFromTwoWords($$$$$%)                                                  # Choose one of two words based on a bit.  The first word is chosen if the bit is B<0> otherwise the second word is chosen.
 {my ($chip, $output, $a, $b, $choose, %options) = @_;                          # Chip, name of component also the chosen word, the first word, the second word, the choosing bit, options
  @_ >= 5 or confess "Five or more parameters";
  my $o = $output;

  my $A = sizeBits($chip, $a);
  my $B = sizeBits($chip, $b);
  $A == $B or confess <<"END" =~ s/\n(.)/ $1/gsr;
Input $a has width $A but input $b has width $B
END

  $chip->not("$o.n", $choose);                                                  # Not of the choosing bit
  for my $i(1..$B)
   {$chip->and(n("$o.a", $i), [n($a, $i),     "$o.n"       ]);                  # Choose first word
    $chip->and(n("$o.b", $i), [n($b, $i),     $choose      ]);                  # Choose second word
    $chip->or (n($o,     $i), [n("$o.a", $i), n("$o.b", $i)]);                  # Or results of choice
   }
  setSizeBits($chip, $o, $B);                                                   # Record bus size

  $chip
 }

sub enableWord($$$$%)                                                           # Output a word or zeros depending on a choice bit.  The first word is chosen if the choice bit is B<1> otherwise all zeroes are chosen.
 {my ($chip, $output, $a, $enable, %options) = @_;                              # Chip, name of component also the chosen word, the first word, the second word, the choosing bit, options
  @_ >= 4 or confess "Four or more parameters";
  my $o = $output;

  my $B = sizeBits($chip, $a);

  $chip->not ("$o.n", $enable);                                                 # Not of the choosing bit
  $chip->bits("$o.z", $B, 0);                                                   # Zero value to transmit if choice bit is B<0>
  for my $i(1..$B)
   {$chip->and(n("$o.a", $i), [n($a,     $i), $enable      ]);                  # Choose second word
    $chip->and(n("$o.b", $i), [n("$o.z", $i),   "$o.n"     ]);                  # Choose first word
    $chip->or (n( $o,    $i), [n("$o.a", $i), n("$o.b", $i)]);                  # Or results of choice
   }
  setSizeBits($chip, $o, $B);                                                   # Record bus size
  $chip
 }

#D2 Masks                                                                       # Point masks and monotone masks. A point mask has a single B<1> in a sea of B<0>s as in B<00100>.  A monotone mask has zero or more B<0>s followed by all B<1>s as in: B<00111>.

sub pointMaskToInteger($$$%)                                                    # Convert a mask B<i> known to have at most a single bit on - also known as a B<point mask> - to an output number B<a> representing the location in the mask of the bit set to B<1>. If no such bit exists in the point mask then output number B<a> is B<0>.
 {my ($chip, $output, $input, %options) = @_;                                   # Chip, output name, input mask, options
  @_ >= 3 or confess "Three or more parameters";
  my $B = sizeBits($chip, $input);                                              # Bits in input bus
  my $I = containingPowerOfTwo($B);                                             # Bits in integer
  my $i = $input;                                                               # The bits in the input mask
  my $o = $output;                                                              # The name of the output bus

  my %b;
  for my $b(1..$B)                                                              # Bits in mask to bits in resulting number
   {my $s = sprintf "%b", $b;
    for my $p(1..length($s))
     {$b{$p}{$b}++ if substr($s, -$p, 1);
     }
   }

  for my $b(sort keys %b)
   {$chip->or(n($o, $b), {map {$_=>n($i, $_)} sort keys $b{$b}->%*});           # Bits needed to drive a bit in the resulting number
   }
  setSizeBits $chip, $o, $I;                                                    # Size of resulting integer
  $chip
 }

sub integerToPointMask($$$%)                                                    # Convert an integer B<i> of specified width to a point mask B<m>. If the input integer is B<0> then the mask is all zeroes as well.
 {my ($chip, $output, $input, %options) = @_;                                   # Chip, output name, input mask, options
  @_ >= 3 or confess "Three or more parameters";
  my $bits = sizeBits($chip, $input);                                           # Size of input integer in bits
  my $B = 2**$bits-1;
  my $o = $output;                                                              # Output mask

  $chip->notBits("$o.n", $input);                                               # Not of each input

  for my $b(1..$B)                                                              # Each bit of the mask
   {my @s = reverse split //, sprintf "%0${bits}b", $b;                         # Bits for this point in the mask
    my %a;
    for my $i(1..@s)
     {$a{$i} = n($s[$i-1] ? 'i' : "$o.n", $i);                                  # Combination of bits to enable this mask bit
     }
    $chip->and(n($output, $b), {%a});                                           # And to set this point in the mask
   }
  setSizeBits($chip, $output, $B);                                              # Size of output bus

  $chip
 }

sub monotoneMaskToInteger($$$%)                                                 # Convert a monotone mask B<i> to an output number B<r> representing the location in the mask of the bit set to B<1>. If no such bit exists in the point then output in B<r> is B<0>.
 {my ($chip, $output, $input, %options) = @_;                                   # Chip, output name, input mask, options
  @_ >= 3 or confess "Three or more parameters";
  my $B = sizeBits($chip, $input);
  my $I = containingPowerOfTwo($B);
  my $o = $output;

  my %b;
  for my $b(1..$B)
   {my $s = sprintf "%b", $b;
    for my $p(1..length($s))
     {$b{$p}{$b}++ if substr($s, -$p, 1);
     }
   }
  $chip->not     (n("$o.n", $_), n($input, $_)) for 1..$B-1;                    # Not of each input
  $chip->continue(n("$o.a", 1),  n($input, 1));
  $chip->and     (n("$o.a", $_), [n("$o.n", $_-1), n('i', $_)]) for 2..$B;      # Look for trailing edge

  for my $b(sort keys %b)
   {$chip->or    (n($o, $b), [map {n("$o.a", $_)} sort keys $b{$b}->%*]);       # Bits needed to drive a bit in the resulting number
   }
  setSizeBits($chip, $o, $I);

  $chip
 }

sub monotoneMaskToPointMask($$$%)                                               # Convert a monotone mask B<i> to a point mask B<o> representing the location in the mask of the first bit set to B<1>. If the monotone mask is all B<0>s then point mask is too.
 {my ($chip, $output, $input, %options) = @_;                                   # Chip, output name, input mask, options
  @_ >= 3 or confess "Three or more parameters";
  my $o = $output;
  my $bits = sizeBits($chip, $input);
  $chip->continue(n($o, 1), n($input, 1));                                      # The first bit in the monotone mask matches the first bit of the point mask
  for my $b(2..$bits)
   {$chip->xor(n($o, $b), n($input, $b-1), n($input, $b));                      # Detect transition
   }
  setSizeBits($chip, $o, $bits);

  $chip
 }

sub integerToMonotoneMask($$$%)                                                 # Convert an integer B<i> of specified width to a monotone mask B<m>. If the input integer is B<0> then the mask is all zeroes.  Otherwise the mask has B<i-1> leading zeroes followed by all ones thereafter.
 {my ($chip, $output, $input, %options) = @_;                                   # Chip, output name, input mask, options
  @_ >= 3 or confess "Three or more parameters";
  my $I = sizeBits($chip, $input);
  my $B = 2**$I-1;
  my $o = $output;

  $chip->notBits("$o.n", $input);                                               # Not of each input

  for my $b(1..$B)                                                              # Each bit of the mask
   {my @s = (reverse split //, sprintf "%0${I}b", $b);                          # Bits for this point in the mask
    my %a;
    for  my $i(1..@s)
     {$a{$i} = n($s[$i-1] ? $input : "$o.n", $i);                               # Choose either the input bit or the not of the input but depending on the number being converted to binary
     }
    $chip->and(n("$o.a", $b), {%a});                                            # Set at this point and beyond
    $chip-> or(n($o, $b), [map {n("$o.a", $_)} 1..$b]);                         # Set mask
   }
  setSizeBits($chip, $o, $B);
  $chip
 }

sub chooseWordUnderMask($$$$%)                                                  # Choose one of a specified number of words B<w>, each of a specified width, using a point mask B<m> placing the selected word in B<o>.  If no word is selected then B<o> will be zero.
 {my ($chip, $output, $input, $mask, %options) = @_;                            # Chip, output, inputs, mask, options
  @_ >= 3 or confess "Three or more parameters";
  my $o = $output;

  my ($words, $bits) = sizeWords($chip, $input);
  my ($mi)           = sizeBits ($chip, $mask);
  $mi == $words or confess <<"END" =~ s/\n(.)/ $1/gsr;
Mask width $mi does not match number of words $words.
END

  for   my $w(1..$words)                                                        # And each bit of each word with the mask
   {for my $b(1..$bits)                                                         # Bits in each word
     {$chip->and(nn("$o.a", $w, $b), [n($mask, $w), nn($input, $w, $b)]);
     }
   }

  for   my $b(1..$bits)                                                         # Bits in each word
   {$chip->or(n($o, $b), [map {nn("$o.a", $_, $b)} 1..$words]);
   }
  setSizeBits($chip, $o, $bits);

  $chip
 }

sub findWord($$$$%)                                                             # Choose one of a specified number of words B<w>, each of a specified width, using a key B<k>.  Return a point mask B<o> indicating the locations of the key if found or or a mask equal to all zeroes if the key is not present.
 {my ($chip, $output, $key, $words, %options) = @_;                             # Chip, found point mask, key, words to search, options
  @_ >= 4 or confess "Four or more parameters";
  my $o = $output;
  my ($W, $B) = sizeWords($chip, $words);
  my $bits    = sizeBits ($chip, $key);
  $B == $bits or confess <<"END" =~ s/\n(.)/ $1/gsr;
Number of bits in each word $B differs from words in key $bits.
END
  for   my $w(1..$W)                                                            # Input words
   {$chip->compareEq(n($o, $w), n($words, $w), $key);                           # Compare each input word with the key to make a mask
   }
  setSizeBits($chip, $o, $W);

  $chip
 }

#D1 Simulate                                                                    # Simulate the behavior of the L<chip> given a set of values on its input gates.

my sub setBit($*$%)                                                             # Set a single bit
 {my ($chip, $name, $value, %options) = @_;                                     # Chip, name of input gates, number to set to, options
  @_ >= 3 or confess "Three or more parameters";
  my $g = getGate($chip, $name);
  my $t = $g->type;
  $g->io == gateOuterInput or confess <<"END" =~ s/\n(.)/ $1/gsr;
Only outer input gates are setable. Gate $name is of type $t
END
  my %i = ($name => $value ? $value : 0);
  %i
 }

sub setBits($*$%)                                                               # Set an array of input gates to a number prior to running a simulation.
 {my ($chip, $name, $value, %options) = @_;                                     # Chip, name of input gates, number to set to, options
  @_ >= 3 or confess "Three or more parameters";
  my $bits = sizeBits($chip, $name);                                            # Size of bus
  my $W = 2**$bits;
  $value >= 0 or confess <<"END";
Value $value is less than 0
END
  $value < $W or confess <<"END";
Value $value is greater then or equal to $W
END
  my @b = reverse split //,  sprintf "%0${bits}b", $value;
  my %i = map {n($name, $_) => $b[$_-1]} 1..$bits;
  %i
 }

sub setWords($$@)                                                               # Set an array of arrays of gates to an array of numbers prior to running a simulation.
 {my ($chip, $name, @values) = @_;                                              # Chip, name of input gates, number of bits in each array element, numbers to set to
  @_ >= 3 or confess "Three or more parameters";
  my ($words, $bits) = sizeWords($chip, $name);
  my %i;
  my $W = 2**$words;
  for   my $w(1..$words)                                                        # Each word
   {my $n = shift @values;
    $n >= 0 or confess <<"END";
Value $n is less than 0
END
    $n < $W or confess <<"END";
 "Value $n is greater then or equal to $W";
END
    my @b = split //,  sprintf "%0${bits}b", $n;
    for my $b(1..$bits)                                                         # Each bit
     {$i{nn($name, $w, $b)} = $b[-$b];
     }
   }
  %i
 }

sub connectBits($*$*%)                                                          # Create a connection list connecting a set of output bits on the one chip to a set of input bits on another chip.
 {my ($oc, $o, $ic, $i, %options) = @_;                                         # First chip, name of gates on first chip, second chip, names of gates on second chip, options
  @_ >= 4 or confess "Four or more parameters";
  my %c;
  my $O = sizeBits($oc, $o);
  my $I = sizeBits($ic, $i);
  $O == $I or confess <<"END";
Mismatch between size of bus $o at $O and bus $i at $I
END

  for my $b(1..$I)                                                              # Bit to connect
   {$c{n($o, $b)} = n($i, $b);                                                  # Connect bits
   }
  %c                                                                            # Connection list
 }

sub connectWords($*$*$$%)                                                       # Create a connection list connecting a set of words on the outer chip to a set of words on the inner chip.
 {my ($oc, $o, $ic, $i, $words, $bits, %options) = @_;                          # First chip, name of gates on first chip, second chip, names of gates on second chip, number of words to connect, options
  @_ >= 6 or confess "Six or more parameters";
  my %c;
  for   my $w(1..$bits)                                                         # Word to connect
   {for my $b(1..$bits)                                                         # Bit to connect
     {$c{nn($o, $w, $b)} = nn($i, $w, $b);                                      # Connection list
     }
   }
  %c                                                                            # Connection list
 }

my sub merge($%)                                                                # Merge a L<chip> and all its sub L<chips> to make a single L<chip>.
 {my ($chip, %options) = @_;                                                    # Chip, options

  my $gates = getGates $chip;                                                   # Gates implementing the chip and all of its sub chips
  setOuterGates ($chip, $gates);                                                # Set the outer gates which are to be connected to in the real word
  removeExcessIO($chip, $gates);                                                # By pass and then remove all interior IO gates as they are no longer needed

  my $c = newChip %$chip, %options, gates=>$gates, installs=>[];                # Create the new chip with all installs expanded
  print($c, %options)     if $options{print};                                   # Print the gates
  printSvg ($c, %options) if $options{svg};                                     # Draw the gates using svg
  checkIO $c;                                                                   # Check all inputs are connected to valid gates and that all outputs are used

  $c
 }

my sub simulationResults($%)                                                    # Simulation results obtained by specifying the inputs to all the L<lgs> on the L<chip> and allowing its output L<lgs> to stabilize.
 {my ($chip, %options) = @_;                                                    # Chip, hash of final values for each gate, options

  genHash("Silicon::Chip::Simulation",                                          # Simulation results
    chip    => $chip,                                                           # Chip being simulated
    changed => $options{changed},                                               # Last time this gate changed
    steps   => $options{steps},                                                 # Number of steps to reach stability
    values  => $options{values},                                                # Values of every output at point of stability
    svg     => $options{svg},                                                   # Name of file containing svg drawing if requested
   );
 }

sub Silicon::Chip::Simulation::value($$%)                                       # Get the value of a gate as seen in a simulation.
 {my ($simulation, $name, %options) = @_;                                       # Chip, gate, options
  @_ >= 2 or confess "Two or more parameters";
  $simulation->values->{$name}                                                  # Value of gate
 }

my sub checkInputs($$%)                                                         # Check that an input value has been provided for every input pin on the chip.
 {my ($chip, $inputs, %options) = @_;                                           # Chip, inputs, hash of final values for each gate, options

  for my $g(values $chip->gates->%*)                                            # Each gate on chip
   {if   ($g->io == gateOuterInput)                                             # Outer input gate
     {my ($i) = values $g->inputs->%*;                                          # Inputs
      if (!defined($$inputs{$i}))                                               # Check we have a corresponding input
       {my $n = $g->output;
        confess "No input value for input gate: $n\n";
       }
     }
   }
 }

sub Silicon::Chip::Simulation::bint($$%)                                        # Represent the state of bits in the simulation results as an unsigned binary integer.
 {my ($simulation, $output, %options) = @_;                                     # Chip, name of gates on bus, options
  @_ >= 2 or confess "Two or more parameters";
  my $B = sizeBits($simulation->chip, $output);
  my %v = $simulation->values->%*;
  my @b;
  for my $b(1..$B)                                                              # Bits
   {push @b, $v{n $output, $b};
   }

  eval join '', '0b', reverse @b;                                               # Convert to number
 }

sub Silicon::Chip::Simulation::wordsToInteger($$%)                              # Represent the state of words in the simulation results as an array of unsigned binary integer.
 {my ($simulation, $output, %options) = @_;                                     # Chip, name of gates on bus, options
  @_ >= 2 or confess "Two or more parameters";
  my ($words, $bits) = sizeWords($simulation->chip, $output);
  my %v = $simulation->values->%*;
  my @w;
  for my $w(1..$words)                                                          # Words
   {my @b;
    for my $b(1..$bits)                                                         # Bits
     {push @b, $v{nn $output, $w, $b};
     }

    push @w,  eval join '', '0b', reverse @b;                                   # Convert to number
   }
  @w
 }

sub Silicon::Chip::Simulation::wordXToInteger($$%)                              # Represent the state of words in the simulation results as an array of unsigned binary integer.
 {my ($simulation, $output, %options) = @_;                                     # Chip, name of gates on bus, options
  @_ >= 2 or confess "Two or more parameters";
  my ($words, $bits) = sizeWords($simulation->chip, $output);
  my %v = $simulation->values->%*;
  my @w;
  for my $b(1..$bits)                                                           # Bits
   {my @b;
    for my $w(1..$words)                                                        # Words
     {push @b, $v{nn $output, $w, $b};
     }

    push @w,  eval join '', '0b', reverse @b;                                   # Convert to number
   }
  @w
 }

my sub simulationStep($$%)                                                      # One step in the simulation of the L<chip> after expansion of inner L<chips>.
 {my ($chip, $values, %options) = @_;                                           # Chip, current value of each gate, options
  my $gates = $chip->gates;                                                     # Gates on chip
  my %changes;                                                                  # Changes made

  for my $G(sort {$$gates{$a}->seq <=> $$gates{$b}->seq} keys %$gates)          # Each gate in sub chip on definition order to get a repeatable order
   {my $g = $$gates{$G};                                                        # Address gate
    my $t = $g->type;                                                           # Gate type
    my $n = $g->output;                                                         # Gate name
    my %i = $g->inputs->%*;                                                     # Inputs to gate
    my @i = map {$$values{$i{$_}}} sort keys %i;                                # Values of inputs to gates in input pin name order

    my $u = 0;                                                                  # Number of undefined inputs
    for my $i(@i)
     {++$u unless defined $i;
     }

    if ($u == 0)                                                                # All inputs defined
     {my $r;                                                                    # Result of gate operation
      if ($t =~ m(\Aand|nand\Z)i)                                               # Elaborate and B<and> and B<nand> gates
       {my $z = grep {!$_} @i;                                                  # Count zero inputs
        $r = $z ? 0 : 1;
        $r = !$r if $t =~ m(\Anand\Z)i;
       }
      elsif ($t =~ m(\A(input)\Z)i)                                             # An B<input> gate takes its value from the list of inputs or from an output gate in an inner chip
       {if (my @i = values $g->inputs->%*)                                      # Get the value of the input gate from the current values
         {my $n = $i[0];
             $r = $$values{$n};
         }
        else
         {confess "No driver for input gate: $n\n";
         }
       }
      elsif ($t =~ m(\A(continue|nor|not|or|output)\Z)i)                        # Elaborate B<not>, B<or> or B<output> gate. A B<continue> gate places its single input unchanged on its output
       {my $o = grep {$_} @i;                                                   # Count one inputs
        $r = $o ? 1 : 0;
        $r = $r ? 0 : 1 if $t =~ m(\Anor|not\Z)i;
       }
      elsif ($t =~ m(\A(nxor|xor)\Z)i)                                          # Elaborate B<xor>
       {@i == 2 or confess "$t gate: '$n' must have exactly two inputs\n";
        $r = $i[0] ^ $i[1] ? 1 : 0;
        $r = $r ? 0 : 1 if $t =~ m(\Anxor\Z)i;
       }
      elsif ($t =~ m(\A(gt|ngt)\Z)i)                                            # Elaborate B<a> more than B<b> - the input pins are assumed to be sorted by name with the first pin as B<a> and the second as B<b>
       {@i == 2 or confess "$t gate: '$n' must have exactly two inputs\n";
        $r = $i[0] > $i[1] ? 1 : 0;
        $r = $r ? 0 : 1 if $t =~ m(\Angt\Z)i;
       }
      elsif ($t =~ m(\A(lt|nlt)\Z)i)                                            # Elaborate B<a> less than B<b> - the input pins are assumed to be sorted by name with the first pin as B<a> and the second as B<b>
       {@i == 2 or confess "$t gate: '$n' must have exactly two inputs\n";
        $r = $i[0] < $i[1] ? 1 : 0;
        $r = $r ? 0 : 1 if $t =~ m(\Anlt\Z)i;
       }
      elsif ($t =~ m(\Aone\Z)i)                                                 # One
       {@i == 0 or confess "$t gate: '$n' must have no inputs\n";
        $r = 1;
       }
      elsif ($t =~ m(\Azero\Z)i)                                                # Zero
       {@i == 0 or confess "$t gate: '$n' must have no inputs\n";
        $r = 0;
       }
      else                                                                      # Unknown gate type
       {confess "Need implementation for '$t' gates";
       }
      $changes{$G} = $r unless defined($$values{$G}) and $$values{$G} == $r;    # Value computed by this gate
     }
   }
  %changes
 }

sub simulate($$%)                                                               # Simulate the action of the L<lgs> on a L<chip> for a given set of inputs until the output value of each L<lg> stabilizes.
 {my ($chip, $inputs, %options) = @_;                                           # Chip, Hash of input names to values, options
  @_ >= 2 or confess "Two or more parameters";
  my $c = merge($chip, %options);                                               # Merge all the sub chips to make one chip with no sub chips
  checkInputs($c, $inputs);                                                     # Confirm that there is an input value for every input to the chip

  my %values = %$inputs;                                                        # The current set of values contains just the inputs at the start of the simulation
  my %changed;                                                                  # Last step on which this gate changed.  We use this to order the gates on layout

  my $T = maxSimulationSteps;                                                   # Maximum steps
  for my $t(0..$T)                                                              # Steps in time
   {my %changes = simulationStep $c, \%values;                                  # Changes made

    if (!keys %changes)                                                         # Keep going until nothing changes
     {my $svg;
      if ($options{svg})                                                        # Draw the gates using svg with the final values attached
       {$svg = printSvg $c, values=>\%values, changed=>\%changed,
                        steps=>$t, %options;
       }
      return simulationResults $chip, values=>\%values, changed=>\%changed,     # Keep going until nothing changes
               steps=>$t, svg=>$svg;
     }

    for my $c(keys %changes)                                                    # Update state of circuit
     {$values{$c} = $changes{$c};
      $changed{$c} = $t;                                                        # Last time we changed this gate
     }
   }

  confess "Out of time after $T steps";                                         # Not enough steps available
 }

#-------------------------------------------------------------------------------
# Export
#-------------------------------------------------------------------------------

use Exporter qw(import);

use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);

# containingFolder

@ISA          = qw(Exporter);
@EXPORT       = qw();
@EXPORT_OK    = qw(connectBits connectWords n nn setBits setWords);
%EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);

#Images https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/

=pod

=encoding utf-8

=for html <p><a href="https://github.com/philiprbrenan/SiliconChip"><img src="https://github.com/philiprbrenan/SiliconChip/workflows/Test/badge.svg"></a>

=head1 Name

Silicon::Chip - Design a L<silicon|https://en.wikipedia.org/wiki/Silicon> L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> by combining L<logic gates|https://en.wikipedia.org/wiki/Logic_gate> and sub L<chips|https://en.wikipedia.org/wiki/Integrated_circuit>.

=head1 Synopsis

Create a chip to compare two 4 bit big endian unsigned integers for equality:

  my $B = 4;                                              # Number of bits

  my $c = Silicon::Chip::newChip(title=>"$B Bit Equals"); # Create chip

  $c->input ("a$_")                       for 1..$B;      # First number
  $c->input ("b$_")                       for 1..$B;      # Second number

  $c->nxor  ("e$_", {1=>"a$_", 2=>"b$_"}) for 1..$B;      # Test each bit for equality
  $c->and   ("and", {map{$_=>"e$_"}           1..$B});    # And tests together to get total equality

  $c->output("out", "and");                               # Output gate

  my $s = $c->simulate({a1=>1, a2=>0, a3=>1, a4=>0,       # Input gate values
                        b1=>1, b2=>0, b3=>1, b4=>0},
                        svg=>"svg/Equals$B");             # Svg drawing of layout

  is_deeply($s->steps,         3);                        # Three steps
  is_deeply($s->values->{out}, 1);                        # Out is 1 for equals

  my $t = $c->simulate({a1=>1, a2=>1, a3=>1, a4=>0,
                        b1=>1, b2=>0, b3=>1, b4=>0});
  is_deeply($t->values->{out}, 0);                        # Out is 0 for not equals

To obtain:

=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/Equals4.svg">

Other circuit diagrams can be seen in folder: L<lib/Silicon/svg|https://github.com/philiprbrenan/SiliconChip/tree/main/lib/Silicon/svg>

=head1 Description

Design a L<silicon|https://en.wikipedia.org/wiki/Silicon> L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> by combining L<logic gates|https://en.wikipedia.org/wiki/Logic_gate> and sub L<chips|https://en.wikipedia.org/wiki/Integrated_circuit>.


Version 20231111.


The following sections describe the methods in each functional area of this
module.  For an alphabetic listing of all methods by name see L<Index|/Index>.



=head1 Construct

Construct a L<Silicon|https://en.wikipedia.org/wiki/Silicon> L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> using standard L<logic gates|https://en.wikipedia.org/wiki/Logic_gate>, components and sub chips combined via buses.

=head2 newChip (%options)

Create a new L<chip|https://en.wikipedia.org/wiki/Integrated_circuit>.

     Parameter  Description
  1  %options   Options

B<Example:>


  if (1)

   {my $c = Silicon::Chip::newChip;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $c->one ("one");
    $c->zero("zero");
    $c->or  ("or",   [qw(one zero)]);
    $c->and ("and",  [qw(one zero)]);
    $c->output("o1", "or");
    $c->output("o2", "and");
    my $s = $c->simulate({}, svg=>q(svg/oneZero));
    is_deeply($s->steps       , 3);
    is_deeply($s->value("o1"), 1);
    is_deeply($s->value("o2"), 0);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/oneZero.svg">

  if (1)                                                                           # Single AND gate

   {my $c = Silicon::Chip::newChip;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $c->input ("i1");
    $c->input ("i2");
    $c->and   ("and1", [qw(i1 i2)]);
    $c->output("o", "and1");
    my $s = $c->simulate({i1=>1, i2=>1});
    ok($s->steps         == 2);
    ok($s->value("and1") == 1);
   }

  if (1)                                                                          # 4 bit equal
   {my $B = 4;                                                                    # Number of bits


    my $c = Silicon::Chip::newChip(title=><<"END");                               # Create chip  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  $B Bit Equals
  END
    $c->input ("a$_")                 for 1..$B;                                  # First number
    $c->input ("b$_")                 for 1..$B;                                  # Second number

    $c->nxor  ("e$_", "a$_", "b$_")   for 1..$B;                                  # Test each bit for equality
    $c->and   ("and", {map{$_=>"e$_"}     1..$B});                                # And tests together to get total equality

    $c->output("out", "and");                                                     # Output gate

    my $s = $c->simulate({a1=>1, a2=>0, a3=>1, a4=>0,                             # Input gate values
                          b1=>1, b2=>0, b3=>1, b4=>0},
                          svg=>q(svg/Equals));                                    # Svg drawing of layout

    is_deeply($s->steps,        3);                                               # Three steps
    is_deeply($s->value("out"), 1);                                               # Out is 1 for equals

    my $t = $c->simulate({a1=>1, a2=>1, a3=>1, a4=>0,
                          b1=>1, b2=>0, b3=>1, b4=>0});
    is_deeply($t->value("out"), 0);                                               # Out is 0 for not equals
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/Equals.svg">


=head2 gate($chip, $type, $output, $input1, $input2)

A L<logic gate|https://en.wikipedia.org/wiki/Logic_gate> chosen from B<and|continue|gt|input|lt|nand|nor|not|nxor|one|or|output|xor|zero>.  The gate type can be used as a method name, so B<-E<gt>gate("and",> can be reduced to B<-E<gt>and(>.

     Parameter  Description
  1  $chip      Chip
  2  $type      Gate type
  3  $output    Output name
  4  $input1    Input from another gate
  5  $input2    Input from another gate

B<Example:>



  if (1)                                                                           # Two AND gates driving an OR gate  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

   {my $c = newChip;
    $c->input ("i11");
    $c->input ("i12");
    $c->and   ("and1", [qw(i11   i12)]);
    $c->input ("i21");
    $c->input ("i22");
    $c->and   ("and2", [qw(i21   i22 )]);
    $c->or    ("or",   [qw(and1  and2)]);
    $c->output( "o", "or");
    my $s = $c->simulate({i11=>1, i12=>1, i21=>1, i22=>1});
    ok($s->steps        == 3);
    ok($s->value("or")  == 1);
       $s  = $c->simulate({i11=>1, i12=>0, i21=>1, i22=>1});
    ok($s->steps        == 3);
    ok($s->value("or")  == 1);
       $s  = $c->simulate({i11=>1, i12=>0, i21=>1, i22=>0});
    ok($s->steps        == 3);
    ok($s->value("o")   == 0);
   }


=head2 connectInput($chip, $in, $to, %options)

Connect a previously defined input gate to the output of another previously gate on the same chip. This allows us to define a set of gates on the chip without having to know, first, all the names of the gates that will provide input to these gates.

     Parameter  Description
  1  $chip      Chip
  2  $in        Input gate
  3  $to        Gate to connect input gate to
  4  %options   Options

B<Example:>


  if (1)                                                                          # Internal input gate
   {my $c = newChip();
       $c->input ('i');                                                           # Input
       $c->input ('j');                                                           # Internal input which we will connect to later
       $c->output(qw(o j));                                                       # Output


       $c->connectInput(qw(j i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my $s = $c->simulate({i=>1});
    is_deeply($s->steps, 1);
    is_deeply($s->value("j"), undef);
    is_deeply($s->value("o"), 1);
   }

  if (1)                                                                           # Internal input gate
   {my @n = qw(3 2 1 2 3);
    my $c = newChip();
       $c->words('i', 2, @n);                                                     # Input
       $c->outputWords(qw(o i));                                                  # Output
    my $s = $c->simulate({});
    is_deeply($s->steps, 2);
    is_deeply([$s->wordsToInteger("i")], [@n]);
   }

  if (1)                                                                           # Internal input gate
   {my @n = qw(3 2 1 2 3);
    my $c = newChip();
       $c->words('i', 2, @n);                                                     # Input
       $c->outputWords(qw(o i));                                                  # Output
    my $s = $c->simulate({});
    is_deeply($s->steps, 2);
    is_deeply([$s->wordsToInteger("i")], [@n]);
   }


=head2 Buses

A bus is an array of bits or an array of arrays of bits

=head3 Bits

An array of bits that can be manipulated via one name.

=head4 setSizeBits ($chip, $name, $bits, %options)

Set the size of a bits bus.

     Parameter  Description
  1  $chip      Chip
  2  $name      Bits bus name
  3  $bits      Options
  4  %options

B<Example:>


  if (1)
   {my $c = newChip();

    $c->setSizeBits ('i', 2);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $c->setSizeWords('j', 3, 2);
    is_deeply($c->sizeBits,  {i => 2, j_1 => 2, j_2 => 2, j_3 => 2});
    is_deeply($c->sizeWords, {j => [3, 2]});
   }


=head4 bits($chip, $name, $bits, $value, %options)

Create a bus set to a specified number.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $bits      Width in bits of bus
  4  $value     Value of bus
  5  %options   Options

B<Example:>


  if (1)
   {my $N = 4;
    for my $i(0..2**$N-1)
     {my $c = Silicon::Chip::newChip;

      $c->bits      ("c", $N, $i);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      $c->outputBits("o", "c");

      my $s = $c->simulate({}, $i == 3 ? (svg=>q(svg/bits)) : ());  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      is_deeply($s->steps, 2);
      is_deeply($s->bint("o"), $i);
     }
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/bits.svg">


=head4 inputBits   ($chip, $name, $bits, %options)

Create an B<input> bus made of bits.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $bits      Width in bits of bus
  4  %options   Options

B<Example:>


  if (1)
   {my $W = 8;
    my $i = newChip(name=>"not");

       $i->inputBits('i',      $W);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $i->notBits  (qw(n i));
       $i->outputBits(qw(o n));

    my $o = newChip(name=>"outer");

       $o->inputBits ('a',     $W);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $o->outputBits(qw(A a));

       $o->inputBits ('b',     $W);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $o->outputBits(qw(B b));

    my %i = connectBits($i, 'i', $o, 'A');
    my %o = connectBits($i, 'o', $o, 'b');
    $o->install($i, {%i}, {%o});

    my %d = setBits($o, 'a', 0b10110);
    my $s = $o->simulate({%d}, svg=>q(svg/not));
    is_deeply($s->bint('B'), 0b11101001);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/not.svg">


=head4 outputBits  ($chip, $name, $input, %options)

Create an B<output> bus made of bits.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Name of inputs
  4  %options   Options

B<Example:>


  if (1)
   {my $W = 8;
    my $i = newChip(name=>"not");
       $i->inputBits('i',      $W);
       $i->notBits  (qw(n i));

       $i->outputBits(qw(o n));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my $o = newChip(name=>"outer");
       $o->inputBits ('a',     $W);

       $o->outputBits(qw(A a));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $o->inputBits ('b',     $W);

       $o->outputBits(qw(B b));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my %i = connectBits($i, 'i', $o, 'A');
    my %o = connectBits($i, 'o', $o, 'b');
    $o->install($i, {%i}, {%o});

    my %d = setBits($o, 'a', 0b10110);
    my $s = $o->simulate({%d}, svg=>q(svg/not));
    is_deeply($s->bint('B'), 0b11101001);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/not.svg">

  if (1)
   {my @B = ((my $W = 4), (my $B = 2));

    my $c = newChip();
       $c->inputWords ('i', @B);
       $c->andWords   (qw(and  i));
       $c->andWordsX  (qw(andX i));
       $c-> orWords   (qw( or  i));
       $c-> orWordsX  (qw( orX i));
       $c->notWords   (qw(n    i));

       $c->outputBits (qw(And  and));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


       $c->outputBits (qw(AndX andX));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


       $c->outputBits (qw(Or   or));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


       $c->outputBits (qw(OrX  orX));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputWords(qw(N    n));
    my %d = setWords($c, 'i', 0b00, 0b01, 0b10, 0b11);
    my $s = $c->simulate({%d}, svg=>"svg/andOrWords$W");

    is_deeply($s->bint('And'),  0b1000);
    is_deeply($s->bint('AndX'), 0b0000);

    is_deeply($s->bint('Or'),  0b1110);
    is_deeply($s->bint('OrX'), 0b11);
    is_deeply([$s->wordsToInteger('N')], [3, 2, 1, 0]);
   }


=head4 notBits ($chip, $name, $input, %options)

Create a B<not> bus made of bits.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Name of inputs
  4  %options   Options

B<Example:>


  if (1)
   {my $W = 8;
    my $i = newChip(name=>"not");
       $i->inputBits('i',      $W);

       $i->notBits  (qw(n i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $i->outputBits(qw(o n));

    my $o = newChip(name=>"outer");
       $o->inputBits ('a',     $W);
       $o->outputBits(qw(A a));
       $o->inputBits ('b',     $W);
       $o->outputBits(qw(B b));

    my %i = connectBits($i, 'i', $o, 'A');
    my %o = connectBits($i, 'o', $o, 'b');
    $o->install($i, {%i}, {%o});

    my %d = setBits($o, 'a', 0b10110);
    my $s = $o->simulate({%d}, svg=>q(svg/not));
    is_deeply($s->bint('B'), 0b11101001);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/not.svg">


=head4 andBits ($chip, $name, $input, %options)

B<and> a bus made of bits.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Name of inputs
  4  %options   Options

B<Example:>


  if (1)
   {my $W = 8;

    my $c = newChip();
       $c-> inputBits('i',         $W);

       $c->   andBits(qw(and  i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->    orBits(qw(or   i));
       $c->  nandBits(qw(nand i));
       $c->   norBits(qw(nor  i));
       $c->output    (qw(And  and));
       $c->output    (qw(Or   or));
       $c->output    (qw(nAnd nand));
       $c->output    (qw(nOr  nor));

    my %d = setBits($c, 'i', 0b10110);
    my $s = $c->simulate({%d}, svg=>q(svg/andOrBits));

    is_deeply($s->value("And"),  0);
    is_deeply($s->value("Or"),   1);
    is_deeply($s->value("nAnd"), 1);
    is_deeply($s->value("nOr"),  0);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/andOrBits.svg">


=head4 nandBits($chip, $name, $input, %options)

B<nand> a bus made of bits.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Name of inputs
  4  %options   Options

B<Example:>


  if (1)
   {my $W = 8;

    my $c = newChip();
       $c-> inputBits('i',         $W);
       $c->   andBits(qw(and  i));
       $c->    orBits(qw(or   i));

       $c->  nandBits(qw(nand i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->   norBits(qw(nor  i));
       $c->output    (qw(And  and));
       $c->output    (qw(Or   or));
       $c->output    (qw(nAnd nand));
       $c->output    (qw(nOr  nor));

    my %d = setBits($c, 'i', 0b10110);
    my $s = $c->simulate({%d}, svg=>q(svg/andOrBits));

    is_deeply($s->value("And"),  0);
    is_deeply($s->value("Or"),   1);
    is_deeply($s->value("nAnd"), 1);
    is_deeply($s->value("nOr"),  0);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/andOrBits.svg">


=head4 orBits  ($chip, $name, $input, %options)

B<or> a bus made of bits.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Options
  4  %options

B<Example:>


  if (1)
   {my $W = 8;

    my $c = newChip();
       $c-> inputBits('i',         $W);
       $c->   andBits(qw(and  i));

       $c->    orBits(qw(or   i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->  nandBits(qw(nand i));
       $c->   norBits(qw(nor  i));
       $c->output    (qw(And  and));
       $c->output    (qw(Or   or));
       $c->output    (qw(nAnd nand));
       $c->output    (qw(nOr  nor));

    my %d = setBits($c, 'i', 0b10110);
    my $s = $c->simulate({%d}, svg=>q(svg/andOrBits));

    is_deeply($s->value("And"),  0);
    is_deeply($s->value("Or"),   1);
    is_deeply($s->value("nAnd"), 1);
    is_deeply($s->value("nOr"),  0);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/andOrBits.svg">


=head4 norBits ($chip, $name, $input, %options)

B<nor> a bus made of bits.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Options
  4  %options

B<Example:>


  if (1)
   {my $W = 8;

    my $c = newChip();
       $c-> inputBits('i',         $W);
       $c->   andBits(qw(and  i));
       $c->    orBits(qw(or   i));
       $c->  nandBits(qw(nand i));

       $c->   norBits(qw(nor  i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->output    (qw(And  and));
       $c->output    (qw(Or   or));
       $c->output    (qw(nAnd nand));
       $c->output    (qw(nOr  nor));

    my %d = setBits($c, 'i', 0b10110);
    my $s = $c->simulate({%d}, svg=>q(svg/andOrBits));

    is_deeply($s->value("And"),  0);
    is_deeply($s->value("Or"),   1);
    is_deeply($s->value("nAnd"), 1);
    is_deeply($s->value("nOr"),  0);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/andOrBits.svg">


=head3 Words

An array of arrays of bits that can be manipulated via one name.

=head4 setSizeWords($chip, $name, $words, $bits, %options)

Set the size of a bits bus.

     Parameter  Description
  1  $chip      Chip
  2  $name      Bits bus name
  3  $words     Words
  4  $bits      Bits per word
  5  %options   Options

B<Example:>


  if (1)
   {my $c = newChip();
    $c->setSizeBits ('i', 2);

    $c->setSizeWords('j', 3, 2);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    is_deeply($c->sizeBits,  {i => 2, j_1 => 2, j_2 => 2, j_3 => 2});
    is_deeply($c->sizeWords, {j => [3, 2]});
   }


=head4 words   ($chip, $name, $bits, @values)

Create a word bus set to specified numbers.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $bits      Width in bits of each word
  4  @values    Values of words

B<Example:>


  if (1)                                                                           # Internal input gate
   {my @n = qw(3 2 1 2 3);
    my $c = newChip();

       $c->words('i', 2, @n);                                                     # Input  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputWords(qw(o i));                                                  # Output
    my $s = $c->simulate({});
    is_deeply($s->steps, 2);
    is_deeply([$s->wordsToInteger("i")], [@n]);
   }

  if (1)                                                                           # Internal input gate
   {my @n = qw(3 2 1 2 3);
    my $c = newChip();

       $c->words('i', 2, @n);                                                     # Input  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputWords(qw(o i));                                                  # Output
    my $s = $c->simulate({});
    is_deeply($s->steps, 2);
    is_deeply([$s->wordsToInteger("i")], [@n]);
   }


=head4 inputWords  ($chip, $name, $words, $bits, %options)

Create an B<input> bus made of words.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $words     Width in words of bus
  4  $bits      Width in bits of each word on bus
  5  %options   Options

B<Example:>


  if (1)
   {my @b = ((my $W = 4), (my $B = 3));

    my $c = newChip();

       $c->inputWords ('i',      @b);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputWords(qw(o i));

    my %d = setWords($c, 'i', 0b000, 0b001, 0b010, 0b011);
    my $s = $c->simulate({%d}, svg=>"svg/words$W");

    is_deeply([$s->wordsToInteger('o')], [0..3]);
    is_deeply([$s->wordXToInteger('o')], [10, 12, 0]);
   }


=head4 outputWords ($chip, $name, $input, %options)

Create an B<output> bus made of words.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Name of inputs
  4  %options   Options

B<Example:>


  if (1)
   {my @b = ((my $W = 4), (my $B = 3));

    my $c = newChip();
       $c->inputWords ('i',      @b);

       $c->outputWords(qw(o i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my %d = setWords($c, 'i', 0b000, 0b001, 0b010, 0b011);
    my $s = $c->simulate({%d}, svg=>"svg/words$W");

    is_deeply([$s->wordsToInteger('o')], [0..3]);
    is_deeply([$s->wordXToInteger('o')], [10, 12, 0]);
   }


=head4 notWords($chip, $name, $input, %options)

Create a B<not> bus made of words.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Name of inputs
  4  %options   Options

B<Example:>


  if (1)
   {my @B = ((my $W = 4), (my $B = 2));

    my $c = newChip();
       $c->inputWords ('i', @B);
       $c->andWords   (qw(and  i));
       $c->andWordsX  (qw(andX i));
       $c-> orWords   (qw( or  i));
       $c-> orWordsX  (qw( orX i));

       $c->notWords   (qw(n    i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputBits (qw(And  and));
       $c->outputBits (qw(AndX andX));
       $c->outputBits (qw(Or   or));
       $c->outputBits (qw(OrX  orX));
       $c->outputWords(qw(N    n));
    my %d = setWords($c, 'i', 0b00, 0b01, 0b10, 0b11);
    my $s = $c->simulate({%d}, svg=>"svg/andOrWords$W");

    is_deeply($s->bint('And'),  0b1000);
    is_deeply($s->bint('AndX'), 0b0000);

    is_deeply($s->bint('Or'),  0b1110);
    is_deeply($s->bint('OrX'), 0b11);
    is_deeply([$s->wordsToInteger('N')], [3, 2, 1, 0]);
   }


=head4 andWords($chip, $name, $input, %options)

B<and> a bus made of words to produce a single word.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Name of inputs
  4  %options   Options

B<Example:>


  if (1)
   {my @B = ((my $W = 4), (my $B = 2));

    my $c = newChip();
       $c->inputWords ('i', @B);

       $c->andWords   (qw(and  i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->andWordsX  (qw(andX i));
       $c-> orWords   (qw( or  i));
       $c-> orWordsX  (qw( orX i));
       $c->notWords   (qw(n    i));
       $c->outputBits (qw(And  and));
       $c->outputBits (qw(AndX andX));
       $c->outputBits (qw(Or   or));
       $c->outputBits (qw(OrX  orX));
       $c->outputWords(qw(N    n));
    my %d = setWords($c, 'i', 0b00, 0b01, 0b10, 0b11);
    my $s = $c->simulate({%d}, svg=>"svg/andOrWords$W");

    is_deeply($s->bint('And'),  0b1000);
    is_deeply($s->bint('AndX'), 0b0000);

    is_deeply($s->bint('Or'),  0b1110);
    is_deeply($s->bint('OrX'), 0b11);
    is_deeply([$s->wordsToInteger('N')], [3, 2, 1, 0]);
   }

  if (1)
   {my @b = ((my $W = 4), (my $B = 3));

    my $c = newChip();
       $c->inputWords ('i',      @b);
       $c->outputWords(qw(o i));

    my %d = setWords($c, 'i', 0b000, 0b001, 0b010, 0b011);
    my $s = $c->simulate({%d}, svg=>"svg/words$W");

    is_deeply([$s->wordsToInteger('o')], [0..3]);
    is_deeply([$s->wordXToInteger('o')], [10, 12, 0]);
   }


=head4 andWordsX   ($chip, $name, $input, %options)

B<and> a bus made of words by and-ing the corresponding bits in each word to make a single word.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Name of inputs
  4  %options   Options

B<Example:>


  if (1)
   {my @B = ((my $W = 4), (my $B = 2));

    my $c = newChip();
       $c->inputWords ('i', @B);
       $c->andWords   (qw(and  i));

       $c->andWordsX  (qw(andX i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c-> orWords   (qw( or  i));
       $c-> orWordsX  (qw( orX i));
       $c->notWords   (qw(n    i));
       $c->outputBits (qw(And  and));
       $c->outputBits (qw(AndX andX));
       $c->outputBits (qw(Or   or));
       $c->outputBits (qw(OrX  orX));
       $c->outputWords(qw(N    n));
    my %d = setWords($c, 'i', 0b00, 0b01, 0b10, 0b11);
    my $s = $c->simulate({%d}, svg=>"svg/andOrWords$W");

    is_deeply($s->bint('And'),  0b1000);
    is_deeply($s->bint('AndX'), 0b0000);

    is_deeply($s->bint('Or'),  0b1110);
    is_deeply($s->bint('OrX'), 0b11);
    is_deeply([$s->wordsToInteger('N')], [3, 2, 1, 0]);
   }


=head4 orWords ($chip, $name, $input, %options)

B<or> a bus made of words to produce a single word.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Name of inputs
  4  %options   Options

B<Example:>


  if (1)
   {my @B = ((my $W = 4), (my $B = 2));

    my $c = newChip();
       $c->inputWords ('i', @B);
       $c->andWords   (qw(and  i));
       $c->andWordsX  (qw(andX i));

       $c-> orWords   (qw( or  i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c-> orWordsX  (qw( orX i));
       $c->notWords   (qw(n    i));
       $c->outputBits (qw(And  and));
       $c->outputBits (qw(AndX andX));
       $c->outputBits (qw(Or   or));
       $c->outputBits (qw(OrX  orX));
       $c->outputWords(qw(N    n));
    my %d = setWords($c, 'i', 0b00, 0b01, 0b10, 0b11);
    my $s = $c->simulate({%d}, svg=>"svg/andOrWords$W");

    is_deeply($s->bint('And'),  0b1000);
    is_deeply($s->bint('AndX'), 0b0000);

    is_deeply($s->bint('Or'),  0b1110);
    is_deeply($s->bint('OrX'), 0b11);
    is_deeply([$s->wordsToInteger('N')], [3, 2, 1, 0]);
   }

  if (1)
   {my @b = ((my $W = 4), (my $B = 3));

    my $c = newChip();
       $c->inputWords ('i',      @b);
       $c->outputWords(qw(o i));

    my %d = setWords($c, 'i', 0b000, 0b001, 0b010, 0b011);
    my $s = $c->simulate({%d}, svg=>"svg/words$W");

    is_deeply([$s->wordsToInteger('o')], [0..3]);
    is_deeply([$s->wordXToInteger('o')], [10, 12, 0]);
   }


=head4 orWordsX($chip, $name, $input, %options)

B<or> a bus made of words by or-ing the corresponding bits in each word to make a single word.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of bus
  3  $input     Name of inputs
  4  %options   Options

B<Example:>


  if (1)
   {my @B = ((my $W = 4), (my $B = 2));

    my $c = newChip();
       $c->inputWords ('i', @B);
       $c->andWords   (qw(and  i));
       $c->andWordsX  (qw(andX i));
       $c-> orWords   (qw( or  i));

       $c-> orWordsX  (qw( orX i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->notWords   (qw(n    i));
       $c->outputBits (qw(And  and));
       $c->outputBits (qw(AndX andX));
       $c->outputBits (qw(Or   or));
       $c->outputBits (qw(OrX  orX));
       $c->outputWords(qw(N    n));
    my %d = setWords($c, 'i', 0b00, 0b01, 0b10, 0b11);
    my $s = $c->simulate({%d}, svg=>"svg/andOrWords$W");

    is_deeply($s->bint('And'),  0b1000);
    is_deeply($s->bint('AndX'), 0b0000);

    is_deeply($s->bint('Or'),  0b1110);
    is_deeply($s->bint('OrX'), 0b11);
    is_deeply([$s->wordsToInteger('N')], [3, 2, 1, 0]);
   }


=head2 Install

Install a chip within a chip as a sub chip.

=head3 install ($chip, $subChip, $inputs, $outputs, %options)

Install a L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> within another L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> specifying the connections between the inner and outer L<chip|https://en.wikipedia.org/wiki/Integrated_circuit>.  The same L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> can be installed multiple times as each L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> description is read only.

     Parameter  Description
  1  $chip      Outer chip
  2  $subChip   Inner chip
  3  $inputs    Inputs of inner chip to to outputs of outer chip
  4  $outputs   Outputs of inner chip to inputs of outer chip
  5  %options   Options

B<Example:>


  if (1)                                                                            # Install one chip inside another chip, specifically one chip that performs NOT is installed once to flip a value
   {my $i = newChip(name=>"not");
       $i-> inputBits('i',     1);
       $i->   notBits(qw(n i));
       $i->outputBits(qw(o n));

    my $o = newChip(name=>"outer");
       $o->inputBits('i', 1); $o->outputBits(qw(n i));
       $o->inputBits('I', 1); $o->outputBits(qw(N I));

    my %i = connectBits($i, 'i', $o, 'n');
    my %o = connectBits($i, 'o', $o, 'I');

    $o->install($i, {%i}, {%o});  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my %d = $o->setBits('i', 1);
    my $s = $o->simulate({%d}, printOff=>"dump/not1", svg=>"svg/not1");

    is_deeply($s->steps,  2);
    is_deeply($s->values, {"(not 1 n_1)"=>0, "i_1"=>1, "N_1"=>0 });
   }


=head1 Visualize

Visualize the L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> in various ways.

=head2 print   ($chip, %options)

Dump the L<logic gates|https://en.wikipedia.org/wiki/Logic_gate> present on a L<chip|https://en.wikipedia.org/wiki/Integrated_circuit>.

     Parameter  Description
  1  $chip      Chip
  2  %options   Gates

B<Example:>


  if (1)
   {my $c = Silicon::Chip::newChip(title=>"And gate");
    $c->input ("i1");
    $c->input ("i2");
    $c->and   ("and1", [qw(i1 i2)]);
    $c->output("o", "and1");
    my $s = $c->simulate({i1=>1, i2=>1});


    is_deeply($c->print, <<END);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  i1                              :     input                           i1
  i2                              :     input                           i2
  and1                            :     and                             i1 i2
  o                               :     output                          and1
  END


    is_deeply($s->print, <<END);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  i1                              :   1 input                           i1
  i2                              :   1 input                           i2
  and1                            :   1 and                             i1 i2
  o                               :   1 output                          and1
  END

    ok($s->printSvg ne $c->printSvg);
   }


=head2 Silicon::Chip::Simulation::print($sim, %options)

Print simulation results as text.

     Parameter  Description
  1  $sim       Simulation
  2  %options   Options

B<Example:>


  if (1)
   {my $c = Silicon::Chip::newChip(title=>"And gate");
    $c->input ("i1");
    $c->input ("i2");
    $c->and   ("and1", [qw(i1 i2)]);
    $c->output("o", "and1");
    my $s = $c->simulate({i1=>1, i2=>1});

    is_deeply($c->print, <<END);
  i1                              :     input                           i1
  i2                              :     input                           i2
  and1                            :     and                             i1 i2
  o                               :     output                          and1
  END

    is_deeply($s->print, <<END);
  i1                              :   1 input                           i1
  i2                              :   1 input                           i2
  and1                            :   1 and                             i1 i2
  o                               :   1 output                          and1
  END

    ok($s->printSvg ne $c->printSvg);
   }


=head2 printSvg($chip, %options)

Dump the L<logic gates|https://en.wikipedia.org/wiki/Logic_gate> on a L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> as an L<Scalar Vector Graphics|https://en.wikipedia.org/wiki/Scalable_Vector_Graphics> drawing to help visualize the structure of the L<chip|https://en.wikipedia.org/wiki/Integrated_circuit>.

     Parameter  Description
  1  $chip      Chip
  2  %options   Options

B<Example:>


  if (1)
   {my $c = Silicon::Chip::newChip(title=>"And gate");
    $c->input ("i1");
    $c->input ("i2");
    $c->and   ("and1", [qw(i1 i2)]);
    $c->output("o", "and1");
    my $s = $c->simulate({i1=>1, i2=>1});

    is_deeply($c->print, <<END);
  i1                              :     input                           i1
  i2                              :     input                           i2
  and1                            :     and                             i1 i2
  o                               :     output                          and1
  END

    is_deeply($s->print, <<END);
  i1                              :   1 input                           i1
  i2                              :   1 input                           i2
  and1                            :   1 and                             i1 i2
  o                               :   1 output                          and1
  END


    ok($s->printSvg ne $c->printSvg);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

   }


=head2 Silicon::Chip::Simulation::printSvg ($sim, %options)

Print simulation results as svg.

     Parameter  Description
  1  $sim       Simulation
  2  %options   Options

B<Example:>


  if (1)
   {my $c = Silicon::Chip::newChip(title=>"And gate");
    $c->input ("i1");
    $c->input ("i2");
    $c->and   ("and1", [qw(i1 i2)]);
    $c->output("o", "and1");
    my $s = $c->simulate({i1=>1, i2=>1});

    is_deeply($c->print, <<END);
  i1                              :     input                           i1
  i2                              :     input                           i2
  and1                            :     and                             i1 i2
  o                               :     output                          and1
  END

    is_deeply($s->print, <<END);
  i1                              :   1 input                           i1
  i2                              :   1 input                           i2
  and1                            :   1 and                             i1 i2
  o                               :   1 output                          and1
  END

    ok($s->printSvg ne $c->printSvg);
   }


=head1 Basic Circuits

Some well known basic circuits.

=head2 n   ($c, $i)

Gate name from single index.

     Parameter  Description
  1  $c         Gate name
  2  $i         Bit number

B<Example:>


  if (1)

   {is_deeply( n(a,1),   "a_1");  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    is_deeply(nn(a,1,2), "a_1_2");
   }


=head2 nn  ($c, $i, $j)

Gate name from double index.

     Parameter  Description
  1  $c         Gate name
  2  $i         Word number
  3  $j         Bit number

B<Example:>


  if (1)
   {is_deeply( n(a,1),   "a_1");

    is_deeply(nn(a,1,2), "a_1_2");  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

   }


=head2 Comparisons

Compare unsigned binary integers of specified bit widths.

=head3 compareEq   ($chip, $output, $a, $b, %options)

Compare two unsigned binary integers of a specified width returning B<1> if they are equal else B<0>.

     Parameter  Description
  1  $chip      Chip
  2  $output    Name of component also the output bus
  3  $a         First integer
  4  $b         Second integer
  5  %options   Options

B<Example:>


  if (1)                                                                           # Compare unsigned integers
   {my $B = 2;

    my $c = Silicon::Chip::newChip(title=><<"END");
  $B Bit Compare Equal
  END
    $c->inputBits($_, $B) for qw(a b);                                            # First and second numbers

    $c->compareEq(qw(o a b));                                                     # Compare equals  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $c->output   (qw(out o));                                                     # Comparison result

    for   my $i(0..2**$B-1)                                                       # Each possible number
     {for my $j(0..2**$B-1)                                                       # Each possible number
       {my %a = $c->setBits('a', $i);                                             # Number a
        my %b = $c->setBits('b', $j);                                             # Number b

        my $s = $c->simulate({%a, %b}, $i==1&&$j==1?(svg=>"svg/CompareEq$B"):()); # Svg drawing of layout

        is_deeply($s->value("out"), $i == $j ? 1 : 0);                            # Equal
        is_deeply($s->steps, 3);                                                  # Number of steps to stability
       }
     }
   }


=head3 compareGt   ($chip, $output, $a, $b, %options)

Compare two unsigned binary integers and return B<1> if the first integer is more than B<b> else B<0>.

     Parameter  Description
  1  $chip      Chip
  2  $output    Name of component also the output bus
  3  $a         First integer
  4  $b         Second integer
  5  %options   Options

B<Example:>


  if (1)                                                                           # Compare 8 bit unsigned integers 'a' > 'b' - the pins used to input 'a' must be alphabetically less than those used for 'b'
   {my $B = 3;
    my $c = Silicon::Chip::newChip(title=><<END);
  $B Bit Compare more than
  END
    $c->inputBits($_, $B) for qw(a b);                                            # First and second numbers

    $c->compareGt(qw(o a b));                                                     # Compare more than  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $c->output   (qw(out o));                                                     # Comparison result

    for   my $i(0..2**$B-1)                                                       # Each possible number
     {for my $j(0..2**$B-1)                                                       # Each possible number
       {my %a = $c->setBits('a', $i);                                             # Number a
        my %b = $c->setBits('b', $j);                                             # Number b

        my $s = $c->simulate({%a, %b}, $i==2&&$j==1?(svg=>"svg/CompareGt$B"):()); # Svg drawing of layout
        is_deeply($s->value("out"), $i > $j ? 1 : 0);                             # More than
        is_deeply($s->steps, 4);                                                  # Number of steps to stability
       }
     }
   }


=head3 compareLt   ($chip, $output, $a, $b, %options)

Compare two unsigned binary integers B<a>, B<b> of a specified width. Output B<out> is B<1> if B<a> is less than B<b> else B<0>.

     Parameter  Description
  1  $chip      Chip
  2  $output    Name of component also the output bus
  3  $a         First integer
  4  $b         Second integer
  5  %options   Options

B<Example:>


  if (1)                                                                           # Compare 8 bit unsigned integers 'a' < 'b' - the pins used to input 'a' must be alphabetically less than those used for 'b'
   {my $B = 3;
    my $c = Silicon::Chip::newChip(title=><<"END");
  $B Bit Compare Less Than
  END
    $c->inputBits($_, $B) for qw(a b);                                            # First and second numbers

    $c->compareLt(qw(o a b));                                                     # Compare less than  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $c->output   (qw(out o));                                                     # Comparison result

    for   my $i(0..2**$B-1)                                                       # Each possible number
     {for my $j(0..2**$B-1)                                                       # Each possible number
       {my %a = $c->setBits('a', $i);                                             # Number a
        my %b = $c->setBits('b', $j);                                             # Number b

        my $s = $c->simulate({%a, %b}, $i==1&&$j==2?(svg=>"svg/CompareLt$B"):()); # Svg drawing of layout
        is_deeply($s->value("out"), $i < $j ? 1 : 0);                             # More than
        is_deeply($s->steps, 4);                                                  # Number of steps to stability
       }
     }
   }


=head3 chooseFromTwoWords  ($chip, $output, $a, $b, $choose, %options)

Choose one of two words based on a bit.  The first word is chosen if the bit is B<0> otherwise the second word is chosen.

     Parameter  Description
  1  $chip      Chip
  2  $output    Name of component also the chosen word
  3  $a         The first word
  4  $b         The second word
  5  $choose    The choosing bit
  6  %options   Options

B<Example:>


  if (1)
   {my $B = 4;

    my $c = newChip();
       $c->inputBits('a', $B);                                                    # First word
       $c->inputBits('b', $B);                                                    # Second word
       $c->input    ('c');                                                        # Chooser

       $c->chooseFromTwoWords(qw(o a b c));                                       # Generate gates  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputBits('out', 'o');                                                # Result

    my %a = setBits($c, 'a', 0b0011);
    my %b = setBits($c, 'b', 0b1100);


    my $s = $c->simulate({%a, %b, c=>1}, svg=>q(svg/chooseFromTwoWords));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    is_deeply($s->steps,               4);
    is_deeply($s->bint('out'), 0b1100);

    my $t = $c->simulate({%a, %b, c=>0});
    is_deeply($t->steps,               4);
    is_deeply($t->bint('out'), 0b0011);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/chooseFromTwoWords.svg">


=head3 enableWord  ($chip, $output, $a, $enable, %options)

Output a word or zeros depending on a choice bit.  The first word is chosen if the choice bit is B<1> otherwise all zeroes are chosen.

     Parameter  Description
  1  $chip      Chip
  2  $output    Name of component also the chosen word
  3  $a         The first word
  4  $enable    The second word
  5  %options   The choosing bit

B<Example:>


  if (1)
   {my $B = 4;

    my $c = newChip();
       $c->inputBits ('a', $B);                                                   # Word
       $c->input     ('c');                                                       # Choice bit

       $c->enableWord(qw(o a c));                                                 # Generate gates  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputBits(qw(out o));                                                 # Result

    my %a = setBits($c, 'a', 3);


    my $s = $c->simulate({%a, c=>1}, svg=>q(svg/enableWord));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    is_deeply($s->steps,               4);
    is_deeply($s->bint('out'), 3);

    my $t = $c->simulate({%a, c=>0});
    is_deeply($t->steps,               4);
    is_deeply($t->bint('out'), 0);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/enableWord.svg">


=head2 Masks

Point masks and monotone masks. A point mask has a single B<1> in a sea of B<0>s as in B<00100>.  A monotone mask has zero or more B<0>s followed by all B<1>s as in: B<00111>.

=head3 pointMaskToInteger  ($chip, $output, $input, %options)

Convert a mask B<i> known to have at most a single bit on - also known as a B<point mask> - to an output number B<a> representing the location in the mask of the bit set to B<1>. If no such bit exists in the point mask then output number B<a> is B<0>.

     Parameter  Description
  1  $chip      Chip
  2  $output    Output name
  3  $input     Input mask
  4  %options   Options

B<Example:>


  if (1)
   {my $B = 4;
    my $N = 2**$B-1;

    my $c = Silicon::Chip::newChip(title=><<"END");
  $B bits point mask to integer
  END
    $c->inputBits         (qw(    i), $N);                                        # Mask with no more than one bit on

    $c->pointMaskToInteger(qw(o   i));                                            # Convert  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $c->outputBits        (qw(out o));                                            # Mask with no more than one bit on

    for my $i(0..$N)                                                              # Each position of mask
     {my %i = setBits($c, 'i', $i ? 1<<($i-1) : 0);                               # Point in each position with zero representing no position
      my $s = $c->simulate(\%i, $i == 5 ? (svg=>"svg/point$B") : ());
      is_deeply($s->steps, 2);
      my %o = $s->values->%*;                                                     # Output bits
      my $n = eval join '', '0b', map {$o{n(o,$_)}} reverse 1..$B;                # Output bits as number
      is_deeply($n, $i);
     }
   }


=head3 integerToPointMask  ($chip, $output, $input, %options)

Convert an integer B<i> of specified width to a point mask B<m>. If the input integer is B<0> then the mask is all zeroes as well.

     Parameter  Description
  1  $chip      Chip
  2  $output    Output name
  3  $input     Input mask
  4  %options   Options

B<Example:>


  if (1)
   {my $B = 3;
    my $N = 2**$B-1;

    my $c = Silicon::Chip::newChip(title=><<"END");
  $B bit integer to $N bits monotone mask.
  END
       $c->inputBits         (qw(  i), $B);                                       # Input bus

       $c->integerToPointMask(qw(m i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputBits        (qw(o m));
    for my $i(0..$N)                                                              # Each position of mask
     {my %i = setBits($c, 'i', $i);
      my $s = $c->simulate(\%i, $i == 5 ? (svg=>"svg/integerToMontoneMask$B"):());
      is_deeply($s->steps, 3);

      my $r = $s->bint('o');                                                      # Mask values
      is_deeply($r, $i ? 1<<($i-1) : 0);                                          # Expected mask
     }
   }


=head3 monotoneMaskToInteger   ($chip, $output, $input, %options)

Convert a monotone mask B<i> to an output number B<r> representing the location in the mask of the bit set to B<1>. If no such bit exists in the point then output in B<r> is B<0>.

     Parameter  Description
  1  $chip      Chip
  2  $output    Output name
  3  $input     Input mask
  4  %options   Options

B<Example:>


  if (1)
   {my $B = 4;
    my $N = 2**$B-1;

    my $c = Silicon::Chip::newChip(title=><<"END");
  $N bits monotone mask to $B bit integer
  END
       $c->inputBits            ('i',     $N);

       $c->monotoneMaskToInteger(qw(m i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputBits           (qw(o m));

    for my $i(0..$N-1)                                                            # Each monotone mask
     {my %i = setBits($c, 'i', $i > 0 ? 1<<$i-1 : 0);
      my $s = $c->simulate(\%i,

        $i == 5 ? (svg=>"svg/monotoneMaskToInteger$B") : ());  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


      is_deeply($s->steps, 4);
      is_deeply($s->bint('m'), $i);
     }
   }


=head3 monotoneMaskToPointMask ($chip, $output, $input, %options)

Convert a monotone mask B<i> to a point mask B<o> representing the location in the mask of the first bit set to B<1>. If the monotone mask is all B<0>s then point mask is too.

     Parameter  Description
  1  $chip      Chip
  2  $output    Output name
  3  $input     Input mask
  4  %options   Options

B<Example:>


  if (1)
   {my $B = 4;

    my $c = newChip();
       $c->inputBits('m', $B);                                                    # Monotone mask

       $c->monotoneMaskToPointMask(qw(o m));                                      # Generate gates  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputBits('out', 'o');                                                # Point mask

    for my $i(0..$B)
     {my %m = $c->setBits('m', eval '0b'.(1 x $i).('0' x ($B-$i)));
      my $s = $c->simulate({%m});
      is_deeply($s->steps,                    2);
      is_deeply($s->bint('out'), $i ? (1<<($B-1)) / (1<<($i-1)) : 0);
     }
   }


=head3 integerToMonotoneMask   ($chip, $output, $input, %options)

Convert an integer B<i> of specified width to a monotone mask B<m>. If the input integer is B<0> then the mask is all zeroes.  Otherwise the mask has B<i-1> leading zeroes followed by all ones thereafter.

     Parameter  Description
  1  $chip      Chip
  2  $output    Output name
  3  $input     Input mask
  4  %options   Options

B<Example:>


  if (1)
   {my $B = 4;
    my $N = 2**$B-1;

    my $c = Silicon::Chip::newChip(title=><<"END");
  Convert $B bit integer to $N bit monotone mask
  END
       $c->inputBits            ('i', $B);                                        # Input gates

       $c->integerToMonotoneMask(qw(m i));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputBits           (qw(o m));                                        # Output gates

    for my $i(0..$N)                                                              # Each position of mask
     {my %i = setBits($c, 'i', $i);                                               # The number to convert
      my $s = $c->simulate(\%i, $i == 2 ? (svg=>"svg/integerToMontoneMask$B"):());
      is_deeply($s->steps, 4);
      is_deeply($s->bint('o'), $i > 0 ? ((1<<$N)-1)>>($i-1)<<($i-1) : 0);# Expected mask
     }
   }


=head3 chooseWordUnderMask ($chip, $output, $input, $mask, %options)

Choose one of a specified number of words B<w>, each of a specified width, using a point mask B<m> placing the selected word in B<o>.  If no word is selected then B<o> will be zero.

     Parameter  Description
  1  $chip      Chip
  2  $output    Output
  3  $input     Inputs
  4  $mask      Mask
  5  %options   Options

B<Example:>


  if (1)
   {my $B = 3; my $W = 4;

    my $c = Silicon::Chip::newChip(title=><<"END");
  Choose one of $W words of $B bits
  END
       $c->inputWords         ('w',       $W, $B);
       $c->inputBits          ('m',       $W);

       $c->chooseWordUnderMask(qw(W w m));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputBits         (qw(o W));

    my %i = setWords($c, 'w', 0b000, 0b001, 0b010, 0b0100);
    my %m = setBits ($c, 'm', 1<<2);                                              # Choose the third word

    my $s = $c->simulate({%i, %m}, svg=>"svg/choose_${W}_$B");

    is_deeply($s->steps, 3);
    is_deeply($s->bint('o'), 0b010);
   }


=head3 findWord($chip, $output, $key, $words, %options)

Choose one of a specified number of words B<w>, each of a specified width, using a key B<k>.  Return a point mask B<o> indicating the locations of the key if found or or a mask equal to all zeroes if the key is not present.

     Parameter  Description
  1  $chip      Chip
  2  $output    Found point mask
  3  $key       Key
  4  $words     Words to search
  5  %options   Options

B<Example:>


  if (1)
   {my $B = 3; my $W = 2**$B-1;

    my $c = Silicon::Chip::newChip(title=><<END);
  Search $W words of $B bits
  END
       $c->inputBits ('k',       $B);                                             # Search key
       $c->inputWords('w',       2**$B-1, $B);                                    # Words to search

       $c->findWord  (qw(m k w));                                                 # Find the word  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

       $c->outputBits(qw(M m));                                                   # Output mask

    my %w = setWords($c, 'w', reverse 1..$W);

    for my $k(0..$W)                                                              # Each possible key
     {my %k = setBits($c, 'k', $k);

      my $s = $c->simulate({%k, %w}, $k == 3 ? (svg=>q(svg/findWord)) : ());  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      is_deeply($s->steps, 3);
      is_deeply($s->bint('M'),$k ? 2**($W-$k) : 0);
     }
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/findWord.svg">


=head1 Simulate

Simulate the behavior of the L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> given a set of values on its input gates.

=head2 setBits ($chip, $name, $value, %options)

Set an array of input gates to a number prior to running a simulation.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of input gates
  3  $value     Number to set to
  4  %options   Options

B<Example:>


  if (1)                                                                           # Compare two 4 bit unsigned integers 'a' > 'b' - the pins used to input 'a' must be alphabetically less than those used for 'b'
   {my $B = 4;                                                                    # Number of bits
    my $c = Silicon::Chip::newChip(title=><<"END");
  $B Bit Compare
  END
    $c->inputBits("a", $B);                                                       # First number
    $c->inputBits("b", $B);                                                       # Second number
    $c->nxor (n(e,$_), n(a,$_), n(b,$_)) for 1..$B-1;                             # Test each bit for equality
    $c->gt   (n(g,$_), n(a,$_), n(b,$_)) for 1..$B;                               # Test each bit pair for greater

    for my $b(2..$B)
     {$c->and(n(c,$b), [(map {n(e, $_)} 1..$b-1), n(g,$b)]);                      # Greater on one bit and all preceding bits are equal
     }
    $c->or    ("or",  [n(g,1), (map {n(c, $_)} 2..$B)]);                          # Any set bit indicates that 'a' is more than 'b'
    $c->output("out", "or");                                                      # Output 1 if a > b else 0


    my %a = $c->setBits('a', 0);                                                  # Number a  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my %b = $c->setBits('b', 0);                                                  # Number b  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my $s = $c->simulate({%a, %b, n(a,2)=>1, n(b,2)=>1});                         # Two equal numbers
    is_deeply($s->value("out"), 0);

    my $t = $c->simulate({%a, %b, n(a,2)=>1}, svg=>q(svg/Compare));               # Svg drawing of layout
    is_deeply($t->value("out"), 1);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/Compare.svg">

  if (1)
   {my $B = 3; my $W = 4;

    my $c = Silicon::Chip::newChip(title=><<"END");
  Choose one of $W words of $B bits
  END
       $c->inputWords         ('w',       $W, $B);
       $c->inputBits          ('m',       $W);
       $c->chooseWordUnderMask(qw(W w m));
       $c->outputBits         (qw(o W));

    my %i = setWords($c, 'w', 0b000, 0b001, 0b010, 0b0100);

    my %m = setBits ($c, 'm', 1<<2);                                              # Choose the third word  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my $s = $c->simulate({%i, %m}, svg=>"svg/choose_${W}_$B");

    is_deeply($s->steps, 3);
    is_deeply($s->bint('o'), 0b010);
   }


=head2 setWords($chip, $name, @values)

Set an array of arrays of gates to an array of numbers prior to running a simulation.

     Parameter  Description
  1  $chip      Chip
  2  $name      Name of input gates
  3  @values    Number of bits in each array element

B<Example:>


  if (1)
   {my $B = 3; my $W = 4;

    my $c = Silicon::Chip::newChip(title=><<"END");
  Choose one of $W words of $B bits
  END
       $c->inputWords         ('w',       $W, $B);
       $c->inputBits          ('m',       $W);
       $c->chooseWordUnderMask(qw(W w m));
       $c->outputBits         (qw(o W));


    my %i = setWords($c, 'w', 0b000, 0b001, 0b010, 0b0100);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my %m = setBits ($c, 'm', 1<<2);                                              # Choose the third word

    my $s = $c->simulate({%i, %m}, svg=>"svg/choose_${W}_$B");

    is_deeply($s->steps, 3);
    is_deeply($s->bint('o'), 0b010);
   }


=head2 connectBits ($oc, $o, $ic, $i, %options)

Create a connection list connecting a set of output bits on the one chip to a set of input bits on another chip.

     Parameter  Description
  1  $oc        First chip
  2  $o         Name of gates on first chip
  3  $ic        Second chip
  4  $i         Names of gates on second chip
  5  %options   Options

B<Example:>


  if (1)                                                                            # Install one chip inside another chip, specifically one chip that performs NOT is installed once to flip a value
   {my $i = newChip(name=>"not");
       $i-> inputBits('i',     1);
       $i->   notBits(qw(n i));
       $i->outputBits(qw(o n));

    my $o = newChip(name=>"outer");
       $o->inputBits('i', 1); $o->outputBits(qw(n i));
       $o->inputBits('I', 1); $o->outputBits(qw(N I));


    my %i = connectBits($i, 'i', $o, 'n');  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my %o = connectBits($i, 'o', $o, 'I');  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $o->install($i, {%i}, {%o});
    my %d = $o->setBits('i', 1);
    my $s = $o->simulate({%d}, printOff=>"dump/not1", svg=>"svg/not1");

    is_deeply($s->steps,  2);
    is_deeply($s->values, {"(not 1 n_1)"=>0, "i_1"=>1, "N_1"=>0 });
   }


=head2 connectWords($oc, $o, $ic, $i, $words, $bits, %options)

Create a connection list connecting a set of words on the outer chip to a set of words on the inner chip.

     Parameter  Description
  1  $oc        First chip
  2  $o         Name of gates on first chip
  3  $ic        Second chip
  4  $i         Names of gates on second chip
  5  $words     Number of words to connect
  6  $bits      Options
  7  %options

B<Example:>


  if (1)                                                                           # Install one chip inside another chip, specifically one chip that performs NOT is installed three times sequentially to flip a value
   {my $i = newChip(name=>"not");
       $i-> inputWords('i', 1, 1);
       $i->   notWords(qw(n i));
       $i->outputWords(qw(o n));

    my $o = newChip(name=>"outer");
       $o->inputWords('i', 1, 1); $o->output(nn('n', 1, 1), nn('i', 1, 1));
       $o->inputWords('I', 1, 1); $o->output(nn('N', 1, 1), nn('I', 1, 1));


    my %i = connectWords($i, 'i', $o, 'n', 1, 1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my %o = connectWords($i, 'o', $o, 'I', 1, 1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $o->install($i, {%i}, {%o});
    my %d = $o->setWords('i', 1);
    my $s = $o->simulate({%d}, printOff=>"dump/not1", svg=>"svg/not1");

    is_deeply($s->steps,  2);
    is_deeply($s->values, { "(not 1 n_1_1)" => 0, "i_1_1" => 1, "N_1_1" => 0 });
   }


=head2 Silicon::Chip::Simulation::value($simulation, $name, %options)

Get the value of a gate as seen in a simulation

     Parameter    Description
  1  $simulation  Chip
  2  $name        Gate
  3  %options     Options

B<Example:>


  if (1)                                                                          # Internal input gate
   {my $c = newChip();
       $c->input ('i');                                                           # Input
       $c->input ('j');                                                           # Internal input which we will connect to later
       $c->output(qw(o j));                                                       # Output

       $c->connectInput(qw(j i));

    my $s = $c->simulate({i=>1});
    is_deeply($s->steps, 1);
    is_deeply($s->value("j"), undef);
    is_deeply($s->value("o"), 1);
   }

  if (1)                                                                           # Internal input gate
   {my @n = qw(3 2 1 2 3);
    my $c = newChip();
       $c->words('i', 2, @n);                                                     # Input
       $c->outputWords(qw(o i));                                                  # Output
    my $s = $c->simulate({});
    is_deeply($s->steps, 2);
    is_deeply([$s->wordsToInteger("i")], [@n]);
   }

  if (1)                                                                           # Internal input gate
   {my @n = qw(3 2 1 2 3);
    my $c = newChip();
       $c->words('i', 2, @n);                                                     # Input
       $c->outputWords(qw(o i));                                                  # Output
    my $s = $c->simulate({});
    is_deeply($s->steps, 2);
    is_deeply([$s->wordsToInteger("i")], [@n]);
   }


=head2 Silicon::Chip::Simulation::bint ($simulation, $output, %options)

Represent the state of bits in the simulation results as an unsigned binary integer.

     Parameter    Description
  1  $simulation  Chip
  2  $output      Name of gates on bus
  3  %options     Options

B<Example:>


  if (1)
   {my $W = 8;
    my $i = newChip(name=>"not");
       $i->inputBits('i',      $W);
       $i->notBits  (qw(n i));
       $i->outputBits(qw(o n));

    my $o = newChip(name=>"outer");
       $o->inputBits ('a',     $W);
       $o->outputBits(qw(A a));
       $o->inputBits ('b',     $W);
       $o->outputBits(qw(B b));

    my %i = connectBits($i, 'i', $o, 'A');
    my %o = connectBits($i, 'o', $o, 'b');
    $o->install($i, {%i}, {%o});

    my %d = setBits($o, 'a', 0b10110);
    my $s = $o->simulate({%d}, svg=>q(svg/not));
    is_deeply($s->bint('B'), 0b11101001);
   }


=for html <img src="https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/svg/not.svg">


=head2 Silicon::Chip::Simulation::wordsToInteger   ($simulation, $output, %options)

Represent the state of words in the simulation results as an array of unsigned binary integer.

     Parameter    Description
  1  $simulation  Chip
  2  $output      Name of gates on bus
  3  %options     Options

B<Example:>


  if (1)
   {my @b = ((my $W = 4), (my $B = 3));

    my $c = newChip();
       $c->inputWords ('i',      @b);
       $c->outputWords(qw(o i));

    my %d = setWords($c, 'i', 0b000, 0b001, 0b010, 0b011);
    my $s = $c->simulate({%d}, svg=>"svg/words$W");

    is_deeply([$s->wordsToInteger('o')], [0..3]);
    is_deeply([$s->wordXToInteger('o')], [10, 12, 0]);
   }


=head2 Silicon::Chip::Simulation::wordXToInteger   ($simulation, $output, %options)

Represent the state of words in the simulation results as an array of unsigned binary integer.

     Parameter    Description
  1  $simulation  Chip
  2  $output      Name of gates on bus
  3  %options     Options

B<Example:>


  if (1)
   {my @b = ((my $W = 4), (my $B = 3));

    my $c = newChip();
       $c->inputWords ('i',      @b);
       $c->outputWords(qw(o i));

    my %d = setWords($c, 'i', 0b000, 0b001, 0b010, 0b011);
    my $s = $c->simulate({%d}, svg=>"svg/words$W");

    is_deeply([$s->wordsToInteger('o')], [0..3]);
    is_deeply([$s->wordXToInteger('o')], [10, 12, 0]);
   }


=head2 simulate($chip, $inputs, %options)

Simulate the action of the L<logic gates|https://en.wikipedia.org/wiki/Logic_gate> on a L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> for a given set of inputs until the output value of each L<logic gate|https://en.wikipedia.org/wiki/Logic_gate> stabilizes.

     Parameter  Description
  1  $chip      Chip
  2  $inputs    Hash of input names to values
  3  %options   Options

B<Example:>


  if (1)
   {my $i = newChip(name=>"inner");
       $i->input ("Ii");
       $i->not   ("In", "Ii");
       $i->output( "Io", "In");

    my $o = newChip(name=>"outer");
       $o->input ("Oi1");
       $o->output("Oo1", "Oi1");
       $o->input ("Oi2");
       $o->output("Oo2", "Oi2");
       $o->input ("Oi3");
       $o->output("Oo3", "Oi3");
       $o->input ("Oi4");
       $o->output("Oo",  "Oi4");

    $o->install($i, {Ii=>"Oo1"}, {Io=>"Oi2"});
    $o->install($i, {Ii=>"Oo2"}, {Io=>"Oi3"});
    $o->install($i, {Ii=>"Oo3"}, {Io=>"Oi4"});


    my $s = $o->simulate({Oi1=>1}, printOff=>"dump/not3", svg=>"svg/not3");  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    is_deeply($s->value("Oo"), 0);
    is_deeply($s->steps,       4);
   }



=head1 Hash Definitions




=head2 Silicon::Chip Definition


Chip description




=head3 Output fields


=head4 gateSeq

Gate sequence number - this allows us to display the gates in the order they were defined ti simplify the understanding of drawn layouts

=head4 gates

Gates in chip

=head4 installs

Chips installed within the chip

=head4 name

Name of chip

=head4 sizeBits

Sizes of buses

=head4 sizeWords

Sizes of buses

=head4 title

Title if known



=head1 Private Methods

=head2 AUTOLOAD($chip, @options)

Autoload by L<logic gate|https://en.wikipedia.org/wiki/Logic_gate> name to provide a more readable way to specify the L<logic gates|https://en.wikipedia.org/wiki/Logic_gate> on a L<chip|https://en.wikipedia.org/wiki/Integrated_circuit>.

     Parameter  Description
  1  $chip      Chip
  2  @options   Options


=head1 Index


1 L<andBits|/andBits> - B<and> a bus made of bits.

2 L<andWords|/andWords> - B<and> a bus made of words to produce a single word.

3 L<andWordsX|/andWordsX> - B<and> a bus made of words by and-ing the corresponding bits in each word to make a single word.

4 L<AUTOLOAD|/AUTOLOAD> - Autoload by L<logic gate|https://en.wikipedia.org/wiki/Logic_gate> name to provide a more readable way to specify the L<logic gates|https://en.wikipedia.org/wiki/Logic_gate> on a L<chip|https://en.wikipedia.org/wiki/Integrated_circuit>.

5 L<bits|/bits> - Create a bus set to a specified number.

6 L<chooseFromTwoWords|/chooseFromTwoWords> - Choose one of two words based on a bit.

7 L<chooseWordUnderMask|/chooseWordUnderMask> - Choose one of a specified number of words B<w>, each of a specified width, using a point mask B<m> placing the selected word in B<o>.

8 L<compareEq|/compareEq> - Compare two unsigned binary integers of a specified width returning B<1> if they are equal else B<0>.

9 L<compareGt|/compareGt> - Compare two unsigned binary integers and return B<1> if the first integer is more than B<b> else B<0>.

10 L<compareLt|/compareLt> - Compare two unsigned binary integers B<a>, B<b> of a specified width.

11 L<connectBits|/connectBits> - Create a connection list connecting a set of output bits on the one chip to a set of input bits on another chip.

12 L<connectInput|/connectInput> - Connect a previously defined input gate to the output of another previously gate on the same chip.

13 L<connectWords|/connectWords> - Create a connection list connecting a set of words on the outer chip to a set of words on the inner chip.

14 L<enableWord|/enableWord> - Output a word or zeros depending on a choice bit.

15 L<findWord|/findWord> - Choose one of a specified number of words B<w>, each of a specified width, using a key B<k>.

16 L<gate|/gate> - A L<logic gate|https://en.wikipedia.org/wiki/Logic_gate> chosen from B<and|continue|gt|input|lt|nand|nor|not|nxor|one|or|output|xor|zero>.

17 L<inputBits|/inputBits> - Create an B<input> bus made of bits.

18 L<inputWords|/inputWords> - Create an B<input> bus made of words.

19 L<install|/install> - Install a L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> within another L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> specifying the connections between the inner and outer L<chip|https://en.wikipedia.org/wiki/Integrated_circuit>.

20 L<integerToMonotoneMask|/integerToMonotoneMask> - Convert an integer B<i> of specified width to a monotone mask B<m>.

21 L<integerToPointMask|/integerToPointMask> - Convert an integer B<i> of specified width to a point mask B<m>.

22 L<monotoneMaskToInteger|/monotoneMaskToInteger> - Convert a monotone mask B<i> to an output number B<r> representing the location in the mask of the bit set to B<1>.

23 L<monotoneMaskToPointMask|/monotoneMaskToPointMask> - Convert a monotone mask B<i> to a point mask B<o> representing the location in the mask of the first bit set to B<1>.

24 L<n|/n> - Gate name from single index.

25 L<nandBits|/nandBits> - B<nand> a bus made of bits.

26 L<newChip|/newChip> - Create a new L<chip|https://en.wikipedia.org/wiki/Integrated_circuit>.

27 L<nn|/nn> - Gate name from double index.

28 L<norBits|/norBits> - B<nor> a bus made of bits.

29 L<notBits|/notBits> - Create a B<not> bus made of bits.

30 L<notWords|/notWords> - Create a B<not> bus made of words.

31 L<orBits|/orBits> - B<or> a bus made of bits.

32 L<orWords|/orWords> - B<or> a bus made of words to produce a single word.

33 L<orWordsX|/orWordsX> - B<or> a bus made of words by or-ing the corresponding bits in each word to make a single word.

34 L<outputBits|/outputBits> - Create an B<output> bus made of bits.

35 L<outputWords|/outputWords> - Create an B<output> bus made of words.

36 L<pointMaskToInteger|/pointMaskToInteger> - Convert a mask B<i> known to have at most a single bit on - also known as a B<point mask> - to an output number B<a> representing the location in the mask of the bit set to B<1>.

37 L<print|/print> - Dump the L<logic gates|https://en.wikipedia.org/wiki/Logic_gate> present on a L<chip|https://en.wikipedia.org/wiki/Integrated_circuit>.

38 L<printSvg|/printSvg> - Dump the L<logic gates|https://en.wikipedia.org/wiki/Logic_gate> on a L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> as an L<Scalar Vector Graphics|https://en.wikipedia.org/wiki/Scalable_Vector_Graphics> drawing to help visualize the structure of the L<chip|https://en.wikipedia.org/wiki/Integrated_circuit>.

39 L<setBits|/setBits> - Set an array of input gates to a number prior to running a simulation.

40 L<setSizeBits|/setSizeBits> - Set the size of a bits bus.

41 L<setSizeWords|/setSizeWords> - Set the size of a bits bus.

42 L<setWords|/setWords> - Set an array of arrays of gates to an array of numbers prior to running a simulation.

43 L<Silicon::Chip::Simulation::bint|/Silicon::Chip::Simulation::bint> - Represent the state of bits in the simulation results as an unsigned binary integer.

44 L<Silicon::Chip::Simulation::print|/Silicon::Chip::Simulation::print> - Print simulation results as text.

45 L<Silicon::Chip::Simulation::printSvg|/Silicon::Chip::Simulation::printSvg> - Print simulation results as svg.

46 L<Silicon::Chip::Simulation::value|/Silicon::Chip::Simulation::value> - Get the value of a gate as seen in a simulation

47 L<Silicon::Chip::Simulation::wordsToInteger|/Silicon::Chip::Simulation::wordsToInteger> - Represent the state of words in the simulation results as an array of unsigned binary integer.

48 L<Silicon::Chip::Simulation::wordXToInteger|/Silicon::Chip::Simulation::wordXToInteger> - Represent the state of words in the simulation results as an array of unsigned binary integer.

49 L<simulate|/simulate> - Simulate the action of the L<logic gates|https://en.wikipedia.org/wiki/Logic_gate> on a L<chip|https://en.wikipedia.org/wiki/Integrated_circuit> for a given set of inputs until the output value of each L<logic gate|https://en.wikipedia.org/wiki/Logic_gate> stabilizes.

50 L<words|/words> - Create a word bus set to specified numbers.

=head1 Installation

This module is written in 100% Pure Perl and, thus, it is easy to read,
comprehend, use, modify and install via B<cpan>:

  sudo cpan install Silicon::Chip

=head1 Author

L<philiprbrenan@gmail.com|mailto:philiprbrenan@gmail.com>

L<http://www.appaapps.com|http://www.appaapps.com>

=head1 Copyright

Copyright (c) 2016-2023 Philip R Brenan.

This module is free software. It may be used, redistributed and/or modified
under the same terms as Perl itself.

=cut



#D0 Tests                                                                       # Tests and examples
goto finish if caller;                                                          # Skip testing if we are being called as a module
clearFolder(q(svg), 99);                                                        # Clear the output svg folder
eval "use Test::More tests=>531;";
eval "Test::More->builder->output('/dev/null');" if -e q(/home/phil/);
eval {goto latest};

#svg https://raw.githubusercontent.com/philiprbrenan/SiliconChip/main/lib/Silicon/

if (1)                                                                          #Tn #Tnn
 {is_deeply( n(a,1),   "a_1");
  is_deeply(nn(a,1,2), "a_1_2");
 }

if (1)                                                                          # Unused output
 {my $c = Silicon::Chip::newChip;
  $c->input( "i1");
  eval {$c->simulate({i1=>1})};
  ok($@ =~ m(Output from gate 'i1' is never used)i);
 }

if (1)                                                                          # Gate already specified
 {my $c = Silicon::Chip::newChip;
        $c->input("i1");
  eval {$c->input("i1")};
  ok($@ =~ m(Gate: 'i1' has already been specified));
 }

#latest:;
if (1)                                                                          # Check all inputs have values
 {my $c = Silicon::Chip::newChip;
  $c->input ("i1");
  $c->input ("i2");
  $c->and   ("and", [qw(i1 i2)]);
  $c->output("o",   q(and));
  eval {$c->simulate({i1=>1, i22=>1})};
  ok($@ =~ m(No input value for input gate: i2)i);
 }

#latest:;
if (1)                                                                          # Check each input to each gate receives output from another gate
 {my $c = Silicon::Chip::newChip;
  $c->input("i1");
  $c->input("i2");
  $c->and  ("and1", [qw(i1 i2)]);
  $c->output( "o", q(an1));
  eval {$c->simulate({i1=>1, i2=>1})};
  ok($@ =~ m(No output driving input 'an1' on 'output' gate 'o')i);
 }

#latest:;
if (1)                                                                          #Tzero
 {my $c = Silicon::Chip::newChip;
  $c->zero  ("z");
  $c->output("o", "z");
  my $s = $c->simulate({}, svg=>q(svg/zero));
  is_deeply($s->steps,      2);
  is_deeply($s->value("o"), 0);
 }

#latest:;
if (1)                                                                          #Tone
 {my $c = Silicon::Chip::newChip;
  $c->one ("o");
  $c->output("O", "o");
  my $s = $c->simulate({}, svg=>q(svg/one));
  is_deeply($s->steps      , 2);
  is_deeply($s->value("O"), 1);
 }

#latest:;
if (1)                                                                          #TnewChip
 {my $c = Silicon::Chip::newChip;
  $c->one ("one");
  $c->zero("zero");
  $c->or  ("or",   [qw(one zero)]);
  $c->and ("and",  [qw(one zero)]);
  $c->output("o1", "or");
  $c->output("o2", "and");
  my $s = $c->simulate({}, svg=>q(svg/oneZero));
  is_deeply($s->steps       , 3);
  is_deeply($s->value("o1"), 1);
  is_deeply($s->value("o2"), 0);
 }

#latest:;
if (1)                                                                          #Tbits
 {my $N = 4;
  for my $i(0..2**$N-1)
   {my $c = Silicon::Chip::newChip;
    $c->bits      ("c", $N, $i);
    $c->outputBits("o", "c");
    my $s = $c->simulate({}, $i == 3 ? (svg=>q(svg/bits)) : ());
    is_deeply($s->steps, 2);
    is_deeply($s->bint("o"), $i);
   }
 }

#latest:;
if (1)                                                                          #TnewChip # Single AND gate
 {my $c = Silicon::Chip::newChip;
  $c->input ("i1");
  $c->input ("i2");
  $c->and   ("and1", [qw(i1 i2)]);
  $c->output("o", "and1");
  my $s = $c->simulate({i1=>1, i2=>1});
  ok($s->steps         == 2);
  ok($s->value("and1") == 1);
 }

#latest:;
if (1)                                                                          #TSilicon::Chip::Simulation::print #Tprint #TSilicon::Chip::Simulation::printSvg #TprintSvg
 {my $c = Silicon::Chip::newChip(title=>"And gate");
  $c->input ("i1");
  $c->input ("i2");
  $c->and   ("and1", [qw(i1 i2)]);
  $c->output("o", "and1");
  my $s = $c->simulate({i1=>1, i2=>1});

  is_deeply($c->print, <<END);
i1                              :     input                           i1
i2                              :     input                           i2
and1                            :     and                             i1 i2
o                               :     output                          and1
END

  is_deeply($s->print, <<END);
i1                              :   1 input                           i1
i2                              :   1 input                           i2
and1                            :   1 and                             i1 i2
o                               :   1 output                          and1
END

  ok($s->printSvg ne $c->printSvg);
 }

#latest:;
if (1)                                                                          # Three AND gates in a tree
 {my $c = Silicon::Chip::newChip;
  $c->input( "i11");
  $c->input( "i12");
  $c->and(    "and1", [qw(i11 i12)]);
  $c->input( "i21");
  $c->input( "i22");
  $c->and(    "and2", [qw(i21   i22)]);
  $c->and(    "and",  [qw(and1 and2)]);
  $c->output( "o", "and");
  my $s = $c->simulate({i11=>1, i12=>1, i21=>1, i22=>1});
  ok($s->steps        == 3);
  ok($s->value("and") == 1);
     $s = $c->simulate({i11=>1, i12=>0, i21=>1, i22=>1});
  ok($s->steps        == 3);
  ok($s->value("and") == 0);
 }

#latest:;
if (1)                                                                          #Tgate # Two AND gates driving an OR gate
 {my $c = newChip;
  $c->input ("i11");
  $c->input ("i12");
  $c->and   ("and1", [qw(i11   i12)]);
  $c->input ("i21");
  $c->input ("i22");
  $c->and   ("and2", [qw(i21   i22 )]);
  $c->or    ("or",   [qw(and1  and2)]);
  $c->output( "o", "or");
  my $s = $c->simulate({i11=>1, i12=>1, i21=>1, i22=>1});
  ok($s->steps        == 3);
  ok($s->value("or")  == 1);
     $s  = $c->simulate({i11=>1, i12=>0, i21=>1, i22=>1});
  ok($s->steps        == 3);
  ok($s->value("or")  == 1);
     $s  = $c->simulate({i11=>1, i12=>0, i21=>1, i22=>0});
  ok($s->steps        == 3);
  ok($s->value("o")   == 0);
 }

#latest:;
if (1)                                                                          # 4 bit equal #TnewChip
 {my $B = 4;                                                                    # Number of bits

  my $c = Silicon::Chip::newChip(title=><<"END");                               # Create chip
$B Bit Equals
END
  $c->input ("a$_")                 for 1..$B;                                  # First number
  $c->input ("b$_")                 for 1..$B;                                  # Second number

  $c->nxor  ("e$_", "a$_", "b$_")   for 1..$B;                                  # Test each bit for equality
  $c->and   ("and", {map{$_=>"e$_"}     1..$B});                                # And tests together to get total equality

  $c->output("out", "and");                                                     # Output gate

  my $s = $c->simulate({a1=>1, a2=>0, a3=>1, a4=>0,                             # Input gate values
                        b1=>1, b2=>0, b3=>1, b4=>0},
                        svg=>q(svg/Equals));                                    # Svg drawing of layout

  is_deeply($s->steps,        3);                                               # Three steps
  is_deeply($s->value("out"), 1);                                               # Out is 1 for equals

  my $t = $c->simulate({a1=>1, a2=>1, a3=>1, a4=>0,
                        b1=>1, b2=>0, b3=>1, b4=>0});
  is_deeply($t->value("out"), 0);                                               # Out is 0 for not equals
 }

#latest:;
if (1)                                                                          #TsetBits # Compare two 4 bit unsigned integers 'a' > 'b' - the pins used to input 'a' must be alphabetically less than those used for 'b'
 {my $B = 4;                                                                    # Number of bits
  my $c = Silicon::Chip::newChip(title=><<"END");
$B Bit Compare
END
  $c->inputBits("a", $B);                                                       # First number
  $c->inputBits("b", $B);                                                       # Second number
  $c->nxor (n(e,$_), n(a,$_), n(b,$_)) for 1..$B-1;                             # Test each bit for equality
  $c->gt   (n(g,$_), n(a,$_), n(b,$_)) for 1..$B;                               # Test each bit pair for greater

  for my $b(2..$B)
   {$c->and(n(c,$b), [(map {n(e, $_)} 1..$b-1), n(g,$b)]);                      # Greater on one bit and all preceding bits are equal
   }
  $c->or    ("or",  [n(g,1), (map {n(c, $_)} 2..$B)]);                          # Any set bit indicates that 'a' is more than 'b'
  $c->output("out", "or");                                                      # Output 1 if a > b else 0

  my %a = $c->setBits('a', 0);                                                  # Number a
  my %b = $c->setBits('b', 0);                                                  # Number b

  my $s = $c->simulate({%a, %b, n(a,2)=>1, n(b,2)=>1});                         # Two equal numbers
  is_deeply($s->value("out"), 0);

  my $t = $c->simulate({%a, %b, n(a,2)=>1}, svg=>q(svg/Compare));               # Svg drawing of layout
  is_deeply($t->value("out"), 1);
 }

#latest:;
if (1)                                                                          #TcompareEq # Compare unsigned integers
 {my $B = 2;

  my $c = Silicon::Chip::newChip(title=><<"END");
$B Bit Compare Equal
END
  $c->inputBits($_, $B) for qw(a b);                                            # First and second numbers
  $c->compareEq(qw(o a b));                                                     # Compare equals
  $c->output   (qw(out o));                                                     # Comparison result

  for   my $i(0..2**$B-1)                                                       # Each possible number
   {for my $j(0..2**$B-1)                                                       # Each possible number
     {my %a = $c->setBits('a', $i);                                             # Number a
      my %b = $c->setBits('b', $j);                                             # Number b

      my $s = $c->simulate({%a, %b}, $i==1&&$j==1?(svg=>"svg/CompareEq$B"):()); # Svg drawing of layout

      is_deeply($s->value("out"), $i == $j ? 1 : 0);                            # Equal
      is_deeply($s->steps, 3);                                                  # Number of steps to stability
     }
   }
 }

#latest:;
if (1)                                                                          #TcompareGt # Compare 8 bit unsigned integers 'a' > 'b' - the pins used to input 'a' must be alphabetically less than those used for 'b'
 {my $B = 3;
  my $c = Silicon::Chip::newChip(title=><<END);
$B Bit Compare more than
END
  $c->inputBits($_, $B) for qw(a b);                                            # First and second numbers
  $c->compareGt(qw(o a b));                                                     # Compare more than
  $c->output   (qw(out o));                                                     # Comparison result

  for   my $i(0..2**$B-1)                                                       # Each possible number
   {for my $j(0..2**$B-1)                                                       # Each possible number
     {my %a = $c->setBits('a', $i);                                             # Number a
      my %b = $c->setBits('b', $j);                                             # Number b

      my $s = $c->simulate({%a, %b}, $i==2&&$j==1?(svg=>"svg/CompareGt$B"):()); # Svg drawing of layout
      is_deeply($s->value("out"), $i > $j ? 1 : 0);                             # More than
      is_deeply($s->steps, 4);                                                  # Number of steps to stability
     }
   }
 }

#latest:;
if (1)                                                                          #TcompareLt # Compare 8 bit unsigned integers 'a' < 'b' - the pins used to input 'a' must be alphabetically less than those used for 'b'
 {my $B = 3;
  my $c = Silicon::Chip::newChip(title=><<"END");
$B Bit Compare Less Than
END
  $c->inputBits($_, $B) for qw(a b);                                            # First and second numbers
  $c->compareLt(qw(o a b));                                                     # Compare less than
  $c->output   (qw(out o));                                                     # Comparison result

  for   my $i(0..2**$B-1)                                                       # Each possible number
   {for my $j(0..2**$B-1)                                                       # Each possible number
     {my %a = $c->setBits('a', $i);                                             # Number a
      my %b = $c->setBits('b', $j);                                             # Number b

      my $s = $c->simulate({%a, %b}, $i==1&&$j==2?(svg=>"svg/CompareLt$B"):()); # Svg drawing of layout
      is_deeply($s->value("out"), $i < $j ? 1 : 0);                             # More than
      is_deeply($s->steps, 4);                                                  # Number of steps to stability
     }
   }
 }

#latest:;
if (1)                                                                          # Masked multiplexer: copy B bit word selected by mask from W possible locations
 {my $B = 4; my $W = 4;
  my $c = newChip;
  for my $w(1..$W)                                                              # Input words
   {$c->input("s$w");                                                           # Selection mask
    for my $b(1..$B)                                                            # Bits of input word
     {$c->input("i$w$b");
      $c->and(   "s$w$b", ["i$w$b", "s$w"]);
     }
   }
  for my $b(1..$B)                                                              # Or selected bits together to make output
   {$c->or    ("c$b", [map {"s$b$_"} 1..$W]);                                   # Combine the selected bits to make a word
    $c->output("o$b", "c$b");                                                   # Output the word selected
   }
  my $s = $c->simulate(
   {s1 =>0, s2 =>0, s3 =>1, s4 =>0,
    i11=>0, i12=>0, i13=>0, i14=>1,
    i21=>0, i22=>0, i23=>1, i24=>0,
    i31=>0, i32=>1, i33=>0, i34=>0,
    i41=>1, i42=>0, i43=>0, i44=>0});

  is_deeply([@{$s->values}{qw(o1 o2 o3 o4)}], [qw(0 0 1 0)]);                   # Number selected by mask
  is_deeply($s->steps, 3);
 }

#latest:;
if (1)                                                                          # Rename a gate
 {my $i = newChip(name=>"inner");
          $i->input ("i");
  my $n = $i->not   ("n",  "i");
          $i->output("io", "n");

  my $ci = cloneGate $i, $n;
  renameGate $i, $ci, "aaa";
  is_deeply($ci->inputs,   { n => "i" });
  is_deeply($ci->output,  "(aaa n)");
  is_deeply($ci->io, 0);
 }

#latest:;
if (1)                                                                          #Tinstall #TconnectBits # Install one chip inside another chip, specifically one chip that performs NOT is installed once to flip a value
 {my $i = newChip(name=>"not");
     $i-> inputBits('i',     1);
     $i->   notBits(qw(n i));
     $i->outputBits(qw(o n));

  my $o = newChip(name=>"outer");
     $o->inputBits('i', 1); $o->outputBits(qw(n i));
     $o->inputBits('I', 1); $o->outputBits(qw(N I));

  my %i = connectBits($i, 'i', $o, 'n');
  my %o = connectBits($i, 'o', $o, 'I');
  $o->install($i, {%i}, {%o});
  my %d = $o->setBits('i', 1);
  my $s = $o->simulate({%d}, printOff=>"dump/not1", svg=>"svg/not1");

  is_deeply($s->steps,  2);
  is_deeply($s->values, {"(not 1 n_1)"=>0, "i_1"=>1, "N_1"=>0 });
 }

#latest:;
if (1)                                                                          #TconnectWords # Install one chip inside another chip, specifically one chip that performs NOT is installed three times sequentially to flip a value
 {my $i = newChip(name=>"not");
     $i-> inputWords('i', 1, 1);
     $i->   notWords(qw(n i));
     $i->outputWords(qw(o n));

  my $o = newChip(name=>"outer");
     $o->inputWords('i', 1, 1); $o->output(nn('n', 1, 1), nn('i', 1, 1));
     $o->inputWords('I', 1, 1); $o->output(nn('N', 1, 1), nn('I', 1, 1));

  my %i = connectWords($i, 'i', $o, 'n', 1, 1);
  my %o = connectWords($i, 'o', $o, 'I', 1, 1);
  $o->install($i, {%i}, {%o});
  my %d = $o->setWords('i', 1);
  my $s = $o->simulate({%d}, printOff=>"dump/not1", svg=>"svg/not1");

  is_deeply($s->steps,  2);
  is_deeply($s->values, { "(not 1 n_1_1)" => 0, "i_1_1" => 1, "N_1_1" => 0 });
 }

#latest:;
if (1)                                                                          #Tsimulate
 {my $i = newChip(name=>"inner");
     $i->input ("Ii");
     $i->not   ("In", "Ii");
     $i->output( "Io", "In");

  my $o = newChip(name=>"outer");
     $o->input ("Oi1");
     $o->output("Oo1", "Oi1");
     $o->input ("Oi2");
     $o->output("Oo2", "Oi2");
     $o->input ("Oi3");
     $o->output("Oo3", "Oi3");
     $o->input ("Oi4");
     $o->output("Oo",  "Oi4");

  $o->install($i, {Ii=>"Oo1"}, {Io=>"Oi2"});
  $o->install($i, {Ii=>"Oo2"}, {Io=>"Oi3"});
  $o->install($i, {Ii=>"Oo3"}, {Io=>"Oi4"});

  my $s = $o->simulate({Oi1=>1}, printOff=>"dump/not3", svg=>"svg/not3");
  is_deeply($s->value("Oo"), 0);
  is_deeply($s->steps,       4);
 }

#latest:;
if (1)                                                                          #TpointMaskToInteger
 {my $B = 4;
  my $N = 2**$B-1;

  my $c = Silicon::Chip::newChip(title=><<"END");
$B bits point mask to integer
END
  $c->inputBits         (qw(    i), $N);                                        # Mask with no more than one bit on
  $c->pointMaskToInteger(qw(o   i));                                            # Convert
  $c->outputBits        (qw(out o));                                            # Mask with no more than one bit on

  for my $i(0..$N)                                                              # Each position of mask
   {my %i = setBits($c, 'i', $i ? 1<<($i-1) : 0);                               # Point in each position with zero representing no position
    my $s = $c->simulate(\%i, $i == 5 ? (svg=>"svg/point$B") : ());
    is_deeply($s->steps, 2);
    my %o = $s->values->%*;                                                     # Output bits
    my $n = eval join '', '0b', map {$o{n(o,$_)}} reverse 1..$B;                # Output bits as number
    is_deeply($n, $i);
   }
 }

#latest:;
if (1)                                                                          #TintegerToPointMask
 {my $B = 3;
  my $N = 2**$B-1;

  my $c = Silicon::Chip::newChip(title=><<"END");
$B bit integer to $N bits monotone mask.
END
     $c->inputBits         (qw(  i), $B);                                       # Input bus
     $c->integerToPointMask(qw(m i));
     $c->outputBits        (qw(o m));
  for my $i(0..$N)                                                              # Each position of mask
   {my %i = setBits($c, 'i', $i);
    my $s = $c->simulate(\%i, $i == 5 ? (svg=>"svg/integerToMontoneMask$B"):());
    is_deeply($s->steps, 3);

    my $r = $s->bint('o');                                                      # Mask values
    is_deeply($r, $i ? 1<<($i-1) : 0);                                          # Expected mask
   }
 }

#latest:;
if (1)                                                                          #TmonotoneMaskToInteger
 {my $B = 4;
  my $N = 2**$B-1;

  my $c = Silicon::Chip::newChip(title=><<"END");
$N bits monotone mask to $B bit integer
END
     $c->inputBits            ('i',     $N);
     $c->monotoneMaskToInteger(qw(m i));
     $c->outputBits           (qw(o m));

  for my $i(0..$N-1)                                                            # Each monotone mask
   {my %i = setBits($c, 'i', $i > 0 ? 1<<$i-1 : 0);
    my $s = $c->simulate(\%i,
      $i == 5 ? (svg=>"svg/monotoneMaskToInteger$B") : ());

    is_deeply($s->steps, 4);
    is_deeply($s->bint('m'), $i);
   }
 }

#latest:;
if (1)                                                                          #TintegerToMonotoneMask
 {my $B = 4;
  my $N = 2**$B-1;

  my $c = Silicon::Chip::newChip(title=><<"END");
Convert $B bit integer to $N bit monotone mask
END
     $c->inputBits            ('i', $B);                                        # Input gates
     $c->integerToMonotoneMask(qw(m i));
     $c->outputBits           (qw(o m));                                        # Output gates

  for my $i(0..$N)                                                              # Each position of mask
   {my %i = setBits($c, 'i', $i);                                               # The number to convert
    my $s = $c->simulate(\%i, $i == 2 ? (svg=>"svg/integerToMontoneMask$B"):());
    is_deeply($s->steps, 4);
    is_deeply($s->bint('o'), $i > 0 ? ((1<<$N)-1)>>($i-1)<<($i-1) : 0);         # Expected mask
   }
 }

#latest:;
if (1)                                                                          #TchooseWordUnderMask #TsetBits #TsetWords
 {my $B = 3; my $W = 4;

  my $c = Silicon::Chip::newChip(title=><<"END");
Choose one of $W words of $B bits
END
     $c->inputWords         ('w',       $W, $B);
     $c->inputBits          ('m',       $W);
     $c->chooseWordUnderMask(qw(W w m));
     $c->outputBits         (qw(o W));

  my %i = setWords($c, 'w', 0b000, 0b001, 0b010, 0b0100);
  my %m = setBits ($c, 'm', 1<<2);                                              # Choose the third word

  my $s = $c->simulate({%i, %m}, svg=>"svg/choose_${W}_$B");

  is_deeply($s->steps, 3);
  is_deeply($s->bint('o'), 0b010);
 }

#latest:;
if (1)                                                                          #TfindWord
 {my $B = 3; my $W = 2**$B-1;

  my $c = Silicon::Chip::newChip(title=><<END);
Search $W words of $B bits
END
     $c->inputBits ('k',       $B);                                             # Search key
     $c->inputWords('w',       2**$B-1, $B);                                    # Words to search
     $c->findWord  (qw(m k w));                                                 # Find the word
     $c->outputBits(qw(M m));                                                   # Output mask

  my %w = setWords($c, 'w', reverse 1..$W);

  for my $k(0..$W)                                                              # Each possible key
   {my %k = setBits($c, 'k', $k);
    my $s = $c->simulate({%k, %w}, $k == 3 ? (svg=>q(svg/findWord)) : ());
    is_deeply($s->steps, 3);
    is_deeply($s->bint('M'),$k ? 2**($W-$k) : 0);
   }
 }

#latest:;
if (1)                                                                          #TinputBits #ToutputBits #TnotBits #TSilicon::Chip::Simulation::bint
 {my $W = 8;
  my $i = newChip(name=>"not");
     $i->inputBits('i',      $W);
     $i->notBits  (qw(n i));
     $i->outputBits(qw(o n));

  my $o = newChip(name=>"outer");
     $o->inputBits ('a',     $W);
     $o->outputBits(qw(A a));
     $o->inputBits ('b',     $W);
     $o->outputBits(qw(B b));

  my %i = connectBits($i, 'i', $o, 'A');
  my %o = connectBits($i, 'o', $o, 'b');
  $o->install($i, {%i}, {%o});

  my %d = setBits($o, 'a', 0b10110);
  my $s = $o->simulate({%d}, svg=>q(svg/not));
  is_deeply($s->bint('B'), 0b11101001);
 }

#latest:;
if (1)                                                                          #TandBits #TorBits #TnandBits #TnorBits
 {my $W = 8;

  my $c = newChip();
     $c-> inputBits('i',         $W);
     $c->   andBits(qw(and  i));
     $c->    orBits(qw(or   i));
     $c->  nandBits(qw(nand i));
     $c->   norBits(qw(nor  i));
     $c->output    (qw(And  and));
     $c->output    (qw(Or   or));
     $c->output    (qw(nAnd nand));
     $c->output    (qw(nOr  nor));

  my %d = setBits($c, 'i', 0b10110);
  my $s = $c->simulate({%d}, svg=>q(svg/andOrBits));

  is_deeply($s->value("And"),  0);
  is_deeply($s->value("Or"),   1);
  is_deeply($s->value("nAnd"), 1);
  is_deeply($s->value("nOr"),  0);
 }

#latest:;
if (1)                                                                          #TandWords #TandWordsX #TorWords #TorWordsX #ToutputBits #TnotWords
 {my @B = ((my $W = 4), (my $B = 2));

  my $c = newChip();
     $c->inputWords ('i', @B);
     $c->andWords   (qw(and  i));
     $c->andWordsX  (qw(andX i));
     $c-> orWords   (qw( or  i));
     $c-> orWordsX  (qw( orX i));
     $c->notWords   (qw(n    i));
     $c->outputBits (qw(And  and));
     $c->outputBits (qw(AndX andX));
     $c->outputBits (qw(Or   or));
     $c->outputBits (qw(OrX  orX));
     $c->outputWords(qw(N    n));
  my %d = setWords($c, 'i', 0b00, 0b01, 0b10, 0b11);
  my $s = $c->simulate({%d}, svg=>"svg/andOrWords$W");

  is_deeply($s->bint('And'),  0b1000);
  is_deeply($s->bint('AndX'), 0b0000);

  is_deeply($s->bint('Or'),  0b1110);
  is_deeply($s->bint('OrX'), 0b11);
  is_deeply([$s->wordsToInteger('N')], [3, 2, 1, 0]);
 }

#latest:;
if (1)                                                                          #TandWords #TorWords #TSilicon::Chip::Simulation::wordXToInteger #TSilicon::Chip::Simulation::wordsToInteger  #TinputWords #ToutputWords
 {my @b = ((my $W = 4), (my $B = 3));

  my $c = newChip();
     $c->inputWords ('i',      @b);
     $c->outputWords(qw(o i));

  my %d = setWords($c, 'i', 0b000, 0b001, 0b010, 0b011);
  my $s = $c->simulate({%d}, svg=>"svg/words$W");

  is_deeply([$s->wordsToInteger('o')], [0..3]);
  is_deeply([$s->wordXToInteger('o')], [10, 12, 0]);
 }

#latest:;
if (1)                                                                          #TchooseFromTwoWords
 {my $B = 4;

  my $c = newChip();
     $c->inputBits('a', $B);                                                    # First word
     $c->inputBits('b', $B);                                                    # Second word
     $c->input    ('c');                                                        # Chooser
     $c->chooseFromTwoWords(qw(o a b c));                                       # Generate gates
     $c->outputBits('out', 'o');                                                # Result

  my %a = setBits($c, 'a', 0b0011);
  my %b = setBits($c, 'b', 0b1100);

  my $s = $c->simulate({%a, %b, c=>1}, svg=>q(svg/chooseFromTwoWords));
  is_deeply($s->steps,               4);
  is_deeply($s->bint('out'), 0b1100);

  my $t = $c->simulate({%a, %b, c=>0});
  is_deeply($t->steps,               4);
  is_deeply($t->bint('out'), 0b0011);
 }

#latest:;
if (1)                                                                          #TenableWord
 {my $B = 4;

  my $c = newChip();
     $c->inputBits ('a', $B);                                                   # Word
     $c->input     ('c');                                                       # Choice bit
     $c->enableWord(qw(o a c));                                                 # Generate gates
     $c->outputBits(qw(out o));                                                 # Result

  my %a = setBits($c, 'a', 3);

  my $s = $c->simulate({%a, c=>1}, svg=>q(svg/enableWord));
  is_deeply($s->steps,               4);
  is_deeply($s->bint('out'), 3);

  my $t = $c->simulate({%a, c=>0});
  is_deeply($t->steps,               4);
  is_deeply($t->bint('out'), 0);
 }

#latest:;
if (1)                                                                          #TmonotoneMaskToPointMask
 {my $B = 4;

  my $c = newChip();
     $c->inputBits('m', $B);                                                    # Monotone mask
     $c->monotoneMaskToPointMask(qw(o m));                                      # Generate gates
     $c->outputBits('out', 'o');                                                # Point mask

  for my $i(0..$B)
   {my %m = $c->setBits('m', eval '0b'.(1 x $i).('0' x ($B-$i)));
    my $s = $c->simulate({%m});
    is_deeply($s->steps,                    2);
    is_deeply($s->bint('out'), $i ? (1<<($B-1)) / (1<<($i-1)) : 0);
   }
 }

#latest:;
if (1)                                                                          # Internal input gate  #TconnectInput #TSilicon::Chip::Simulation::value
 {my $c = newChip();
     $c->input ('i');                                                           # Input
     $c->input ('j');                                                           # Internal input which we will connect to later
     $c->output(qw(o j));                                                       # Output

     $c->connectInput(qw(j i));

  my $s = $c->simulate({i=>1});
  is_deeply($s->steps, 1);
  is_deeply($s->value("j"), undef);
  is_deeply($s->value("o"), 1);
 }

#latest:;
if (1)                                                                          #Twords # Internal input gate  #TconnectInput #TSilicon::Chip::Simulation::value
 {my @n = qw(3 2 1 2 3);
  my $c = newChip();
     $c->words('i', 2, @n);                                                     # Input
     $c->outputWords(qw(o i));                                                  # Output
  my $s = $c->simulate({});
  is_deeply($s->steps, 2);
  is_deeply([$s->wordsToInteger("i")], [@n]);
 }

#latest:;
if (1)                                                                          #Twords # Internal input gate  #TconnectInput #TSilicon::Chip::Simulation::value
 {my @n = qw(3 2 1 2 3);
  my $c = newChip();
     $c->words('i', 2, @n);                                                     # Input
     $c->outputWords(qw(o i));                                                  # Output
  my $s = $c->simulate({});
  is_deeply($s->steps, 2);
  is_deeply([$s->wordsToInteger("i")], [@n]);
 }

#latest:;
if (1)                                                                          #TsetSizeBits #TsetSizeWords
 {my $c = newChip();
  $c->setSizeBits ('i', 2);
  $c->setSizeWords('j', 3, 2);
  is_deeply($c->sizeBits,  {i => 2, j_1 => 2, j_2 => 2, j_3 => 2});
  is_deeply($c->sizeWords, {j => [3, 2]});
 }

#done_testing();
finish: 1;
