/*
 * This file was generated automatically by ExtUtils::ParseXS version 3.63 from the
 * contents of Key.xs. Do not edit this file, edit Key.xs instead.
 *
 *    ANY CHANGES MADE HERE WILL BE LOST!
 *
 */

#line 1 "Key.xs"
/* -*- Mode: C -*- */

#define PERL_NO_GET_CONTEXT 1

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

#if (PERL_VERSION < 7)
#include "sort.h"
#endif

static I32
ix_sv_cmp(pTHX_ SV **a, SV **b) {
    return sv_cmp(*a, *b);
}

static I32
ix_rsv_cmp(pTHX_ SV **a, SV **b) {
    return sv_cmp(*b, *a);
}

static I32
ix_lsv_cmp(pTHX_ SV **a, SV **b) {
    return sv_cmp_locale(*a, *b);
}

static I32
ix_rlsv_cmp(pTHX_ SV **a, SV **b) {
    return sv_cmp_locale(*b, *a);
}

static I32
ix_n_cmp(pTHX_ NV *a, NV *b) {
    NV nv1 = *a;
    NV nv2 = *b;
    return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0;
}

static I32
ix_rn_cmp(pTHX_ NV *a, NV *b) {
    NV nv1 = *b;
    NV nv2 = *a;
    return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0;
}

static I32
ix_i_cmp(pTHX_ IV *a, IV *b) {
    IV iv1 = *a;
    IV iv2 = *b;
    return iv1 < iv2 ? -1 : iv1 > iv2 ? 1 : 0;
}

static I32
ix_ri_cmp(pTHX_ IV *a, IV *b) {
    IV iv1 = *b;
    IV iv2 = *a;
    return iv1 < iv2 ? -1 : iv1 > iv2 ? 1 : 0;
}

static I32
ix_u_cmp(pTHX_ UV *a, UV *b) {
    UV uv1 = *a;
    UV uv2 = *b;
    return uv1 < uv2 ? -1 : uv1 > uv2 ? 1 : 0;
}

static I32
ix_ru_cmp(pTHX_ UV *a, UV *b) {
    UV uv1 = *b;
    UV uv2 = *a;
    return uv1 < uv2 ? -1 : uv1 > uv2 ? 1 : 0;
}

static void *v_alloc(pTHX_ IV n, IV lsize) {
    void *r;
    Newxc(r, n<<lsize, char, void);
    SAVEFREEPV(r);
    return r;
}

static void *av_alloc(pTHX_ IV n, IV lsize) {
    AV *av=(AV*)sv_2mortal((SV*)newAV());
    av_fill(av, n-1);
    return AvARRAY(av);
}

static void i_store(pTHX_ SV *v, void *to) {
    *((IV*)to)=SvIV(v);
}

static void u_store(pTHX_ SV *v, void *to) {
    *((UV*)to)=SvUV(v);
}

static void n_store(pTHX_ SV *v, void *to) {
    *((NV*)to)=SvNV(v);
}

static void sv_store(pTHX_ SV *v, void *to) {
    *((SV**)to)=SvREFCNT_inc(v);
}

#define lsizeof(A) (ilog2(sizeof(A)))


static int ilog2(int i) {
    if (i>256) croak("internal error");
    if (i>128) return 8;
    if (i>64) return 7;
    if (i>32) return 6;
    if (i>16) return 5;
    if (i>8) return 4;
    if (i>4) return 3;
    if (i>2) return 2;
    if (i>1) return 1;
    return 0;
}

/* sorting types:

   0 => string
   1 => locale
   2 => number
   3 => integer
   4 => unsigned_integer
   5 => single precission float - not implemented
   
   128 => reverse string
   129 => reverse locale
   130 => reverse number
   131 => reverse integer
   132 => reverse unsigned_integer
   133 => reverse s. p. float - not implemented

*/

typedef I32 (*COMPARE_t)(pTHX_ void*, void*);
typedef void (*STORE_t)(pTHX_ SV*, void*);

