#include <stdio.h>
#define FS 25 //sampling frequency
#define BUFFER_SIZE (FS* 2)
int min( int x, int y) {
if ( x< y) {
return x;
}
else { return y; }
}
//maxim_find_peaks( an_ir_valley_locs, &n_npks, an_x = avg.ir, BUFFER_SIZE, n_th1, 4, 15 );//peak_height, peak_distance, max_num_peaks
void printArr( int * arr, int count) {
int i = 0 ;
while ( i < count) {
i++;
}
}
void maxim_peaks_above_min_height( int * pn_locs, int * n_npks, int * pn_x, int n_size, int n_min_height )
/**
* \brief Find peaks above n_min_height
* \par Details
* Find all peaks above MIN_HEIGHT
*
* \retval None
*/
{
int i = 1 , n_width;
* n_npks = 0 ;
while ( i < n_size- 1 ) {
if ( pn_x[ i] > n_min_height && pn_x[ i] > pn_x[ i- 1 ] ) { // find left edge of potential peaks
n_width = 1 ;
while ( i+ n_width < n_size && pn_x[ i] == pn_x[ i+ n_width] ) // find flat peaks
n_width++;
if ( pn_x[ i] > pn_x[ i+ n_width] && ( * n_npks) < 15 ) { // find right edge of peaks
pn_locs[ ( * n_npks) ++ ] = i;
// for flat peaks, peak location is left edge
i += n_width+ 1 ;
}
else
i += n_width;
}
else
i++;
}
}
void maxim_sort_ascend( int * pn_x, int n_size)
/**
* \brief Sort array
* \par Details
* Sort array in ascending order (insertion sort algorithm)
*
* \retval None
*/
{
int i, j, n_temp;
for ( i = 1 ; i < n_size; i++ ) {
n_temp = pn_x[ i] ;
for ( j = i; j > 0 && n_temp < pn_x[ j- 1 ] ; j-- )
pn_x[ j] = pn_x[ j- 1 ] ;
pn_x[ j] = n_temp;
}
}
void maxim_sort_indices_descend( int * pn_x, int * pn_indx, int n_size)
/**
* \brief Sort indices
* \par Details
* Sort indices according to descending order (insertion sort algorithm)
*
* \retval None
*/
{
int i, j, n_temp;
for ( i = 1 ; i < n_size; i++ ) {
n_temp = pn_indx[ i] ;
for ( j = i; j > 0 && pn_x[ n_temp] > pn_x[ pn_indx[ j- 1 ] ] ; j-- )
pn_indx[ j] = pn_indx[ j- 1 ] ;
pn_indx[ j] = n_temp;
}
}
void maxim_remove_close_peaks( int * pn_locs, int * pn_npks, int * pn_x, int n_min_distance)
/**
* \brief Remove peaks
* \par Details
* Remove peaks separated by less than MIN_DISTANCE
*
* \retval None
*/
{
//maxim_remove_close_peaks( pn_locs = loclist, n_npks = count, pn_x = ir, n_min_distance = dis );
int i, j, n_old_npks, n_dist;
/* Order peaks from large to small */
//maxim_remove_close_peaks( pn_locs = list, pn_npks = count, pn_x = ir, n_min_distance = min );
maxim_sort_indices_descend( pn_x, pn_locs, * pn_npks ) ;
printArr( pn_locs, * pn_npks) ;
for ( i = - 1 ; i < * pn_npks; i++ ) {
n_old_npks = * pn_npks;
* pn_npks = i+ 1 ;
for ( j = i+ 1 ; j < n_old_npks; j++ ) {
n_dist = pn_locs[ j] - ( i == - 1 ? - 1 : pn_locs[ i] ) ; // lag-zero peak of autocorr is at index -1
if ( n_dist > n_min_distance || n_dist < - n_min_distance )
pn_locs[ ( * pn_npks) ++ ] = pn_locs[ j] ;
}
}
printArr( pn_locs, * pn_npks) ;
// Resort indices into ascending order
maxim_sort_ascend( pn_locs, * pn_npks ) ;
printArr( pn_locs, * pn_npks) ;
}
void maxim_find_peaks( int * pn_locs, int * n_npks, int * pn_x, int n_size, int n_min_height, int n_min_distance, int n_max_num )
/**
* \brief Find peaks
* \par Details
* Find at most MAX_NUM peaks above MIN_HEIGHT separated by at least MIN_DISTANCE
*
* \retval None
*/
{
maxim_peaks_above_min_height( pn_locs, n_npks, pn_x, n_size, n_min_height ) ;
printArr( pn_locs, * n_npks) ;
maxim_remove_close_peaks( pn_locs, n_npks, pn_x, n_min_distance ) ;
printArr( pn_locs, * n_npks) ;
* n_npks = min( * n_npks, n_max_num ) ;
}
void maxim_heart_rate_and_oxygen_saturation( unsigned int * pun_ir_buffer, int n_ir_buffer_length,
int * pn_heart_rate, int * pch_hr_valid, int unsigned * an_x)
{
unsigned int un_ir_mean, un_only_once ;
int k, n_i_ratio_count;
int i, s, m, n_exact_ir_valley_locs_count, n_middle_idx;
int n_th1, n_npks, n_c_min;
int an_ir_valley_locs[ 15 ] ;
int n_peak_interval_sum;
int n_y_ac, n_x_ac;
int n_spo2_calc;
int n_y_dc_max, n_x_dc_max;
int n_y_dc_max_idx, n_x_dc_max_idx;
int an_ratio[ 5 ] , n_ratio_average;
int n_nume, n_denom ;
// calculates DC mean and subtract DC from ir
un_ir_mean = 0 ;
for ( k= 0 ; k< n_ir_buffer_length ; k++ ) un_ir_mean += pun_ir_buffer[ k] ;
un_ir_mean = un_ir_mean/ n_ir_buffer_length ;
printf ( "mean is %d\n " , un_ir_mean
) ;
// remove DC and invert signal so that we can use peak detector as valley detector
for ( k= 0 ; k< n_ir_buffer_length ; k++ )
an_x[ k] = - 1 * ( pun_ir_buffer[ k] - un_ir_mean) ;
printf ( "ir array, dc removed " ) ; printArr( an_x, 50 ) ;
// 4 pt Moving Average
for ( k= 0 ; k< BUFFER_SIZE- 4 ; k++ ) {
an_x[ k] = ( an_x[ k] + an_x[ k+ 1 ] + an_x[ k+ 2 ] + an_x[ k+ 3 ] ) / ( int ) 4 ;
}
// calculate threshold
n_th1= 0 ;
for ( k= 0 ; k< BUFFER_SIZE ; k++ ) {
n_th1 += an_x[ k] ;
}
n_th1= n_th1/ ( BUFFER_SIZE) ;
if ( n_th1< 30 ) n_th1= 30 ; // min allowed
if ( n_th1> 60 ) n_th1= 60 ; // max allowed
for ( k= 0 ; k< 15 ; k++ ) an_ir_valley_locs[ k] = 0 ;
// since we flipped signal, we use peak detector as valley detector
printf ( "Array for averaged ir is " ) ; printArr( an_x, 50 ) ;
maxim_find_peaks( an_ir_valley_locs, & n_npks, an_x, BUFFER_SIZE, n_th1, 4 , 15 ) ; //peak_height, peak_distance, max_num_peaks
printArr( an_ir_valley_locs, n_npks) ;
n_peak_interval_sum = 0 ;
if ( n_npks>= 2 ) {
for ( k= 1 ; k< n_npks; k++ ) n_peak_interval_sum += ( an_ir_valley_locs[ k] - an_ir_valley_locs[ k - 1 ] ) ;
n_peak_interval_sum = n_peak_interval_sum/ ( n_npks- 1 ) ;
* pn_heart_rate = ( int ) ( ( FS* 60 ) / n_peak_interval_sum ) ;
* pch_hr_valid = 1 ;
}
else {
* pn_heart_rate = - 999 ; // unable to calculate because # of peaks are too small
* pch_hr_valid = 0 ;
}
}
int main( void ) {
// your code goes here
int ir[ 50 ] = { 6232 , 13423 , 319 , 8754 , 3660 , 15297 , 321 , 1028 , 2250 , 2828 , 7738 , 5072 , 2127 , 7393 , 76 , 12470 , 5088 , 902 , 16257 , 15355 , 6248 , 9790 , 5723 , 7578 , 8159 , 7071 , 4029 , 5814 , 7005 , 10472 , 11111 , 6675 , 9635 , 15670 , 643 , 4874 , 4433 , 9862 , 15868 , 10150 , 12961 , 15544 , 961 , 1341 , 15611 , 6682 , 1158 , 2765 , 9992 , 9486 } ;
int count = 0 ;
unsigned int list[ 15 ] ;
int hr = 0 ;
int valid = 0 ;
int irA[ 50 ] ;
maxim_heart_rate_and_oxygen_saturation( ir, 50 ,& hr, & valid, irA) ;
/*
maxim_peaks_above_min_height(list, &count, ir, 50, 10000 );
printArr(list, count);
maxim_remove_close_peaks(list, &count, ir, 4);
*/
printf ( "Hr valid is %d\n " , valid
) ; return 0 ;
}
I2luY2x1ZGUgPHN0ZGlvLmg+CgojZGVmaW5lIEZTIDI1ICAgIC8vc2FtcGxpbmcgZnJlcXVlbmN5CiNkZWZpbmUgQlVGRkVSX1NJWkUgIChGUyogMikgCgoKaW50IG1pbihpbnQgeCwgaW50IHkpewoJaWYgKHg8eSl7CgkJcmV0dXJuIHg7Cgl9CgllbHNle3JldHVybiB5O30KfQovL21heGltX2ZpbmRfcGVha3MoICBhbl9pcl92YWxsZXlfbG9jcywgJm5fbnBrcywgICAgICAgICBhbl94ID0gYXZnLmlyLCAgQlVGRkVSX1NJWkUsICAgIG5fdGgxLCAgICAgICAgICAgICAgIDQsICAgICAgICAgICAgICAgICAgICAgICAgIDE1ICk7Ly9wZWFrX2hlaWdodCwgcGVha19kaXN0YW5jZSwgbWF4X251bV9wZWFrcyAKdm9pZCBwcmludEFycihpbnQgKmFyciwgaW50IGNvdW50KXsKCWludCBpID0gMDsKCXByaW50ZigiWyIpOwoJd2hpbGUoaSA8IGNvdW50KXsKCQlwcmludGYoIiAlZCIsIGFycltpXSk7CgkJaSsrOwoJfQoJcHJpbnRmKCIgXVxuIik7Cn0KCgp2b2lkIG1heGltX3BlYWtzX2Fib3ZlX21pbl9oZWlnaHQoIGludCAqcG5fbG9jcywgaW50ICpuX25wa3MsICBpbnQgICpwbl94LCBpbnQgbl9zaXplLCBpbnQgbl9taW5faGVpZ2h0ICkKLyoqCiogXGJyaWVmICAgICAgICBGaW5kIHBlYWtzIGFib3ZlIG5fbWluX2hlaWdodAoqIFxwYXIgICAgICAgICAgRGV0YWlscwoqICAgICAgICAgICAgICAgRmluZCBhbGwgcGVha3MgYWJvdmUgTUlOX0hFSUdIVAoqCiogXHJldHZhbCAgICAgICBOb25lCiovCnsKICBpbnQgaSA9IDEsIG5fd2lkdGg7CiAgKm5fbnBrcyA9IDA7CiAgCiAgd2hpbGUgKGkgPCBuX3NpemUtMSl7CiAgICBpZiAocG5feFtpXSA+IG5fbWluX2hlaWdodCAmJiBwbl94W2ldID4gcG5feFtpLTFdKXsgICAgICAvLyBmaW5kIGxlZnQgZWRnZSBvZiBwb3RlbnRpYWwgcGVha3MKICAgICAgbl93aWR0aCA9IDE7CiAgICAgIHdoaWxlIChpK25fd2lkdGggPCBuX3NpemUgJiYgcG5feFtpXSA9PSBwbl94W2krbl93aWR0aF0pICAvLyBmaW5kIGZsYXQgcGVha3MKICAgICAgICBuX3dpZHRoKys7CiAgICAgIGlmIChwbl94W2ldID4gcG5feFtpK25fd2lkdGhdICYmICgqbl9ucGtzKSA8IDE1ICl7ICAgICAgLy8gZmluZCByaWdodCBlZGdlIG9mIHBlYWtzCiAgICAgICAgcG5fbG9jc1soKm5fbnBrcykrK10gPSBpOyAgICAKICAgICAgICAvLyBmb3IgZmxhdCBwZWFrcywgcGVhayBsb2NhdGlvbiBpcyBsZWZ0IGVkZ2UKICAgICAgICBpICs9IG5fd2lkdGgrMTsKICAgICAgfQogICAgICBlbHNlCiAgICAgICAgaSArPSBuX3dpZHRoOwogICAgfQogICAgZWxzZQogICAgICBpKys7CiAgfQp9CgoKdm9pZCBtYXhpbV9zb3J0X2FzY2VuZChpbnQgICpwbl94LCBpbnQgbl9zaXplKSAKLyoqCiogXGJyaWVmICAgICAgICBTb3J0IGFycmF5CiogXHBhciAgICAgICAgICBEZXRhaWxzCiogICAgICAgICAgICAgICBTb3J0IGFycmF5IGluIGFzY2VuZGluZyBvcmRlciAoaW5zZXJ0aW9uIHNvcnQgYWxnb3JpdGhtKQoqCiogXHJldHZhbCAgICAgICBOb25lCiovCnsKCQogIGludCBpLCBqLCBuX3RlbXA7CiAgZm9yIChpID0gMTsgaSA8IG5fc2l6ZTsgaSsrKSB7CiAgICBuX3RlbXAgPSBwbl94W2ldOwogICAgZm9yIChqID0gaTsgaiA+IDAgJiYgbl90ZW1wIDwgcG5feFtqLTFdOyBqLS0pCiAgICAgICAgcG5feFtqXSA9IHBuX3hbai0xXTsKICAgIHBuX3hbal0gPSBuX3RlbXA7CiAgfQp9Cgp2b2lkIG1heGltX3NvcnRfaW5kaWNlc19kZXNjZW5kKCAgaW50ICAqcG5feCwgaW50ICpwbl9pbmR4LCBpbnQgbl9zaXplKQovKioKKiBcYnJpZWYgICAgICAgIFNvcnQgaW5kaWNlcwoqIFxwYXIgICAgICAgICAgRGV0YWlscwoqICAgICAgICAgICAgICAgU29ydCBpbmRpY2VzIGFjY29yZGluZyB0byBkZXNjZW5kaW5nIG9yZGVyIChpbnNlcnRpb24gc29ydCBhbGdvcml0aG0pCioKKiBccmV0dmFsICAgICAgIE5vbmUKKi8gCnsKCQogIGludCBpLCBqLCBuX3RlbXA7CiAgZm9yIChpID0gMTsgaSA8IG5fc2l6ZTsgaSsrKSB7CiAgICBuX3RlbXAgPSBwbl9pbmR4W2ldOwogICAgZm9yIChqID0gaTsgaiA+IDAgJiYgcG5feFtuX3RlbXBdID4gcG5feFtwbl9pbmR4W2otMV1dOyBqLS0pCiAgICAgIHBuX2luZHhbal0gPSBwbl9pbmR4W2otMV07CiAgICBwbl9pbmR4W2pdID0gbl90ZW1wOwogIH0KfQoKCnZvaWQgbWF4aW1fcmVtb3ZlX2Nsb3NlX3BlYWtzKGludCAqcG5fbG9jcywgaW50ICpwbl9ucGtzLCBpbnQgKnBuX3gsIGludCBuX21pbl9kaXN0YW5jZSkKLyoqCiogXGJyaWVmICAgICAgICBSZW1vdmUgcGVha3MKKiBccGFyICAgICAgICAgIERldGFpbHMKKiAgICAgICAgICAgICAgIFJlbW92ZSBwZWFrcyBzZXBhcmF0ZWQgYnkgbGVzcyB0aGFuIE1JTl9ESVNUQU5DRQoqCiogXHJldHZhbCAgICAgICBOb25lCiovCnsKICAvL21heGltX3JlbW92ZV9jbG9zZV9wZWFrcyggcG5fbG9jcyA9IGxvY2xpc3QsIG5fbnBrcyA9IGNvdW50LCBwbl94ID0gaXIsIG5fbWluX2Rpc3RhbmNlID0gZGlzICk7ICAKICBpbnQgaSwgaiwgbl9vbGRfbnBrcywgbl9kaXN0OwogICAgCiAgLyogT3JkZXIgcGVha3MgZnJvbSBsYXJnZSB0byBzbWFsbCAqLwogIAogIAogIC8vbWF4aW1fcmVtb3ZlX2Nsb3NlX3BlYWtzKCBwbl9sb2NzID0gbGlzdCwgcG5fbnBrcyA9IGNvdW50LCBwbl94ID0gaXIsIG5fbWluX2Rpc3RhbmNlID0gbWluICk7CiAgCiAgbWF4aW1fc29ydF9pbmRpY2VzX2Rlc2NlbmQoIHBuX3gsIHBuX2xvY3MsICpwbl9ucGtzICk7CiAgCiAgcHJpbnRBcnIocG5fbG9jcywgKnBuX25wa3MpOwogIGZvciAoIGkgPSAtMTsgaSA8ICpwbl9ucGtzOyBpKysgKXsKICAgIG5fb2xkX25wa3MgPSAqcG5fbnBrczsKICAgICpwbl9ucGtzID0gaSsxOwogICAgZm9yICggaiA9IGkrMTsgaiA8IG5fb2xkX25wa3M7IGorKyApewogICAgICBuX2Rpc3QgPSAgcG5fbG9jc1tqXSAtICggaSA9PSAtMSA/IC0xIDogcG5fbG9jc1tpXSApOyAvLyBsYWctemVybyBwZWFrIG9mIGF1dG9jb3JyIGlzIGF0IGluZGV4IC0xCiAgICAgIGlmICggbl9kaXN0ID4gbl9taW5fZGlzdGFuY2UgfHwgbl9kaXN0IDwgLW5fbWluX2Rpc3RhbmNlICkKICAgICAgICBwbl9sb2NzWygqcG5fbnBrcykrK10gPSBwbl9sb2NzW2pdOwogICAgfQogIH0KICBwcmludEFycihwbl9sb2NzLCAqcG5fbnBrcyk7CiAgLy8gUmVzb3J0IGluZGljZXMgaW50byBhc2NlbmRpbmcgb3JkZXIKICBtYXhpbV9zb3J0X2FzY2VuZCggcG5fbG9jcywgKnBuX25wa3MgKTsKICBwcmludEFycihwbl9sb2NzLCAqcG5fbnBrcyk7Cn0KCnZvaWQgbWF4aW1fZmluZF9wZWFrcyggaW50ICpwbl9sb2NzLCBpbnQgKm5fbnBrcywgIGludCAgKnBuX3gsIGludCBuX3NpemUsIGludCBuX21pbl9oZWlnaHQsIGludCBuX21pbl9kaXN0YW5jZSwgaW50IG5fbWF4X251bSApCi8qKgoqIFxicmllZiAgICAgICAgRmluZCBwZWFrcwoqIFxwYXIgICAgICAgICAgRGV0YWlscwoqICAgICAgICAgICAgICAgRmluZCBhdCBtb3N0IE1BWF9OVU0gcGVha3MgYWJvdmUgTUlOX0hFSUdIVCBzZXBhcmF0ZWQgYnkgYXQgbGVhc3QgTUlOX0RJU1RBTkNFCioKKiBccmV0dmFsICAgICAgIE5vbmUKKi8KewogIG1heGltX3BlYWtzX2Fib3ZlX21pbl9oZWlnaHQoIHBuX2xvY3MsIG5fbnBrcywgcG5feCwgbl9zaXplLCBuX21pbl9oZWlnaHQgKTsKICBwcmludGYoIkFib3ZlIG1pbiBhcnI6ICIpOwogIHByaW50QXJyKHBuX2xvY3MsICpuX25wa3MpOwogIG1heGltX3JlbW92ZV9jbG9zZV9wZWFrcyggcG5fbG9jcywgbl9ucGtzLCBwbl94LCBuX21pbl9kaXN0YW5jZSApOwogIHByaW50ZigicmVtb3ZlIGNsb3NlIDoiKTsKICBwcmludEFycihwbl9sb2NzLCAqbl9ucGtzKTsKICpuX25wa3MgPSBtaW4oICpuX25wa3MsIG5fbWF4X251bSApOwp9Cgp2b2lkIG1heGltX2hlYXJ0X3JhdGVfYW5kX294eWdlbl9zYXR1cmF0aW9uKHVuc2lnbmVkIGludCAqcHVuX2lyX2J1ZmZlciwgaW50IG5faXJfYnVmZmVyX2xlbmd0aCwKICAgICAgICAgICAgICAgIGludCAqcG5faGVhcnRfcmF0ZSwgaW50ICpwY2hfaHJfdmFsaWQsIGludCB1bnNpZ25lZCAqYW5feCkKewogIHVuc2lnbmVkIGludCB1bl9pcl9tZWFuLHVuX29ubHlfb25jZSA7CiAgaW50IGssIG5faV9yYXRpb19jb3VudDsKICBpbnQgaSwgcywgbSwgbl9leGFjdF9pcl92YWxsZXlfbG9jc19jb3VudCwgbl9taWRkbGVfaWR4OwogIGludCBuX3RoMSwgbl9ucGtzLCBuX2NfbWluOyAgIAogIGludCBhbl9pcl92YWxsZXlfbG9jc1sxNV0gOwogIGludCBuX3BlYWtfaW50ZXJ2YWxfc3VtOwogIAogIGludCBuX3lfYWMsIG5feF9hYzsKICBpbnQgbl9zcG8yX2NhbGM7IAogIGludCBuX3lfZGNfbWF4LCBuX3hfZGNfbWF4OyAKICBpbnQgbl95X2RjX21heF9pZHgsIG5feF9kY19tYXhfaWR4OyAKICBpbnQgYW5fcmF0aW9bNV0sIG5fcmF0aW9fYXZlcmFnZTsgCiAgaW50IG5fbnVtZSwgbl9kZW5vbSA7CgogIC8vIGNhbGN1bGF0ZXMgREMgbWVhbiBhbmQgc3VidHJhY3QgREMgZnJvbSBpcgogIHVuX2lyX21lYW4gPTA7IAogIGZvciAoaz0wIDsgazxuX2lyX2J1ZmZlcl9sZW5ndGggOyBrKysgKSB1bl9pcl9tZWFuICs9IHB1bl9pcl9idWZmZXJba10gOwogIHVuX2lyX21lYW4gPXVuX2lyX21lYW4vbl9pcl9idWZmZXJfbGVuZ3RoIDsKICBwcmludGYoIm1lYW4gaXMgJWRcbiIsdW5faXJfbWVhbik7CiAgICAKICAvLyByZW1vdmUgREMgYW5kIGludmVydCBzaWduYWwgc28gdGhhdCB3ZSBjYW4gdXNlIHBlYWsgZGV0ZWN0b3IgYXMgdmFsbGV5IGRldGVjdG9yCiAgZm9yIChrPTAgOyBrPG5faXJfYnVmZmVyX2xlbmd0aCA7IGsrKyApICAKICAgIGFuX3hba10gPSAtMSoocHVuX2lyX2J1ZmZlcltrXSAtIHVuX2lyX21lYW4pIDsgCiAgICAKICBwcmludGYoImlyIGFycmF5LCBkYyByZW1vdmVkICIpOwogIHByaW50QXJyKGFuX3gsIDUwKTsKICAvLyA0IHB0IE1vdmluZyBBdmVyYWdlCiAgZm9yKGs9MDsgazwgQlVGRkVSX1NJWkUtNDsgaysrKXsKICAgIGFuX3hba109KCBhbl94W2tdK2FuX3hbaysxXSsgYW5feFtrKzJdKyBhbl94W2srM10pLyhpbnQpNDsgICAgICAgIAogIH0KICAvLyBjYWxjdWxhdGUgdGhyZXNob2xkICAKICBuX3RoMT0wOyAKICBmb3IgKCBrPTAgOyBrPEJVRkZFUl9TSVpFIDtrKyspewogICAgbl90aDEgKz0gIGFuX3hba107CiAgfQogIG5fdGgxPSAgbl90aDEvICggQlVGRkVSX1NJWkUpOwogIHByaW50ZigibWluIGlzICVkXG4iLCBuX3RoMSk7CiAgaWYoIG5fdGgxPDMwKSBuX3RoMT0zMDsgLy8gbWluIGFsbG93ZWQKICBpZiggbl90aDE+NjApIG5fdGgxPTYwOyAvLyBtYXggYWxsb3dlZAoKICBmb3IgKCBrPTAgOyBrPDE1O2srKykgYW5faXJfdmFsbGV5X2xvY3Nba109MDsKICAvLyBzaW5jZSB3ZSBmbGlwcGVkIHNpZ25hbCwgd2UgdXNlIHBlYWsgZGV0ZWN0b3IgYXMgdmFsbGV5IGRldGVjdG9yCiAKIAogIHByaW50ZigiQXJyYXkgZm9yIGF2ZXJhZ2VkIGlyIGlzICIpOwogIHByaW50QXJyKGFuX3gsIDUwKTsKIAogCiAgbWF4aW1fZmluZF9wZWFrcyggYW5faXJfdmFsbGV5X2xvY3MsICZuX25wa3MsIGFuX3gsIEJVRkZFUl9TSVpFLCBuX3RoMSwgNCwgMTUgKTsvL3BlYWtfaGVpZ2h0LCBwZWFrX2Rpc3RhbmNlLCBtYXhfbnVtX3BlYWtzIAogIAogIHByaW50ZigiQXJyYXkgZm9yIGxpc3QgaXMiKTsKICBwcmludEFycihhbl9pcl92YWxsZXlfbG9jcywgbl9ucGtzKTsKICAKICBuX3BlYWtfaW50ZXJ2YWxfc3VtID0wOwogIGlmIChuX25wa3M+PTIpewogICAgZm9yIChrPTE7IGs8bl9ucGtzOyBrKyspIG5fcGVha19pbnRlcnZhbF9zdW0gKz0gKGFuX2lyX3ZhbGxleV9sb2NzW2tdIC1hbl9pcl92YWxsZXlfbG9jc1trIC0xXSApIDsKICAgIG5fcGVha19pbnRlcnZhbF9zdW0gPW5fcGVha19pbnRlcnZhbF9zdW0vKG5fbnBrcy0xKTsKICAgICpwbl9oZWFydF9yYXRlID0oaW50KSggKEZTKjYwKS8gbl9wZWFrX2ludGVydmFsX3N1bSApOwogICAgKnBjaF9ocl92YWxpZCAgPSAxOwogIH0KICBlbHNlICB7IAogICAgKnBuX2hlYXJ0X3JhdGUgPSAtOTk5OyAvLyB1bmFibGUgdG8gY2FsY3VsYXRlIGJlY2F1c2UgIyBvZiBwZWFrcyBhcmUgdG9vIHNtYWxsCiAgICAqcGNoX2hyX3ZhbGlkICA9IDA7CiAgfQp9CgoKaW50IG1haW4odm9pZCkgewoJLy8geW91ciBjb2RlIGdvZXMgaGVyZQoJCglpbnQgaXJbNTBdID0gezYyMzIsIDEzNDIzLCAzMTksIDg3NTQsIDM2NjAsIDE1Mjk3LCAzMjEsIDEwMjgsIDIyNTAsIDI4MjgsIDc3MzgsIDUwNzIsIDIxMjcsIDczOTMsIDc2LCAxMjQ3MCwgNTA4OCwgOTAyLCAxNjI1NywgMTUzNTUsIDYyNDgsIDk3OTAsIDU3MjMsIDc1NzgsIDgxNTksIDcwNzEsIDQwMjksIDU4MTQsIDcwMDUsIDEwNDcyLCAxMTExMSwgNjY3NSwgOTYzNSwgMTU2NzAsIDY0MywgNDg3NCwgNDQzMywgOTg2MiwgMTU4NjgsIDEwMTUwLCAxMjk2MSwgMTU1NDQsIDk2MSwgMTM0MSwgMTU2MTEsIDY2ODIsIDExNTgsIDI3NjUsIDk5OTIsIDk0ODZ9OwoJaW50IGNvdW50ID0gMDsKCXVuc2lnbmVkIGludCBsaXN0WzE1XTsKCWludCBociA9IDA7CglpbnQgdmFsaWQgPSAwOwoJaW50IGlyQVs1MF07CQoJbWF4aW1faGVhcnRfcmF0ZV9hbmRfb3h5Z2VuX3NhdHVyYXRpb24oaXIsNTAsJmhyLCAmdmFsaWQsIGlyQSk7CiAgICAvKgoJbWF4aW1fcGVha3NfYWJvdmVfbWluX2hlaWdodChsaXN0LCAmY291bnQsIGlyLCA1MCwgMTAwMDAgKTsKCXByaW50QXJyKGxpc3QsIGNvdW50KTsKCW1heGltX3JlbW92ZV9jbG9zZV9wZWFrcyhsaXN0LCAmY291bnQsIGlyLCA0KTsgCgkqLwoJcHJpbnRmKCJIciBpcyAlZFxuIiwgaHIpOwoJcHJpbnRmKCJIciB2YWxpZCBpcyAlZFxuIiwgdmFsaWQpOwoJcmV0dXJuIDA7Cn0KCg==