Data Tranformation

Data Science Programming

KELOMPOK 3

Joans Henky Servatius S (52240017)

Nova Sitorus (52240023)

Olivia Meilinda Davtin P (52240011)

Nabila Anggita Putri (52240002)

Rachelia Bevina Tambajong (52240021)

Dwi Sri Yanti Manullang (52240030)

Chello Frhino Mike M (52240031)

May 02, 2025

KELOMPOK 3

kel

1 Temporal Transformation

FINANCIAL MARKET

library(dplyr)
library(tidyverse)
library(readr)
library(zoo)
library(DT)
financial_market <- read_csv("financial market.csv")
datatable(financial_market, options = list(pageLength = 5))

1.1 Lag,Diff,Rolling

  1. Lag: Mengambil nilai dari periode sebelumnya. Berguna untuk membandingkan data sekarang dengan data masa lalu.

  2. Diff: Menghitung selisih antar elemen berturut-turut.. Membantu mengukur perubahan antar waktu.

  3. Rolling Mean: Rata-rata bergerak dari beberapa nilai terakhir (misalnya 5 hari terakhir). Digunakan untuk melihat tren jangka pendek dengan menghaluskan fluktuasi data.

financial_market <- read_csv("financial market.csv")

# Lanjutkan transformasi
Tempral <- financial_market %>%
  mutate(Date = ymd(Date))  # ganti 'Date' kalau nama kolomnya berbeda

# Hitung Lag, Diff, Rolling Mean untuk Stock_Price, Volume_Traded, Market_Cap
LDR <- financial_market %>%
  arrange(Sector, Date) %>%
  group_by(Sector) %>%
  mutate(
    # Lag
    Lag_Stock_Price = lag(Stock_Price),
    Lag_Volume = lag(Volume_Traded),
    Lag_Market_Cap = lag(Market_Cap),
    
    # Diff
    Diff_Stock_Price = round(Stock_Price - Lag_Stock_Price, 2),
    Diff_Volume = Volume_Traded - Lag_Volume,
    Diff_Market_Cap = Market_Cap - Lag_Market_Cap,
    
    # Rolling Mean (3 hari terakhir)
    RollingMean_Stock_Price_3 = round(zoo::rollapply(Stock_Price, width = 3, FUN = mean, fill = NA, align = "right"), 2),
    RollingMean_Volume_3 = round(zoo::rollapply(Volume_Traded, width = 3, FUN = mean, fill = NA, align = "right")),
    RollingMean_Market_Cap_3 = round(zoo::rollapply(Market_Cap, width = 3, FUN = mean, fill = NA, align = "right"))
  ) %>%
  ungroup()

# Tampilkan hasil
datatable(LDR, options = list(pageLength = 5))
  1. Stock_Price: Harga saham sering dianalisis berdasarkan perubahan waktu (misal: naik/turun harga harian).Di-lag untuk melihat harga sebelumnya, diff untuk selisih harian, dan rolling mean untuk tren jangka pendek.

  2. Volume_Traded: Volume transaksi bisa fluktuatif, perlu dilihat perubahannya dan rata-rata dalam window waktu. Di-lag dan diff untuk deteksi lonjakan aktivitas, rolling mean untuk kestabilan volume.

  3. Market_Cap: Di-diff dan rolling mean untuk memantau perubahan nilai pasar saham secara bertahap.

1.2 Extract Date

library(dplyr)
library(lubridate)
library(stringr)

Extract <- financial_market %>%
  mutate(
    Date = as.Date(Date, format = "%m/%d/%Y"),  # konversi dari karakter ke Date
    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),
    Sector = as.factor(Sector),
    Performance = as.factor(Performance)
  ) %>%
  bind_cols(
    as.data.frame(model.matrix(~ Sector - 1, data = .)),
    as.data.frame(model.matrix(~ Performance - 1, data = .))
  )

# Lihat beberapa baris pertama
datatable(Extract, options = list(pageLength = 5))

Konversi Tanggal: Variabel Date dikonversi dari format karakter ke format Date agar dapat dianalisis secara temporal.

Ekstraksi Informasi Waktu: Dari tanggal tersebut, ditambahkan beberapa informasi waktu:

Day_of_Week: hari dalam minggu,

Month: bulan (dalam format label, misalnya Jan, Feb),

Year: tahun transaksi,

Is_Weekend: indikator apakah hari tersebut akhir pekan (Sabtu/Minggu).

1.3 Cumulative Value

Tempral3 <- financial_market %>%
  arrange(Stock_ID, Date) %>%
  group_by(Stock_ID) %>%
  mutate(
    Cumulative_Volume = cumsum(Volume_Traded),
    Cumulative_MarketCap = cumsum(Market_Cap),
    Cumulative_AvgPrice = cummean(Stock_Price)
  ) %>%
  ungroup()

head(Tempral3)

Potongan kode ini digunakan untuk menghitung akumulasi data pasar saham secara temporal berdasarkan setiap Stock_ID. Data pertama-tama diurutkan berdasarkan Stock_ID dan Date agar analisis bersifat time-series. Kemudian, untuk setiap stock (group_by(Stock_ID)), dilakukan tiga perhitungan:

  1. Cumulative_Volume: total volume perdagangan yang terus dijumlahkan dari waktu ke waktu.
  2. Cumulative_MarketCap: akumulasi kapitalisasi pasar hingga tanggal tertentu.
  3. Cumulative_AvgPrice: rata-rata harga saham secara kumulatif (hingga titik waktu tersebut) menggunakan cummean().

Fungsi ungroup() digunakan agar hasilnya tidak lagi bersifat kelompok. Hasil akhir memberi gambaran perkembangan historis kinerja setiap saham dari waktu ke waktu.

2 Distribution Transformation

2.1 Log Transform

Variabel yang dipilih adalah Kapasitas pasar alasannya adalah, karena kapasitas pasar mempunyai angka yang sangat besar dan distribusinya sangat miring ke kanan (right-skewed).

library(dplyr)

min_positive <- min(financial_market$Market_Cap[financial_market$Market_Cap > 0])

Log <- financial_market %>%
  mutate(
    Safe_Market_Cap = ifelse(Market_Cap <= 0, min_positive, Market_Cap),
    Log_Market_Cap = log1p(Safe_Market_Cap)
  )
head(Log)

Interpretasi:

Transformasi log digunakan untuk:

  • Menstabilkan varians yang meningkat seiring dengan nilai variabel.

  • Mengurangi skewness (kemiringan distribusi) terutama pada data yang terdistribusi skewed ke kanan seperti Kapitalisasi.Pasar.

  • Dalam kode, nilai nol atau negatif diganti dengan nilai positif terkecil agar log() tidak error.

  • Fungsi log1p(x) (yaitu log(x + 1)) digunakan sebagai bentuk aman untuk data nol.

Hasil:

Distribusi menjadi lebih simetris dan varians antar level data menjadi lebih seimbang, sehingga lebih cocok untuk analisis regresi.

  • Bertambah 2 variabel, yaitu: Safe_Kapitalisasi_Pasar dan Log_Kapitalisasi_Pasar

2.2 Box-Cox

library(bestNormalize)
financial_market <- read.csv("financial market.csv")

Box_Cox <- financial_market %>%
  mutate(
    YeoJ_Volume_Traded = bestNormalize(Volume_Traded)$x.t,
    YeoJ_Market_Cap = bestNormalize(Market_Cap)$x.t
  )
head(Box_Cox)

Interpretasi:

  • Menggunakan fungsi bestNormalize() yang otomatis memilih metode transformasi terbaik agar data mendekati distribusi normal.

  • Transformasi dilakukan pada Volume_Diperdagangkan dan Kapitalisasi.Pasar, dan hasilnya disimpan sebagai kolom baru.

  • Praktik ini ideal untuk preprocessing sebelum modeling, karena membantu mengurangi skewness dan menstabilkan varians data.

2.3 Stabilisasi Varians

library(DT)

Stabilize <- financial_market %>%
  mutate(
    sqrt_Volume_Traded = sqrt(pmax(Volume_Traded, 0)),
    sqrt_Market_Cap = sqrt(pmax(Market_Cap, 0))
  )%>%
  ungroup()


datatable(Stabilize, options = list(pageLength = 5))

2.3.1 Interpretasi:

Transformasi akar kuadrat (sqrt) sering digunakan dalam analisis data keuangan atau statistik untuk:

  • Menstabilkan varians: Data seperti Volume_Traded dan Market_Cap bisa sangat bervariasi antar perusahaan (ada yang kecil, ada yang sangat besar). Transformasi akar membantu mengurangi efek ekstrem ini.

  • Mengurangi skewness: Banyak variabel keuangan bersifat sangat miring ke kanan (right-skewed). Akar kuadrat membuat distribusinya lebih simetris (dekat normal), yang penting untuk analisis regresi atau machine learning.

  • Menghindari masalah nilai negatif: pmax(…, 0) memastikan nilai negatif diubah menjadi nol sebelum dihitung akarnya, karena akar dari bilangan negatif tidak valid dalam konteks real.

2.3.2 Contoh Interpretasi Kasus:

Misalnya, dua perusahaan:

  • Perusahaan A: Market_Cap = 1.000.000
  • Perusahaan B: Market_Cap = 10.000

Tanpa transformasi, Perusahaan A terlihat 100× lebih besar. Setelah akar kuadrat:

  • sqrt(1.000.000) = 1000
  • sqrt(10.000) = 100

Sekarang Perusahaan A hanya terlihat 10× lebih besar, membuat analisis lebih proporsional.

3 Scaling & Normalization

3.1 Z-Score Standardization

# Standardisasi Z-Score: mengubah data agar memiliki rata-rata 0 dan standar deviasi 1
financial_scaled <- financial_market %>%
  mutate(
    Stock_Price_Std = scale(Stock_Price),
    Volume_Traded_Std = scale(Volume_Traded),
    Market_Cap_Std = scale(Market_Cap),
    PE_Ratio_Std = scale(PE_Ratio),
    Dividend_Yield_Std = scale(Dividend_Yield),
    Return_on_Equity_Std = scale(Return_on_Equity)
  )
head(financial_scaled)

3.1.1 Penjelasan:

  1. financial_market adalah data frame yang berisi data pasar keuangan. Kolom-kolomnya mencakup variabel seperti harga saham (Stock_Price), volume perdagangan (Volume_Traded), kapitalisasi pasar (Market_Cap), rasio PE (PE_Ratio), dividen (Dividend_Yield), dan ROE (Return_on_Equity).

  2. Fungsi mutate() dari paket dplyr digunakan untuk menambahkan kolom-kolom baru hasil transformasi dari kolom yang ada.

  3. Z-Score Standardization adalah metode transformasi data yang mengubah setiap nilai menjadi ukuran dalam satuan standar deviasi dari rata-rata.

  4. Fungsi scale() adalah fungsi bawaan R untuk melakukan Z-score standardization, yaitu: \[ Z = \frac{X - \mu } {\sigma} \]

  • X: nilai asli

  • \(\mu\) : rata-rata data

  • \(\sigma\): standar deviasi data

Hasilnya adalah data baru yang:

  • Memiliki rata-rata = 0

  • Memiliki standar deviasi = 1

  1. Kolom baru dengan akhiran _Std dibuat untuk setiap variabel, seperti:
  • Stock_Price_Std

  • Volume_Traded_Std

  • Market_Cap_Std

  • PE_Ratio_Std

  • Dividend_Yield_Std

  • Return_on_Equity_Std

  1. head(financial_scaled) digunakan untuk menampilkan 6 baris pertama dari data yang telah ditransformasi.

3.1.2 Contoh Perhitungan Manual

Misalkan data Stock_Price: [100, 120, 150, 130, 110]

  1. Hitung Mean:

\[ \mu = \frac{100 + 120 + 150 + 130 + 110 } {5} = \frac{610}{5} = 122\]

  1. Hitung Standar Deviasi (σ):

\[ \sigma = \sqrt{\frac{(100 - 122)^2 + (120 - 122)^2 + (150 - 122)^2 + (130 - 122)^2 + (110 - 122)^2 } {5}} \]

\[ = \sqrt{\frac{484 + 4 + 784 + 64 + 144}{5}} = \sqrt{\frac{1480}{5}} = \sqrt{296} = 17.2 \]

  1. Z-Score untuk nilai 150:

\[ Z = \frac{150 - 122 } {17.2} = \frac{28}{17.2} = 1.63 \]

3.2 Min-Max Normalization

# Normalisasi Min-Max: mengubah skala data ke dalam rentang [0, 1]
financial_normalized <- financial_market %>%
  mutate(
    Stock_Price_Norm = (Stock_Price - min(Stock_Price)) / (max(Stock_Price) - min(Stock_Price)),
    Volume_Traded_Norm = (Volume_Traded - min(Volume_Traded)) / (max(Volume_Traded) - min(Volume_Traded)),
    Market_Cap_Norm = (Market_Cap - min(Market_Cap)) / (max(Market_Cap - min(Market_Cap))),
    PE_Ratio_Norm = (PE_Ratio - min(PE_Ratio)) / (max(PE_Ratio) - min(PE_Ratio)),
    Dividend_Yield_Norm = (Dividend_Yield - min(Dividend_Yield)) / (max(Dividend_Yield) - min(Dividend_Yield)),
    Return_on_Equity_Norm = (Return_on_Equity - min(Return_on_Equity)) / (max(Return_on_Equity) - min(Return_on_Equity))
  )
head(financial_normalized)

3.2.1 Penjelasan

  1. financial_market adalah data frame yang berisi data pasar keuangan. Kolom-kolomnya mencakup variabel seperti harga saham (Stock_Price), volume perdagangan (Volume_Traded), kapitalisasi pasar (Market_Cap), rasio PE (PE_Ratio), dividen (Dividend_Yield), dan ROE (Return_on_Equity).

  2. Fungsi mutate() dari paket dplyr digunakan untuk menambahkan kolom-kolom baru hasil transformasi dari kolom yang ada.

  3. Normalisasi Min-Max adalah metode penskalaan data ke dalam rentang tetap, yaitu [0, 1], dengan menggunakan rumus berikut: \[ X_{norm} = \frac{X - X_{min} } {X_{max} - X_{min}} \]

  • X: nilai asli

  • \(X_{min}\) : nilai minimum dari fitur

  • \(X_{min}\) : nilai maksimum dari fitur