static void
_keysort(pTHX_ IV type, SV *keygen, SV **values, I32 offset, I32 ax, IV len) {
    dSP;
    if (len) {
	void *keys;
	void **ixkeys;
	IV i;
	SV **from, **to;

	IV lsize;
	COMPARE_t cmp;
	STORE_t store;

#if (PERL_VERSION < 9)
        int hints = PL_curcop->op_private;
#else
        int hints = CopHINTS_get(PL_curcop);
#endif

        /* fprintf (stderr, "hints=0x%x, int=0x%x, loc=0x%x\n", hints, HINT_INTEGER, HINT_LOCALE );fflush(stderr); */

	switch(type) {
	case 0:
	case 128:
	    if (hints & HINT_LOCALE) type = type | 128;
	    break;
	case 2:
	case 130:
	    if (hints & HINT_INTEGER) type = type | 1;
	    break;
	}

	switch(type) {
	case 0:
	    cmp = (COMPARE_t)&ix_sv_cmp;
	    lsize = lsizeof(SV*);
	    keys = av_alloc(aTHX_ len, lsize);
	    store = &sv_store;
	    break;
	case 1:
	    cmp = (COMPARE_t)&ix_lsv_cmp;
	    lsize = lsizeof(SV*);
	    keys = av_alloc(aTHX_ len, lsize);
	    store = &sv_store;
	    break;
	case 2:
	    cmp = (COMPARE_t)&ix_n_cmp;
	    lsize = lsizeof(NV);
	    keys = v_alloc(aTHX_ len, lsize);
	    store = &n_store;
	    break;
	case 3:
	    cmp = (COMPARE_t)&ix_i_cmp;
	    lsize = lsizeof(IV);
	    keys = v_alloc(aTHX_ len, lsize);
	    store = &i_store;
	    break;
	case 4:
	    cmp = (COMPARE_t)&ix_u_cmp;
	    lsize = lsizeof(UV);
	    keys = v_alloc(aTHX_ len, lsize);
	    store = &u_store;
	    break;
	case 128:
	    cmp = (COMPARE_t)&ix_rsv_cmp;
	    lsize = lsizeof(SV*);
	    keys = av_alloc(aTHX_ len, lsize);
	    store = &sv_store;
	    break;
	case 129:
	    cmp = (COMPARE_t)&ix_rlsv_cmp;
	    lsize = lsizeof(SV*);
	    keys = av_alloc(aTHX_ len, lsize);
	    store = &sv_store;
	    break;
	case 130:
	    cmp = (COMPARE_t)&ix_rn_cmp;
	    lsize = lsizeof(NV);
	    keys = v_alloc(aTHX_ len, lsize);
	    store = &n_store;
	    break;
	case 131:
	    cmp = (COMPARE_t)&ix_ri_cmp;
	    lsize = lsizeof(IV);
	    keys = v_alloc(aTHX_ len, lsize);
	    store = &i_store;
	    break;
	case 132:
	    cmp = (COMPARE_t)&ix_ru_cmp;
	    lsize = lsizeof(UV);
	    keys = v_alloc(aTHX_ len, lsize);
	    store = &u_store;
	    break;
	default:
	    croak("unsupported sort type %d", type);
	}

	Newx(ixkeys, len, void*);
	SAVEFREEPV(ixkeys);
	if (keygen) {
	    for (i=0; i<len; i++) {
		IV count;
		SV *current;
		SV *result;
		void *target;
		/* warn("values=%p SP=%p SP-len=%p, &ST(0)=%p\n", values, SP, SP-len, &ST(0)); */
		ENTER;
		SAVETMPS;
                SAVE_DEFSV;
		current = values ? values[i] : ST(i + offset);
		DEFSV = sv_2mortal(current ? SvREFCNT_inc(current) : newSV(0));
		PUSHMARK(SP);
		PUTBACK;
		count = call_sv(keygen, G_SCALAR);
		SPAGAIN;
		if (count != 1)
		    croak("wrong number of results returned from key generation sub");
		result = POPs;
		/* warn("key: %_\n", result); */
		ixkeys[i] = target = ((char*)keys) + (i << lsize);
		(*store)(aTHX_ result, target);
		FREETMPS;
		LEAVE;
	    }
	}
	else {
	    for (i = 0; i < len; i++) {
		void *target;
		SV *current = values ? values[i] : ST(i + offset);
		ixkeys[i] = target = ((char*)keys) + (i << lsize);

		(*store)(aTHX_
			 current ? current : sv_2mortal(newSV(0)),
			 target);
	    }
	}
	sortsv((SV**)ixkeys, len, (SVCOMPARE_t)cmp);
	if (values) {
	    from = to = values;
	}
	else {
	    from = &ST(offset);
	    to = &ST(0);
	}
	for(i = 0; i < len; i++) {
            IV j = ( ((char*)(ixkeys[i])) - ((char*)keys) )>>lsize;
	    ixkeys[i] = from[j];
	}
	for(i = 0; i < len; i++) {
	    to[i] = (SV*)ixkeys[i];
	}
    }
}

typedef struct multikey {
    COMPARE_t cmp;
    void *data;
    IV lsize;
} MK;


static I32 _multikeycmp(pTHX_ void *a, void *b) {
    MK *keys = (MK*)PL_sortcop;
    IV r = (*(keys->cmp))(aTHX_ a, b);
    if (r) 
	return r;
    else {
	IV ixa = ( ((char*)a) - ((char*)(keys->data)) ) >> keys->lsize;
	IV ixb = ( ((char*)b) - ((char*)(keys->data)) ) >> keys->lsize;
	COMPARE_t cmp;
	while(1) {
	    keys++;
	    cmp=keys->cmp;
	    if (!cmp)
		return 0;
	    a = ((char*)(keys->data))+(ixa<<keys->lsize);
	    b = ((char*)(keys->data))+(ixb<<keys->lsize);
	    r = (*cmp)(aTHX_ a, b);
	    if (r)
		return r;
	}
    }
    return 0; /* dead code just to remove warnings from some
	       * compilers */
}

static I32 _secondkeycmp(pTHX_ void *a, void *b) {
    MK *keys = (MK*)PL_sortcop;
    IV ixa = ( ((char*)a) - ((char*)(keys->data)) ) >> keys->lsize;
    IV ixb = ( ((char*)b) - ((char*)(keys->data)) ) >> keys->lsize;
    COMPARE_t cmp;
    while(1) {
	I32 r;
	keys++;
	cmp=keys->cmp;
	if (!cmp)
	    return 0;
	a = ((char*)(keys->data))+(ixa<<keys->lsize);
	b = ((char*)(keys->data))+(ixb<<keys->lsize);
	r = (*cmp)(aTHX_ a, b);
	if (r)
	    return r;
    }
    return 0; /* dead code just to remove warnings from some
	       * compilers */
}

