Ujian Tengah Semester

Pemograman Sains Data 1

Logo


1 Operasi dan Tipe Data Dasar


Program ini ditulis dalam bahasa R, dan bertujuan untuk:

  1. Menerima dua bilangan dari pengguna.

  2. Melakukan empat operasi matematika dasar: penjumlahan, perkalian, pembagian, dan perpangkatan.

  3. Menampilkan hasil dari setiap operasi.

  4. Menampilkan tipe data dari setiap hasil operasi.

# Menerima dua bilangan dari pengguna
bilangan_pertama <- as.numeric(readline(prompt = "Masukkan bilangan pertama: "))
## Masukkan bilangan pertama:
bilangan_kedua <- as.numeric(readline(prompt = "Masukkan bilangan kedua: "))
## Masukkan bilangan kedua:
#  a. Menghitung hasil operasi matematika
penjumlahan <- bilangan_pertama + bilangan_kedua
perkalian <- bilangan_pertama * bilangan_kedua
pembagian <- bilangan_pertama / bilangan_kedua
pangkat <- bilangan_pertama ^ bilangan_kedua

# b. Menampilkan hasil operasi matematika
cat("Hasil Penjumlahan: ", penjumlahan, "\n")
## Hasil Penjumlahan:  NA
cat("Hasil Perkalian: ", perkalian, "\n")
## Hasil Perkalian:  NA
cat("Hasil Pembagian: ", pembagian, "\n")
## Hasil Pembagian:  NA
cat("Hasil Pangkat: ", pangkat, "\n")
## Hasil Pangkat:  NA
# c. Menampilkan tipe data masing-masing hasil operasi matematika
cat("Tipe data hasil Penjumlahan: ", class(penjumlahan), "\n")
## Tipe data hasil Penjumlahan:  numeric
cat("Tipe data hasil Perkalian: ", class(perkalian), "\n")
## Tipe data hasil Perkalian:  numeric
cat("Tipe data hasil Pembagian: ", class(pembagian), "\n")
## Tipe data hasil Pembagian:  numeric
cat("Tipe data hasil Pangkat: ", class(pangkat), "\n")
## Tipe data hasil Pangkat:  numeric

Penjelasan Kode :

  1. Input Dari Pemgguna
  • readline() meminta input dari pengguna berupa teks.

  • as.numeric() mengubah input tersebut menjadi angka (tipe numeric).

  • Nilai ini disimpan ke dalam variabel bilangan_pertama dan bilangan_kedua.

  1. Operasi Matematika

Keempat operasi dilakukan langsung dengan operator matematika standar di R:

  • \(+\) untuk penjumlahan

  • \(*\) untuk perkalian

  • \(/\) untuk pembagian

  • \(^\) untuk perpangkatan

  1. Menampilkan Hasil
  • Fungsi cat() mencetak teks dan nilai variabel ke layar.

  • *digunakan untuk pindah baris.

  1. Menampilkan Tipe Data
  • Fungsi class() digunakan untuk mengetahui tipe data dari suatu variabel.

  • Karena semua operasi menggunakan angka, hasil dari class(…) umumnya akan berupa “numeric”.


2 Struktur Kendali (Control Flow)


Program ini dibuat untuk:

  1. Menerima input nilai ujian dari pengguna.

  2. Memastikan nilai berada pada rentang yang valid (0–100).

  3. Memberikan kategori penilaian berdasarkan nilai tersebut.

  4. Menampilkan pesan kesalahan jika input tidak valid.

# a. Menerima input nilai ujian dari pengguna
nilai <- as.numeric(readline(prompt = "Masukkan nilai ujian (0-100): "))
## Masukkan nilai ujian (0-100):
# Validasi nilai harus berada dalam rentang 0–100
if (!is.na(nilai) && nilai >= 0 && nilai <= 100) {
  # b. Menampilkan keterangan berdasarkan nilai
  if (nilai >= 85) {
    cat("Sangat Baik\n")
  } else if (nilai >= 70 && nilai <= 84) {
    cat("Baik\n")
  } else if (nilai >= 60 && nilai <= 69) {
    cat("Cukup\n")
  } else if(nilai < 60) {
    cat("Perlu Perbaikan\n")
  }
} else {
  cat("Nilai ujian tidak valid. Harap masukkan nilai antara 0-100.\n")
}
## Nilai ujian tidak valid. Harap masukkan nilai antara 0-100.

Penjelasan Kode:

  1. Input Nilai Ujian
  • readline() digunakan untuk mengambil input dari pengguna dalam bentuk teks.

  • as.numeric() mengubah input menjadi angka.

  • Nilai disimpan dalam variabel nilai.

  1. Validasi Nilai
  • !is.na(nilai) memastikan nilai bukan NA (bukan kosong atau gagal dikonversi).

  • nilai >= 0 && nilai <= 100 memastikan nilai berada dalam rentang valid.

  • Jika kondisi ini terpenuhi, program lanjut mengevaluasi kategori nilai.

  1. Kategori Penilaian
  • Setiap hasil ditampilkan dengan cat() disertai *untuk pindah baris.
  1. Penanganan Input Tidak Valid
  • Jika nilai tidak memenuhi syarat valid, maka akan ditampilkan pesan kesalahan.

3 Fungsi dan Perulangan


Program ini dibuat untuk:

  1. Menerima input bilangan bulat dari pengguna sebagai batas atas.

  2. Mencetak semua bilangan kelipatan 4 dari 1 hingga bilangan tersebut.

  3. Menampilkan pesan kesalahan jika input tidak valid.

# Menerima input integer dari pengguna
n <- as.integer(readline(prompt = "Masukkan nilai batasan dari kelipatan genap: "))
## Masukkan nilai batasan dari kelipatan genap:
# Cek apakah n valid
if (is.na(n) || n <= 0) {
  cat("Input tidak valid. Harap masukkan bilangan bulat positif.\n")
} else {
  # Menyimpan hasil bilangan genap kelipatan 4 dalam vektor
  kelipatan_4 <- c()

  # Loop dari 1 sampai n
  for (i in 1:n) {
    if (i %% 4 == 0) {
      kelipatan_4 <- c(kelipatan_4, i)
    }
  }

  # Tampilkan hasil
  cat("Kelipatan 4 dari 1 sampai", n, "adalah:\n")
  cat(paste(kelipatan_4, collapse = ", "), "\n")
}
## Input tidak valid. Harap masukkan bilangan bulat positif.

Penjelasan Kode:

  1. Input Dari Pengguna
  • readline() meminta input dari pengguna dalam bentuk teks.

  • as.integer() mengubah input menjadi bilangan bulat.

  • Nilai ini disimpan dalam variabel n.

  1. Validasi Input
  • is.na(n) mengecek apakah input gagal dikonversi ke integer.

  • n <= 0 memastikan nilai adalah bilangan bulat positif.

  • Jika kondisi ini terpenuhi, maka pengguna diberi peringatan.

  1. Perulangan dan Pengecekan Kelipatan
  • for (i in 1:n) artinya program akan memeriksa setiap bilangan dari 1 sampai n.

  • i %% 4 == 0 mengecek apakah i adalah kelipatan 4 (sisa bagi = 0).

  • c(kelipatan_4, i) menambahkan nilai i ke vektor kelipatan_4.

  1. Menampilkan Hasil
  • cat() mencetak hasil ke layar.

  • paste(…, collapse = “,”) digunakan untuk menggabungkan elemen vektor kelipatan_4 menjadi satu string dengan pemisah koma.