Hasilnya adalah data baru yang:

  • Memiliki nilai terendah = 0

  • Memiliki nilai tertinggi = 1

  • Tetap mempertahankan distribusi asli datanya (namun rentangnya dipersempit)

  1. Kolom baru dengan akhiran _Norm dibuat untuk setiap variabel, seperti:
  • Stock_Price_Norm

  • Volume_Traded_Norm

  • Market_Cap_Norm

  • PE_Ratio_Norm

  • Dividend_Yield_Norm

  • Return_on_Equity_Norm

  1. Fungsi head(financial_normalized) digunakan untuk menampilkan 6 baris pertama dari data yang telah dinormalisasi.

3.2.2 Contoh Perhitungan Manual

Data Stock_Price:[100, 120, 150, 130, 110]

  1. Minimum dan Maksimum:
  • Min: 100

  • Max: 150

  1. Min-Max Normalization untuk nilai 130: \[ X_{norm} = \frac{130 - 100 } {150 - 100} = \frac{30}{50} = 0.6\]

4 Categorical Encoding

Di dalam data, kadang kita menemukan kolom yang isinya bukan angka, tapi kata-kata atau kategori, misalnya:

  • Sector = “Finance”, “Retail”, “Technology”

  • Performance = “Positive”, “Negative”, “Stable”

library(readr)
library(dplyr)

# Baca file CSV-nya
financial_market <- read_csv("financial market.csv")

# Tampilkan 6 data teratas
head(financial_market)

Masalahnya, komputer hanya bisa membaca angka. Maka, kita perlu mengubah kata-kata ini menjadi angka. Caranya disebut Categorical Encoding.

4.1 One Hot Encoding

Gampangnya: Kita bikin kolom baru untuk setiap kategori, lalu kita kasih angka 1 jika cocok, dan 0 kalau tidak.

Kenapa Nggak Pakai Angka Biasa (1, 2, 3)?

Karena kalau kita kasih angka seperti:

  • Positive = 1

  • Negative = 2

  • Stable = 3

Komputer bisa salah paham dan mengira ada urutan atau ranking, padahal nggak ada.

Padahal kategori itu cuma label, bukan nilai.

Makanya kita pakai biner (0 dan 1), biar komputer ngerti bahwa semua kategori itu setara, nggak ada yang lebih tinggi atau lebih rendah.

berikut kodenya:

library(fastDummies)

# Ubah kolom Sector & Performance jadi bentuk angka biner (0/1)
one_hot <- dummy_cols(financial_market, 
                      select_columns = c("Sector", "Performance"), 
                      remove_first_dummy = FALSE, 
                      remove_selected_columns = TRUE)

# Tampilkan 6 data hasil one hot encoding
head(one_hot)

4.2 Frequency Encoding

Gampangnya: Kita hitung berapa kali setiap kategori muncul, lalu setiap data diganti dengan angka frekuensinya.

# Fungsi untuk menghitung frekuensi (berapa sering muncul)
freq_enc <- function(col) {
  tab <- table(col)
  return(as.numeric(tab[col]) / length(col))
}

# Tambahkan kolom frekuensi ke data
data_freq <- financial_market %>%
  mutate(
    Sector_freq = freq_enc(Sector),
    Performance_freq = freq_enc(Performance)
  )

# Tampilkan 6 data teratas
head(data_freq)

Hasilnya:
Akan muncul dua kolom baru:

  • Sector_freq: berisi angka frekuensi dari nilai di kolom Sector

  • Performance_freq: frekuensi dari kolom Performance

Setiap baris akan berisi angka pecahan (misalnya 0.360, 0.336) yang menunjukkan seberapa sering nilai tersebut muncul dalam keseluruhan data.

5 Feature Engineering

# Feature Engineering pada Financial_market

Feature_Eng <- financial_market %>%
  mutate(
    # 1. New Features from Raw Data
    Price_Per_Volume = Stock_Price / (Volume_Traded + 1e-5),  # Hindari pembagian dengan nol
    
    # 2. Product of Features, Crossed Terms
    PE_x_ROE = PE_Ratio * Return_on_Equity,
    
    # 3. Price per Unit, Efficiency
    Yield_to_PE = Dividend_Yield / (PE_Ratio + 1e-5),  # Yield per PE

    # 4. Ranking, Percentile
    MarketCap_Rank = rank(-Market_Cap),  # Rank market cap terbesar ke terkecil
    ROE_Quartile = ntile(Return_on_Equity, 4),  # Bagi ROE ke dalam 4 kuartil
    
    # 5. From IDs: Prefix, Length, Pattern
    Stock_ID_Prefix = substr(Stock_ID, 1, 3),
    Stock_ID_Length = nchar(Stock_ID)
  ) %>%
  
  # 6. Avg, Sum, Count by Group
  group_by(Sector) %>%
  mutate(
    Avg_PE_Sector = mean(PE_Ratio, na.rm = TRUE),
    Avg_ROE_Sector = mean(Return_on_Equity, na.rm = TRUE),
    Total_Companies_Sector = n()
  ) %>%
  ungroup()

head(Feature_Eng)

5.0.1 Penjelasan

  1. New Features from Raw Data
  • Price_Per_Volume: Mengukur seberapa besar harga saham dibanding volume perdagangannya. Digunakan untuk melihat apakah saham diperdagangkan terlalu banyak atau terlalu sedikit terhadap nilainya.

    • Ditambah 1e-5 untuk mencegah pembagian nol.
  1. Product of Features, Crossed Terms
  • Mengalikan Price to Earnings Ratio (P/E) dengan Return on Equity (ROE) menghasilkan interaksi antar dua metrik penting:

    • PE Ratio menunjukkan valuasi saham.

    • ROE menunjukkan efisiensi perusahaan menghasilkan laba dari modal sendiri.

  • Interaksi ini bisa memberi sinyal apakah saham undervalued atau overvalued dengan memperhitungkan efisiensi.

  1. Price per Unit, Efficiency
  • Yield_to_PE: Efisiensi hasil dividen terhadap nilai valuasi perusahaan.

    • Saham dengan rasio tinggi dianggap memberikan return dividen yang besar dengan valuasi rendah (menarik untuk investor dividen).

    • Lagi-lagi ditambah 1e-5 untuk menghindari pembagian nol.

  1. Ranking, Percentile
  • MarketCap_Rank: Memberi ranking terhadap perusahaan berdasarkan kapitalisasi pasar dari terbesar (rank 1) ke terkecil.

  • ROE_Quartile: Mengelompokkan ROE ke dalam 4 kuartil. Tujuannya untuk klasifikasi performa perusahaan berdasarkan ROE:

    • Kuartil 1: ROE rendah

    • Kuartil 4: ROE tinggi (perusahaan sangat menguntungkan)

  1. From IDs: Prefix, Length, Pattern
  • Stock_ID_Prefix: Mengambil 3 karakter awal dari ID saham. Ini bisa menunjukkan kode kategori, jenis saham, atau asal bursa.

  • Stock_ID_Length: Panjang dari ID saham. Bisa membantu deteksi format ID yang tidak lazim.

  1. Avg, Sum, Count by Group
  • Fitur ini menghitung statistik agregat berdasarkan Sector:

    • Avg_PE_Sector: Rata-rata PE untuk seluruh perusahaan di sektor tersebut.

    • Avg_ROE_Sector: Rata-rata ROE.

    • Total_Companies_Sector: Jumlah perusahaan dalam sektor.

  • Berguna untuk membandingkan kinerja perusahaan dengan rata-rata sektornya (relative valuation).

5.1 Interaction Features

Financial_interaction <- financial_market %>%
  mutate(
    Impact_Score = Stock_Price * Return_on_Equity
  )
head(Financial_interaction)

5.1.1 Penjelasan

  • Impact_Score: Mencerminkan “kekuatan pasar” suatu saham berdasarkan harga dan return ekuitasnya.

    • Semakin tinggi nilai ini, semakin signifikan pengaruh saham terhadap pasar atau investor.

5.2 Ratio Features

Financial_ratio <- financial_market %>%
  mutate(
    Dividend_to_Price = Dividend_Yield / (Stock_Price + 1e-5)
  )
head(Financial_ratio)

5.2.1 Penjelasan

  • Dividend_to_Price: Mengukur imbal hasil dividen relatif terhadap harga saham.

    • Saham dengan rasio tinggi memberikan dividen besar meskipun harganya rendah.

    • Cocok untuk identifikasi saham “dividen murah”.

5.3 Group Aggregation

Sector_summary <- financial_market %>%
  group_by(Sector) %>%
  summarise(
    Avg_Market_Cap = mean(Market_Cap),
    Max_Stock_Price = max(Stock_Price),
    Company_Count = n()
  )

Financial_merged <- left_join(financial_market, Sector_summary, by = "Sector")
head(Financial_merged)

5.3.1 Penjelasan

  • Sector_summary membuat ringkasan sektor:

    • Avg_Market_Cap: Kapitalisasi pasar rata-rata di sektor.

    • Max_Stock_Price: Harga saham tertinggi di sektor.

    • Company_Count: Jumlah perusahaan per sektor.

  • Lalu left_join dilakukan untuk menggabungkan informasi ini kembali ke data utama, memungkinkan perbandingan antar perusahaan dalam satu sektor.

5.4 Rank Transformation

Financial_rank_transform <- financial_market %>%
  mutate(
    ROE_Rank = rank(-Return_on_Equity)
  )
head(Financial_rank_transform)

5.4.1 Penjelasan

  • ROE_Rank: Memberi peringkat terhadap perusahaan berdasarkan ROE tertinggi ke terendah.

    • Dapat digunakan untuk mengurutkan dan memilih saham paling menguntungkan.

5.5 Text Cleaning & Feature Creation

Financial_text_feature <- financial_market %>%
  mutate(
    Stock_Num_Suffix = as.numeric(str_extract(Stock_ID, "\\d+"))
  )
head(Financial_text_feature)

5.5.1 Penjelasan

  • Stock_Num_Suffix: Mengekstrak angka dari ID saham. Ini berguna jika ID saham mengandung pola yang menyiratkan kategori, batch, atau grup.

    • Contoh: ID “ABC123” → suffix = 123

5.6 Cumulative Features

Financial_cumulative <- financial_market %>%
  arrange(Sector, Date) %>%
  group_by(Sector) %>%
  mutate(
    Cumulative_Volume = cumsum(Volume_Traded)
  ) %>%
  ungroup()
head(Financial_cumulative)

5.6.1 Penjelasan

  • Cumulative_Volume: Menjumlahkan total volume perdagangan dari waktu ke waktu untuk setiap sektor.

    • Cocok untuk melihat tren kumulatif aktivitas pasar di sektor tertentu.

    • Bisa digunakan dalam analisis momentum atau tren jangka panjang.

5.7 Kesimpulan Umum

Semua proses feature engineering ini bertujuan untuk:

  1. Memperkaya data: Membuat fitur baru yang bisa menangkap pola-pola tersembunyi di data mentah.

  2. Mempermudah analisis eksploratif: Dengan agregasi, ranking, dan transformasi, kita bisa lebih mudah melihat perusahaan unggulan, tren sektor, dan perbandingan antar perusahaan.

  3. Meningkatkan performa model machine learning atau prediksi: Fitur yang informatif dan relevan dapat membantu model mengenali hubungan antara variabel lebih baik.

  4. Memberi insight bisnis dan investasi: Seperti pemeringkatan saham, klasifikasi sektor, dan identifikasi saham dengan performa keuangan baik.

6 Deteksi Outlier Menggunakan Z-score dan IQR

library(dplyr)

# --- Z-score method for detecting outliers ---
data_outliers <- financial_market %>%
  mutate(
    z_scores_SP = scale(Stock_Price),
    Outlier_Flag = ifelse(abs(z_scores_SP) > 3, "Outlier", "Normal")
  )

# --- IQR method for detecting and removing outliers ---
Q1 <- quantile(financial_market$Total_Price, 0.25)
Q3 <- quantile(financial_market$Total_Price, 0.75)
IQR_val <- Q3 - Q1

data_outliers_iqr <- financial_market %>%
  filter(
    Stock_Price > (Q1 - 1.5 * IQR_val) & 
    Stock_Price < (Q3 + 1.5 * IQR_val)
  )

head(data_outliers)

1. Deteksi Outlier dengan Metode Z-Score

  • Anda menggunakan Z-score method untuk mendeteksi outlier pada kolom Stock_Price.
  • Z-score mengukur seberapa jauh suatu nilai dari rata-rata dalam satuan standar deviasi.
    • Rumus: Z = (x - mean) / sd
  • Baris data yang memiliki nilai Z-score lebih dari 3 atau kurang dari -3 dianggap outlier.
  • Kolom baru Outlier_Flag ditambahkan, berisi label:
    • Outlier jika nilai Z-score melebihi 3 atau kurang dari -3,
    • Normal jika masih dalam batas normal.

2. Pembersihan Outlier dengan Metode IQR

  • Metode kedua menggunakan Interquartile Range (IQR) pada kolom Total_Price untuk mendeteksi dan menghapus outlier.

  • Perhitungan:

    • Q1 (kuartil 1) = nilai pada persentil ke-25,
    • Q3 (kuartil 3) = nilai pada persentil ke-75,
    • IQR = Q3 - Q1.
  • Data dianggap outlier jika berada di luar rentang: \[ \left[ Q_1 - 1.5 \times \text{IQR},\; Q_3 + 1.5 \times \text{IQR} \right] \]

  • Baris yang berada di luar batas ini difilter dan dihapus dari data dengan filter().

  • Z-score untuk mendeteksi outlier pada Stock_Price,

  • IQR untuk menghapus outlier pada Total_Price.

  • Metode ini penting untuk membersihkan data, sehingga analisis atau model yang akan dibangun tidak bias oleh nilai ekstrem.

7 Discretization (Binning)

library(dplyr)

# Baca data
financial_market <- financial_market

# Binning (Equal-frequency) dengan quantile
binned_data <- financial_market %>%
  mutate(
    Price_Level = cut(
      Stock_Price,
      breaks = quantile(Stock_Price, probs = c(0, 0.33, 0.66, 1), na.rm = TRUE),
      labels = c("Low", "Medium", "High"),
      include.lowest = TRUE
    )
  )

#Tampilkan hasil binning
head(binned_data)

1. Tujuan Binning

  • Tujuannya adalah untuk mengelompokkan data numerik dari kolom Stock_Price menjadi beberapa kategori diskrit (dalam hal ini: Low, Medium, High).
  • Ini disebut **equal-frequency binning*, karena setiap kelompok akan memiliki jumlah data yang kurang lebih sama.

