private int decryptBlockWithIV(
        byte[] in,
        int inOff,
        byte[] out,
        int outOff)
        throws DataLengthException, IllegalStateException
    {
        if ((inOff + blockSize) > in.length)
        {
            throw new DataLengthException("input buffer too short");
        }

        if ((outOff + blockSize) > out.length)
        {
            throw new DataLengthException("output buffer too short");
        }
        
        if (count == 0)
        {
            for (int n = 0; n < blockSize; n++) 
            {
                FR[n] = in[inOff + n];
            }
            
            cipher.processBlock(FR, 0, FRE, 0);

            count += blockSize;

            return 0;
        }
        else if (count == blockSize)
        {
            // copy in buffer so that this mode works if in and out are the same 
            System.arraycopy(in, inOff, tmp, 0, blockSize);
        
            System.arraycopy(FR, 2, FR, 0, blockSize - 2);
            
            FR[blockSize - 2] = tmp[0];
            FR[blockSize - 1] = tmp[1];

            cipher.processBlock(FR, 0, FRE, 0);

            for (int n = 0; n < blockSize - 2; n++) 
            {
                out[outOff + n] = encryptByte(tmp[n + 2], n);
            }

            System.arraycopy(tmp, 2, FR, 0, blockSize - 2);

            count += 2;

            return blockSize - 2;
        }
        else if (count >= blockSize + 2)
        {
            // copy in buffer so that this mode works if in and out are the same 
            System.arraycopy(in, inOff, tmp, 0, blockSize);

            out[outOff + 0] = encryptByte(tmp[0], blockSize - 2);
            out[outOff + 1] = encryptByte(tmp[1], blockSize - 1);

            System.arraycopy(tmp, 0, FR, blockSize - 2, 2);

            cipher.processBlock(FR, 0, FRE, 0);
            
            for (int n = 0; n < blockSize - 2; n++) 
            {
                out[outOff + n + 2] = encryptByte(tmp[n + 2], n);
            }
            
            System.arraycopy(tmp, 2, FR, 0, blockSize - 2);
            
        } 
        
        return blockSize;
    }