static I32
ix_sv_mcmp(pTHX_ SV **a, SV **b) {
    I32 r = sv_cmp(*a, *b);
    if (r) return r;
    return _secondkeycmp(aTHX_ a, b);
}

static I32
ix_rsv_mcmp(pTHX_ SV **a, SV **b) {
    I32 r = sv_cmp(*b, *a);
    if (r) return r;
    return _secondkeycmp(aTHX_ a, b);
}

static I32
ix_lsv_mcmp(pTHX_ SV **a, SV **b) {
    I32 r = sv_cmp_locale(*a, *b);
    if (r) return r;
    return _secondkeycmp(aTHX_ a, b);
}

static I32
ix_rlsv_mcmp(pTHX_ SV **a, SV **b) {
    I32 r = sv_cmp_locale(*b, *a);
    if (r) return r;
    return _secondkeycmp(aTHX_ a, b);
}

static I32
ix_n_mcmp(pTHX_ NV *a, NV *b) {
    NV nv1 = *a;
    NV nv2 = *b;
    return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : _secondkeycmp(aTHX_ a, b);
}

static I32
ix_rn_mcmp(pTHX_ NV *a, NV *b) {
    NV nv1 = *b;
    NV nv2 = *a;
    return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : _secondkeycmp(aTHX_ a, b);
}

static I32
ix_i_mcmp(pTHX_ IV *a, IV *b) {
    IV iv1 = *a;
    IV iv2 = *b;
    return iv1 < iv2 ? -1 : iv1 > iv2 ? 1 : _secondkeycmp(aTHX_ a, b);
}

static I32
ix_ri_mcmp(pTHX_ IV *a, IV *b) {
    IV iv1 = *b;
    IV iv2 = *a;
    return iv1 < iv2 ? -1 : iv1 > iv2 ? 1 : _secondkeycmp(aTHX_ a, b);
}

static I32
ix_u_mcmp(pTHX_ UV *a, UV *b) {
    UV uv1 = *a;
    UV uv2 = *b;
    return uv1 < uv2 ? -1 : uv1 > uv2 ? 1 : _secondkeycmp(aTHX_ a, b);
}

static I32
ix_ru_mcmp(pTHX_ UV *a, UV *b) {
    UV uv1 = *b;
    UV uv2 = *a;
    return uv1 < uv2 ? -1 : uv1 > uv2 ? 1 : _secondkeycmp(aTHX_ a, b);
}