2. Proses yang Dillakukan

  • quantile() digunakan untuk menentukan batas-batas (breaks) berdasarkan persentil:
    • 0% (minimum),
    • 33% (sepertiga data),
    • 66% (dua pertiga data),
    • 100% (maksimum).
  • Kemudian fungsi cut() membagi kolom Stock_Price ke dalam tiga kelompok berdasarkan batas-batas tersebut, yaitu:
    • Low: nilai dari kuantil ke-0 hingga ke-33,
    • Medium: kuantil ke-33 hingga ke-66,
    • High: kuantil ke-66 hingga ke-100.
  • Kolom hasil kategorisasi tersebut dinamakan Price_Level.

8 Seasonality

library(dplyr)
library(lubridate)

Seasonality <- financial_market %>%
  mutate(
    Date = as.Date(Date, format = "%m/%d/%Y"),
    Year = year(Date),
    DayOfYear = yday(Date),
    DaysInYear = if_else(leap_year(Date), 366, 365),
    sin_year = sin(2 * pi * DayOfYear / DaysInYear),
    cos_year = cos(2 * pi * DayOfYear / DaysInYear)
  )

head(Seasonality)

Kesimpulan yang Didukung oleh Data

  1. Musim Maret–Juni (sin_year tinggi):
  • 2023-03-15 dan 2023-06-05 menunjukkan sin_year sangat tinggi dan performa masing-masing adalah Stable dan Positive.

  • Ini mendukung pola bahwa kuartal 2 (Q2) sering menunjukkan performa kuat atau stabil.

  1. Akhir Tahun – November (sin_year negatif tinggi):
  • 2020-11-16 (sin_year = -0.6979) : Stable

  • Tidak ekstrem negatif, tapi mendekati. Menunjukkan kinerja yang tidak terlalu kuat, sesuai dengan dugaan bahwa di akhir tahun bisa terjadi tekanan pasar (misalnya, aksi ambil untung/take profit).

  1. Awal Tahun – Januari (sin dan cos mendekati ekstrem positif):
  • 2023-01-02 : sin_year ≈ 0, cos_year ≈ 1 : Stable

  • Sesuai dengan interpretasi bahwa awal tahun cenderung netral atau belum menunjukkan tren jelas.

  1. Pertengahan Tahun – Juli (sin_year mendekati nol, cos_year negatif tinggi):
  • 2021-07-14 : Positive meskipun sin_year = -0.2135 : berarti ada potensi kinerja baik di pertengahan tahun, meskipun secara musiman berada dalam transisiturun.
  1. Anomali:
  • 2023-03-22 : sin_year sangat tinggi (0.9845) tapi performa Negative. Ini menunjukkan bahwa meskipun sinyal musiman mendukung pertumbuhan, faktor lain (mungkin eksternal atau sektor tertentu) bisa memengaruhi negatif.
