#! perl -slw use strict; use Inline C => <<'END_OF_PERCENT2_C_CODE'; #define SvSIOK(sv) ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV)) == SVf_IOK) #define SvNSIV(sv) (SvNOK(sv) ? SvNVX(sv) : (SvSIOK(sv) ? SvIVX(sv) : sv_2nv(sv))) static I32 S_sv_ncmp(pTHX_ SV *a, SV *b) { const NV nv1 = SvNSIV(a); const NV nv2 = SvNSIV(b); return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0; } void percent2(SV* sv, ...) { Inline_Stack_Vars; I32 i; I32 arrayLen; I32 remove_count; AV* data; AV* data_tmp; double value; int index; SV** pvalue; double retval; AV* ret; ret = newAV(); data_tmp = newAV(); data = (AV*)SvRV(Inline_Stack_Item(0)); if( SvTYPE(data) != SVt_PVAV ) { return; } /* determine the length of the array */ arrayLen = av_len(data) + 1; if (arrayLen > 0) { /* use new tmp array to prevent reordering caller array*/ for (i = 0; i < arrayLen; i++) { pvalue = av_fetch(data,i,0); av_push( data_tmp, newSVsv( *pvalue ) ); // Copy the SVs so that you don't destroy the original array when freeing the temp } /* sort array in ascending order */ sortsv(AvARRAY(data_tmp),arrayLen, S_sv_ncmp); /* loop through data array and delete undef entries */ remove_count = 0; for (i = 0; i < arrayLen; i++) { /* Fetch the scalar located at i from the array.*/ pvalue = av_fetch(data_tmp,i,0); if (!SvOK(*pvalue)) { remove_count++; } else { break; } } for (i = 0; i < remove_count; i++) { av_shift(data_tmp); } arrayLen -= remove_count; } if (arrayLen > 0) { /* loop through percent args and find given value in array */ for (i = 1; i < Inline_Stack_Items; i++) { value = SvNV(Inline_Stack_Item(i)); if (value <= 100 && value >= 0){ if (value == 100){ value -= 1e-13; } index = (int)((arrayLen-1) * value/100); /* fetch scalar located at calculated index*/ pvalue = av_fetch(data_tmp,index,0); av_push( ret, newSVsv( *pvalue ) ); /// ditto } } } /* push into return stack */ Inline_Stack_Reset; arrayLen = av_len(ret) + 1; if (arrayLen > 0) { for (i = 0; i < arrayLen; i++) { /* fetch the scalar located at i from the array.*/ pvalue = av_fetch(ret,i,0); /* dereference the scalar into a numeric value. */ retval = SvNV(*pvalue); Inline_Stack_Push( sv_2mortal( newSVnv( retval ) ) ); // Mortalise the return values } } else { Inline_Stack_Push( sv_2mortal( newSVnv(0) ) ); // Mortalise the return values } Inline_Stack_Done; av_undef( ret ); // free array memory SvREFCNT_dec( ret ); // free the AV itself av_undef( data_tmp ); // ditto SvREFCNT_dec( data_tmp ); } END_OF_PERCENT2_C_CODE my @data = map int( rand 100 ), 1 .. 100;; print join '|', percent2( \@data, 1, 50 ) for 1 .. 1e9;;