static void
_multikeysort(pTHX_ SV *keytypes, SV *keygen, SV *post,
	      SV**values, I32 from_offset, I32 ax, I32 len) {
    dSP;
    STRLEN nkeys;
    unsigned char *types=(unsigned char *)SvPV(keytypes, nkeys);

    if (nkeys<1)
	croak("empty multikey type list passed");

    if (len) {
	IV i;
	MK *keys;
	STORE_t *store;
	void **ixkeys;
	SV **from, **to;
	COMPARE_t cmp = (COMPARE_t)&_multikeycmp;

	Newx(keys, nkeys+1, MK);
	SAVEFREEPV(keys);
	Newx(store, nkeys, STORE_t);
	SAVEFREEPV(store);
	
	for(i=0; i<nkeys; i++) {
	    MK *key = keys+i;
	    switch(types[i]) {
	    case 0:
		if (i==0) cmp = (COMPARE_t)&ix_sv_mcmp;
		key->cmp = (COMPARE_t)&ix_sv_cmp;
		key->lsize = lsizeof(SV*);
		key->data = av_alloc(aTHX_ len, key->lsize);
		store[i] = &sv_store;
		break;
	    case 1:
		if (i==0) cmp = (COMPARE_t)&ix_lsv_mcmp;
		key->cmp = (COMPARE_t)&ix_lsv_cmp;
		key->lsize = lsizeof(SV*);
		key->data = av_alloc(aTHX_ len, key->lsize);
		store[i] = &sv_store;
		break;
	    case 2:
		if (i==0) cmp = (COMPARE_t)&ix_n_mcmp;
		key->cmp = (COMPARE_t)&ix_n_cmp;
		key->lsize = lsizeof(NV);
		key->data = v_alloc(aTHX_ len, key->lsize);
		store[i] = &n_store;
		break;
	    case 3:
		if (i==0) cmp = (COMPARE_t)&ix_i_mcmp;
		key->cmp = (COMPARE_t)&ix_i_cmp;
		key->lsize = lsizeof(IV);
		key->data = v_alloc(aTHX_ len, key->lsize);
		store[i] = &i_store;
		break;
	    case 4:
		if (i==0) cmp = (COMPARE_t)&ix_u_mcmp;
		key->cmp = (COMPARE_t)&ix_u_cmp;
		key->lsize = lsizeof(UV);
		key->data = v_alloc(aTHX_ len, key->lsize);
		store[i] = &u_store;
		break;
	    case 128:
		if (i==0) cmp = (COMPARE_t)&ix_rsv_mcmp;
		key->cmp = (COMPARE_t)&ix_rsv_cmp;
		key->lsize = lsizeof(SV*);
		key->data = av_alloc(aTHX_ len, key->lsize);
		store[i] = &sv_store;
		break;
	    case 129:
		if (i==0) cmp = (COMPARE_t)&ix_rlsv_mcmp;
		key->cmp = (COMPARE_t)&ix_rlsv_cmp;
		key->lsize = lsizeof(SV*);
		key->data = av_alloc(aTHX_ len, key->lsize);
		store[i] = &sv_store;
		break;
	    case 130:
		if (i==0) cmp = (COMPARE_t)&ix_rn_mcmp;
		key->cmp = (COMPARE_t)&ix_rn_cmp;
		key->lsize = lsizeof(NV);
		key->data = v_alloc(aTHX_ len, key->lsize);
		store[i] = &n_store;
		break;
	    case 131:
		if (i==0) cmp = (COMPARE_t)&ix_ri_mcmp;
		key->cmp = (COMPARE_t)&ix_ri_cmp;
		key->lsize = lsizeof(IV);
		key->data = v_alloc(aTHX_ len, key->lsize);
		store[i] = &i_store;
                break;
	    case 132:
		if (i==0) cmp = (COMPARE_t)&ix_ru_mcmp;
		key->cmp = (COMPARE_t)&ix_ru_cmp;
		key->lsize = lsizeof(UV);
		key->data = v_alloc(aTHX_ len, key->lsize);
		store[i] = &u_store;
		break;
	    default:
		croak("unsupported sort type %d", types[i]);
	    }
	}

	keys[nkeys].cmp = 0;
	keys[nkeys].data = 0;
	keys[nkeys].lsize = 0;
	    
	Newx(ixkeys, len, void*);
	SAVEFREEPV(ixkeys);
	for (i=0; i<len; i++) {
	    IV count;
	    SV *current;
	    void *target;
	    ENTER;
	    SAVETMPS;
            SAVE_DEFSV;
	    current = values ? values[i] : ST(i+from_offset);
	    DEFSV = sv_2mortal(current ? SvREFCNT_inc(current) : newSV(0));
	    PUSHMARK(SP);
	    PUTBACK;
	    count = call_sv(keygen, G_ARRAY);
	    SPAGAIN;
	    if (post) {
		PUSHMARK(SP-count);
		PUTBACK;
		count = call_sv(post, G_ARRAY);
		SPAGAIN;
	    }
	    if (count != nkeys)
		croak("wrong number of results returned "
		      "from multikey generation sub "
		      "(%d expected, %d returned)",
		      nkeys, count);
	    while(count-- > 0) {
		SV *result = POPs;
		MK *key = keys+count;
		target = ((char*)(key->data)) + (i<<key->lsize);
		(*(store[count]))(aTHX_ result, target);
	    }
	    ixkeys[i] = target;
	    FREETMPS;
	    LEAVE;
	}
	SAVEVPTR(PL_sortcop);
	PL_sortcop = (OP*)keys;
	sortsv((SV**)ixkeys, len, (SVCOMPARE_t)cmp);
	if (values) {
	    from = to = values;
	}
	else {
	    from = &ST(from_offset);
	    to = &ST(0);
	}
	for(i=0; i<len; i++) {
	    IV j = ( ((char*)(ixkeys[i])) - ((char*)(keys->data)) )>>keys->lsize;
	    ixkeys[i] = from[j];
	}
	for(i=0; i<len; i++) {
	    to[i] = (SV*)ixkeys[i];
	}
    }
}

static AV *
_xclosure_defaults(pTHX_ CV *cv) {
    MAGIC *magic = mg_find((SV*)cv, '~');
    if (magic) {
	if ( magic->mg_obj
	     && SvTYPE((SV*)(magic->mg_obj)) == SVt_PVAV )
	    return (AV*)(magic->mg_obj);
	croak("internal error: bad XSUB closure");
    }
    return NULL;
}

static void
_xclosure_make(pTHX_ CV *cv, AV *defaults) {
    sv_magic((SV*)cv, (SV*)defaults, '~', "XCLOSURE", 0);
}

XS(XS_Sort__Key__multikeysort);
XS(XS_Sort__Key__multikeysort)
{
    dXSARGS;
    SV *gen=0;
    SV *post=0;
    SV *types=0;
    IV offset=0;

    AV *defaults = _xclosure_defaults(aTHX_ cv);

    if (defaults) {
	types = *(av_fetch(defaults, 0, 1));
	gen = *(av_fetch(defaults, 1, 1));
	post = *(av_fetch(defaults, 2, 1));
	if (!SvOK(post))
	    post = 0;
    }

    if (!types || !SvOK(types)) {
	if (items--)
	    types = ST(offset++);
	else
	    croak("not enough arguments");

    }
    if (!gen || !SvOK(gen)) {
	if (items--)
	    gen = ST(offset++);
	else
	    croak("not enough arguments");
    }

    _multikeysort(aTHX_ types, gen, post, 0, offset, ax, items);
    SP=&ST(items-1);
    PUTBACK;
    return;
}


