PEMROGRAMAN SAINS DATA 1

EXAM 1 SMT 2

Logo

Pendahuluan

Transformasi data adalah teknik dalam pemrosesan data yang bertujuan untuk mengubah format, struktur, atau nilai data agar lebih sesuai untuk analisis, eksplorasi, dan pemodelan. Beberapa alasan utama transformasi data adalah:

  • Menstabilkan Variansi – Data dengan skala yang sangat berbeda dapat menyebabkan model menjadi tidak stabil atau sulit dipahami. Dengan transformasi, distribusi data dapat disesuaikan agar lebih seragam.
  • Mengurangi Skewness – Data sering kali memiliki distribusi yang miring (skewed), yang dapat mempengaruhi performa model statistik. Transformasi dapat membantu mendekatkan distribusi ke bentuk normal.
  • Menangani Outlier – Outlier atau nilai ekstrem dapat mendistorsi hasil analisis dan pemodelan. Teknik transformasi dapat membantu mengurangi pengaruhnya.
  • Mengubah Data Kategorikal – Data kategorikal tidak dapat langsung digunakan dalam banyak algoritma machine learning, sehingga perlu diubah menjadi bentuk numerik.
  • Mendeteksi Pola Musiman dan Tren – Dalam analisis deret waktu, transformasi membantu mengidentifikasi pola musiman dan tren dengan lebih jelas.
  • Mengurangi Dimensi Data – Ketika dataset memiliki terlalu banyak variabel, analisis menjadi sulit dan memakan waktu. Teknik seperti Principal Component Analysis (PCA) membantu menyederhanakan struktur data tanpa kehilangan informasi penting.

Laporan ini akan membahas berbagai teknik transformasi data.


Transformasi Data

1.Temporal Transformation

1.1 Lag, diff, rolling

Lagging adalah teknik yang digunakan untuk menggeser data ke belakang dalam satuan waktu tertentu. Ini sangat berguna dalam analisis deret waktu untuk memahami keterkaitan antara nilai saat ini dan masa lalu.

Teknik ini sering digunakan dalam model prediktif seperti ARIMA dan regresi deret waktu. Dengan menerapkan lagging, kita dapat melihat pola pergerakan suatu variabel terhadap waktu, yang memungkinkan analisis yang lebih mendalam.

Kelebihan:

  • Membantu memahami hubungan temporal antara variabel.
  • Berguna dalam pemodelan prediktif berbasis deret waktu.
  • Membantu dalam pembuatan fitur baru untuk model machine learning berbasis deret waktu.

Kekurangan:

  • Dapat menyebabkan kehilangan data pada awal periode.
  • Jika lag terlalu besar, informasi yang relevan bisa hilang.
  • Tidak selalu efektif jika pola hubungan antarwaktu tidak konsisten.

Contoh Penggunaan:

  • Dalam keuangan, harga saham hari ini bisa dipengaruhi oleh harga sebelumnya.
  • Dalam meteorologi, suhu hari ini mungkin bergantung pada suhu beberapa hari yang lalu.

Temporal <- health_data %>%
  arrange(Patient_ID, Date) %>%
  group_by(Patient_ID) %>%
  mutate(
    Lag_BP = lag(Blood_Pressure),
    Diff_BP = Blood_Pressure - lag(Blood_Pressure),
    RollingMean_BP_3 = zoo::rollapply(Blood_Pressure, width = 3, FUN = mean, fill = NA, align = "right")
  ) %>%
  ungroup()

Penjelasan Fungsi

Fungsi Tujuan
arrange(Patient_ID, Date) Mengurutkan data berdasarkan pasien dan tanggal untuk memastikan kronologi.
group_by(Patient_ID) Agar transformasi dilakukan per individu, bukan seluruh dataset.
lag(Blood_Pressure) Mengambil tekanan darah sebelumnya untuk masing-masing pasien.
Diff_BP Selisih antara nilai sekarang dan nilai sebelumnya — digunakan untuk mendeteksi tren naik/turun.
rollapply(…, width = 3) Rata-rata dari 3 nilai terakhir untuk membantu melihat tren yang lebih stabil (rolling mean).

Interpretasi Kesehatan

Fitur Transformasi Arti & Manfaat
Lag_BP Melihat efek masa lalu terhadap kondisi saat ini. Penting untuk model prediksi.
Diff_BP Mengindikasikan peningkatan atau penurunan tekanan darah. Bisa menandakan kondisi stabil atau memburuk.
RollingMean_BP_3 Membantu memfilter fluktuasi jangka pendek. Jika nilai saat ini jauh di atas rata-rata, bisa jadi alarm anomali.

Hasil Transformasi

Semua nilai Lag_BP, Diff_BP, dan RollingMean_BP_3 bernilai NA karena ini adalah baris pertama untuk masing-masing pasien. Tanpa observasi sebelumnya, tidak mungkin menghitung nilai lag, selisih, atau rata-rata bergulir.

1.2 Extract Hour, Day, Month, Year

Ekstraksi waktu adalah proses mengambil bagian spesifik dari timestamp, seperti jam, hari, bulan, atau tahun.
Ini berguna untuk analisis musiman atau pola perilaku pengguna berdasarkan waktu.

Kelebihan:

  • Membantu menemukan pola berdasarkan waktu (contoh: pembelian lebih banyak di akhir pekan).
  • Memungkinkan pembuatan fitur baru dari data waktu.

Kekurangan:

  • Jika tidak relevan dengan analisis, fitur ini bisa menjadi noise.
  • Membutuhkan format tanggal/waktu yang benar.

Extract <- health_data %>%
  mutate(
    Day_of_Week = weekdays(Date),
    Month = month(Date, label = TRUE),
    Year = year(Date),
    Is_Weekend = ifelse(Day_of_Week %in% c("Saturday", "Sunday"), 1, 0),
    Location = as.factor(Location),
    Health_Condition = as.factor(Health_Condition)
  ) %>%
  bind_cols(
    as.data.frame(model.matrix(~ Location - 1, data = .)),
    as.data.frame(model.matrix(~ Health_Condition - 1, data = .))
  )

Penjelasan Fungsi

Fungsi Tujuan
weekdays(Date) Menentukan nama hari berdasarkan tanggal (misalnya Senin, Selasa, dll.).
month(Date, label = TRUE) Mengambil bulan dari tanggal dan memberi label (contoh: Jan, Feb, dst.).
year(Date) Mengambil tahun dari tanggal untuk analisis musiman atau tahunan.
ifelse(Day_of_Week %in% c(“Saturday”, “Sunday”), 1, 0) Menentukan apakah tanggal tersebut jatuh pada akhir pekan (Sabtu/Minggu).
as.factor(Location) Mengubah lokasi menjadi variabel kategori.
as.factor(Health_Condition) Mengubah kondisi kesehatan menjadi variabel kategori.
model.matrix(~ Location - 1) Menggunakan one-hot encoding untuk mengonversi lokasi menjadi kolom-kolom biner.
model.matrix(~ Health_Condition - 1) Menggunakan one-hot encoding untuk mengonversi kondisi kesehatan menjadi kolom-kolom biner.

Interpretasi Kesehatan:

Fitur Transformasi Arti & Manfaat
Day_of_Week Menyediakan informasi hari dalam minggu. Berguna untuk mendeteksi pola harian, misalnya apakah ada lebih banyak kunjungan rumah sakit di akhir pekan.
Month Menyediakan informasi tentang bulan. Dapat digunakan untuk mendeteksi pola musiman dalam data, seperti peningkatan kasus penyakit tertentu di bulan tertentu.
Year Memberikan informasi tahunan. Berguna untuk analisis jangka panjang dan tren tahunan, seperti peningkatan atau penurunan kesehatan masyarakat dari tahun ke tahun.
Is_Weekend Menunjukkan apakah data berasal dari akhir pekan atau tidak. Bisa menjadi fitur untuk melihat perilaku atau pola yang terjadi pada hari-hari tertentu.
Location (One-hot) Mengonversi lokasi menjadi variabel biner. Dapat digunakan untuk menganalisis apakah lokasi tertentu berhubungan dengan kondisi kesehatan tertentu.
Health_Condition (One-hot) Mengonversi kondisi kesehatan menjadi variabel biner. Membantu dalam analisis terkait dengan jenis kondisi kesehatan dan pengaruhnya terhadap variabel lainnya.

Hasil Transformasi

Dari hasil transformasi data yang melibatkan ekstraksi informasi waktu dan encoding untuk lokasi serta kondisi kesehatan pasien, berikut adalah contoh tampilan data yang diperoleh:

Kesimpulan

  • Ekstraksi Waktu: Data berhasil diubah dengan mengekstraksi elemen-elemen waktu seperti Hari dalam Minggu, Bulan, dan Tahun, yang memungkinkan analisis lebih lanjut terkait dengan pola musiman dan perilaku berdasarkan waktu.
    • Is_Weekend dihasilkan dengan memberikan nilai 1 jika hari tersebut adalah akhir pekan (Sabtu atau Minggu) dan 0 untuk hari lainnya, memungkinkan identifikasi tren berdasarkan akhir pekan atau hari kerja.
  • Location & Health Condition Encoding: Lokasi dan kondisi kesehatan pasien telah diubah menjadi *variabel biner untuk memudahkan analisis statistik dan pemodelan prediktif.
    • Variabel lokasi seperti Location_Jakarta, Location_Bandung, dan Location_Surabaya diubah menjadi indikator biner untuk setiap lokasi.
    • Kondisi kesehatan pasien, seperti Health_Condition_Healthy, Health_Condition_Hypertension, dan Health_Condition_Diabetes, juga dipetakan dalam bentuk variabel biner yang menggambarkan kondisi medis setiap individu.

1.3 Cumulative Sum/Mean/Count

Teknik kumulatif menghitung total berjalan (running total), jumlah kemunculan, atau rata-rata kumulatif dari data berdasarkan waktu.

Kelebihan:

  • Sangat berguna untuk melihat pertumbuhan akumulatif.
  • Membantu dalam memodelkan perilaku jangka panjang.

Kekurangan:

  • Hasil kumulatif bisa mengaburkan perubahan lokal kecil.

  • Bisa mempengaruhi distribusi data menjadi lebih smooth (terlalu halus).

Contoh Penggunaan dalam R:


Temporal3 <- health_data %>%
  group_by(Patient_ID) %>%
  mutate(
    Cumulative_BP = cumsum(Blood_Pressure),
    Cumulative_Glucose = cumsum(Glucose),
    Cumulative_AvgHR = cummean(Heart_Rate)
  ) %>%
  ungroup()

Penjelasan Fungsi

Fungsi Tujuan
cumsum(Blood_Pressure) Menghitung jumlah kumulatif tekanan darah sepanjang waktu.
cumsum(Glucose) Menghitung jumlah kumulatif kadar glukosa sepanjang waktu.
cummean(Heart_Rate) Menghitung rata-rata kumulatif detak jantung sepanjang waktu.

Interpretasi Kesehatan:

Fitur Transformasi Arti & Manfaat
Cumulative_BP Menyediakan total kumulatif dari tekanan darah pasien sepanjang waktu. Ini bisa memberikan gambaran mengenai peningkatan atau penurunan kondisi kesehatan secara keseluruhan, misalnya dalam kasus hipertensi.
Cumulative_Glucose Menyediakan total kumulatif kadar glukosa pasien. Ini dapat membantu memantau fluktuasi kadar glukosa sepanjang waktu dan memberikan gambaran tentang kestabilan kondisi pasien dengan diabetes.
Cumulative_AvgHR Menyediakan rata-rata kumulatif detak jantung pasien. Ini dapat membantu melihat tren detak jantung pasien dari waktu ke waktu, apakah terjadi peningkatan atau penurunan yang signifikan.

Hasil Transformasi

Kesimpulan:

  • Cumulative Sum (cumsum) digunakan untuk mengukur total kumulatif dari variabel tertentu (seperti tekanan darah atau kadar glukosa) dari waktu ke waktu. Teknik ini sangat berguna untuk memahami bagaimana suatu kondisi berkembang dalam jangka panjang.

  • Cumulative Mean (cummean) digunakan untuk melihat tren rata-rata jangka panjang, seperti detak jantung. Ini dapat membantu dalam memantau kestabilan kondisi kesehatan pasien.

  • Kelemahan teknik kumulatif: Hasil kumulatif bisa mempengaruhi distribusi data, membuat data terlihat lebih smooth dan dapat mengaburkan perubahan lokal kecil yang mungkin signifikan. Oleh karena itu, harus digunakan dengan hati-hati tergantung pada tujuan analisis.


2.Transformasi Distribusi

2.1 Log Transform


min_positive <- min(health_data$Glucose[health_data$Glucose > 0])

Log <- health_data %>%
  mutate(
    Safe_Glucose = ifelse(Glucose <= 0, min_positive, Glucose),
    Log_Glucose = log1p(Safe_Glucose)
  )

Penjelasan Fungsi

Fungsi Tujuan
min(health_data\(Glucose[health_data\)Glucose > 0]) Mencari nilai glukosa positif terkecil, untuk menggantikan nilai nol atau negatif.
ifelse(Glucose <= 0, min_positive, Glucose) Mengganti nilai glukosa nol atau negatif dengan nilai glukosa positif terkecil agar aman untuk transformasi logaritma.
log1p(Safe_Glucose) Menghitung logaritma natural dari (glukosa + 1), agar distribusi data lebih normal dan menghindari log(0).

Interpretasi Kesehatan

Fitur Transformasi Arti & Manfaat
Safe_Glucose Menjamin semua nilai glukosa bernilai positif sebelum transformasi logaritma. Ini penting karena log(0) tidak terdefinisi, dan nilai nol bisa muncul akibat kesalahan pencatatan atau alat medis.
Log_Glucose Transformasi logaritma membuat distribusi glukosa yang awalnya miring (skewed) menjadi lebih normal. Ini membantu meningkatkan performa model prediktif dan analisis statistik.

Hasil Transfortasi

Kesimpulan

  • Transformasi log1p() (log(x + 1)) berguna untuk menangani data skewed dan nilai nol, terutama dalam data kesehatan seperti glukosa.
  • Mengganti nilai 0 atau negatif dengan nilai minimum positif adalah strategi robust untuk menghindari error matematis.
  • Teknik ini sering digunakan sebelum pemodelan statistik atau machine learning, agar hasil analisis menjadi lebih akurat dan stabil.

2.2 Box-Cox



Box_Cox <- health_data %>%
  mutate(
    YeoJ_BP = bestNormalize(Blood_Pressure)$x.t,
    YeoJ_Chol = bestNormalize(Cholesterol)$x.t
  )
  

Penjelasan Fungsi

Fungsi Tujuan
bestNormalize(Blood_Pressure)\(x.t | Menerapkan transformasi normalisasi terbaik (seperti Yeo-Johnson) ke variabel tekanan darah untuk membuat distribusinya lebih mendekati normal. | | bestNormalize(Cholesterol)\)x.t Menerapkan transformasi normalisasi terbaik ke variabel kolesterol agar distribusinya lebih simetris dan cocok untuk analisis statistik atau machine learning.

Interpretasi Kesehatan

Fitur Transformasi Arti & Manfaat
YeoJ_BP Menghasilkan versi transformasi dari Blood_Pressure yang sudah dinormalisasi menggunakan metode Yeo-Johnson atau metode terbaik lainnya. Ini penting untuk analisis statistik yang mengasumsikan distribusi normal, seperti regresi linier.
YeoJ_Chol Menghasilkan versi transformasi dari Cholesterol yang telah dinormalisasi. Ini berguna untuk mengurangi pengaruh outlier dan membuat data lebih cocok untuk pemodelan.

Hasil Transformasi

Kesimpulan

  • Fungsi bestNormalize() secara otomatis memilih transformasi terbaik (misalnya Yeo-Johnson, Box-Cox, log, atau lainnya) untuk membuat distribusi data lebih mendekati normal.
  • Transformasi ini sangat bermanfaat ketika variabel input memiliki distribusi skewed atau mengandung outlier yang dapat mempengaruhi hasil analisis.
  • Penggunaan normalisasi seperti ini sering diterapkan sebelum regresi, klasifikasi, atau clustering agar model lebih akurat dan stabil.

2.3 Variance Stabilization


Variance_Stab <- health_data %>%
  mutate(
    Sqrt_Cholesterol = sqrt(Cholesterol),
    Sqrt_BMI = sqrt(BMI)
  )

Penjelasan Fungsi

Fungsi Tujuan
sqrt(Cholesterol) Mentransformasi kolesterol dengan akar kuadrat untuk menstabilkan variansi.
sqrt(BMI) Mentransformasi indeks massa tubuh (BMI) dengan akar kuadrat agar distribusinya lebih normal dan mengurangi efek outlier.

Interpretasi Kesehatan

Fitur Transformasi Arti & Manfaat
Sqrt_Cholesterol Mengurangi sebaran ekstrem pada nilai kolesterol tinggi. Ini membantu dalam analisis regresi atau machine learning agar model tidak terlalu dipengaruhi oleh outlier.
Sqrt_BMI Membantu menstabilkan varians BMI dan mendekatkan distribusi ke bentuk normal. Ini penting jika data akan digunakan dalam model statistik yang mengasumsikan distribusi normal.

Hasil Interpretasi

Kesimpulan

  • Transformasi akar kuadrat (sqrt) adalah metode sederhana dan efektif untuk menstabilkan variansi pada data yang positif skewed (condong ke kanan).
  • Ini penting dalam analisis data medis karena banyak nilai biometrik (seperti kolesterol dan BMI) tidak terdistribusi normal dan bisa memiliki outlier.
  • Teknik ini dapat meningkatkan performa dan interpretabilitas model statistik yang sensitif terhadap bentuk distribusi data.

3. Scaling & Normalization

3.1 Standardization (Z-score)

Proses mengubah variabel numerik yang memiliki skala berbeda menjadi skala yang sama dengan rata-rata 0 dan deviasi standar 1.


# Z-score Normalization
Z_score <- health_data %>%
  mutate(
    Age_Z = scale(age),
    BMI_Z = scale(bmi),
    Blood_Pressure_Z = scale(blood_pressure),
    Cholesterol_Z = scale(cholesterol),
    Glucose_Z = scale(glucose)
  )

Penjelasan Kode

Fungsi Tujuan
scale(Age) Menstandarkan usia agar berada dalam skala satuan.
scale(BMI) Menstandarkan nilai indeks massa tubuh (IMT).
scale(Blood_Pressure) Menstandarkan tekanan darah agar tidak dominan karena skalanya besar.
scale(Cholesterol) Menstandarkan kolesterol agar setara dengan fitur lain.
scale(Glucose) Menstandarkan glukosa agar adil dalam model prediksi.
scale(Heart_Rate) Menstandarkan detak jantung agar memiliki skala yang sama.

Interpretasi Kesehatan

Fitur Transformasi Arti & Manfaat
Age_std Membantu melihat pengaruh usia tanpa dipengaruhi oleh rentang besar pada data usia.
BMI_Std Membantu mendeteksi pasien dengan risiko obesitas yang memengaruhi tekanan darah.
bloodpreasure_Std Membuat tekanan darah menjadi fitur yang sebanding untuk prediksi atau klasifikasi.
kolestrol_std Menyetarakan pengaruh kolesterol dalam model prediksi hipertensi.
Glukosa_std Memungkinkan pengaruh glukosa diamati secara proporsional tanpa bias skala.
Heartrate_std Mempermudah analisis tren detak jantung terhadap kondisi lain secara setara.

Hasil Transformasi

Kesimpulan

Variabel-variabel seperti Age, BMI, Blood Pressure, Cholesterol, Glucose, dan Heart Rate yang sebelumnya memiliki rentang skala yang sangat berbeda sekarang ada di skala yang sama. Penskalaan ini bisa membantu model machine learning untuk tidak cenderung menganggap satu fitur lebih penting daripada yang lain hanya karena memiliki rentang yang lebih besar.

Dalam mendeteksi pasien yang memiliki faktor risiko dalam mempengaruhi tekanan darah yang akan mengakibatkan hipertensi bisa diliat dari nilai BMI, kolestrol dan glukosa. ketiga fitur itu memiliki rentang skala yang berbeda beda ada yang di puluhan ataupun ratusan. Model mechine learning akan memberi bobot pada fitur kolestrol karena memiliki rentang yang besar. Setelah di skala ketiga fitur itu ada di skala satuan. Disini model bisa menilai dari ketiga fitur tersebut apakah bisa mempengaruhi tekanan darah tinggi atau tidak yang akan mengakibatkan hipertensi.

Kekurangan Skala ini masih terpengaruh terhadap outlier dibandingkan dengan Robust Scaler.

3.2 Robust Scaler

Proses merubah variabel numerik yang memiliki skala berbeda menjadi skala yang sama dalam dataset dan tahan terhadap outlier. Namun, masih terpengaruhi oleh nilai yang sangat ekstrim. Penskalaan ini diperkuat dengan median dan IQR dari tiap fitur.


# RobustScaler

Robust <- health_data %>%
  mutate(
    Age_robust = (age - median(age)) / IQR(age),
    BMI_robust = (bmi - median(bmi)) / IQR(bmi),
    BloodPressure_robust = (blood_pressure - median(blood_pressure)) / IQR(blood_pressure),
    Cholesterol_robust = (cholesterol - median(cholesterol)) / IQR(cholesterol),
    Glucose_robust = (glucose - median(glucose)) / IQR(glucose),
    HeartRate_robust = (heart_rate - median(heart_rate)) / IQR(heart_rate)
  )

Penjelasan Kode

Fungsi Transformasi Tujuan
(age - median(age)) / IQR(age) Menstandarkan usia berdasarkan median dan IQR agar tidak dipengaruhi outlier.
(bmi - median(bmi)) / IQR(bmi) Menyesuaikan nilai BMI ke skala yang lebih netral terhadap nilai ekstrem.
(blood_pressure - median(…)) / IQR(…) Mengubah tekanan darah ke skala yang stabil dan tahan terhadap lonjakan nilai.
(cholesterol - median(…)) / IQR(…) Mengatur kolesterol agar setara skalanya dengan fitur lain meskipun outlier ada.
(glucose - median(…)) / IQR(…) Menormalkan kadar gula darah agar tidak didominasi oleh nilai ekstrem.
(heart_rate - median(…)) / IQR(…) Menstabilkan nilai detak jantung untuk analisis komparatif antar pasien.

Interpretasi Kesehatan

Fitur Transformasi Arti & Manfaat
Age_robust Usia pasien kini distandarisasi tanpa dipengaruhi ekstrem umur yang terlalu muda atau tua. Berguna untuk model prediktif yang sensitif terhadap variasi usia.
BMI_robust Menilai risiko kelebihan berat badan terhadap hipertensi secara adil dan netral.
BloodPressure_robust Memberi gambaran tekanan darah yang adil saat dibandingkan antar pasien dengan data bervariasi.
Cholesterol_robust Menghindari bias model terhadap pasien dengan kadar kolesterol sangat tinggi.
Glucose_robust Memastikan model melihat pola kadar glukosa tanpa tertipu nilai yang terlalu ekstrem.
HeartRate_robust Menjadikan detak jantung setara secara skala dengan fitur lain untuk analisis prediksi kesehatan jantung.

Hasil Transformasi

Kesimpulan

Sama hal nya dengan Skala Standarisasi, Robust scaler memiliki skala yang lebih kecil dibanding dengan Skala (standarisasi) karena median dan IQR tidak dipengaruhi oleh nilai ekstrem yang jauh dari distribusi data lainnya. Ini menjadikannya lebih stabil saat data memiliki outlier.

Ini membuat model mampu mengevaluasi pengaruh BMI, kolesterol, dan glukosa secara adil terhadap tekanan darah, baik sistolik maupun diastolik. Akhirnya, ini mendukung proses deteksi risiko hipertensi dengan pendekatan statistik yang lebih akurat dan tidak bias.

3.3 Normalization

Proses merubah skala dalam rentang tertentu umumnya 0-1 dengan menggunakan nilai minimum dan maksimum yang menghasilkan keseimbangan data. Normalisasi ini akan sangat terpengaruh oleh nilai ekstrem.


# Min-Max Normalization
Min_Max <- health_data %>%
  mutate(
    Age_Norm = (Age - min(Age, na.rm = TRUE)) / (max(Age, na.rm = TRUE) - min(Age, na.rm = TRUE)),
    BMI_Norm = (BMI - min(BMI, na.rm = TRUE)) / (max(BMI, na.rm = TRUE) - min(BMI, na.rm = TRUE)),
    Blood_Pressure_Norm = (Blood_Pressure - min(Blood_Pressure, na.rm = TRUE)) / (max(Blood_Pressure, na.rm = TRUE) - min(Blood_Pressure, na.rm = TRUE)),
    Cholesterol_Norm = (Cholesterol - min(Cholesterol, na.rm = TRUE)) / (max(Cholesterol, na.rm = TRUE) - min(Cholesterol, na.rm = TRUE)),
    Glucose_Norm = (Glucose - min(Glucose, na.rm = TRUE)) / (max(Glucose, na.rm = TRUE) - min(Glucose, na.rm = TRUE))
  )

Penjelasan Kode

Fungsi Tujuan
(x - min(x)) / (max(x) - min(x)) Mengubah skala data agar berada dalam rentang 0 hingga 1, tanpa mengubah bentuk distribusi aslinya. Berguna untuk algoritma machine learning yang sensitif terhadap skala, seperti KNN dan SVM.
na.rm = TRUE Mengabaikan nilai missing agar proses normalisasi tetap berjalan tanpa error.

Interpretasi Kesehatan

Fitur Ternormalisasi Arti & Manfaat
Age_Norm Mengonversi umur pasien ke skala 0–1 untuk memastikan model tidak bias terhadap rentang usia yang luas.
BMI_Norm Memastikan nilai indeks massa tubuh dibandingkan secara adil dengan fitur lain.
Blood_Pressure_Norm Menstandarkan tekanan darah agar bisa dibandingkan langsung dengan variabel lain dalam model prediktif.
Cholesterol_Norm Membuat kadar kolesterol dapat diinterpretasikan dalam skala seragam.
Glucose_Norm Membantu dalam menyeimbangkan fitur glukosa dengan fitur lainnya tanpa memperbesar bobotnya.

Hasil Transformasi

Kesimpulan

Sama hal nya dengan Scaling, normalisasi ini untuk merubah rentang agar model bisa menganggap data di setiap fitur nya memiliki bobot yang sama. Hanya saja jika normalisasi dalam rentang diantara 0 sampai 1. Sedangkan, skala masih ada nilai yang rentang diatas nilai 1.

Kelebihan dalam normalisasi yaitu menjaga data dalam rentang yang seragam tanpa dipengaruhi outlier secara ekstrem. dan Kelemahannya yaitu sangat sensitif terhadap outlier, karena min() dan max() bisa terpengaruh nilai ekstrem.

4. Kategorial Encoding

4.1 One-Hot Encoding

One-hot encoding adalah metode transformasi variabel kategorik menjadi bentuk numerik biner (0/1) agar bisa digunakan dalam analisis statistik atau machine learning.

Kelebihan:

  • Menghilangkan makna ordinal dari kategori (tidak mengasumsikan urutan).
  • Cocok untuk algoritma yang tidak bisa menangani data kategorik secara langsung.

Kekurangan:

  • Menambah jumlah fitur secara signifikan (terutama jika kategori banyak).
  • Bisa menyebabkan curse of dimensionality.

Contoh Penggunaan dalam R:


categorical_cols <- names(health_data)[sapply(health_data, function(x) is.factor(x) || is.character(x))]
one_hot <- dummy_cols(
  health_data,
  select_columns = categorical_cols,
  remove_first_dummy = TRUE,
  remove_selected_columns = TRUE
)

Penjelasan Fungsi

Fungsi Tujuan
dummy_cols() Membuat kolom dummy dari data kategorik.
remove_first_dummy = TRUE Menghindari dummy variable trap (multikolinearitas) dengan menghapus satu kategori.
remove_selected_columns = TRUE Menghapus kolom kategorik asli dari dataset.

Interpretasi Kesehatan:

Fitur One-Hot Makna
Location_Bandung = 1 Pasien berasal dari Bandung.
Health_Condition_Diabetes = 1 Pasien memiliki kondisi diabetes.

Hasil Transformasi

Kesimpulan:

  • Transformasi ini mempermudah penggunaan data kategorik dalam model prediksi dan analisis statistik.

  • Menghindari kesalahan interpretasi urutan pada variabel kategorik.

  • Cocok digunakan bersama metode statistik dan machine learning modern.

4.2 Frequency Encoding

Frequency encoding adalah metode pengkodean data kategorik dengan mengganti setiap kategori dengan frekuensinya dalam data. Artinya, nilai kategori yang lebih sering muncul akan mendapatkan nilai numerik yang lebih tinggi.

Kelebihan:

  • Tidak menambah dimensi seperti one-hot encoding.

  • Menyimpan informasi distribusi kategori.

  • Efisien untuk dataset dengan banyak kategori unik.

Kekurangan:

  • Tidak cocok jika kategori dengan frekuensi yang sama memiliki makna berbeda.

  • Bisa memunculkan data leakage jika tidak dilakukan dengan hati-hati (misalnya saat digunakan sebelum data split).

