// Path: src/main/java/com/github/aelstad/keccakj/core/KeccakStateUtils.java
// public enum StateOp {
// 	ZERO, GET, VALIDATE, XOR_IN, XOR_TRANSFORM, WRAP, UNWRAP;
// 
// 	public boolean isIn() {
// 		return (this ==StateOp.XOR_IN || this == XOR_TRANSFORM || this ==StateOp.WRAP || this == StateOp.UNWRAP || this == VALIDATE);
// 	}
// 
// 	public boolean isOut() {
// 		return (this == StateOp.GET || this == XOR_TRANSFORM || this==StateOp.WRAP || this == StateOp.UNWRAP);
// 	}
// 
// };
// 
// Path: src/main/java/com/github/aelstad/keccakj/io/BitInputStream.java
// public abstract class BitInputStream extends InputStream {		
// 	@Override
// 	public abstract void close();
// 	
// 	
// 	@Override
// 	public int read(byte[] b, int off, int len) {
// 		return (int) (readBits(b, ((long)off)<<3, ((long)len)<<3)>>3);
// 	}
// 	
// 	/**
// 	 * Transform input to output with the input stream as a keystream 
// 	 * 
// 	 * @param input Input byte-array
// 	 * @param inputOff Input offset
// 	 * @param output Output byte-array
// 	 * @param outputOff Output offset
// 	 * @param len length in bytes
// 	 * @return Number of bytes transformed
// 	 */
// 	public int transform(byte[] input, int inputOff, byte[] output, int outputOff, int len) {
// 		return (int) (transformBits(input, ((long)inputOff)<<3, output, ((long)outputOff)<<3, ((long)len)<<3)>>3);
// 	}
// 
// 
// 	@Override
// 	public int read(byte[] b)  {
// 		return this.read(b, 0, b.length)>>3;
// 	}
// 	
// 	/**
// 	 * Transform input to output using the input stream as a keystream 
// 	 * 
// 	 * @param input Input byte array
// 	 * @param output Output byte array
// 	 * 
// 	 * @return Number of bytes transformed
// 	 */
// 	public int transform(byte[] input, byte[] output) {
// 		return (int) (transformBits(input, 0l, output, 0l, ((long)input.length)<<3)>>3);
// 	}
// 
// 
// 	@Override
// 	public int read() {
// 		byte[] buf = new byte[1];
// 		readBits(buf, 0, 8);
// 		
// 		return ((int) buf[0]) & 0xff;		
// 	}
// 
// 
// 	public abstract long readBits(byte[] arg, long bitOff, long bitLen);
// 	
// 	/**
// 	 * Transform input to output using the input stream as a keystream 
// 	 * 
// 	 * @param input Input byte array
// 	 * @param inputOff Input offset in bits
// 	 * @param output Output byte array
// 	 * @param outputOff Output offset in bits
// 	 * @param bitLen Number of bits
// 	 * @return Number of bits transformed
// 	 */
// 	public abstract long transformBits(byte[] input, long inputOff, byte[] output, long outputOff, long bitLen);
// }
// 
// Path: src/main/java/com/github/aelstad/keccakj/io/BitOutputStream.java
// public abstract class BitOutputStream extends OutputStream {
// 
// 	@Override
// 	public abstract void close();
// 
// 	@Override
// 	public void write(byte[] b, int off, int len) {
// 		writeBits(b, ((long) (off))<<3, ((long)len)<<3);
// 	}
// 
// 	@Override
// 	public void write(byte[] b) {
// 		write(b, 0, b.length);
// 	}
// 	
// 	@Override
// 	public void write(int b) {
// 		writeBits(new byte[] { (byte) b }, 0, 8);
// 	}
// 
// 	public abstract void writeBits(byte[] arg, long bitOff, long bitLen);
// 	
// }


import java.io.FilterOutputStream;
import java.io.IOException;
import java.util.Arrays;
import com.github.aelstad.keccakj.core.KeccakStateUtils.StateOp;
import com.github.aelstad.keccakj.io.BitInputStream;
import com.github.aelstad.keccakj.io.BitOutputStream;

	public KeccakSponge(int capacityInBits, byte domainPadding, int domainPaddingBitLength) {
		this.keccak1600 = new Keccak1600(capacityInBits);
		this.domainPadding = domainPadding;
		this.domainPaddingBitLength = domainPaddingBitLength;
		this.ratePos = 0;
	}

	public KeccakSponge(int rounds, int capacityInBits, byte domainPadding, int domainPaddingBitLength) {
		this.keccak1600 = new Keccak1600(capacityInBits, rounds);
		this.domainPadding = domainPadding;
		this.domainPaddingBitLength = domainPaddingBitLength;
		this.ratePos = 0;
	}

	public void reset() {
		if(absorbStream != null) {
			absorbStream.open();
		}
	}

	public BitInputStream getSqueezeStream() {
		if(squeezeStream == null) {
			squeezeStream = new SqueezeStream();
		}
		squeezeStream.open();

		return squeezeStream;
	}