XS(XS_Sort__Key__multikeysort_inplace);
XS(XS_Sort__Key__multikeysort_inplace)
{
    dXSARGS;
    SV *gen = 0;
    SV *post = 0;
    SV *types = 0;
    AV *values;

    AV *magic_values=0;
    I32 len;
    I32 offset=0;

    AV *defaults = _xclosure_defaults(aTHX_ cv);

    if (defaults) {
	types = *(av_fetch(defaults, 0, 1));
	gen = *(av_fetch(defaults, 1, 1));
	post = *(av_fetch(defaults, 2, 1));
	if (!SvOK(post))
	    post = 0;
    }

    SP-=items;

    if (!types || !SvOK(types)) {
	if (items--)
	    types = ST(offset++);
	else
	    croak("not enough arguments, packed multikey type descriptor required");
    }
    if (!gen || !SvOK(gen)) {
	if (items--)
	    gen = ST(offset++);
	else
	    croak("not enough arguments, reference to multikey generation subroutine required");
    }

    if(!(SvROK(gen) && SvTYPE(SvRV(gen))==SVt_PVCV))
       croak("wrong argument type, subroutine reference required");

    if (items != 1)
	croak("not enough arguments, array reference required");

    if (SvROK(ST(offset)) && SvTYPE(SvRV(ST(offset)))==SVt_PVAV)
	values = (AV*)SvRV(ST(offset));
    else croak("wrong argument type, array reference required");

    if ((len=av_len(values)+1)) {
	/* warn("ix=%d\n", ix); */
	if (SvMAGICAL(values) || AvREIFY(values)) {
	    int i;
	    magic_values = values;
	    values = (AV*)sv_2mortal((SV*)newAV());
	    av_extend(values, len-1);
	    for (i=0; i<len; i++) {
		SV **currentp = av_fetch(magic_values, i, 0);
		av_store( values, i,
			  ( currentp
			    ? SvREFCNT_inc(*currentp)
			    : newSV(0) ) );
	    }
	}
	
	_multikeysort(aTHX_ types, gen, post, AvARRAY(values), 0, 0, len);
	
	if (magic_values) {
	    int i;
	    SV **values_array = AvARRAY(values);
	    for(i=0; i<len; i++) {
		SV *current = values_array[i];
		if (!current) current = &PL_sv_undef;
		if (!av_store(magic_values, i, SvREFCNT_inc(current)))
		    SvREFCNT_dec(current);
	    }
	}
    }
    PUTBACK;
}


#line 729 "Key.c"
#ifndef PERL_UNUSED_VAR
#  define PERL_UNUSED_VAR(var) if (0) var = var
#endif

#ifndef dVAR
#  define dVAR		dNOOP
#endif


/* This stuff is not part of the API! You have been warned. */
#ifndef PERL_VERSION_DECIMAL
#  define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
#endif
#ifndef PERL_DECIMAL_VERSION
#  define PERL_DECIMAL_VERSION \
	  PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
