The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
/* list.c */

#include "common.h"
#include "list.h"

#define list_SORTED		(1 << 0)

int list_append( list * l, const list_ITY p ) {
    l->flags &= ~list_SORTED;
    return buffer_append( l, &p, sizeof( p ) );
}

int list_build( list * l, const list_ITY v, size_t sz ) {
    int err;

    if ( err = list_init( l, sz ), ERR_None != err ) {
        return err;
    }

    for ( ; v; v = list_NEXT( v ) ) {
        if ( err = list_append( l, v ), ERR_None != err ) {
            return err;
        }
    }

    return ERR_None;
}

static int list_compare( const void *a, const void *b ) {
    return list_CMP( a, b );
}

void list_sort( list * l ) {
    if ( ( l->flags & list_SORTED ) == 0 ) {
        qsort( l->buf, list_used( l ), list_ISZ, list_compare );
        l->flags |= list_SORTED;
    }
}

long list_true_diff( list * a, list * b, const void *p,
                     list_callback added, list_callback removed ) {
    list_ITY *a_ar;
    list_ITY *b_ar;
    long a_p, b_p, diff;
    size_t a_sz, b_sz;

    list_sort( a );
    list_sort( b );

    a_ar = list_ar( a );
    a_sz = list_used( a );
    a_p = 0;
    b_ar = list_ar( b );
    b_sz = list_used( b );
    b_p = 0;
    diff = 0;

    while ( a_p < a_sz || b_p < b_sz ) {
        while ( a_p < a_sz &&
                ( b_p == b_sz ||
                  list_CMP( &b_ar[b_p], &a_ar[a_p] ) > 0 ) ) {
            if ( removed ) {
                removed( a_ar[a_p], p );
            }
            a_p++;
            diff++;
        }
        while ( b_p < b_sz &&
                ( a_p == a_sz ||
                  list_CMP( &a_ar[a_p], &b_ar[b_p] ) > 0 ) ) {
            if ( added ) {
                added( b_ar[b_p], p );
            }
            b_p++;
            diff++;
        }
        while ( a_p < a_sz && b_p < b_sz &&
                list_CMP( &a_ar[a_p], &b_ar[b_p] ) == 0 ) {
            a_p++;
            b_p++;
        }
    }

    return diff;
}