//  FOPS3.C

#include <string.h>
#include <memory.h>
#include <assert.h>
#include <stdlib.h>

#include "me5.h"

//  COMPRESS #defined in ME5.H
#define COPULATION true
#define ADDITION true
#define INNER true
#define SHUFFLE true

//  Block to be encrypted is at buffer, and consists of block_size bytes.
//  1 <= block_size <= 9212
/*--------------------*/
void encrypt_block(void)
{
#if INNER || ADDITION
unsigned int i;
#endif

#if INNER
unsigned int j, segment_size;
#endif

if ( demo )
    {
    i = (rand()%9) + 1;        //  1 <= i <= 9

    block_size = (i*block_size)/(i+1);
    //  Multiply block_size by between 1/2 and 9/10 to simulate compression.

    //  Fill buffer with random bytes (overwrite plaintext).
    for ( i=0; i<block_size; i++ )
        buffer[i] = (unsigned char)(rand() + i);

    me5_expand_block = (rand()>>2)&1;
    }
else
    {
    #if COPULATION
    //  Divide block at a random point and interchange the halves.
    if ( first_half_size > 0 )
        {
        memcpy(buffer1,buffer,first_half_size);
        memmove(buffer,buffer+first_half_size,block_size-first_half_size);
        memcpy(buffer+block_size-first_half_size,buffer1,first_half_size);
        }
    #endif

    #if COMPRESS
    //  Attempt to compress the block.
    block_size = me5_compress_block(block_size,comp_fact);
    //  Second parameter must be in the range 10 through 12.
    //  This sets the me5_expand_block flag to true iff compression occurs.
    //  1 <= block_size <= 9212
    #else
    me5_expand_block = false;
    #endif

    #if ADDITION
    //  Add or subtract random numbers to bytes in block.
    for ( i=0; i<block_size; i++ )
        {
        if ( rnd()%3 )
            buffer[i] += pm();
        else
            buffer[i] -= pm();
        }
    #endif

    #if INNER
    //  Use the inner encryption process on the "plaintext" segments of this block.
    //  This does not change block_size.

    j = 0;

    while ( true )
        {
        //  Copy plaintext for use by define_next_keytext_segment().
        memcpy(pt,&buffer[j],keylength);
        if ( j + keylength <= block_size )
            segment_size = keylength;
        else
            segment_size = block_size - j;
        for ( i=0; i<segment_size; i++ )
            buffer[j+i] ^= kt[i];
        #if SHUFFLE
        shuffle(&buffer[j],segment_size);
        #endif
        j += keylength;
        if ( j >= block_size )
            break;
        segment++;
        define_next_keytext_segment();
        } 
    #endif
    }
}
 
//  Returns true if no error, otherwise
//  false because of block expansion error
/*-------------------*/
int decrypt_block(void)
{
#if INNER || ADDITION
unsigned int i;
#endif

#if INNER
unsigned int j, segment_size;
#endif

#if COMPRESS
int err_flag;
#endif

#if ADDITION
//  Generate the random numbers need for the addition/subtraction operation 
//  which follows the inner decryption.
for ( i=0; i<block_size; i++ )
    {
    if ( rnd()%3 )
        pm_buff[i] = 256 - pm();
    else
        pm_buff[i] = pm();
    }
#endif

#if INNER
//  Use the inner decryption process on the ciphertext segments of this block.
//  This does not change block_size.

j = 0;

while ( true )
    {
    if ( j + keylength <= block_size )
        segment_size = keylength;
    else
        segment_size = block_size - j;
    #if SHUFFLE
    unshuffle(&buffer[j],rnd_int_buff,segment_size);
    #endif
    for ( i=0; i<segment_size; i++ )
        buffer[j+i] ^= kt[i];
    //  Copy plaintext for use by crypt().
    memcpy(pt,&buffer[j],keylength);
    j += keylength;
    if ( j >= block_size )
        break;
    segment++;
    define_next_keytext_segment();
    } 
#endif

#if ADDITION 
//  Perform the addition/subtraction operation.
for ( i=0; i<block_size; i++ )
    buffer[i] += pm_buff[i];
#endif

#if COMPRESS
//  Expand the block if it was compressed.
if ( me5_expand_block )
    {
    block_size = me5_decompress_block(block_size,comp_fact,&err_flag);
    //  Returns error code in err_flag:
    //   0 = no error
    //  -1 if output buffer overflow
    //  -2 if code expansion error
    //  -3 if returned block size is zero
    if ( err_flag )
        return ( false );
    }
#endif

first_half_size %= block_size;

#if COPULATION
//  Interchange the two halves to recover the original plaintext.
if ( first_half_size > 0 )
    {
    memcpy(buffer1,buffer,block_size-first_half_size);
    memmove(buffer,buffer+block_size-first_half_size,first_half_size);
    memcpy(buffer+first_half_size,buffer1,block_size-first_half_size);
    }
#endif

return ( true );
}