Contoh Penggunaan dalam R:


# Fungsi frequency encoding
freq_enc <- function(col) {
  tab <- table(col)
  return(as.numeric(tab[col]) / length(col))
}

# Pastikan kolom lowercase
colnames(health_data) <- tolower(colnames(health_data))

# Kolom yang ingin di-encode
target_cols <- c("year", "location", "health_condition")
available_cols <- intersect(target_cols, colnames(health_data))

# Encoding frekuensi
Frequency <- health_data %>%
  mutate(across(all_of(available_cols), ~ freq_enc(.), .names = "{.col}_freq"))

Penjelasan Fungsi

Fungsi Tujuan
table(col) Menghitung jumlah kemunculan tiap kategori.
as.numeric(tab[col]) / length(col) Mengubah nilai kategori menjadi proporsi kemunculannya.
across(…, .names = “{.col}_freq”) Menambahkan nama kolom hasil encoding.

Interpretasi Kesehatan:

Fitur Encoded Makna
location_freq = 0.30 Lokasi tersebut muncul sebanyak 30% dari total data.
health_condition_freq = 0.10 Kondisi kesehatan tersebut hanya muncul di 10% data.

Hasil Transformasi

Kesimpulan:

  • Frequency encoding memberikan bobot proporsional terhadap kemunculan kategori.

  • Lebih ringan secara komputasi dibanding one-hot encoding, namun tetap menyimpan makna statistik dari data kategorik.

  • Cocok digunakan dalam model linear dan tree-based seperti Random Forest dan XGBoost.

5. Feature Engineering

Feature engineering adalah proses menciptakan fitur-fitur baru dari data yang ada untuk meningkatkan performa model machine learning. Salah satu teknik yang sering digunakan adalah interaction features, yaitu menggabungkan dua atau lebih fitur untuk membentuk fitur baru yang bisa menangkap hubungan nonlinier antar variabel.

5.1 Interaction Features

Interaction features dibuat dengan mengalikan atau menggabungkan dua fitur untuk merepresentasikan hubungan antara keduanya. Dalam kasus ini, dibuat fitur interaksi antara umur (age) dan BMI (Body Mass Index) untuk melihat dampak gabungan usia dan berat badan terhadap kesehatan.

Kegunaan:

  • Membantu model menangkap efek gabungan antar

  • Berguna dalam model linear yang tidak secara eksplisit menangkap interaksi antar fitur.

Contoh Kode R:


# Simpan nama kolom BMI ke variabel
bmi_col <- grep("bmi|imt", colnames(health_data), value = TRUE)[1]

# Buat interaksi antara age dan BMI
data_interaction <- health_data %>%
  mutate(
    Age_BMI_Impact = age * .data[[bmi_col]]
  )

Penjelasan Fungsi

Langkah Penjelasan
grep(“bmi imt”, …)
age * .data[[bmi_col]] Mengalikan umur dan BMI untuk membuat fitur baru Age_BMI_Impact.
mutate() Menambahkan fitur baru ke dalam data.

Interpretasi Kesehatan:

  • Fitur Age_BMI_Impact akan bernilai tinggi jika seseorang memiliki usia dan BMI yang sama-sama tinggi.

  • Dapat menunjukkan risiko kesehatan gabungan akibat usia lanjut dan berat badan berlebih.

Hasil Transformasi

Kesimpulan:

  • Fitur interaksi berguna untuk mengungkap relasi tersembunyi antar variabel.
  • Dalam kasus data kesehatan, interaksi antara usia dan BMI bisa menjadi indikator penting terhadap risiko penyakit kronis.

5.2 Ratio Features

Ratio features adalah fitur yang dibentuk dari perbandingan dua variabel numerik. Teknik ini sering digunakan untuk menormalkan data, menyoroti ketidakseimbangan, atau menangkap hubungan proporsional antar variabel.

Contoh Kasus:

Dalam data kesehatan, membandingkan kadar kolesterol terhadap kadar glukosa dapat memberikan insight tambahan tentang profil metabolik seseorang. Maka dibuat fitur baru: Cholesterol to Glucose Ratio.

Contoh Kode R:


data_ratio <- health_data %>%
  mutate(
    Cholesterol_Glucose_Ratio = cholesterol / (glucose + 1e-5)
  )

Penjelasan Fungsi

Langkah Penjelasan
cholesterol / (glucose + 1e-5) Rasio antara kolesterol dan glukosa; penambahan 1e-5 mencegah pembagian dengan nol.
mutate() Menambahkan fitur rasio baru ke dalam data.

Interpretasi Kesehatan:

  • Rasio yang tinggi bisa menunjukkan potensi risiko gangguan metabolik.

  • Berguna untuk menyaring individu dengan kolesterol tinggi tetapi kadar glukosa normal atau sebaliknya.

Hasil Transformasi

Kesimpulan:

  • Ratio features memperkaya dataset dengan informasi proporsional.
  • Dalam konteks medis, rasio ini membantu dalam mendeteksi ketidakseimbangan biokimia yang tidak terlihat dari nilai absolut saja.

5.3 Group Aggregation

Group aggregation adalah teknik untuk menghitung ringkasan statistik berdasarkan kelompok tertentu dalam data, misalnya berdasarkan ID pasien, lokasi, atau waktu. Teknik ini berguna untuk menciptakan fitur baru yang merangkum informasi historis atau berulang.

Contoh Kasus:

Pada dataset kesehatan, satu pasien bisa memiliki beberapa entri kunjungan. Maka kita bisa menghitung rata-rata dan maksimum kadar glukosa untuk tiap patient_id, serta jumlah kunjungan sebagai fitur tambahan.

Contoh Kode R:


# Aggregate by patient
patient_glucose <- health_data %>%
  group_by(patient_id) %>%
  summarise(
    Avg_Glucose = mean(glucose, na.rm = TRUE),
    Max_Glucose = max(glucose, na.rm = TRUE),
    Visits = n(),
    .groups = "drop"
  )

# Join with original data
health_data_joined <- left_join(health_data, patient_glucose, by = "patient_id")
Langkah Penjelasan
group_by(patient_id) Mengelompokkan data berdasarkan ID pasien.
summarise() Menghitung nilai rata-rata, maksimum, dan jumlah kunjungan.
left_join() Menggabungkan hasil agregasi kembali ke data utama.

Manfaat dalam Konteks Medis:

  • Avg_Glucose dan Max_Glucose mencerminkan kontrol gula darah pasien secara longitudinal.

  • Visits dapat menunjukkan frekuensi kontrol atau keparahan kondisi pasien.

Hasil Transformasi

Kesimpulan:

  • Group aggregation menghasilkan fitur yang memperkaya data dengan informasi ringkasan per entitas.
  • Cocok untuk data yang bersifat longitudinal, seperti rekam medis atau transaksi pelanggan.

5.4 Rank Transformation

Rank transformation adalah teknik untuk mengubah nilai numerik menjadi urutan berdasarkan posisi relatifnya dalam dataset. Ini berguna untuk mengidentifikasi posisi atau peringkat suatu entitas berdasarkan variabel tertentu tanpa memerlukan distribusi data yang spesifik.

Contoh Kasus:

Dalam dataset kesehatan, kita dapat mengurutkan kadar glukosa pasien dan memberi peringkat kepada mereka berdasarkan level glukosa, dengan pasien yang memiliki kadar glukosa tertinggi mendapat peringkat teratas.

Contoh Kode R:


data_ranked <- health_data %>%
  mutate(
    Glucose_Rank = rank(-glucose)
  )

Penjelasan Fungsi

Langkah Penjelasan
rank(-glucose) Menghitung peringkat berdasarkan kadar glukosa, dengan tanda minus - untuk memberikan peringkat tertinggi pada nilai terbesar.

Manfaat dalam Konteks Medis:

  • Glucose_Rank memberikan gambaran tentang posisi relatif pasien berdasarkan kadar glukosa mereka. Ini berguna untuk memahami siapa yang berada dalam kelompok dengan kadar glukosa tertinggi atau terendah.

Hasil Transformasi

Kesimpulan:

Rank transformation cocok untuk analisis yang mengutamakan urutan daripada nilai absolut, seperti identifikasi pasien dengan kondisi paling kritis berdasarkan peringkat.

5.5 Text Cleaning & Feature Creation

Text cleaning dan pembuatan fitur dari teks adalah langkah penting dalam proses pembersihan data, khususnya ketika kita bekerja dengan data yang mengandung informasi dalam format teks yang tidak terstruktur. Salah satu langkah penting adalah mengekstraksi informasi relevan dari teks, seperti angka atau kata-kata kunci, untuk digunakan dalam analisis lebih lanjut.

Contoh Kasus:

Dalam dataset kesehatan, kolom patient_id mungkin mengandung angka dan karakter lain, yang mana kita bisa mengekstraksi angka saja untuk membuat fitur baru.

Contoh Kode R:


data_text <- health_data %>%
  mutate(
    Patient_Num = as.numeric(gsub("[^0-9]", "", patient_id))
  )

Penjelasan Fungsi

Langkah Penjelasan
gsub(“[^0-9]”, ““, patient_id) Menggunakan fungsi gsub untuk menghapus karakter non-angka dan mengekstraksi hanya angka dari kolom patient_id.
as.numeric() Mengonversi hasil ekstraksi menjadi format numerik.

Manfaat dalam Konteks Medis:

  • Patient_Num memberikan nomor pasien dalam format numerik yang bisa digunakan untuk analisis lebih lanjut, seperti pengelompokan atau identifikasi pasien berdasarkan ID numerik.

Hasil Transformasi

Kesimpulan:

Langkah pembersihan teks ini membantu mengubah ID pasien yang mengandung karakter menjadi format yang lebih mudah untuk dianalisis, terutama saat ID pasien diperlukan dalam analisis statistik atau pembelajaran mesin.

5.6 Cumulative Features

Cumulative features mengacu pada fitur yang dihitung secara berurutan berdasarkan data yang terurut, seperti akumulasi nilai dari variabel tertentu sepanjang waktu. Cumulative features sering digunakan dalam analisis time series atau data yang bersifat kronologis, seperti penghitungan jumlah kumulatif dari suatu parameter kesehatan.

Contoh Kasus:

Dalam dataset kesehatan, kita dapat menghitung jumlah kumulatif kadar glukosa dari waktu ke waktu untuk setiap pasien, yang memberikan gambaran tentang perubahan tingkat glukosa sepanjang waktu.

Contoh Kode R:


data_cumulative <- health_data %>%
  arrange(patient_id, date) %>%
  group_by(patient_id) %>%
  mutate(
    Cumulative_Glucose = cumsum(glucose)
  ) %>%
  ungroup()

Penjelasan Fungsi

Langkah Penjelasan
arrange(patient_id, date) Mengurutkan data berdasarkan ID pasien dan tanggal untuk memastikan bahwa penghitungan kumulatif dilakukan berdasarkan urutan waktu.
group_by(patient_id) Mengelompokkan data berdasarkan ID pasien agar perhitungan kumulatif dilakukan per pasien.
cumsum(glucose) Menghitung jumlah kumulatif kadar glukosa untuk setiap pasien dari waktu ke waktu.

Manfaat dalam Konteks Medis:

  • Cumulative_Glucose memberikan informasi mengenai akumulasi kadar glukosa pasien sepanjang waktu, yang bisa digunakan untuk memahami perubahan pola glukosa, mengidentifikasi tren, dan mendeteksi kemungkinan masalah kesehatan.

Hasil Transformasi

Kesimpulan:

Penghitungan fitur kumulatif seperti Cumulative_Glucose memungkinkan pemahaman yang lebih dalam mengenai perjalanan kesehatan pasien dari waktu ke waktu dan dapat membantu dalam pemodelan prediktif, seperti deteksi risiko diabetes atau komplikasi terkait glukosa.

6. Outlier Handling

Dalam analisis data, outlier atau data yang menyimpang jauh dari pola umum dapat mempengaruhi hasil analisis dan model. Oleh karena itu, penting untuk mendeteksi dan menangani outlier dengan tepat. Beberapa metode yang umum digunakan untuk mendeteksi outlier antara lain Z-score dan Interquartile Range (IQR).

Contoh Kasus:

Pada dataset kesehatan ini, kita akan menggunakan dua metode untuk mendeteksi dan menangani outlier:

  1. Z-score untuk mendeteksi outlier pada kadar glukosa.

  2. IQR untuk mendeteksi outlier pada indeks massa tubuh (BMI).

Contoh Kode R:


# Ensure all column names are lowercase
colnames(health_data) <- tolower(colnames(health_data))

# Get the exact name of glucose and BMI columns
glucose_col <- grep("glukosa|glucose", colnames(health_data), value = TRUE)[1]
bmi_col <- grep("imt|bmi", colnames(health_data), value = TRUE)[1]

# Z-score method for outlier detection (using Glucose)
z_scores <- scale(health_data[[glucose_col]])

data_outliers <- health_data %>%
  mutate(
    Outlier_Flag = ifelse(abs(z_scores) > 3, "Outlier", "Normal")
  )

# IQR method for outlier removal (using BMI)
Q1 <- quantile(health_data[[bmi_col]], 0.25, na.rm = TRUE)
Q3 <- quantile(health_data[[bmi_col]], 0.75, na.rm = TRUE)
IQR_val <- Q3 - Q1

data_outliers_IQR <- health_data %>%
  filter(
    .data[[bmi_col]] > (Q1 - 1.5 * IQR_val) &
    .data[[bmi_col]] < (Q3 + 1.5 * IQR_val)
  )

Metode Z-Score untuk Deteksi Outlier:

Z-score digunakan untuk mendeteksi seberapa jauh suatu nilai dari rata-rata dalam satuan deviasi standar. Dalam kasus ini, kita menghitung Z-score untuk kadar glukosa, dan jika Z-score lebih besar dari 3 atau lebih kecil dari -3, nilai tersebut dianggap sebagai outlier.

Langkah Penjelasan
scale(health_data[[glucose_col]]) Menghitung Z-score untuk kadar glukosa.
ifelse(abs(z_scores) > 3, “Outlier”, “Normal”) Menandai nilai yang memiliki Z-score lebih dari 3 atau kurang dari -3 sebagai outlier.

Metode IQR untuk Penghapusan Outlier:

Metode IQR menghitung rentang interkuartil (IQR) yang merupakan selisih antara kuartil ketiga (Q3) dan kuartil pertama (Q1). Nilai yang berada di luar rentang [Q1 - 1.5 IQR, Q3 + 1.5 IQR] dianggap sebagai outlier.

Langkah Penjelasan
quantile(health_data[[bmi_col]], 0.25) Menghitung kuartil pertama (Q1) dari data BMI.
quantile(health_data[[bmi_col]], 0.75) Menghitung kuartil ketiga (Q3) dari data BMI.
filter(.data[[bmi_col]] > (Q1 - 1.5 * IQR_val) & .data[[bmi_col]] < (Q3 + 1.5 * IQR_val)) Menyaring data BMI untuk menghapus nilai-nilai outlier di luar rentang IQR.

Manfaat Mengelola Outlier:

  • Deteksi Dini: Mengidentifikasi outlier dengan metode yang tepat memungkinkan pemahaman yang lebih baik tentang data dan membantu mendeteksi masalah dalam pengukuran atau kondisi yang tidak biasa.
  • Modeling: Menghapus atau menangani outlier dapat meningkatkan kinerja model prediktif dengan menghindari distorsi yang dapat disebabkan oleh data yang ekstrem.

Hasil Transformasi

Kesimpulan:

  • Dengan menggunakan metode Z-score dan IQR, kita dapat mendeteksi dan menangani outlier pada data kesehatan untuk memastikan bahwa analisis dan model yang dibangun lebih akurat dan representatif terhadap data asli.

7. Discretization

Discretization adalah proses mengubah variabel kontinu menjadi kategori atau interval. Hal ini sering dilakukan untuk meningkatkan pemahaman atau interpretasi data, serta memudahkan penerapan model klasifikasi. Salah satu contoh yang umum adalah mengubah usia menjadi kategori seperti “Muda”, “Paruh Baya”, dan “Tua”.

Contoh Kasus:

Pada dataset kesehatan ini, kita akan mendiskretisasikan kolom usia menjadi tiga kategori: 1. Young (Muda) 2. Middle-aged (Paruh Baya) 3. Old (Tua)

Kategori ini akan didasarkan pada pembagian kuantil data usia.

Contoh Kode R:



# Make sure column names are lowercase
colnames(health_data) <- tolower(colnames(health_data))

# Convert 'age' to numeric (if not already)
health_data <- health_data %>%
  mutate(usia = as.numeric(age))

# Binning 'age' into age categories
binning <- health_data %>%
  mutate(
    age_level = cut(
      age,
      breaks = quantile(age, probs = c(0, 0.33, 0.66, 1), na.rm = TRUE),
      labels = c("Young", "Middle-aged", "Old"),
      include.lowest = TRUE
    )
  )

Penjelasan:

  • cut(age, breaks = quantile(age, probs = c(0, 0.33, 0.66, 1), na.rm = TRUE)) digunakan untuk membagi usia berdasarkan kuantil.

    • 0-33% usia pertama akan digolongkan ke dalam kategori “Young” (Muda).

    • 34-66% usia kedua akan digolongkan ke dalam kategori “Middle-aged” (Paruh Baya).

    • 67-100% usia ketiga akan digolongkan ke dalam kategori “Old” (Tua).

  • Fungsi cut() ini juga memberikan label pada setiap kategori, sehingga memudahkan interpretasi data.

Manfaat Discretization:

  • Penyederhanaan Model: Dengan mengubah variabel kontinu menjadi kategori, kita dapat mengurangi kompleksitas model dan memudahkan interpretasi.
  • Memperbaiki Model Klasifikasi: Kategorisasi dapat meningkatkan kinerja model klasifikasi, terutama jika data kontinu memiliki distribusi yang sangat miring atau tidak normal.

Hasil Transformasi

Kesimpulan:

Discretization dapat bermanfaat untuk mempermudah pemahaman dan analisis data, terutama dalam kasus di mana hubungan antara variabel kontinu dan hasil yang diinginkan tidak linier atau sulit untuk dimodelkan secara langsung.

8. Seasonality

Seasonality atau musiman adalah pola yang muncul secara periodik dalam data yang terkait dengan waktu, seperti pola tahunan, bulanan, atau mingguan. Fitur seasonality sangat penting dalam analisis data time series untuk memahami fluktuasi yang terkait dengan perubahan musiman dalam tahun.

Pada dataset ini, kita akan membuat fitur musiman berdasarkan informasi tanggal untuk menangkap pola tahunan.

Contoh:

  • Fourier Transform untuk ekstraksi frekuensi.
  • Deteksi Musiman dalam data bulanan/tahunan.

Contoh Kasus:

Untuk memodelkan seasonality dalam dataset kesehatan ini, kita akan menambahkan fitur-fitur berikut: - Tahun (Year)

  • Hari dalam Tahun (Day of Year)

  • Fungsi Sinus dan Cosinus untuk mengubah data musiman ke dalam format numerik yang dapat digunakan oleh model machine learning.

Contoh Kode R:


# Ensure lowercase column names
colnames(health_data) <- tolower(colnames(health_data))

# Try to find the column that represents the date
date_col <- grep("tanggal|date", colnames(health_data), value = TRUE)[1]

# Convert the column to Date type if not already
health_data[[date_col]] <- as.Date(health_data[[date_col]])

# Create seasonality features
seasonality <- health_data %>%
  mutate(
    year = year(.data[[date_col]]),
    day_of_year = yday(.data[[date_col]]),
    days_in_year = if_else(leap_year(.data[[date_col]]), 366, 365),
    sin_year = sin(2 * pi * day_of_year / days_in_year),
    cos_year = cos(2 * pi * day_of_year / days_in_year)
  )

Penjelasan:

  • Tahun (Year): Menyimpan informasi tahun dari kolom tanggal.

  • Hari dalam Tahun (Day of Year): Menghitung hari ke-berapa dalam tahun tersebut.

  • Sinus dan Cosinus (sin_year dan cos_year): Mengonversi hari dalam tahun menjadi nilai sinus dan cosinus untuk menangkap sifat musiman dalam bentuk numerik. Ini adalah teknik umum untuk menghindari masalah dengan model yang tidak dapat menangani data musiman yang bersifat siklis.

Manfaat Seasonality:

  • Memahami Pola Musiman: Fitur musiman dapat membantu dalam memprediksi pola yang berulang, seperti fluktuasi kesehatan pada periode tertentu dalam setahun.
  • Peningkatan Akurasi Model: Dengan memasukkan informasi musiman dalam model, kita dapat meningkatkan kemampuan prediksi terutama untuk data time series.

Hasil Transformasi

Kesimpulan:

Fitur seasonality sangat berguna dalam dataset dengan komponen waktu yang mempengaruhi hasil, seperti kesehatan yang dapat dipengaruhi oleh perubahan musiman. Dengan menggunakan fitur-fitur musiman, model dapat lebih efektif dalam menangkap pola yang terkait dengan perubahan musiman dan meningkatkan akurasi prediksi.

Penutup

Transformasi data adalah tahap penting yang menentukan kualitas analisis dan pemodelan. Teknik-teknik yang telah dibahas seperti transformasi temporal, distribusi, scaling, encoding, feature engineering, hingga handling outlier, semuanya bertujuan untuk membuat data lebih “bersahabat” dengan algoritma analisis. Dengan pemilihan metode yang tepat, kita dapat meningkatkan akurasi model dan membuat interpretasi data menjadi lebih baik.

