def crypt(self, input_block, encrypt_mode):
        """
        DES Encrypt 
        INPUT:
            input_block, Int or Long (64bit)
            encrypt_mode, Bool (True = Encrypt Mode; False = Decrypt Mode)
        OUTPUT:
            output_block, Int or Long (64bit)
        """
        assert (type(input_block) == type(1) or type(input_block) == type(1L))
        assert (type(self.key) == type(1) or type(self.key) == type(1L))
        assert (type(encrypt_mode) == type(True))

        permuted_block = 0
        # Initial Permutation
        for i in range(0, 64):
            permuted_block = bits.set_bit(
                permuted_block, i + 1,
                bits.get_bit(input_block, self.DES_IP[i], 64), 64)

        if DEBUG is True:
            print("Permuted: {0}".format(hex(permuted_block)))

        # Permuted Input L and R
        left_block = bits.get_bits(permuted_block, 1, 32, 64, 32)  # 32bits
        right_block = bits.get_bits(permuted_block, 33, 64, 64, 32)  # 32bits

        if DEBUG is True:
            print("Permuted L: {0} | Permuted R: {1}".format(
                hex(left_block), hex(right_block)))

        # Fiestal Rounds
        for i in range(0, 16):
            temp_left = left_block
            left_block = right_block  # 32bits, temporary holder
            if encrypt_mode is True:
                right_block = temp_left ^ self.cipher_function(
                    right_block, self.key_schedule_list[i])
            else:
                right_block = temp_left ^ self.cipher_function(
                    right_block, self.key_schedule_list[15 - i])

        # Preoutput Block R'L'
        preoutput_block = (right_block << 32) + left_block  # 64bits

        # Inverse Initial Permutation
        output_block = 0
        for i in range(0, 64):
            output_block = bits.set_bit(
                output_block, i + 1,
                bits.get_bit(preoutput_block, self.DES_FP[i], 64), 64)

        return output_block
    def cipher_function(self, r, k):
        """
            The DES Cipher Function
            
            INPUTS:
                r, Integer or Long (32bit)
                k, Int or Long (48bit)
            OUTPUT:
                Integer or Long (32bit)
        """
        assert (type(r) == type(1) or type(r) == type(1L))
        assert (type(k) == type(1) or type(k) == type(1L))

        expanded_r = 0  # 48bit
        for i in range(0, 48):
            expanded_r = bits.set_bit(expanded_r, i + 1,
                                      bits.get_bit(r, self.DES_E[i], 32), 48)

        output = expanded_r ^ k  # 48bit in size

        # Substitution Boxes
        chunks = []  # Each chunk is 6 bits
        for i in range(0, 8):
            chunks.append(bits.get_bits(output, i * 6 + 1, (i + 1) * 6, 48, 6))

        chunk_row = lambda chunk: (bits.get_bit(
            chunk, 1, 6) << 1) + bits.get_bit(chunk, 6, 6)  # 2 Bit Integer

        chunk_col = lambda chunk: bits.get_bits(chunk, 2, 5, 6, 4
                                                )  # 4 Bit Integer

        substitute_chunk = lambda chunk, box: \
            box[chunk_row(chunk)][chunk_col(chunk)]

        output = 0  # 32bit
        for i in range(0, 8):
            output += substitute_chunk(chunks[i], self.DES_S[i]) << (
                (7 - i) * 4)

        final_output = 0  #32bit
        for i in range(0, 32):
            final_output = bits.set_bit(
                final_output, i + 1, bits.get_bit(output, self.DES_P[i], 32),
                32)

        return final_output
    def crypt(self, input_block, encrypt_mode):
        """
        DES Encrypt 
        INPUT:
            input_block, Int or Long (64bit)
            encrypt_mode, Bool (True = Encrypt Mode; False = Decrypt Mode)
        OUTPUT:
            output_block, Int or Long (64bit)
        """
        assert(type(input_block) == type(1) or type(input_block) == type(1L))
        assert(type(self.key) == type(1) or type(self.key) == type(1L))
        assert(type(encrypt_mode) == type(True))

        permuted_block = 0
        # Initial Permutation
        for i in range(0, 64):
            permuted_block = bits.set_bit(permuted_block, i+1, bits.get_bit(input_block, self.DES_IP[i], 64), 64)

        if DEBUG is True:
            print("Permuted: {0}".format(hex(permuted_block)))

        # Permuted Input L and R
        left_block = bits.get_bits(permuted_block, 1, 32, 64, 32) # 32bits
        right_block = bits.get_bits(permuted_block, 33, 64, 64, 32) # 32bits

        if DEBUG is True:
            print("Permuted L: {0} | Permuted R: {1}".format(hex(left_block), hex(right_block)))

        # Fiestal Rounds
        for i in range(0, 16):
            temp_left = left_block
            left_block = right_block # 32bits, temporary holder
            if encrypt_mode is True:
                right_block = temp_left ^ self.cipher_function(right_block, self.key_schedule_list[i])
            else:
                right_block = temp_left ^ self.cipher_function(right_block, self.key_schedule_list[15-i])

        # Preoutput Block R'L'
        preoutput_block = (right_block << 32) + left_block # 64bits

        # Inverse Initial Permutation
        output_block = 0
        for i in range(0, 64):
            output_block = bits.set_bit(output_block, i+1, bits.get_bit(preoutput_block, self.DES_FP[i], 64), 64)    

        return output_block
    def cipher_function(self, r, k):
        """
            The DES Cipher Function
            
            INPUTS:
                r, Integer or Long (32bit)
                k, Int or Long (48bit)
            OUTPUT:
                Integer or Long (32bit)
        """
        assert(type(r) == type(1) or type(r) == type(1L))
        assert(type(k) == type(1) or type(k) == type(1L))

        expanded_r = 0 # 48bit
        for i in range(0, 48):
            expanded_r = bits.set_bit(expanded_r, i+1, bits.get_bit(r, self.DES_E[i], 32), 48)

        output = expanded_r ^ k # 48bit in size

        # Substitution Boxes
        chunks = [] # Each chunk is 6 bits
        for i in range(0, 8):
            chunks.append(bits.get_bits(output, i * 6 + 1, (i + 1) * 6, 48, 6))

        chunk_row = lambda chunk: (bits.get_bit(chunk, 1, 6) << 1) + bits.get_bit(chunk, 6, 6) # 2 Bit Integer

        chunk_col = lambda chunk: bits.get_bits(chunk, 2, 5, 6, 4) # 4 Bit Integer

        substitute_chunk = lambda chunk, box: \
            box[chunk_row(chunk)][chunk_col(chunk)]

        output = 0 # 32bit
        for i in range(0, 8):
            output += substitute_chunk(chunks[i], self.DES_S[i]) << ((7 - i) * 4)

        final_output = 0 #32bit
        for i in range(0, 32):
            final_output = bits.set_bit(final_output, i+1, bits.get_bit(output, self.DES_P[i], 32), 32)

        return final_output