#include <cmath>
#include <vector>
#include <limits>
#include <cstdint>
#include <fstream>
#include <random>
#include <string>
#include <algorithm>
//FMで自分が何をコントロールしているのかさっぱりわからん。。。Orz
//Becarefull wait a play generate file.
//it's easy crash the your ear.
static const int Sample_ = 44100 ;
template < class Float>
std:: vector < Float> MakeBuffer( std:: size_t SampleRate, std:: size_t MilliSec) {
std:: vector < Float> v( static_cast < std:: size_t > ( SampleRate* ( MilliSec / ( static_cast < Float> ( 1000.0 ) ) ) ) ) ;
return v;
}
template < class Float>
bool MakeSinWave( std:: vector < Float> & D,std:: size_t SampleRate, std:: size_t Hz ) {
Float Hz_ = Hz;
Float SampleRate_ = SampleRate;
Float One = SampleRate_ / Hz_;
Float PI = 3.14159265359 ; //from google.
Float Rad = ( ( 2 * PI) / One) ;
for ( std:: size_t i = 0 ; i < D.size ( ) ; i++ ) {
D[ i] = std:: sin ( Rad* std:: fmod ( static_cast < Float> ( i) , One) ) ;
}
return true ;
}
template < class Float>
bool MakeSawWave( std:: vector < Float> & D,std:: size_t SampleRate, std:: size_t Hz ) {
Float Hz_ = Hz;
Float SampleRate_ = SampleRate;
Float One = SampleRate_ / Hz_;
Float Value = 2.0 / One;
for ( std:: size_t i = 0 ; i < D.size ( ) ; i++ ) {
D[ i] = std:: fmod ( Value* i,2 ) - 1 ;
}
return true ;
}
template < class Float>
bool MakeTriangleWave( std:: vector < Float> & D,std:: size_t SampleRate, std:: size_t Hz ) {
Float Hz_ = Hz;
Float SampleRate_ = SampleRate;
Float One = SampleRate_ / Hz_;
Float Value = 1.0 / ( One/ 4 ) ;
Float B = - 1 ;
Float Sign = 1 ;
bool F = false ;
for ( std:: size_t i = 0 ; i < D.size ( ) ; i++ ) {
if ( std:: abs ( B + ( Value* Sign) ) > 1 ) {
B - = ( std:: fmod ( std:: abs ( B + ( Value* Sign) ) , 1 ) * Sign) ;
Sign * = - 1 ;
}
else {
B + = Value* Sign;
}
D[ i] = B;
}
return true ;
}
template < class Float>
bool MakeSquareWave( std:: vector < Float> & D,std:: size_t SampleRate, std:: size_t Hz ) {
Float Hz_ = Hz;
Float SampleRate_ = SampleRate;
Float One = SampleRate_ / Hz_;
Float Value = 1 ;
Float Sign = 1 ;
Float A = 0 ;
Float B = 0 ;
for ( std:: size_t i = 0 ; i < D.size ( ) ; i++ ) {
if ( static_cast < int > ( A) > static_cast < int > ( B) ) {
B = A;
Sign * = - 1 ;
}
A = i / ( One/ 2 ) ;
D[ i] = Value* Sign;
}
return true ;
}
template < class Float>
bool MakeHzNoise( std:: vector < Float> & D,std:: size_t SampleRate, std:: size_t Hz ) {
Float Hz_ = Hz;
Float SampleRate_ = SampleRate;
Float One = SampleRate_ / Hz_;
Float Value = 1 ;
Float A = 0 ;
Float B = 0 ;
std:: random_device rd;
std:: mt19937 mt( rd( ) ) ;
std:: uniform_real_distribution < Float> URD( - 1 , 1 ) ;
for ( std:: size_t i = 0 ; i < D.size ( ) ; i++ ) {
if ( static_cast < int > ( A) > static_cast < int > ( B) ) {
B = A;
Value = URD( mt) ;
}
A = i / ( One/ 2 ) ;
D[ i] = Value;
}
return true ;
}
template < class Float>
bool MakeParfectNoise( std:: vector < Float> & D,std:: size_t SampleRate, std:: size_t Hz ) {
Float Hz_ = Hz;
Float SampleRate_ = SampleRate;
Float One = SampleRate_ / Hz_;
std:: random_device rd;
std:: mt19937 mt( rd( ) ) ;
std:: uniform_real_distribution < Float> URD( - 1 , 1 ) ;
for ( std:: size_t i = 0 ; i < D.size ( ) ; i++ ) {
D[ i] = URD( mt) ;
}
return true ;
}
template < class Float>
Float Sin( const Float& D) { //[0,1]
Float PI = 3.14159265359 ; //from google.
Float Delta = std:: fmod ( std:: abs ( D) , 1 ) ;
return std:: sin ( 2 * PI* Delta) ;
}
template < class Float>
Float Saw( const Float& D) { //[0,1]
Float Delta = std:: fmod ( std:: abs ( D) + 0.5 , 1 ) ;
return - 1 + ( Delta * 2 ) ;
}
template < class Float>
Float Square( const Float& D) { //[0,1]
Float Delta = std:: fmod ( std:: abs ( D) , 1 ) ;
return Delta > 0.5 ? 1 : - 1 ;
}
template < class Float>
Float Triangle( const Float& D) { //[0,1]
Float Delta = std:: fmod ( std:: abs ( D) , 1 ) ;
std:: uint64_t F = static_cast < std:: uint64_t > ( Delta / 0.25 ) ;
Float V = std:: fmod ( Delta, 0.25 ) * 4 ;
switch ( F)
{
case 0 :
return V;
case 1 :
return 1 - V;
case 2 :
return - V;
case 3 :
return - 1 + V;
default :
break ;
}
return 0 ;
}
template < class Float>
Float Trapezoid( const Float& D) { //[0,1]
Float Delta = std:: fmod ( std:: abs ( D) , 1 ) ;
std:: uint64_t F = static_cast < std:: uint64_t > ( Delta / ( 1 / 8.0 ) ) ;
Float V = std:: fmod ( Delta, ( 1 / 8.0 ) ) * 8 ;
switch ( F)
{
case 0 :
return V;
case 1 :
case 2 :
return 1 ;
case 3 :
return 1 - V;
case 4 :
return - V;
case 5 :
case 6 :
return - 1 ;
case 7 :
return - 1 + V;
default :
break ;
}
return 0 ;
}
template < class Float>
Float Noize( const Float& D) { //[0,1]
Float Delta = std:: fmod ( std:: abs ( D) , 1 ) ;
std:: mt19937 mt( static_cast < unsigned int > ( std:: numeric_limits < unsigned int > :: max ( ) * Delta) ) ;
std:: uniform_real_distribution < Float> uid( - 1 , 1 ) ;
return uid( mt) ;
}
template < class Float>
Float PerfectNoize( const Float& D) { //[0,1]
std:: random_device rd;
std:: uniform_real_distribution < Float> uid( - 1 , 1 ) ;
return uid( rd) ;
}
template < class Float , class F>
bool GeneratorTmplate( std:: vector < Float> & D, std:: size_t SampleRate, std:: size_t Hz,F Fun) {
Float Hz_ = Hz;
Float SampleRate_ = SampleRate;
Float One = 1 / ( SampleRate_ / Hz_) ;
for ( std:: size_t i = 0 ; i < D.size ( ) ; i++ ) {
D[ i] = Fun( One* i) ;
}
return true ;
}
//https://e...content-available-to-author-only...a.org/wiki/Frequency_modulation_synthesis#Spectral_analysis
template < class Float>
std:: vector < Float> FM_( const std:: vector < Float> & D,const std:: vector < Float> & M, double FirstTheta, double SecandTheta ) { //, double Time) {
std:: vector < Float> R;
double PI = 3.14159265358979323846 ;
double Rad = 180.0 / PI;
for ( std:: size_t i = 1 ; i < D.size ( ) ; i++ ) {
//double T = (M[i % M.size()] * std::sin((FirstTheta*Rad) + (SecandTheta*Rad)*i ))*(static_cast<double>(D.size()) / static_cast<double>(i));
double T = ( static_cast < double > ( D.size ( ) ) / static_cast < double > ( i) ) ;
double Mul = std:: sin ( ( FirstTheta* Rad) * T + M[ i % M.size ( ) ] * std:: sin ( ( SecandTheta* Rad) ) * T) ;
R.push_back ( D[ i] * Mul) ;
}
return R;
}
template < class Float,class F1,class F2>
Float FMOne( const Float& A, const Float& B,const Float& InitA, const Float& InitB, const Float& Time, F1 Fun1, F2 Fun2) {
return A* Fun1( InitA* Time + B* Fun2( InitB* Time) ) ;
}
template < class Float,class F1,class F2>
std:: vector < Float> FM( const std:: vector < Float> & D, const std:: vector < Float> & M, const Float& InitA, const Float& InitB, F1 Fun1, F2 Fun2) {
std:: vector < Float> R;
for ( std:: size_t i = 1 ; i < D.size ( ) ; i++ ) {
Float V = FMOne( D[ i] , M[ i% M.size ( ) ] , InitA, InitB, i / static_cast < Float> ( D.size ( ) ) , Fun1, Fun2) ;
R.push_back ( V) ;
}
return R;
}
template < class Float,class F1,class F2,class F3>
Float FMOne2( const Float& A, F3 B,const Float& InitB, F1 Fun1,const Float& Init1, F2 Fun2,const Float& Init2 ,const Float& Time) {
return A* Fun1( Init1* Time + B( Time* InitB) * Fun2( Init2* Time) ) ;
}
template < class Float,class F1,class F2,class F3>
std:: vector < Float> FM2( const std:: vector < Float> & D, F3 B,const Float& InitB, F1 Fun1,const Float& Init1, F2 Fun2,const Float& Init2 ) {
std:: vector < Float> R;
for ( std:: size_t i = 1 ; i < D.size ( ) ; i++ ) {
Float V = FMOne2( D[ i] ,B,InitB,Fun1,Init1,Fun2,Init2, i / static_cast < Float> ( D.size ( ) ) ) ;
R.push_back ( V) ;
}
return R;
}
template < class Int,class Float>
std:: vector < Int> MakeWaveInstance( std:: vector < Float> & F,double Volume) {
std:: vector < Int> R;
for ( auto o : F) {
R.push_back ( static_cast < Int> ( o* ( std:: numeric_limits < Int> :: max ( ) ) * Volume) ) ;
}
return R;
}
template < class Int>
bool WriteWaveFile( std:: vector < Int> & D, std:: uint64_t Hz, std:: uint16_t Channel, std:: string Name = "out.wav" ) {
std:: ofstream ofs( Name, std:: ios :: binary ) ;
char RIFF[ ] = "RIFF" ;
char WAVE[ ] = "WAVE" ;
char fmt[ ] = "fmt " ;
char data[ ] = "data" ;
std:: uint32_t FSize = ( 44 - 8 ) + ( D.size ( ) * sizeof ( std:: vector < Int> :: value_type ) ) ;
std:: uint32_t ui32 = 0 ;
std:: uint16_t ui16 = 0 ;
ofs.write ( RIFF, 4 ) ;
ofs.write ( reinterpret_cast < const char * > ( & FSize) , 4 ) ; //fsize-8.
ofs.write ( WAVE, 4 ) ;
ofs.write ( fmt, 4 ) ;
ui32 = 16 ;
ofs.write ( reinterpret_cast < const char * > ( & ui32) , 4 ) ; //fmt chunk size
ui16 = 1 ;
ofs.write ( reinterpret_cast < const char * > ( & ui16) , 2 ) ; //sound type. PCM = 1.
ui16 = Channel;
ofs.write ( reinterpret_cast < const char * > ( & ui16) , 2 ) ; //Channnel count.
ui32 = static_cast < std:: uint32_t > ( Hz) ;
ofs.write ( reinterpret_cast < const char * > ( & ui32) , 4 ) ; //Sampling Hz.
ui32 = static_cast < std:: uint32_t > ( Hz* Channel* ( sizeof ( std:: vector < Int> :: value_type ) ) ) ;
ofs.write ( reinterpret_cast < const char * > ( & ui32) , 4 ) ; //data speed??
ui16 = Channel* ( sizeof ( std:: vector < Int> :: value_type ) ) ;
ofs.write ( reinterpret_cast < const char * > ( & ui16) , 2 ) ; //BlockSize??
ui16 = ( sizeof ( std:: vector < Int> :: value_type ) * 8 ) ;
ofs.write ( reinterpret_cast < const char * > ( & ui16) , 2 ) ; //Sampling Bit.
ofs.write ( data, 4 ) ;
ui32 = static_cast < std:: uint32_t > ( D.size ( ) * sizeof ( std:: vector < Int> :: value_type ) ) ;
ofs.write ( reinterpret_cast < const char * > ( & ui32) , 4 ) ; //Sample's ALL SIZE.max at 32bit.
ofs.write ( reinterpret_cast < const char * > ( & D[ 0 ] ) , D.size ( ) * sizeof ( std:: vector < Int> :: value_type ) ) ; //WriteSamples.
return true ;
}
template < class T>
bool VolumeChenger( std:: vector < T> & D ,double Percentage) { ///1.0 => 100 Percent;
for ( auto o : D) {
o * = Percentage;
}
return true ;
}
template < class Float>
bool VolumeNormalize( std:: vector < Float> & D ) {
Float Max= 0 ;
Float M = 0 ;
for ( std:: size_t i = 0 ; i < D.size ( ) ; i++ ) {
Max = std:: max < Float> ( Max, std:: abs ( D[ i] ) ) ;
}
M = 1 / Max;
for ( auto o : D) {
o * = M;
}
return true ;
}
void Test( std:: size_t SampleRate,std:: size_t MilliSec,double Volume) {
std:: vector < std:: int16_t > D;
std:: vector < double > B;
std:: string Path = "./wav/" ;
B = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeSinWave( B, Sample_, 220 ) ;
//VolumeChenger(B, Volume);
D = MakeWaveInstance< std:: int16_t > ( B, Volume) ;
WriteWaveFile( D, Sample_, 1 , ( Path+ "Sin.wav" ) ) ;
B = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeSawWave( B, Sample_, 220 ) ;
//VolumeChenger(B, Volume);
D = MakeWaveInstance< std:: int16_t > ( B, Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "Saw.wav" ) ;
B = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeTriangleWave( B, Sample_, 220 ) ;
//VolumeChenger(B, Volume);
D = MakeWaveInstance< std:: int16_t > ( B, Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "Triangle.wav" ) ;
B = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeSquareWave( B, Sample_, 220 ) ;
///VolumeChenger(B, Volume);
D = MakeWaveInstance< std:: int16_t > ( B, Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "Square.wav" ) ;
B = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeHzNoise( B, Sample_, 220 ) ;
//VolumeChenger(B, Volume);
D = MakeWaveInstance< std:: int16_t > ( B, Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "HzNoize.wav" ) ;
B = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeParfectNoise( B, Sample_, 220 ) ;
//VolumeChenger(B, Volume);
D = MakeWaveInstance< std:: int16_t > ( B, Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "PNoise.wav" ) ;
}
void GeneratorTest( ) {
std:: vector < std:: int16_t > D;
std:: vector < double > B1;
std:: vector < double > B2;
std:: vector < double > R;
std:: string Path = "./wav/" ;
std:: size_t SampleRate= Sample_;
std:: size_t MilliSec = 2000 ;
double Volume= 1.0 ;
B1 = MakeBuffer< double > ( SampleRate, MilliSec) ;
GeneratorTmplate( B1, Sample_, 220 , Sin< double > ) ;
D = MakeWaveInstance< std:: int16_t > ( B1,Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "SinF.wav" ) ;
B1 = MakeBuffer< double > ( SampleRate, MilliSec) ;
GeneratorTmplate( B1, Sample_, 220 , Saw< double > ) ;
D = MakeWaveInstance< std:: int16_t > ( B1,Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "SawF.wav" ) ;
B1 = MakeBuffer< double > ( SampleRate, MilliSec) ;
GeneratorTmplate( B1, Sample_, 220 , Square< double > ) ;
D = MakeWaveInstance< std:: int16_t > ( B1,Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "SquareF.wav" ) ;
B1 = MakeBuffer< double > ( SampleRate, MilliSec) ;
GeneratorTmplate( B1, Sample_, 220 , Triangle< double > ) ;
D = MakeWaveInstance< std:: int16_t > ( B1,Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "TriangleF.wav" ) ;
B1 = MakeBuffer< double > ( SampleRate, MilliSec) ;
GeneratorTmplate( B1, Sample_, 220 , Trapezoid< double > ) ;
D = MakeWaveInstance< std:: int16_t > ( B1,Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "TrapezoidF.wav" ) ;
B1 = MakeBuffer< double > ( SampleRate, MilliSec) ;
GeneratorTmplate( B1, Sample_, 220 , Noize< double > ) ;
D = MakeWaveInstance< std:: int16_t > ( B1,Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "NoizeF.wav" ) ;
B1 = MakeBuffer< double > ( SampleRate, MilliSec) ;
GeneratorTmplate( B1, Sample_, 220 , PerfectNoize< double > ) ;
D = MakeWaveInstance< std:: int16_t > ( B1,Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "PNoizeF.wav" ) ;
}
void FMTest( ) {
std:: vector < std:: int16_t > D;
std:: vector < double > B1;
std:: vector < double > B2;
std:: vector < double > R;
std:: string Path = "./wav/" ;
std:: size_t SampleRate= Sample_;
std:: size_t MilliSec = 2000 ;
double Volume= 0.8 ;
B1 = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeSawWave( B1, Sample_, 220 ) ;
VolumeChenger( B1, 0.1 ) ;
B2 = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeSinWave( B2, Sample_, 220 ) ;
//MakeHzNoise(B2, Sample_, 220);
//MakeSawWave(B2, Sample_, 220);
VolumeChenger( B2, 0.1 ) ;
R = FM_( B1, B2, 30 , 7 ) ;
//R = FM_(R, B1, 30, 120);
//VolumeNormalize<double>(R);
//VolumeChenger(B2, 1/3.0);
D = MakeWaveInstance< std:: int16_t > ( R,Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "FM.wav" ) ;
}
void FMTest2( ) {
std:: vector < std:: int16_t > D;
std:: vector < double > B1;
std:: vector < double > B2;
std:: vector < double > R;
std:: string Path = "./wav/" ;
std:: size_t SampleRate= Sample_;
std:: size_t MilliSec = 2000 ;
double Volume= 0.8 ;
B1 = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeSinWave( B1, Sample_, 220 ) ;
VolumeChenger( B1, 0.1 ) ;
B2 = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeTriangleWave( B2, Sample_, 220 ) ;
//MakeHzNoise(B2, Sample_, 220);
//MakeSawWave(B2, Sample_, 220);
//VolumeChenger(B2, 0.1);
R = FM< double > ( B1, B2, 30 / 360.0 ,3000 ,Sin< double > ,Sin< double > ) ;
//R = FM(R, B1, 30, 120);
//VolumeNormalize<double>(R);
//VolumeChenger(B2, 1/3.0);
D = MakeWaveInstance< std:: int16_t > ( R,Volume) ;
WriteWaveFile( D, Sample_, 1 , Path+ "FMG.wav" ) ;
}
void FMTest3( ) {
std:: vector < std:: int16_t > D;
std:: vector < double > B1;
std:: vector < double > R;
std:: string Path = "./wav/" ;
std:: size_t SampleRate= Sample_;
std:: size_t MilliSec = 2000 ;
double Volume= 0.8 ;
std:: uint16_t Channel = 1 ;
B1 = MakeBuffer< double > ( SampleRate, MilliSec) ;
MakeSinWave( B1, Sample_, 220 ) ;
R = FM2< double > ( B1,Sin< double > ,1 , Sin< double > ,30 / 360 , Sin< double > ,3000 ) ;
D = MakeWaveInstance< std:: int16_t > ( R,Volume) ;
WriteWaveFile( D, Sample_, Channel, Path+ "FMG2.wav" ) ;
}
int main( ) {
//Test(Sample_,2000,0.8);
//FMTest();
//GeneratorTest();
//FMTest2();
//FMTest3();
return true ;
}
I2luY2x1ZGUgPGNtYXRoPgojaW5jbHVkZSA8dmVjdG9yPgojaW5jbHVkZSA8bGltaXRzPgojaW5jbHVkZSA8Y3N0ZGludD4KI2luY2x1ZGUgPGZzdHJlYW0+CiNpbmNsdWRlIDxyYW5kb20+CiNpbmNsdWRlIDxzdHJpbmc+CiNpbmNsdWRlIDxhbGdvcml0aG0+CgovL0ZN44Gn6Ieq5YiG44GM5L2V44KS44Kz44Oz44OI44Ot44O844Or44GX44Gm44GE44KL44Gu44GL44GV44Gj44Gx44KK44KP44GL44KJ44KT44CC44CC44CCT3J6Ci8vQmVjYXJlZnVsbCB3YWl0IGEgcGxheSBnZW5lcmF0ZSBmaWxlLgovL2l0J3MgZWFzeSBjcmFzaCB0aGUgeW91ciBlYXIuCgpzdGF0aWMgY29uc3QgaW50IFNhbXBsZV8gPSA0NDEwMDsKCnRlbXBsYXRlPGNsYXNzIEZsb2F0PgpzdGQ6OnZlY3RvcjxGbG9hdD4gTWFrZUJ1ZmZlcihzdGQ6OnNpemVfdCBTYW1wbGVSYXRlLCBzdGQ6OnNpemVfdCBNaWxsaVNlYykgewoJc3RkOjp2ZWN0b3I8RmxvYXQ+IHYoc3RhdGljX2Nhc3Q8c3RkOjpzaXplX3Q+KCBTYW1wbGVSYXRlKihNaWxsaVNlYyAvIChzdGF0aWNfY2FzdDxGbG9hdD4oMTAwMC4wKSkpKSk7CglyZXR1cm4gdjsKfQp0ZW1wbGF0ZTxjbGFzcyBGbG9hdD4KYm9vbCBNYWtlU2luV2F2ZShzdGQ6OnZlY3RvcjxGbG9hdD4mIEQsc3RkOjpzaXplX3QgU2FtcGxlUmF0ZSwgc3RkOjpzaXplX3QgSHogKSB7CglGbG9hdCBIel8gPSBIejsKCUZsb2F0IFNhbXBsZVJhdGVfID0gU2FtcGxlUmF0ZTsKCUZsb2F0IE9uZSA9IFNhbXBsZVJhdGVfIC8gSHpfOwoJRmxvYXQgUEkgPSAzLjE0MTU5MjY1MzU5Oy8vZnJvbSBnb29nbGUuCglGbG9hdCBSYWQgPSAoKDIgKiBQSSkgLyBPbmUpOwoJZm9yIChzdGQ6OnNpemVfdCBpID0gMDsgaSA8IEQuc2l6ZSgpOyBpKyspIHsKCQlEW2ldPXN0ZDo6c2luKFJhZCpzdGQ6OmZtb2Qoc3RhdGljX2Nhc3Q8RmxvYXQ+KGkpLCBPbmUpKTsKCX0KCQoJcmV0dXJuIHRydWU7Cn0KdGVtcGxhdGU8Y2xhc3MgRmxvYXQ+CmJvb2wgTWFrZVNhd1dhdmUoc3RkOjp2ZWN0b3I8RmxvYXQ+JiBELHN0ZDo6c2l6ZV90IFNhbXBsZVJhdGUsIHN0ZDo6c2l6ZV90IEh6ICkgewoJRmxvYXQgSHpfID0gSHo7CglGbG9hdCBTYW1wbGVSYXRlXyA9IFNhbXBsZVJhdGU7CglGbG9hdCBPbmUgPSBTYW1wbGVSYXRlXyAvIEh6XzsKCUZsb2F0IFZhbHVlID0gMi4wIC8gT25lOwoKCWZvciAoc3RkOjpzaXplX3QgaSA9IDA7IGkgPCBELnNpemUoKTsgaSsrKSB7CgkJRFtpXT1zdGQ6OmZtb2QoVmFsdWUqaSwyKS0xOwoJfQoJCglyZXR1cm4gdHJ1ZTsKfQp0ZW1wbGF0ZTxjbGFzcyBGbG9hdD4KYm9vbCBNYWtlVHJpYW5nbGVXYXZlKHN0ZDo6dmVjdG9yPEZsb2F0PiYgRCxzdGQ6OnNpemVfdCBTYW1wbGVSYXRlLCBzdGQ6OnNpemVfdCBIeiApIHsKCUZsb2F0IEh6XyA9IEh6OwoJRmxvYXQgU2FtcGxlUmF0ZV8gPSBTYW1wbGVSYXRlOwoJRmxvYXQgT25lID0gU2FtcGxlUmF0ZV8gLyBIel87CglGbG9hdCBWYWx1ZSA9IDEuMCAvKE9uZS80KTsKCUZsb2F0IEIgPSAtMTsKCUZsb2F0IFNpZ24gPSAxOwoJYm9vbCBGID0gZmFsc2U7Cglmb3IgKHN0ZDo6c2l6ZV90IGkgPSAwOyBpIDwgRC5zaXplKCk7IGkrKykgewoJCWlmIChzdGQ6OmFicyhCICsgKFZhbHVlKlNpZ24pKSA+IDEpIHsKCQkJQiAtPSAoc3RkOjpmbW9kKHN0ZDo6YWJzKEIgKyAoVmFsdWUqU2lnbikpLCAxKSpTaWduKTsKCQkJU2lnbiAqPSAtMTsKCQl9CgkJZWxzZSB7CgkJCUIgKz0gVmFsdWUqU2lnbjsKCQl9CgkJRFtpXSA9IEI7Cgl9CgkKCXJldHVybiB0cnVlOwp9CnRlbXBsYXRlPGNsYXNzIEZsb2F0Pgpib29sIE1ha2VTcXVhcmVXYXZlKHN0ZDo6dmVjdG9yPEZsb2F0PiYgRCxzdGQ6OnNpemVfdCBTYW1wbGVSYXRlLCBzdGQ6OnNpemVfdCBIeiApIHsKCUZsb2F0IEh6XyA9IEh6OwoJRmxvYXQgU2FtcGxlUmF0ZV8gPSBTYW1wbGVSYXRlOwoJRmxvYXQgT25lID0gU2FtcGxlUmF0ZV8gLyBIel87CglGbG9hdCBWYWx1ZSA9IDE7CglGbG9hdCBTaWduID0gMTsKCUZsb2F0IEEgPSAwOwoJRmxvYXQgQiA9IDA7Cglmb3IgKHN0ZDo6c2l6ZV90IGkgPSAwOyBpIDwgRC5zaXplKCk7IGkrKykgewoJCWlmIChzdGF0aWNfY2FzdDxpbnQ+KEEpID4gc3RhdGljX2Nhc3Q8aW50PihCKSkgewoJCQlCID0gQTsKCQkJU2lnbiAqPSAtMTsKCQl9CgkJQSA9IGkgLyAoT25lLzIpOwoJCURbaV0gPSBWYWx1ZSpTaWduOwoJfQoJCglyZXR1cm4gdHJ1ZTsKfQp0ZW1wbGF0ZTxjbGFzcyBGbG9hdD4KYm9vbCBNYWtlSHpOb2lzZShzdGQ6OnZlY3RvcjxGbG9hdD4mIEQsc3RkOjpzaXplX3QgU2FtcGxlUmF0ZSwgc3RkOjpzaXplX3QgSHogKSB7CglGbG9hdCBIel8gPSBIejsKCUZsb2F0IFNhbXBsZVJhdGVfID0gU2FtcGxlUmF0ZTsKCUZsb2F0IE9uZSA9IFNhbXBsZVJhdGVfIC8gSHpfOwoJRmxvYXQgVmFsdWUgPSAxOwoJRmxvYXQgQSA9IDA7CglGbG9hdCBCID0gMDsKCXN0ZDo6cmFuZG9tX2RldmljZSByZDsKCXN0ZDo6bXQxOTkzNyBtdChyZCgpKTsKCXN0ZDo6dW5pZm9ybV9yZWFsX2Rpc3RyaWJ1dGlvbjxGbG9hdD4gVVJEKC0xLCAxKTsKCWZvciAoc3RkOjpzaXplX3QgaSA9IDA7IGkgPCBELnNpemUoKTsgaSsrKSB7CgkJaWYgKHN0YXRpY19jYXN0PGludD4oQSkgPiBzdGF0aWNfY2FzdDxpbnQ+KEIpKSB7CgkJCUIgPSBBOwoJCQlWYWx1ZSA9IFVSRChtdCk7CgkJCgkJfQoJCUEgPSBpIC8gKE9uZS8yKTsKCQlEW2ldID0gVmFsdWU7Cgl9CgkKCXJldHVybiB0cnVlOwp9CnRlbXBsYXRlPGNsYXNzIEZsb2F0Pgpib29sIE1ha2VQYXJmZWN0Tm9pc2Uoc3RkOjp2ZWN0b3I8RmxvYXQ+JiBELHN0ZDo6c2l6ZV90IFNhbXBsZVJhdGUsIHN0ZDo6c2l6ZV90IEh6ICkgewoJRmxvYXQgSHpfID0gSHo7CglGbG9hdCBTYW1wbGVSYXRlXyA9IFNhbXBsZVJhdGU7CglGbG9hdCBPbmUgPSBTYW1wbGVSYXRlXyAvIEh6XzsKCXN0ZDo6cmFuZG9tX2RldmljZSByZDsKCXN0ZDo6bXQxOTkzNyBtdChyZCgpKTsKCXN0ZDo6dW5pZm9ybV9yZWFsX2Rpc3RyaWJ1dGlvbjxGbG9hdD4gVVJEKC0xLCAxKTsKCWZvciAoc3RkOjpzaXplX3QgaSA9IDA7IGkgPCBELnNpemUoKTsgaSsrKSB7CgkJRFtpXSA9IFVSRChtdCk7Cgl9CgkKCXJldHVybiB0cnVlOwp9CnRlbXBsYXRlPGNsYXNzIEZsb2F0PgpGbG9hdCBTaW4oY29uc3QgRmxvYXQmIEQpIHsvL1swLDFdCglGbG9hdCBQSSA9IDMuMTQxNTkyNjUzNTk7Ly9mcm9tIGdvb2dsZS4KCUZsb2F0IERlbHRhID0gc3RkOjpmbW9kKHN0ZDo6YWJzKEQpLCAxKTsKCglyZXR1cm4gc3RkOjpzaW4oMipQSSpEZWx0YSk7Cn0KdGVtcGxhdGU8Y2xhc3MgRmxvYXQ+CkZsb2F0IFNhdyhjb25zdCBGbG9hdCYgRCkgey8vWzAsMV0KCUZsb2F0IERlbHRhID0gc3RkOjpmbW9kKHN0ZDo6YWJzKEQpKzAuNSwgMSk7CgoJcmV0dXJuIC0xICsgKERlbHRhICogMik7Cn0KdGVtcGxhdGU8Y2xhc3MgRmxvYXQ+CkZsb2F0IFNxdWFyZShjb25zdCBGbG9hdCYgRCkgey8vWzAsMV0KCUZsb2F0IERlbHRhID0gc3RkOjpmbW9kKHN0ZDo6YWJzKEQpLCAxKTsKCXJldHVybiBEZWx0YSA+IDAuNSA/IDEgOiAtMTsKfQp0ZW1wbGF0ZTxjbGFzcyBGbG9hdD4KRmxvYXQgVHJpYW5nbGUoY29uc3QgRmxvYXQmIEQpIHsvL1swLDFdCQoJRmxvYXQgRGVsdGEgPSBzdGQ6OmZtb2Qoc3RkOjphYnMoRCksIDEpOwoJc3RkOjp1aW50NjRfdCBGID0gc3RhdGljX2Nhc3Q8c3RkOjp1aW50NjRfdD4oRGVsdGEgLyAwLjI1KTsKCUZsb2F0IFYgPSBzdGQ6OmZtb2QoRGVsdGEsIDAuMjUpKjQ7Cglzd2l0Y2ggKEYpCgl7CgkJY2FzZSAwOgoJCQlyZXR1cm4gVjsKCQljYXNlIDE6CgkJCXJldHVybiAxIC0gVjsKCQljYXNlIDI6CgkJCXJldHVybiAtVjsKCQljYXNlIDM6CgkJCXJldHVybiAtMSArIFY7CglkZWZhdWx0OgoJCWJyZWFrOwoJfQoJcmV0dXJuIDA7Cn0KdGVtcGxhdGU8Y2xhc3MgRmxvYXQ+CkZsb2F0IFRyYXBlem9pZChjb25zdCBGbG9hdCYgRCkgey8vWzAsMV0JCglGbG9hdCBEZWx0YSA9IHN0ZDo6Zm1vZChzdGQ6OmFicyhEKSwgMSk7CglzdGQ6OnVpbnQ2NF90IEYgPSBzdGF0aWNfY2FzdDxzdGQ6OnVpbnQ2NF90PihEZWx0YSAvICgxLzguMCkpOwoJRmxvYXQgViA9IHN0ZDo6Zm1vZChEZWx0YSwgKDEvOC4wKSkqODsKCXN3aXRjaCAoRikKCXsKCQljYXNlIDA6CgkJCXJldHVybiBWOwoJCWNhc2UgMToKCQljYXNlIDI6CgkJCXJldHVybiAxOwoJCWNhc2UgMzoJCQoJCQlyZXR1cm4gMSAtIFY7CgkJY2FzZSA0OgoJCQlyZXR1cm4gLSBWOwoJCWNhc2UgNToKCQljYXNlIDY6CgkJCXJldHVybiAtMTsKCQljYXNlIDc6CgkJCXJldHVybiAtMSArIFY7CglkZWZhdWx0OgoJCWJyZWFrOwoJfQoJcmV0dXJuIDA7Cn0KdGVtcGxhdGU8Y2xhc3MgRmxvYXQ+CkZsb2F0IE5vaXplKGNvbnN0IEZsb2F0JiBEKSB7Ly9bMCwxXQoJRmxvYXQgRGVsdGEgPSBzdGQ6OmZtb2Qoc3RkOjphYnMoRCksIDEpOwoJc3RkOjptdDE5OTM3IG10KHN0YXRpY19jYXN0PHVuc2lnbmVkIGludD4oc3RkOjpudW1lcmljX2xpbWl0czx1bnNpZ25lZCBpbnQ+OjptYXgoKSpEZWx0YSkpOwoJc3RkOjp1bmlmb3JtX3JlYWxfZGlzdHJpYnV0aW9uPEZsb2F0PiB1aWQoLTEsIDEpOwoJcmV0dXJuIHVpZChtdCk7Cn0KdGVtcGxhdGU8Y2xhc3MgRmxvYXQ+CkZsb2F0IFBlcmZlY3ROb2l6ZShjb25zdCBGbG9hdCYgRCkgey8vWzAsMV0KCXN0ZDo6cmFuZG9tX2RldmljZSByZDsKCXN0ZDo6dW5pZm9ybV9yZWFsX2Rpc3RyaWJ1dGlvbjxGbG9hdD4gdWlkKC0xLCAxKTsKCXJldHVybiB1aWQocmQpOwp9CnRlbXBsYXRlIDxjbGFzcyBGbG9hdCAsIGNsYXNzIEY+CmJvb2wgR2VuZXJhdG9yVG1wbGF0ZShzdGQ6OnZlY3RvcjxGbG9hdD4mIEQsIHN0ZDo6c2l6ZV90IFNhbXBsZVJhdGUsIHN0ZDo6c2l6ZV90IEh6LEYgRnVuKSB7CglGbG9hdCBIel8gPSBIejsKCUZsb2F0IFNhbXBsZVJhdGVfID0gU2FtcGxlUmF0ZTsKCUZsb2F0IE9uZSA9IDEvKFNhbXBsZVJhdGVfIC8gSHpfKTsKCglmb3IgKHN0ZDo6c2l6ZV90IGkgPSAwOyBpIDwgRC5zaXplKCk7IGkrKykgewoJCURbaV0gPSBGdW4oT25lKmkpOwoJfQoKCXJldHVybiB0cnVlOwp9CgovL2h0dHBzOi8vZS4uLmNvbnRlbnQtYXZhaWxhYmxlLXRvLWF1dGhvci1vbmx5Li4uYS5vcmcvd2lraS9GcmVxdWVuY3lfbW9kdWxhdGlvbl9zeW50aGVzaXMjU3BlY3RyYWxfYW5hbHlzaXMKdGVtcGxhdGUgPGNsYXNzIEZsb2F0PgpzdGQ6OnZlY3RvcjxGbG9hdD4gRk1fKGNvbnN0IHN0ZDo6dmVjdG9yPEZsb2F0PiYgRCxjb25zdCBzdGQ6OnZlY3RvcjxGbG9hdD4mIE0sIGRvdWJsZSBGaXJzdFRoZXRhLCBkb3VibGUgU2VjYW5kVGhldGEgKXsgICAgIC8vLCBkb3VibGUgVGltZSkgewoJc3RkOjp2ZWN0b3I8RmxvYXQ+IFI7Cglkb3VibGUgUEkgPSAzLjE0MTU5MjY1MzU4OTc5MzIzODQ2OwoJZG91YmxlIFJhZCA9IDE4MC4wIC8gUEk7CgoJZm9yIChzdGQ6OnNpemVfdCBpID0gMTsgaSA8IEQuc2l6ZSgpOyBpKyspIHsKCQkvL2RvdWJsZSBUID0gKE1baSAlIE0uc2l6ZSgpXSAqIHN0ZDo6c2luKChGaXJzdFRoZXRhKlJhZCkgKyAoU2VjYW5kVGhldGEqUmFkKSppICkpKihzdGF0aWNfY2FzdDxkb3VibGU+KEQuc2l6ZSgpKSAvIHN0YXRpY19jYXN0PGRvdWJsZT4oaSkpOwoJCWRvdWJsZSBUID0gKHN0YXRpY19jYXN0PGRvdWJsZT4oRC5zaXplKCkpIC8gc3RhdGljX2Nhc3Q8ZG91YmxlPihpKSk7CgkJZG91YmxlIE11bCA9IHN0ZDo6c2luKChGaXJzdFRoZXRhKlJhZCkqVCArIE1baSAlIE0uc2l6ZSgpXSAqIHN0ZDo6c2luKChTZWNhbmRUaGV0YSpSYWQpKSpUKTsKCQlSLnB1c2hfYmFjayhEW2ldICpNdWwpOwoJfQoKCXJldHVybiBSOwoKfQoKdGVtcGxhdGU8Y2xhc3MgRmxvYXQsY2xhc3MgRjEsY2xhc3MgRjI+CkZsb2F0IEZNT25lKGNvbnN0IEZsb2F0JiBBLCBjb25zdCBGbG9hdCYgQixjb25zdCBGbG9hdCYgSW5pdEEsIGNvbnN0IEZsb2F0JiBJbml0QiwgY29uc3QgRmxvYXQmIFRpbWUsIEYxIEZ1bjEsIEYyIEZ1bjIpIHsKCXJldHVybiBBKkZ1bjEoSW5pdEEqVGltZSArIEIqRnVuMihJbml0QipUaW1lKSk7Cn0KdGVtcGxhdGU8Y2xhc3MgRmxvYXQsY2xhc3MgRjEsY2xhc3MgRjI+CnN0ZDo6dmVjdG9yPEZsb2F0PiBGTShjb25zdCBzdGQ6OnZlY3RvcjxGbG9hdD4mIEQsIGNvbnN0IHN0ZDo6dmVjdG9yPEZsb2F0PiYgTSwgY29uc3QgRmxvYXQmIEluaXRBLCBjb25zdCBGbG9hdCYgSW5pdEIsIEYxIEZ1bjEsIEYyIEZ1bjIpIHsJCglzdGQ6OnZlY3RvcjxGbG9hdD4gUjsKCWZvciAoc3RkOjpzaXplX3QgaSA9IDE7IGkgPCBELnNpemUoKTsgaSsrKSB7CgkJRmxvYXQgViA9IEZNT25lKERbaV0sIE1baSVNLnNpemUoKV0sIEluaXRBLCBJbml0QiwgaSAvIHN0YXRpY19jYXN0PEZsb2F0PihELnNpemUoKSksIEZ1bjEsIEZ1bjIpOwoJCVIucHVzaF9iYWNrKFYpOwoJfQoJcmV0dXJuIFI7Cn0KdGVtcGxhdGU8Y2xhc3MgRmxvYXQsY2xhc3MgRjEsY2xhc3MgRjIsY2xhc3MgRjM+CkZsb2F0IEZNT25lMihjb25zdCBGbG9hdCYgQSwgRjMgQixjb25zdCBGbG9hdCYgSW5pdEIsIEYxIEZ1bjEsY29uc3QgRmxvYXQmIEluaXQxLCAgRjIgRnVuMixjb25zdCBGbG9hdCYgSW5pdDIgLGNvbnN0IEZsb2F0JiBUaW1lKSB7CglyZXR1cm4gQSpGdW4xKEluaXQxKlRpbWUgKyBCKFRpbWUqSW5pdEIpKkZ1bjIoSW5pdDIqVGltZSkpOwp9CnRlbXBsYXRlPGNsYXNzIEZsb2F0LGNsYXNzIEYxLGNsYXNzIEYyLGNsYXNzIEYzPgpzdGQ6OnZlY3RvcjxGbG9hdD4gRk0yKGNvbnN0IHN0ZDo6dmVjdG9yPEZsb2F0PiYgRCwgRjMgQixjb25zdCBGbG9hdCYgSW5pdEIsIEYxIEZ1bjEsY29uc3QgRmxvYXQmIEluaXQxLCAgRjIgRnVuMixjb25zdCBGbG9hdCYgSW5pdDIgKSB7CQoJc3RkOjp2ZWN0b3I8RmxvYXQ+IFI7Cglmb3IgKHN0ZDo6c2l6ZV90IGkgPSAxOyBpIDwgRC5zaXplKCk7IGkrKykgewoJCUZsb2F0IFYgPSBGTU9uZTIoRFtpXSxCLEluaXRCLEZ1bjEsSW5pdDEsRnVuMixJbml0MiwgaSAvIHN0YXRpY19jYXN0PEZsb2F0PihELnNpemUoKSkpOwoJCVIucHVzaF9iYWNrKFYpOwoJfQoJcmV0dXJuIFI7Cn0KdGVtcGxhdGU8Y2xhc3MgSW50LGNsYXNzIEZsb2F0PgpzdGQ6OnZlY3RvcjxJbnQ+IE1ha2VXYXZlSW5zdGFuY2Uoc3RkOjp2ZWN0b3I8RmxvYXQ+JiBGLGRvdWJsZSBWb2x1bWUpIHsKCXN0ZDo6dmVjdG9yPEludD4gUjsKCWZvciAoYXV0byBvIDogRikgewoJCVIucHVzaF9iYWNrKHN0YXRpY19jYXN0PEludD4obyooc3RkOjpudW1lcmljX2xpbWl0czxJbnQ+OjptYXgoKSkqVm9sdW1lKSk7Cgl9CglyZXR1cm4gUjsKfQp0ZW1wbGF0ZTxjbGFzcyBJbnQ+CmJvb2wgV3JpdGVXYXZlRmlsZShzdGQ6OnZlY3RvcjxJbnQ+JiBELCBzdGQ6OnVpbnQ2NF90IEh6LCBzdGQ6OnVpbnQxNl90IENoYW5uZWwsIHN0ZDo6c3RyaW5nIE5hbWUgPSAib3V0LndhdiIpIHsKCXN0ZDo6b2ZzdHJlYW0gb2ZzKE5hbWUsIHN0ZDo6aW9zOjpiaW5hcnkpOwoJY2hhciBSSUZGW10gPSAiUklGRiI7CgljaGFyIFdBVkVbXSA9ICJXQVZFIjsKCWNoYXIgZm10W10gPSAiZm10ICI7CgljaGFyIGRhdGFbXSA9ICJkYXRhIjsKCXN0ZDo6dWludDMyX3QgRlNpemUgPSAoNDQgLSA4KSArIChELnNpemUoKSAqIHNpemVvZihzdGQ6OnZlY3RvcjxJbnQ+Ojp2YWx1ZV90eXBlKSk7CglzdGQ6OnVpbnQzMl90IHVpMzIgPSAwOwoJc3RkOjp1aW50MTZfdCB1aTE2ID0gMDsKCW9mcy53cml0ZShSSUZGLCA0KTsKCW9mcy53cml0ZShyZWludGVycHJldF9jYXN0PGNvbnN0IGNoYXIqPigmRlNpemUpLCA0KTsvL2ZzaXplLTguCQoJb2ZzLndyaXRlKFdBVkUsIDQpOwoJb2ZzLndyaXRlKGZtdCwgNCk7Cgl1aTMyID0gMTY7CglvZnMud3JpdGUocmVpbnRlcnByZXRfY2FzdDxjb25zdCBjaGFyKj4oJnVpMzIpLCA0KTsvL2ZtdCBjaHVuayBzaXplCgl1aTE2ID0gMTsKCW9mcy53cml0ZShyZWludGVycHJldF9jYXN0PGNvbnN0IGNoYXIqPigmdWkxNiksIDIpOy8vc291bmQgdHlwZS4gUENNID0gMS4KCXVpMTYgPSBDaGFubmVsOwoJb2ZzLndyaXRlKHJlaW50ZXJwcmV0X2Nhc3Q8Y29uc3QgY2hhcio+KCZ1aTE2KSwgMik7Ly9DaGFubm5lbCBjb3VudC4KCXVpMzIgPSBzdGF0aWNfY2FzdDxzdGQ6OnVpbnQzMl90PihIeik7CglvZnMud3JpdGUocmVpbnRlcnByZXRfY2FzdDxjb25zdCBjaGFyKj4oJnVpMzIpLCA0KTsvL1NhbXBsaW5nIEh6LgoJdWkzMiA9IHN0YXRpY19jYXN0PHN0ZDo6dWludDMyX3Q+KEh6KkNoYW5uZWwqKHNpemVvZihzdGQ6OnZlY3RvcjxJbnQ+Ojp2YWx1ZV90eXBlKSkpOwoJb2ZzLndyaXRlKHJlaW50ZXJwcmV0X2Nhc3Q8Y29uc3QgY2hhcio+KCZ1aTMyKSwgNCk7Ly9kYXRhIHNwZWVkPz8KCXVpMTYgPSBDaGFubmVsKihzaXplb2Yoc3RkOjp2ZWN0b3I8SW50Pjo6dmFsdWVfdHlwZSkpOwoJb2ZzLndyaXRlKHJlaW50ZXJwcmV0X2Nhc3Q8Y29uc3QgY2hhcio+KCZ1aTE2KSwgMik7Ly9CbG9ja1NpemU/PwoJdWkxNiA9IChzaXplb2Yoc3RkOjp2ZWN0b3I8SW50Pjo6dmFsdWVfdHlwZSkgKiA4KTsKCW9mcy53cml0ZShyZWludGVycHJldF9jYXN0PGNvbnN0IGNoYXIqPigmdWkxNiksIDIpOy8vU2FtcGxpbmcgQml0LgoJb2ZzLndyaXRlKGRhdGEsIDQpOwoJdWkzMiA9IHN0YXRpY19jYXN0PHN0ZDo6dWludDMyX3Q+KEQuc2l6ZSgpICogc2l6ZW9mKHN0ZDo6dmVjdG9yPEludD46OnZhbHVlX3R5cGUpKTsKCW9mcy53cml0ZShyZWludGVycHJldF9jYXN0PGNvbnN0IGNoYXIqPigmdWkzMiksIDQpOy8vU2FtcGxlJ3MgQUxMIFNJWkUubWF4IGF0IDMyYml0LgoKCW9mcy53cml0ZShyZWludGVycHJldF9jYXN0PGNvbnN0IGNoYXIqPigmRFswXSksIEQuc2l6ZSgpICogc2l6ZW9mKHN0ZDo6dmVjdG9yPEludD46OnZhbHVlX3R5cGUpKTsvL1dyaXRlU2FtcGxlcy4KCglyZXR1cm4gdHJ1ZTsKfQp0ZW1wbGF0ZTxjbGFzcyBUPgpib29sIFZvbHVtZUNoZW5nZXIoc3RkOjp2ZWN0b3I8VD4mIEQgLGRvdWJsZSBQZXJjZW50YWdlKSB7Ly8vMS4wID0+IDEwMCBQZXJjZW50OwoJZm9yIChhdXRvIG8gOiBEKSB7CgkJbyAqPSBQZXJjZW50YWdlOwoJfQoJcmV0dXJuIHRydWU7Cn0KdGVtcGxhdGU8Y2xhc3MgRmxvYXQ+CmJvb2wgVm9sdW1lTm9ybWFsaXplKHN0ZDo6dmVjdG9yPEZsb2F0PiYgRCApIHsKCUZsb2F0IE1heD0wOwoJRmxvYXQgTSA9IDA7Cglmb3IgKHN0ZDo6c2l6ZV90IGkgPSAwOyBpIDwgRC5zaXplKCk7aSsrKSB7CgkJTWF4ID0gc3RkOjptYXg8RmxvYXQ+KE1heCwgc3RkOjphYnMoRFtpXSkpOwoJfQoJTSA9IDEgLyBNYXg7Cglmb3IgKGF1dG8gbyA6IEQpIHsKCQlvICo9IE07Cgl9CglyZXR1cm4gdHJ1ZTsKfQp2b2lkIFRlc3Qoc3RkOjpzaXplX3QgU2FtcGxlUmF0ZSxzdGQ6OnNpemVfdCBNaWxsaVNlYyxkb3VibGUgVm9sdW1lKSB7CglzdGQ6OnZlY3RvcjxzdGQ6OmludDE2X3Q+IEQ7CglzdGQ6OnZlY3Rvcjxkb3VibGU+IEI7CglzdGQ6OnN0cmluZyBQYXRoID0gIi4vd2F2LyI7CgoJQiA9IE1ha2VCdWZmZXI8ZG91YmxlPihTYW1wbGVSYXRlLCBNaWxsaVNlYyk7CglNYWtlU2luV2F2ZShCLCBTYW1wbGVfLCAyMjApOwoJLy9Wb2x1bWVDaGVuZ2VyKEIsIFZvbHVtZSk7CglEID0gTWFrZVdhdmVJbnN0YW5jZTxzdGQ6OmludDE2X3Q+KEIsIFZvbHVtZSk7CglXcml0ZVdhdmVGaWxlKEQsIFNhbXBsZV8sIDEsIChQYXRoKyJTaW4ud2F2IikpOwoKCUIgPSBNYWtlQnVmZmVyPGRvdWJsZT4oU2FtcGxlUmF0ZSwgTWlsbGlTZWMpOwoJTWFrZVNhd1dhdmUoQiwgU2FtcGxlXywgMjIwKTsJCgkvL1ZvbHVtZUNoZW5nZXIoQiwgVm9sdW1lKTsKCUQgPSBNYWtlV2F2ZUluc3RhbmNlPHN0ZDo6aW50MTZfdD4oQiwgVm9sdW1lKTsKCVdyaXRlV2F2ZUZpbGUoRCwgU2FtcGxlXywgMSwgUGF0aCsiU2F3LndhdiIpOwkKCQoJQiA9IE1ha2VCdWZmZXI8ZG91YmxlPihTYW1wbGVSYXRlLCBNaWxsaVNlYyk7CQoJTWFrZVRyaWFuZ2xlV2F2ZShCLCBTYW1wbGVfLCAyMjApOwoJLy9Wb2x1bWVDaGVuZ2VyKEIsIFZvbHVtZSk7CglEID0gTWFrZVdhdmVJbnN0YW5jZTxzdGQ6OmludDE2X3Q+KEIsIFZvbHVtZSk7CglXcml0ZVdhdmVGaWxlKEQsIFNhbXBsZV8sIDEsIFBhdGgrIlRyaWFuZ2xlLndhdiIpOwkKCQoJQiA9IE1ha2VCdWZmZXI8ZG91YmxlPihTYW1wbGVSYXRlLCBNaWxsaVNlYyk7CQoJTWFrZVNxdWFyZVdhdmUoQiwgU2FtcGxlXywgMjIwKTsKCS8vL1ZvbHVtZUNoZW5nZXIoQiwgVm9sdW1lKTsJCglEID0gTWFrZVdhdmVJbnN0YW5jZTxzdGQ6OmludDE2X3Q+KEIsIFZvbHVtZSk7CglXcml0ZVdhdmVGaWxlKEQsIFNhbXBsZV8sIDEsIFBhdGgrIlNxdWFyZS53YXYiKTsJCgkKCUIgPSBNYWtlQnVmZmVyPGRvdWJsZT4oU2FtcGxlUmF0ZSwgTWlsbGlTZWMpOwkKCU1ha2VIek5vaXNlKEIsIFNhbXBsZV8sIDIyMCk7CgkvL1ZvbHVtZUNoZW5nZXIoQiwgVm9sdW1lKTsKCUQgPSBNYWtlV2F2ZUluc3RhbmNlPHN0ZDo6aW50MTZfdD4oQiwgVm9sdW1lKTsKCVdyaXRlV2F2ZUZpbGUoRCwgU2FtcGxlXywgMSwgUGF0aCsiSHpOb2l6ZS53YXYiKTsJCgkKCUIgPSBNYWtlQnVmZmVyPGRvdWJsZT4oU2FtcGxlUmF0ZSwgTWlsbGlTZWMpOwkKCU1ha2VQYXJmZWN0Tm9pc2UoQiwgU2FtcGxlXywgMjIwKTsKCS8vVm9sdW1lQ2hlbmdlcihCLCBWb2x1bWUpOwoJRCA9IE1ha2VXYXZlSW5zdGFuY2U8c3RkOjppbnQxNl90PihCLCBWb2x1bWUpOwoJV3JpdGVXYXZlRmlsZShELCBTYW1wbGVfLCAxLCBQYXRoKyJQTm9pc2Uud2F2Iik7Cgp9CnZvaWQgR2VuZXJhdG9yVGVzdCgpIHsKc3RkOjp2ZWN0b3I8c3RkOjppbnQxNl90PiBEOwoJc3RkOjp2ZWN0b3I8ZG91YmxlPiBCMTsKCXN0ZDo6dmVjdG9yPGRvdWJsZT4gQjI7CglzdGQ6OnZlY3Rvcjxkb3VibGU+IFI7CglzdGQ6OnN0cmluZyBQYXRoID0gIi4vd2F2LyI7CgoJc3RkOjpzaXplX3QgU2FtcGxlUmF0ZT1TYW1wbGVfOwoJc3RkOjpzaXplX3QgTWlsbGlTZWMgPSAyMDAwOwoJZG91YmxlIFZvbHVtZT0xLjA7CgoJQjEgPSBNYWtlQnVmZmVyPGRvdWJsZT4oU2FtcGxlUmF0ZSwgTWlsbGlTZWMpOwkKCUdlbmVyYXRvclRtcGxhdGUoQjEsIFNhbXBsZV8sIDIyMCwgU2luPGRvdWJsZT4pOwkKCUQgPSBNYWtlV2F2ZUluc3RhbmNlPHN0ZDo6aW50MTZfdD4oQjEsVm9sdW1lKTsKCVdyaXRlV2F2ZUZpbGUoRCwgU2FtcGxlXywgMSwgUGF0aCsiU2luRi53YXYiKTsKCglCMSA9IE1ha2VCdWZmZXI8ZG91YmxlPihTYW1wbGVSYXRlLCBNaWxsaVNlYyk7CQoJR2VuZXJhdG9yVG1wbGF0ZShCMSwgU2FtcGxlXywgMjIwLCBTYXc8ZG91YmxlPik7CQoJRCA9IE1ha2VXYXZlSW5zdGFuY2U8c3RkOjppbnQxNl90PihCMSxWb2x1bWUpOwoJV3JpdGVXYXZlRmlsZShELCBTYW1wbGVfLCAxLCBQYXRoKyJTYXdGLndhdiIpOwoKCUIxID0gTWFrZUJ1ZmZlcjxkb3VibGU+KFNhbXBsZVJhdGUsIE1pbGxpU2VjKTsJCglHZW5lcmF0b3JUbXBsYXRlKEIxLCBTYW1wbGVfLCAyMjAsIFNxdWFyZTxkb3VibGU+KTsJCglEID0gTWFrZVdhdmVJbnN0YW5jZTxzdGQ6OmludDE2X3Q+KEIxLFZvbHVtZSk7CglXcml0ZVdhdmVGaWxlKEQsIFNhbXBsZV8sIDEsIFBhdGgrIlNxdWFyZUYud2F2Iik7CgoJQjEgPSBNYWtlQnVmZmVyPGRvdWJsZT4oU2FtcGxlUmF0ZSwgTWlsbGlTZWMpOwkKCUdlbmVyYXRvclRtcGxhdGUoQjEsIFNhbXBsZV8sIDIyMCwgVHJpYW5nbGU8ZG91YmxlPik7CQoJRCA9IE1ha2VXYXZlSW5zdGFuY2U8c3RkOjppbnQxNl90PihCMSxWb2x1bWUpOwoJV3JpdGVXYXZlRmlsZShELCBTYW1wbGVfLCAxLCBQYXRoKyJUcmlhbmdsZUYud2F2Iik7CgoJQjEgPSBNYWtlQnVmZmVyPGRvdWJsZT4oU2FtcGxlUmF0ZSwgTWlsbGlTZWMpOwkKCUdlbmVyYXRvclRtcGxhdGUoQjEsIFNhbXBsZV8sIDIyMCwgVHJhcGV6b2lkPGRvdWJsZT4pOwoJRCA9IE1ha2VXYXZlSW5zdGFuY2U8c3RkOjppbnQxNl90PihCMSxWb2x1bWUpOwoJV3JpdGVXYXZlRmlsZShELCBTYW1wbGVfLCAxLCBQYXRoKyJUcmFwZXpvaWRGLndhdiIpOwoKCUIxID0gTWFrZUJ1ZmZlcjxkb3VibGU+KFNhbXBsZVJhdGUsIE1pbGxpU2VjKTsJCglHZW5lcmF0b3JUbXBsYXRlKEIxLCBTYW1wbGVfLCAyMjAsIE5vaXplPGRvdWJsZT4pOwkKCUQgPSBNYWtlV2F2ZUluc3RhbmNlPHN0ZDo6aW50MTZfdD4oQjEsVm9sdW1lKTsKCVdyaXRlV2F2ZUZpbGUoRCwgU2FtcGxlXywgMSwgUGF0aCsiTm9pemVGLndhdiIpOwoJCglCMSA9IE1ha2VCdWZmZXI8ZG91YmxlPihTYW1wbGVSYXRlLCBNaWxsaVNlYyk7CQoJR2VuZXJhdG9yVG1wbGF0ZShCMSwgU2FtcGxlXywgMjIwLCBQZXJmZWN0Tm9pemU8ZG91YmxlPik7CQoJRCA9IE1ha2VXYXZlSW5zdGFuY2U8c3RkOjppbnQxNl90PihCMSxWb2x1bWUpOwoJV3JpdGVXYXZlRmlsZShELCBTYW1wbGVfLCAxLCBQYXRoKyJQTm9pemVGLndhdiIpOwp9CnZvaWQgRk1UZXN0KCkgewpzdGQ6OnZlY3RvcjxzdGQ6OmludDE2X3Q+IEQ7CglzdGQ6OnZlY3Rvcjxkb3VibGU+IEIxOwoJc3RkOjp2ZWN0b3I8ZG91YmxlPiBCMjsKCXN0ZDo6dmVjdG9yPGRvdWJsZT4gUjsKCXN0ZDo6c3RyaW5nIFBhdGggPSAiLi93YXYvIjsKCglzdGQ6OnNpemVfdCBTYW1wbGVSYXRlPVNhbXBsZV87CglzdGQ6OnNpemVfdCBNaWxsaVNlYyA9IDIwMDA7Cglkb3VibGUgVm9sdW1lPTAuODsKCglCMSA9IE1ha2VCdWZmZXI8ZG91YmxlPihTYW1wbGVSYXRlLCBNaWxsaVNlYyk7CQoJTWFrZVNhd1dhdmUoQjEsIFNhbXBsZV8sIDIyMCk7CglWb2x1bWVDaGVuZ2VyKEIxLCAwLjEpOwoJQjIgPSBNYWtlQnVmZmVyPGRvdWJsZT4oU2FtcGxlUmF0ZSwgTWlsbGlTZWMpOwkKCU1ha2VTaW5XYXZlKEIyLCBTYW1wbGVfLCAyMjApOwoJLy9NYWtlSHpOb2lzZShCMiwgU2FtcGxlXywgMjIwKTsKCS8vTWFrZVNhd1dhdmUoQjIsIFNhbXBsZV8sIDIyMCk7CglWb2x1bWVDaGVuZ2VyKEIyLCAwLjEpOwoKCVIgPSBGTV8oQjEsIEIyLCAzMCwgNyk7CgkvL1IgPSBGTV8oUiwgQjEsIDMwLCAxMjApOwoKCS8vVm9sdW1lTm9ybWFsaXplPGRvdWJsZT4oUik7CgkvL1ZvbHVtZUNoZW5nZXIoQjIsIDEvMy4wKTsKCUQgPSBNYWtlV2F2ZUluc3RhbmNlPHN0ZDo6aW50MTZfdD4oUixWb2x1bWUpOwoJV3JpdGVXYXZlRmlsZShELCBTYW1wbGVfLCAxLCBQYXRoKyJGTS53YXYiKTsKfQp2b2lkIEZNVGVzdDIoKSB7CnN0ZDo6dmVjdG9yPHN0ZDo6aW50MTZfdD4gRDsKCXN0ZDo6dmVjdG9yPGRvdWJsZT4gQjE7CglzdGQ6OnZlY3Rvcjxkb3VibGU+IEIyOwoJc3RkOjp2ZWN0b3I8ZG91YmxlPiBSOwoJc3RkOjpzdHJpbmcgUGF0aCA9ICIuL3dhdi8iOwoKCXN0ZDo6c2l6ZV90IFNhbXBsZVJhdGU9U2FtcGxlXzsKCXN0ZDo6c2l6ZV90IE1pbGxpU2VjID0gMjAwMDsKCWRvdWJsZSBWb2x1bWU9MC44OwoKCUIxID0gTWFrZUJ1ZmZlcjxkb3VibGU+KFNhbXBsZVJhdGUsIE1pbGxpU2VjKTsJCglNYWtlU2luV2F2ZShCMSwgU2FtcGxlXywgMjIwKTsKCVZvbHVtZUNoZW5nZXIoQjEsIDAuMSk7CglCMiA9IE1ha2VCdWZmZXI8ZG91YmxlPihTYW1wbGVSYXRlLCBNaWxsaVNlYyk7CQoJTWFrZVRyaWFuZ2xlV2F2ZShCMiwgU2FtcGxlXywgMjIwKTsKCS8vTWFrZUh6Tm9pc2UoQjIsIFNhbXBsZV8sIDIyMCk7CgkvL01ha2VTYXdXYXZlKEIyLCBTYW1wbGVfLCAyMjApOwoJLy9Wb2x1bWVDaGVuZ2VyKEIyLCAwLjEpOwoKCVIgPSBGTTxkb3VibGU+KEIxLCBCMiwgMzAvMzYwLjAsMzAwMCxTaW48ZG91YmxlPixTaW48ZG91YmxlPik7CgkvL1IgPSBGTShSLCBCMSwgMzAsIDEyMCk7CgoJLy9Wb2x1bWVOb3JtYWxpemU8ZG91YmxlPihSKTsKCS8vVm9sdW1lQ2hlbmdlcihCMiwgMS8zLjApOwoJRCA9IE1ha2VXYXZlSW5zdGFuY2U8c3RkOjppbnQxNl90PihSLFZvbHVtZSk7CglXcml0ZVdhdmVGaWxlKEQsIFNhbXBsZV8sIDEsIFBhdGgrIkZNRy53YXYiKTsKfQp2b2lkIEZNVGVzdDMoKSB7CglzdGQ6OnZlY3RvcjxzdGQ6OmludDE2X3Q+IEQ7CglzdGQ6OnZlY3Rvcjxkb3VibGU+IEIxOwoJc3RkOjp2ZWN0b3I8ZG91YmxlPiBSOwoJc3RkOjpzdHJpbmcgUGF0aCA9ICIuL3dhdi8iOwoKCXN0ZDo6c2l6ZV90IFNhbXBsZVJhdGU9U2FtcGxlXzsKCXN0ZDo6c2l6ZV90IE1pbGxpU2VjID0gMjAwMDsKCWRvdWJsZSBWb2x1bWU9MC44OwoJc3RkOjp1aW50MTZfdCBDaGFubmVsID0gMTsKCglCMSA9IE1ha2VCdWZmZXI8ZG91YmxlPihTYW1wbGVSYXRlLCBNaWxsaVNlYyk7CQoJTWFrZVNpbldhdmUoQjEsIFNhbXBsZV8sIDIyMCk7CgkKCVIgPSBGTTI8ZG91YmxlPihCMSxTaW48ZG91YmxlPiwxLCBTaW48ZG91YmxlPiwzMC8zNjAsIFNpbjxkb3VibGU+LDMwMDApOwoJRCA9IE1ha2VXYXZlSW5zdGFuY2U8c3RkOjppbnQxNl90PihSLFZvbHVtZSk7CglXcml0ZVdhdmVGaWxlKEQsIFNhbXBsZV8sIENoYW5uZWwsIFBhdGgrIkZNRzIud2F2Iik7Cn0KCmludCBtYWluKCkgewoJLy9UZXN0KFNhbXBsZV8sMjAwMCwwLjgpOwoJLy9GTVRlc3QoKTsKCS8vR2VuZXJhdG9yVGVzdCgpOwoJLy9GTVRlc3QyKCk7CgkvL0ZNVGVzdDMoKTsKCXJldHVybiB0cnVlOwoJCn0=