LS0tICANCnRpdGxlOiAiUEVNUk9HUkFNQU4gU0FJTlMgREFUQSAxIiAgDQpzdWJ0aXRsZTogIkVYQU0gMSBTTVQgMiIgIA0KYXV0aG9yOiAiRGFkYW4gUmFtZGFuIEhpZGF5YXQgKDUyMjQwMDI4KSIgIA0KZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpLCAnJUIgJWQsICVZJylgIiAgDQpvdXRwdXQ6ICANCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246ICAgDQogICAgc2VsZl9jb250YWluZWQ6IHRydWUgIA0KICAgIHRodW1ibmFpbHM6IHRydWUgIA0KICAgIGxpZ2h0Ym94OiB0cnVlICANCiAgICBnYWxsZXJ5OiB0cnVlICANCiAgICBsaWJfZGlyOiBsaWJzICANCiAgICBkZl9wcmludDogInBhZ2VkIiAgDQogICAgY29kZV9mb2xkaW5nOiAic2hvdyIgIA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUgIA0KICAgIGNzczogInN0eWxlLmNzcyIgIA0KLS0tICANCg0KPGltZyBzcmM9IkZPVE8gVEVSQkFSVV8xMXpvbi5qcGciIGFsdD0iTG9nbyIgc3R5bGU9IndpZHRoOjIwMHB4OyBkaXNwbGF5OiBibG9jazsgbWFyZ2luOiBhdXRvOyI+DQoNCiMgKipQZW5kYWh1bHVhbioqICANClRyYW5zZm9ybWFzaSBkYXRhIGFkYWxhaCB0ZWtuaWsgZGFsYW0gcGVtcm9zZXNhbiBkYXRhIHlhbmcgYmVydHVqdWFuIHVudHVrIG1lbmd1YmFoIGZvcm1hdCwgc3RydWt0dXIsIGF0YXUgbmlsYWkgZGF0YSBhZ2FyIGxlYmloIHNlc3VhaSB1bnR1ayBhbmFsaXNpcywgZWtzcGxvcmFzaSwgZGFuIHBlbW9kZWxhbi4gQmViZXJhcGEgYWxhc2FuIHV0YW1hIHRyYW5zZm9ybWFzaSBkYXRhIGFkYWxhaDogIA0KDQotICoqTWVuc3RhYmlsa2FuIFZhcmlhbnNpKiog4oCTIERhdGEgZGVuZ2FuIHNrYWxhIHlhbmcgc2FuZ2F0IGJlcmJlZGEgZGFwYXQgbWVueWViYWJrYW4gbW9kZWwgbWVuamFkaSB0aWRhayBzdGFiaWwgYXRhdSBzdWxpdCBkaXBhaGFtaS4gRGVuZ2FuIHRyYW5zZm9ybWFzaSwgZGlzdHJpYnVzaSBkYXRhIGRhcGF0IGRpc2VzdWFpa2FuIGFnYXIgbGViaWggc2VyYWdhbS4NCi0gKipNZW5ndXJhbmdpIFNrZXduZXNzKiog4oCTIERhdGEgc2VyaW5nIGthbGkgbWVtaWxpa2kgZGlzdHJpYnVzaSB5YW5nIG1pcmluZyAoc2tld2VkKSwgeWFuZyBkYXBhdCBtZW1wZW5nYXJ1aGkgcGVyZm9ybWEgbW9kZWwgc3RhdGlzdGlrLiBUcmFuc2Zvcm1hc2kgZGFwYXQgbWVtYmFudHUgbWVuZGVrYXRrYW4gZGlzdHJpYnVzaSBrZSBiZW50dWsgbm9ybWFsLg0KLSAqKk1lbmFuZ2FuaSBPdXRsaWVyKiog4oCTIE91dGxpZXIgYXRhdSBuaWxhaSBla3N0cmVtIGRhcGF0IG1lbmRpc3RvcnNpIGhhc2lsIGFuYWxpc2lzIGRhbiBwZW1vZGVsYW4uIFRla25payB0cmFuc2Zvcm1hc2kgZGFwYXQgbWVtYmFudHUgbWVuZ3VyYW5naSBwZW5nYXJ1aG55YS4NCi0gKipNZW5ndWJhaCBEYXRhIEthdGVnb3Jpa2FsKiog4oCTIERhdGEga2F0ZWdvcmlrYWwgdGlkYWsgZGFwYXQgbGFuZ3N1bmcgZGlndW5ha2FuIGRhbGFtIGJhbnlhayBhbGdvcml0bWEgbWFjaGluZSBsZWFybmluZywgc2VoaW5nZ2EgcGVybHUgZGl1YmFoIG1lbmphZGkgYmVudHVrIG51bWVyaWsuDQotICoqTWVuZGV0ZWtzaSBQb2xhIE11c2ltYW4gZGFuIFRyZW4qKiDigJMgRGFsYW0gYW5hbGlzaXMgZGVyZXQgd2FrdHUsIHRyYW5zZm9ybWFzaSBtZW1iYW50dSBtZW5naWRlbnRpZmlrYXNpIHBvbGEgbXVzaW1hbiBkYW4gdHJlbiBkZW5nYW4gbGViaWggamVsYXMuDQotICoqTWVuZ3VyYW5naSBEaW1lbnNpIERhdGEqKiDigJMgS2V0aWthIGRhdGFzZXQgbWVtaWxpa2kgdGVybGFsdSBiYW55YWsgdmFyaWFiZWwsIGFuYWxpc2lzIG1lbmphZGkgc3VsaXQgZGFuIG1lbWFrYW4gd2FrdHUuIFRla25payBzZXBlcnRpIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkgbWVtYmFudHUgbWVueWVkZXJoYW5ha2FuIHN0cnVrdHVyIGRhdGEgdGFucGEga2VoaWxhbmdhbiBpbmZvcm1hc2kgcGVudGluZy4NCg0KTGFwb3JhbiBpbmkgYWthbiBtZW1iYWhhcyBiZXJiYWdhaSB0ZWtuaWsgdHJhbnNmb3JtYXNpIGRhdGEuDQoNCi0tLQ0KDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgRW5zdXJlIGFsbCByZXF1aXJlZCBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkDQpwYWNrYWdlcyA8LSBjKCJkcGx5ciIsICJzdHJpbmdpIiwgImx1YnJpZGF0ZSIsICJEVCIpDQpuZXdfcGFja2FnZXMgPC0gcGFja2FnZXNbIShwYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywgIlBhY2thZ2UiXSldDQppZihsZW5ndGgobmV3X3BhY2thZ2VzKSkgaW5zdGFsbC5wYWNrYWdlcyhuZXdfcGFja2FnZXMpDQoNCiMgTG9hZCBsaWJyYXJpZXMNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHN0cmluZ2kpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoRFQpDQoNCiMgQ3JlYXRlIGEgY29tcGxleCBkdW1teSBoZWFsdGggZGF0YXNldCBmb3IgZGF0YSB0cmFuc2Zvcm1hdGlvbg0Kc2V0LnNlZWQoNDIpDQpuIDwtIDUwMA0KDQpkYXRlcyA8LSBzZXEuRGF0ZShmcm9tID0gYXMuRGF0ZSgiMjAyMC0wMS0wMSIpLCB0byA9IGFzLkRhdGUoIjIwMjQtMTItMzEiKSwgYnkgPSAiZGF5IikNCnNhbXBsZV9kYXRlcyA8LSBzYW1wbGUoZGF0ZXMsIG4sIHJlcGxhY2UgPSBUUlVFKQ0KDQojIFNpbXVsYXRlIGhlYWx0aCBwYXJhbWV0ZXJzDQphZ2UgPC0gc2FtcGxlKDE4OjgwLCBuLCByZXBsYWNlID0gVFJVRSkNCmJtaSA8LSByb3VuZChybm9ybShuLCBtZWFuID0gMjUsIHNkID0gNCksIDEpICAjIEJNSSAoQm9keSBNYXNzIEluZGV4KQ0KYmxvb2RfcHJlc3N1cmUgPC0gcm91bmQocm5vcm0obiwgbWVhbiA9IDEyMCwgc2QgPSAxNSksIDEpICAjIFN5c3RvbGljIEJQDQpjaG9sZXN0ZXJvbCA8LSByb3VuZChybm9ybShuLCBtZWFuID0gMjAwLCBzZCA9IDQwKSwgMSkgICMgQ2hvbGVzdGVyb2wgKG1nL2RMKQ0KZ2x1Y29zZSA8LSByb3VuZChybm9ybShuLCBtZWFuID0gOTAsIHNkID0gMjApLCAxKSAgIyBHbHVjb3NlIChtZy9kTCkNCmhlYXJ0X3JhdGUgPC0gcm91bmQocm5vcm0obiwgbWVhbiA9IDc1LCBzZCA9IDEwKSwgMSkgICMgSGVhcnQgUmF0ZSAoYnBtKQ0KDQojIFNpbXVsYXRlIGxvY2F0aW9uIGFuZCBoZWFsdGggY29uZGl0aW9uDQpsb2NhdGlvbiA8LSBzYW1wbGUoYygiSmFrYXJ0YSIsICJCYW5kdW5nIiwgIlN1cmFiYXlhIiwgIk1lZGFuIiwgIk1ha2Fzc2FyIiksIG4sIHJlcGxhY2UgPSBUUlVFKQ0KaGVhbHRoX2NvbmRpdGlvbiA8LSBzYW1wbGUoYygiSGVhbHRoeSIsICJIeXBlcnRlbnNpb24iLCAiRGlhYmV0ZXMiLCAiT2Jlc2l0eSIsICJDYXJkaW92YXNjdWxhciBEaXNlYXNlIiksIG4sIHJlcGxhY2UgPSBUUlVFKQ0KDQojIFNpbXVsYXRlIHNlYXNvbmFsIGltcGFjdCBvbiBoZWFsdGgNCnNlYXNvbiA8LSBjYXNlX3doZW4oDQogIG1vbnRoKHNhbXBsZV9kYXRlcykgJWluJSBjKDExLCAxMiwgMSwgMikgfiAiUmFpbnkgU2Vhc29uIiwNCiAgbW9udGgoc2FtcGxlX2RhdGVzKSAlaW4lIGMoNiwgNywgOCwgOSkgfiAiRHJ5IFNlYXNvbiIsDQogIFRSVUUgfiAiVHJhbnNpdGlvbmFsIFNlYXNvbiINCikNCg0KIyBDcmVhdGUgdGhlIGhlYWx0aCBkYXRhc2V0DQpoZWFsdGhfZGF0YSA8LSB0aWJibGUoDQogIFBhdGllbnRfSUQgPSBzdHJpX3JhbmRfc3RyaW5ncyhuLCAxMiksDQogIERhdGUgPSBzYW1wbGVfZGF0ZXMsDQogIEFnZSA9IGFnZSwNCiAgQk1JID0gYm1pLA0KICBCbG9vZF9QcmVzc3VyZSA9IGJsb29kX3ByZXNzdXJlLA0KICBDaG9sZXN0ZXJvbCA9IGNob2xlc3Rlcm9sLA0KICBHbHVjb3NlID0gZ2x1Y29zZSwNCiAgSGVhcnRfUmF0ZSA9IGhlYXJ0X3JhdGUsDQogIExvY2F0aW9uID0gbG9jYXRpb24sDQogIEhlYWx0aF9Db25kaXRpb24gPSBoZWFsdGhfY29uZGl0aW9uLA0KICBTZWFzb24gPSBzZWFzb24NCikNCg0KIyBTaG93IGludGVyYWN0aXZlIHRhYmxlIHdpdGggZG93bmxvYWQgYnV0dG9ucw0KZGF0YXRhYmxlKA0KICBoZWFsdGhfZGF0YSwNCiAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywNCiAgb3B0aW9ucyA9IGxpc3QoDQogICAgZG9tID0gJ0JmcnRpcCcsDQogICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpLA0KICAgIHNjcm9sbFkgPSAiNDAwcHgiLA0KICAgIHNjcm9sbENvbGxhcHNlID0gVFJVRSwNCiAgICBwYWdpbmcgPSBGQUxTRQ0KICApLA0KICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oDQogICAgc3R5bGUgPSAnY2FwdGlvbi1zaWRlOiB0b3A7IHRleHQtYWxpZ246IGxlZnQ7IA0KICAgICAgICAgICAgIGZvbnQtc2l6ZTogMThweDsgZm9udC13ZWlnaHQ6IGJvbGQ7Jw0KICApLA0KICBjbGFzcyA9ICdzdHJpcGUgaG92ZXIgY29tcGFjdCcNCikNCmBgYA0KDQojIFRyYW5zZm9ybWFzaSBEYXRhDQoNCiMgMS5UZW1wb3JhbCBUcmFuc2Zvcm1hdGlvbg0KDQojIyAxLjEgTGFnLCBkaWZmLCByb2xsaW5nDQoNCkxhZ2dpbmcgYWRhbGFoIHRla25payB5YW5nIGRpZ3VuYWthbiB1bnR1ayBtZW5nZ2VzZXIgZGF0YSBrZSBiZWxha2FuZyBkYWxhbSBzYXR1YW4gd2FrdHUgdGVydGVudHUuIEluaSBzYW5nYXQgYmVyZ3VuYSBkYWxhbSBhbmFsaXNpcyBkZXJldCB3YWt0dSB1bnR1ayBtZW1haGFtaSBrZXRlcmthaXRhbiBhbnRhcmEgbmlsYWkgc2FhdCBpbmkgZGFuIG1hc2EgbGFsdS4NCg0KVGVrbmlrIGluaSBzZXJpbmcgZGlndW5ha2FuIGRhbGFtIG1vZGVsIHByZWRpa3RpZiBzZXBlcnRpIEFSSU1BIGRhbiByZWdyZXNpIGRlcmV0IHdha3R1LiBEZW5nYW4gbWVuZXJhcGthbiBsYWdnaW5nLCBraXRhIGRhcGF0IG1lbGloYXQgcG9sYSBwZXJnZXJha2FuIHN1YXR1IHZhcmlhYmVsIHRlcmhhZGFwIHdha3R1LCB5YW5nIG1lbXVuZ2tpbmthbiBhbmFsaXNpcyB5YW5nIGxlYmloIG1lbmRhbGFtLg0KDQojIyMgS2VsZWJpaGFuOiAgDQotIE1lbWJhbnR1IG1lbWFoYW1pIGh1YnVuZ2FuIHRlbXBvcmFsIGFudGFyYSB2YXJpYWJlbC4NCi0gQmVyZ3VuYSBkYWxhbSBwZW1vZGVsYW4gcHJlZGlrdGlmIGJlcmJhc2lzIGRlcmV0IHdha3R1Lg0KLSBNZW1iYW50dSBkYWxhbSBwZW1idWF0YW4gZml0dXIgYmFydSB1bnR1ayBtb2RlbCBtYWNoaW5lIGxlYXJuaW5nIGJlcmJhc2lzIGRlcmV0IHdha3R1Lg0KDQojIyMgS2VrdXJhbmdhbjoNCi0gRGFwYXQgbWVueWViYWJrYW4ga2VoaWxhbmdhbiBkYXRhIHBhZGEgYXdhbCBwZXJpb2RlLg0KLSBKaWthIGxhZyB0ZXJsYWx1IGJlc2FyLCBpbmZvcm1hc2kgeWFuZyByZWxldmFuIGJpc2EgaGlsYW5nLg0KLSBUaWRhayBzZWxhbHUgZWZla3RpZiBqaWthIHBvbGEgaHVidW5nYW4gYW50YXJ3YWt0dSB0aWRhayBrb25zaXN0ZW4uDQoNCg0KKkNvbnRvaCBQZW5nZ3VuYWFuOioNCg0KLSBEYWxhbSBrZXVhbmdhbiwgaGFyZ2Egc2FoYW0gaGFyaSBpbmkgYmlzYSBkaXBlbmdhcnVoaSBvbGVoIGhhcmdhIHNlYmVsdW1ueWEuDQotIERhbGFtIG1ldGVvcm9sb2dpLCBzdWh1IGhhcmkgaW5pIG11bmdraW4gYmVyZ2FudHVuZyBwYWRhIHN1aHUgYmViZXJhcGEgaGFyaSB5YW5nIGxhbHUuDQoNCmBgYHt9DQoNClRlbXBvcmFsIDwtIGhlYWx0aF9kYXRhICU+JQ0KICBhcnJhbmdlKFBhdGllbnRfSUQsIERhdGUpICU+JQ0KICBncm91cF9ieShQYXRpZW50X0lEKSAlPiUNCiAgbXV0YXRlKA0KICAgIExhZ19CUCA9IGxhZyhCbG9vZF9QcmVzc3VyZSksDQogICAgRGlmZl9CUCA9IEJsb29kX1ByZXNzdXJlIC0gbGFnKEJsb29kX1ByZXNzdXJlKSwNCiAgICBSb2xsaW5nTWVhbl9CUF8zID0gem9vOjpyb2xsYXBwbHkoQmxvb2RfUHJlc3N1cmUsIHdpZHRoID0gMywgRlVOID0gbWVhbiwgZmlsbCA9IE5BLCBhbGlnbiA9ICJyaWdodCIpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgICANCg0KDQojIyMgUGVuamVsYXNhbiBGdW5nc2kgDQoNCnwgRnVuZ3NpIHwgVHVqdWFuIHwNCnwtLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS18DQp8IGFycmFuZ2UoUGF0aWVudF9JRCwgRGF0ZSkgfCBNZW5ndXJ1dGthbiBkYXRhIGJlcmRhc2Fya2FuIHBhc2llbiBkYW4gdGFuZ2dhbCB1bnR1ayBtZW1hc3Rpa2FuIGtyb25vbG9naS4gfA0KfCBncm91cF9ieShQYXRpZW50X0lEKSB8IEFnYXIgdHJhbnNmb3JtYXNpIGRpbGFrdWthbiBwZXIgaW5kaXZpZHUsIGJ1a2FuIHNlbHVydWggZGF0YXNldC4gfA0KfCBsYWcoQmxvb2RfUHJlc3N1cmUpIHwgTWVuZ2FtYmlsIHRla2FuYW4gZGFyYWggc2ViZWx1bW55YSB1bnR1ayBtYXNpbmctbWFzaW5nIHBhc2llbi4gfA0KfCBEaWZmX0JQIHwgU2VsaXNpaCBhbnRhcmEgbmlsYWkgc2VrYXJhbmcgZGFuIG5pbGFpIHNlYmVsdW1ueWEg4oCUIGRpZ3VuYWthbiB1bnR1ayBtZW5kZXRla3NpIHRyZW4gbmFpay90dXJ1bi4gfA0KfCByb2xsYXBwbHkoLi4uLCB3aWR0aCA9IDMpIHwgUmF0YS1yYXRhIGRhcmkgMyBuaWxhaSB0ZXJha2hpciB1bnR1ayBtZW1iYW50dSBtZWxpaGF0IHRyZW4geWFuZyBsZWJpaCBzdGFiaWwgKHJvbGxpbmcgbWVhbikuIHwNCg0KDQojIyMgSW50ZXJwcmV0YXNpIEtlc2VoYXRhbg0KDQp8IEZpdHVyIFRyYW5zZm9ybWFzaSB8IEFydGkgJiBNYW5mYWF0IHwNCnwtLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgTGFnX0JQICAgICAgICAgICAgICAgfCBNZWxpaGF0IGVmZWsgbWFzYSBsYWx1IHRlcmhhZGFwIGtvbmRpc2kgc2FhdCBpbmkuIFBlbnRpbmcgdW50dWsgbW9kZWwgcHJlZGlrc2kuIHwNCnwgRGlmZl9CUCAgICAgICAgICAgICAgfCBNZW5naW5kaWthc2lrYW4gcGVuaW5na2F0YW4gYXRhdSBwZW51cnVuYW4gdGVrYW5hbiBkYXJhaC4gQmlzYSBtZW5hbmRha2FuIGtvbmRpc2kgc3RhYmlsIGF0YXUgbWVtYnVydWsuIHwNCnwgUm9sbGluZ01lYW5fQlBfMyAgICAgfCBNZW1iYW50dSBtZW1maWx0ZXIgZmx1a3R1YXNpIGphbmdrYSBwZW5kZWsuIEppa2EgbmlsYWkgc2FhdCBpbmkgamF1aCBkaSBhdGFzIHJhdGEtcmF0YSwgYmlzYSBqYWRpIGFsYXJtIGFub21hbGkuIHwNCg0KDQojIyMgSGFzaWwgVHJhbnNmb3JtYXNpIA0KDQpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQ0KDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHpvbykNCmxpYnJhcnkoYmVzdE5vcm1hbGl6ZSkNCmxpYnJhcnkoRFQpDQoNClRlbXBvcmFsIDwtIGhlYWx0aF9kYXRhICU+JQ0KICBhcnJhbmdlKFBhdGllbnRfSUQsIERhdGUpICU+JQ0KICBncm91cF9ieShQYXRpZW50X0lEKSAlPiUNCiAgbXV0YXRlKA0KICAgIExhZ19CUCA9IGxhZyhCbG9vZF9QcmVzc3VyZSksDQogICAgRGlmZl9CUCA9IEJsb29kX1ByZXNzdXJlIC0gbGFnKEJsb29kX1ByZXNzdXJlKSwNCiAgICBSb2xsaW5nTWVhbl9CUF8zID0gem9vOjpyb2xsYXBwbHkoQmxvb2RfUHJlc3N1cmUsIHdpZHRoID0gMywgRlVOID0gbWVhbiwgZmlsbCA9IE5BLCBhbGlnbiA9ICJyaWdodCIpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpEVDo6ZGF0YXRhYmxlKFRlbXBvcmFsLCBjYXB0aW9uID0gIkxhZywgRGlmZiwgUm9sbGluZyAtIEJsb29kIFByZXNzdXJlIikNCmBgYA0KDQoNCg0KDQpTZW11YSBuaWxhaSAqTGFnX0JQLCBEaWZmX0JQLCBkYW4gUm9sbGluZ01lYW5fQlBfMyogYmVybmlsYWkgTkEga2FyZW5hIGluaSBhZGFsYWggYmFyaXMgcGVydGFtYSB1bnR1ayBtYXNpbmctbWFzaW5nIHBhc2llbi4gVGFucGEgb2JzZXJ2YXNpIHNlYmVsdW1ueWEsIHRpZGFrIG11bmdraW4gbWVuZ2hpdHVuZyBuaWxhaSBsYWcsIHNlbGlzaWgsIGF0YXUgcmF0YS1yYXRhIGJlcmd1bGlyLg0KDQoNCiMjIDEuMiBFeHRyYWN0IEhvdXIsIERheSwgTW9udGgsIFllYXINCg0KRWtzdHJha3NpIHdha3R1IGFkYWxhaCBwcm9zZXMgbWVuZ2FtYmlsIGJhZ2lhbiBzcGVzaWZpayBkYXJpIHRpbWVzdGFtcCwgc2VwZXJ0aSBqYW0sIGhhcmksIGJ1bGFuLCBhdGF1IHRhaHVuLiAgDQpJbmkgYmVyZ3VuYSB1bnR1ayBhbmFsaXNpcyBtdXNpbWFuIGF0YXUgcG9sYSBwZXJpbGFrdSBwZW5nZ3VuYSBiZXJkYXNhcmthbiB3YWt0dS4NCg0KIyMjIEtlbGViaWhhbjoNCi0gTWVtYmFudHUgbWVuZW11a2FuIHBvbGEgYmVyZGFzYXJrYW4gd2FrdHUgKGNvbnRvaDogcGVtYmVsaWFuIGxlYmloIGJhbnlhayBkaSBha2hpciBwZWthbikuDQotIE1lbXVuZ2tpbmthbiBwZW1idWF0YW4gZml0dXIgYmFydSBkYXJpIGRhdGEgd2FrdHUuDQoNCiMjIyBLZWt1cmFuZ2FuOg0KLSBKaWthIHRpZGFrIHJlbGV2YW4gZGVuZ2FuIGFuYWxpc2lzLCBmaXR1ciBpbmkgYmlzYSBtZW5qYWRpIG5vaXNlLg0KLSBNZW1idXR1aGthbiBmb3JtYXQgdGFuZ2dhbC93YWt0dSB5YW5nIGJlbmFyLg0KDQpgYGB7fQ0KDQpFeHRyYWN0IDwtIGhlYWx0aF9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgRGF5X29mX1dlZWsgPSB3ZWVrZGF5cyhEYXRlKSwNCiAgICBNb250aCA9IG1vbnRoKERhdGUsIGxhYmVsID0gVFJVRSksDQogICAgWWVhciA9IHllYXIoRGF0ZSksDQogICAgSXNfV2Vla2VuZCA9IGlmZWxzZShEYXlfb2ZfV2VlayAlaW4lIGMoIlNhdHVyZGF5IiwgIlN1bmRheSIpLCAxLCAwKSwNCiAgICBMb2NhdGlvbiA9IGFzLmZhY3RvcihMb2NhdGlvbiksDQogICAgSGVhbHRoX0NvbmRpdGlvbiA9IGFzLmZhY3RvcihIZWFsdGhfQ29uZGl0aW9uKQ0KICApICU+JQ0KICBiaW5kX2NvbHMoDQogICAgYXMuZGF0YS5mcmFtZShtb2RlbC5tYXRyaXgofiBMb2NhdGlvbiAtIDEsIGRhdGEgPSAuKSksDQogICAgYXMuZGF0YS5mcmFtZShtb2RlbC5tYXRyaXgofiBIZWFsdGhfQ29uZGl0aW9uIC0gMSwgZGF0YSA9IC4pKQ0KICApDQpgYGAgDQoNCg0KDQojIyMgUGVuamVsYXNhbiBGdW5nc2kgDQoNCnwgRnVuZ3NpIHwgVHVqdWFuIHwNCnwtLS0tLS0tLXwtLS0tLS0tLXwNCnwgd2Vla2RheXMoRGF0ZSkgfCBNZW5lbnR1a2FuIG5hbWEgaGFyaSBiZXJkYXNhcmthbiB0YW5nZ2FsIChtaXNhbG55YSBTZW5pbiwgU2VsYXNhLCBkbGwuKS4gfA0KfCBtb250aChEYXRlLCBsYWJlbCA9IFRSVUUpIHwgTWVuZ2FtYmlsIGJ1bGFuIGRhcmkgdGFuZ2dhbCBkYW4gbWVtYmVyaSBsYWJlbCAoY29udG9oOiBKYW4sIEZlYiwgZHN0LikuIHwNCnwgeWVhcihEYXRlKSB8IE1lbmdhbWJpbCB0YWh1biBkYXJpIHRhbmdnYWwgdW50dWsgYW5hbGlzaXMgbXVzaW1hbiBhdGF1IHRhaHVuYW4uIHwNCnwgaWZlbHNlKERheV9vZl9XZWVrICVpbiUgYygiU2F0dXJkYXkiLCAiU3VuZGF5IiksIDEsIDApIHwgTWVuZW50dWthbiBhcGFrYWggdGFuZ2dhbCB0ZXJzZWJ1dCBqYXR1aCBwYWRhIGFraGlyIHBla2FuIChTYWJ0dS9NaW5nZ3UpLiB8DQp8IGFzLmZhY3RvcihMb2NhdGlvbikgfCBNZW5ndWJhaCBsb2thc2kgbWVuamFkaSB2YXJpYWJlbCBrYXRlZ29yaS4gfA0KfCBhcy5mYWN0b3IoSGVhbHRoX0NvbmRpdGlvbikgfCBNZW5ndWJhaCBrb25kaXNpIGtlc2VoYXRhbiBtZW5qYWRpIHZhcmlhYmVsIGthdGVnb3JpLiB8DQp8IG1vZGVsLm1hdHJpeCh+IExvY2F0aW9uIC0gMSkgfCBNZW5nZ3VuYWthbiBvbmUtaG90IGVuY29kaW5nIHVudHVrIG1lbmdvbnZlcnNpIGxva2FzaSBtZW5qYWRpIGtvbG9tLWtvbG9tIGJpbmVyLiB8DQp8IG1vZGVsLm1hdHJpeCh+IEhlYWx0aF9Db25kaXRpb24gLSAxKSB8IE1lbmdndW5ha2FuIG9uZS1ob3QgZW5jb2RpbmcgdW50dWsgbWVuZ29udmVyc2kga29uZGlzaSBrZXNlaGF0YW4gbWVuamFkaSBrb2xvbS1rb2xvbSBiaW5lci4gfA0KDQoNCg0KIyMjICBJbnRlcnByZXRhc2kgS2VzZWhhdGFuOg0KDQp8IEZpdHVyIFRyYW5zZm9ybWFzaSB8IEFydGkgJiBNYW5mYWF0IHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBEYXlfb2ZfV2VlayAgICAgICAgICB8IE1lbnllZGlha2FuIGluZm9ybWFzaSBoYXJpIGRhbGFtIG1pbmdndS4gQmVyZ3VuYSB1bnR1ayBtZW5kZXRla3NpIHBvbGEgaGFyaWFuLCBtaXNhbG55YSBhcGFrYWggYWRhIGxlYmloIGJhbnlhayBrdW5qdW5nYW4gcnVtYWggc2FraXQgZGkgYWtoaXIgcGVrYW4uIHwNCnwgTW9udGggICAgICAgICAgICAgICAgfCBNZW55ZWRpYWthbiBpbmZvcm1hc2kgdGVudGFuZyBidWxhbi4gRGFwYXQgZGlndW5ha2FuIHVudHVrIG1lbmRldGVrc2kgcG9sYSBtdXNpbWFuIGRhbGFtIGRhdGEsIHNlcGVydGkgcGVuaW5na2F0YW4ga2FzdXMgcGVueWFraXQgdGVydGVudHUgZGkgYnVsYW4gdGVydGVudHUuIHwNCnwgWWVhciAgICAgICAgICAgICAgICAgfCBNZW1iZXJpa2FuIGluZm9ybWFzaSB0YWh1bmFuLiBCZXJndW5hIHVudHVrIGFuYWxpc2lzIGphbmdrYSBwYW5qYW5nIGRhbiB0cmVuIHRhaHVuYW4sIHNlcGVydGkgcGVuaW5na2F0YW4gYXRhdSBwZW51cnVuYW4ga2VzZWhhdGFuIG1hc3lhcmFrYXQgZGFyaSB0YWh1biBrZSB0YWh1bi4gfA0KfCBJc19XZWVrZW5kICAgICAgICAgICB8IE1lbnVuanVra2FuIGFwYWthaCBkYXRhIGJlcmFzYWwgZGFyaSBha2hpciBwZWthbiBhdGF1IHRpZGFrLiBCaXNhIG1lbmphZGkgZml0dXIgdW50dWsgbWVsaWhhdCBwZXJpbGFrdSBhdGF1IHBvbGEgeWFuZyB0ZXJqYWRpIHBhZGEgaGFyaS1oYXJpIHRlcnRlbnR1LiB8DQp8IExvY2F0aW9uIChPbmUtaG90KSAgIHwgTWVuZ29udmVyc2kgbG9rYXNpIG1lbmphZGkgdmFyaWFiZWwgYmluZXIuIERhcGF0IGRpZ3VuYWthbiB1bnR1ayBtZW5nYW5hbGlzaXMgYXBha2FoIGxva2FzaSB0ZXJ0ZW50dSBiZXJodWJ1bmdhbiBkZW5nYW4ga29uZGlzaSBrZXNlaGF0YW4gdGVydGVudHUuIHwNCnwgSGVhbHRoX0NvbmRpdGlvbiAoT25lLWhvdCkgfCBNZW5nb252ZXJzaSBrb25kaXNpIGtlc2VoYXRhbiBtZW5qYWRpIHZhcmlhYmVsIGJpbmVyLiBNZW1iYW50dSBkYWxhbSBhbmFsaXNpcyB0ZXJrYWl0IGRlbmdhbiBqZW5pcyBrb25kaXNpIGtlc2VoYXRhbiBkYW4gcGVuZ2FydWhueWEgdGVyaGFkYXAgdmFyaWFiZWwgbGFpbm55YS4gfA0KDQoNCg0KIyMjIEhhc2lsIFRyYW5zZm9ybWFzaSANCg0KRGFyaSBoYXNpbCB0cmFuc2Zvcm1hc2kgZGF0YSB5YW5nIG1lbGliYXRrYW4gZWtzdHJha3NpIGluZm9ybWFzaSB3YWt0dSBkYW4gZW5jb2RpbmcgdW50dWsgbG9rYXNpIHNlcnRhIGtvbmRpc2kga2VzZWhhdGFuIHBhc2llbiwgYmVyaWt1dCBhZGFsYWggY29udG9oIHRhbXBpbGFuIGRhdGEgeWFuZyBkaXBlcm9sZWg6DQoNCg0KYGBge3IgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1GQUxTRX0NCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeSh6b28pDQpsaWJyYXJ5KGJlc3ROb3JtYWxpemUpDQpsaWJyYXJ5KERUKQ0KDQpFeHRyYWN0IDwtIGhlYWx0aF9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgRGF5X29mX1dlZWsgPSB3ZWVrZGF5cyhEYXRlKSwNCiAgICBNb250aCA9IG1vbnRoKERhdGUsIGxhYmVsID0gVFJVRSksDQogICAgWWVhciA9IHllYXIoRGF0ZSksDQogICAgSXNfV2Vla2VuZCA9IGlmZWxzZShEYXlfb2ZfV2VlayAlaW4lIGMoIlNhdHVyZGF5IiwgIlN1bmRheSIpLCAxLCAwKSwNCiAgICBMb2NhdGlvbiA9IGFzLmZhY3RvcihMb2NhdGlvbiksDQogICAgSGVhbHRoX0NvbmRpdGlvbiA9IGFzLmZhY3RvcihIZWFsdGhfQ29uZGl0aW9uKQ0KICApICU+JQ0KICBiaW5kX2NvbHMoDQogICAgYXMuZGF0YS5mcmFtZShtb2RlbC5tYXRyaXgofiBMb2NhdGlvbiAtIDEsIGRhdGEgPSAuKSksDQogICAgYXMuZGF0YS5mcmFtZShtb2RlbC5tYXRyaXgofiBIZWFsdGhfQ29uZGl0aW9uIC0gMSwgZGF0YSA9IC4pKQ0KICApDQoNCkRUOjpkYXRhdGFibGUoRXh0cmFjdCwgY2FwdGlvbiA9ICJFeHRyYWN0IERhdGUgQ29tcG9uZW50cyIpDQpgYGANCg0KDQoNCiMjIyBLZXNpbXB1bGFuDQoNCi0gKkVrc3RyYWtzaSBXYWt0dToqIERhdGEgYmVyaGFzaWwgZGl1YmFoIGRlbmdhbiBtZW5nZWtzdHJha3NpIGVsZW1lbi1lbGVtZW4gd2FrdHUgc2VwZXJ0aSAqSGFyaSBkYWxhbSBNaW5nZ3UsIEJ1bGFuLCBkYW4gVGFodW4qLCB5YW5nIG1lbXVuZ2tpbmthbiBhbmFsaXNpcyBsZWJpaCBsYW5qdXQgdGVya2FpdCBkZW5nYW4gcG9sYSBtdXNpbWFuIGRhbiBwZXJpbGFrdSBiZXJkYXNhcmthbiB3YWt0dS4NCiAgLSBJc19XZWVrZW5kIGRpaGFzaWxrYW4gZGVuZ2FuIG1lbWJlcmlrYW4gbmlsYWkgMSBqaWthIGhhcmkgdGVyc2VidXQgYWRhbGFoIGFraGlyIHBla2FuIChTYWJ0dSBhdGF1IE1pbmdndSkgZGFuIDAgdW50dWsgaGFyaSBsYWlubnlhLCBtZW11bmdraW5rYW4gaWRlbnRpZmlrYXNpIHRyZW4gYmVyZGFzYXJrYW4gYWtoaXIgcGVrYW4gYXRhdSBoYXJpIGtlcmphLg0KICANCi0gKkxvY2F0aW9uICYgSGVhbHRoIENvbmRpdGlvbiBFbmNvZGluZzoqIExva2FzaSBkYW4ga29uZGlzaSBrZXNlaGF0YW4gcGFzaWVuIHRlbGFoIGRpdWJhaCBtZW5qYWRpICp2YXJpYWJlbCBiaW5lciB1bnR1ayBtZW11ZGFoa2FuIGFuYWxpc2lzIHN0YXRpc3RpayBkYW4gcGVtb2RlbGFuIHByZWRpa3RpZi4NCiAgLSBWYXJpYWJlbCBsb2thc2kgc2VwZXJ0aSAqTG9jYXRpb25fSmFrYXJ0YSwgTG9jYXRpb25fQmFuZHVuZywgZGFuIExvY2F0aW9uX1N1cmFiYXlhKiBkaXViYWggbWVuamFkaSBpbmRpa2F0b3IgYmluZXIgdW50dWsgc2V0aWFwIGxva2FzaS4NCiAgLSBLb25kaXNpIGtlc2VoYXRhbiBwYXNpZW4sIHNlcGVydGkgKkhlYWx0aF9Db25kaXRpb25fSGVhbHRoeSwgSGVhbHRoX0NvbmRpdGlvbl9IeXBlcnRlbnNpb24sIGRhbiBIZWFsdGhfQ29uZGl0aW9uX0RpYWJldGVzKiwganVnYSBkaXBldGFrYW4gZGFsYW0gYmVudHVrIHZhcmlhYmVsIGJpbmVyIHlhbmcgbWVuZ2dhbWJhcmthbiBrb25kaXNpIG1lZGlzIHNldGlhcCBpbmRpdmlkdS4NCg0KDQojIyAxLjMgQ3VtdWxhdGl2ZSBTdW0vTWVhbi9Db3VudA0KDQpUZWtuaWsga3VtdWxhdGlmIG1lbmdoaXR1bmcgdG90YWwgYmVyamFsYW4gKHJ1bm5pbmcgdG90YWwpLCBqdW1sYWgga2VtdW5jdWxhbiwgYXRhdSByYXRhLXJhdGEga3VtdWxhdGlmIGRhcmkgZGF0YSBiZXJkYXNhcmthbiB3YWt0dS4NCg0KIyMjIEtlbGViaWhhbjoNCi0gU2FuZ2F0IGJlcmd1bmEgdW50dWsgbWVsaWhhdCBwZXJ0dW1idWhhbiBha3VtdWxhdGlmLg0KLSBNZW1iYW50dSBkYWxhbSBtZW1vZGVsa2FuIHBlcmlsYWt1IGphbmdrYSBwYW5qYW5nLg0KDQojIyMgS2VrdXJhbmdhbjoNCg0KLSBIYXNpbCBrdW11bGF0aWYgYmlzYSBtZW5nYWJ1cmthbiBwZXJ1YmFoYW4gbG9rYWwga2VjaWwuDQoNCi0gQmlzYSBtZW1wZW5nYXJ1aGkgZGlzdHJpYnVzaSBkYXRhIG1lbmphZGkgbGViaWggc21vb3RoICh0ZXJsYWx1IGhhbHVzKS4NCg0KKkNvbnRvaCBQZW5nZ3VuYWFuIGRhbGFtIFI6Kg0KDQpgYGB7fQ0KDQpUZW1wb3JhbDMgPC0gaGVhbHRoX2RhdGEgJT4lDQogIGdyb3VwX2J5KFBhdGllbnRfSUQpICU+JQ0KICBtdXRhdGUoDQogICAgQ3VtdWxhdGl2ZV9CUCA9IGN1bXN1bShCbG9vZF9QcmVzc3VyZSksDQogICAgQ3VtdWxhdGl2ZV9HbHVjb3NlID0gY3Vtc3VtKEdsdWNvc2UpLA0KICAgIEN1bXVsYXRpdmVfQXZnSFIgPSBjdW1tZWFuKEhlYXJ0X1JhdGUpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgIA0KDQoNCg0KIyMjIFBlbmplbGFzYW4gRnVuZ3NpIA0KDQp8IEZ1bmdzaSB8IFR1anVhbiB8DQp8LS0tLS0tLS18LS0tLS0tLS18DQp8IGN1bXN1bShCbG9vZF9QcmVzc3VyZSkgfCBNZW5naGl0dW5nIGp1bWxhaCBrdW11bGF0aWYgdGVrYW5hbiBkYXJhaCBzZXBhbmphbmcgd2FrdHUuIHwNCnwgY3Vtc3VtKEdsdWNvc2UpIHwgTWVuZ2hpdHVuZyBqdW1sYWgga3VtdWxhdGlmIGthZGFyIGdsdWtvc2Egc2VwYW5qYW5nIHdha3R1LiB8DQp8IGN1bW1lYW4oSGVhcnRfUmF0ZSkgfCBNZW5naGl0dW5nIHJhdGEtcmF0YSBrdW11bGF0aWYgZGV0YWsgamFudHVuZyBzZXBhbmphbmcgd2FrdHUuIHwNCg0KDQojIyMgSW50ZXJwcmV0YXNpIEtlc2VoYXRhbjoNCg0KfCBGaXR1ciBUcmFuc2Zvcm1hc2kgfCBBcnRpICYgTWFuZmFhdCB8DQp8LS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgQ3VtdWxhdGl2ZV9CUCAgICAgICAgIHwgTWVueWVkaWFrYW4gdG90YWwga3VtdWxhdGlmIGRhcmkgdGVrYW5hbiBkYXJhaCBwYXNpZW4gc2VwYW5qYW5nIHdha3R1LiBJbmkgYmlzYSBtZW1iZXJpa2FuIGdhbWJhcmFuIG1lbmdlbmFpIHBlbmluZ2thdGFuIGF0YXUgcGVudXJ1bmFuIGtvbmRpc2kga2VzZWhhdGFuIHNlY2FyYSBrZXNlbHVydWhhbiwgbWlzYWxueWEgZGFsYW0ga2FzdXMgaGlwZXJ0ZW5zaS4gfA0KfCBDdW11bGF0aXZlX0dsdWNvc2UgICAgfCBNZW55ZWRpYWthbiB0b3RhbCBrdW11bGF0aWYga2FkYXIgZ2x1a29zYSBwYXNpZW4uIEluaSBkYXBhdCBtZW1iYW50dSBtZW1hbnRhdSBmbHVrdHVhc2kga2FkYXIgZ2x1a29zYSBzZXBhbmphbmcgd2FrdHUgZGFuIG1lbWJlcmlrYW4gZ2FtYmFyYW4gdGVudGFuZyBrZXN0YWJpbGFuIGtvbmRpc2kgcGFzaWVuIGRlbmdhbiBkaWFiZXRlcy4gfA0KfCBDdW11bGF0aXZlX0F2Z0hSICAgICAgfCBNZW55ZWRpYWthbiByYXRhLXJhdGEga3VtdWxhdGlmIGRldGFrIGphbnR1bmcgcGFzaWVuLiBJbmkgZGFwYXQgbWVtYmFudHUgbWVsaWhhdCB0cmVuIGRldGFrIGphbnR1bmcgcGFzaWVuIGRhcmkgd2FrdHUga2Ugd2FrdHUsIGFwYWthaCB0ZXJqYWRpIHBlbmluZ2thdGFuIGF0YXUgcGVudXJ1bmFuIHlhbmcgc2lnbmlmaWthbi4gfA0KDQoNCiMjIyBIYXNpbCBUcmFuc2Zvcm1hc2kNCg0KYGBge3IgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1GQUxTRX0NCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeSh6b28pDQpsaWJyYXJ5KGJlc3ROb3JtYWxpemUpDQpsaWJyYXJ5KERUKQ0KDQpUZW1wb3JhbDMgPC0gaGVhbHRoX2RhdGEgJT4lDQogIGdyb3VwX2J5KFBhdGllbnRfSUQpICU+JQ0KICBtdXRhdGUoDQogICAgQ3VtdWxhdGl2ZV9CUCA9IGN1bXN1bShCbG9vZF9QcmVzc3VyZSksDQogICAgQ3VtdWxhdGl2ZV9HbHVjb3NlID0gY3Vtc3VtKEdsdWNvc2UpLA0KICAgIEN1bXVsYXRpdmVfQXZnSFIgPSBjdW1tZWFuKEhlYXJ0X1JhdGUpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpEVDo6ZGF0YXRhYmxlKFRlbXBvcmFsMywgY2FwdGlvbiA9ICJDdW11bGF0aXZlIFZhbHVlcyIpDQpgYGANCg0KIyMjIEtlc2ltcHVsYW46DQoNCi0gKkN1bXVsYXRpdmUgU3VtIChjdW1zdW0pKiBkaWd1bmFrYW4gdW50dWsgbWVuZ3VrdXIgdG90YWwga3VtdWxhdGlmIGRhcmkgdmFyaWFiZWwgdGVydGVudHUgKHNlcGVydGkgdGVrYW5hbiBkYXJhaCBhdGF1IGthZGFyIGdsdWtvc2EpIGRhcmkgd2FrdHUga2Ugd2FrdHUuIFRla25payBpbmkgc2FuZ2F0IGJlcmd1bmEgdW50dWsgbWVtYWhhbWkgYmFnYWltYW5hIHN1YXR1IGtvbmRpc2kgYmVya2VtYmFuZyBkYWxhbSBqYW5na2EgcGFuamFuZy4NCg0KLSAqQ3VtdWxhdGl2ZSBNZWFuIChjdW1tZWFuKSogZGlndW5ha2FuIHVudHVrIG1lbGloYXQgdHJlbiByYXRhLXJhdGEgamFuZ2thIHBhbmphbmcsIHNlcGVydGkgZGV0YWsgamFudHVuZy4gSW5pIGRhcGF0IG1lbWJhbnR1IGRhbGFtIG1lbWFudGF1IGtlc3RhYmlsYW4ga29uZGlzaSBrZXNlaGF0YW4gcGFzaWVuLg0KDQotICpLZWxlbWFoYW4gdGVrbmlrIGt1bXVsYXRpZio6IEhhc2lsIGt1bXVsYXRpZiBiaXNhIG1lbXBlbmdhcnVoaSBkaXN0cmlidXNpIGRhdGEsIG1lbWJ1YXQgZGF0YSB0ZXJsaWhhdCBsZWJpaCBzbW9vdGggZGFuIGRhcGF0IG1lbmdhYnVya2FuIHBlcnViYWhhbiBsb2thbCBrZWNpbCB5YW5nIG11bmdraW4gc2lnbmlmaWthbi4gT2xlaCBrYXJlbmEgaXR1LCBoYXJ1cyBkaWd1bmFrYW4gZGVuZ2FuIGhhdGktaGF0aSB0ZXJnYW50dW5nIHBhZGEgdHVqdWFuwqBhbmFsaXNpcy4NCg0KLS0tDQoNCiMgMi5UcmFuc2Zvcm1hc2kgRGlzdHJpYnVzaQ0KDQojIyAyLjEgTG9nIFRyYW5zZm9ybQ0KDQpgYGB7fQ0KDQptaW5fcG9zaXRpdmUgPC0gbWluKGhlYWx0aF9kYXRhJEdsdWNvc2VbaGVhbHRoX2RhdGEkR2x1Y29zZSA+IDBdKQ0KDQpMb2cgPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBTYWZlX0dsdWNvc2UgPSBpZmVsc2UoR2x1Y29zZSA8PSAwLCBtaW5fcG9zaXRpdmUsIEdsdWNvc2UpLA0KICAgIExvZ19HbHVjb3NlID0gbG9nMXAoU2FmZV9HbHVjb3NlKQ0KICApDQpgYGANCg0KDQojIyMgUGVuamVsYXNhbiBGdW5nc2kNCg0KfCBGdW5nc2kgfCBUdWp1YW4gfA0KfC0tLS0tLS0tfC0tLS0tLS0tfA0KfCBtaW4oaGVhbHRoX2RhdGEkR2x1Y29zZVtoZWFsdGhfZGF0YSRHbHVjb3NlID4gMF0pIHwgTWVuY2FyaSBuaWxhaSBnbHVrb3NhIHBvc2l0aWYgdGVya2VjaWwsIHVudHVrIG1lbmdnYW50aWthbiBuaWxhaSBub2wgYXRhdSBuZWdhdGlmLiB8DQp8IGlmZWxzZShHbHVjb3NlIDw9IDAsIG1pbl9wb3NpdGl2ZSwgR2x1Y29zZSkgfCBNZW5nZ2FudGkgbmlsYWkgZ2x1a29zYSBub2wgYXRhdSBuZWdhdGlmIGRlbmdhbiBuaWxhaSBnbHVrb3NhIHBvc2l0aWYgdGVya2VjaWwgYWdhciBhbWFuIHVudHVrIHRyYW5zZm9ybWFzaSBsb2dhcml0bWEuIHwNCnwgbG9nMXAoU2FmZV9HbHVjb3NlKSB8IE1lbmdoaXR1bmcgbG9nYXJpdG1hIG5hdHVyYWwgZGFyaSAoZ2x1a29zYSArIDEpLCBhZ2FyIGRpc3RyaWJ1c2kgZGF0YSBsZWJpaCBub3JtYWwgZGFuIG1lbmdoaW5kYXJpIGxvZygwKS4gfA0KDQotLS0NCg0KIyMjIEludGVycHJldGFzaSBLZXNlaGF0YW4gDQoNCnwgRml0dXIgVHJhbnNmb3JtYXNpIHwgQXJ0aSAmIE1hbmZhYXQgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IFNhZmVfR2x1Y29zZSAgICAgICAgIHwgTWVuamFtaW4gc2VtdWEgbmlsYWkgZ2x1a29zYSBiZXJuaWxhaSBwb3NpdGlmIHNlYmVsdW0gdHJhbnNmb3JtYXNpIGxvZ2FyaXRtYS4gSW5pIHBlbnRpbmcga2FyZW5hIGxvZygwKSB0aWRhayB0ZXJkZWZpbmlzaSwgZGFuIG5pbGFpIG5vbCBiaXNhIG11bmN1bCBha2liYXQga2VzYWxhaGFuIHBlbmNhdGF0YW4gYXRhdSBhbGF0IG1lZGlzLiB8DQp8IExvZ19HbHVjb3NlICAgICAgICAgIHwgVHJhbnNmb3JtYXNpIGxvZ2FyaXRtYSBtZW1idWF0IGRpc3RyaWJ1c2kgZ2x1a29zYSB5YW5nIGF3YWxueWEgbWlyaW5nIChza2V3ZWQpIG1lbmphZGkgbGViaWggbm9ybWFsLiBJbmkgbWVtYmFudHUgbWVuaW5na2F0a2FuIHBlcmZvcm1hIG1vZGVsIHByZWRpa3RpZiBkYW4gYW5hbGlzaXMgc3RhdGlzdGlrLiB8DQoNCi0tLQ0KDQojIyMgSGFzaWwgVHJhbnNmb3J0YXNpIA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1GQUxTRX0NCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeSh6b28pDQpsaWJyYXJ5KGJlc3ROb3JtYWxpemUpDQpsaWJyYXJ5KERUKQ0KDQptaW5fcG9zaXRpdmUgPC0gbWluKGhlYWx0aF9kYXRhJEdsdWNvc2VbaGVhbHRoX2RhdGEkR2x1Y29zZSA+IDBdKQ0KDQpMb2cgPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBTYWZlX0dsdWNvc2UgPSBpZmVsc2UoR2x1Y29zZSA8PSAwLCBtaW5fcG9zaXRpdmUsIEdsdWNvc2UpLA0KICAgIExvZ19HbHVjb3NlID0gbG9nMXAoU2FmZV9HbHVjb3NlKQ0KICApDQoNCkRUOjpkYXRhdGFibGUoTG9nLCBjYXB0aW9uID0gIkxvZyBUcmFuc2Zvcm0gb24gR2x1Y29zZSIpDQpgYGANCg0KDQoNCiMjIyBLZXNpbXB1bGFuIA0KDQotIFRyYW5zZm9ybWFzaSBsb2cxcCgpIChsb2coeCArIDEpKSBiZXJndW5hIHVudHVrIG1lbmFuZ2FuaSBkYXRhIHNrZXdlZCBkYW4gbmlsYWkgbm9sLCB0ZXJ1dGFtYSBkYWxhbSBkYXRhIGtlc2VoYXRhbiBzZXBlcnRpIGdsdWtvc2EuDQotIE1lbmdnYW50aSBuaWxhaSAwIGF0YXUgbmVnYXRpZiBkZW5nYW4gbmlsYWkgbWluaW11bSBwb3NpdGlmIGFkYWxhaCBzdHJhdGVnaSByb2J1c3QgdW50dWsgbWVuZ2hpbmRhcmkgZXJyb3IgbWF0ZW1hdGlzLg0KLSBUZWtuaWsgaW5pIHNlcmluZyBkaWd1bmFrYW4gc2ViZWx1bSBwZW1vZGVsYW4gc3RhdGlzdGlrIGF0YXUgbWFjaGluZSBsZWFybmluZywgYWdhciBoYXNpbCBhbmFsaXNpcyBtZW5qYWRpIGxlYmloIGFrdXJhdCBkYW4gc3RhYmlsLg0KDQotLS0NCg0KIyMgMi4yIEJveC1Db3gNCg0KDQpgYGB7fQ0KDQoNCkJveF9Db3ggPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBZZW9KX0JQID0gYmVzdE5vcm1hbGl6ZShCbG9vZF9QcmVzc3VyZSkkeC50LA0KICAgIFllb0pfQ2hvbCA9IGJlc3ROb3JtYWxpemUoQ2hvbGVzdGVyb2wpJHgudA0KICApDQogIA0KYGBgDQoNCiMjIyBQZW5qZWxhc2FuIEZ1bmdzaSANCg0KfCBGdW5nc2kgfCBUdWp1YW4gfA0KfC0tLS0tLS0tfC0tLS0tLS0tfA0KfCBiZXN0Tm9ybWFsaXplKEJsb29kX1ByZXNzdXJlKSR4LnQgfCBNZW5lcmFwa2FuIHRyYW5zZm9ybWFzaSBub3JtYWxpc2FzaSB0ZXJiYWlrIChzZXBlcnRpIFllby1Kb2huc29uKSBrZSB2YXJpYWJlbCB0ZWthbmFuIGRhcmFoIHVudHVrIG1lbWJ1YXQgZGlzdHJpYnVzaW55YSBsZWJpaCBtZW5kZWthdGkgbm9ybWFsLiB8DQp8IGJlc3ROb3JtYWxpemUoQ2hvbGVzdGVyb2wpJHgudCB8IE1lbmVyYXBrYW4gdHJhbnNmb3JtYXNpIG5vcm1hbGlzYXNpIHRlcmJhaWsga2UgdmFyaWFiZWwga29sZXN0ZXJvbCBhZ2FyIGRpc3RyaWJ1c2lueWEgbGViaWggc2ltZXRyaXMgZGFuIGNvY29rIHVudHVrIGFuYWxpc2lzIHN0YXRpc3RpayBhdGF1IG1hY2hpbmUgbGVhcm5pbmcuIHwNCg0KLS0tDQoNCiMjIyBJbnRlcnByZXRhc2kgS2VzZWhhdGFuIA0KDQp8IEZpdHVyIFRyYW5zZm9ybWFzaSB8IEFydGkgJiBNYW5mYWF0IHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBZZW9KX0JQIHwgTWVuZ2hhc2lsa2FuIHZlcnNpIHRyYW5zZm9ybWFzaSBkYXJpIEJsb29kX1ByZXNzdXJlIHlhbmcgc3VkYWggZGlub3JtYWxpc2FzaSBtZW5nZ3VuYWthbiBtZXRvZGUgWWVvLUpvaG5zb24gYXRhdSBtZXRvZGUgdGVyYmFpayBsYWlubnlhLiBJbmkgcGVudGluZyB1bnR1ayBhbmFsaXNpcyBzdGF0aXN0aWsgeWFuZyBtZW5nYXN1bXNpa2FuIGRpc3RyaWJ1c2kgbm9ybWFsLCBzZXBlcnRpIHJlZ3Jlc2kgbGluaWVyLiB8DQp8IFllb0pfQ2hvbCB8IE1lbmdoYXNpbGthbiB2ZXJzaSB0cmFuc2Zvcm1hc2kgZGFyaSBDaG9sZXN0ZXJvbCB5YW5nIHRlbGFoIGRpbm9ybWFsaXNhc2kuIEluaSBiZXJndW5hIHVudHVrIG1lbmd1cmFuZ2kgcGVuZ2FydWggb3V0bGllciBkYW4gbWVtYnVhdCBkYXRhIGxlYmloIGNvY29rIHVudHVrIHBlbW9kZWxhbi4gfA0KDQotLS0NCg0KIyMjIEhhc2lsIFRyYW5zZm9ybWFzaQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1GQUxTRX0NCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeSh6b28pDQpsaWJyYXJ5KGJlc3ROb3JtYWxpemUpDQpsaWJyYXJ5KERUKQ0KDQpCb3hfQ294IDwtIGhlYWx0aF9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgWWVvSl9CUCA9IGJlc3ROb3JtYWxpemUoQmxvb2RfUHJlc3N1cmUpJHgudCwNCiAgICBZZW9KX0Nob2wgPSBiZXN0Tm9ybWFsaXplKENob2xlc3Rlcm9sKSR4LnQNCiAgKQ0KDQpEVDo6ZGF0YXRhYmxlKEJveF9Db3gsIGNhcHRpb24gPSAiQm94LUNveCAvIFllby1Kb2huc29uIFRyYW5zZm9ybSIpDQpgYGANCg0KDQojIyMgS2VzaW1wdWxhbiANCg0KLSBGdW5nc2kgYmVzdE5vcm1hbGl6ZSgpIHNlY2FyYSBvdG9tYXRpcyBtZW1pbGloIHRyYW5zZm9ybWFzaSB0ZXJiYWlrIChtaXNhbG55YSBZZW8tSm9obnNvbiwgQm94LUNveCwgbG9nLCBhdGF1IGxhaW5ueWEpIHVudHVrIG1lbWJ1YXQgZGlzdHJpYnVzaSBkYXRhIGxlYmloIG1lbmRla2F0aSBub3JtYWwuDQotIFRyYW5zZm9ybWFzaSBpbmkgc2FuZ2F0IGJlcm1hbmZhYXQga2V0aWthIHZhcmlhYmVsIGlucHV0IG1lbWlsaWtpIGRpc3RyaWJ1c2kgc2tld2VkIGF0YXUgbWVuZ2FuZHVuZyBvdXRsaWVyIHlhbmcgZGFwYXQgbWVtcGVuZ2FydWhpIGhhc2lsIGFuYWxpc2lzLg0KLSBQZW5nZ3VuYWFuIG5vcm1hbGlzYXNpIHNlcGVydGkgaW5pIHNlcmluZyBkaXRlcmFwa2FuIHNlYmVsdW0gcmVncmVzaSwga2xhc2lmaWthc2ksIGF0YXUgY2x1c3RlcmluZyBhZ2FyIG1vZGVsIGxlYmloIGFrdXJhdCBkYW4gc3RhYmlsLg0KDQotLS0NCg0KIyMgMi4zIFZhcmlhbmNlIFN0YWJpbGl6YXRpb24NCg0KYGBge30NCg0KVmFyaWFuY2VfU3RhYiA8LSBoZWFsdGhfZGF0YSAlPiUNCiAgbXV0YXRlKA0KICAgIFNxcnRfQ2hvbGVzdGVyb2wgPSBzcXJ0KENob2xlc3Rlcm9sKSwNCiAgICBTcXJ0X0JNSSA9IHNxcnQoQk1JKQ0KICApDQpgYGAgIA0KDQoNCg0KDQojIyMgUGVuamVsYXNhbiBGdW5nc2kgDQoNCnwgRnVuZ3NpICAgICAgICAgICAgICAgICAgICAgICAgIHwgVHVqdWFuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBzcXJ0KENob2xlc3Rlcm9sKSAgICAgICAgICAgIHwgTWVudHJhbnNmb3JtYXNpIGtvbGVzdGVyb2wgZGVuZ2FuIGFrYXIga3VhZHJhdCB1bnR1ayBtZW5zdGFiaWxrYW4gdmFyaWFuc2kuIHwNCnwgc3FydChCTUkpICAgICAgICAgICAgICAgICAgICB8IE1lbnRyYW5zZm9ybWFzaSBpbmRla3MgbWFzc2EgdHVidWggKEJNSSkgZGVuZ2FuIGFrYXIga3VhZHJhdCBhZ2FyIGRpc3RyaWJ1c2lueWEgbGViaWggbm9ybWFsIGRhbiBtZW5ndXJhbmdpIGVmZWsgb3V0bGllci4gfA0KDQotLS0NCg0KIyMjIEludGVycHJldGFzaSBLZXNlaGF0YW4gDQoNCnwgRml0dXIgVHJhbnNmb3JtYXNpIHwgQXJ0aSAmIE1hbmZhYXQgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgU3FydF9DaG9sZXN0ZXJvbCAgIHwgTWVuZ3VyYW5naSBzZWJhcmFuIGVrc3RyZW0gcGFkYSBuaWxhaSBrb2xlc3Rlcm9sIHRpbmdnaS4gSW5pIG1lbWJhbnR1IGRhbGFtIGFuYWxpc2lzIHJlZ3Jlc2kgYXRhdSBtYWNoaW5lIGxlYXJuaW5nIGFnYXIgbW9kZWwgdGlkYWsgdGVybGFsdSBkaXBlbmdhcnVoaSBvbGVoIG91dGxpZXIuIHwNCnwgU3FydF9CTUkgICAgICAgICAgIHwgTWVtYmFudHUgbWVuc3RhYmlsa2FuIHZhcmlhbnMgQk1JIGRhbiBtZW5kZWthdGthbiBkaXN0cmlidXNpIGtlIGJlbnR1ayBub3JtYWwuIEluaSBwZW50aW5nIGppa2EgZGF0YSBha2FuIGRpZ3VuYWthbiBkYWxhbSBtb2RlbCBzdGF0aXN0aWsgeWFuZyBtZW5nYXN1bXNpa2FuIGRpc3RyaWJ1c2kgbm9ybWFsLiB8DQoNCi0tLQ0KDQojIyMgSGFzaWwgSW50ZXJwcmV0YXNpIA0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQ0KDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHpvbykNCmxpYnJhcnkoYmVzdE5vcm1hbGl6ZSkNCmxpYnJhcnkoRFQpDQoNClZhcmlhbmNlX1N0YWIgPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBTcXJ0X0Nob2xlc3Rlcm9sID0gc3FydChDaG9sZXN0ZXJvbCksDQogICAgU3FydF9CTUkgPSBzcXJ0KEJNSSkNCiAgKQ0KDQpEVDo6ZGF0YXRhYmxlKFZhcmlhbmNlX1N0YWIsIGNhcHRpb24gPSAiVmFyaWFuY2UgU3RhYmlsaXphdGlvbiB1c2luZyBzcXJ0KCkiKQ0KYGBgDQoNCg0KIyMjIEtlc2ltcHVsYW4gDQoNCi0gVHJhbnNmb3JtYXNpIGFrYXIga3VhZHJhdCAoc3FydCkgYWRhbGFoIG1ldG9kZSBzZWRlcmhhbmEgZGFuIGVmZWt0aWYgdW50dWsgbWVuc3RhYmlsa2FuIHZhcmlhbnNpIHBhZGEgZGF0YSB5YW5nIHBvc2l0aWYgc2tld2VkIChjb25kb25nIGtlIGthbmFuKS4NCi0gSW5pIHBlbnRpbmcgZGFsYW0gYW5hbGlzaXMgZGF0YSBtZWRpcyBrYXJlbmEgYmFueWFrIG5pbGFpIGJpb21ldHJpayAoc2VwZXJ0aSBrb2xlc3Rlcm9sIGRhbiBCTUkpIHRpZGFrIHRlcmRpc3RyaWJ1c2kgbm9ybWFsIGRhbiBiaXNhIG1lbWlsaWtpIG91dGxpZXIuDQotIFRla25payBpbmkgZGFwYXQgbWVuaW5na2F0a2FuIHBlcmZvcm1hIGRhbiBpbnRlcnByZXRhYmlsaXRhcyBtb2RlbCBzdGF0aXN0aWsgeWFuZyBzZW5zaXRpZiB0ZXJoYWRhcCBiZW50dWsgZGlzdHJpYnVzaSBkYXRhLg0KDQoNCi0tLQ0KDQoNCiMgMy4gU2NhbGluZyAmIE5vcm1hbGl6YXRpb24gDQoNCiMjIDMuMSBTdGFuZGFyZGl6YXRpb24gKFotc2NvcmUpDQoNClByb3NlcyBtZW5ndWJhaCB2YXJpYWJlbCBudW1lcmlrIHlhbmcgbWVtaWxpa2kgc2thbGEgYmVyYmVkYSBtZW5qYWRpIHNrYWxhIHlhbmcgc2FtYSBkZW5nYW4gcmF0YS1yYXRhIDAgZGFuIGRldmlhc2kgc3RhbmRhciAxLg0KDQpgYGB7fQ0KDQojIFotc2NvcmUgTm9ybWFsaXphdGlvbg0KWl9zY29yZSA8LSBoZWFsdGhfZGF0YSAlPiUNCiAgbXV0YXRlKA0KICAgIEFnZV9aID0gc2NhbGUoYWdlKSwNCiAgICBCTUlfWiA9IHNjYWxlKGJtaSksDQogICAgQmxvb2RfUHJlc3N1cmVfWiA9IHNjYWxlKGJsb29kX3ByZXNzdXJlKSwNCiAgICBDaG9sZXN0ZXJvbF9aID0gc2NhbGUoY2hvbGVzdGVyb2wpLA0KICAgIEdsdWNvc2VfWiA9IHNjYWxlKGdsdWNvc2UpDQogICkNCmBgYCAgDQoNCg0KDQojIyMgUGVuamVsYXNhbiBLb2RlIA0KDQp8IEZ1bmdzaSAgICAgICAgICAgICAgICAgICAgICAgICAgfCBUdWp1YW4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IHNjYWxlKEFnZSkgICAgICAgICAgICAgICAgICAgfCBNZW5zdGFuZGFya2FuIHVzaWEgYWdhciBiZXJhZGEgZGFsYW0gc2thbGEgc2F0dWFuLiAgICAgICAgICAgICAgICAgICAgfA0KfCBzY2FsZShCTUkpICAgICAgICAgICAgICAgICAgIHwgTWVuc3RhbmRhcmthbiBuaWxhaSBpbmRla3MgbWFzc2EgdHVidWggKElNVCkuICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgc2NhbGUoQmxvb2RfUHJlc3N1cmUpICAgICAgICB8IE1lbnN0YW5kYXJrYW4gdGVrYW5hbiBkYXJhaCBhZ2FyIHRpZGFrIGRvbWluYW4ga2FyZW5hIHNrYWxhbnlhIGJlc2FyLiB8DQp8IHNjYWxlKENob2xlc3Rlcm9sKSAgICAgICAgICAgfCBNZW5zdGFuZGFya2FuIGtvbGVzdGVyb2wgYWdhciBzZXRhcmEgZGVuZ2FuIGZpdHVyIGxhaW4uICAgICAgICAgICAgICAgfA0KfCBzY2FsZShHbHVjb3NlKSAgICAgICAgICAgICAgIHwgTWVuc3RhbmRhcmthbiBnbHVrb3NhIGFnYXIgYWRpbCBkYWxhbSBtb2RlbCBwcmVkaWtzaS4gICAgICAgICAgICAgICAgIHwNCnwgc2NhbGUoSGVhcnRfUmF0ZSkgICAgICAgICAgICB8IE1lbnN0YW5kYXJrYW4gZGV0YWsgamFudHVuZyBhZ2FyIG1lbWlsaWtpIHNrYWxhIHlhbmcgc2FtYS4gICAgICAgICAgICB8DQoNCg0KIyMjICBJbnRlcnByZXRhc2kgS2VzZWhhdGFuDQoNCnwgRml0dXIgVHJhbnNmb3JtYXNpIHwgQXJ0aSAmIE1hbmZhYXQgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBBZ2Vfc3RkICAgICAgICAgICAgfCBNZW1iYW50dSBtZWxpaGF0IHBlbmdhcnVoIHVzaWEgdGFucGEgZGlwZW5nYXJ1aGkgb2xlaCByZW50YW5nIGJlc2FyIHBhZGEgZGF0YSB1c2lhLiB8DQp8IEJNSV9TdGQgICAgICAgICAgICB8IE1lbWJhbnR1IG1lbmRldGVrc2kgcGFzaWVuIGRlbmdhbiByaXNpa28gb2Jlc2l0YXMgeWFuZyBtZW1lbmdhcnVoaSB0ZWthbmFuIGRhcmFoLiB8DQp8IGJsb29kcHJlYXN1cmVfU3RkICB8IE1lbWJ1YXQgdGVrYW5hbiBkYXJhaCBtZW5qYWRpIGZpdHVyIHlhbmcgc2ViYW5kaW5nIHVudHVrIHByZWRpa3NpIGF0YXUga2xhc2lmaWthc2kuIHwNCnwga29sZXN0cm9sX3N0ZCAgICAgIHwgTWVueWV0YXJha2FuIHBlbmdhcnVoIGtvbGVzdGVyb2wgZGFsYW0gbW9kZWwgcHJlZGlrc2kgaGlwZXJ0ZW5zaS4gfA0KfCBHbHVrb3NhX3N0ZCAgICAgICAgfCBNZW11bmdraW5rYW4gcGVuZ2FydWggZ2x1a29zYSBkaWFtYXRpIHNlY2FyYSBwcm9wb3JzaW9uYWwgdGFucGEgYmlhcyBza2FsYS4gfA0KfCBIZWFydHJhdGVfc3RkICAgICAgfCBNZW1wZXJtdWRhaCBhbmFsaXNpcyB0cmVuIGRldGFrIGphbnR1bmcgdGVyaGFkYXAga29uZGlzaSBsYWluIHNlY2FyYSBzZXRhcmEuIHwNCg0KDQojIyMgSGFzaWwgVHJhbnNmb3JtYXNpDQoNCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCg0KIyBaLVNjb3JlIFN0YW5kYXJkaXphdGlvbg0KDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHpvbykNCmxpYnJhcnkoYmVzdE5vcm1hbGl6ZSkNCmxpYnJhcnkoRFQpDQoNCiMgWi1zY29yZSBOb3JtYWxpemF0aW9uDQpaX3Njb3JlIDwtIGhlYWx0aF9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgQWdlX1ogPSBzY2FsZShhZ2UpLA0KICAgIEJNSV9aID0gc2NhbGUoYm1pKSwNCiAgICBCbG9vZF9QcmVzc3VyZV9aID0gc2NhbGUoYmxvb2RfcHJlc3N1cmUpLA0KICAgIENob2xlc3Rlcm9sX1ogPSBzY2FsZShjaG9sZXN0ZXJvbCksDQogICAgR2x1Y29zZV9aID0gc2NhbGUoZ2x1Y29zZSkNCiAgKQ0KDQojIExpaGF0IGhhc2lsIGF3YWwNCkRUOjpkYXRhdGFibGUoWl9zY29yZSwgY2FwdGlvbiA9ICJaX3Njb3JlKCkiKQ0KDQpgYGANCg0KDQoNCg0KIyMjIEtlc2ltcHVsYW4NCg0KVmFyaWFiZWwtdmFyaWFiZWwgc2VwZXJ0aSBBZ2UsIEJNSSwgQmxvb2QgUHJlc3N1cmUsIENob2xlc3Rlcm9sLCBHbHVjb3NlLCBkYW4gSGVhcnQgUmF0ZSB5YW5nIHNlYmVsdW1ueWEgbWVtaWxpa2kgcmVudGFuZyBza2FsYSB5YW5nIHNhbmdhdCBiZXJiZWRhIHNla2FyYW5nIGFkYSBkaSBza2FsYSB5YW5nIHNhbWEuIFBlbnNrYWxhYW4gaW5pIGJpc2EgbWVtYmFudHUgbW9kZWwgbWFjaGluZSBsZWFybmluZyB1bnR1ayB0aWRhayBjZW5kZXJ1bmcgbWVuZ2FuZ2dhcCBzYXR1IGZpdHVyIGxlYmloIHBlbnRpbmcgZGFyaXBhZGEgeWFuZyBsYWluIGhhbnlhIGthcmVuYSBtZW1pbGlraSByZW50YW5nIHlhbmcgbGViaWggYmVzYXIuDQoNCkRhbGFtIG1lbmRldGVrc2kgcGFzaWVuIHlhbmcgbWVtaWxpa2kgZmFrdG9yIHJpc2lrbyBkYWxhbSBtZW1wZW5nYXJ1aGkgdGVrYW5hbiBkYXJhaCB5YW5nIGFrYW4gbWVuZ2FraWJhdGthbiAgaGlwZXJ0ZW5zaSBiaXNhIGRpbGlhdCBkYXJpIG5pbGFpIEJNSSwga29sZXN0cm9sIGRhbiBnbHVrb3NhLiBrZXRpZ2EgZml0dXIgaXR1IG1lbWlsaWtpIHJlbnRhbmcgc2thbGEgeWFuZyBiZXJiZWRhIGJlZGEgYWRhIHlhbmcgZGkgcHVsdWhhbiBhdGF1cHVuIHJhdHVzYW4uIE1vZGVsIG1lY2hpbmUgbGVhcm5pbmcgYWthbiBtZW1iZXJpIGJvYm90IHBhZGEgZml0dXIga29sZXN0cm9sIGthcmVuYSBtZW1pbGlraSByZW50YW5nIHlhbmcgYmVzYXIuIFNldGVsYWggZGkgc2thbGEga2V0aWdhIGZpdHVyIGl0dSBhZGEgZGkgc2thbGEgc2F0dWFuLiBEaXNpbmkgbW9kZWwgYmlzYSBtZW5pbGFpIGRhcmkga2V0aWdhIGZpdHVyIHRlcnNlYnV0IGFwYWthaCBiaXNhIG1lbXBlbmdhcnVoaSB0ZWthbmFuIGRhcmFoIHRpbmdnaSBhdGF1IHRpZGFrIHlhbmcgYWthbiBtZW5nYWtpYmF0a2FuIGhpcGVydGVuc2kuIA0KDQpLZWt1cmFuZ2FuIFNrYWxhIGluaSBtYXNpaCB0ZXJwZW5nYXJ1aCB0ZXJoYWRhcCBvdXRsaWVyIGRpYmFuZGluZ2thbiBkZW5nYW4gUm9idXN0IFNjYWxlci4gDQoNCg0KIyMgMy4yIFJvYnVzdCBTY2FsZXIgDQoNClByb3NlcyBtZXJ1YmFoIHZhcmlhYmVsIG51bWVyaWsgeWFuZyBtZW1pbGlraSBza2FsYSBiZXJiZWRhIG1lbmphZGkgc2thbGEgeWFuZyBzYW1hIGRhbGFtIGRhdGFzZXQgZGFuIHRhaGFuIHRlcmhhZGFwIG91dGxpZXIuIE5hbXVuLCBtYXNpaCB0ZXJwZW5nYXJ1aGkgb2xlaCBuaWxhaSB5YW5nIHNhbmdhdCBla3N0cmltLiBQZW5za2FsYWFuIGluaSBkaXBlcmt1YXQgZGVuZ2FuIG1lZGlhbiBkYW4gSVFSIGRhcmkgdGlhcCBmaXR1ci4gDQoNCmBgYHt9DQoNCiMgUm9idXN0U2NhbGVyDQoNClJvYnVzdCA8LSBoZWFsdGhfZGF0YSAlPiUNCiAgbXV0YXRlKA0KICAgIEFnZV9yb2J1c3QgPSAoYWdlIC0gbWVkaWFuKGFnZSkpIC8gSVFSKGFnZSksDQogICAgQk1JX3JvYnVzdCA9IChibWkgLSBtZWRpYW4oYm1pKSkgLyBJUVIoYm1pKSwNCiAgICBCbG9vZFByZXNzdXJlX3JvYnVzdCA9IChibG9vZF9wcmVzc3VyZSAtIG1lZGlhbihibG9vZF9wcmVzc3VyZSkpIC8gSVFSKGJsb29kX3ByZXNzdXJlKSwNCiAgICBDaG9sZXN0ZXJvbF9yb2J1c3QgPSAoY2hvbGVzdGVyb2wgLSBtZWRpYW4oY2hvbGVzdGVyb2wpKSAvIElRUihjaG9sZXN0ZXJvbCksDQogICAgR2x1Y29zZV9yb2J1c3QgPSAoZ2x1Y29zZSAtIG1lZGlhbihnbHVjb3NlKSkgLyBJUVIoZ2x1Y29zZSksDQogICAgSGVhcnRSYXRlX3JvYnVzdCA9IChoZWFydF9yYXRlIC0gbWVkaWFuKGhlYXJ0X3JhdGUpKSAvIElRUihoZWFydF9yYXRlKQ0KICApDQpgYGAgIA0KDQoNCg0KIyMjIFBlbmplbGFzYW4gS29kZSANCg0KfCBGdW5nc2kgVHJhbnNmb3JtYXNpICAgICAgICAgICAgICAgICAgICAgICAgfCBUdWp1YW4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgKGFnZSAtIG1lZGlhbihhZ2UpKSAvIElRUihhZ2UpICAgICAgICAgIHwgTWVuc3RhbmRhcmthbiB1c2lhIGJlcmRhc2Fya2FuIG1lZGlhbiBkYW4gSVFSIGFnYXIgdGlkYWsgZGlwZW5nYXJ1aGkgb3V0bGllci4gfA0KfCAoYm1pIC0gbWVkaWFuKGJtaSkpIC8gSVFSKGJtaSkgICAgICAgICAgfCBNZW55ZXN1YWlrYW4gbmlsYWkgQk1JIGtlIHNrYWxhIHlhbmcgbGViaWggbmV0cmFsIHRlcmhhZGFwIG5pbGFpIGVrc3RyZW0uICAgICB8DQp8IChibG9vZF9wcmVzc3VyZSAtIG1lZGlhbiguLi4pKSAvIElRUiguLi4pIHwgTWVuZ3ViYWggdGVrYW5hbiBkYXJhaCBrZSBza2FsYSB5YW5nIHN0YWJpbCBkYW4gdGFoYW4gdGVyaGFkYXAgbG9uamFrYW4gbmlsYWkuIHwNCnwgKGNob2xlc3Rlcm9sIC0gbWVkaWFuKC4uLikpIC8gSVFSKC4uLikgIHwgTWVuZ2F0dXIga29sZXN0ZXJvbCBhZ2FyIHNldGFyYSBza2FsYW55YSBkZW5nYW4gZml0dXIgbGFpbiBtZXNraXB1biBvdXRsaWVyIGFkYS4gfA0KfCAoZ2x1Y29zZSAtIG1lZGlhbiguLi4pKSAvIElRUiguLi4pICAgICAgfCBNZW5vcm1hbGthbiBrYWRhciBndWxhIGRhcmFoIGFnYXIgdGlkYWsgZGlkb21pbmFzaSBvbGVoIG5pbGFpIGVrc3RyZW0uICAgICAgIHwNCnwgKGhlYXJ0X3JhdGUgLSBtZWRpYW4oLi4uKSkgLyBJUVIoLi4uKSAgIHwgTWVuc3RhYmlsa2FuIG5pbGFpIGRldGFrIGphbnR1bmcgdW50dWsgYW5hbGlzaXMga29tcGFyYXRpZiBhbnRhciBwYXNpZW4uICAgICB8DQoNCg0KIyMjIEludGVycHJldGFzaSBLZXNlaGF0YW4NCg0KfCBGaXR1ciBUcmFuc2Zvcm1hc2kgICAgICB8IEFydGkgJiBNYW5mYWF0IHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBBZ2Vfcm9idXN0ICAgICAgICAgICAgICAgIHwgVXNpYSBwYXNpZW4ga2luaSBkaXN0YW5kYXJpc2FzaSB0YW5wYSBkaXBlbmdhcnVoaSBla3N0cmVtIHVtdXIgeWFuZyB0ZXJsYWx1IG11ZGEgYXRhdSB0dWEuIEJlcmd1bmEgdW50dWsgbW9kZWwgcHJlZGlrdGlmIHlhbmcgc2Vuc2l0aWYgdGVyaGFkYXAgdmFyaWFzaSB1c2lhLiB8DQp8IEJNSV9yb2J1c3QgICAgICAgICAgICAgICAgfCBNZW5pbGFpIHJpc2lrbyBrZWxlYmloYW4gYmVyYXQgYmFkYW4gdGVyaGFkYXAgaGlwZXJ0ZW5zaSBzZWNhcmEgYWRpbCBkYW4gbmV0cmFsLiB8DQp8IEJsb29kUHJlc3N1cmVfcm9idXN0ICAgICAgfCBNZW1iZXJpIGdhbWJhcmFuIHRla2FuYW4gZGFyYWggeWFuZyBhZGlsIHNhYXQgZGliYW5kaW5na2FuIGFudGFyIHBhc2llbiBkZW5nYW4gZGF0YSBiZXJ2YXJpYXNpLiB8DQp8IENob2xlc3Rlcm9sX3JvYnVzdCAgICAgICAgfCBNZW5naGluZGFyaSBiaWFzIG1vZGVsIHRlcmhhZGFwIHBhc2llbiBkZW5nYW4ga2FkYXIga29sZXN0ZXJvbCBzYW5nYXQgdGluZ2dpLiB8DQp8IEdsdWNvc2Vfcm9idXN0ICAgICAgICAgICAgfCBNZW1hc3Rpa2FuIG1vZGVsIG1lbGloYXQgcG9sYSBrYWRhciBnbHVrb3NhIHRhbnBhIHRlcnRpcHUgbmlsYWkgeWFuZyB0ZXJsYWx1IGVrc3RyZW0uIHwNCnwgSGVhcnRSYXRlX3JvYnVzdCAgICAgICAgICB8IE1lbmphZGlrYW4gZGV0YWsgamFudHVuZyBzZXRhcmEgc2VjYXJhIHNrYWxhIGRlbmdhbiBmaXR1ciBsYWluIHVudHVrIGFuYWxpc2lzIHByZWRpa3NpIGtlc2VoYXRhbiBqYW50dW5nLiB8DQoNCg0KIyMjIEhhc2lsIFRyYW5zZm9ybWFzaQ0KDQpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQoNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoem9vKQ0KbGlicmFyeShiZXN0Tm9ybWFsaXplKQ0KbGlicmFyeShEVCkNCg0KIyBSb2J1c3RTY2FsZXINCg0KUm9idXN0IDwtIGhlYWx0aF9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgQWdlX3JvYnVzdCA9IChhZ2UgLSBtZWRpYW4oYWdlKSkgLyBJUVIoYWdlKSwNCiAgICBCTUlfcm9idXN0ID0gKGJtaSAtIG1lZGlhbihibWkpKSAvIElRUihibWkpLA0KICAgIEJsb29kUHJlc3N1cmVfcm9idXN0ID0gKGJsb29kX3ByZXNzdXJlIC0gbWVkaWFuKGJsb29kX3ByZXNzdXJlKSkgLyBJUVIoYmxvb2RfcHJlc3N1cmUpLA0KICAgIENob2xlc3Rlcm9sX3JvYnVzdCA9IChjaG9sZXN0ZXJvbCAtIG1lZGlhbihjaG9sZXN0ZXJvbCkpIC8gSVFSKGNob2xlc3Rlcm9sKSwNCiAgICBHbHVjb3NlX3JvYnVzdCA9IChnbHVjb3NlIC0gbWVkaWFuKGdsdWNvc2UpKSAvIElRUihnbHVjb3NlKSwNCiAgICBIZWFydFJhdGVfcm9idXN0ID0gKGhlYXJ0X3JhdGUgLSBtZWRpYW4oaGVhcnRfcmF0ZSkpIC8gSVFSKGhlYXJ0X3JhdGUpDQogICkNCg0KIyBMaWhhdCBoYXNpbCBhd2FsDQpEVDo6ZGF0YXRhYmxlKFJvYnVzdCwgY2FwdGlvbiA9ICJSb2J1c3QgU2NhbGVyKCkiKQ0KYGBgDQoNCg0KDQojIyMgS2VzaW1wdWxhbiANCg0KU2FtYSBoYWwgbnlhIGRlbmdhbiBTa2FsYSBTdGFuZGFyaXNhc2ksIFJvYnVzdCBzY2FsZXIgbWVtaWxpa2kgc2thbGEgeWFuZyBsZWJpaCBrZWNpbCBkaWJhbmRpbmcgZGVuZ2FuIFNrYWxhIChzdGFuZGFyaXNhc2kpIGthcmVuYSBtZWRpYW4gZGFuIElRUiB0aWRhayBkaXBlbmdhcnVoaSBvbGVoIG5pbGFpIGVrc3RyZW0geWFuZyBqYXVoIGRhcmkgZGlzdHJpYnVzaSBkYXRhIGxhaW5ueWEuIEluaSBtZW5qYWRpa2FubnlhIGxlYmloIHN0YWJpbCBzYWF0IGRhdGEgbWVtaWxpa2kgb3V0bGllci4NCg0KSW5pIG1lbWJ1YXQgbW9kZWwgbWFtcHUgbWVuZ2V2YWx1YXNpIHBlbmdhcnVoIEJNSSwga29sZXN0ZXJvbCwgZGFuIGdsdWtvc2Egc2VjYXJhIGFkaWwgdGVyaGFkYXAgdGVrYW5hbiBkYXJhaCwgYmFpayBzaXN0b2xpayBtYXVwdW4gZGlhc3RvbGlrLiBBa2hpcm55YSwgaW5pIG1lbmR1a3VuZyBwcm9zZXMgZGV0ZWtzaSByaXNpa28gaGlwZXJ0ZW5zaSBkZW5nYW4gcGVuZGVrYXRhbiBzdGF0aXN0aWsgeWFuZyBsZWJpaCBha3VyYXQgZGFuIHRpZGFrIGJpYXMuDQoNCiMjIDMuMyBOb3JtYWxpemF0aW9uIA0KDQpQcm9zZXMgbWVydWJhaCBza2FsYSBkYWxhbSByZW50YW5nIHRlcnRlbnR1IHVtdW1ueWEgMC0xIGRlbmdhbiBtZW5nZ3VuYWthbiBuaWxhaSBtaW5pbXVtIGRhbiBtYWtzaW11bSB5YW5nIG1lbmdoYXNpbGthbiBrZXNlaW1iYW5nYW4gZGF0YS4gTm9ybWFsaXNhc2kgaW5pIGFrYW4gc2FuZ2F0IHRlcnBlbmdhcnVoIG9sZWggbmlsYWkgZWtzdHJlbS4NCg0KYGBge30NCg0KIyBNaW4tTWF4IE5vcm1hbGl6YXRpb24NCk1pbl9NYXggPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBBZ2VfTm9ybSA9IChBZ2UgLSBtaW4oQWdlLCBuYS5ybSA9IFRSVUUpKSAvIChtYXgoQWdlLCBuYS5ybSA9IFRSVUUpIC0gbWluKEFnZSwgbmEucm0gPSBUUlVFKSksDQogICAgQk1JX05vcm0gPSAoQk1JIC0gbWluKEJNSSwgbmEucm0gPSBUUlVFKSkgLyAobWF4KEJNSSwgbmEucm0gPSBUUlVFKSAtIG1pbihCTUksIG5hLnJtID0gVFJVRSkpLA0KICAgIEJsb29kX1ByZXNzdXJlX05vcm0gPSAoQmxvb2RfUHJlc3N1cmUgLSBtaW4oQmxvb2RfUHJlc3N1cmUsIG5hLnJtID0gVFJVRSkpIC8gKG1heChCbG9vZF9QcmVzc3VyZSwgbmEucm0gPSBUUlVFKSAtIG1pbihCbG9vZF9QcmVzc3VyZSwgbmEucm0gPSBUUlVFKSksDQogICAgQ2hvbGVzdGVyb2xfTm9ybSA9IChDaG9sZXN0ZXJvbCAtIG1pbihDaG9sZXN0ZXJvbCwgbmEucm0gPSBUUlVFKSkgLyAobWF4KENob2xlc3Rlcm9sLCBuYS5ybSA9IFRSVUUpIC0gbWluKENob2xlc3Rlcm9sLCBuYS5ybSA9IFRSVUUpKSwNCiAgICBHbHVjb3NlX05vcm0gPSAoR2x1Y29zZSAtIG1pbihHbHVjb3NlLCBuYS5ybSA9IFRSVUUpKSAvIChtYXgoR2x1Y29zZSwgbmEucm0gPSBUUlVFKSAtIG1pbihHbHVjb3NlLCBuYS5ybSA9IFRSVUUpKQ0KICApDQpgYGAgIA0KDQoNCg0KIyMjIFBlbmplbGFzYW4gS29kZSANCg0KfCBGdW5nc2kgfCBUdWp1YW4gfA0KfC0tLS0tLS0tfC0tLS0tLS0tfA0KfCAoeCAtIG1pbih4KSkgLyAobWF4KHgpIC0gbWluKHgpKSB8IE1lbmd1YmFoIHNrYWxhIGRhdGEgYWdhciBiZXJhZGEgZGFsYW0gcmVudGFuZyAwIGhpbmdnYSAxLCB0YW5wYSBtZW5ndWJhaCBiZW50dWsgZGlzdHJpYnVzaSBhc2xpbnlhLiBCZXJndW5hIHVudHVrIGFsZ29yaXRtYSBtYWNoaW5lIGxlYXJuaW5nIHlhbmcgc2Vuc2l0aWYgdGVyaGFkYXAgc2thbGEsIHNlcGVydGkgS05OIGRhbiBTVk0uIHwNCnwgbmEucm0gPSBUUlVFIHwgTWVuZ2FiYWlrYW4gbmlsYWkgbWlzc2luZyBhZ2FyIHByb3NlcyBub3JtYWxpc2FzaSB0ZXRhcCBiZXJqYWxhbiB0YW5wYSBlcnJvci4gfA0KDQoNCiMjIyBJbnRlcnByZXRhc2kgS2VzZWhhdGFuDQoNCnwgRml0dXIgVGVybm9ybWFsaXNhc2kgfCBBcnRpICYgTWFuZmFhdCB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgQWdlX05vcm0gICAgICAgICAgICAgICB8IE1lbmdvbnZlcnNpIHVtdXIgcGFzaWVuIGtlIHNrYWxhIDDigJMxIHVudHVrIG1lbWFzdGlrYW4gbW9kZWwgdGlkYWsgYmlhcyB0ZXJoYWRhcCByZW50YW5nIHVzaWEgeWFuZyBsdWFzLiB8DQp8IEJNSV9Ob3JtICAgICAgICAgICAgICAgfCBNZW1hc3Rpa2FuIG5pbGFpIGluZGVrcyBtYXNzYSB0dWJ1aCBkaWJhbmRpbmdrYW4gc2VjYXJhIGFkaWwgZGVuZ2FuIGZpdHVyIGxhaW4uIHwNCnwgQmxvb2RfUHJlc3N1cmVfTm9ybSAgICB8IE1lbnN0YW5kYXJrYW4gdGVrYW5hbiBkYXJhaCBhZ2FyIGJpc2EgZGliYW5kaW5na2FuIGxhbmdzdW5nIGRlbmdhbiB2YXJpYWJlbCBsYWluIGRhbGFtIG1vZGVsIHByZWRpa3RpZi4gfA0KfCBDaG9sZXN0ZXJvbF9Ob3JtICAgICAgIHwgTWVtYnVhdCBrYWRhciBrb2xlc3Rlcm9sIGRhcGF0IGRpaW50ZXJwcmV0YXNpa2FuIGRhbGFtIHNrYWxhIHNlcmFnYW0uIHwNCnwgR2x1Y29zZV9Ob3JtICAgICAgICAgICB8IE1lbWJhbnR1IGRhbGFtIG1lbnllaW1iYW5na2FuIGZpdHVyIGdsdWtvc2EgZGVuZ2FuIGZpdHVyIGxhaW5ueWEgdGFucGEgbWVtcGVyYmVzYXIgYm9ib3RueWEuIHwNCg0KDQojIyMgSGFzaWwgVHJhbnNmb3JtYXNpDQoNCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHpvbykNCmxpYnJhcnkoYmVzdE5vcm1hbGl6ZSkNCmxpYnJhcnkoRFQpDQoNCiMgUGFzdGlrYW4gbmFtYSBrb2xvbSBzdWRhaCBiZXJzaWgNCmNvbG5hbWVzKGhlYWx0aF9kYXRhKSA8LSBtYWtlLm5hbWVzKG5hbWVzKGhlYWx0aF9kYXRhKSkNCg0KDQojIE1pbi1NYXggTm9ybWFsaXphdGlvbg0KTWluX01heCA8LSBoZWFsdGhfZGF0YSAlPiUNCiAgbXV0YXRlKA0KICAgIEFnZV9Ob3JtID0gKEFnZSAtIG1pbihBZ2UsIG5hLnJtID0gVFJVRSkpIC8gKG1heChBZ2UsIG5hLnJtID0gVFJVRSkgLSBtaW4oQWdlLCBuYS5ybSA9IFRSVUUpKSwNCiAgICBCTUlfTm9ybSA9IChCTUkgLSBtaW4oQk1JLCBuYS5ybSA9IFRSVUUpKSAvIChtYXgoQk1JLCBuYS5ybSA9IFRSVUUpIC0gbWluKEJNSSwgbmEucm0gPSBUUlVFKSksDQogICAgQmxvb2RfUHJlc3N1cmVfTm9ybSA9IChCbG9vZF9QcmVzc3VyZSAtIG1pbihCbG9vZF9QcmVzc3VyZSwgbmEucm0gPSBUUlVFKSkgLyAobWF4KEJsb29kX1ByZXNzdXJlLCBuYS5ybSA9IFRSVUUpIC0gbWluKEJsb29kX1ByZXNzdXJlLCBuYS5ybSA9IFRSVUUpKSwNCiAgICBDaG9sZXN0ZXJvbF9Ob3JtID0gKENob2xlc3Rlcm9sIC0gbWluKENob2xlc3Rlcm9sLCBuYS5ybSA9IFRSVUUpKSAvIChtYXgoQ2hvbGVzdGVyb2wsIG5hLnJtID0gVFJVRSkgLSBtaW4oQ2hvbGVzdGVyb2wsIG5hLnJtID0gVFJVRSkpLA0KICAgIEdsdWNvc2VfTm9ybSA9IChHbHVjb3NlIC0gbWluKEdsdWNvc2UsIG5hLnJtID0gVFJVRSkpIC8gKG1heChHbHVjb3NlLCBuYS5ybSA9IFRSVUUpIC0gbWluKEdsdWNvc2UsIG5hLnJtID0gVFJVRSkpDQogICkNCg0KIyBUYW1waWxrYW4gdGFiZWwgaW50ZXJha3RpZiBoYXNpbCBub3JtYWxpc2FzaQ0KRFQ6OmRhdGF0YWJsZShNaW5fTWF4LCBjYXB0aW9uID0gIk1pbi1NYXggTm9ybWFsaXphdGlvbiBvZiBIZWFsdGggRGF0YSIpDQpgYGANCg0KDQoNCiMjIyBLZXNpbXB1bGFuIA0KDQpTYW1hIGhhbCBueWEgZGVuZ2FuIFNjYWxpbmcsIG5vcm1hbGlzYXNpIGluaSB1bnR1ayBtZXJ1YmFoIHJlbnRhbmcgYWdhciBtb2RlbCBiaXNhIG1lbmdhbmdnYXAgZGF0YSBkaSBzZXRpYXAgZml0dXIgbnlhIG1lbWlsaWtpIGJvYm90IHlhbmcgc2FtYS4gSGFueWEgc2FqYSBqaWthIG5vcm1hbGlzYXNpIGRhbGFtIHJlbnRhbmcgZGlhbnRhcmEgMCBzYW1wYWkgMS4gU2VkYW5na2FuLCBza2FsYSBtYXNpaCBhZGEgbmlsYWkgeWFuZyByZW50YW5nIGRpYXRhcyBuaWxhaSAxLiANCg0KS2VsZWJpaGFuIGRhbGFtIG5vcm1hbGlzYXNpIHlhaXR1IG1lbmphZ2EgZGF0YSBkYWxhbSByZW50YW5nIHlhbmcgc2VyYWdhbSB0YW5wYSBkaXBlbmdhcnVoaSBvdXRsaWVyIHNlY2FyYSBla3N0cmVtLiBkYW4gS2VsZW1haGFubnlhIHlhaXR1IHNhbmdhdCBzZW5zaXRpZiB0ZXJoYWRhcCBvdXRsaWVyLCBrYXJlbmEgbWluKCkgZGFuIG1heCgpIGJpc2EgdGVycGVuZ2FydWggbmlsYWkgZWtzdHJlbS4NCg0KDQoNCiMgNC4gS2F0ZWdvcmlhbCBFbmNvZGluZyANCg0KIyMgNC4xIE9uZS1Ib3QgRW5jb2RpbmcNCg0KT25lLWhvdCBlbmNvZGluZyBhZGFsYWggbWV0b2RlIHRyYW5zZm9ybWFzaSB2YXJpYWJlbCBrYXRlZ29yaWsgbWVuamFkaSBiZW50dWsgbnVtZXJpayBiaW5lciAoMC8xKSBhZ2FyIGJpc2EgZGlndW5ha2FuIGRhbGFtIGFuYWxpc2lzIHN0YXRpc3RpayBhdGF1IG1hY2hpbmUgbGVhcm5pbmcuDQoNCiMjIyBLZWxlYmloYW46DQotIE1lbmdoaWxhbmdrYW4gbWFrbmEgb3JkaW5hbCBkYXJpIGthdGVnb3JpICh0aWRhayBtZW5nYXN1bXNpa2FuIHVydXRhbikuDQotIENvY29rIHVudHVrIGFsZ29yaXRtYSB5YW5nIHRpZGFrIGJpc2EgbWVuYW5nYW5pIGRhdGEga2F0ZWdvcmlrIHNlY2FyYSBsYW5nc3VuZy4NCg0KIyMjIEtla3VyYW5nYW46DQotIE1lbmFtYmFoIGp1bWxhaCBmaXR1ciBzZWNhcmEgc2lnbmlmaWthbiAodGVydXRhbWEgamlrYSBrYXRlZ29yaSBiYW55YWspLg0KLSBCaXNhIG1lbnllYmFia2FuIGN1cnNlIG9mIGRpbWVuc2lvbmFsaXR5Lg0KDQoqQ29udG9oIFBlbmdndW5hYW4gZGFsYW0gUjoqDQoNCmBgYHt9DQoNCmNhdGVnb3JpY2FsX2NvbHMgPC0gbmFtZXMoaGVhbHRoX2RhdGEpW3NhcHBseShoZWFsdGhfZGF0YSwgZnVuY3Rpb24oeCkgaXMuZmFjdG9yKHgpIHx8IGlzLmNoYXJhY3Rlcih4KSldDQpvbmVfaG90IDwtIGR1bW15X2NvbHMoDQogIGhlYWx0aF9kYXRhLA0KICBzZWxlY3RfY29sdW1ucyA9IGNhdGVnb3JpY2FsX2NvbHMsDQogIHJlbW92ZV9maXJzdF9kdW1teSA9IFRSVUUsDQogIHJlbW92ZV9zZWxlY3RlZF9jb2x1bW5zID0gVFJVRQ0KKQ0KYGBgDQoNCg0KDQojIyMgUGVuamVsYXNhbiBGdW5nc2kgDQoNCnwgRnVuZ3NpIHwgVHVqdWFuIHwNCnwtLS0tLS0tLXwtLS0tLS0tLXwNCnwgZHVtbXlfY29scygpIHwgTWVtYnVhdCBrb2xvbSBkdW1teSBkYXJpIGRhdGEga2F0ZWdvcmlrLiB8DQp8IHJlbW92ZV9maXJzdF9kdW1teSA9IFRSVUUgfCBNZW5naGluZGFyaSBkdW1teSB2YXJpYWJsZSB0cmFwIChtdWx0aWtvbGluZWFyaXRhcykgZGVuZ2FuIG1lbmdoYXB1cyBzYXR1IGthdGVnb3JpLiB8DQp8IHJlbW92ZV9zZWxlY3RlZF9jb2x1bW5zID0gVFJVRSB8IE1lbmdoYXB1cyBrb2xvbSBrYXRlZ29yaWsgYXNsaSBkYXJpIGRhdGFzZXQuIHwNCg0KDQojIyMgSW50ZXJwcmV0YXNpIEtlc2VoYXRhbjoNCg0KfCBGaXR1ciBPbmUtSG90IHwgTWFrbmEgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS18DQp8IExvY2F0aW9uX0JhbmR1bmcgPSAxIHwgUGFzaWVuIGJlcmFzYWwgZGFyaSBCYW5kdW5nLiB8DQp8IEhlYWx0aF9Db25kaXRpb25fRGlhYmV0ZXMgPSAxIHwgUGFzaWVuIG1lbWlsaWtpIGtvbmRpc2kgZGlhYmV0ZXMuIHwNCg0KDQojIyMgSGFzaWwgVHJhbnNmb3JtYXNpDQoNCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGZhc3REdW1taWVzKQ0KbGlicmFyeShEVCkNCg0KY2F0ZWdvcmljYWxfY29scyA8LSBuYW1lcyhoZWFsdGhfZGF0YSlbc2FwcGx5KGhlYWx0aF9kYXRhLCBmdW5jdGlvbih4KSBpcy5mYWN0b3IoeCkgfHwgaXMuY2hhcmFjdGVyKHgpKV0NCm9uZV9ob3QgPC0gZHVtbXlfY29scygNCiAgaGVhbHRoX2RhdGEsDQogIHNlbGVjdF9jb2x1bW5zID0gY2F0ZWdvcmljYWxfY29scywNCiAgcmVtb3ZlX2ZpcnN0X2R1bW15ID0gVFJVRSwNCiAgcmVtb3ZlX3NlbGVjdGVkX2NvbHVtbnMgPSBUUlVFDQopDQoNCkRUOjpkYXRhdGFibGUob25lX2hvdCwgY2FwdGlvbiA9ICJIYXNpbCBPbmUtSG90IEVuY29kaW5nIChUYW5wYSBLb2xvbSBQZXJ0YW1hIGRhbiBBc2xpKSIpDQpgYGANCg0KDQoNCiMjIyBLZXNpbXB1bGFuOg0KDQotIFRyYW5zZm9ybWFzaSBpbmkgbWVtcGVybXVkYWggcGVuZ2d1bmFhbiBkYXRhIGthdGVnb3JpayBkYWxhbSBtb2RlbCBwcmVkaWtzaSBkYW4gYW5hbGlzaXMgc3RhdGlzdGlrLg0KDQotIE1lbmdoaW5kYXJpIGtlc2FsYWhhbiBpbnRlcnByZXRhc2kgdXJ1dGFuIHBhZGEgdmFyaWFiZWwga2F0ZWdvcmlrLg0KDQotIENvY29rIGRpZ3VuYWthbiBiZXJzYW1hIG1ldG9kZSBzdGF0aXN0aWsgZGFuIG1hY2hpbmUgbGVhcm5pbmcgbW9kZXJuLg0KDQoNCiMjIDQuMiBGcmVxdWVuY3kgRW5jb2RpbmcNCg0KRnJlcXVlbmN5IGVuY29kaW5nIGFkYWxhaCBtZXRvZGUgcGVuZ2tvZGVhbiBkYXRhIGthdGVnb3JpayBkZW5nYW4gbWVuZ2dhbnRpIHNldGlhcCBrYXRlZ29yaSBkZW5nYW4gZnJla3VlbnNpbnlhIGRhbGFtIGRhdGEuIEFydGlueWEsIG5pbGFpIGthdGVnb3JpIHlhbmcgbGViaWggc2VyaW5nIG11bmN1bCBha2FuIG1lbmRhcGF0a2FuIG5pbGFpIG51bWVyaWsgeWFuZyBsZWJpaCB0aW5nZ2kuDQoNCiMjIyBLZWxlYmloYW46DQotIFRpZGFrIG1lbmFtYmFoIGRpbWVuc2kgc2VwZXJ0aSBvbmUtaG90IGVuY29kaW5nLg0KDQotIE1lbnlpbXBhbiBpbmZvcm1hc2kgZGlzdHJpYnVzaSBrYXRlZ29yaS4NCg0KLSBFZmlzaWVuIHVudHVrIGRhdGFzZXQgZGVuZ2FuIGJhbnlhayBrYXRlZ29yaSB1bmlrLg0KDQojIyMgS2VrdXJhbmdhbjoNCi0gVGlkYWsgY29jb2sgamlrYSBrYXRlZ29yaSBkZW5nYW4gZnJla3VlbnNpIHlhbmcgc2FtYSBtZW1pbGlraSBtYWtuYSBiZXJiZWRhLg0KDQotIEJpc2EgbWVtdW5jdWxrYW4gZGF0YSBsZWFrYWdlIGppa2EgdGlkYWsgZGlsYWt1a2FuIGRlbmdhbiBoYXRpLWhhdGkgKG1pc2FsbnlhIHNhYXQgZGlndW5ha2FuIHNlYmVsdW0gZGF0YSBzcGxpdCkuDQoNCipDb250b2ggUGVuZ2d1bmFhbiBkYWxhbSBSOioNCg0KYGBge30NCg0KIyBGdW5nc2kgZnJlcXVlbmN5IGVuY29kaW5nDQpmcmVxX2VuYyA8LSBmdW5jdGlvbihjb2wpIHsNCiAgdGFiIDwtIHRhYmxlKGNvbCkNCiAgcmV0dXJuKGFzLm51bWVyaWModGFiW2NvbF0pIC8gbGVuZ3RoKGNvbCkpDQp9DQoNCiMgUGFzdGlrYW4ga29sb20gbG93ZXJjYXNlDQpjb2xuYW1lcyhoZWFsdGhfZGF0YSkgPC0gdG9sb3dlcihjb2xuYW1lcyhoZWFsdGhfZGF0YSkpDQoNCiMgS29sb20geWFuZyBpbmdpbiBkaS1lbmNvZGUNCnRhcmdldF9jb2xzIDwtIGMoInllYXIiLCAibG9jYXRpb24iLCAiaGVhbHRoX2NvbmRpdGlvbiIpDQphdmFpbGFibGVfY29scyA8LSBpbnRlcnNlY3QodGFyZ2V0X2NvbHMsIGNvbG5hbWVzKGhlYWx0aF9kYXRhKSkNCg0KIyBFbmNvZGluZyBmcmVrdWVuc2kNCkZyZXF1ZW5jeSA8LSBoZWFsdGhfZGF0YSAlPiUNCiAgbXV0YXRlKGFjcm9zcyhhbGxfb2YoYXZhaWxhYmxlX2NvbHMpLCB+IGZyZXFfZW5jKC4pLCAubmFtZXMgPSAiey5jb2x9X2ZyZXEiKSkNCmBgYA0KDQoNCg0KIyMjIFBlbmplbGFzYW4gRnVuZ3NpIA0KDQoNCnwgRnVuZ3NpIHwgVHVqdWFuIHwNCnwtLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCB0YWJsZShjb2wpIHwgTWVuZ2hpdHVuZyBqdW1sYWgga2VtdW5jdWxhbiB0aWFwIGthdGVnb3JpLiB8DQp8IGFzLm51bWVyaWModGFiW2NvbF0pIC8gbGVuZ3RoKGNvbCkgfCBNZW5ndWJhaCBuaWxhaSBrYXRlZ29yaSBtZW5qYWRpIHByb3BvcnNpIGtlbXVuY3VsYW5ueWEuIHwNCnwgYWNyb3NzKC4uLiwgLm5hbWVzID0gInsuY29sfV9mcmVxIikgfCBNZW5hbWJhaGthbiBuYW1hIGtvbG9tIGhhc2lsIGVuY29kaW5nLiB8DQoNCg0KIyMjIEludGVycHJldGFzaSBLZXNlaGF0YW46DQoNCnwgRml0dXIgRW5jb2RlZCB8IE1ha25hIHwNCnwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgbG9jYXRpb25fZnJlcSA9IDAuMzAgfCBMb2thc2kgdGVyc2VidXQgbXVuY3VsIHNlYmFueWFrIDMwJSBkYXJpIHRvdGFsIGRhdGEuIHwNCnwgaGVhbHRoX2NvbmRpdGlvbl9mcmVxID0gMC4xMCB8IEtvbmRpc2kga2VzZWhhdGFuIHRlcnNlYnV0IGhhbnlhIG11bmN1bCBkaSAxMCUgZGF0YS4gfA0KDQoNCiMjIyBIYXNpbCBUcmFuc2Zvcm1hc2kNCg0KYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShEVCkNCg0KIyBGdW5nc2kgZnJlcXVlbmN5IGVuY29kaW5nDQpmcmVxX2VuYyA8LSBmdW5jdGlvbihjb2wpIHsNCiAgdGFiIDwtIHRhYmxlKGNvbCkNCiAgcmV0dXJuKGFzLm51bWVyaWModGFiW2NvbF0pIC8gbGVuZ3RoKGNvbCkpDQp9DQoNCiMgUGFzdGlrYW4ga29sb20gbG93ZXJjYXNlDQpjb2xuYW1lcyhoZWFsdGhfZGF0YSkgPC0gdG9sb3dlcihjb2xuYW1lcyhoZWFsdGhfZGF0YSkpDQoNCiMgS29sb20geWFuZyBpbmdpbiBkaS1lbmNvZGUNCnRhcmdldF9jb2xzIDwtIGMoInllYXIiLCAibG9jYXRpb24iLCAiaGVhbHRoX2NvbmRpdGlvbiIpDQphdmFpbGFibGVfY29scyA8LSBpbnRlcnNlY3QodGFyZ2V0X2NvbHMsIGNvbG5hbWVzKGhlYWx0aF9kYXRhKSkNCg0KIyBFbmNvZGluZyBmcmVrdWVuc2kNCkZyZXF1ZW5jeSA8LSBoZWFsdGhfZGF0YSAlPiUNCiAgbXV0YXRlKGFjcm9zcyhhbGxfb2YoYXZhaWxhYmxlX2NvbHMpLCB+IGZyZXFfZW5jKC4pLCAubmFtZXMgPSAiey5jb2x9X2ZyZXEiKSkNCg0KIyBUYW1waWxrYW4gaGFzaWwNCkRUOjpkYXRhdGFibGUoRnJlcXVlbmN5LCBjYXB0aW9uID0gIkZyZXF1ZW5jeSBFbmNvZGluZyIpDQpgYGANCg0KDQoNCiMjIyBLZXNpbXB1bGFuOg0KDQotIEZyZXF1ZW5jeSBlbmNvZGluZyBtZW1iZXJpa2FuIGJvYm90IHByb3BvcnNpb25hbCB0ZXJoYWRhcCBrZW11bmN1bGFuIGthdGVnb3JpLg0KDQotIExlYmloIHJpbmdhbiBzZWNhcmEga29tcHV0YXNpIGRpYmFuZGluZyBvbmUtaG90IGVuY29kaW5nLCBuYW11biB0ZXRhcCBtZW55aW1wYW4gbWFrbmEgc3RhdGlzdGlrIGRhcmkgZGF0YSBrYXRlZ29yaWsuDQoNCi0gQ29jb2sgZGlndW5ha2FuIGRhbGFtIG1vZGVsIGxpbmVhciBkYW4gdHJlZS1iYXNlZCBzZXBlcnRpIFJhbmRvbSBGb3Jlc3QgZGFuIFhHQm9vc3QuDQoNCg0KIyA1LiBGZWF0dXJlIEVuZ2luZWVyaW5nDQoNCkZlYXR1cmUgZW5naW5lZXJpbmcgYWRhbGFoIHByb3NlcyBtZW5jaXB0YWthbiBmaXR1ci1maXR1ciBiYXJ1IGRhcmkgZGF0YSB5YW5nIGFkYSB1bnR1ayBtZW5pbmdrYXRrYW4gcGVyZm9ybWEgbW9kZWwgbWFjaGluZSBsZWFybmluZy4gU2FsYWggc2F0dSB0ZWtuaWsgeWFuZyBzZXJpbmcgZGlndW5ha2FuIGFkYWxhaCAqaW50ZXJhY3Rpb24gZmVhdHVyZXMqLCB5YWl0dSBtZW5nZ2FidW5na2FuIGR1YSBhdGF1IGxlYmloIGZpdHVyIHVudHVrIG1lbWJlbnR1ayBmaXR1ciBiYXJ1IHlhbmcgYmlzYSBtZW5hbmdrYXAgaHVidW5nYW4gbm9ubGluaWVyIGFudGFyIHZhcmlhYmVsLg0KDQoNCiMjIDUuMSBJbnRlcmFjdGlvbiBGZWF0dXJlcw0KDQpJbnRlcmFjdGlvbiBmZWF0dXJlcyBkaWJ1YXQgZGVuZ2FuIG1lbmdhbGlrYW4gYXRhdSBtZW5nZ2FidW5na2FuIGR1YSBmaXR1ciB1bnR1ayBtZXJlcHJlc2VudGFzaWthbiBodWJ1bmdhbiBhbnRhcmEga2VkdWFueWEuIERhbGFtIGthc3VzIGluaSwgZGlidWF0IGZpdHVyIGludGVyYWtzaSBhbnRhcmEgKnVtdXIgKGFnZSkqIGRhbiAqQk1JIChCb2R5IE1hc3MgSW5kZXgpKiB1bnR1ayBtZWxpaGF0IGRhbXBhayBnYWJ1bmdhbiB1c2lhIGRhbiBiZXJhdCBiYWRhbiB0ZXJoYWRhcCBrZXNlaGF0YW4uDQoNCiMjIyBLZWd1bmFhbjoNCg0KLSBNZW1iYW50dSBtb2RlbCBtZW5hbmdrYXAgZWZlayBnYWJ1bmdhbiBhbnRhciANCg0KLSBCZXJndW5hIGRhbGFtIG1vZGVsIGxpbmVhciB5YW5nIHRpZGFrIHNlY2FyYSBla3NwbGlzaXQgbWVuYW5na2FwIGludGVyYWtzaSBhbnRhciBmaXR1ci4NCg0KIyMjIENvbnRvaCBLb2RlIFI6DQoNCmBgYHt9DQoNCiMgU2ltcGFuIG5hbWEga29sb20gQk1JIGtlIHZhcmlhYmVsDQpibWlfY29sIDwtIGdyZXAoImJtaXxpbXQiLCBjb2xuYW1lcyhoZWFsdGhfZGF0YSksIHZhbHVlID0gVFJVRSlbMV0NCg0KIyBCdWF0IGludGVyYWtzaSBhbnRhcmEgYWdlIGRhbiBCTUkNCmRhdGFfaW50ZXJhY3Rpb24gPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBBZ2VfQk1JX0ltcGFjdCA9IGFnZSAqIC5kYXRhW1tibWlfY29sXV0NCiAgKQ0KYGBgDQoNCg0KDQojIyMgUGVuamVsYXNhbiBGdW5nc2kNCg0KfCBMYW5na2FoIHwgUGVuamVsYXNhbiB8DQp8LS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IGdyZXAoImJtaXxpbXQiLCAuLi4pIHwgU2VjYXJhIG90b21hdGlzIG1lbmRldGVrc2kgbmFtYSBrb2xvbSBCTUkgKGJpc2EgImJtaSIsICJpbXQiLCBkc2IpLiB8DQp8IGFnZSAqIC5kYXRhW1tibWlfY29sXV0gfCBNZW5nYWxpa2FuIHVtdXIgZGFuIEJNSSB1bnR1ayBtZW1idWF0IGZpdHVyIGJhcnUgQWdlX0JNSV9JbXBhY3QuIHwNCnwgbXV0YXRlKCkgfCBNZW5hbWJhaGthbiBmaXR1ciBiYXJ1IGtlIGRhbGFtIGRhdGEuIHwNCg0KDQojIyMgSW50ZXJwcmV0YXNpIEtlc2VoYXRhbjoNCg0KLSBGaXR1ciBBZ2VfQk1JX0ltcGFjdCBha2FuIGJlcm5pbGFpIHRpbmdnaSBqaWthIHNlc2VvcmFuZyBtZW1pbGlraSB1c2lhIGRhbiBCTUkgeWFuZyBzYW1hLXNhbWEgdGluZ2dpLg0KDQotIERhcGF0IG1lbnVuanVra2FuIHJpc2lrbyBrZXNlaGF0YW4gZ2FidW5nYW4gYWtpYmF0IHVzaWEgbGFuanV0IGRhbiBiZXJhdCBiYWRhbiBiZXJsZWJpaC4NCg0KDQojIyMgSGFzaWwgVHJhbnNmb3JtYXNpIA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KERUKQ0KDQojIFNpbXBhbiBuYW1hIGtvbG9tIEJNSSBrZSB2YXJpYWJlbA0KYm1pX2NvbCA8LSBncmVwKCJibWl8aW10IiwgY29sbmFtZXMoaGVhbHRoX2RhdGEpLCB2YWx1ZSA9IFRSVUUpWzFdDQoNCiMgQnVhdCBpbnRlcmFrc2kgYW50YXJhIGFnZSBkYW4gQk1JDQpkYXRhX2ludGVyYWN0aW9uIDwtIGhlYWx0aF9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgQWdlX0JNSV9JbXBhY3QgPSBhZ2UgKiAuZGF0YVtbYm1pX2NvbF1dDQogICkNCg0KIyBUYW1waWxrYW4gaGFzaWwNCkRUOjpkYXRhdGFibGUoaGVhZChkYXRhX2ludGVyYWN0aW9uKSwgY2FwdGlvbiA9ICJJbnRlcmFjdGlvbiBiZXR3ZWVuIEFnZSBhbmQgQk1JIikNCmBgYA0KDQoNCg0KIyMjIEtlc2ltcHVsYW46DQoNCi0gRml0dXIgaW50ZXJha3NpIGJlcmd1bmEgdW50dWsgbWVuZ3VuZ2thcCByZWxhc2kgdGVyc2VtYnVueWkgYW50YXIgdmFyaWFiZWwuDQotIERhbGFtIGthc3VzIGRhdGEga2VzZWhhdGFuLCBpbnRlcmFrc2kgYW50YXJhIHVzaWEgZGFuIEJNSSBiaXNhIG1lbmphZGkgaW5kaWthdG9yIHBlbnRpbmcgdGVyaGFkYXAgcmlzaWtvIHBlbnlha2l0IGtyb25pcy4NCg0KDQojIyA1LjIgUmF0aW8gRmVhdHVyZXMNCg0KUmF0aW8gZmVhdHVyZXMgYWRhbGFoIGZpdHVyIHlhbmcgZGliZW50dWsgZGFyaSBwZXJiYW5kaW5nYW4gZHVhIHZhcmlhYmVsIG51bWVyaWsuIFRla25payBpbmkgc2VyaW5nIGRpZ3VuYWthbiB1bnR1ayBtZW5vcm1hbGthbiBkYXRhLCBtZW55b3JvdGkga2V0aWRha3NlaW1iYW5nYW4sIGF0YXUgbWVuYW5na2FwIGh1YnVuZ2FuIHByb3BvcnNpb25hbCBhbnRhciB2YXJpYWJlbC4NCg0KDQojIyMgQ29udG9oIEthc3VzOg0KDQpEYWxhbSBkYXRhIGtlc2VoYXRhbiwgbWVtYmFuZGluZ2thbiBrYWRhciBrb2xlc3Rlcm9sIHRlcmhhZGFwIGthZGFyIGdsdWtvc2EgZGFwYXQgbWVtYmVyaWthbiBpbnNpZ2h0IHRhbWJhaGFuIHRlbnRhbmcgcHJvZmlsIG1ldGFib2xpayBzZXNlb3JhbmcuIE1ha2EgZGlidWF0IGZpdHVyIGJhcnU6ICpDaG9sZXN0ZXJvbCB0byBHbHVjb3NlIFJhdGlvKi4NCg0KIyMjIENvbnRvaCBLb2RlIFI6DQoNCmBgYHt9DQoNCmRhdGFfcmF0aW8gPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBDaG9sZXN0ZXJvbF9HbHVjb3NlX1JhdGlvID0gY2hvbGVzdGVyb2wgLyAoZ2x1Y29zZSArIDFlLTUpDQogICkNCmBgYA0KDQoNCg0KIyMjIFBlbmplbGFzYW4gRnVuZ3NpDQoNCnwgTGFuZ2thaCB8IFBlbmplbGFzYW4gfA0KfC0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCnwgY2hvbGVzdGVyb2wgLyAoZ2x1Y29zZSArIDFlLTUpIHwgUmFzaW8gYW50YXJhIGtvbGVzdGVyb2wgZGFuIGdsdWtvc2E7IHBlbmFtYmFoYW4gMWUtNSBtZW5jZWdhaCBwZW1iYWdpYW4gZGVuZ2FuIG5vbC4gfA0KfCBtdXRhdGUoKSB8IE1lbmFtYmFoa2FuIGZpdHVyIHJhc2lvIGJhcnUga2UgZGFsYW0gZGF0YS4gfA0KDQoNCg0KIyMjIEludGVycHJldGFzaSBLZXNlaGF0YW46DQoNCi0gUmFzaW8geWFuZyB0aW5nZ2kgYmlzYSBtZW51bmp1a2thbiBwb3RlbnNpIHJpc2lrbyBnYW5nZ3VhbiBtZXRhYm9saWsuDQoNCi0gQmVyZ3VuYSB1bnR1ayBtZW55YXJpbmcgaW5kaXZpZHUgZGVuZ2FuIGtvbGVzdGVyb2wgdGluZ2dpIHRldGFwaSBrYWRhciBnbHVrb3NhIG5vcm1hbCBhdGF1IHNlYmFsaWtueWEuDQoNCg0KIyMjIEhhc2lsIFRyYW5zZm9ybWFzaQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoRFQpDQoNCmRhdGFfcmF0aW8gPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBDaG9sZXN0ZXJvbF9HbHVjb3NlX1JhdGlvID0gY2hvbGVzdGVyb2wgLyAoZ2x1Y29zZSArIDFlLTUpDQogICkNCg0KRFQ6OmRhdGF0YWJsZShoZWFkKGRhdGFfcmF0aW8pLCBjYXB0aW9uID0gIkNob2xlc3Rlcm9sIHRvIEdsdWNvc2UgUmF0aW8iKQ0KYGBgDQoNCg0KDQojIyMgS2VzaW1wdWxhbjoNCg0KLSBSYXRpbyBmZWF0dXJlcyBtZW1wZXJrYXlhIGRhdGFzZXQgZGVuZ2FuIGluZm9ybWFzaSBwcm9wb3JzaW9uYWwuDQotIERhbGFtIGtvbnRla3MgbWVkaXMsIHJhc2lvIGluaSBtZW1iYW50dSBkYWxhbSBtZW5kZXRla3NpIGtldGlkYWtzZWltYmFuZ2FuIGJpb2tpbWlhIHlhbmcgdGlkYWsgdGVybGloYXQgZGFyaSBuaWxhaSBhYnNvbHV0IHNhamEuDQoNCg0KDQojIyA1LjMgR3JvdXAgQWdncmVnYXRpb24NCg0KR3JvdXAgYWdncmVnYXRpb24gYWRhbGFoIHRla25payB1bnR1ayBtZW5naGl0dW5nIHJpbmdrYXNhbiBzdGF0aXN0aWsgYmVyZGFzYXJrYW4ga2Vsb21wb2sgdGVydGVudHUgZGFsYW0gZGF0YSwgbWlzYWxueWEgYmVyZGFzYXJrYW4gSUQgcGFzaWVuLCBsb2thc2ksIGF0YXUgd2FrdHUuIFRla25payBpbmkgYmVyZ3VuYSB1bnR1ayBtZW5jaXB0YWthbiBmaXR1ciBiYXJ1IHlhbmcgbWVyYW5na3VtIGluZm9ybWFzaSBoaXN0b3JpcyBhdGF1IGJlcnVsYW5nLg0KDQoNCiMjIyBDb250b2ggS2FzdXM6DQoNClBhZGEgZGF0YXNldCBrZXNlaGF0YW4sIHNhdHUgcGFzaWVuIGJpc2EgbWVtaWxpa2kgYmViZXJhcGEgZW50cmkga3VuanVuZ2FuLiBNYWthIGtpdGEgYmlzYSBtZW5naGl0dW5nIHJhdGEtcmF0YSBkYW4gbWFrc2ltdW0ga2FkYXIgZ2x1a29zYSB1bnR1ayB0aWFwICpwYXRpZW50X2lkKiwgc2VydGEganVtbGFoIGt1bmp1bmdhbiBzZWJhZ2FpIGZpdHVyIHRhbWJhaGFuLg0KDQojIyMgQ29udG9oIEtvZGUgUjoNCg0KYGBge30NCg0KIyBBZ2dyZWdhdGUgYnkgcGF0aWVudA0KcGF0aWVudF9nbHVjb3NlIDwtIGhlYWx0aF9kYXRhICU+JQ0KICBncm91cF9ieShwYXRpZW50X2lkKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIEF2Z19HbHVjb3NlID0gbWVhbihnbHVjb3NlLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1heF9HbHVjb3NlID0gbWF4KGdsdWNvc2UsIG5hLnJtID0gVFJVRSksDQogICAgVmlzaXRzID0gbigpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKQ0KDQojIEpvaW4gd2l0aCBvcmlnaW5hbCBkYXRhDQpoZWFsdGhfZGF0YV9qb2luZWQgPC0gbGVmdF9qb2luKGhlYWx0aF9kYXRhLCBwYXRpZW50X2dsdWNvc2UsIGJ5ID0gInBhdGllbnRfaWQiKQ0KYGBgDQoNCg0KfCBMYW5na2FoIHwgUGVuamVsYXNhbiB8DQp8LS0tLS0tLS18LS0tLS0tLS0tLS0tfA0KfCBncm91cF9ieShwYXRpZW50X2lkKSB8IE1lbmdlbG9tcG9ra2FuIGRhdGEgYmVyZGFzYXJrYW4gSUQgcGFzaWVuLiB8DQp8IHN1bW1hcmlzZSgpIHwgTWVuZ2hpdHVuZyBuaWxhaSByYXRhLXJhdGEsIG1ha3NpbXVtLCBkYW4ganVtbGFoIGt1bmp1bmdhbi4gfA0KfCBsZWZ0X2pvaW4oKSB8IE1lbmdnYWJ1bmdrYW4gaGFzaWwgYWdyZWdhc2kga2VtYmFsaSBrZSBkYXRhIHV0YW1hLiB8DQoNCg0KIyMjICBNYW5mYWF0IGRhbGFtIEtvbnRla3MgTWVkaXM6DQoNCi0gKkF2Z19HbHVjb3NlKiBkYW4gKk1heF9HbHVjb3NlKiBtZW5jZXJtaW5rYW4ga29udHJvbCBndWxhIGRhcmFoIHBhc2llbiBzZWNhcmEgbG9uZ2l0dWRpbmFsLg0KDQotICpWaXNpdHMqIGRhcGF0IG1lbnVuanVra2FuIGZyZWt1ZW5zaSBrb250cm9sIGF0YXUga2VwYXJhaGFuIGtvbmRpc2kgcGFzaWVuLg0KDQoNCiMjIyBIYXNpbCBUcmFuc2Zvcm1hc2kNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShEVCkNCg0KIyBBZ2dyZWdhdGUgYnkgcGF0aWVudA0KcGF0aWVudF9nbHVjb3NlIDwtIGhlYWx0aF9kYXRhICU+JQ0KICBncm91cF9ieShwYXRpZW50X2lkKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIEF2Z19HbHVjb3NlID0gbWVhbihnbHVjb3NlLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1heF9HbHVjb3NlID0gbWF4KGdsdWNvc2UsIG5hLnJtID0gVFJVRSksDQogICAgVmlzaXRzID0gbigpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKQ0KDQojIEpvaW4gd2l0aCBvcmlnaW5hbCBkYXRhDQpoZWFsdGhfZGF0YV9qb2luZWQgPC0gbGVmdF9qb2luKGhlYWx0aF9kYXRhLCBwYXRpZW50X2dsdWNvc2UsIGJ5ID0gInBhdGllbnRfaWQiKQ0KDQpEVDo6ZGF0YXRhYmxlKGhlYWQoaGVhbHRoX2RhdGFfam9pbmVkKSwgY2FwdGlvbiA9ICJHbHVjb3NlIEFnZ3JlZ2F0aW9uIHBlciBQYXRpZW50IikNCmBgYA0KDQoNCg0KIyMjICBLZXNpbXB1bGFuOg0KDQotIEdyb3VwIGFnZ3JlZ2F0aW9uIG1lbmdoYXNpbGthbiBmaXR1ciB5YW5nIG1lbXBlcmtheWEgZGF0YSBkZW5nYW4gaW5mb3JtYXNpIHJpbmdrYXNhbiBwZXIgZW50aXRhcy4NCi0gQ29jb2sgdW50dWsgZGF0YSB5YW5nIGJlcnNpZmF0IGxvbmdpdHVkaW5hbCwgc2VwZXJ0aSByZWthbSBtZWRpcyBhdGF1IHRyYW5zYWtzaSBwZWxhbmdnYW4uDQoNCg0KIyMgNS40IFJhbmsgVHJhbnNmb3JtYXRpb24NCg0KUmFuayB0cmFuc2Zvcm1hdGlvbiBhZGFsYWggdGVrbmlrIHVudHVrIG1lbmd1YmFoIG5pbGFpIG51bWVyaWsgbWVuamFkaSB1cnV0YW4gYmVyZGFzYXJrYW4gcG9zaXNpIHJlbGF0aWZueWEgZGFsYW0gZGF0YXNldC4gSW5pIGJlcmd1bmEgdW50dWsgbWVuZ2lkZW50aWZpa2FzaSBwb3Npc2kgYXRhdSBwZXJpbmdrYXQgc3VhdHUgZW50aXRhcyBiZXJkYXNhcmthbiB2YXJpYWJlbCB0ZXJ0ZW50dSB0YW5wYSBtZW1lcmx1a2FuIGRpc3RyaWJ1c2kgZGF0YSB5YW5nIHNwZXNpZmlrLg0KDQoNCiMjIyBDb250b2ggS2FzdXM6DQoNCkRhbGFtIGRhdGFzZXQga2VzZWhhdGFuLCBraXRhIGRhcGF0IG1lbmd1cnV0a2FuIGthZGFyIGdsdWtvc2EgcGFzaWVuIGRhbiBtZW1iZXJpIHBlcmluZ2thdCBrZXBhZGEgbWVyZWthIGJlcmRhc2Fya2FuIGxldmVsIGdsdWtvc2EsIGRlbmdhbiBwYXNpZW4geWFuZyBtZW1pbGlraSBrYWRhciBnbHVrb3NhIHRlcnRpbmdnaSBtZW5kYXBhdCBwZXJpbmdrYXQgdGVyYXRhcy4NCg0KIyMjIENvbnRvaCBLb2RlIFI6DQoNCmBgYHt9DQoNCmRhdGFfcmFua2VkIDwtIGhlYWx0aF9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgR2x1Y29zZV9SYW5rID0gcmFuaygtZ2x1Y29zZSkNCiAgKQ0KDQpgYGANCg0KIyMjIFBlbmplbGFzYW4gRnVuZ3NpIA0KDQoNCnwgTGFuZ2thaCB8IFBlbmplbGFzYW4gfA0KfC0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCnwgcmFuaygtZ2x1Y29zZSkgfCBNZW5naGl0dW5nIHBlcmluZ2thdCBiZXJkYXNhcmthbiBrYWRhciBnbHVrb3NhLCBkZW5nYW4gdGFuZGEgbWludXMgLSB1bnR1ayBtZW1iZXJpa2FuIHBlcmluZ2thdCB0ZXJ0aW5nZ2kgcGFkYSBuaWxhaSB0ZXJiZXNhci4gfA0KDQoNCiMjIyAgTWFuZmFhdCBkYWxhbSBLb250ZWtzIE1lZGlzOg0KDQotICpHbHVjb3NlX1JhbmsqIG1lbWJlcmlrYW4gZ2FtYmFyYW4gdGVudGFuZyBwb3Npc2kgcmVsYXRpZiBwYXNpZW4gYmVyZGFzYXJrYW4ga2FkYXIgZ2x1a29zYSBtZXJla2EuIEluaSBiZXJndW5hIHVudHVrIG1lbWFoYW1pIHNpYXBhIHlhbmcgYmVyYWRhIGRhbGFtIGtlbG9tcG9rIGRlbmdhbiBrYWRhciBnbHVrb3NhIHRlcnRpbmdnaSBhdGF1IHRlcmVuZGFoLg0KDQoNCiMjIyBIYXNpbCBUcmFuc2Zvcm1hc2kNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShEVCkNCg0KZGF0YV9yYW5rZWQgPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBHbHVjb3NlX1JhbmsgPSByYW5rKC1nbHVjb3NlKQ0KICApDQoNCkRUOjpkYXRhdGFibGUoaGVhZChkYXRhX3JhbmtlZCksIGNhcHRpb24gPSAiUmFua2luZyBieSBHbHVjb3NlIExldmVsIikNCmBgYA0KDQoNCg0KIyMjICBLZXNpbXB1bGFuOg0KDQpSYW5rIHRyYW5zZm9ybWF0aW9uIGNvY29rIHVudHVrIGFuYWxpc2lzIHlhbmcgbWVuZ3V0YW1ha2FuIHVydXRhbiBkYXJpcGFkYSBuaWxhaSBhYnNvbHV0LCBzZXBlcnRpIGlkZW50aWZpa2FzaSBwYXNpZW4gZGVuZ2FuIGtvbmRpc2kgcGFsaW5nIGtyaXRpcyBiZXJkYXNhcmthbiBwZXJpbmdrYXQuDQoNCg0KIyMgNS41IFRleHQgQ2xlYW5pbmcgJiBGZWF0dXJlIENyZWF0aW9uDQoNClRleHQgY2xlYW5pbmcgZGFuIHBlbWJ1YXRhbiBmaXR1ciBkYXJpIHRla3MgYWRhbGFoIGxhbmdrYWggcGVudGluZyBkYWxhbSBwcm9zZXMgcGVtYmVyc2loYW4gZGF0YSwga2h1c3VzbnlhIGtldGlrYSBraXRhIGJla2VyamEgZGVuZ2FuIGRhdGEgeWFuZyBtZW5nYW5kdW5nIGluZm9ybWFzaSBkYWxhbSBmb3JtYXQgdGVrcyB5YW5nIHRpZGFrIHRlcnN0cnVrdHVyLiBTYWxhaCBzYXR1IGxhbmdrYWggcGVudGluZyBhZGFsYWggbWVuZ2Vrc3RyYWtzaSBpbmZvcm1hc2kgcmVsZXZhbiBkYXJpIHRla3MsIHNlcGVydGkgYW5na2EgYXRhdSBrYXRhLWthdGEga3VuY2ksIHVudHVrIGRpZ3VuYWthbiBkYWxhbSBhbmFsaXNpcyBsZWJpaCBsYW5qdXQuDQoNCg0KIyMjIENvbnRvaCBLYXN1czoNCg0KRGFsYW0gZGF0YXNldCBrZXNlaGF0YW4sIGtvbG9tIHBhdGllbnRfaWQgbXVuZ2tpbiBtZW5nYW5kdW5nIGFuZ2thIGRhbiBrYXJha3RlciBsYWluLCB5YW5nIG1hbmEga2l0YSBiaXNhIG1lbmdla3N0cmFrc2kgYW5na2Egc2FqYSB1bnR1ayBtZW1idWF0IGZpdHVyIGJhcnUuDQoNCiMjIyBDb250b2ggS29kZSBSOg0KDQpgYGB7fQ0KDQpkYXRhX3RleHQgPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBQYXRpZW50X051bSA9IGFzLm51bWVyaWMoZ3N1YigiW14wLTldIiwgIiIsIHBhdGllbnRfaWQpKQ0KICApDQpgYGAgIA0KDQoNCiMjIyBQZW5qZWxhc2FuIEZ1bmdzaSANCg0KfCBMYW5na2FoIHwgUGVuamVsYXNhbiB8DQp8LS0tLS0tLS18LS0tLS0tLS0tLS0tfA0KfCBnc3ViKCJbXjAtOV0iLCAiIiwgcGF0aWVudF9pZCkgfCBNZW5nZ3VuYWthbiBmdW5nc2kgZ3N1YiB1bnR1ayBtZW5naGFwdXMga2FyYWt0ZXIgbm9uLWFuZ2thIGRhbiBtZW5nZWtzdHJha3NpIGhhbnlhIGFuZ2thIGRhcmkga29sb20gcGF0aWVudF9pZC4gfA0KfCBhcy5udW1lcmljKCkgfCBNZW5nb252ZXJzaSBoYXNpbCBla3N0cmFrc2kgbWVuamFkaSBmb3JtYXQgbnVtZXJpay4gfA0KDQoNCiMjIyAgTWFuZmFhdCBkYWxhbSBLb250ZWtzIE1lZGlzOg0KDQotICpQYXRpZW50X051bSogbWVtYmVyaWthbiBub21vciBwYXNpZW4gZGFsYW0gZm9ybWF0IG51bWVyaWsgeWFuZyBiaXNhIGRpZ3VuYWthbiB1bnR1ayBhbmFsaXNpcyBsZWJpaCBsYW5qdXQsIHNlcGVydGkgcGVuZ2Vsb21wb2thbiBhdGF1IGlkZW50aWZpa2FzaSBwYXNpZW4gYmVyZGFzYXJrYW4gSUQgbnVtZXJpay4NCg0KDQojIyMgSGFzaWwgVHJhbnNmb3JtYXNpDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoRFQpDQoNCmRhdGFfdGV4dCA8LSBoZWFsdGhfZGF0YSAlPiUNCiAgbXV0YXRlKA0KICAgIFBhdGllbnRfTnVtID0gYXMubnVtZXJpYyhnc3ViKCJbXjAtOV0iLCAiIiwgcGF0aWVudF9pZCkpDQogICkNCg0KRFQ6OmRhdGF0YWJsZShoZWFkKGRhdGFfdGV4dCksIGNhcHRpb24gPSAiRXh0cmFjdCBOdW1iZXJzIGZyb20gUGF0aWVudCBJRCIpDQpgYGANCg0KDQoNCiMjIyAgS2VzaW1wdWxhbjoNCg0KTGFuZ2thaCBwZW1iZXJzaWhhbiB0ZWtzIGluaSBtZW1iYW50dSBtZW5ndWJhaCBJRCBwYXNpZW4geWFuZyBtZW5nYW5kdW5nIGthcmFrdGVyIG1lbmphZGkgZm9ybWF0IHlhbmcgbGViaWggbXVkYWggdW50dWsgZGlhbmFsaXNpcywgdGVydXRhbWEgc2FhdCBJRCBwYXNpZW4gZGlwZXJsdWthbiBkYWxhbSBhbmFsaXNpcyBzdGF0aXN0aWsgYXRhdSBwZW1iZWxhamFyYW4gbWVzaW4uDQoNCg0KIyMgNS42IEN1bXVsYXRpdmUgRmVhdHVyZXMNCg0KQ3VtdWxhdGl2ZSBmZWF0dXJlcyBtZW5nYWN1IHBhZGEgZml0dXIgeWFuZyBkaWhpdHVuZyBzZWNhcmEgYmVydXJ1dGFuIGJlcmRhc2Fya2FuIGRhdGEgeWFuZyB0ZXJ1cnV0LCBzZXBlcnRpIGFrdW11bGFzaSBuaWxhaSBkYXJpIHZhcmlhYmVsIHRlcnRlbnR1IHNlcGFuamFuZyB3YWt0dS4gQ3VtdWxhdGl2ZSBmZWF0dXJlcyBzZXJpbmcgZGlndW5ha2FuIGRhbGFtIGFuYWxpc2lzIHRpbWUgc2VyaWVzIGF0YXUgZGF0YSB5YW5nIGJlcnNpZmF0IGtyb25vbG9naXMsIHNlcGVydGkgcGVuZ2hpdHVuZ2FuIGp1bWxhaCBrdW11bGF0aWYgZGFyaSBzdWF0dSBwYXJhbWV0ZXIga2VzZWhhdGFuLg0KDQoNCiMjIyBDb250b2ggS2FzdXM6DQoNCkRhbGFtIGRhdGFzZXQga2VzZWhhdGFuLCBraXRhIGRhcGF0IG1lbmdoaXR1bmcganVtbGFoIGt1bXVsYXRpZiBrYWRhciBnbHVrb3NhIGRhcmkgd2FrdHUga2Ugd2FrdHUgdW50dWsgc2V0aWFwIHBhc2llbiwgeWFuZyBtZW1iZXJpa2FuIGdhbWJhcmFuIHRlbnRhbmcgcGVydWJhaGFuIHRpbmdrYXQgZ2x1a29zYSBzZXBhbmphbmcgd2FrdHUuDQoNCiMjIyBDb250b2ggS29kZSBSOg0KDQpgYGB7fQ0KDQpkYXRhX2N1bXVsYXRpdmUgPC0gaGVhbHRoX2RhdGEgJT4lDQogIGFycmFuZ2UocGF0aWVudF9pZCwgZGF0ZSkgJT4lDQogIGdyb3VwX2J5KHBhdGllbnRfaWQpICU+JQ0KICBtdXRhdGUoDQogICAgQ3VtdWxhdGl2ZV9HbHVjb3NlID0gY3Vtc3VtKGdsdWNvc2UpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgICANCg0KDQojIyMgUGVuamVsYXNhbiBGdW5nc2kgDQoNCnwgTGFuZ2thaCB8IFBlbmplbGFzYW4gfA0KfC0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCnwgYXJyYW5nZShwYXRpZW50X2lkLCBkYXRlKSB8IE1lbmd1cnV0a2FuIGRhdGEgYmVyZGFzYXJrYW4gSUQgcGFzaWVuIGRhbiB0YW5nZ2FsIHVudHVrIG1lbWFzdGlrYW4gYmFod2EgcGVuZ2hpdHVuZ2FuIGt1bXVsYXRpZiBkaWxha3VrYW4gYmVyZGFzYXJrYW4gdXJ1dGFuIHdha3R1LiB8DQp8IGdyb3VwX2J5KHBhdGllbnRfaWQpIHwgTWVuZ2Vsb21wb2trYW4gZGF0YSBiZXJkYXNhcmthbiBJRCBwYXNpZW4gYWdhciBwZXJoaXR1bmdhbiBrdW11bGF0aWYgZGlsYWt1a2FuIHBlciBwYXNpZW4uIHwNCnwgY3Vtc3VtKGdsdWNvc2UpIHwgTWVuZ2hpdHVuZyBqdW1sYWgga3VtdWxhdGlmIGthZGFyIGdsdWtvc2EgdW50dWsgc2V0aWFwIHBhc2llbiBkYXJpIHdha3R1IGtlIHdha3R1LiB8DQoNCg0KIyMjIE1hbmZhYXQgZGFsYW0gS29udGVrcyBNZWRpczoNCg0KLSAqQ3VtdWxhdGl2ZV9HbHVjb3NlKiBtZW1iZXJpa2FuIGluZm9ybWFzaSBtZW5nZW5haSBha3VtdWxhc2kga2FkYXIgZ2x1a29zYSBwYXNpZW4gc2VwYW5qYW5nIHdha3R1LCB5YW5nIGJpc2EgZGlndW5ha2FuIHVudHVrIG1lbWFoYW1pIHBlcnViYWhhbiBwb2xhIGdsdWtvc2EsIG1lbmdpZGVudGlmaWthc2kgdHJlbiwgZGFuIG1lbmRldGVrc2kga2VtdW5na2luYW4gbWFzYWxhaCBrZXNlaGF0YW4uDQoNCg0KIyMjIEhhc2lsIFRyYW5zZm9ybWFzaSANCg0KYGBge3IsIG1hc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShEVCkNCg0KZGF0YV9jdW11bGF0aXZlIDwtIGhlYWx0aF9kYXRhICU+JQ0KICBhcnJhbmdlKHBhdGllbnRfaWQsIGRhdGUpICU+JQ0KICBncm91cF9ieShwYXRpZW50X2lkKSAlPiUNCiAgbXV0YXRlKA0KICAgIEN1bXVsYXRpdmVfR2x1Y29zZSA9IGN1bXN1bShnbHVjb3NlKQ0KICApICU+JQ0KICB1bmdyb3VwKCkNCg0KRFQ6OmRhdGF0YWJsZShoZWFkKGRhdGFfY3VtdWxhdGl2ZSksIGNhcHRpb24gPSAiQ3VtdWxhdGl2ZSBHbHVjb3NlIHBlciBQYXRpZW50IikNCmBgYA0KDQoNCg0KIyMjIEtlc2ltcHVsYW46DQoNClBlbmdoaXR1bmdhbiBmaXR1ciBrdW11bGF0aWYgc2VwZXJ0aSAqQ3VtdWxhdGl2ZV9HbHVjb3NlKiBtZW11bmdraW5rYW4gcGVtYWhhbWFuIHlhbmcgbGViaWggZGFsYW0gbWVuZ2VuYWkgcGVyamFsYW5hbiBrZXNlaGF0YW4gcGFzaWVuIGRhcmkgd2FrdHUga2Ugd2FrdHUgZGFuIGRhcGF0IG1lbWJhbnR1IGRhbGFtIHBlbW9kZWxhbiBwcmVkaWt0aWYsIHNlcGVydGkgZGV0ZWtzaSByaXNpa28gZGlhYmV0ZXMgYXRhdSBrb21wbGlrYXNpIHRlcmthaXQgZ2x1a29zYS4NCg0KDQojIDYuIE91dGxpZXIgSGFuZGxpbmcNCg0KRGFsYW0gYW5hbGlzaXMgZGF0YSwgb3V0bGllciBhdGF1IGRhdGEgeWFuZyBtZW55aW1wYW5nIGphdWggZGFyaSBwb2xhIHVtdW0gZGFwYXQgbWVtcGVuZ2FydWhpIGhhc2lsIGFuYWxpc2lzIGRhbiBtb2RlbC4gT2xlaCBrYXJlbmEgaXR1LCBwZW50aW5nIHVudHVrIG1lbmRldGVrc2kgZGFuIG1lbmFuZ2FuaSBvdXRsaWVyIGRlbmdhbiB0ZXBhdC4gQmViZXJhcGEgbWV0b2RlIHlhbmcgdW11bSBkaWd1bmFrYW4gdW50dWsgbWVuZGV0ZWtzaSBvdXRsaWVyIGFudGFyYSBsYWluIFotc2NvcmUgZGFuIEludGVycXVhcnRpbGUgUmFuZ2UgKElRUikuDQoNCg0KIyMjIENvbnRvaCBLYXN1czoNCg0KUGFkYSBkYXRhc2V0IGtlc2VoYXRhbiBpbmksIGtpdGEgYWthbiBtZW5nZ3VuYWthbiBkdWEgbWV0b2RlIHVudHVrIG1lbmRldGVrc2kgZGFuIG1lbmFuZ2FuaSBvdXRsaWVyOg0KDQoxLiAqWi1zY29yZSogdW50dWsgbWVuZGV0ZWtzaSBvdXRsaWVyIHBhZGEga2FkYXIgZ2x1a29zYS4NCg0KMi4gKklRUiogdW50dWsgbWVuZGV0ZWtzaSBvdXRsaWVyIHBhZGEgaW5kZWtzIG1hc3NhIHR1YnVoIChCTUkpLg0KDQojIyMgQ29udG9oIEtvZGUgUjoNCg0KDQpgYGB7fQ0KDQojIEVuc3VyZSBhbGwgY29sdW1uIG5hbWVzIGFyZSBsb3dlcmNhc2UNCmNvbG5hbWVzKGhlYWx0aF9kYXRhKSA8LSB0b2xvd2VyKGNvbG5hbWVzKGhlYWx0aF9kYXRhKSkNCg0KIyBHZXQgdGhlIGV4YWN0IG5hbWUgb2YgZ2x1Y29zZSBhbmQgQk1JIGNvbHVtbnMNCmdsdWNvc2VfY29sIDwtIGdyZXAoImdsdWtvc2F8Z2x1Y29zZSIsIGNvbG5hbWVzKGhlYWx0aF9kYXRhKSwgdmFsdWUgPSBUUlVFKVsxXQ0KYm1pX2NvbCA8LSBncmVwKCJpbXR8Ym1pIiwgY29sbmFtZXMoaGVhbHRoX2RhdGEpLCB2YWx1ZSA9IFRSVUUpWzFdDQoNCiMgWi1zY29yZSBtZXRob2QgZm9yIG91dGxpZXIgZGV0ZWN0aW9uICh1c2luZyBHbHVjb3NlKQ0Kel9zY29yZXMgPC0gc2NhbGUoaGVhbHRoX2RhdGFbW2dsdWNvc2VfY29sXV0pDQoNCmRhdGFfb3V0bGllcnMgPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBPdXRsaWVyX0ZsYWcgPSBpZmVsc2UoYWJzKHpfc2NvcmVzKSA+IDMsICJPdXRsaWVyIiwgIk5vcm1hbCIpDQogICkNCg0KIyBJUVIgbWV0aG9kIGZvciBvdXRsaWVyIHJlbW92YWwgKHVzaW5nIEJNSSkNClExIDwtIHF1YW50aWxlKGhlYWx0aF9kYXRhW1tibWlfY29sXV0sIDAuMjUsIG5hLnJtID0gVFJVRSkNClEzIDwtIHF1YW50aWxlKGhlYWx0aF9kYXRhW1tibWlfY29sXV0sIDAuNzUsIG5hLnJtID0gVFJVRSkNCklRUl92YWwgPC0gUTMgLSBRMQ0KDQpkYXRhX291dGxpZXJzX0lRUiA8LSBoZWFsdGhfZGF0YSAlPiUNCiAgZmlsdGVyKA0KICAgIC5kYXRhW1tibWlfY29sXV0gPiAoUTEgLSAxLjUgKiBJUVJfdmFsKSAmDQogICAgLmRhdGFbW2JtaV9jb2xdXSA8IChRMyArIDEuNSAqIElRUl92YWwpDQogICkNCg0KYGBgDQoNCg0KIyMjIE1ldG9kZSBaLVNjb3JlIHVudHVrIERldGVrc2kgT3V0bGllcjoNCg0KWi1zY29yZSBkaWd1bmFrYW4gdW50dWsgbWVuZGV0ZWtzaSBzZWJlcmFwYSBqYXVoIHN1YXR1IG5pbGFpIGRhcmkgcmF0YS1yYXRhIGRhbGFtIHNhdHVhbiBkZXZpYXNpIHN0YW5kYXIuIERhbGFtIGthc3VzIGluaSwga2l0YSBtZW5naGl0dW5nIFotc2NvcmUgdW50dWsga2FkYXIgZ2x1a29zYSwgZGFuIGppa2EgWi1zY29yZSBsZWJpaCBiZXNhciBkYXJpIDMgYXRhdSBsZWJpaCBrZWNpbCBkYXJpIC0zLCBuaWxhaSB0ZXJzZWJ1dCBkaWFuZ2dhcCBzZWJhZ2FpIG91dGxpZXIuDQoNCnwgTGFuZ2thaCB8IFBlbmplbGFzYW4gfA0KfC0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCnwgc2NhbGUoaGVhbHRoX2RhdGFbW2dsdWNvc2VfY29sXV0pIHwgTWVuZ2hpdHVuZyBaLXNjb3JlIHVudHVrIGthZGFyIGdsdWtvc2EuIHwNCnwgaWZlbHNlKGFicyh6X3Njb3JlcykgPiAzLCAiT3V0bGllciIsICJOb3JtYWwiKSB8IE1lbmFuZGFpIG5pbGFpIHlhbmcgbWVtaWxpa2kgWi1zY29yZSBsZWJpaCBkYXJpIDMgYXRhdSBrdXJhbmcgZGFyaSAtMyBzZWJhZ2FpIG91dGxpZXIuIHwNCg0KDQojIyMgTWV0b2RlIElRUiB1bnR1ayBQZW5naGFwdXNhbiBPdXRsaWVyOg0KDQpNZXRvZGUgSVFSIG1lbmdoaXR1bmcgcmVudGFuZyBpbnRlcmt1YXJ0aWwgKElRUikgeWFuZyBtZXJ1cGFrYW4gc2VsaXNpaCBhbnRhcmEga3VhcnRpbCBrZXRpZ2EgKFEzKSBkYW4ga3VhcnRpbCBwZXJ0YW1hIChRMSkuIE5pbGFpIHlhbmcgYmVyYWRhIGRpIGx1YXIgcmVudGFuZyAqW1ExIC0gMS41ICBJUVIsIFEzICsgMS41ICBJUVJdKiBkaWFuZ2dhcCBzZWJhZ2FpIG91dGxpZXIuDQoNCnwgTGFuZ2thaCB8IFBlbmplbGFzYW4gfA0KfC0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCnwgcXVhbnRpbGUoaGVhbHRoX2RhdGFbW2JtaV9jb2xdXSwgMC4yNSkgfCBNZW5naGl0dW5nIGt1YXJ0aWwgcGVydGFtYSAoUTEpIGRhcmkgZGF0YSBCTUkuIHwNCnwgcXVhbnRpbGUoaGVhbHRoX2RhdGFbW2JtaV9jb2xdXSwgMC43NSkgfCBNZW5naGl0dW5nIGt1YXJ0aWwga2V0aWdhIChRMykgZGFyaSBkYXRhIEJNSS4gfA0KfCBmaWx0ZXIoLmRhdGFbW2JtaV9jb2xdXSA+IChRMSAtIDEuNSAqIElRUl92YWwpICYgLmRhdGFbW2JtaV9jb2xdXSA8IChRMyArIDEuNSAqIElRUl92YWwpKSB8IE1lbnlhcmluZyBkYXRhIEJNSSB1bnR1ayBtZW5naGFwdXMgbmlsYWktbmlsYWkgb3V0bGllciBkaSBsdWFyIHJlbnRhbmcgSVFSLiB8DQoNCg0KIyMjIE1hbmZhYXQgTWVuZ2Vsb2xhIE91dGxpZXI6DQoNCi0gKkRldGVrc2kgRGluaToqIE1lbmdpZGVudGlmaWthc2kgb3V0bGllciBkZW5nYW4gbWV0b2RlIHlhbmcgdGVwYXQgbWVtdW5na2lua2FuIHBlbWFoYW1hbiB5YW5nIGxlYmloIGJhaWsgdGVudGFuZyBkYXRhIGRhbiBtZW1iYW50dSBtZW5kZXRla3NpIG1hc2FsYWggZGFsYW0gcGVuZ3VrdXJhbiBhdGF1IGtvbmRpc2kgeWFuZyB0aWRhayBiaWFzYS4NCi0gKk1vZGVsaW5nOiogTWVuZ2hhcHVzIGF0YXUgbWVuYW5nYW5pIG91dGxpZXIgZGFwYXQgbWVuaW5na2F0a2FuIGtpbmVyamEgbW9kZWwgcHJlZGlrdGlmIGRlbmdhbiBtZW5naGluZGFyaSBkaXN0b3JzaSB5YW5nIGRhcGF0IGRpc2ViYWJrYW4gb2xlaCBkYXRhIHlhbmcgZWtzdHJlbS4NCg0KDQojIyMgSGFzaWwgVHJhbnNmb3JtYXNpIA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KERUKQ0KDQojIEVuc3VyZSBhbGwgY29sdW1uIG5hbWVzIGFyZSBsb3dlcmNhc2UNCmNvbG5hbWVzKGhlYWx0aF9kYXRhKSA8LSB0b2xvd2VyKGNvbG5hbWVzKGhlYWx0aF9kYXRhKSkNCg0KIyBHZXQgdGhlIGV4YWN0IG5hbWUgb2YgZ2x1Y29zZSBhbmQgQk1JIGNvbHVtbnMNCmdsdWNvc2VfY29sIDwtIGdyZXAoImdsdWtvc2F8Z2x1Y29zZSIsIGNvbG5hbWVzKGhlYWx0aF9kYXRhKSwgdmFsdWUgPSBUUlVFKVsxXQ0KYm1pX2NvbCA8LSBncmVwKCJpbXR8Ym1pIiwgY29sbmFtZXMoaGVhbHRoX2RhdGEpLCB2YWx1ZSA9IFRSVUUpWzFdDQoNCiMgWi1zY29yZSBtZXRob2QgZm9yIG91dGxpZXIgZGV0ZWN0aW9uICh1c2luZyBHbHVjb3NlKQ0Kel9zY29yZXMgPC0gc2NhbGUoaGVhbHRoX2RhdGFbW2dsdWNvc2VfY29sXV0pDQoNCmRhdGFfb3V0bGllcnMgPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBPdXRsaWVyX0ZsYWcgPSBpZmVsc2UoYWJzKHpfc2NvcmVzKSA+IDMsICJPdXRsaWVyIiwgIk5vcm1hbCIpDQogICkNCg0KIyBJUVIgbWV0aG9kIGZvciBvdXRsaWVyIHJlbW92YWwgKHVzaW5nIEJNSSkNClExIDwtIHF1YW50aWxlKGhlYWx0aF9kYXRhW1tibWlfY29sXV0sIDAuMjUsIG5hLnJtID0gVFJVRSkNClEzIDwtIHF1YW50aWxlKGhlYWx0aF9kYXRhW1tibWlfY29sXV0sIDAuNzUsIG5hLnJtID0gVFJVRSkNCklRUl92YWwgPC0gUTMgLSBRMQ0KDQpkYXRhX291dGxpZXJzX0lRUiA8LSBoZWFsdGhfZGF0YSAlPiUNCiAgZmlsdGVyKA0KICAgIC5kYXRhW1tibWlfY29sXV0gPiAoUTEgLSAxLjUgKiBJUVJfdmFsKSAmDQogICAgLmRhdGFbW2JtaV9jb2xdXSA8IChRMyArIDEuNSAqIElRUl92YWwpDQogICkNCg0KIyBEaXNwbGF5IHJlc3VsdHMNCkRUOjpkYXRhdGFibGUoaGVhZChkYXRhX291dGxpZXJzKSwgY2FwdGlvbiA9ICJPdXRsaWVyIERldGVjdGlvbiBVc2luZyBaLVNjb3JlIChHbHVjb3NlKSIpDQpEVDo6ZGF0YXRhYmxlKGhlYWQoZGF0YV9vdXRsaWVyc19JUVIpLCBjYXB0aW9uID0gIkZpbHRlcmVkIERhdGEgVXNpbmcgSVFSIE1ldGhvZCAoQk1JKSIpDQpgYGANCg0KDQoNCiMjIyAgS2VzaW1wdWxhbjoNCg0KLSBEZW5nYW4gbWVuZ2d1bmFrYW4gbWV0b2RlICpaLXNjb3JlKiBkYW4gKklRUiosIGtpdGEgZGFwYXQgbWVuZGV0ZWtzaSBkYW4gbWVuYW5nYW5pIG91dGxpZXIgcGFkYSBkYXRhIGtlc2VoYXRhbiB1bnR1ayBtZW1hc3Rpa2FuIGJhaHdhIGFuYWxpc2lzIGRhbiBtb2RlbCB5YW5nIGRpYmFuZ3VuIGxlYmloIGFrdXJhdCBkYW4gcmVwcmVzZW50YXRpZiB0ZXJoYWRhcCBkYXRhIGFzbGkuDQoNCg0KIyA3LiBEaXNjcmV0aXphdGlvbg0KDQpEaXNjcmV0aXphdGlvbiBhZGFsYWggcHJvc2VzIG1lbmd1YmFoIHZhcmlhYmVsIGtvbnRpbnUgbWVuamFkaSBrYXRlZ29yaSBhdGF1IGludGVydmFsLiBIYWwgaW5pIHNlcmluZyBkaWxha3VrYW4gdW50dWsgbWVuaW5na2F0a2FuIHBlbWFoYW1hbiBhdGF1IGludGVycHJldGFzaSBkYXRhLCBzZXJ0YSBtZW11ZGFoa2FuIHBlbmVyYXBhbiBtb2RlbCBrbGFzaWZpa2FzaS4gU2FsYWggc2F0dSBjb250b2ggeWFuZyB1bXVtIGFkYWxhaCBtZW5ndWJhaCB1c2lhIG1lbmphZGkga2F0ZWdvcmkgc2VwZXJ0aSAiTXVkYSIsICJQYXJ1aCBCYXlhIiwgZGFuICJUdWEiLg0KDQoNCiMjIyBDb250b2ggS2FzdXM6DQoNClBhZGEgZGF0YXNldCBrZXNlaGF0YW4gaW5pLCBraXRhIGFrYW4gbWVuZGlza3JldGlzYXNpa2FuIGtvbG9tICp1c2lhKiBtZW5qYWRpIHRpZ2Ega2F0ZWdvcmk6DQoxLiAqWW91bmcqIChNdWRhKQ0KMi4gKk1pZGRsZS1hZ2VkKiAoUGFydWggQmF5YSkNCjMuICpPbGQqIChUdWEpDQoNCkthdGVnb3JpIGluaSBha2FuIGRpZGFzYXJrYW4gcGFkYSBwZW1iYWdpYW4ga3VhbnRpbCBkYXRhIHVzaWEuDQoNCiMjIyBDb250b2ggS29kZSBSOg0KDQpgYGB7fQ0KDQoNCiMgTWFrZSBzdXJlIGNvbHVtbiBuYW1lcyBhcmUgbG93ZXJjYXNlDQpjb2xuYW1lcyhoZWFsdGhfZGF0YSkgPC0gdG9sb3dlcihjb2xuYW1lcyhoZWFsdGhfZGF0YSkpDQoNCiMgQ29udmVydCAnYWdlJyB0byBudW1lcmljIChpZiBub3QgYWxyZWFkeSkNCmhlYWx0aF9kYXRhIDwtIGhlYWx0aF9kYXRhICU+JQ0KICBtdXRhdGUodXNpYSA9IGFzLm51bWVyaWMoYWdlKSkNCg0KIyBCaW5uaW5nICdhZ2UnIGludG8gYWdlIGNhdGVnb3JpZXMNCmJpbm5pbmcgPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBhZ2VfbGV2ZWwgPSBjdXQoDQogICAgICBhZ2UsDQogICAgICBicmVha3MgPSBxdWFudGlsZShhZ2UsIHByb2JzID0gYygwLCAwLjMzLCAwLjY2LCAxKSwgbmEucm0gPSBUUlVFKSwNCiAgICAgIGxhYmVscyA9IGMoIllvdW5nIiwgIk1pZGRsZS1hZ2VkIiwgIk9sZCIpLA0KICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFDQogICAgKQ0KICApDQpgYGANCg0KDQojIyMgUGVuamVsYXNhbjoNCg0KLSBjdXQoYWdlLCBicmVha3MgPSBxdWFudGlsZShhZ2UsIHByb2JzID0gYygwLCAwLjMzLCAwLjY2LCAxKSwgbmEucm0gPSBUUlVFKSkgZGlndW5ha2FuIHVudHVrIG1lbWJhZ2kgdXNpYSBiZXJkYXNhcmthbiBrdWFudGlsLiANCg0KICAtICowLTMzJSogdXNpYSBwZXJ0YW1hIGFrYW4gZGlnb2xvbmdrYW4ga2UgZGFsYW0ga2F0ZWdvcmkgIllvdW5nIiAoTXVkYSkuDQogIA0KICAtICozNC02NiUqIHVzaWEga2VkdWEgYWthbiBkaWdvbG9uZ2thbiBrZSBkYWxhbSBrYXRlZ29yaSAiTWlkZGxlLWFnZWQiIChQYXJ1aCBCYXlhKS4NCiAgDQogIC0gKjY3LTEwMCUqIHVzaWEga2V0aWdhIGFrYW4gZGlnb2xvbmdrYW4ga2UgZGFsYW0ga2F0ZWdvcmkgIk9sZCIgKFR1YSkuDQogIA0KLSBGdW5nc2kgY3V0KCkgaW5pIGp1Z2EgbWVtYmVyaWthbiBsYWJlbCBwYWRhIHNldGlhcCBrYXRlZ29yaSwgc2VoaW5nZ2EgbWVtdWRhaGthbiBpbnRlcnByZXRhc2kgZGF0YS4NCg0KDQojIyMgTWFuZmFhdCBEaXNjcmV0aXphdGlvbjoNCi0gKlBlbnllZGVyaGFuYWFuIE1vZGVsOiogRGVuZ2FuIG1lbmd1YmFoIHZhcmlhYmVsIGtvbnRpbnUgbWVuamFkaSBrYXRlZ29yaSwga2l0YSBkYXBhdCBtZW5ndXJhbmdpIGtvbXBsZWtzaXRhcyBtb2RlbCBkYW4gbWVtdWRhaGthbiBpbnRlcnByZXRhc2kuDQotICpNZW1wZXJiYWlraSBNb2RlbCBLbGFzaWZpa2FzaToqIEthdGVnb3Jpc2FzaSBkYXBhdCBtZW5pbmdrYXRrYW4ga2luZXJqYSBtb2RlbCBrbGFzaWZpa2FzaSwgdGVydXRhbWEgamlrYSBkYXRhIGtvbnRpbnUgbWVtaWxpa2kgZGlzdHJpYnVzaSB5YW5nIHNhbmdhdCBtaXJpbmcgYXRhdSB0aWRhayBub3JtYWwuDQoNCg0KIyMjIEhhc2lsIFRyYW5zZm9ybWFzaQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KERUKQ0KDQojIE1ha2Ugc3VyZSBjb2x1bW4gbmFtZXMgYXJlIGxvd2VyY2FzZQ0KY29sbmFtZXMoaGVhbHRoX2RhdGEpIDwtIHRvbG93ZXIoY29sbmFtZXMoaGVhbHRoX2RhdGEpKQ0KDQojIENvbnZlcnQgJ2FnZScgdG8gbnVtZXJpYyAoaWYgbm90IGFscmVhZHkpDQpoZWFsdGhfZGF0YSA8LSBoZWFsdGhfZGF0YSAlPiUNCiAgbXV0YXRlKHVzaWEgPSBhcy5udW1lcmljKGFnZSkpDQoNCiMgQmlubmluZyAnYWdlJyBpbnRvIGFnZSBjYXRlZ29yaWVzDQpiaW5uaW5nIDwtIGhlYWx0aF9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgYWdlX2xldmVsID0gY3V0KA0KICAgICAgYWdlLA0KICAgICAgYnJlYWtzID0gcXVhbnRpbGUoYWdlLCBwcm9icyA9IGMoMCwgMC4zMywgMC42NiwgMSksIG5hLnJtID0gVFJVRSksDQogICAgICBsYWJlbHMgPSBjKCJZb3VuZyIsICJNaWRkbGUtYWdlZCIsICJPbGQiKSwNCiAgICAgIGluY2x1ZGUubG93ZXN0ID0gVFJVRQ0KICAgICkNCiAgKQ0KDQojIFNob3cgcmVzdWx0cyBpbiBhIGRhdGF0YWJsZQ0KRFQ6OmRhdGF0YWJsZShoZWFkKGJpbm5pbmcpLCBjYXB0aW9uID0gIkJpbm5pbmcgQWdlIGludG8gQ2F0ZWdvcmllcyIpDQpgYGANCg0KDQoNCiMjIyBLZXNpbXB1bGFuOg0KRGlzY3JldGl6YXRpb24gZGFwYXQgYmVybWFuZmFhdCB1bnR1ayBtZW1wZXJtdWRhaCBwZW1haGFtYW4gZGFuIGFuYWxpc2lzIGRhdGEsIHRlcnV0YW1hIGRhbGFtIGthc3VzIGRpIG1hbmEgaHVidW5nYW4gYW50YXJhIHZhcmlhYmVsIGtvbnRpbnUgZGFuIGhhc2lsIHlhbmcgZGlpbmdpbmthbiB0aWRhayBsaW5pZXIgYXRhdSBzdWxpdCB1bnR1ayBkaW1vZGVsa2FuIHNlY2FyYSBsYW5nc3VuZy4NCg0KDQojIDguIFNlYXNvbmFsaXR5DQoNClNlYXNvbmFsaXR5IGF0YXUgbXVzaW1hbiBhZGFsYWggcG9sYSB5YW5nIG11bmN1bCBzZWNhcmEgcGVyaW9kaWsgZGFsYW0gZGF0YSB5YW5nIHRlcmthaXQgZGVuZ2FuIHdha3R1LCBzZXBlcnRpIHBvbGEgdGFodW5hbiwgYnVsYW5hbiwgYXRhdSBtaW5nZ3Vhbi4gRml0dXIgc2Vhc29uYWxpdHkgc2FuZ2F0IHBlbnRpbmcgZGFsYW0gYW5hbGlzaXMgZGF0YSB0aW1lIHNlcmllcyB1bnR1ayBtZW1haGFtaSBmbHVrdHVhc2kgeWFuZyB0ZXJrYWl0IGRlbmdhbiBwZXJ1YmFoYW4gbXVzaW1hbiBkYWxhbSB0YWh1bi4NCg0KUGFkYSBkYXRhc2V0IGluaSwga2l0YSBha2FuIG1lbWJ1YXQgZml0dXIgbXVzaW1hbiBiZXJkYXNhcmthbiBpbmZvcm1hc2kgdGFuZ2dhbCB1bnR1ayBtZW5hbmdrYXAgcG9sYSB0YWh1bmFuLg0KDQojIyAqKkNvbnRvaDoqKiAgDQotICoqRm91cmllciBUcmFuc2Zvcm0qKiB1bnR1ayBla3N0cmFrc2kgZnJla3VlbnNpLg0KLSAqKkRldGVrc2kgTXVzaW1hbioqIGRhbGFtIGRhdGEgYnVsYW5hbi90YWh1bmFuLg0KDQojIyMgQ29udG9oIEthc3VzOg0KDQpVbnR1ayBtZW1vZGVsa2FuIHNlYXNvbmFsaXR5IGRhbGFtIGRhdGFzZXQga2VzZWhhdGFuIGluaSwga2l0YSBha2FuIG1lbmFtYmFoa2FuIGZpdHVyLWZpdHVyIGJlcmlrdXQ6DQotICpUYWh1biogKFllYXIpDQoNCi0gKkhhcmkgZGFsYW0gVGFodW4qIChEYXkgb2YgWWVhcikNCg0KLSAqRnVuZ3NpIFNpbnVzIGRhbiBDb3NpbnVzKiB1bnR1ayBtZW5ndWJhaCBkYXRhIG11c2ltYW4ga2UgZGFsYW0gZm9ybWF0IG51bWVyaWsgeWFuZyBkYXBhdCBkaWd1bmFrYW4gb2xlaCBtb2RlbCBtYWNoaW5lIGxlYXJuaW5nLg0KDQojIyMgQ29udG9oIEtvZGUgUjoNCg0KYGBge30NCg0KIyBFbnN1cmUgbG93ZXJjYXNlIGNvbHVtbiBuYW1lcw0KY29sbmFtZXMoaGVhbHRoX2RhdGEpIDwtIHRvbG93ZXIoY29sbmFtZXMoaGVhbHRoX2RhdGEpKQ0KDQojIFRyeSB0byBmaW5kIHRoZSBjb2x1bW4gdGhhdCByZXByZXNlbnRzIHRoZSBkYXRlDQpkYXRlX2NvbCA8LSBncmVwKCJ0YW5nZ2FsfGRhdGUiLCBjb2xuYW1lcyhoZWFsdGhfZGF0YSksIHZhbHVlID0gVFJVRSlbMV0NCg0KIyBDb252ZXJ0IHRoZSBjb2x1bW4gdG8gRGF0ZSB0eXBlIGlmIG5vdCBhbHJlYWR5DQpoZWFsdGhfZGF0YVtbZGF0ZV9jb2xdXSA8LSBhcy5EYXRlKGhlYWx0aF9kYXRhW1tkYXRlX2NvbF1dKQ0KDQojIENyZWF0ZSBzZWFzb25hbGl0eSBmZWF0dXJlcw0Kc2Vhc29uYWxpdHkgPC0gaGVhbHRoX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICB5ZWFyID0geWVhciguZGF0YVtbZGF0ZV9jb2xdXSksDQogICAgZGF5X29mX3llYXIgPSB5ZGF5KC5kYXRhW1tkYXRlX2NvbF1dKSwNCiAgICBkYXlzX2luX3llYXIgPSBpZl9lbHNlKGxlYXBfeWVhciguZGF0YVtbZGF0ZV9jb2xdXSksIDM2NiwgMzY1KSwNCiAgICBzaW5feWVhciA9IHNpbigyICogcGkgKiBkYXlfb2ZfeWVhciAvIGRheXNfaW5feWVhciksDQogICAgY29zX3llYXIgPSBjb3MoMiAqIHBpICogZGF5X29mX3llYXIgLyBkYXlzX2luX3llYXIpDQogICkNCmBgYCAgDQoNCg0KDQojIyMgUGVuamVsYXNhbjoNCg0KLSAqVGFodW4gKFllYXIpOiogTWVueWltcGFuIGluZm9ybWFzaSB0YWh1biBkYXJpIGtvbG9tIHRhbmdnYWwuDQoNCi0gKkhhcmkgZGFsYW0gVGFodW4gKERheSBvZiBZZWFyKToqIE1lbmdoaXR1bmcgaGFyaSBrZS1iZXJhcGEgZGFsYW0gdGFodW4gdGVyc2VidXQuDQoNCi0gKlNpbnVzIGRhbiBDb3NpbnVzIChzaW5feWVhciBkYW4gY29zX3llYXIpOiogTWVuZ29udmVyc2kgaGFyaSBkYWxhbSB0YWh1biBtZW5qYWRpIG5pbGFpIHNpbnVzIGRhbiBjb3NpbnVzIHVudHVrIG1lbmFuZ2thcCBzaWZhdCBtdXNpbWFuIGRhbGFtIGJlbnR1ayBudW1lcmlrLiBJbmkgYWRhbGFoIHRla25payB1bXVtIHVudHVrIG1lbmdoaW5kYXJpIG1hc2FsYWggZGVuZ2FuIG1vZGVsIHlhbmcgdGlkYWsgZGFwYXQgbWVuYW5nYW5pIGRhdGEgbXVzaW1hbiB5YW5nIGJlcnNpZmF0IHNpa2xpcy4NCg0KDQoNCiMjIyBNYW5mYWF0IFNlYXNvbmFsaXR5Og0KLSBNZW1haGFtaSBQb2xhIE11c2ltYW46IEZpdHVyIG11c2ltYW4gZGFwYXQgbWVtYmFudHUgZGFsYW0gbWVtcHJlZGlrc2kgcG9sYSB5YW5nIGJlcnVsYW5nLCBzZXBlcnRpIGZsdWt0dWFzaSBrZXNlaGF0YW4gcGFkYSBwZXJpb2RlIHRlcnRlbnR1IGRhbGFtIHNldGFodW4uDQotIFBlbmluZ2thdGFuIEFrdXJhc2kgTW9kZWw6IERlbmdhbiBtZW1hc3Vra2FuIGluZm9ybWFzaSBtdXNpbWFuIGRhbGFtIG1vZGVsLCBraXRhIGRhcGF0IG1lbmluZ2thdGthbiBrZW1hbXB1YW4gcHJlZGlrc2kgdGVydXRhbWEgdW50dWsgZGF0YSB0aW1lIHNlcmllcy4NCg0KDQoNCiMjIyBIYXNpbCBUcmFuc2Zvcm1hc2kgDQoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoRFQpDQoNCiMgRW5zdXJlIGxvd2VyY2FzZSBjb2x1bW4gbmFtZXMNCmNvbG5hbWVzKGhlYWx0aF9kYXRhKSA8LSB0b2xvd2VyKGNvbG5hbWVzKGhlYWx0aF9kYXRhKSkNCg0KIyBUcnkgdG8gZmluZCB0aGUgY29sdW1uIHRoYXQgcmVwcmVzZW50cyB0aGUgZGF0ZQ0KZGF0ZV9jb2wgPC0gZ3JlcCgidGFuZ2dhbHxkYXRlIiwgY29sbmFtZXMoaGVhbHRoX2RhdGEpLCB2YWx1ZSA9IFRSVUUpWzFdDQoNCiMgQ29udmVydCB0aGUgY29sdW1uIHRvIERhdGUgdHlwZSBpZiBub3QgYWxyZWFkeQ0KaGVhbHRoX2RhdGFbW2RhdGVfY29sXV0gPC0gYXMuRGF0ZShoZWFsdGhfZGF0YVtbZGF0ZV9jb2xdXSkNCg0KIyBDcmVhdGUgc2Vhc29uYWxpdHkgZmVhdHVyZXMNCnNlYXNvbmFsaXR5IDwtIGhlYWx0aF9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgeWVhciA9IHllYXIoLmRhdGFbW2RhdGVfY29sXV0pLA0KICAgIGRheV9vZl95ZWFyID0geWRheSguZGF0YVtbZGF0ZV9jb2xdXSksDQogICAgZGF5c19pbl95ZWFyID0gaWZfZWxzZShsZWFwX3llYXIoLmRhdGFbW2RhdGVfY29sXV0pLCAzNjYsIDM2NSksDQogICAgc2luX3llYXIgPSBzaW4oMiAqIHBpICogZGF5X29mX3llYXIgLyBkYXlzX2luX3llYXIpLA0KICAgIGNvc195ZWFyID0gY29zKDIgKiBwaSAqIGRheV9vZl95ZWFyIC8gZGF5c19pbl95ZWFyKQ0KICApDQoNCiMgRGlzcGxheSB0aGUgcmVzdWx0DQpEVDo6ZGF0YXRhYmxlKGhlYWQoc2Vhc29uYWxpdHkpLCBjYXB0aW9uID0gIlNlYXNvbmFsaXR5IEZlYXR1cmVzIEJhc2VkIG9uIERhdGUiKQ0KYGBgDQoNCg0KIyMjICBLZXNpbXB1bGFuOg0KRml0dXIgc2Vhc29uYWxpdHkgc2FuZ2F0IGJlcmd1bmEgZGFsYW0gZGF0YXNldCBkZW5nYW4ga29tcG9uZW4gd2FrdHUgeWFuZyBtZW1wZW5nYXJ1aGkgaGFzaWwsIHNlcGVydGkga2VzZWhhdGFuIHlhbmcgZGFwYXQgZGlwZW5nYXJ1aGkgb2xlaCBwZXJ1YmFoYW4gbXVzaW1hbi4gRGVuZ2FuIG1lbmdndW5ha2FuIGZpdHVyLWZpdHVyIG11c2ltYW4sIG1vZGVsIGRhcGF0IGxlYmloIGVmZWt0aWYgZGFsYW0gbWVuYW5na2FwIHBvbGEgeWFuZyB0ZXJrYWl0IGRlbmdhbiBwZXJ1YmFoYW4gbXVzaW1hbiBkYW4gbWVuaW5na2F0a2FuIGFrdXJhc2kgcHJlZGlrc2kuDQoNCiMgKipQZW51dHVwKiogIA0KDQpUcmFuc2Zvcm1hc2kgZGF0YSBhZGFsYWggdGFoYXAgcGVudGluZyB5YW5nIG1lbmVudHVrYW4ga3VhbGl0YXMgYW5hbGlzaXMgZGFuIHBlbW9kZWxhbi4gVGVrbmlrLXRla25payB5YW5nIHRlbGFoIGRpYmFoYXMgc2VwZXJ0aSB0cmFuc2Zvcm1hc2kgdGVtcG9yYWwsIGRpc3RyaWJ1c2ksIHNjYWxpbmcsIGVuY29kaW5nLCBmZWF0dXJlIGVuZ2luZWVyaW5nLCBoaW5nZ2EgaGFuZGxpbmcgb3V0bGllciwgc2VtdWFueWEgYmVydHVqdWFuIHVudHVrIG1lbWJ1YXQgZGF0YSBsZWJpaCAiYmVyc2FoYWJhdCIgZGVuZ2FuIGFsZ29yaXRtYSBhbmFsaXNpcy4gRGVuZ2FuIHBlbWlsaWhhbiBtZXRvZGUgeWFuZyB0ZXBhdCwga2l0YSBkYXBhdCBtZW5pbmdrYXRrYW4gYWt1cmFzaSBtb2RlbCBkYW4gbWVtYnVhdCBpbnRlcnByZXRhc2kgZGF0YSBtZW5qYWRpIGxlYmloIGJhaWsuDQoNCg0KIyAqKlJlZmVyZW5zaSoqDQoNCg0KaHR0cHM6Ly9ib29rZG93bi5vcmcvZHNjaWVuY2VsYWJzL2RhdGFfc2NpZW5jZV9wcm9ncmFtbWluZy8wNC1EYXRhX0NvbGxlY3Rpb24uaHRtbA0KDQpodHRwczovL3d3dy5zdGF0aXN0aWtpYW4uY29tLzIwMTMvMDEvdHJhbnNmb3JtYXNpLWRhdGEuaHRtbA0KDQpodHRwczovL2V4c2lnaHQuaWQvYmxvZy8yMDIzLzA1LzE0L3RyYW5zZm9ybWFzaS1kYXRhLXBhcnQtMS8NCg0KaHR0cHM6Ly9wYXRyYXN0YXRpc3Rpa2EuY29tL3RyYW5zZm9ybWFzaS1kYXRhLw0KDQpodHRwczovL3d3dy5yZXZvdS5jby9rb3Nha2F0YS9kYXRhLXRyYW5zZm9ybWF0aW9uDQo=