// Path: src/main/java/com/github/aelstad/keccakj/core/KeccakStateUtils.java
// public enum StateOp {
// 	ZERO, GET, VALIDATE, XOR_IN, XOR_TRANSFORM, WRAP, UNWRAP;
// 
// 	public boolean isIn() {
// 		return (this ==StateOp.XOR_IN || this == XOR_TRANSFORM || this ==StateOp.WRAP || this == StateOp.UNWRAP || this == VALIDATE);
// 	}
// 
// 	public boolean isOut() {
// 		return (this == StateOp.GET || this == XOR_TRANSFORM || this==StateOp.WRAP || this == StateOp.UNWRAP);
// 	}
// 
// };
// 
// Path: src/main/java/com/github/aelstad/keccakj/io/BitInputStream.java
// public abstract class BitInputStream extends InputStream {		
// 	@Override
// 	public abstract void close();
// 	
// 	
// 	@Override
// 	public int read(byte[] b, int off, int len) {
// 		return (int) (readBits(b, ((long)off)<<3, ((long)len)<<3)>>3);
// 	}
// 	
// 	/**
// 	 * Transform input to output with the input stream as a keystream 
// 	 * 
// 	 * @param input Input byte-array
// 	 * @param inputOff Input offset
// 	 * @param output Output byte-array
// 	 * @param outputOff Output offset
// 	 * @param len length in bytes
// 	 * @return Number of bytes transformed
// 	 */
// 	public int transform(byte[] input, int inputOff, byte[] output, int outputOff, int len) {
// 		return (int) (transformBits(input, ((long)inputOff)<<3, output, ((long)outputOff)<<3, ((long)len)<<3)>>3);
// 	}
// 
// 
// 	@Override
// 	public int read(byte[] b)  {
// 		return this.read(b, 0, b.length)>>3;
// 	}
// 	
// 	/**
// 	 * Transform input to output using the input stream as a keystream 
// 	 * 
// 	 * @param input Input byte array
// 	 * @param output Output byte array
// 	 * 
// 	 * @return Number of bytes transformed
// 	 */
// 	public int transform(byte[] input, byte[] output) {
// 		return (int) (transformBits(input, 0l, output, 0l, ((long)input.length)<<3)>>3);
// 	}
// 
// 
// 	@Override
// 	public int read() {
// 		byte[] buf = new byte[1];
// 		readBits(buf, 0, 8);
// 		
// 		return ((int) buf[0]) & 0xff;		
// 	}
// 
// 
// 	public abstract long readBits(byte[] arg, long bitOff, long bitLen);
// 	
// 	/**
// 	 * Transform input to output using the input stream as a keystream 
// 	 * 
// 	 * @param input Input byte array
// 	 * @param inputOff Input offset in bits
// 	 * @param output Output byte array
// 	 * @param outputOff Output offset in bits
// 	 * @param bitLen Number of bits
// 	 * @return Number of bits transformed
// 	 */
// 	public abstract long transformBits(byte[] input, long inputOff, byte[] output, long outputOff, long bitLen);
// }
// 
// Path: src/main/java/com/github/aelstad/keccakj/io/BitOutputStream.java
// public abstract class BitOutputStream extends OutputStream {
// 
// 	@Override
// 	public abstract void close();
// 
// 	@Override
// 	public void write(byte[] b, int off, int len) {
// 		writeBits(b, ((long) (off))<<3, ((long)len)<<3);
// 	}
// 
// 	@Override
// 	public void write(byte[] b) {
// 		write(b, 0, b.length);
// 	}
// 	
// 	@Override
// 	public void write(int b) {
// 		writeBits(new byte[] { (byte) b }, 0, 8);
// 	}
// 
// 	public abstract void writeBits(byte[] arg, long bitOff, long bitLen);
// 	
// }


// Path: src/main/java/com/github/aelstad/keccakj/core/KeccakSponge.java
import java.io.FilterOutputStream;
import java.io.IOException;
import java.util.Arrays;
import com.github.aelstad.keccakj.core.KeccakStateUtils.StateOp;
import com.github.aelstad.keccakj.io.BitInputStream;
import com.github.aelstad.keccakj.io.BitOutputStream;


	public KeccakSponge(int capacityInBits, byte domainPadding, int domainPaddingBitLength) {
		this.keccak1600 = new Keccak1600(capacityInBits);
		this.domainPadding = domainPadding;
		this.domainPaddingBitLength = domainPaddingBitLength;
		this.ratePos = 0;
	}

	public KeccakSponge(int rounds, int capacityInBits, byte domainPadding, int domainPaddingBitLength) {
		this.keccak1600 = new Keccak1600(capacityInBits, rounds);
		this.domainPadding = domainPadding;
		this.domainPaddingBitLength = domainPaddingBitLength;
		this.ratePos = 0;
	}

	public void reset() {
		if(absorbStream != null) {
			absorbStream.open();
		}
	}

	public BitInputStream getSqueezeStream() {
		if(squeezeStream == null) {
			squeezeStream = new SqueezeStream();
		}
		squeezeStream.open();

		return squeezeStream;
	}

	public BitOutputStream getAbsorbStream() {