4 Studi Kasus


4.1 Data Collection

Program ini dibuat untuk:

  1. Membaca satu sheet tertentu dari file Excel.

  2. Menampilkan jumlah baris dan kolom dari data tersebut

# Install dan panggil package yang diperlukan
library(readxl)
## Warning: package 'readxl' was built under R version 4.4.3
# Baca sheet "Append1" dari file Excel
data <- read_excel("data_penjualan_kuartal1_full.append.xlsx", sheet = "Append1")

# Tampilkan jumlah baris dan kolom
cat("Jumlah baris:", nrow(data), "\n")
## Jumlah baris: 150
cat("Jumlah kolom:", ncol(data), "\n")
## Jumlah kolom: 9

Penjelasan Kode :

  1. Memanggil Package:
  • readxl adalah package di R untuk membaca file Excel (.xls dan .xlsx).

  • Package ini harus di-install terlebih dahulu dengan install.packages(“readxl”) jika belum tersedia.

  1. Membaca Data dari Sheet Tertentu
  • read_excel() digunakan untuk membaca file Excel.

  • Argumen sheet = “Append1” menandakan bahwa hanya sheet bernama “Append1” yang akan dibaca.

  • Data hasil pembacaan disimpan dalam variabel data.

  1. Menampilkan Dimensi Data
  • nrow(data) menghitung jumlah baris (observasi).

  • ncol(data) menghitung jumlah kolom (variabel).

  • cat() digunakan untuk mencetak informasi ke konsol.

4.2 Data Cleaning

Program ini dibuat untuk:

  1. Membersihkan dan mempersiapkan data penjualan dari file Excel agar siap dianalisis. Proses ini melibatkan:
  • standarisasi tanggal,

  • konversi kolom numerik,

  • perhitungan ulang nilai total,

  • penanganan data tidak valid,

  • penghapusan data kosong.

library(readxl)
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.4.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(stringr)
library(lubridate)
## Warning: package 'lubridate' was built under R version 4.4.3
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
library(writexl)
## Warning: package 'writexl' was built under R version 4.4.3
# Load data
df <- read_excel("data_penjualan_kuartal1_full.append.xlsx")

# a. Standarkan format tanggal
df <- df %>% mutate(Tanggal = format(dmy(Tanggal), "%Y-%m-%d"))

# b. Ubah Harga dan Jumlah ke numerik
# Harga: hapus karakter non-digit
df$Harga <- as.numeric(gsub("[^0-9]", "", df$Harga))

# Jumlah: ubah teks angka ke numerik
text_to_number <- c("satu"=1, "dua"=2, "tiga"=3, "empat"=4, "lima"=5,
                    "enam"=6, "tujuh"=7, "delapan"=8, "sembilan"=9, "sepuluh"=10)

df$Jumlah <- ifelse(df$Jumlah %in% names(text_to_number),
                    text_to_number[df$Jumlah],
                    df$Jumlah)
df$Jumlah <- as.numeric(df$Jumlah)

# c. Hitung ulang kolom Total
df <- df %>% mutate(Total = Harga * Jumlah)

# d. Ganti nilai tidak valid dengan NA
df[df == "-" | df == "Rp" | df == "_anonymous_"] <- NA

# e. Hapus baris dengan Produk kosong atau "-"
df <- df %>% filter(!Produk %in% c("-", NA, ""))

# Simpan hasil
write_xlsx(df, "data_penjualan_bersih.xlsx")

# Tampilkan beberapa baris pertama setelah setiap langkah

head(df)

Penjelasan Kode:

  1. Library yang Digunakan
  • readxl : Membaca file Excel.

  • dplyr : Memudahkan manipulasi data (filter, mutate, dll).

  • stringr : Pemrosesan string (tidak dipakai eksplisit di sini, tapi berguna).

  • lubridate : Untuk parsing dan formatting tanggal.

  • writexl : Untuk menyimpan data ke Excel kembali.

  1. Membaca Data
  • Membaca seluruh isi file Excel ke dalam variabel df.
  1. Standarisasi Format Tanggal
  • Mengubah kolom Tanggal ke format YYYY-MM-DD setelah memastikan bahwa urutan tanggalnya day-month-year.
  1. Konversi Harga dan Jumlah
  • Menghapus semua karakter non-angka (misalnya simbol Rp, titik, koma) dan ubah menjadi numerik.

  • Konversi teks angka (seperti “lima”) menjadi angka (5).

  • Kemudian as.numeric() untuk memastikan semuanya bertipe numerik.

  1. Hitung Kolom Total
  • Membuat ulang kolom Total berdasarkan hasil perkalian Harga dan Jumlah.
  1. Ganti Nilai Tak Valid dengan NA
  • Semua nilai tidak valid diganti menjadi NA agar bisa difilter atau dianalisis lebih baik.
  1. Hapus Baris dengan Produk Kosong
  • Hapus semua baris yang memiliki nilai Produk kosong, tanda -, atau NA.
  1. Simpan Data ke File Baru
  • Simpan hasil akhir ke file Excel baru untuk dokumentasi atau analisis lanjutan.
  1. Tampilkan Data
  • Menampilkan beberapa baris awal untuk melihat hasil akhir pembersihan data.

4.3 Data Transformation

Program ini dibuat untuk:

  1. Melakukan analisis ringkasan terhadap data penjualan yang telah dibersihkan sebelumnya, mencakup:
  • Penjualan per kategori produk

  • Jumlah transaksi per kota

  • Total penjualan bulanan

library(readxl)
library(dplyr)
library(lubridate)
library(writexl)

# Load data (asumsi data sudah dibersihkan)
df <- read_excel("data_penjualan_bersih.xlsx")

# a. Tambahkan kolom Bulan
df <- df %>% mutate(Bulan = format(ymd(Tanggal), "%Y-%m"))

# b. Hitung total penjualan per kategori produk
total_per_kategori <- df %>%
  group_by(Kategori) %>%
  summarise(`Total Penjualan` = sum(Total, na.rm = TRUE))

# c. Hitung jumlah transaksi dari setiap kota
transaksi_per_kota <- df %>%
  group_by(Kota) %>%
  summarise(`Jumlah Transaksi` = n())

# d. Ringkasan jumlah total penjualan per bulan
total_per_bulan <- df %>%
  group_by(Bulan) %>%
  summarise(`Total Penjualan Bulanan` = sum(Total, na.rm = TRUE))

# (Opsional) Simpan semua hasil ke file Excel
write_xlsx(list(
  "Penjualan per Kategori" = total_per_kategori,
  "Transaksi per Kota" = transaksi_per_kota,
  "Penjualan per Bulan" = total_per_bulan
), "ringkasan_penjualan.xlsx")