LS0tDQp0aXRsZTogIkRhdGEgVHJhbmZvcm1hdGlvbiINCnN1YnRpdGxlOiAiRGF0YSBTY2llbmNlIFByb2dyYW1taW5nIg0KYXV0aG9yOiANCiAgLSAiS0VMT01QT0sgMyINCiAgLSAiSm9hbnMgSGVua3kgU2VydmF0aXVzIFMgKDUyMjQwMDE3KSINCiAgLSAiTm92YSBTaXRvcnVzICg1MjI0MDAyMykiDQogIC0gIk9saXZpYSBNZWlsaW5kYSBEYXZ0aW4gUCAoNTIyNDAwMTEpIg0KICAtICJOYWJpbGEgQW5nZ2l0YSBQdXRyaSAoNTIyNDAwMDIpIg0KICAtICJSYWNoZWxpYSBCZXZpbmEgVGFtYmFqb25nICg1MjI0MDAyMSkiDQogIC0gIkR3aSBTcmkgWWFudGkgTWFudWxsYW5nICg1MjI0MDAzMCkiDQogIC0gIkNoZWxsbyBGcmhpbm8gTWlrZSBNICg1MjI0MDAzMSkiDQpkYXRlOiAgImByIGZvcm1hdChTeXMuRGF0ZSgpLCAnJUIgJWQsICVZJylgIg0Kb3V0cHV0Og0KICBybWRmb3JtYXRzOjpkb3duY3V0ZTogICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9qdWJhL3JtZGZvcm1hdHMNCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQ0KICAgIHRodW1ibmFpbHM6IHRydWUNCiAgICBsaWdodGJveDogdHJ1ZQ0KICAgIGdhbGxlcnk6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICBsaWJfZGlyOiBsaWJzDQogICAgZGZfcHJpbnQ6ICJwYWdlZCINCiAgICBjb2RlX2ZvbGRpbmc6ICJzaG93Ig0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIA0KLS0tDQoNCjxzdHlsZT4NCiAgYm9keSB7DQogICAgdGV4dC1hbGlnbjoganVzdGlmeTsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOiB3aGl0ZTsNCiAgICBvdmVyZmxvdy14OiBhdXRvOw0KICAgIGZvbnQtZmFtaWx5OiBzZXJpZjsNCiAgfQ0KPC9zdHlsZT4NCg0KKipLRUxPTVBPSyAzKioNCg0KPGltZyBpZD0ia2Vsb21wb2szIiBzcmM9IkM6L1VzZXJzL1VTRVIvRG9jdW1lbnRzL0RTUEtFTDMva2VsMy5qcGciIGFsdD0ia2VsIiBzdHlsZT0id2lkdGg6ODAwcHg7IGRpc3BsYXk6IGJsb2NrOyBtYXJnaW46IGF1dG87Ij4NCg0KDQoNCg0KIyBUZW1wb3JhbCBUcmFuc2Zvcm1hdGlvbg0KDQoqKkZJTkFOQ0lBTCBNQVJLRVQqKg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoem9vKQ0KbGlicmFyeShEVCkNCmZpbmFuY2lhbF9tYXJrZXQgPC0gcmVhZF9jc3YoImZpbmFuY2lhbCBtYXJrZXQuY3N2IikNCmRhdGF0YWJsZShmaW5hbmNpYWxfbWFya2V0LCBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gNSkpDQpgYGANCg0KIyMgTGFnLERpZmYsUm9sbGluZw0KDQoxLiAqKkxhZyoqOiBNZW5nYW1iaWwgbmlsYWkgZGFyaSBwZXJpb2RlIHNlYmVsdW1ueWEuIEJlcmd1bmEgdW50dWsgbWVtYmFuZGluZ2thbiBkYXRhIHNla2FyYW5nIGRlbmdhbiBkYXRhIG1hc2EgbGFsdS4NCg0KMi4gKipEaWZmKio6IE1lbmdoaXR1bmcgc2VsaXNpaCBhbnRhciBlbGVtZW4gYmVydHVydXQtdHVydXQuLiBNZW1iYW50dSBtZW5ndWt1ciBwZXJ1YmFoYW4gYW50YXIgd2FrdHUuDQoNCjMuICoqUm9sbGluZyBNZWFuKio6IFJhdGEtcmF0YSBiZXJnZXJhayBkYXJpIGJlYmVyYXBhIG5pbGFpIHRlcmFraGlyIChtaXNhbG55YSA1IGhhcmkgdGVyYWtoaXIpLiBEaWd1bmFrYW4gdW50dWsgbWVsaWhhdCB0cmVuIGphbmdrYSBwZW5kZWsgZGVuZ2FuIG1lbmdoYWx1c2thbiBmbHVrdHVhc2kgZGF0YS4NCg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCmZpbmFuY2lhbF9tYXJrZXQgPC0gcmVhZF9jc3YoImZpbmFuY2lhbCBtYXJrZXQuY3N2IikNCg0KIyBMYW5qdXRrYW4gdHJhbnNmb3JtYXNpDQpUZW1wcmFsIDwtIGZpbmFuY2lhbF9tYXJrZXQgJT4lDQogIG11dGF0ZShEYXRlID0geW1kKERhdGUpKSAgIyBnYW50aSAnRGF0ZScga2FsYXUgbmFtYSBrb2xvbW55YSBiZXJiZWRhDQoNCiMgSGl0dW5nIExhZywgRGlmZiwgUm9sbGluZyBNZWFuIHVudHVrIFN0b2NrX1ByaWNlLCBWb2x1bWVfVHJhZGVkLCBNYXJrZXRfQ2FwDQpMRFIgPC0gZmluYW5jaWFsX21hcmtldCAlPiUNCiAgYXJyYW5nZShTZWN0b3IsIERhdGUpICU+JQ0KICBncm91cF9ieShTZWN0b3IpICU+JQ0KICBtdXRhdGUoDQogICAgIyBMYWcNCiAgICBMYWdfU3RvY2tfUHJpY2UgPSBsYWcoU3RvY2tfUHJpY2UpLA0KICAgIExhZ19Wb2x1bWUgPSBsYWcoVm9sdW1lX1RyYWRlZCksDQogICAgTGFnX01hcmtldF9DYXAgPSBsYWcoTWFya2V0X0NhcCksDQogICAgDQogICAgIyBEaWZmDQogICAgRGlmZl9TdG9ja19QcmljZSA9IHJvdW5kKFN0b2NrX1ByaWNlIC0gTGFnX1N0b2NrX1ByaWNlLCAyKSwNCiAgICBEaWZmX1ZvbHVtZSA9IFZvbHVtZV9UcmFkZWQgLSBMYWdfVm9sdW1lLA0KICAgIERpZmZfTWFya2V0X0NhcCA9IE1hcmtldF9DYXAgLSBMYWdfTWFya2V0X0NhcCwNCiAgICANCiAgICAjIFJvbGxpbmcgTWVhbiAoMyBoYXJpIHRlcmFraGlyKQ0KICAgIFJvbGxpbmdNZWFuX1N0b2NrX1ByaWNlXzMgPSByb3VuZCh6b286OnJvbGxhcHBseShTdG9ja19QcmljZSwgd2lkdGggPSAzLCBGVU4gPSBtZWFuLCBmaWxsID0gTkEsIGFsaWduID0gInJpZ2h0IiksIDIpLA0KICAgIFJvbGxpbmdNZWFuX1ZvbHVtZV8zID0gcm91bmQoem9vOjpyb2xsYXBwbHkoVm9sdW1lX1RyYWRlZCwgd2lkdGggPSAzLCBGVU4gPSBtZWFuLCBmaWxsID0gTkEsIGFsaWduID0gInJpZ2h0IikpLA0KICAgIFJvbGxpbmdNZWFuX01hcmtldF9DYXBfMyA9IHJvdW5kKHpvbzo6cm9sbGFwcGx5KE1hcmtldF9DYXAsIHdpZHRoID0gMywgRlVOID0gbWVhbiwgZmlsbCA9IE5BLCBhbGlnbiA9ICJyaWdodCIpKQ0KICApICU+JQ0KICB1bmdyb3VwKCkNCg0KIyBUYW1waWxrYW4gaGFzaWwNCmRhdGF0YWJsZShMRFIsIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSA1KSkNCmBgYA0KMS4gKipTdG9ja19QcmljZSoqOiBIYXJnYSBzYWhhbSBzZXJpbmcgZGlhbmFsaXNpcyBiZXJkYXNhcmthbiBwZXJ1YmFoYW4gd2FrdHUgKG1pc2FsOiBuYWlrL3R1cnVuIGhhcmdhIGhhcmlhbikuRGktKmxhZyogdW50dWsgbWVsaWhhdCBoYXJnYSBzZWJlbHVtbnlhLCAqZGlmZiogdW50dWsgc2VsaXNpaCBoYXJpYW4sIGRhbiAqcm9sbGluZyBtZWFuKiB1bnR1ayB0cmVuIGphbmdrYSBwZW5kZWsuDQoNCjIuICoqVm9sdW1lX1RyYWRlZCoqOiBWb2x1bWUgdHJhbnNha3NpIGJpc2EgZmx1a3R1YXRpZiwgcGVybHUgZGlsaWhhdCBwZXJ1YmFoYW5ueWEgZGFuIHJhdGEtcmF0YSBkYWxhbSB3aW5kb3cgd2FrdHUuIERpLSpsYWcqIGRhbiAqZGlmZiogdW50dWsgZGV0ZWtzaSBsb25qYWthbiBha3Rpdml0YXMsICpyb2xsaW5nIG1lYW4qIHVudHVrIGtlc3RhYmlsYW4gdm9sdW1lLg0KDQozLiAqKk1hcmtldF9DYXAqKjogRGktKmRpZmYqIGRhbiAqcm9sbGluZyBtZWFuKiB1bnR1ayBtZW1hbnRhdSBwZXJ1YmFoYW4gbmlsYWkgcGFzYXIgc2FoYW0gc2VjYXJhIGJlcnRhaGFwLg0KDQoNCiMjIEV4dHJhY3QgRGF0ZQ0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoc3RyaW5ncikNCg0KRXh0cmFjdCA8LSBmaW5hbmNpYWxfbWFya2V0ICU+JQ0KICBtdXRhdGUoDQogICAgRGF0ZSA9IGFzLkRhdGUoRGF0ZSwgZm9ybWF0ID0gIiVtLyVkLyVZIiksICAjIGtvbnZlcnNpIGRhcmkga2FyYWt0ZXIga2UgRGF0ZQ0KICAgIERheV9vZl9XZWVrID0gd2Vla2RheXMoRGF0ZSksDQogICAgTW9udGggPSBtb250aChEYXRlLCBsYWJlbCA9IFRSVUUpLA0KICAgIFllYXIgPSB5ZWFyKERhdGUpLA0KICAgIElzX1dlZWtlbmQgPSBpZmVsc2UoRGF5X29mX1dlZWsgJWluJSBjKCJTYXR1cmRheSIsICJTdW5kYXkiKSwgMSwgMCksDQogICAgU2VjdG9yID0gYXMuZmFjdG9yKFNlY3RvciksDQogICAgUGVyZm9ybWFuY2UgPSBhcy5mYWN0b3IoUGVyZm9ybWFuY2UpDQogICkgJT4lDQogIGJpbmRfY29scygNCiAgICBhcy5kYXRhLmZyYW1lKG1vZGVsLm1hdHJpeCh+IFNlY3RvciAtIDEsIGRhdGEgPSAuKSksDQogICAgYXMuZGF0YS5mcmFtZShtb2RlbC5tYXRyaXgofiBQZXJmb3JtYW5jZSAtIDEsIGRhdGEgPSAuKSkNCiAgKQ0KDQojIExpaGF0IGJlYmVyYXBhIGJhcmlzIHBlcnRhbWENCmRhdGF0YWJsZShFeHRyYWN0LCBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gNSkpDQoNCmBgYA0KS29udmVyc2kgVGFuZ2dhbDogVmFyaWFiZWwgRGF0ZSBkaWtvbnZlcnNpIGRhcmkgZm9ybWF0IGthcmFrdGVyIGtlIGZvcm1hdCBEYXRlIGFnYXIgZGFwYXQgZGlhbmFsaXNpcyBzZWNhcmEgdGVtcG9yYWwuDQoNCkVrc3RyYWtzaSBJbmZvcm1hc2kgV2FrdHU6IERhcmkgdGFuZ2dhbCB0ZXJzZWJ1dCwgZGl0YW1iYWhrYW4gYmViZXJhcGEgaW5mb3JtYXNpIHdha3R1Og0KDQpEYXlfb2ZfV2VlazogaGFyaSBkYWxhbSBtaW5nZ3UsDQoNCk1vbnRoOiBidWxhbiAoZGFsYW0gZm9ybWF0IGxhYmVsLCBtaXNhbG55YSBKYW4sIEZlYiksDQoNClllYXI6IHRhaHVuIHRyYW5zYWtzaSwNCg0KSXNfV2Vla2VuZDogaW5kaWthdG9yIGFwYWthaCBoYXJpIHRlcnNlYnV0IGFraGlyIHBla2FuIChTYWJ0dS9NaW5nZ3UpLg0KDQojIyBDdW11bGF0aXZlIFZhbHVlDQogDQpgYGB7cn0NClRlbXByYWwzIDwtIGZpbmFuY2lhbF9tYXJrZXQgJT4lDQogIGFycmFuZ2UoU3RvY2tfSUQsIERhdGUpICU+JQ0KICBncm91cF9ieShTdG9ja19JRCkgJT4lDQogIG11dGF0ZSgNCiAgICBDdW11bGF0aXZlX1ZvbHVtZSA9IGN1bXN1bShWb2x1bWVfVHJhZGVkKSwNCiAgICBDdW11bGF0aXZlX01hcmtldENhcCA9IGN1bXN1bShNYXJrZXRfQ2FwKSwNCiAgICBDdW11bGF0aXZlX0F2Z1ByaWNlID0gY3VtbWVhbihTdG9ja19QcmljZSkNCiAgKSAlPiUNCiAgdW5ncm91cCgpDQoNCmhlYWQoVGVtcHJhbDMpDQoNCmBgYA0KUG90b25nYW4ga29kZSBpbmkgZGlndW5ha2FuIHVudHVrIG1lbmdoaXR1bmcgYWt1bXVsYXNpIGRhdGEgcGFzYXIgc2FoYW0gc2VjYXJhIHRlbXBvcmFsIGJlcmRhc2Fya2FuIHNldGlhcCBgU3RvY2tfSURgLiBEYXRhIHBlcnRhbWEtdGFtYSBkaXVydXRrYW4gYmVyZGFzYXJrYW4gYFN0b2NrX0lEYCBkYW4gYERhdGVgIGFnYXIgYW5hbGlzaXMgYmVyc2lmYXQgdGltZS1zZXJpZXMuIEtlbXVkaWFuLCB1bnR1ayBzZXRpYXAgc3RvY2sgKGBncm91cF9ieShTdG9ja19JRClgKSwgZGlsYWt1a2FuIHRpZ2EgcGVyaGl0dW5nYW46IA0KDQoxLiAqKkN1bXVsYXRpdmVfVm9sdW1lKio6IHRvdGFsIHZvbHVtZSBwZXJkYWdhbmdhbiB5YW5nIHRlcnVzIGRpanVtbGFoa2FuIGRhcmkgd2FrdHUga2Ugd2FrdHUuDQoyLiAqKkN1bXVsYXRpdmVfTWFya2V0Q2FwKio6IGFrdW11bGFzaSBrYXBpdGFsaXNhc2kgcGFzYXIgaGluZ2dhIHRhbmdnYWwgdGVydGVudHUuDQozLiAqKkN1bXVsYXRpdmVfQXZnUHJpY2UqKjogcmF0YS1yYXRhIGhhcmdhIHNhaGFtIHNlY2FyYSBrdW11bGF0aWYgKGhpbmdnYSB0aXRpayB3YWt0dSB0ZXJzZWJ1dCkgbWVuZ2d1bmFrYW4gYGN1bW1lYW4oKWAuDQoNCkZ1bmdzaSBgdW5ncm91cCgpYCBkaWd1bmFrYW4gYWdhciBoYXNpbG55YSB0aWRhayBsYWdpIGJlcnNpZmF0IGtlbG9tcG9rLiBIYXNpbCBha2hpciBtZW1iZXJpIGdhbWJhcmFuIHBlcmtlbWJhbmdhbiBoaXN0b3JpcyBraW5lcmphIHNldGlhcCBzYWhhbSBkYXJpIHdha3R1IGtlIHdha3R1LiAgDQoNCg0KIyBEaXN0cmlidXRpb24gVHJhbnNmb3JtYXRpb24NCg0KIyMgTG9nIFRyYW5zZm9ybQ0KVmFyaWFiZWwgeWFuZyBkaXBpbGloIGFkYWxhaCBLYXBhc2l0YXMgcGFzYXIgYWxhc2FubnlhIGFkYWxhaCwga2FyZW5hIGthcGFzaXRhcyBwYXNhciBtZW1wdW55YWkgYW5na2EgeWFuZyBzYW5nYXQgYmVzYXIgZGFuIGRpc3RyaWJ1c2lueWEgc2FuZ2F0IG1pcmluZyBrZSBrYW5hbiAocmlnaHQtc2tld2VkKS4NCg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQoNCm1pbl9wb3NpdGl2ZSA8LSBtaW4oZmluYW5jaWFsX21hcmtldCRNYXJrZXRfQ2FwW2ZpbmFuY2lhbF9tYXJrZXQkTWFya2V0X0NhcCA+IDBdKQ0KDQpMb2cgPC0gZmluYW5jaWFsX21hcmtldCAlPiUNCiAgbXV0YXRlKA0KICAgIFNhZmVfTWFya2V0X0NhcCA9IGlmZWxzZShNYXJrZXRfQ2FwIDw9IDAsIG1pbl9wb3NpdGl2ZSwgTWFya2V0X0NhcCksDQogICAgTG9nX01hcmtldF9DYXAgPSBsb2cxcChTYWZlX01hcmtldF9DYXApDQogICkNCmhlYWQoTG9nKQ0KYGBgDQoNCipJbnRlcnByZXRhc2k6Kg0KDQpUcmFuc2Zvcm1hc2kgbG9nIGRpZ3VuYWthbiB1bnR1azoNCg0KLSBNZW5zdGFiaWxrYW4gdmFyaWFucyB5YW5nIG1lbmluZ2thdCBzZWlyaW5nIGRlbmdhbiBuaWxhaSB2YXJpYWJlbC4NCg0KLSBNZW5ndXJhbmdpIHNrZXduZXNzIChrZW1pcmluZ2FuIGRpc3RyaWJ1c2kpIHRlcnV0YW1hIHBhZGEgZGF0YSB5YW5nIHRlcmRpc3RyaWJ1c2kgc2tld2VkIGtlIGthbmFuIHNlcGVydGkgS2FwaXRhbGlzYXNpLlBhc2FyLg0KDQotIERhbGFtIGtvZGUsIG5pbGFpIG5vbCBhdGF1IG5lZ2F0aWYgZGlnYW50aSBkZW5nYW4gbmlsYWkgcG9zaXRpZiB0ZXJrZWNpbCBhZ2FyIGBsb2coKWAgdGlkYWsgZXJyb3IuDQoNCi0gRnVuZ3NpIGBsb2cxcCh4KWAgYCh5YWl0dSBsb2coeCArIDEpKWAgZGlndW5ha2FuIHNlYmFnYWkgYmVudHVrIGFtYW4gdW50dWsgZGF0YSBub2wuDQoNCipIYXNpbDoqIA0KDQpEaXN0cmlidXNpIG1lbmphZGkgbGViaWggc2ltZXRyaXMgZGFuIHZhcmlhbnMgYW50YXIgbGV2ZWwgZGF0YSBtZW5qYWRpIGxlYmloIHNlaW1iYW5nLCBzZWhpbmdnYSBsZWJpaCBjb2NvayB1bnR1ayBhbmFsaXNpcyByZWdyZXNpLg0KDQotIEJlcnRhbWJhaCAyIHZhcmlhYmVsLCB5YWl0dTogU2FmZV9LYXBpdGFsaXNhc2lfUGFzYXIgZGFuIExvZ19LYXBpdGFsaXNhc2lfUGFzYXINCg0KIyMgQm94LUNveA0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGJlc3ROb3JtYWxpemUpDQpmaW5hbmNpYWxfbWFya2V0IDwtIHJlYWQuY3N2KCJmaW5hbmNpYWwgbWFya2V0LmNzdiIpDQoNCkJveF9Db3ggPC0gZmluYW5jaWFsX21hcmtldCAlPiUNCiAgbXV0YXRlKA0KICAgIFllb0pfVm9sdW1lX1RyYWRlZCA9IGJlc3ROb3JtYWxpemUoVm9sdW1lX1RyYWRlZCkkeC50LA0KICAgIFllb0pfTWFya2V0X0NhcCA9IGJlc3ROb3JtYWxpemUoTWFya2V0X0NhcCkkeC50DQogICkNCmhlYWQoQm94X0NveCkNCmBgYA0KDQoqSW50ZXJwcmV0YXNpOioNCg0KLSBNZW5nZ3VuYWthbiBmdW5nc2kgYmVzdE5vcm1hbGl6ZSgpIHlhbmcgb3RvbWF0aXMgbWVtaWxpaCBtZXRvZGUgdHJhbnNmb3JtYXNpIHRlcmJhaWsgYWdhciBkYXRhIG1lbmRla2F0aSBkaXN0cmlidXNpIG5vcm1hbC4gIA0KDQotIFRyYW5zZm9ybWFzaSBkaWxha3VrYW4gcGFkYSBWb2x1bWVfRGlwZXJkYWdhbmdrYW4gZGFuIEthcGl0YWxpc2FzaS5QYXNhciwgZGFuIGhhc2lsbnlhIGRpc2ltcGFuIHNlYmFnYWkga29sb20gYmFydS4gIA0KDQotIFByYWt0aWsgaW5pIGlkZWFsIHVudHVrIHByZXByb2Nlc3Npbmcgc2ViZWx1bSBtb2RlbGluZywga2FyZW5hIG1lbWJhbnR1IG1lbmd1cmFuZ2kgc2tld25lc3MgZGFuIG1lbnN0YWJpbGthbiB2YXJpYW5zIGRhdGEuDQoNCiMjIFN0YWJpbGlzYXNpIFZhcmlhbnMNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KERUKQ0KDQpTdGFiaWxpemUgPC0gZmluYW5jaWFsX21hcmtldCAlPiUNCiAgbXV0YXRlKA0KICAgIHNxcnRfVm9sdW1lX1RyYWRlZCA9IHNxcnQocG1heChWb2x1bWVfVHJhZGVkLCAwKSksDQogICAgc3FydF9NYXJrZXRfQ2FwID0gc3FydChwbWF4KE1hcmtldF9DYXAsIDApKQ0KICApJT4lDQogIHVuZ3JvdXAoKQ0KDQoNCmRhdGF0YWJsZShTdGFiaWxpemUsIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSA1KSkNCmBgYA0KIyMjIEludGVycHJldGFzaToNCg0KVHJhbnNmb3JtYXNpIGFrYXIga3VhZHJhdCAoc3FydCkgc2VyaW5nIGRpZ3VuYWthbiBkYWxhbSBhbmFsaXNpcyBkYXRhIGtldWFuZ2FuIGF0YXUgc3RhdGlzdGlrIHVudHVrOg0KDQoqICpNZW5zdGFiaWxrYW4gdmFyaWFucyo6IERhdGEgc2VwZXJ0aSBWb2x1bWVfVHJhZGVkIGRhbiBNYXJrZXRfQ2FwIGJpc2Egc2FuZ2F0IGJlcnZhcmlhc2kgYW50YXIgcGVydXNhaGFhbiAoYWRhIHlhbmcga2VjaWwsIGFkYSB5YW5nIHNhbmdhdCBiZXNhcikuIFRyYW5zZm9ybWFzaSBha2FyIG1lbWJhbnR1IG1lbmd1cmFuZ2kgZWZlayBla3N0cmVtIGluaS4NCg0KKiAqTWVuZ3VyYW5naSBza2V3bmVzcyo6IEJhbnlhayB2YXJpYWJlbCBrZXVhbmdhbiBiZXJzaWZhdCBzYW5nYXQgbWlyaW5nIGtlIGthbmFuIChyaWdodC1za2V3ZWQpLiBBa2FyIGt1YWRyYXQgbWVtYnVhdCBkaXN0cmlidXNpbnlhIGxlYmloIHNpbWV0cmlzIChkZWthdCBub3JtYWwpLCB5YW5nIHBlbnRpbmcgdW50dWsgYW5hbGlzaXMgcmVncmVzaSBhdGF1IG1hY2hpbmUgbGVhcm5pbmcuDQoNCiogKk1lbmdoaW5kYXJpIG1hc2FsYWggbmlsYWkgbmVnYXRpZio6IHBtYXgoLi4uLCAwKSBtZW1hc3Rpa2FuIG5pbGFpIG5lZ2F0aWYgZGl1YmFoIG1lbmphZGkgbm9sIHNlYmVsdW0gZGloaXR1bmcgYWthcm55YSwga2FyZW5hIGFrYXIgZGFyaSBiaWxhbmdhbiBuZWdhdGlmIHRpZGFrIHZhbGlkIGRhbGFtwqBrb250ZWtzwqByZWFsLg0KDQojIyMgQ29udG9oIEludGVycHJldGFzaSBLYXN1czoNCg0KTWlzYWxueWEsIGR1YSBwZXJ1c2FoYWFuOg0KDQotIFBlcnVzYWhhYW4gQTogTWFya2V0X0NhcCA9IDEuMDAwLjAwMCAgDQotIFBlcnVzYWhhYW4gQjogTWFya2V0X0NhcCA9IDEwLjAwMA0KDQpUYW5wYSB0cmFuc2Zvcm1hc2ksIFBlcnVzYWhhYW4gQSB0ZXJsaWhhdCAqMTAww5cgbGViaWggYmVzYXIqLiBTZXRlbGFoIGFrYXIga3VhZHJhdDoNCg0KLSBzcXJ0KDEuMDAwLjAwMCkgPSAxMDAwICANCi0gc3FydCgxMC4wMDApID0gMTAwICANCg0KU2VrYXJhbmcgUGVydXNhaGFhbiBBIGhhbnlhIHRlcmxpaGF0ICoxMMOXIGxlYmloIGJlc2FyKiwgbWVtYnVhdCBhbmFsaXNpcyBsZWJpaMKgcHJvcG9yc2lvbmFsLg0KDQojIFNjYWxpbmcgJiBOb3JtYWxpemF0aW9uDQoNCiMjIFotU2NvcmUgU3RhbmRhcmRpemF0aW9uDQoNCmBgYHtyfQ0KIyBTdGFuZGFyZGlzYXNpIFotU2NvcmU6IG1lbmd1YmFoIGRhdGEgYWdhciBtZW1pbGlraSByYXRhLXJhdGEgMCBkYW4gc3RhbmRhciBkZXZpYXNpIDENCmZpbmFuY2lhbF9zY2FsZWQgPC0gZmluYW5jaWFsX21hcmtldCAlPiUNCiAgbXV0YXRlKA0KICAgIFN0b2NrX1ByaWNlX1N0ZCA9IHNjYWxlKFN0b2NrX1ByaWNlKSwNCiAgICBWb2x1bWVfVHJhZGVkX1N0ZCA9IHNjYWxlKFZvbHVtZV9UcmFkZWQpLA0KICAgIE1hcmtldF9DYXBfU3RkID0gc2NhbGUoTWFya2V0X0NhcCksDQogICAgUEVfUmF0aW9fU3RkID0gc2NhbGUoUEVfUmF0aW8pLA0KICAgIERpdmlkZW5kX1lpZWxkX1N0ZCA9IHNjYWxlKERpdmlkZW5kX1lpZWxkKSwNCiAgICBSZXR1cm5fb25fRXF1aXR5X1N0ZCA9IHNjYWxlKFJldHVybl9vbl9FcXVpdHkpDQogICkNCmhlYWQoZmluYW5jaWFsX3NjYWxlZCkNCmBgYA0KDQojIyMgUGVuamVsYXNhbjoNCg0KMS4gZmluYW5jaWFsX21hcmtldCBhZGFsYWggZGF0YSBmcmFtZSB5YW5nIGJlcmlzaSBkYXRhIHBhc2FyIGtldWFuZ2FuLiBLb2xvbS1rb2xvbW55YSBtZW5jYWt1cCB2YXJpYWJlbCBzZXBlcnRpIGhhcmdhIHNhaGFtIChTdG9ja19QcmljZSksIHZvbHVtZSBwZXJkYWdhbmdhbiAoVm9sdW1lX1RyYWRlZCksIGthcGl0YWxpc2FzaSBwYXNhciAoTWFya2V0X0NhcCksIHJhc2lvIFBFIChQRV9SYXRpbyksIGRpdmlkZW4gKERpdmlkZW5kX1lpZWxkKSwgZGFuIFJPRSAoUmV0dXJuX29uX0VxdWl0eSkuDQoNCjIuIEZ1bmdzaSBtdXRhdGUoKSBkYXJpIHBha2V0IGRwbHlyIGRpZ3VuYWthbiB1bnR1ayBtZW5hbWJhaGthbiBrb2xvbS1rb2xvbSBiYXJ1IGhhc2lsIHRyYW5zZm9ybWFzaSBkYXJpIGtvbG9tIHlhbmcgYWRhLg0KDQozLiBaLVNjb3JlIFN0YW5kYXJkaXphdGlvbiBhZGFsYWggbWV0b2RlIHRyYW5zZm9ybWFzaSBkYXRhIHlhbmcgbWVuZ3ViYWggc2V0aWFwIG5pbGFpIG1lbmphZGkgdWt1cmFuIGRhbGFtIHNhdHVhbiBzdGFuZGFyIGRldmlhc2kgZGFyaSByYXRhLXJhdGEuIA0KDQo0LiBGdW5nc2kgc2NhbGUoKSBhZGFsYWggZnVuZ3NpIGJhd2FhbiBSIHVudHVrIG1lbGFrdWthbiBaLXNjb3JlIHN0YW5kYXJkaXphdGlvbiwgeWFpdHU6DQokJCBaID0gXGZyYWN7WCAtIFxtdSB9IHtcc2lnbWF9ICQkDQoNCi0gWDogbmlsYWkgYXNsaQ0KDQotICRcbXUkIDogcmF0YS1yYXRhIGRhdGENCg0KLSAkXHNpZ21hJDogc3RhbmRhciBkZXZpYXNpIGRhdGENCg0KSGFzaWxueWEgYWRhbGFoIGRhdGEgYmFydSB5YW5nOg0KDQotIE1lbWlsaWtpIHJhdGEtcmF0YSA9IDANCg0KLSBNZW1pbGlraSBzdGFuZGFyIGRldmlhc2kgPSAxDQoNCjUuIEtvbG9tIGJhcnUgZGVuZ2FuIGFraGlyYW4gX1N0ZCBkaWJ1YXQgdW50dWsgc2V0aWFwIHZhcmlhYmVsLCBzZXBlcnRpOg0KDQotIFN0b2NrX1ByaWNlX1N0ZA0KDQotIFZvbHVtZV9UcmFkZWRfU3RkDQoNCi0gTWFya2V0X0NhcF9TdGQNCg0KLSBQRV9SYXRpb19TdGQNCg0KLSBEaXZpZGVuZF9ZaWVsZF9TdGQNCg0KLSBSZXR1cm5fb25fRXF1aXR5X1N0ZA0KDQo2LiBoZWFkKGZpbmFuY2lhbF9zY2FsZWQpIGRpZ3VuYWthbiB1bnR1ayBtZW5hbXBpbGthbiA2IGJhcmlzIHBlcnRhbWEgZGFyaSBkYXRhIHlhbmcgdGVsYWggZGl0cmFuc2Zvcm1hc2kuDQoNCiMjIyBDb250b2ggUGVyaGl0dW5nYW4gTWFudWFsDQoNCk1pc2Fsa2FuIGRhdGEgU3RvY2tfUHJpY2U6IFsxMDAsIDEyMCwgMTUwLCAxMzAsIDExMF0NCg0KMS4gSGl0dW5nIE1lYW46DQoNCiQkIFxtdSA9IFxmcmFjezEwMCArIDEyMCArIDE1MCArIDEzMCArIDExMCB9IHs1fSA9IFxmcmFjezYxMH17NX0gPSAxMjIkJA0KDQoyLiBIaXR1bmcgU3RhbmRhciBEZXZpYXNpICjPgyk6DQoNCiQkIFxzaWdtYSA9IFxzcXJ0e1xmcmFjeygxMDAgLSAxMjIpXjIgKyAoMTIwIC0gMTIyKV4yICsgKDE1MCAtIDEyMileMiArICgxMzAgLSAxMjIpXjIgKyAoMTEwIC0gMTIyKV4yIH0gezV9fSAkJA0KDQokJCA9IFxzcXJ0e1xmcmFjezQ4NCArIDQgKyA3ODQgKyA2NCArIDE0NH17NX19ID0gXHNxcnR7XGZyYWN7MTQ4MH17NX19ID0gXHNxcnR7Mjk2fSA9IDE3LjIgJCQNCg0KMy4gWi1TY29yZSB1bnR1ayBuaWxhaSAxNTA6DQoNCiQkIFogPSBcZnJhY3sxNTAgLSAxMjIgfSB7MTcuMn0gPSBcZnJhY3syOH17MTcuMn3CoD3CoDEuNjPCoCQkDQoNCiMjIE1pbi1NYXggTm9ybWFsaXphdGlvbg0KDQpgYGB7cn0NCiMgTm9ybWFsaXNhc2kgTWluLU1heDogbWVuZ3ViYWggc2thbGEgZGF0YSBrZSBkYWxhbSByZW50YW5nIFswLCAxXQ0KZmluYW5jaWFsX25vcm1hbGl6ZWQgPC0gZmluYW5jaWFsX21hcmtldCAlPiUNCiAgbXV0YXRlKA0KICAgIFN0b2NrX1ByaWNlX05vcm0gPSAoU3RvY2tfUHJpY2UgLSBtaW4oU3RvY2tfUHJpY2UpKSAvIChtYXgoU3RvY2tfUHJpY2UpIC0gbWluKFN0b2NrX1ByaWNlKSksDQogICAgVm9sdW1lX1RyYWRlZF9Ob3JtID0gKFZvbHVtZV9UcmFkZWQgLSBtaW4oVm9sdW1lX1RyYWRlZCkpIC8gKG1heChWb2x1bWVfVHJhZGVkKSAtIG1pbihWb2x1bWVfVHJhZGVkKSksDQogICAgTWFya2V0X0NhcF9Ob3JtID0gKE1hcmtldF9DYXAgLSBtaW4oTWFya2V0X0NhcCkpIC8gKG1heChNYXJrZXRfQ2FwIC0gbWluKE1hcmtldF9DYXApKSksDQogICAgUEVfUmF0aW9fTm9ybSA9IChQRV9SYXRpbyAtIG1pbihQRV9SYXRpbykpIC8gKG1heChQRV9SYXRpbykgLSBtaW4oUEVfUmF0aW8pKSwNCiAgICBEaXZpZGVuZF9ZaWVsZF9Ob3JtID0gKERpdmlkZW5kX1lpZWxkIC0gbWluKERpdmlkZW5kX1lpZWxkKSkgLyAobWF4KERpdmlkZW5kX1lpZWxkKSAtIG1pbihEaXZpZGVuZF9ZaWVsZCkpLA0KICAgIFJldHVybl9vbl9FcXVpdHlfTm9ybSA9IChSZXR1cm5fb25fRXF1aXR5IC0gbWluKFJldHVybl9vbl9FcXVpdHkpKSAvIChtYXgoUmV0dXJuX29uX0VxdWl0eSkgLSBtaW4oUmV0dXJuX29uX0VxdWl0eSkpDQogICkNCmhlYWQoZmluYW5jaWFsX25vcm1hbGl6ZWQpDQpgYGANCiMjIyBQZW5qZWxhc2FuDQoNCjEuIGZpbmFuY2lhbF9tYXJrZXQgYWRhbGFoIGRhdGEgZnJhbWUgeWFuZyBiZXJpc2kgZGF0YSBwYXNhciBrZXVhbmdhbi4gS29sb20ta29sb21ueWEgbWVuY2FrdXAgdmFyaWFiZWwgc2VwZXJ0aSBoYXJnYSBzYWhhbSAoU3RvY2tfUHJpY2UpLCB2b2x1bWUgcGVyZGFnYW5nYW4gKFZvbHVtZV9UcmFkZWQpLCBrYXBpdGFsaXNhc2kgcGFzYXIgKE1hcmtldF9DYXApLCByYXNpbyBQRSAoUEVfUmF0aW8pLCBkaXZpZGVuIChEaXZpZGVuZF9ZaWVsZCksIGRhbiBST0UgKFJldHVybl9vbl9FcXVpdHkpLg0KDQoyLiBGdW5nc2kgbXV0YXRlKCkgZGFyaSBwYWtldCBkcGx5ciBkaWd1bmFrYW4gdW50dWsgbWVuYW1iYWhrYW4ga29sb20ta29sb20gYmFydSBoYXNpbCB0cmFuc2Zvcm1hc2kgZGFyaSBrb2xvbSB5YW5nIGFkYS4NCg0KMy4gTm9ybWFsaXNhc2kgTWluLU1heCBhZGFsYWggbWV0b2RlIHBlbnNrYWxhYW4gZGF0YSBrZSBkYWxhbSByZW50YW5nIHRldGFwLCB5YWl0dSBbMCwgMV0sIGRlbmdhbiBtZW5nZ3VuYWthbiBydW11cyBiZXJpa3V0Og0KJCQgWF97bm9ybX0gPSBcZnJhY3tYIC0gWF97bWlufSB9IHtYX3ttYXh9IC0gWF97bWlufX0gJCQNCg0KLSBYOiBuaWxhaSBhc2xpDQoNCi0gJFhfe21pbn0kIDogbmlsYWkgbWluaW11bSBkYXJpIGZpdHVyDQoNCi0gJFhfe21pbn0kIDogbmlsYWkgbWFrc2ltdW0gZGFyaSBmaXR1cg0KIA0KSGFzaWxueWEgYWRhbGFoIGRhdGEgYmFydSB5YW5nOg0KDQotIE1lbWlsaWtpIG5pbGFpIHRlcmVuZGFoID0gMA0KDQotIE1lbWlsaWtpIG5pbGFpIHRlcnRpbmdnaSA9IDENCg0KLSBUZXRhcCBtZW1wZXJ0YWhhbmthbiBkaXN0cmlidXNpIGFzbGkgZGF0YW55YSAobmFtdW4gcmVudGFuZ255YSBkaXBlcnNlbXBpdCkNCg0KNC4gS29sb20gYmFydSBkZW5nYW4gYWtoaXJhbiBfTm9ybSBkaWJ1YXQgdW50dWsgc2V0aWFwIHZhcmlhYmVsLCBzZXBlcnRpOg0KDQotIFN0b2NrX1ByaWNlX05vcm0NCg0KLSBWb2x1bWVfVHJhZGVkX05vcm0NCg0KLSBNYXJrZXRfQ2FwX05vcm0NCg0KLSBQRV9SYXRpb19Ob3JtDQoNCi0gRGl2aWRlbmRfWWllbGRfTm9ybQ0KDQotIFJldHVybl9vbl9FcXVpdHlfTm9ybQ0KDQo1LiBGdW5nc2kgaGVhZChmaW5hbmNpYWxfbm9ybWFsaXplZCkgZGlndW5ha2FuIHVudHVrIG1lbmFtcGlsa2FuIDYgYmFyaXMgcGVydGFtYSBkYXJpIGRhdGEgeWFuZyB0ZWxhaCBkaW5vcm1hbGlzYXNpLg0KDQojIyMgQ29udG9oIFBlcmhpdHVuZ2FuIE1hbnVhbA0KDQpEYXRhIFN0b2NrX1ByaWNlOlsxMDAsIDEyMCwgMTUwLCAxMzAsIDExMF0NCg0KMS4gTWluaW11bSBkYW4gTWFrc2ltdW06DQoNCi0gTWluOiAxMDANCg0KLSBNYXg6IDE1MA0KDQoyLiBNaW4tTWF4IE5vcm1hbGl6YXRpb24gdW50dWsgbmlsYWkgMTMwOg0KJCQgWF97bm9ybX0gPSBcZnJhY3sxMzAgLSAxMDAgfSB7MTUwIC0gMTAwfSA9IFxmcmFjezMwfXs1MH3CoD3CoDAuNiQkDQoNCiMgQ2F0ZWdvcmljYWwgRW5jb2RpbmcNCg0KRGkgZGFsYW0gZGF0YSwga2FkYW5nIGtpdGEgbWVuZW11a2FuIGtvbG9tIHlhbmcgaXNpbnlhIGJ1a2FuIGFuZ2thLCB0YXBpICprYXRhLWthdGEqIGF0YXUgKmthdGVnb3JpKiwgbWlzYWxueWE6DQogDQogDQotIFNlY3RvciA9IOKAnEZpbmFuY2XigJ0sIOKAnFJldGFpbOKAnSwg4oCcVGVjaG5vbG9neeKAnQ0KIA0KLSBQZXJmb3JtYW5jZSA9IOKAnFBvc2l0aXZl4oCdLCDigJxOZWdhdGl2ZeKAnSwg4oCcU3RhYmxl4oCdDQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojIEJhY2EgZmlsZSBDU1YtbnlhDQpmaW5hbmNpYWxfbWFya2V0IDwtIHJlYWRfY3N2KCJmaW5hbmNpYWwgbWFya2V0LmNzdiIpDQoNCiMgVGFtcGlsa2FuIDYgZGF0YSB0ZXJhdGFzDQpoZWFkKGZpbmFuY2lhbF9tYXJrZXQpDQpgYGANCg0KTWFzYWxhaG55YSwgKiprb21wdXRlcioqIGhhbnlhIGJpc2EgbWVtYmFjYSBhbmdrYS4gTWFrYSwga2l0YSBwZXJsdSBtZW5ndWJhaCBrYXRhLWthdGEgaW5pIG1lbmphZGkgYW5na2EuIENhcmFueWEgZGlzZWJ1dCAqKkNhdGVnb3JpY2FsIEVuY29kaW5nKiouDQogDQogIA0KIyMgT25lIEhvdCBFbmNvZGluZw0KIA0KKkdhbXBhbmdueWE6KiBLaXRhIGJpa2luIGtvbG9tIGJhcnUgdW50dWsgc2V0aWFwIGthdGVnb3JpLCBsYWx1IGtpdGEga2FzaWggKmFuZ2thIDEgamlrYSBjb2NvaywgZGFuIDAqICprYWxhdSB0aWRhayouDQoNCipLZW5hcGEgTmdnYWsgUGFrYWkgQW5na2EgQmlhc2EgKDEsIDIsIDMpPyoNCiANCkthcmVuYSBrYWxhdSBraXRhIGthc2loIGFuZ2thIHNlcGVydGk6DQogDQogDQotIFBvc2l0aXZlID0gMQ0KIA0KLSBOZWdhdGl2ZSA9IDINCiANCi0gU3RhYmxlID0gMw0KIA0KIA0KKipLb21wdXRlciBiaXNhIHNhbGFoIHBhaGFtKiogZGFuIG1lbmdpcmEgYWRhICoqdXJ1dGFuIGF0YXUgcmFua2luZyoqLCBwYWRhaGFsICoqbmdnYWsgYWRhKiouDQogDQpQYWRhaGFsIGthdGVnb3JpIGl0dSBjdW1hICoqbGFiZWwqKiwgYnVrYW4gbmlsYWkuDQogDQpNYWthbnlhIGtpdGEgcGFrYWkgKipiaW5lciAoMCBkYW4gMSkqKiwgYmlhciBrb21wdXRlciBuZ2VydGkgYmFod2Egc2VtdWEga2F0ZWdvcmkgaXR1ICoqc2V0YXJhKiosIG5nZ2FrIGFkYSB5YW5nIGxlYmloIHRpbmdnaSBhdGF1IGxlYmloIHJlbmRhaC4NCg0KYmVyaWt1dCBrb2RlbnlhOg0KIA0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShmYXN0RHVtbWllcykNCg0KIyBVYmFoIGtvbG9tIFNlY3RvciAmIFBlcmZvcm1hbmNlIGphZGkgYmVudHVrIGFuZ2thIGJpbmVyICgwLzEpDQpvbmVfaG90IDwtIGR1bW15X2NvbHMoZmluYW5jaWFsX21hcmtldCwgDQogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0X2NvbHVtbnMgPSBjKCJTZWN0b3IiLCAiUGVyZm9ybWFuY2UiKSwgDQogICAgICAgICAgICAgICAgICAgICAgcmVtb3ZlX2ZpcnN0X2R1bW15ID0gRkFMU0UsIA0KICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZV9zZWxlY3RlZF9jb2x1bW5zID0gVFJVRSkNCg0KIyBUYW1waWxrYW4gNiBkYXRhIGhhc2lsIG9uZSBob3QgZW5jb2RpbmcNCmhlYWQob25lX2hvdCkNCmBgYA0KICANCiMjIEZyZXF1ZW5jeSBFbmNvZGluZw0KIA0KKkdhbXBhbmdueWE6KiBLaXRhIGhpdHVuZyAqKmJlcmFwYSBrYWxpKiogc2V0aWFwIGthdGVnb3JpIG11bmN1bCwgbGFsdSAqKnNldGlhcCBkYXRhIGRpZ2FudGkgZGVuZ2FuIGFuZ2thIGZyZWt1ZW5zaW55YSoqLg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQojIEZ1bmdzaSB1bnR1ayBtZW5naGl0dW5nIGZyZWt1ZW5zaSAoYmVyYXBhIHNlcmluZyBtdW5jdWwpDQpmcmVxX2VuYyA8LSBmdW5jdGlvbihjb2wpIHsNCiAgdGFiIDwtIHRhYmxlKGNvbCkNCiAgcmV0dXJuKGFzLm51bWVyaWModGFiW2NvbF0pIC8gbGVuZ3RoKGNvbCkpDQp9DQoNCiMgVGFtYmFoa2FuIGtvbG9tIGZyZWt1ZW5zaSBrZSBkYXRhDQpkYXRhX2ZyZXEgPC0gZmluYW5jaWFsX21hcmtldCAlPiUNCiAgbXV0YXRlKA0KICAgIFNlY3Rvcl9mcmVxID0gZnJlcV9lbmMoU2VjdG9yKSwNCiAgICBQZXJmb3JtYW5jZV9mcmVxID0gZnJlcV9lbmMoUGVyZm9ybWFuY2UpDQogICkNCg0KIyBUYW1waWxrYW4gNiBkYXRhIHRlcmF0YXMNCmhlYWQoZGF0YV9mcmVxKQ0KYGBgDQoqSGFzaWxueWE6KiAgDQpBa2FuIG11bmN1bCBkdWEga29sb20gYmFydToNCg0KLSBTZWN0b3JfZnJlcTogYmVyaXNpIGFuZ2thIGZyZWt1ZW5zaSBkYXJpIG5pbGFpIGRpIGtvbG9tIFNlY3Rvcg0KDQotIFBlcmZvcm1hbmNlX2ZyZXE6IGZyZWt1ZW5zaSBkYXJpIGtvbG9tIFBlcmZvcm1hbmNlDQoNClNldGlhcCBiYXJpcyBha2FuIGJlcmlzaSBhbmdrYSBwZWNhaGFuIChtaXNhbG55YSAwLjM2MCwgMC4zMzYpIHlhbmcgbWVudW5qdWtrYW4gKipzZWJlcmFwYSBzZXJpbmcgbmlsYWkgdGVyc2VidXQgbXVuY3VsIGRhbGFtIGtlc2VsdXJ1aGFuIGRhdGEqKi4NCg0KDQojIEZlYXR1cmUgRW5naW5lZXJpbmcNCg0KYGBge3J9DQojIEZlYXR1cmUgRW5naW5lZXJpbmcgcGFkYSBGaW5hbmNpYWxfbWFya2V0DQoNCkZlYXR1cmVfRW5nIDwtIGZpbmFuY2lhbF9tYXJrZXQgJT4lDQogIG11dGF0ZSgNCiAgICAjIDEuIE5ldyBGZWF0dXJlcyBmcm9tIFJhdyBEYXRhDQogICAgUHJpY2VfUGVyX1ZvbHVtZSA9IFN0b2NrX1ByaWNlIC8gKFZvbHVtZV9UcmFkZWQgKyAxZS01KSwgICMgSGluZGFyaSBwZW1iYWdpYW4gZGVuZ2FuIG5vbA0KICAgIA0KICAgICMgMi4gUHJvZHVjdCBvZiBGZWF0dXJlcywgQ3Jvc3NlZCBUZXJtcw0KICAgIFBFX3hfUk9FID0gUEVfUmF0aW8gKiBSZXR1cm5fb25fRXF1aXR5LA0KICAgIA0KICAgICMgMy4gUHJpY2UgcGVyIFVuaXQsIEVmZmljaWVuY3kNCiAgICBZaWVsZF90b19QRSA9IERpdmlkZW5kX1lpZWxkIC8gKFBFX1JhdGlvICsgMWUtNSksICAjIFlpZWxkIHBlciBQRQ0KDQogICAgIyA0LiBSYW5raW5nLCBQZXJjZW50aWxlDQogICAgTWFya2V0Q2FwX1JhbmsgPSByYW5rKC1NYXJrZXRfQ2FwKSwgICMgUmFuayBtYXJrZXQgY2FwIHRlcmJlc2FyIGtlIHRlcmtlY2lsDQogICAgUk9FX1F1YXJ0aWxlID0gbnRpbGUoUmV0dXJuX29uX0VxdWl0eSwgNCksICAjIEJhZ2kgUk9FIGtlIGRhbGFtIDQga3VhcnRpbA0KICAgIA0KICAgICMgNS4gRnJvbSBJRHM6IFByZWZpeCwgTGVuZ3RoLCBQYXR0ZXJuDQogICAgU3RvY2tfSURfUHJlZml4ID0gc3Vic3RyKFN0b2NrX0lELCAxLCAzKSwNCiAgICBTdG9ja19JRF9MZW5ndGggPSBuY2hhcihTdG9ja19JRCkNCiAgKSAlPiUNCiAgDQogICMgNi4gQXZnLCBTdW0sIENvdW50IGJ5IEdyb3VwDQogIGdyb3VwX2J5KFNlY3RvcikgJT4lDQogIG11dGF0ZSgNCiAgICBBdmdfUEVfU2VjdG9yID0gbWVhbihQRV9SYXRpbywgbmEucm0gPSBUUlVFKSwNCiAgICBBdmdfUk9FX1NlY3RvciA9IG1lYW4oUmV0dXJuX29uX0VxdWl0eSwgbmEucm0gPSBUUlVFKSwNCiAgICBUb3RhbF9Db21wYW5pZXNfU2VjdG9yID0gbigpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpoZWFkKEZlYXR1cmVfRW5nKQ0KYGBgDQoNCiMjIyBQZW5qZWxhc2FuDQoxLiBOZXcgRmVhdHVyZXMgZnJvbSBSYXcgRGF0YQ0KDQotIFByaWNlX1Blcl9Wb2x1bWU6IE1lbmd1a3VyIHNlYmVyYXBhIGJlc2FyIGhhcmdhIHNhaGFtIGRpYmFuZGluZyB2b2x1bWUgcGVyZGFnYW5nYW5ueWEuIERpZ3VuYWthbiB1bnR1ayBtZWxpaGF0IGFwYWthaCBzYWhhbSBkaXBlcmRhZ2FuZ2thbiB0ZXJsYWx1IGJhbnlhayBhdGF1IHRlcmxhbHUgc2VkaWtpdCB0ZXJoYWRhcCBuaWxhaW55YS4NCg0KICAtIERpdGFtYmFoIDFlLTUgdW50dWsgbWVuY2VnYWggcGVtYmFnaWFuIG5vbC4NCg0KMi4gUHJvZHVjdCBvZiBGZWF0dXJlcywgQ3Jvc3NlZCBUZXJtcw0KDQotIE1lbmdhbGlrYW4gUHJpY2UgdG8gRWFybmluZ3MgUmF0aW8gKFAvRSkgZGVuZ2FuIFJldHVybiBvbiBFcXVpdHkgKFJPRSkgbWVuZ2hhc2lsa2FuIGludGVyYWtzaSBhbnRhciBkdWEgbWV0cmlrIHBlbnRpbmc6DQoNCiAgLSBQRSBSYXRpbyBtZW51bmp1a2thbiB2YWx1YXNpIHNhaGFtLg0KDQogIC0gUk9FIG1lbnVuanVra2FuIGVmaXNpZW5zaSBwZXJ1c2FoYWFuIG1lbmdoYXNpbGthbiBsYWJhIGRhcmkgbW9kYWwgc2VuZGlyaS4NCg0KLSBJbnRlcmFrc2kgaW5pIGJpc2EgbWVtYmVyaSBzaW55YWwgYXBha2FoIHNhaGFtIHVuZGVydmFsdWVkIGF0YXUgb3ZlcnZhbHVlZCBkZW5nYW4gbWVtcGVyaGl0dW5na2FuIGVmaXNpZW5zaS4NCg0KMy4gUHJpY2UgcGVyIFVuaXQsIEVmZmljaWVuY3kNCg0KLSBZaWVsZF90b19QRTogRWZpc2llbnNpIGhhc2lsIGRpdmlkZW4gdGVyaGFkYXAgbmlsYWkgdmFsdWFzaSBwZXJ1c2FoYWFuLg0KDQogIC0gU2FoYW0gZGVuZ2FuIHJhc2lvIHRpbmdnaSBkaWFuZ2dhcCBtZW1iZXJpa2FuIHJldHVybiBkaXZpZGVuIHlhbmcgYmVzYXIgZGVuZ2FuIHZhbHVhc2kgcmVuZGFoIChtZW5hcmlrIHVudHVrIGludmVzdG9yIGRpdmlkZW4pLg0KDQogIC0gTGFnaS1sYWdpIGRpdGFtYmFoIDFlLTUgdW50dWsgbWVuZ2hpbmRhcmkgcGVtYmFnaWFuIG5vbC4NCg0KNC4gUmFua2luZywgUGVyY2VudGlsZQ0KDQotIE1hcmtldENhcF9SYW5rOiBNZW1iZXJpIHJhbmtpbmcgdGVyaGFkYXAgcGVydXNhaGFhbiBiZXJkYXNhcmthbiBrYXBpdGFsaXNhc2kgcGFzYXIgZGFyaSB0ZXJiZXNhciAocmFuayAxKSBrZSB0ZXJrZWNpbC4NCg0KLSBST0VfUXVhcnRpbGU6IE1lbmdlbG9tcG9ra2FuIFJPRSBrZSBkYWxhbSA0IGt1YXJ0aWwuIFR1anVhbm55YSB1bnR1ayBrbGFzaWZpa2FzaSBwZXJmb3JtYSBwZXJ1c2FoYWFuIGJlcmRhc2Fya2FuIFJPRToNCg0KICAtIEt1YXJ0aWwgMTogUk9FIHJlbmRhaA0KDQogIC0gS3VhcnRpbCA0OiBST0UgdGluZ2dpIChwZXJ1c2FoYWFuIHNhbmdhdCBtZW5ndW50dW5na2FuKQ0KDQo1LiBGcm9tIElEczogUHJlZml4LCBMZW5ndGgsIFBhdHRlcm4NCg0KLSBTdG9ja19JRF9QcmVmaXg6IE1lbmdhbWJpbCAzIGthcmFrdGVyIGF3YWwgZGFyaSBJRCBzYWhhbS4gSW5pIGJpc2EgbWVudW5qdWtrYW4ga29kZSBrYXRlZ29yaSwgamVuaXMgc2FoYW0sIGF0YXUgYXNhbCBidXJzYS4NCg0KLSBTdG9ja19JRF9MZW5ndGg6IFBhbmphbmcgZGFyaSBJRCBzYWhhbS4gQmlzYSBtZW1iYW50dSBkZXRla3NpIGZvcm1hdCBJRCB5YW5nIHRpZGFrIGxhemltLg0KDQo2LiBBdmcsIFN1bSwgQ291bnQgYnkgR3JvdXANCi0gRml0dXIgaW5pIG1lbmdoaXR1bmcgc3RhdGlzdGlrIGFncmVnYXQgYmVyZGFzYXJrYW4gU2VjdG9yOg0KDQogIC0gQXZnX1BFX1NlY3RvcjogUmF0YS1yYXRhIFBFIHVudHVrIHNlbHVydWggcGVydXNhaGFhbiBkaSBzZWt0b3IgdGVyc2VidXQuDQoNCiAgLSBBdmdfUk9FX1NlY3RvcjogUmF0YS1yYXRhIFJPRS4NCg0KICAtIFRvdGFsX0NvbXBhbmllc19TZWN0b3I6IEp1bWxhaCBwZXJ1c2FoYWFuIGRhbGFtIHNla3Rvci4NCg0KLSBCZXJndW5hIHVudHVrIG1lbWJhbmRpbmdrYW4ga2luZXJqYSBwZXJ1c2FoYWFuIGRlbmdhbiByYXRhLXJhdGEgc2VrdG9ybnlhIChyZWxhdGl2ZSB2YWx1YXRpb24pLg0KDQojIyBJbnRlcmFjdGlvbiBGZWF0dXJlcyANCg0KYGBge3J9DQpGaW5hbmNpYWxfaW50ZXJhY3Rpb24gPC0gZmluYW5jaWFsX21hcmtldCAlPiUNCiAgbXV0YXRlKA0KICAgIEltcGFjdF9TY29yZSA9IFN0b2NrX1ByaWNlICogUmV0dXJuX29uX0VxdWl0eQ0KICApDQpoZWFkKEZpbmFuY2lhbF9pbnRlcmFjdGlvbikNCmBgYA0KDQojIyMgUGVuamVsYXNhbg0KLSBJbXBhY3RfU2NvcmU6IE1lbmNlcm1pbmthbiAia2VrdWF0YW4gcGFzYXIiIHN1YXR1IHNhaGFtIGJlcmRhc2Fya2FuIGhhcmdhIGRhbiByZXR1cm4gZWt1aXRhc255YS4NCg0KICAtIFNlbWFraW4gdGluZ2dpIG5pbGFpIGluaSwgc2VtYWtpbiBzaWduaWZpa2FuIHBlbmdhcnVoIHNhaGFtIHRlcmhhZGFwIHBhc2FyIGF0YXUgaW52ZXN0b3IuDQoNCiMjIFJhdGlvIEZlYXR1cmVzDQoNCmBgYHtyfQ0KRmluYW5jaWFsX3JhdGlvIDwtIGZpbmFuY2lhbF9tYXJrZXQgJT4lDQogIG11dGF0ZSgNCiAgICBEaXZpZGVuZF90b19QcmljZSA9IERpdmlkZW5kX1lpZWxkIC8gKFN0b2NrX1ByaWNlICsgMWUtNSkNCiAgKQ0KaGVhZChGaW5hbmNpYWxfcmF0aW8pDQpgYGANCg0KIyMjIFBlbmplbGFzYW4NCi0gRGl2aWRlbmRfdG9fUHJpY2U6IE1lbmd1a3VyIGltYmFsIGhhc2lsIGRpdmlkZW4gcmVsYXRpZiB0ZXJoYWRhcCBoYXJnYSBzYWhhbS4NCg0KICAtIFNhaGFtIGRlbmdhbiByYXNpbyB0aW5nZ2kgbWVtYmVyaWthbiBkaXZpZGVuIGJlc2FyIG1lc2tpcHVuIGhhcmdhbnlhIHJlbmRhaC4NCg0KICAtIENvY29rIHVudHVrIGlkZW50aWZpa2FzaSBzYWhhbSAiZGl2aWRlbiBtdXJhaCIuDQoNCiMjIEdyb3VwIEFnZ3JlZ2F0aW9uDQpgYGB7cn0NClNlY3Rvcl9zdW1tYXJ5IDwtIGZpbmFuY2lhbF9tYXJrZXQgJT4lDQogIGdyb3VwX2J5KFNlY3RvcikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBBdmdfTWFya2V0X0NhcCA9IG1lYW4oTWFya2V0X0NhcCksDQogICAgTWF4X1N0b2NrX1ByaWNlID0gbWF4KFN0b2NrX1ByaWNlKSwNCiAgICBDb21wYW55X0NvdW50ID0gbigpDQogICkNCg0KRmluYW5jaWFsX21lcmdlZCA8LSBsZWZ0X2pvaW4oZmluYW5jaWFsX21hcmtldCwgU2VjdG9yX3N1bW1hcnksIGJ5ID0gIlNlY3RvciIpDQpoZWFkKEZpbmFuY2lhbF9tZXJnZWQpDQpgYGANCg0KIyMjIFBlbmplbGFzYW4NCi0gU2VjdG9yX3N1bW1hcnkgbWVtYnVhdCByaW5na2FzYW4gc2VrdG9yOg0KDQogIC0gQXZnX01hcmtldF9DYXA6IEthcGl0YWxpc2FzaSBwYXNhciByYXRhLXJhdGEgZGkgc2VrdG9yLg0KDQogIC0gTWF4X1N0b2NrX1ByaWNlOiBIYXJnYSBzYWhhbSB0ZXJ0aW5nZ2kgZGkgc2VrdG9yLg0KDQogIC0gQ29tcGFueV9Db3VudDogSnVtbGFoIHBlcnVzYWhhYW4gcGVyIHNla3Rvci4NCg0KLSBMYWx1IGxlZnRfam9pbiBkaWxha3VrYW4gdW50dWsgbWVuZ2dhYnVuZ2thbiBpbmZvcm1hc2kgaW5pIGtlbWJhbGkga2UgZGF0YSB1dGFtYSwgbWVtdW5na2lua2FuIHBlcmJhbmRpbmdhbiBhbnRhciBwZXJ1c2FoYWFuIGRhbGFtIHNhdHUgc2VrdG9yLg0KDQojIyBSYW5rIFRyYW5zZm9ybWF0aW9uDQpgYGB7cn0NCkZpbmFuY2lhbF9yYW5rX3RyYW5zZm9ybSA8LSBmaW5hbmNpYWxfbWFya2V0ICU+JQ0KICBtdXRhdGUoDQogICAgUk9FX1JhbmsgPSByYW5rKC1SZXR1cm5fb25fRXF1aXR5KQ0KICApDQpoZWFkKEZpbmFuY2lhbF9yYW5rX3RyYW5zZm9ybSkNCmBgYA0KDQojIyMgUGVuamVsYXNhbg0KLSBST0VfUmFuazogTWVtYmVyaSBwZXJpbmdrYXQgdGVyaGFkYXAgcGVydXNhaGFhbiBiZXJkYXNhcmthbiBST0UgdGVydGluZ2dpIGtlIHRlcmVuZGFoLg0KDQogIC0gRGFwYXQgZGlndW5ha2FuIHVudHVrIG1lbmd1cnV0a2FuIGRhbiBtZW1pbGloIHNhaGFtIHBhbGluZyBtZW5ndW50dW5na2FuLg0KDQojIyBUZXh0IENsZWFuaW5nICYgRmVhdHVyZSBDcmVhdGlvbg0KDQpgYGB7cn0NCkZpbmFuY2lhbF90ZXh0X2ZlYXR1cmUgPC0gZmluYW5jaWFsX21hcmtldCAlPiUNCiAgbXV0YXRlKA0KICAgIFN0b2NrX051bV9TdWZmaXggPSBhcy5udW1lcmljKHN0cl9leHRyYWN0KFN0b2NrX0lELCAiXFxkKyIpKQ0KICApDQpoZWFkKEZpbmFuY2lhbF90ZXh0X2ZlYXR1cmUpDQpgYGANCg0KIyMjIFBlbmplbGFzYW4NCg0KLSBTdG9ja19OdW1fU3VmZml4OiBNZW5nZWtzdHJhayBhbmdrYSBkYXJpIElEIHNhaGFtLiBJbmkgYmVyZ3VuYSBqaWthIElEIHNhaGFtIG1lbmdhbmR1bmcgcG9sYSB5YW5nIG1lbnlpcmF0a2FuIGthdGVnb3JpLCBiYXRjaCwgYXRhdSBncnVwLg0KDQogIC0gQ29udG9oOiBJRCAiQUJDMTIzIiDihpIgc3VmZml4ID0gMTIzDQoNCiMjIEN1bXVsYXRpdmUgRmVhdHVyZXMNCg0KYGBge3J9DQpGaW5hbmNpYWxfY3VtdWxhdGl2ZSA8LSBmaW5hbmNpYWxfbWFya2V0ICU+JQ0KICBhcnJhbmdlKFNlY3RvciwgRGF0ZSkgJT4lDQogIGdyb3VwX2J5KFNlY3RvcikgJT4lDQogIG11dGF0ZSgNCiAgICBDdW11bGF0aXZlX1ZvbHVtZSA9IGN1bXN1bShWb2x1bWVfVHJhZGVkKQ0KICApICU+JQ0KICB1bmdyb3VwKCkNCmhlYWQoRmluYW5jaWFsX2N1bXVsYXRpdmUpDQpgYGANCg0KIyMjIFBlbmplbGFzYW4NCi0gQ3VtdWxhdGl2ZV9Wb2x1bWU6IE1lbmp1bWxhaGthbiB0b3RhbCB2b2x1bWUgcGVyZGFnYW5nYW4gZGFyaSB3YWt0dSBrZSB3YWt0dSB1bnR1ayBzZXRpYXAgc2VrdG9yLg0KDQogIC0gQ29jb2sgdW50dWsgbWVsaWhhdCB0cmVuIGt1bXVsYXRpZiBha3Rpdml0YXMgcGFzYXIgZGkgc2VrdG9yIHRlcnRlbnR1Lg0KDQogIC0gQmlzYSBkaWd1bmFrYW4gZGFsYW0gYW5hbGlzaXMgbW9tZW50dW0gYXRhdSB0cmVuIGphbmdrYSBwYW5qYW5nLg0KDQojIyBLZXNpbXB1bGFuIFVtdW0NClNlbXVhIHByb3NlcyBmZWF0dXJlIGVuZ2luZWVyaW5nIGluaSBiZXJ0dWp1YW4gdW50dWs6DQoNCjEuICpNZW1wZXJrYXlhIGRhdGEqOiBNZW1idWF0IGZpdHVyIGJhcnUgeWFuZyBiaXNhIG1lbmFuZ2thcCBwb2xhLXBvbGEgdGVyc2VtYnVueWkgZGkgZGF0YSBtZW50YWguDQoNCjIuICpNZW1wZXJtdWRhaCBhbmFsaXNpcyBla3NwbG9yYXRpZio6IERlbmdhbiBhZ3JlZ2FzaSwgcmFua2luZywgZGFuIHRyYW5zZm9ybWFzaSwga2l0YSBiaXNhIGxlYmloIG11ZGFoIG1lbGloYXQgcGVydXNhaGFhbiB1bmdndWxhbiwgdHJlbiBzZWt0b3IsIGRhbiBwZXJiYW5kaW5nYW4gYW50YXIgcGVydXNhaGFhbi4NCg0KMy4gKk1lbmluZ2thdGthbiBwZXJmb3JtYSBtb2RlbCBtYWNoaW5lIGxlYXJuaW5nIGF0YXUgcHJlZGlrc2kqOiBGaXR1ciB5YW5nIGluZm9ybWF0aWYgZGFuIHJlbGV2YW4gZGFwYXQgbWVtYmFudHUgbW9kZWwgbWVuZ2VuYWxpIGh1YnVuZ2FuIGFudGFyYSB2YXJpYWJlbCBsZWJpaCBiYWlrLg0KDQo0LiAqTWVtYmVyaSBpbnNpZ2h0IGJpc25pcyBkYW4gaW52ZXN0YXNpKjogU2VwZXJ0aSBwZW1lcmluZ2thdGFuIHNhaGFtLCBrbGFzaWZpa2FzaSBzZWt0b3IsIGRhbiBpZGVudGlmaWthc2kgc2FoYW0gZGVuZ2FuIHBlcmZvcm1hwqBrZXVhbmdhbsKgYmFpay4NCg0KDQoNCg0KIyAqKkRldGVrc2kgT3V0bGllciBNZW5nZ3VuYWthbiBaLXNjb3JlIGRhbiBJUVIqKg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQoNCiMgLS0tIFotc2NvcmUgbWV0aG9kIGZvciBkZXRlY3Rpbmcgb3V0bGllcnMgLS0tDQpkYXRhX291dGxpZXJzIDwtIGZpbmFuY2lhbF9tYXJrZXQgJT4lDQogIG11dGF0ZSgNCiAgICB6X3Njb3Jlc19TUCA9IHNjYWxlKFN0b2NrX1ByaWNlKSwNCiAgICBPdXRsaWVyX0ZsYWcgPSBpZmVsc2UoYWJzKHpfc2NvcmVzX1NQKSA+IDMsICJPdXRsaWVyIiwgIk5vcm1hbCIpDQogICkNCg0KIyAtLS0gSVFSIG1ldGhvZCBmb3IgZGV0ZWN0aW5nIGFuZCByZW1vdmluZyBvdXRsaWVycyAtLS0NClExIDwtIHF1YW50aWxlKGZpbmFuY2lhbF9tYXJrZXQkVG90YWxfUHJpY2UsIDAuMjUpDQpRMyA8LSBxdWFudGlsZShmaW5hbmNpYWxfbWFya2V0JFRvdGFsX1ByaWNlLCAwLjc1KQ0KSVFSX3ZhbCA8LSBRMyAtIFExDQoNCmRhdGFfb3V0bGllcnNfaXFyIDwtIGZpbmFuY2lhbF9tYXJrZXQgJT4lDQogIGZpbHRlcigNCiAgICBTdG9ja19QcmljZSA+IChRMSAtIDEuNSAqIElRUl92YWwpICYgDQogICAgU3RvY2tfUHJpY2UgPCAoUTMgKyAxLjUgKiBJUVJfdmFsKQ0KICApDQoNCmhlYWQoZGF0YV9vdXRsaWVycykNCg0KYGBgDQoqKjEuIERldGVrc2kgT3V0bGllciBkZW5nYW4gTWV0b2RlIFotU2NvcmUqKg0KDQotIEFuZGEgbWVuZ2d1bmFrYW4gKipaLXNjb3JlIG1ldGhvZCoqIHVudHVrIG1lbmRldGVrc2kgb3V0bGllciBwYWRhIGtvbG9tICoqU3RvY2tfUHJpY2UqKi4NCi0gKipaLXNjb3JlKiogbWVuZ3VrdXIgc2ViZXJhcGEgamF1aCBzdWF0dSBuaWxhaSBkYXJpIHJhdGEtcmF0YSBkYWxhbSBzYXR1YW4gc3RhbmRhciBkZXZpYXNpLg0KICAgIC0gUnVtdXM6ICoqWiA9ICh4IC0gbWVhbikgLyBzZCoqDQotIEJhcmlzIGRhdGEgeWFuZyBtZW1pbGlraSBuaWxhaSBaLXNjb3JlIGxlYmloIGRhcmkgMyBhdGF1IGt1cmFuZyBkYXJpIC0zIGRpYW5nZ2FwICoqb3V0bGllci4qKg0KLSBLb2xvbSBiYXJ1ICoqT3V0bGllcl9GbGFnKiogZGl0YW1iYWhrYW4sIGJlcmlzaSBsYWJlbDoNCiAgICAtICoqT3V0bGllcioqIGppa2EgbmlsYWkgWi1zY29yZSBtZWxlYmloaSAzIGF0YXUga3VyYW5nIGRhcmkgLTMsDQogICAgLSAqKk5vcm1hbCoqIGppa2EgbWFzaWggZGFsYW0gYmF0YXMgbm9ybWFsLg0KICAgIA0KKioyLiBQZW1iZXJzaWhhbiBPdXRsaWVyIGRlbmdhbiBNZXRvZGUgSVFSKioNCg0KLSBNZXRvZGUga2VkdWEgbWVuZ2d1bmFrYW4gKipJbnRlcnF1YXJ0aWxlIFJhbmdlIChJUVIpKiogcGFkYSBrb2xvbSAqKlRvdGFsX1ByaWNlKiogdW50dWsgbWVuZGV0ZWtzaSBkYW4gbWVuZ2hhcHVzIG91dGxpZXIuDQotIFBlcmhpdHVuZ2FuOg0KICAgIC0gKipRMSAoa3VhcnRpbCAxKSoqID0gbmlsYWkgcGFkYSBwZXJzZW50aWwga2UtMjUsDQogICAgLSAqKlEzIChrdWFydGlsIDMpKiogPSBuaWxhaSBwYWRhIHBlcnNlbnRpbCBrZS03NSwNCiAgICAtICoqSVFSKiogPSBRMyAtIFExLg0KICAgIA0KLSBEYXRhIGRpYW5nZ2FwICoqb3V0bGllcioqIGppa2EgYmVyYWRhIGRpIGx1YXIgcmVudGFuZzoNClxbDQpcbGVmdFsgUV8xIC0gMS41IFx0aW1lcyBcdGV4dHtJUVJ9LFw7IFFfMyArIDEuNSBcdGltZXMgXHRleHR7SVFSfSBccmlnaHRdDQpcXQ0KDQotIEJhcmlzIHlhbmcgYmVyYWRhIGRpIGx1YXIgYmF0YXMgaW5pIGRpZmlsdGVyIGRhbiBkaWhhcHVzIGRhcmkgZGF0YSBkZW5nYW4gKipmaWx0ZXIoKSoqLg0KDQotIFotc2NvcmUgdW50dWsgbWVuZGV0ZWtzaSBvdXRsaWVyIHBhZGEgU3RvY2tfUHJpY2UsDQotIElRUiB1bnR1ayBtZW5naGFwdXMgb3V0bGllciBwYWRhIFRvdGFsX1ByaWNlLg0KLSBNZXRvZGUgaW5pIHBlbnRpbmcgdW50dWsgbWVtYmVyc2loa2FuIGRhdGEsIHNlaGluZ2dhIGFuYWxpc2lzIGF0YXUgbW9kZWwgeWFuZyBha2FuIGRpYmFuZ3VuIHRpZGFrIGJpYXMgb2xlaCBuaWxhaSBla3N0cmVtLg0KDQoNCg0KDQoNCiMgKipEaXNjcmV0aXphdGlvbiAoQmlubmluZykqKg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQoNCiMgQmFjYSBkYXRhDQpmaW5hbmNpYWxfbWFya2V0IDwtIGZpbmFuY2lhbF9tYXJrZXQNCg0KIyBCaW5uaW5nIChFcXVhbC1mcmVxdWVuY3kpIGRlbmdhbiBxdWFudGlsZQ0KYmlubmVkX2RhdGEgPC0gZmluYW5jaWFsX21hcmtldCAlPiUNCiAgbXV0YXRlKA0KICAgIFByaWNlX0xldmVsID0gY3V0KA0KICAgICAgU3RvY2tfUHJpY2UsDQogICAgICBicmVha3MgPSBxdWFudGlsZShTdG9ja19QcmljZSwgcHJvYnMgPSBjKDAsIDAuMzMsIDAuNjYsIDEpLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgbGFiZWxzID0gYygiTG93IiwgIk1lZGl1bSIsICJIaWdoIiksDQogICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUNCiAgICApDQogICkNCg0KI1RhbXBpbGthbiBoYXNpbCBiaW5uaW5nDQpoZWFkKGJpbm5lZF9kYXRhKQ0KYGBgDQoNCioqMS4gVHVqdWFuIEJpbm5pbmcqKg0KDQotIFR1anVhbm55YSBhZGFsYWggdW50dWsgbWVuZ2Vsb21wb2trYW4gZGF0YSBudW1lcmlrIGRhcmkga29sb20gKipTdG9ja19QcmljZSoqIG1lbmphZGkgYmViZXJhcGEgKiprYXRlZ29yaSBkaXNrcml0KiogKGRhbGFtIGhhbCBpbmk6IExvdywgTWVkaXVtLCBIaWdoKS4NCi0gSW5pIGRpc2VidXQgKiplcXVhbC1mcmVxdWVuY3kgYmlubmluZyosIGthcmVuYSBzZXRpYXAga2Vsb21wb2sgYWthbiBtZW1pbGlraSBqdW1sYWggZGF0YSB5YW5nIGt1cmFuZyBsZWJpaCBzYW1hLg0KDQoqKjIuIFByb3NlcyB5YW5nIERpbGxha3VrYW4qKg0KDQotICoqcXVhbnRpbGUoKSoqIGRpZ3VuYWthbiB1bnR1ayBtZW5lbnR1a2FuIGJhdGFzLWJhdGFzIChicmVha3MpIGJlcmRhc2Fya2FuICoqcGVyc2VudGlsKio6DQogICAgLSAwJSAobWluaW11bSksDQogICAgLSAzMyUgKHNlcGVydGlnYSBkYXRhKSwNCiAgICAtIDY2JSAoZHVhIHBlcnRpZ2EgZGF0YSksDQogICAgLSAxMDAlIChtYWtzaW11bSkuDQogICAgDQotIEtlbXVkaWFuIGZ1bmdzaSBjdXQoKSBtZW1iYWdpIGtvbG9tIFN0b2NrX1ByaWNlIGtlIGRhbGFtIHRpZ2Ega2Vsb21wb2sgYmVyZGFzYXJrYW4gYmF0YXMtYmF0YXMgdGVyc2VidXQsIHlhaXR1Og0KICAgIC0gKipMb3cqKjogbmlsYWkgZGFyaSBrdWFudGlsIGtlLTAgaGluZ2dhIGtlLTMzLA0KICAgIC0gKipNZWRpdW0qKjoga3VhbnRpbCBrZS0zMyBoaW5nZ2Ega2UtNjYsDQogICAgLSAqKkhpZ2gqKjoga3VhbnRpbCBrZS02NiBoaW5nZ2Ega2UtMTAwLg0KDQotIEtvbG9tIGhhc2lsIGthdGVnb3Jpc2FzaSB0ZXJzZWJ1dCBkaW5hbWFrYW4gKipQcmljZV9MZXZlbCoqLg0KDQoNCiMgU2Vhc29uYWxpdHkNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KDQpTZWFzb25hbGl0eSA8LSBmaW5hbmNpYWxfbWFya2V0ICU+JQ0KICBtdXRhdGUoDQogICAgRGF0ZSA9IGFzLkRhdGUoRGF0ZSwgZm9ybWF0ID0gIiVtLyVkLyVZIiksDQogICAgWWVhciA9IHllYXIoRGF0ZSksDQogICAgRGF5T2ZZZWFyID0geWRheShEYXRlKSwNCiAgICBEYXlzSW5ZZWFyID0gaWZfZWxzZShsZWFwX3llYXIoRGF0ZSksIDM2NiwgMzY1KSwNCiAgICBzaW5feWVhciA9IHNpbigyICogcGkgKiBEYXlPZlllYXIgLyBEYXlzSW5ZZWFyKSwNCiAgICBjb3NfeWVhciA9IGNvcygyICogcGkgKiBEYXlPZlllYXIgLyBEYXlzSW5ZZWFyKQ0KICApDQoNCmhlYWQoU2Vhc29uYWxpdHkpDQoNCmBgYA0KDQoqKktlc2ltcHVsYW4geWFuZyBEaWR1a3VuZyBvbGVoIERhdGEqKg0KDQoxLiAqKk11c2ltIE1hcmV04oCTSnVuaSAoc2luX3llYXIgdGluZ2dpKSoqOg0KDQotIDIwMjMtMDMtMTUgZGFuIDIwMjMtMDYtMDUgbWVudW5qdWtrYW4gKipzaW5feWVhcioqIHNhbmdhdCB0aW5nZ2kgZGFuIHBlcmZvcm1hIG1hc2luZy1tYXNpbmcgYWRhbGFoICpTdGFibGUgZGFuIFBvc2l0aXZlKi4NCg0KLSBJbmkgbWVuZHVrdW5nIHBvbGEgYmFod2EgKiprdWFydGFsIDIgKFEyKSoqIHNlcmluZyBtZW51bmp1a2thbiBwZXJmb3JtYSAqKmt1YXQgYXRhdSBzdGFiaWwqKi4NCg0KDQoyLiAqKkFraGlyIFRhaHVuIOKAkyBOb3ZlbWJlciAoc2luX3llYXIgbmVnYXRpZiB0aW5nZ2kpKio6DQoNCi0gMjAyMC0xMS0xNiAoKnNpbl95ZWFyID0gLTAuNjk3OSopIDogKipTdGFibGUqKg0KDQotIFRpZGFrIGVrc3RyZW0gbmVnYXRpZiwgdGFwaSBtZW5kZWthdGkuIE1lbnVuanVra2FuICoqa2luZXJqYSB5YW5nIHRpZGFrIHRlcmxhbHUga3VhdCoqLCBzZXN1YWkgZGVuZ2FuIGR1Z2FhbiBiYWh3YSBkaSBha2hpciB0YWh1biBiaXNhIHRlcmphZGkgdGVrYW5hbiBwYXNhciAobWlzYWxueWEsIGFrc2kgYW1iaWwgdW50dW5nL3Rha2UgcHJvZml0KS4NCg0KDQozLiAqKkF3YWwgVGFodW4g4oCTIEphbnVhcmkgKHNpbiBkYW4gY29zIG1lbmRla2F0aSBla3N0cmVtIHBvc2l0aWYpKio6DQoNCi0gMjAyMy0wMS0wMiA6IHNpbl95ZWFyIOKJiCAwLCBjb3NfeWVhciDiiYggMSA6ICoqU3RhYmxlKioNCg0KLSBTZXN1YWkgZGVuZ2FuIGludGVycHJldGFzaSBiYWh3YSBhd2FsIHRhaHVuIGNlbmRlcnVuZyBuZXRyYWwgYXRhdSBiZWx1bSBtZW51bmp1a2thbiB0cmVuIGplbGFzLg0KDQoNCjQuICoqUGVydGVuZ2FoYW4gVGFodW4g4oCTIEp1bGkgKHNpbl95ZWFyIG1lbmRla2F0aSBub2wsIGNvc195ZWFyIG5lZ2F0aWYgdGluZ2dpKSoqOg0KDQotIDIwMjEtMDctMTQgOiAqUG9zaXRpdmUqIG1lc2tpcHVuIHNpbl95ZWFyID0gLTAuMjEzNSA6IGJlcmFydGkgYWRhIHBvdGVuc2kga2luZXJqYSBiYWlrIGRpIHBlcnRlbmdhaGFuIHRhaHVuLCBtZXNraXB1biBzZWNhcmEgbXVzaW1hbiBiZXJhZGEgZGFsYW0gdHJhbnNpc2l0dXJ1bi4NCg0KNS4gKipBbm9tYWxpKio6DQoNCi0gMjAyMy0wMy0yMiA6ICoqc2luX3llYXIqKiBzYW5nYXQgdGluZ2dpICgwLjk4NDUpIHRhcGkgcGVyZm9ybWEgTmVnYXRpdmUuIEluaSBtZW51bmp1a2thbiBiYWh3YSBtZXNraXB1biBzaW55YWwgbXVzaW1hbiBtZW5kdWt1bmcgcGVydHVtYnVoYW4sIGZha3RvciBsYWluIChtdW5na2luIGVrc3Rlcm5hbCBhdGF1IHNla3RvciB0ZXJ0ZW50dSkgYmlzYSBtZW1lbmdhcnVoaSBuZWdhdGlmLg0KDQo=