#endif
#ifndef PERL_VERSION_GE
#  define PERL_VERSION_GE(r,v,s) \
	  (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
#endif
#ifndef PERL_VERSION_LE
#  define PERL_VERSION_LE(r,v,s) \
	  (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
#endif

/* XS_INTERNAL is the explicit static-linkage variant of the default
 * XS macro.
 *
 * XS_EXTERNAL is the same as XS_INTERNAL except it does not include
 * "static", ie. it exports XSUB symbols. You probably don't want that
 * for anything but the BOOT XSUB.
 *
 * See XSUB.h in core!
 */


/* TODO: This might be compatible further back than 5.10.0. */
#if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1)
#  undef XS_EXTERNAL
#  undef XS_INTERNAL
#  if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING)
#    define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name)
#    define XS_INTERNAL(name) static XSPROTO(name)
#  endif
#  if defined(__SYMBIAN32__)
#    define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
#    define XS_INTERNAL(name) EXPORT_C static XSPROTO(name)
#  endif
#  ifndef XS_EXTERNAL
#    if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus)
#      define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__)
#      define XS_INTERNAL(name) static void name(pTHX_ CV* cv __attribute__unused__)
#    else
#      ifdef __cplusplus
#        define XS_EXTERNAL(name) extern "C" XSPROTO(name)
#        define XS_INTERNAL(name) static XSPROTO(name)
#      else
#        define XS_EXTERNAL(name) XSPROTO(name)
#        define XS_INTERNAL(name) static XSPROTO(name)
#      endif
#    endif
#  endif
#endif

/* perl >= 5.10.0 && perl <= 5.15.1 */


/* The XS_EXTERNAL macro is used for functions that must not be static
 * like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL
 * macro defined, the best we can do is assume XS is the same.
 * Dito for XS_INTERNAL.
 */
#ifndef XS_EXTERNAL
#  define XS_EXTERNAL(name) XS(name)
#endif
#ifndef XS_INTERNAL
#  define XS_INTERNAL(name) XS(name)
#endif

/* Now, finally, after all this mess, we want an ExtUtils::ParseXS
 * internal macro that we're free to redefine for varying linkage due
 * to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use
 * XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to!
 */

#undef XS_EUPXS
#if defined(PERL_EUPXS_ALWAYS_EXPORT)
#  define XS_EUPXS(name) XS_EXTERNAL(name)
#else
   /* default to internal */
#  define XS_EUPXS(name) XS_INTERNAL(name)
#endif

#ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
#define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)

/* prototype to pass -Wmissing-prototypes */
static void
S_croak_xs_usage(const CV *const cv, const char *const params);

static void
S_croak_xs_usage(const CV *const cv, const char *const params)
{
    const GV *const gv = CvGV(cv);

    PERL_ARGS_ASSERT_CROAK_XS_USAGE;

    if (gv) {
        const char *const gvname = GvNAME(gv);
        const HV *const stash = GvSTASH(gv);
        const char *const hvname = stash ? HvNAME(stash) : NULL;

        if (hvname)
	    Perl_croak_nocontext("Usage: %s::%s(%s)", hvname, gvname, params);
        else
	    Perl_croak_nocontext("Usage: %s(%s)", gvname, params);
    } else {
        /* Pants. I don't think that it should be possible to get here. */
	Perl_croak_nocontext("Usage: CODE(0x%" UVxf ")(%s)", PTR2UV(cv), params);
    }
}
#undef  PERL_ARGS_ASSERT_CROAK_XS_USAGE

#define croak_xs_usage        S_croak_xs_usage

#endif

/* NOTE: the prototype of newXSproto() is different in versions of perls,
 * so we define a portable version of newXSproto()
 */
#ifdef newXS_flags
#define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
#else
#define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
#endif /* !defined(newXS_flags) */

#if PERL_VERSION_LE(5, 21, 5)
#  define newXS_deffile(a,b) Perl_newXS(aTHX_ a,b,file)
#else
#  define newXS_deffile(a,b) Perl_newXS_deffile(aTHX_ a,b)
#endif

/* simple backcompat versions of the TARGx() macros with no optimisation */
#ifndef TARGi
#  define TARGi(iv, do_taint) sv_setiv_mg(TARG, iv)
#  define TARGu(uv, do_taint) sv_setuv_mg(TARG, uv)
#  define TARGn(nv, do_taint) sv_setnv_mg(TARG, nv)
#endif

#line 880 "Key.c"

XS_EUPXS(XS_Sort__Key_keysort); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Sort__Key_keysort)
{
    dVAR; dXSARGS;
    dXSI32;
    if (items < 1)
       croak_xs_usage(cv,  "keygen, ...");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	SV *	keygen = ST(0)
;
#line 736 "Key.xs"
    items--;
    if (items) {
	_keysort(aTHX_ ix, keygen, 0, 1, ax, items);
        SPAGAIN;
	SP = &ST(items-1);
    }
#line 901 "Key.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Sort__Key_keysort_inplace); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Sort__Key_keysort_inplace)
{
    dVAR; dXSARGS;
    dXSI32;
    if (items != 2)
       croak_xs_usage(cv,  "keygen, values");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
#line 748 "Key.xs"
    AV *magic_values=0;
    int len;
#line 921 "Key.c"
	SV *	keygen = ST(0)
;
	AV *	values;

	if (SvROK(ST(1)) && SvTYPE(SvRV(ST(1)))==SVt_PVAV)
	    values = (AV*)SvRV(ST(1));
	else
	    Perl_croak(aTHX_ "values is not an array reference")
;
#line 761 "Key.xs"
    if ((len=av_len(values)+1)) {
	/* warn("ix=%d\n", ix); */
	if (SvMAGICAL(values) || AvREIFY(values)) {
	    int i;
	    magic_values = values;
	    values = (AV*)sv_2mortal((SV*)newAV());
	    av_extend(values, len-1);
	    for (i=0; i<len; i++) {
		SV **currentp = av_fetch(magic_values, i, 0);
		av_store( values, i,
			  ( currentp
			    ? SvREFCNT_inc(*currentp)
			    : newSV(0) ) );
	    }
	}
	_keysort(aTHX_ ix, keygen, AvARRAY(values), 0, 0, len);
        SPAGAIN;
	if (magic_values) {
	    int i;
	    SV **values_array = AvARRAY(values);
	    for(i=0; i<len; i++) {
		SV *current = values_array[i];
		if (!current) current = &PL_sv_undef;
		if (!av_store(magic_values, i, SvREFCNT_inc(current)))
		    SvREFCNT_dec(current);
	    }
	}
    }
#line 960 "Key.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Sort__Key__sort); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Sort__Key__sort)
{
    dVAR; dXSARGS;
    dXSI32;
    PERL_UNUSED_VAR(cv); /* -W */
    PERL_UNUSED_VAR(items); /* -W */
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
#line 804 "Key.xs"
    if (items) {
	_keysort(aTHX_ ix, 0, 0, 0, ax, items);
        SPAGAIN;
	SP = &ST(items-1);
    }
#line 983 "Key.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Sort__Key__sort_inplace); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Sort__Key__sort_inplace)
{
    dVAR; dXSARGS;
    dXSI32;
    if (items != 1)
       croak_xs_usage(cv,  "values");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
#line 814 "Key.xs"
    AV *magic_values=0;
    int len;
#line 1003 "Key.c"
	AV *	values;

	if (SvROK(ST(0)) && SvTYPE(SvRV(ST(0)))==SVt_PVAV)
	    values = (AV*)SvRV(ST(0));
	else
	    Perl_croak(aTHX_ "values is not an array reference")
;
#line 827 "Key.xs"
    if ((len=av_len(values)+1)) {
	/* warn("ix=%d\n", ix); */
	if (SvMAGICAL(values) || AvREIFY(values)) {
	    int i;
	    magic_values = values;
	    values = (AV*)sv_2mortal((SV*)newAV());
	    av_extend(values, len-1);
	    for (i=0; i<len; i++) {
		SV **currentp = av_fetch(magic_values, i, 0);
		av_store( values, i,
			  ( currentp
			    ? SvREFCNT_inc(*currentp)
			    : newSV(0) ) );
	    }
	}

	_keysort(aTHX_ ix, 0, AvARRAY(values), 0, 0, len);
        SPAGAIN;
	if (magic_values) {
	    int i;
	    SV **values_array = AvARRAY(values);
	    for(i=0; i<len; i++) {
		SV *current = values_array[i];
		if (!current) current = &PL_sv_undef;
		if (!av_store(magic_values, i, SvREFCNT_inc(current)))
		    SvREFCNT_dec(current);
	    }
	}
    }
#line 1041 "Key.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Sort__Key__multikeysorter); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Sort__Key__multikeysorter)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "types, gen, post");
    {
#line 863 "Key.xs"
    AV *defaults;
#line 1057 "Key.c"
	CV *	RETVAL;
	SV *	types = ST(0)
;
	SV *	gen = ST(1)
;
	SV *	post = ST(2)
;
#line 865 "Key.xs"
    if (!SvOK(types) || sv_len(types)<1)
	croak("invalid packed types argument");
    RETVAL = newXS(0, &XS_Sort__Key__multikeysort, __FILE__);
    defaults = (AV*)sv_2mortal((SV*)newAV());
    av_store(defaults, 0, newSVsv(types));
    av_store(defaults, 1, newSVsv(gen));
    av_store(defaults, 2, newSVsv(post));
    _xclosure_make(aTHX_ RETVAL, defaults);
    if (!SvOK(gen))
	sv_setpv((SV*)RETVAL, "&@");
#line 1076 "Key.c"
	{
	    SV * RETVALSV;
	    RETVALSV = newRV((SV*)RETVAL);
	    RETVALSV = sv_2mortal(RETVALSV);
	    ST(0) = RETVALSV;
	}
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Sort__Key__multikeysorter_inplace); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Sort__Key__multikeysorter_inplace)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "types, gen, post");
    {
#line 881 "Key.xs"
    AV *defaults;
#line 1097 "Key.c"
	CV *	RETVAL;
	SV *	types = ST(0)
;
	SV *	gen = ST(1)
;
	SV *	post = ST(2)
;
#line 883 "Key.xs"
    if (!SvOK(types) || sv_len(types)<1)
	croak("invalid packed types argument");
    RETVAL = newXS(0, &XS_Sort__Key__multikeysort_inplace, __FILE__);
    defaults = (AV*)sv_2mortal((SV*)newAV());
    av_store(defaults, 0, newSVsv(types));
    av_store(defaults, 1, newSVsv(gen));
    av_store(defaults, 2, newSVsv(post));
    _xclosure_make(aTHX_ RETVAL, defaults);
    if (!SvOK(gen))
	sv_setpv((SV*)RETVAL, "&\\@");
    else
	sv_setpv((SV*)RETVAL, "\\@");
#line 1118 "Key.c"
	{
	    SV * RETVALSV;
	    RETVALSV = newRV((SV*)RETVAL);
	    RETVALSV = sv_2mortal(RETVALSV);
	    ST(0) = RETVALSV;
	}
    }
    XSRETURN(1);
}

#ifdef __cplusplus
extern "C" {
#endif
XS_EXTERNAL(boot_Sort__Key); /* prototype to pass -Wmissing-prototypes */
XS_EXTERNAL(boot_Sort__Key)
{
#if PERL_VERSION_LE(5, 21, 5)
    dVAR; dXSARGS;
#else
    dVAR; dXSBOOTARGSXSAPIVERCHK;
#endif
#if PERL_VERSION_LE(5, 8, 999) /* PERL_VERSION_LT is 5.33+ */
    char* file = __FILE__;
#else
    const char* file = __FILE__;
#endif

    PERL_UNUSED_VAR(file);

    PERL_UNUSED_VAR(cv); /* -W */
    PERL_UNUSED_VAR(items); /* -W */
#if PERL_VERSION_LE(5, 21, 5)
    XS_VERSION_BOOTCHECK;
#  ifdef XS_APIVERSION_BOOTCHECK
    XS_APIVERSION_BOOTCHECK;
#  endif
#endif

    {
        CV * cv;

        cv = newXSproto_portable("Sort::Key::ikeysort", XS_Sort__Key_keysort, file, "&@");
        XSANY.any_i32 = 3;
        cv = newXSproto_portable("Sort::Key::keysort", XS_Sort__Key_keysort, file, "&@");
        XSANY.any_i32 = 0;
        cv = newXSproto_portable("Sort::Key::lkeysort", XS_Sort__Key_keysort, file, "&@");
        XSANY.any_i32 = 1;
        cv = newXSproto_portable("Sort::Key::nkeysort", XS_Sort__Key_keysort, file, "&@");
        XSANY.any_i32 = 2;
        cv = newXSproto_portable("Sort::Key::rikeysort", XS_Sort__Key_keysort, file, "&@");
        XSANY.any_i32 = 131;
        cv = newXSproto_portable("Sort::Key::rkeysort", XS_Sort__Key_keysort, file, "&@");
        XSANY.any_i32 = 128;
        cv = newXSproto_portable("Sort::Key::rlkeysort", XS_Sort__Key_keysort, file, "&@");
        XSANY.any_i32 = 129;
        cv = newXSproto_portable("Sort::Key::rnkeysort", XS_Sort__Key_keysort, file, "&@");
        XSANY.any_i32 = 130;
        cv = newXSproto_portable("Sort::Key::rukeysort", XS_Sort__Key_keysort, file, "&@");
        XSANY.any_i32 = 132;
        cv = newXSproto_portable("Sort::Key::ukeysort", XS_Sort__Key_keysort, file, "&@");
        XSANY.any_i32 = 4;
        cv = newXSproto_portable("Sort::Key::ikeysort_inplace", XS_Sort__Key_keysort_inplace, file, "&\\@");
        XSANY.any_i32 = 3;
        cv = newXSproto_portable("Sort::Key::keysort_inplace", XS_Sort__Key_keysort_inplace, file, "&\\@");
        XSANY.any_i32 = 0;
        cv = newXSproto_portable("Sort::Key::lkeysort_inplace", XS_Sort__Key_keysort_inplace, file, "&\\@");
        XSANY.any_i32 = 1;
        cv = newXSproto_portable("Sort::Key::nkeysort_inplace", XS_Sort__Key_keysort_inplace, file, "&\\@");
        XSANY.any_i32 = 2;
        cv = newXSproto_portable("Sort::Key::rikeysort_inplace", XS_Sort__Key_keysort_inplace, file, "&\\@");
        XSANY.any_i32 = 131;
        cv = newXSproto_portable("Sort::Key::rkeysort_inplace", XS_Sort__Key_keysort_inplace, file, "&\\@");
        XSANY.any_i32 = 128;
        cv = newXSproto_portable("Sort::Key::rlkeysort_inplace", XS_Sort__Key_keysort_inplace, file, "&\\@");
        XSANY.any_i32 = 129;
        cv = newXSproto_portable("Sort::Key::rnkeysort_inplace", XS_Sort__Key_keysort_inplace, file, "&\\@");
        XSANY.any_i32 = 130;
        cv = newXSproto_portable("Sort::Key::rukeysort_inplace", XS_Sort__Key_keysort_inplace, file, "&\\@");
        XSANY.any_i32 = 132;
        cv = newXSproto_portable("Sort::Key::ukeysort_inplace", XS_Sort__Key_keysort_inplace, file, "&\\@");
        XSANY.any_i32 = 4;
        cv = newXSproto_portable("Sort::Key::_sort", XS_Sort__Key__sort, file, "@");
        XSANY.any_i32 = 0;
        cv = newXSproto_portable("Sort::Key::isort", XS_Sort__Key__sort, file, "@");
        XSANY.any_i32 = 3;
        cv = newXSproto_portable("Sort::Key::lsort", XS_Sort__Key__sort, file, "@");
        XSANY.any_i32 = 1;
        cv = newXSproto_portable("Sort::Key::nsort", XS_Sort__Key__sort, file, "@");
        XSANY.any_i32 = 2;
        cv = newXSproto_portable("Sort::Key::risort", XS_Sort__Key__sort, file, "@");
        XSANY.any_i32 = 131;
        cv = newXSproto_portable("Sort::Key::rlsort", XS_Sort__Key__sort, file, "@");
        XSANY.any_i32 = 129;
        cv = newXSproto_portable("Sort::Key::rnsort", XS_Sort__Key__sort, file, "@");
        XSANY.any_i32 = 130;
        cv = newXSproto_portable("Sort::Key::rsort", XS_Sort__Key__sort, file, "@");
        XSANY.any_i32 = 128;
        cv = newXSproto_portable("Sort::Key::rusort", XS_Sort__Key__sort, file, "@");
        XSANY.any_i32 = 132;
        cv = newXSproto_portable("Sort::Key::usort", XS_Sort__Key__sort, file, "@");
        XSANY.any_i32 = 4;
        cv = newXSproto_portable("Sort::Key::_sort_inplace", XS_Sort__Key__sort_inplace, file, "\\@");
        XSANY.any_i32 = 0;
        cv = newXSproto_portable("Sort::Key::isort_inplace", XS_Sort__Key__sort_inplace, file, "\\@");
        XSANY.any_i32 = 3;
        cv = newXSproto_portable("Sort::Key::lsort_inplace", XS_Sort__Key__sort_inplace, file, "\\@");
        XSANY.any_i32 = 1;
        cv = newXSproto_portable("Sort::Key::nsort_inplace", XS_Sort__Key__sort_inplace, file, "\\@");
        XSANY.any_i32 = 2;
        cv = newXSproto_portable("Sort::Key::risort_inplace", XS_Sort__Key__sort_inplace, file, "\\@");
        XSANY.any_i32 = 131;
        cv = newXSproto_portable("Sort::Key::rlsort_inplace", XS_Sort__Key__sort_inplace, file, "\\@");
        XSANY.any_i32 = 129;
        cv = newXSproto_portable("Sort::Key::rnsort_inplace", XS_Sort__Key__sort_inplace, file, "\\@");
        XSANY.any_i32 = 130;
        cv = newXSproto_portable("Sort::Key::rsort_inplace", XS_Sort__Key__sort_inplace, file, "\\@");
        XSANY.any_i32 = 128;
        cv = newXSproto_portable("Sort::Key::rusort_inplace", XS_Sort__Key__sort_inplace, file, "\\@");
        XSANY.any_i32 = 132;
        cv = newXSproto_portable("Sort::Key::usort_inplace", XS_Sort__Key__sort_inplace, file, "\\@");
        XSANY.any_i32 = 4;
        newXS_deffile("Sort::Key::_multikeysorter", XS_Sort__Key__multikeysorter);
        newXS_deffile("Sort::Key::_multikeysorter_inplace", XS_Sort__Key__multikeysorter_inplace);
    }
#if PERL_VERSION_LE(5, 21, 5)
#  if PERL_VERSION_GE(5, 9, 0)
    if (PL_unitcheckav)
        call_list(PL_scopestack_ix, PL_unitcheckav);
#  endif
    XSRETURN_YES;
#else
    Perl_xs_boot_epilog(aTHX_ ax);
#endif
}

#ifdef __cplusplus
}
#endif
