import jazz.circuit.*;
import jazz.circuit.expr.BoolExpr.*;

// Create the following meta-circuit:
//
//   input I0, I1;
//   output O;
//
//   O = (I0 ^ P) & (reg(O) & I1)
//   P = #1(10)
//
// and return a function that can be used to instanciate the meta-circuit
// on a given boolean algebra given
//
//   - the register operator on that algebra
//   - the constant generator for that algebra
//   - two inputs I0 and I1 in that algebra
//
var circuit = ( O.setEq(I0 ^ P)
              ; O.setAnd(reg(O) & I1)
              ; create )
{
  I0 = newInput(0);
  I1 = newInput(1);
  O = newLocal();
  P = constant(#1(10));
  
  fun create(r, c)(i0, i1) = instanciate([O])([i0, i1], r, c)[0];
}

// Instanciate the circuit on nets
device NetCircuit {
  input i0: Net;
  input i1: Net;
  output o: Net;
  
  // Instanciate the circuit on nets, giving the input values as well as the
  // delay and constant functions for nets (i.e., "reg" and "Net" respectively)
  o = circuit(Net.reg, Net.constant)(i0, i1);
}

// Export the device
export NetCircuit();

// Instanciate the circuit on streams
device StreamCircuit {
  public n: int;
  input i0: Net[n];
  input i1: Net[n];
  output o: Net[n];

  // Package the input as streams (array of nets)
  I0 = new Stream(nets = i0);
  I1 = new Stream(nets = i1);

  // Instanciate the circuit on streams, giving the input values as well as
  // the delay and constant functions for streams
  O = circuit(Stream.reg, Stream.constant)(I0, I1);

  // Unpackage the nets from the stream
  o = O.nets;
}

// Export the device for n=4
export StreamCircuit(n = 4);