module top;
  reg passed = 1'b1;
  wire out, cout0, cout1;
  reg sel, in_1, in_0;
  reg pout;

`ifdef __ICARUS__
  // This is technically incorrect for 1'bz inputs. The standard
  // states that we should produce 1'bx for that case (idiotic)!
  localparam zz_blend = 1'bz;
`else
  localparam zz_blend = 1'bx;
`endif

  assign cout0 = sel ? 1'bz : in_0;
  assign cout1 = sel ? in_1: 1'bz;
  assign out = sel ? in_1: in_0;

  task automatic check;
    input bit, in_1, in_0;
    input [63:0] comment;
    begin
      if (sel === 1'b1) begin
        if (bit !== in_1) begin
          $display("FAILED: %0s sel = 1'b1, expected %b, got %b",
                   comment, in_1, bit);
          passed = 1'b0;
        end
      end else if (sel === 1'b0) begin
        if (bit !== in_0) begin
          $display("FAILED: %0s sel = 1'b0, expected %b, got %b",
                   comment, in_0, bit);
          passed = 1'b0;
        end
      end else begin
        if (in_0 === 1'bz && in_1 === 1'bz && bit !== zz_blend) begin
          $display("FAILED: %0s sel = 1'bx/z & ins = %b, expected 1'b%b, got %b",
                   comment, in_0, zz_blend, bit);
          passed = 1'b0;
        end else if (in_0 === in_1 && in_0 !== bit) begin
          $display("FAILED: %0s sel = 1'bx/z & ins = %b, expected 1'b%b, got %b",
                   comment, in_0, in_0, bit);
          passed = 1'b0;
        end else if (in_0 !== in_1 && bit !== 1'bx) begin
          $display("FAILED: %0s sel = 1'bx/z & %b %b, expected 1'bx, got %b",
                   comment, in_1, in_0, bit);
          passed = 1'b0;
        end
      end
    end
  endtask

  // Check the 1 case as a constant Z
  always @(cout0) begin
    check(cout0, 1'bz, in_0, "CZ 1");
  end

  // Check the 0 case as a constant Z
  always @(cout1) begin
    check(cout1, in_1, 1'bz, "CZ 0");
  end

  // Check the continuous assign
  always @(out) begin
    check(out, in_1, in_0, "CA");
  end

  // Check procedural assign.
  always @(sel, in_1, in_0) begin
    check(sel ? in_1 : in_0, in_1, in_0, "PR");
  end

  initial begin
    #1 sel = 1'b1;
    #1 in_1 = 1'b0;
    #1 in_1 = 1'b1;
    #1 in_1 = 1'bz;
    #1 in_1 = 1'bx;

    #1 sel = 1'b0;
    #1 in_0 = 1'b0;
    #1 in_0 = 1'b1;
    #1 in_0 = 1'bz;
    #1 in_0 = 1'bx;

    #1 sel = 1'bx;
    #1 in_1 = 1'b0; //
    #1 in_0 = 1'b0;
    #1 in_0 = 1'b1;
    #1 in_0 = 1'bz;
    #1 in_0 = 1'bx;
    #1 in_1 = 1'b1; //
    #1 in_0 = 1'b0;
    #1 in_0 = 1'b1;
    #1 in_0 = 1'bz;
    #1 in_0 = 1'bx;
    #1 in_1 = 1'bz; //
    #1 in_0 = 1'b0;
    #1 in_0 = 1'b1;
    #1 in_0 = 1'bz;
    #1 in_0 = 1'bx;
    #1 in_1 = 1'bx; //
    #1 in_0 = 1'b0;
    #1 in_0 = 1'b1;
    #1 in_0 = 1'bz;
    #1 in_0 = 1'bx;

    #1 sel = 1'bz;
    #1 in_1 = 1'b0; //
    #1 in_0 = 1'b0;
    #1 in_0 = 1'b1;
    #1 in_0 = 1'bz;
    #1 in_0 = 1'bx;
    #1 in_1 = 1'b1; //
    #1 in_0 = 1'b0;
    #1 in_0 = 1'b1;
    #1 in_0 = 1'bz;
    #1 in_0 = 1'bx;
    #1 in_1 = 1'bz; //
    #1 in_0 = 1'b0;
    #1 in_0 = 1'b1;
    #1 in_0 = 1'bz;
    #1 in_0 = 1'bx;
    #1 in_1 = 1'bx; //
    #1 in_0 = 1'b0;
    #1 in_0 = 1'b1;
    #1 in_0 = 1'bz;
    #1 in_0 = 1'bx;

    #1 if (passed) $display("PASSED");
  end
endmodule