cat("Total Penjualan per Kategori:\n")
## Total Penjualan per Kategori:
print(total_per_kategori)
## # A tibble: 4 × 2
##   Kategori   `Total Penjualan`
##   <chr>                  <dbl>
## 1 Aksesoris          181400000
## 2 Elektronik          87750000
## 3 Fashion            143600000
## 4 <NA>               194700000
cat("\nJumlah Transaksi per Kota:\n")
## 
## Jumlah Transaksi per Kota:
print(transaksi_per_kota)
## # A tibble: 4 × 2
##   Kota     `Jumlah Transaksi`
##   <chr>                 <int>
## 1 Bandung                  10
## 2 Jakarta                  10
## 3 Surabaya                 10
## 4 <NA>                     12
cat("\nTotal Penjualan per Bulan:\n")
## 
## Total Penjualan per Bulan:
print(total_per_bulan)
## # A tibble: 2 × 2
##   Bulan   `Total Penjualan Bulanan`
##   <chr>                       <dbl>
## 1 2024-01                 489150000
## 2 2024-02                 118300000

Penjelasan Kode:

  1. Tambah Kolom Bulan
  • Ekstrak bagian bulan dari kolom Tanggal dan simpan dalam format YYYY-MM.
  1. Total Penjualan per Kategori
  • Menjumlahkan nilai Total untuk tiap kategori, mengabaikan NA.
  1. Jumlah Transaksi per Kota
  • Menghitung jumlah baris (transaksi) untuk setiap kota.
  1. Ringkasan Penjualan Bulanan
  • Menjumlahkan total penjualan per bulan.
  1. Simpan ke Excel (opsional tapi sangat berguna)
  • Menyimpan tiga tabel ringkasan ke dalam satu file Excel, tiap tabel di sheet terpisah.
  1. Cetak Hasil ke Konsol
  • Menampilkan hasil ke terminal atau konsol R.
LS0tDQp0aXRsZTogIlVqaWFuIFRlbmdhaCBTZW1lc3RlciINCnN1YnRpdGxlOiAiUGVtb2dyYW1hbiBTYWlucyBEYXRhIDEiDQphdXRob3I6IA0KICAtICJEd2kgU3JpIFlhbnRpIE1hbnVsbGFuZyINCmRhdGU6ICAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclQiAlZCwgJVknKWAiDQpvdXRwdXQ6DQogIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOiAgICMgaHR0cHM6Ly9naXRodWIuY29tL2p1YmEvcm1kZm9ybWF0cw0KICAgIHNlbGZfY29udGFpbmVkOiB0cnVlDQogICAgdGh1bWJuYWlsczogdHJ1ZQ0KICAgIGxpZ2h0Ym94OiB0cnVlDQogICAgZ2FsbGVyeTogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGxpYl9kaXI6IGxpYnMNCiAgICBkZl9wcmludDogInBhZ2VkIg0KICAgIGNvZGVfZm9sZGluZzogInNob3ciDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgY3NzOiAiQzovVXNlcnMvRHdpIE1hbnVsbGFuZy9Eb3dubG9hZHMvc3R5bGUuY3NzIg0KLS0tDQo8aW1nIGlkPSJsb2dvLXV0YW1hIiBzcmM9IkM6L1VzZXJzL0R3aSBNYW51bGxhbmcvUGljdHVyZXMvQ2FtZXJhIFJvbGwvZHdpLmpwZyIgYWx0PSJMb2dvIiBzdHlsZT0id2lkdGg6MjAwcHg7IGRpc3BsYXk6IGJsb2NrOyBtYXJnaW46IGF1dG87Ij4NCg0KLS0tDQoNCiMgKipPcGVyYXNpIGRhbiBUaXBlIERhdGEgRGFzYXIqKg0KDQotLS0NCg0KKipQcm9ncmFtIGluaSBkaXR1bGlzIGRhbGFtIGJhaGFzYSBSLCBkYW4gYmVydHVqdWFuIHVudHVrKio6DQoNCjEuIE1lbmVyaW1hIGR1YSBiaWxhbmdhbiBkYXJpIHBlbmdndW5hLg0KDQoyLiBNZWxha3VrYW4gZW1wYXQgb3BlcmFzaSBtYXRlbWF0aWthIGRhc2FyOiBwZW5qdW1sYWhhbiwgcGVya2FsaWFuLCBwZW1iYWdpYW4sIGRhbiBwZXJwYW5na2F0YW4uDQoNCjMuIE1lbmFtcGlsa2FuIGhhc2lsIGRhcmkgc2V0aWFwIG9wZXJhc2kuDQoNCjQuIE1lbmFtcGlsa2FuIHRpcGUgZGF0YSBkYXJpIHNldGlhcCBoYXNpbCBvcGVyYXNpLg0KDQoNCmBgYHtyLCAgIG1lc3NhZ2U9VFJVRSwgd2FybmluZz1UUlVFLCBlY2hvPVRSVUV9DQoNCiMgTWVuZXJpbWEgZHVhIGJpbGFuZ2FuIGRhcmkgcGVuZ2d1bmENCmJpbGFuZ2FuX3BlcnRhbWEgPC0gYXMubnVtZXJpYyhyZWFkbGluZShwcm9tcHQgPSAiTWFzdWtrYW4gYmlsYW5nYW4gcGVydGFtYTogIikpDQpiaWxhbmdhbl9rZWR1YSA8LSBhcy5udW1lcmljKHJlYWRsaW5lKHByb21wdCA9ICJNYXN1a2thbiBiaWxhbmdhbiBrZWR1YTogIikpDQoNCiMgIGEuIE1lbmdoaXR1bmcgaGFzaWwgb3BlcmFzaSBtYXRlbWF0aWthDQpwZW5qdW1sYWhhbiA8LSBiaWxhbmdhbl9wZXJ0YW1hICsgYmlsYW5nYW5fa2VkdWENCnBlcmthbGlhbiA8LSBiaWxhbmdhbl9wZXJ0YW1hICogYmlsYW5nYW5fa2VkdWENCnBlbWJhZ2lhbiA8LSBiaWxhbmdhbl9wZXJ0YW1hIC8gYmlsYW5nYW5fa2VkdWENCnBhbmdrYXQgPC0gYmlsYW5nYW5fcGVydGFtYSBeIGJpbGFuZ2FuX2tlZHVhDQoNCiMgYi4gTWVuYW1waWxrYW4gaGFzaWwgb3BlcmFzaSBtYXRlbWF0aWthDQpjYXQoIkhhc2lsIFBlbmp1bWxhaGFuOiAiLCBwZW5qdW1sYWhhbiwgIlxuIikNCmNhdCgiSGFzaWwgUGVya2FsaWFuOiAiLCBwZXJrYWxpYW4sICJcbiIpDQpjYXQoIkhhc2lsIFBlbWJhZ2lhbjogIiwgcGVtYmFnaWFuLCAiXG4iKQ0KY2F0KCJIYXNpbCBQYW5na2F0OiAiLCBwYW5na2F0LCAiXG4iKQ0KDQojIGMuIE1lbmFtcGlsa2FuIHRpcGUgZGF0YSBtYXNpbmctbWFzaW5nIGhhc2lsIG9wZXJhc2kgbWF0ZW1hdGlrYQ0KY2F0KCJUaXBlIGRhdGEgaGFzaWwgUGVuanVtbGFoYW46ICIsIGNsYXNzKHBlbmp1bWxhaGFuKSwgIlxuIikNCmNhdCgiVGlwZSBkYXRhIGhhc2lsIFBlcmthbGlhbjogIiwgY2xhc3MocGVya2FsaWFuKSwgIlxuIikNCmNhdCgiVGlwZSBkYXRhIGhhc2lsIFBlbWJhZ2lhbjogIiwgY2xhc3MocGVtYmFnaWFuKSwgIlxuIikNCmNhdCgiVGlwZSBkYXRhIGhhc2lsIFBhbmdrYXQ6ICIsIGNsYXNzKHBhbmdrYXQpLCAiXG4iKQ0KDQpgYGANCg0KKipQZW5qZWxhc2FuIEtvZGUqKiA6DQoNCjEuICoqSW5wdXQgRGFyaSBQZW1nZ3VuYSoqDQoNCi0gKnJlYWRsaW5lKCkqIG1lbWludGEgaW5wdXQgZGFyaSBwZW5nZ3VuYSBiZXJ1cGEgdGVrcy4NCg0KLSAqYXMubnVtZXJpYygpKiBtZW5ndWJhaCBpbnB1dCB0ZXJzZWJ1dCBtZW5qYWRpIGFuZ2thICh0aXBlICoqbnVtZXJpYyoqKS4NCg0KLSBOaWxhaSBpbmkgZGlzaW1wYW4ga2UgZGFsYW0gdmFyaWFiZWwgKipiaWxhbmdhbl9wZXJ0YW1hKiogZGFuICoqYmlsYW5nYW5fa2VkdWEqKi4NCg0KDQoyLiAqKk9wZXJhc2kgTWF0ZW1hdGlrYSoqDQoNCktlZW1wYXQgb3BlcmFzaSBkaWxha3VrYW4gbGFuZ3N1bmcgZGVuZ2FuIG9wZXJhdG9yIG1hdGVtYXRpa2Egc3RhbmRhciBkaSBSOg0KDQotICQrJCB1bnR1ayBwZW5qdW1sYWhhbg0KDQotICQqJCB1bnR1ayBwZXJrYWxpYW4NCg0KLSAkLyQgdW50dWsgcGVtYmFnaWFuDQoNCi0gJF4kIHVudHVrIHBlcnBhbmdrYXRhbg0KDQoNCjMuICoqTWVuYW1waWxrYW4gSGFzaWwqKg0KDQotIEZ1bmdzaSAqY2F0KCkqIG1lbmNldGFrIHRla3MgZGFuIG5pbGFpIHZhcmlhYmVsIGtlIGxheWFyLg0KDQotICpcbiogZGlndW5ha2FuIHVudHVrIHBpbmRhaCBiYXJpcy4NCg0KDQo0LiAqKk1lbmFtcGlsa2FuIFRpcGUgRGF0YSoqDQoNCi0gRnVuZ3NpICpjbGFzcygpKiBkaWd1bmFrYW4gdW50dWsgbWVuZ2V0YWh1aSB0aXBlIGRhdGEgZGFyaSBzdWF0dSB2YXJpYWJlbC4NCg0KLSBLYXJlbmEgc2VtdWEgb3BlcmFzaSBtZW5nZ3VuYWthbiBhbmdrYSwgaGFzaWwgZGFyaSAqY2xhc3MoLi4uKSogdW11bW55YSBha2FuIGJlcnVwYSAqIm51bWVyaWMiKi4NCg0KDQotLS0NCg0KIyBTdHJ1a3R1ciBLZW5kYWxpIChDb250cm9sIEZsb3cpDQoNCi0tLQ0KDQoqKlByb2dyYW0gaW5pIGRpYnVhdCB1bnR1ayoqOg0KDQoxLiBNZW5lcmltYSBpbnB1dCBuaWxhaSB1amlhbiBkYXJpIHBlbmdndW5hLg0KDQoyLiBNZW1hc3Rpa2FuIG5pbGFpIGJlcmFkYSBwYWRhIHJlbnRhbmcgeWFuZyB2YWxpZCAoMOKAkzEwMCkuDQoNCjMuIE1lbWJlcmlrYW4ga2F0ZWdvcmkgcGVuaWxhaWFuIGJlcmRhc2Fya2FuIG5pbGFpIHRlcnNlYnV0Lg0KDQo0LiBNZW5hbXBpbGthbiBwZXNhbiBrZXNhbGFoYW4gamlrYSBpbnB1dCB0aWRhayB2YWxpZC4NCg0KYGBge3IsICAgbWVzc2FnZT1UUlVFLCB3YXJuaW5nPVRSVUUsIGVjaG89VFJVRX0NCg0KIyBhLiBNZW5lcmltYSBpbnB1dCBuaWxhaSB1amlhbiBkYXJpIHBlbmdndW5hDQpuaWxhaSA8LSBhcy5udW1lcmljKHJlYWRsaW5lKHByb21wdCA9ICJNYXN1a2thbiBuaWxhaSB1amlhbiAoMC0xMDApOiAiKSkNCg0KIyBWYWxpZGFzaSBuaWxhaSBoYXJ1cyBiZXJhZGEgZGFsYW0gcmVudGFuZyAw4oCTMTAwDQppZiAoIWlzLm5hKG5pbGFpKSAmJiBuaWxhaSA+PSAwICYmIG5pbGFpIDw9IDEwMCkgew0KICAjIGIuIE1lbmFtcGlsa2FuIGtldGVyYW5nYW4gYmVyZGFzYXJrYW4gbmlsYWkNCiAgaWYgKG5pbGFpID49IDg1KSB7DQogICAgY2F0KCJTYW5nYXQgQmFpa1xuIikNCiAgfSBlbHNlIGlmIChuaWxhaSA+PSA3MCAmJiBuaWxhaSA8PSA4NCkgew0KICAgIGNhdCgiQmFpa1xuIikNCiAgfSBlbHNlIGlmIChuaWxhaSA+PSA2MCAmJiBuaWxhaSA8PSA2OSkgew0KICAgIGNhdCgiQ3VrdXBcbiIpDQogIH0gZWxzZSBpZihuaWxhaSA8IDYwKSB7DQogICAgY2F0KCJQZXJsdSBQZXJiYWlrYW5cbiIpDQogIH0NCn0gZWxzZSB7DQogIGNhdCgiTmlsYWkgdWppYW4gdGlkYWsgdmFsaWQuIEhhcmFwIG1hc3Vra2FuIG5pbGFpIGFudGFyYSAwLTEwMC5cbiIpDQp9DQpgYGANCg0KKipQZW5qZWxhc2FuIEtvZGUqKjoNCg0KMS4gKipJbnB1dCBOaWxhaSBVamlhbioqDQoNCi0gKnJlYWRsaW5lKCkqIGRpZ3VuYWthbiB1bnR1ayBtZW5nYW1iaWwgaW5wdXQgZGFyaSBwZW5nZ3VuYSBkYWxhbSBiZW50dWsgdGVrcy4NCg0KLSAqYXMubnVtZXJpYygpKiBtZW5ndWJhaCBpbnB1dCBtZW5qYWRpIGFuZ2thLg0KDQotIE5pbGFpIGRpc2ltcGFuIGRhbGFtIHZhcmlhYmVsIG5pbGFpLg0KDQoNCjIuICoqVmFsaWRhc2kgTmlsYWkqKg0KDQotICoqIWlzLm5hKG5pbGFpKSoqIG1lbWFzdGlrYW4gbmlsYWkgYnVrYW4gKk5BKiAoYnVrYW4ga29zb25nIGF0YXUgZ2FnYWwgZGlrb252ZXJzaSkuDQoNCi0gKipuaWxhaSA+PSAwICYmIG5pbGFpIDw9IDEwMCoqIG1lbWFzdGlrYW4gbmlsYWkgYmVyYWRhIGRhbGFtIHJlbnRhbmcgdmFsaWQuDQoNCi0gSmlrYSBrb25kaXNpIGluaSB0ZXJwZW51aGksIHByb2dyYW0gbGFuanV0IG1lbmdldmFsdWFzaSBrYXRlZ29yaSBuaWxhaS4NCg0KDQozLiAqKkthdGVnb3JpIFBlbmlsYWlhbioqDQoNCi0gU2V0aWFwIGhhc2lsIGRpdGFtcGlsa2FuIGRlbmdhbiAqY2F0KCkqIGRpc2VydGFpICpcbiogdW50dWsgcGluZGFoIGJhcmlzLg0KDQoNCjQuICoqUGVuYW5nYW5hbiBJbnB1dCBUaWRhayBWYWxpZCoqDQoNCi0gSmlrYSBuaWxhaSB0aWRhayBtZW1lbnVoaSBzeWFyYXQgdmFsaWQsIG1ha2EgYWthbiBkaXRhbXBpbGthbiBwZXNhbiBrZXNhbGFoYW4uDQoNCg0KLS0tDQoNCiMgRnVuZ3NpIGRhbiBQZXJ1bGFuZ2FuDQoNCi0tLQ0KDQoqKlByb2dyYW0gaW5pIGRpYnVhdCB1bnR1ayoqOg0KDQoxLiBNZW5lcmltYSBpbnB1dCBiaWxhbmdhbiBidWxhdCBkYXJpIHBlbmdndW5hIHNlYmFnYWkgYmF0YXMgYXRhcy4NCg0KMi4gTWVuY2V0YWsgc2VtdWEgYmlsYW5nYW4ga2VsaXBhdGFuIDQgZGFyaSAxIGhpbmdnYSBiaWxhbmdhbiB0ZXJzZWJ1dC4NCg0KMy4gTWVuYW1waWxrYW4gcGVzYW4ga2VzYWxhaGFuIGppa2EgaW5wdXQgdGlkYWsgdmFsaWQuDQoNCg0KYGBge3IsICAgbWVzc2FnZT1UUlVFLCB3YXJuaW5nPVRSVUUsIGVjaG89VFJVRX0NCg0KIyBNZW5lcmltYSBpbnB1dCBpbnRlZ2VyIGRhcmkgcGVuZ2d1bmENCm4gPC0gYXMuaW50ZWdlcihyZWFkbGluZShwcm9tcHQgPSAiTWFzdWtrYW4gbmlsYWkgYmF0YXNhbiBkYXJpIGtlbGlwYXRhbiBnZW5hcDogIikpDQoNCiMgQ2VrIGFwYWthaCBuIHZhbGlkDQppZiAoaXMubmEobikgfHwgbiA8PSAwKSB7DQogIGNhdCgiSW5wdXQgdGlkYWsgdmFsaWQuIEhhcmFwIG1hc3Vra2FuIGJpbGFuZ2FuIGJ1bGF0IHBvc2l0aWYuXG4iKQ0KfSBlbHNlIHsNCiAgIyBNZW55aW1wYW4gaGFzaWwgYmlsYW5nYW4gZ2VuYXAga2VsaXBhdGFuIDQgZGFsYW0gdmVrdG9yDQogIGtlbGlwYXRhbl80IDwtIGMoKQ0KDQogICMgTG9vcCBkYXJpIDEgc2FtcGFpIG4NCiAgZm9yIChpIGluIDE6bikgew0KICAgIGlmIChpICUlIDQgPT0gMCkgew0KICAgICAga2VsaXBhdGFuXzQgPC0gYyhrZWxpcGF0YW5fNCwgaSkNCiAgICB9DQogIH0NCg0KICAjIFRhbXBpbGthbiBoYXNpbA0KICBjYXQoIktlbGlwYXRhbiA0IGRhcmkgMSBzYW1wYWkiLCBuLCAiYWRhbGFoOlxuIikNCiAgY2F0KHBhc3RlKGtlbGlwYXRhbl80LCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQ0KfQ0KDQoNCmBgYA0KKipQZW5qZWxhc2FuIEtvZGUqKjoNCg0KMS4gKipJbnB1dCBEYXJpIFBlbmdndW5hKioNCg0KLSAqcmVhZGxpbmUoKSogbWVtaW50YSBpbnB1dCBkYXJpIHBlbmdndW5hIGRhbGFtIGJlbnR1ayB0ZWtzLg0KDQotICphcy5pbnRlZ2VyKCkqIG1lbmd1YmFoIGlucHV0IG1lbmphZGkgYmlsYW5nYW4gYnVsYXQuDQoNCi0gTmlsYWkgaW5pIGRpc2ltcGFuIGRhbGFtIHZhcmlhYmVsICpuKi4NCg0KDQoyLiAqKlZhbGlkYXNpIElucHV0KioNCg0KLSAqaXMubmEobikqIG1lbmdlY2VrIGFwYWthaCBpbnB1dCBnYWdhbCBkaWtvbnZlcnNpIGtlIGludGVnZXIuDQoNCi0gKm4gPD0gMCogbWVtYXN0aWthbiBuaWxhaSBhZGFsYWggYmlsYW5nYW4gYnVsYXQgKipwb3NpdGlmKiouDQoNCi0gSmlrYSBrb25kaXNpIGluaSB0ZXJwZW51aGksIG1ha2EgcGVuZ2d1bmEgZGliZXJpIHBlcmluZ2F0YW4uDQoNCg0KMy4gKipQZXJ1bGFuZ2FuIGRhbiBQZW5nZWNla2FuIEtlbGlwYXRhbioqDQoNCi0gKmZvciAoaSBpbiAxOm4pKiBhcnRpbnlhIHByb2dyYW0gYWthbiBtZW1lcmlrc2Egc2V0aWFwIGJpbGFuZ2FuIGRhcmkgMSBzYW1wYWkgKm4qLg0KDQotICppICUlIDQgPT0gMCogbWVuZ2VjZWsgYXBha2FoIGkgYWRhbGFoIGtlbGlwYXRhbiA0IChzaXNhIGJhZ2kgPSAwKS4NCg0KLSAqYyhrZWxpcGF0YW5fNCwgaSkgKm1lbmFtYmFoa2FuIG5pbGFpICppKiBrZSB2ZWt0b3IgKiprZWxpcGF0YW5fNCoqLg0KDQoNCjQuICoqTWVuYW1waWxrYW4gSGFzaWwqKg0KDQotICpjYXQoKSogbWVuY2V0YWsgaGFzaWwga2UgbGF5YXIuDQoNCi0gKnBhc3RlKC4uLiwgY29sbGFwc2UgPSAiLCAiKSogZGlndW5ha2FuIHVudHVrIG1lbmdnYWJ1bmdrYW4gZWxlbWVuIHZla3RvciBrZWxpcGF0YW5fNCBtZW5qYWRpIHNhdHUgc3RyaW5nIGRlbmdhbiBwZW1pc2FoIGtvbWEuDQoNCg0KLS0tDQoNCiMgU3R1ZGkgS2FzdXMNCg0KLS0tDQoNCiMjIERhdGEgQ29sbGVjdGlvbg0KDQoqKlByb2dyYW0gaW5pIGRpYnVhdCB1bnR1ayoqOg0KDQoxLiBNZW1iYWNhIHNhdHUgc2hlZXQgdGVydGVudHUgZGFyaSBmaWxlIEV4Y2VsLg0KDQoyLiBNZW5hbXBpbGthbiBqdW1sYWggYmFyaXMgZGFuIGtvbG9tIGRhcmkgZGF0YSB0ZXJzZWJ1dA0KDQoNCmBgYHtyLCAgIG1lc3NhZ2U9VFJVRSwgd2FybmluZz1UUlVFLCBlY2hvPVRSVUV9DQojIEluc3RhbGwgZGFuIHBhbmdnaWwgcGFja2FnZSB5YW5nIGRpcGVybHVrYW4NCmxpYnJhcnkocmVhZHhsKQ0KDQojIEJhY2Egc2hlZXQgIkFwcGVuZDEiIGRhcmkgZmlsZSBFeGNlbA0KZGF0YSA8LSByZWFkX2V4Y2VsKCJkYXRhX3Blbmp1YWxhbl9rdWFydGFsMV9mdWxsLmFwcGVuZC54bHN4Iiwgc2hlZXQgPSAiQXBwZW5kMSIpDQoNCiMgVGFtcGlsa2FuIGp1bWxhaCBiYXJpcyBkYW4ga29sb20NCmNhdCgiSnVtbGFoIGJhcmlzOiIsIG5yb3coZGF0YSksICJcbiIpDQpjYXQoIkp1bWxhaCBrb2xvbToiLCBuY29sKGRhdGEpLCAiXG4iKQ0KYGBgDQoNCioqUGVuamVsYXNhbiBLb2RlKiogOg0KDQoxLiAqKk1lbWFuZ2dpbCBQYWNrYWdlKio6DQoNCi0gKnJlYWR4bCogYWRhbGFoIHBhY2thZ2UgZGkgUiB1bnR1ayBtZW1iYWNhIGZpbGUgRXhjZWwgKCoueGxzKiBkYW4gKi54bHN4KikuDQoNCi0gUGFja2FnZSBpbmkgaGFydXMgZGktaW5zdGFsbCB0ZXJsZWJpaCBkYWh1bHUgZGVuZ2FuICppbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKSogamlrYSBiZWx1bSB0ZXJzZWRpYS4NCg0KDQoyLiAqKk1lbWJhY2EgRGF0YSBkYXJpIFNoZWV0IFRlcnRlbnR1KioNCg0KLSAqcmVhZF9leGNlbCgpKiBkaWd1bmFrYW4gdW50dWsgbWVtYmFjYSBmaWxlIEV4Y2VsLg0KDQotIEFyZ3VtZW4gKnNoZWV0ID0gIkFwcGVuZDEiKiBtZW5hbmRha2FuIGJhaHdhIGhhbnlhIHNoZWV0IGJlcm5hbWEgIkFwcGVuZDEiIHlhbmcgYWthbiBkaWJhY2EuDQoNCi0gRGF0YSBoYXNpbCBwZW1iYWNhYW4gZGlzaW1wYW4gZGFsYW0gdmFyaWFiZWwgKipkYXRhKiouDQoNCg0KMy4gKipNZW5hbXBpbGthbiBEaW1lbnNpIERhdGEqKg0KDQotICpucm93KGRhdGEpKiBtZW5naGl0dW5nIGp1bWxhaCBiYXJpcyAob2JzZXJ2YXNpKS4NCg0KLSAqbmNvbChkYXRhKSogbWVuZ2hpdHVuZyBqdW1sYWgga29sb20gKHZhcmlhYmVsKS4NCg0KLSAqY2F0KCkqIGRpZ3VuYWthbiB1bnR1ayBtZW5jZXRhayBpbmZvcm1hc2kga2Uga29uc29sLg0KDQoNCiMjIERhdGEgQ2xlYW5pbmcNCg0KKipQcm9ncmFtIGluaSBkaWJ1YXQgdW50dWsqKjoNCg0KMS4gTWVtYmVyc2loa2FuIGRhbiBtZW1wZXJzaWFwa2FuIGRhdGEgcGVuanVhbGFuIGRhcmkgZmlsZSBFeGNlbCBhZ2FyIHNpYXAgZGlhbmFsaXNpcy4gUHJvc2VzIGluaSBtZWxpYmF0a2FuOg0KDQotIHN0YW5kYXJpc2FzaSB0YW5nZ2FsLA0KDQotIGtvbnZlcnNpIGtvbG9tIG51bWVyaWssDQoNCi0gcGVyaGl0dW5nYW4gdWxhbmcgbmlsYWkgdG90YWwsDQoNCi0gcGVuYW5nYW5hbiBkYXRhIHRpZGFrIHZhbGlkLA0KDQotIHBlbmdoYXB1c2FuIGRhdGEga29zb25nLg0KDQpgYGB7ciwgICBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9VFJVRSwgZWNobz1UUlVFfQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHdyaXRleGwpDQoNCiMgTG9hZCBkYXRhDQpkZiA8LSByZWFkX2V4Y2VsKCJkYXRhX3Blbmp1YWxhbl9rdWFydGFsMV9mdWxsLmFwcGVuZC54bHN4IikNCg0KIyBhLiBTdGFuZGFya2FuIGZvcm1hdCB0YW5nZ2FsDQpkZiA8LSBkZiAlPiUgbXV0YXRlKFRhbmdnYWwgPSBmb3JtYXQoZG15KFRhbmdnYWwpLCAiJVktJW0tJWQiKSkNCg0KIyBiLiBVYmFoIEhhcmdhIGRhbiBKdW1sYWgga2UgbnVtZXJpaw0KIyBIYXJnYTogaGFwdXMga2FyYWt0ZXIgbm9uLWRpZ2l0DQpkZiRIYXJnYSA8LSBhcy5udW1lcmljKGdzdWIoIlteMC05XSIsICIiLCBkZiRIYXJnYSkpDQoNCiMgSnVtbGFoOiB1YmFoIHRla3MgYW5na2Ega2UgbnVtZXJpaw0KdGV4dF90b19udW1iZXIgPC0gYygic2F0dSI9MSwgImR1YSI9MiwgInRpZ2EiPTMsICJlbXBhdCI9NCwgImxpbWEiPTUsDQogICAgICAgICAgICAgICAgICAgICJlbmFtIj02LCAidHVqdWgiPTcsICJkZWxhcGFuIj04LCAic2VtYmlsYW4iPTksICJzZXB1bHVoIj0xMCkNCg0KZGYkSnVtbGFoIDwtIGlmZWxzZShkZiRKdW1sYWggJWluJSBuYW1lcyh0ZXh0X3RvX251bWJlciksDQogICAgICAgICAgICAgICAgICAgIHRleHRfdG9fbnVtYmVyW2RmJEp1bWxhaF0sDQogICAgICAgICAgICAgICAgICAgIGRmJEp1bWxhaCkNCmRmJEp1bWxhaCA8LSBhcy5udW1lcmljKGRmJEp1bWxhaCkNCg0KIyBjLiBIaXR1bmcgdWxhbmcga29sb20gVG90YWwNCmRmIDwtIGRmICU+JSBtdXRhdGUoVG90YWwgPSBIYXJnYSAqIEp1bWxhaCkNCg0KIyBkLiBHYW50aSBuaWxhaSB0aWRhayB2YWxpZCBkZW5nYW4gTkENCmRmW2RmID09ICItIiB8IGRmID09ICJScCIgfCBkZiA9PSAiX2Fub255bW91c18iXSA8LSBOQQ0KDQojIGUuIEhhcHVzIGJhcmlzIGRlbmdhbiBQcm9kdWsga29zb25nIGF0YXUgIi0iDQpkZiA8LSBkZiAlPiUgZmlsdGVyKCFQcm9kdWsgJWluJSBjKCItIiwgTkEsICIiKSkNCg0KIyBTaW1wYW4gaGFzaWwNCndyaXRlX3hsc3goZGYsICJkYXRhX3Blbmp1YWxhbl9iZXJzaWgueGxzeCIpDQoNCiMgVGFtcGlsa2FuIGJlYmVyYXBhIGJhcmlzIHBlcnRhbWEgc2V0ZWxhaCBzZXRpYXAgbGFuZ2thaA0KDQpoZWFkKGRmKQ0KDQpgYGANCg0KKipQZW5qZWxhc2FuIEtvZGUqKjoNCg0KMS4gKipMaWJyYXJ5IHlhbmcgRGlndW5ha2FuKioNCg0KLSAqcmVhZHhsKiA6IE1lbWJhY2EgZmlsZSBFeGNlbC4NCg0KLSAqZHBseXIqIDogTWVtdWRhaGthbiBtYW5pcHVsYXNpIGRhdGEgKGZpbHRlciwgbXV0YXRlLCBkbGwpLg0KDQotICpzdHJpbmdyKiA6IFBlbXJvc2VzYW4gc3RyaW5nICh0aWRhayBkaXBha2FpIGVrc3BsaXNpdCBkaSBzaW5pLCB0YXBpIGJlcmd1bmEpLg0KDQotICpsdWJyaWRhdGUqIDogVW50dWsgcGFyc2luZyBkYW4gZm9ybWF0dGluZyB0YW5nZ2FsLg0KDQotICp3cml0ZXhsKiA6IFVudHVrIG1lbnlpbXBhbiBkYXRhIGtlIEV4Y2VsIGtlbWJhbGkuDQoNCg0KMi4gKipNZW1iYWNhIERhdGEqKg0KDQotIE1lbWJhY2Egc2VsdXJ1aCBpc2kgZmlsZSBFeGNlbCBrZSBkYWxhbSB2YXJpYWJlbCAqZGYqLg0KDQoNCjMuICoqU3RhbmRhcmlzYXNpIEZvcm1hdCBUYW5nZ2FsKioNCg0KLSBNZW5ndWJhaCBrb2xvbSAqKlRhbmdnYWwqKiBrZSBmb3JtYXQgKllZWVktTU0tRCpEIHNldGVsYWggbWVtYXN0aWthbiBiYWh3YSB1cnV0YW4gdGFuZ2dhbG55YSAqZGF5LW1vbnRoLXllYXIqLg0KDQoNCjQuICoqS29udmVyc2kgSGFyZ2EgZGFuIEp1bWxhaCoqDQoNCi0gTWVuZ2hhcHVzIHNlbXVhIGthcmFrdGVyIG5vbi1hbmdrYSAobWlzYWxueWEgc2ltYm9sICpScCosIHRpdGlrLCBrb21hKSBkYW4gdWJhaCBtZW5qYWRpIG51bWVyaWsuDQoNCi0gS29udmVyc2kgdGVrcyBhbmdrYSAoc2VwZXJ0aSAqKiJsaW1hIioqKSBtZW5qYWRpIGFuZ2thICgqKjUqKikuDQoNCi0gS2VtdWRpYW4gKmFzLm51bWVyaWMoKSogdW50dWsgbWVtYXN0aWthbiBzZW11YW55YSBiZXJ0aXBlIG51bWVyaWsuDQoNCg0KNS4gKipIaXR1bmcgS29sb20gVG90YWwqKg0KDQotIE1lbWJ1YXQgdWxhbmcga29sb20gKipUb3RhbCoqIGJlcmRhc2Fya2FuIGhhc2lsIHBlcmthbGlhbiAqKkhhcmdhKiogZGFuICoqSnVtbGFoKiouDQoNCg0KNi4gKipHYW50aSBOaWxhaSBUYWsgVmFsaWQgZGVuZ2FuKiogKk5BKg0KDQotIFNlbXVhIG5pbGFpIHRpZGFrIHZhbGlkIGRpZ2FudGkgbWVuamFkaSAqTkEqIGFnYXIgYmlzYSBkaWZpbHRlciBhdGF1IGRpYW5hbGlzaXMgbGViaWggYmFpay4NCg0KDQo3LiAqKkhhcHVzIEJhcmlzIGRlbmdhbiBQcm9kdWsgS29zb25nKioNCg0KLSBIYXB1cyBzZW11YSBiYXJpcyB5YW5nIG1lbWlsaWtpIG5pbGFpICpQcm9kdWsqIGtvc29uZywgdGFuZGEgKi0qLCBhdGF1ICpOQSouDQoNCg0KOC4gKipTaW1wYW4gRGF0YSBrZSBGaWxlIEJhcnUqKg0KDQotIFNpbXBhbiBoYXNpbCBha2hpciBrZSBmaWxlIEV4Y2VsIGJhcnUgdW50dWsgZG9rdW1lbnRhc2kgYXRhdSBhbmFsaXNpcyBsYW5qdXRhbi4NCg0KDQo5LiAqKlRhbXBpbGthbiBEYXRhKioNCg0KLSBNZW5hbXBpbGthbiBiZWJlcmFwYSBiYXJpcyBhd2FsIHVudHVrIG1lbGloYXQgaGFzaWwgYWtoaXIgcGVtYmVyc2loYW4gZGF0YS4NCg0KDQojIyBEYXRhIFRyYW5zZm9ybWF0aW9uDQoNCioqUHJvZ3JhbSBpbmkgZGlidWF0IHVudHVrKio6DQoNCjEuIE1lbGFrdWthbiBhbmFsaXNpcyByaW5na2FzYW4gdGVyaGFkYXAgZGF0YSBwZW5qdWFsYW4geWFuZyB0ZWxhaCBkaWJlcnNpaGthbiBzZWJlbHVtbnlhLCBtZW5jYWt1cDoNCg0KLSBQZW5qdWFsYW4gcGVyIGthdGVnb3JpIHByb2R1aw0KDQotIEp1bWxhaCB0cmFuc2Frc2kgcGVyIGtvdGENCg0KLSBUb3RhbCBwZW5qdWFsYW4gYnVsYW5hbg0KDQpgYGB7ciwgICBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9VFJVRSwgZWNobz1UUlVFfQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHdyaXRleGwpDQoNCiMgTG9hZCBkYXRhIChhc3Vtc2kgZGF0YSBzdWRhaCBkaWJlcnNpaGthbikNCmRmIDwtIHJlYWRfZXhjZWwoImRhdGFfcGVuanVhbGFuX2JlcnNpaC54bHN4IikNCg0KIyBhLiBUYW1iYWhrYW4ga29sb20gQnVsYW4NCmRmIDwtIGRmICU+JSBtdXRhdGUoQnVsYW4gPSBmb3JtYXQoeW1kKFRhbmdnYWwpLCAiJVktJW0iKSkNCg0KIyBiLiBIaXR1bmcgdG90YWwgcGVuanVhbGFuIHBlciBrYXRlZ29yaSBwcm9kdWsNCnRvdGFsX3Blcl9rYXRlZ29yaSA8LSBkZiAlPiUNCiAgZ3JvdXBfYnkoS2F0ZWdvcmkpICU+JQ0KICBzdW1tYXJpc2UoYFRvdGFsIFBlbmp1YWxhbmAgPSBzdW0oVG90YWwsIG5hLnJtID0gVFJVRSkpDQoNCiMgYy4gSGl0dW5nIGp1bWxhaCB0cmFuc2Frc2kgZGFyaSBzZXRpYXAga290YQ0KdHJhbnNha3NpX3Blcl9rb3RhIDwtIGRmICU+JQ0KICBncm91cF9ieShLb3RhKSAlPiUNCiAgc3VtbWFyaXNlKGBKdW1sYWggVHJhbnNha3NpYCA9IG4oKSkNCg0KIyBkLiBSaW5na2FzYW4ganVtbGFoIHRvdGFsIHBlbmp1YWxhbiBwZXIgYnVsYW4NCnRvdGFsX3Blcl9idWxhbiA8LSBkZiAlPiUNCiAgZ3JvdXBfYnkoQnVsYW4pICU+JQ0KICBzdW1tYXJpc2UoYFRvdGFsIFBlbmp1YWxhbiBCdWxhbmFuYCA9IHN1bShUb3RhbCwgbmEucm0gPSBUUlVFKSkNCg0KIyAoT3BzaW9uYWwpIFNpbXBhbiBzZW11YSBoYXNpbCBrZSBmaWxlIEV4Y2VsDQp3cml0ZV94bHN4KGxpc3QoDQogICJQZW5qdWFsYW4gcGVyIEthdGVnb3JpIiA9IHRvdGFsX3Blcl9rYXRlZ29yaSwNCiAgIlRyYW5zYWtzaSBwZXIgS290YSIgPSB0cmFuc2Frc2lfcGVyX2tvdGEsDQogICJQZW5qdWFsYW4gcGVyIEJ1bGFuIiA9IHRvdGFsX3Blcl9idWxhbg0KKSwgInJpbmdrYXNhbl9wZW5qdWFsYW4ueGxzeCIpDQoNCmNhdCgiVG90YWwgUGVuanVhbGFuIHBlciBLYXRlZ29yaTpcbiIpDQpwcmludCh0b3RhbF9wZXJfa2F0ZWdvcmkpDQoNCmNhdCgiXG5KdW1sYWggVHJhbnNha3NpIHBlciBLb3RhOlxuIikNCnByaW50KHRyYW5zYWtzaV9wZXJfa290YSkNCg0KY2F0KCJcblRvdGFsIFBlbmp1YWxhbiBwZXIgQnVsYW46XG4iKQ0KcHJpbnQodG90YWxfcGVyX2J1bGFuKQ0KDQpgYGANCg0KDQoqKlBlbmplbGFzYW4gS29kZSoqOg0KDQoxLiAqKlRhbWJhaCBLb2xvbSBCdWxhbioqDQoNCi0gRWtzdHJhayBiYWdpYW4gYnVsYW4gZGFyaSBrb2xvbSAqKlRhbmdnYWwqKiBkYW4gc2ltcGFuIGRhbGFtIGZvcm1hdCAqWVlZWS1NTSouIA0KDQoNCjIuICoqVG90YWwgUGVuanVhbGFuIHBlciBLYXRlZ29yaSoqDQoNCi0gTWVuanVtbGFoa2FuIG5pbGFpICpUb3RhbCogdW50dWsgdGlhcCBrYXRlZ29yaSwgbWVuZ2FiYWlrYW4gKk5BKi4NCg0KDQozLiAqKkp1bWxhaCBUcmFuc2Frc2kgcGVyIEtvdGEqKg0KDQotIE1lbmdoaXR1bmcganVtbGFoIGJhcmlzICh0cmFuc2Frc2kpIHVudHVrIHNldGlhcCBrb3RhLg0KDQoNCjQuICoqUmluZ2thc2FuIFBlbmp1YWxhbiBCdWxhbmFuKioNCg0KLSBNZW5qdW1sYWhrYW4gdG90YWwgcGVuanVhbGFuIHBlciBidWxhbi4NCg0KDQo1LiAqKlNpbXBhbiBrZSBFeGNlbCAob3BzaW9uYWwgdGFwaSBzYW5nYXQgYmVyZ3VuYSkqKg0KDQotIE1lbnlpbXBhbiB0aWdhIHRhYmVsIHJpbmdrYXNhbiBrZSBkYWxhbSBzYXR1IGZpbGUgRXhjZWwsIHRpYXAgdGFiZWwgZGkgc2hlZXQgdGVycGlzYWguDQoNCg0KNi4gKipDZXRhayBIYXNpbCBrZSBLb25zb2wqKg0KDQotIE1lbmFtcGlsa2FuIGhhc2lsIGtlIHRlcm1pbmFsIGF0YXUga29uc29sIFIuDQoNCg==