Abstract

Maternal health complications remain a critical challenge in rural, resource-limited settings like Bangladesh. This study investigates primary risk factors by analyzing 1,014 health records collected through an IoT-based monitoring system across rural healthcare facilities. Elevated systolic and diastolic blood pressures in the high-risk group, even among younger individuals, confirm blood pressure control as a fundamental risk indicator. Markedly elevated blood sugar levels in the high-risk group underscore the importance of comprehensive metabolic monitoring. Age revealed a complex risk profile, with high-risk individuals generally older, yet young women under 30 also significantly represented. This finding challenges simplistic age-based risk assumptions. Using advanced machine learning techniques, specifically random forest classification, researchers developed predictive models stratifying maternal health risks into low, medium, and high-risk categories. These models provide nuanced insights into the significance of various health indicators in predicting adverse outcomes. The research offers a scalable methodology for early risk identification and targeted interventions, illuminating potential strategies for addressing maternal health challenges in low-resource environments. The findings challenge traditional approaches, emphasizing the need for individualized, comprehensive risk assessment and proactive intervention strategies. Ultimately, this research contributes crucial knowledge to improve maternal health outcomes, offering a data-driven approach to understanding and mitigating risks in vulnerable populations.

Introduction

In rural Bangladesh, maternal health complications pose significant challenges, especially in resource-limited settings. This study examines key risk factors by analyzing 1,014 health records from Internet of Things (IoT)-based monitoring system across rural healthcare facilities.

Research question

What are the primary risk factors for maternal health complications in rural Bangladesh?

By analyzing the features in the dataset, researchers could identify the factors most strongly associated with adverse maternal outcomes.

Loading Data and packages

library(tidyverse)
library(openintro)
#URL of the dataset
url <- "https://raw.githubusercontent.com/lburenkov/maternalrisk/refs/heads/main/Maternal%20Health%20Risk%20Data%20Set.csv"

#Loading the dataset into a data frame
df <- read.csv(url)

#Displaying the first few rows of the dataset
head(df)
##   Age SystolicBP DiastolicBP    BS BodyTemp HeartRate RiskLevel
## 1  25        130          80 15.00       98        86 high risk
## 2  35        140          90 13.00       98        70 high risk
## 3  29         90          70  8.00      100        80 high risk
## 4  30        140          85  7.00       98        70 high risk
## 5  35        120          60  6.10       98        76  low risk
## 6  23        140          80  7.01       98        70 high risk

Data exploration and analysis

Checking for missing values

#Just checking for missing values
colSums(is.na(df)) #Should all be 0 since no missing values are mentioned
##         Age  SystolicBP DiastolicBP          BS    BodyTemp   HeartRate 
##           0           0           0           0           0           0 
##   RiskLevel 
##           0

Dataset Summary and Structure

#Checking structure and summary
str(df)        #Checking data types
## 'data.frame':    1014 obs. of  7 variables:
##  $ Age        : int  25 35 29 30 35 23 23 35 32 42 ...
##  $ SystolicBP : int  130 140 90 140 120 140 130 85 120 130 ...
##  $ DiastolicBP: int  80 90 70 85 60 80 70 60 90 80 ...
##  $ BS         : num  15 13 8 7 6.1 7.01 7.01 11 6.9 18 ...
##  $ BodyTemp   : num  98 98 100 98 98 98 98 102 98 98 ...
##  $ HeartRate  : int  86 70 80 70 76 70 78 86 70 70 ...
##  $ RiskLevel  : chr  "high risk" "high risk" "high risk" "high risk" ...
summary(df)    #Summary of features
##       Age          SystolicBP     DiastolicBP           BS        
##  Min.   :10.00   Min.   : 70.0   Min.   : 49.00   Min.   : 6.000  
##  1st Qu.:19.00   1st Qu.:100.0   1st Qu.: 65.00   1st Qu.: 6.900  
##  Median :26.00   Median :120.0   Median : 80.00   Median : 7.500  
##  Mean   :29.87   Mean   :113.2   Mean   : 76.46   Mean   : 8.726  
##  3rd Qu.:39.00   3rd Qu.:120.0   3rd Qu.: 90.00   3rd Qu.: 8.000  
##  Max.   :70.00   Max.   :160.0   Max.   :100.00   Max.   :19.000  
##     BodyTemp        HeartRate     RiskLevel        
##  Min.   : 98.00   Min.   : 7.0   Length:1014       
##  1st Qu.: 98.00   1st Qu.:70.0   Class :character  
##  Median : 98.00   Median :76.0   Mode  :character  
##  Mean   : 98.67   Mean   :74.3                     
##  3rd Qu.: 98.00   3rd Qu.:80.0                     
##  Max.   :103.00   Max.   :90.0

Some relevant observations from this preview of this dataset:

Age:

The ages range from 10 to 70 years. The distribution seems right-skewed with a mean higher than the median. A closer look at age groups could provide insights (e.g., teenagers, adults, older individuals). SystolicBP and DiastolicBP:

The ranges appear plausible, but values at the extremes (e.g., 70 for SystolicBP and 49 for DiastolicBP) might need further validation. Median SystolicBP (120) and DiastolicBP (80) align with common healthy ranges. BS (Blood Sugar):

A wide range from 6 to 19 mmol/L. Mean (8.726) and median (7.5) indicate possible high blood sugar cases skewing the data. BodyTemp:

Most values are clustered around 98°F, but there are outliers like 103°F. HeartRate:

Resting heart rate ranges from 7 (anomalous or error?) to 90 bpm. Median (76 bpm) is within a normal range for healthy adults. RiskLevel:

This categorical variable is the target. Analyzing its distribution is crucial to understand class balance.

Validate and clean data

#Checking for unexpected values (e.g., HeartRate < 40 or > 200)
filter(df, HeartRate < 40 | HeartRate > 200)
##   Age SystolicBP DiastolicBP  BS BodyTemp HeartRate RiskLevel
## 1  16        120          75 7.9       98         7  low risk
## 2  16        120          75 7.9       98         7  low risk
#Investigating unusually low or high Blood Sugar and BodyTemp
filter(df, BS > 15 | BodyTemp > 100 | BodyTemp < 97)
##     Age SystolicBP DiastolicBP   BS BodyTemp HeartRate RiskLevel
## 1    35         85          60 11.0      102        86 high risk
## 2    42        130          80 18.0       98        70 high risk
## 3    30        120          80  6.9      101        76  mid risk
## 4    40        140         100 18.0       98        90 high risk
## 5    12         95          60  6.1      102        60  low risk
## 6    17         85          60  9.0      102        86  mid risk
## 7    32        120          65  6.0      101        76  mid risk
## 8    26         85          60  6.0      101        86  mid risk
## 9    44        120          90 16.0       98        80  mid risk
## 10   13         90          65  7.8      101        80  mid risk
## 11   28        115          60  7.8      101        86  mid risk
## 12   34         85          60 11.0      102        86 high risk
## 13   42        140         100 18.0       98        90 high risk
## 14   50        140          95 17.0       98        60 high risk
## 15   38        135          60  7.9      101        86 high risk
## 16   30        120          80  7.9      101        76 high risk
## 17   55        140         100 18.0       98        90 high risk
## 18   40        160         100 19.0       98        77 high risk
## 19   32        140          90 18.0       98        88 high risk
## 20   55        140          95 19.0       98        77 high risk
## 21   40        160         100 19.0       98        77 high risk
## 22   32        140          90 18.0       98        88 high risk
## 23   22         90          60  7.5      102        60 high risk
## 24   55        140          95 19.0       98        77 high risk
## 25   50        130         100 16.0       98        75 high risk
## 26   18        120          80  6.9      102        76  mid risk
## 27   17         90          60  6.9      101        76  mid risk
## 28   17         90          63  6.9      101        70  mid risk
## 29   25        120          90  6.7      101        80  mid risk
## 30   17        120          80  6.7      102        76  mid risk
## 31   14         90          65  7.0      101        70 high risk
## 32   17        110          75 12.0      101        76 high risk
## 33   40        160         100 19.0       98        77 high risk
## 34   32        140          90 18.0       98        88 high risk
## 35   12         90          60  7.9      102        66 high risk
## 36   12         95          60  6.1      102        60  low risk
## 37   55        140          95 19.0       98        77 high risk
## 38   50        130         100 16.0       98        75 high risk
## 39   13         90          65  7.9      101        80  mid risk
## 40   17         90          65  6.1      103        67 high risk
## 41   28         83          60  8.0      101        86 high risk
## 42   17         85          60  9.0      102        86 high risk
## 43   50        140          95 17.0       98        60 high risk
## 44   28         85          60  9.0      101        86  mid risk
## 45   17         85          60  9.0      102        86  mid risk
## 46   55        140          80  7.2      101        76 high risk
## 47   40        140         100 18.0       98        77 high risk
## 48   28        120          80  9.0      102        76 high risk
## 49   17         90          60 11.0      101        78 high risk
## 50   17         90          63  8.0      101        70 high risk
## 51   25        120          90 12.0      101        80 high risk
## 52   17        120          80  7.0      102        76 high risk
## 53   19         90          65 11.0      101        70 high risk
## 54   32        120          65  6.0      101        76  mid risk
## 55   17        110          75 13.0      101        76 high risk
## 56   40        160         100 19.0       98        77 high risk
## 57   32        140          90 18.0       98        88 high risk
## 58   12         90          60  8.0      102        66 high risk
## 59   12         90          60 11.0      102        60 high risk
## 60   55        140          95 19.0       98        77 high risk
## 61   50        130         100 16.0       98        76 high risk
## 62   13         90          65  9.0      101        80 high risk
## 63   17         90          65  7.7      103        67 high risk
## 64   26         85          60  6.0      101        86  mid risk
## 65   17         85          60  6.3      102        86 high risk
## 66   55        120          90 18.0       98        60 high risk
## 67   35         85          60 19.0       98        86 high risk
## 68   43        120          90 18.0       98        70 high risk
## 69   44        120          90 16.0       98        80  mid risk
## 70   45        120          80  6.9      103        70  low risk
## 71   70         85          60  6.9      102        70  low risk
## 72   65        120          90  6.9      103        76  low risk
## 73   55        120          80  6.9      102        80  low risk
## 74   45         90          60 18.0      101        70 high risk
## 75   22        120          80  6.9      103        76  low risk
## 76   17        110          75  6.9      101        76 high risk
## 77   40        160         100 19.0       98        77 high risk
## 78   32        140          90 18.0       98        88 high risk
## 79   12         90          60  7.8      102        60 high risk
## 80   55        140          95 19.0       98        77 high risk
## 81   50        130         100 16.0       98        75 high risk
## 82   13         90          65  7.8      101        80  mid risk
## 83   17         90          65  7.8      103        67 high risk
## 84   28        115          60  7.8      101        86  mid risk
## 85   17         85          69  7.8      102        86 high risk
## 86   50        130          80 16.0      102        76  mid risk
## 87   27        120          90  6.8      102        68  mid risk
## 88   55        100          70  6.8      101        80  mid risk
## 89   60        140          80 16.0       98        66 high risk
## 90   17        140         100  6.8      103        80 high risk
## 91   36        140         100  6.8      102        76 high risk
## 92   40        140         100 13.0      101        66 high risk
## 93   36        140         100  6.8      102        76 high risk
## 94   40        140         100 13.0      101        66 high risk
## 95   35         85          60 11.0      102        86 high risk
## 96   43        130          80 18.0       98        70  mid risk
## 97   34         85          60 11.0      102        86 high risk
## 98   42        130          80 18.0       98        70  mid risk
## 99   30        120          80  6.8      101        76  low risk
## 100  42        140         100 18.0       98        90 high risk
## 101  18        120          80  6.8      102        76  low risk
## 102  17         90          60  7.9      101        76  low risk
## 103  50        140          95 17.0       98        60 high risk
## 104  38        135          60  7.9      101        86 high risk
## 105  17         85          60  7.9      102        86  low risk
## 106  30        120          80  7.9      101        76 high risk
## 107  55        140         100 18.0       98        90 high risk
## 108  18        120          80  7.9      102        76  mid risk
## 109  17         90          60  7.5      101        76  low risk
## 110  17         90          63  7.5      101        70  low risk
## 111  25        120          90  7.5      101        80  low risk
## 112  17        120          80  7.5      102        76  low risk
## 113  19         90          65  7.5      101        70  low risk
## 114  18         85          60  7.5      101        86  mid risk
## 115  17         85          60  7.5      102        86  low risk
## 116  30        120          80  7.5      101        76  mid risk
## 117  40        160         100 19.0       98        77 high risk
## 118  32        140          90 18.0       98        88 high risk
## 119  12         90          60  7.5      102        66  low risk
## 120  12         90          60  7.5      102        60  low risk
## 121  55        140          95 19.0       98        77 high risk
## 122  50        130         100 16.0       98        75  mid risk
## 123  13         90          65  7.5      101        80  low risk
## 124  17         90          65  7.5      103        67  low risk
## 125  28        115          60  7.5      101        86  mid risk
## 126  17         85          60  7.5      102        86  low risk
## 127  40        160         100 19.0       98        77 high risk
## 128  32        140          90 18.0       98        88 high risk
## 129  12         90          60  7.5      102        66  mid risk
## 130  22         90          60  7.5      102        60 high risk
## 131  55        140          95 19.0       98        77 high risk
## 132  50        130         100 16.0       98        75 high risk
## 133  55        140          95 19.0       98        77 high risk
## 134  50        130         100 16.0       98        75  mid risk
## 135  13         90          65  7.5      101        80 high risk
## 136  17         90          65  7.5      103        67  mid risk
## 137  27        135          60  7.5      101        86 high risk
## 138  17         85          60  7.5      101        86 high risk
## 139  50        140          95 17.0       98        60 high risk
## 140  28         85          60  9.0      101        86  mid risk
## 141  28         95          60 10.0      101        86 high risk
## 142  17         90          60  9.0      102        86  mid risk
## 143  30        120          80  9.0      101        76  mid risk
## 144  35         85          60 11.0      102        86 high risk
## 145  42        130          80 18.0       98        70 high risk
## 146  40        140         100 18.0       98        90 high risk
## 147  14         90          65  7.0      101        70 high risk
## 148  17        110          75 12.0      101        76 high risk
## 149  40        160         100 19.0       98        77 high risk
## 150  30        120          80  6.9      101        76  mid risk
## 151  18        120          80  6.9      102        76  mid risk
## 152  17         90          60  6.9      101        76  mid risk
## 153  17         90          63  6.9      101        70  mid risk
## 154  25        120          90  6.7      101        80  mid risk
## 155  17        120          80  6.7      102        76  mid risk
## 156  13         90          65  7.9      101        80  mid risk
## 157  28         85          60  9.0      101        86  mid risk
## 158  17         85          60  9.0      102        86  mid risk
## 159  30        120          80  6.9      101        76  mid risk
## 160  18        120          80  6.9      102        76  mid risk
## 161  17         90          60  6.9      101        76  mid risk
## 162  17         90          63  6.9      101        70  mid risk
## 163  25        120          90  6.7      101        80  mid risk
## 164  17        120          80  6.7      102        76  mid risk
## 165  13         90          65  7.9      101        80  mid risk
## 166  28         85          60  9.0      101        86  mid risk
## 167  17         85          60  9.0      102        86  mid risk
## 168  32        120          65  6.0      101        76  mid risk
## 169  26         85          60  6.0      101        86  mid risk
## 170  44        120          90 16.0       98        80  mid risk
## 171  13         90          65  7.8      101        80  mid risk
## 172  28        115          60  7.8      101        86  mid risk
## 173  50        130          80 16.0      102        76  mid risk
## 174  27        120          90  6.8      102        68  mid risk
## 175  55        100          70  6.8      101        80  mid risk
## 176  43        130          80 18.0       98        70  mid risk
## 177  42        130          80 18.0       98        70  mid risk
## 178  18        120          80  7.9      102        76  mid risk
## 179  18         85          60  7.5      101        86  mid risk
## 180  30        120          80  7.5      101        76  mid risk
## 181  50        130         100 16.0       98        75  mid risk
## 182  28        115          60  7.5      101        86  mid risk
## 183  12         90          60  7.5      102        66  mid risk
## 184  50        130         100 16.0       98        75  mid risk
## 185  17         90          65  7.5      103        67  mid risk
## 186  28         85          60  9.0      101        86  mid risk
## 187  17         90          60  9.0      102        86  mid risk
## 188  30        120          80  9.0      101        76  mid risk
## 189  30        120          80  6.9      101        76  mid risk
## 190  18        120          80  6.9      102        76  mid risk
## 191  17         90          60  6.9      101        76  mid risk
## 192  17         90          63  6.9      101        70  mid risk
## 193  25        120          90  6.7      101        80  mid risk
## 194  17        120          80  6.7      102        76  mid risk
## 195  13         90          65  7.9      101        80  mid risk
## 196  28         85          60  9.0      101        86  mid risk
## 197  17         85          60  9.0      102        86  mid risk
## 198  32        120          65  6.0      101        76  mid risk
## 199  30        120          80  6.8      101        76  low risk
## 200  18        120          80  6.8      102        76  low risk
## 201  17         90          60  7.9      101        76  low risk
## 202  17         85          60  7.9      102        86  low risk
## 203  17         90          60  7.5      101        76  low risk
## 204  17         90          63  7.5      101        70  low risk
## 205  25        120          90  7.5      101        80  low risk
## 206  17        120          80  7.5      102        76  low risk
## 207  19         90          65  7.5      101        70  low risk
## 208  17         85          60  7.5      102        86  low risk
## 209  12         90          60  7.5      102        66  low risk
## 210  12         90          60  7.5      102        60  low risk
## 211  13         90          65  7.5      101        80  low risk
## 212  17         90          65  7.5      103        67  low risk
## 213  17         85          60  7.5      102        86  low risk
## 214  40        140         100 18.0       98        90 high risk
## 215  14         90          65  7.0      101        70 high risk
## 216  17        110          75 12.0      101        76 high risk
## 217  40        160         100 19.0       98        77 high risk
## 218  32        140          90 18.0       98        88 high risk
## 219  12         90          60  7.9      102        66 high risk
## 220  55        140          95 19.0       98        77 high risk
## 221  50        130         100 16.0       98        75 high risk
## 222  17         90          65  6.1      103        67 high risk
## 223  28         83          60  8.0      101        86 high risk
## 224  17         85          60  9.0      102        86 high risk
## 225  50        140          95 17.0       98        60 high risk
## 226  55        140          80  7.2      101        76 high risk
## 227  40        140         100 18.0       98        77 high risk
## 228  28        120          80  9.0      102        76 high risk
## 229  17         90          60 11.0      101        78 high risk
## 230  17         90          63  8.0      101        70 high risk
## 231  25        120          90 12.0      101        80 high risk
## 232  17        120          80  7.0      102        76 high risk
## 233  19         90          65 11.0      101        70 high risk
## 234  17        110          75 13.0      101        76 high risk
## 235  40        160         100 19.0       98        77 high risk
## 236  32        140          90 18.0       98        88 high risk
## 237  12         90          60  8.0      102        66 high risk
## 238  12         90          60 11.0      102        60 high risk
## 239  55        140          95 19.0       98        77 high risk
## 240  50        130         100 16.0       98        76 high risk
## 241  13         90          65  9.0      101        80 high risk
## 242  17         90          65  7.7      103        67 high risk
## 243  17         85          60  6.3      102        86 high risk
## 244  55        120          90 18.0       98        60 high risk
## 245  35         85          60 19.0       98        86 high risk
## 246  43        120          90 18.0       98        70 high risk
## 247  32        120          65  6.0      101        76  mid risk
#Handling outliers by capping values or removing them
df <- df %>%
  filter(HeartRate >= 40 & HeartRate <= 200) %>%
  filter(BS <= 15 & BodyTemp <= 100 & BodyTemp >= 97)

This imbalance could pose a challenge for classification tasks as models tend to favor the majority class (Low Risk) over the minority classes (High Risk and Mid Risk). To address this, several techniques can be employed, such as resampling, class weighting, or using specialized algorithms.

#Class distribution
table(df$RiskLevel)
## 
## high risk  low risk  mid risk 
##       148       367       250
#Bar plot to visualize
ggplot(df, aes(x = RiskLevel, fill = RiskLevel)) +
  geom_bar() +
  labs(title = "Risk Level Distribution", x = "Risk Level", y = "Count") +
  theme_minimal()

Oversampling

Decision for this Dataset The dataset has 1014 instances, which is relatively small. Removing too many samples (via undersampling) could harm the model’s generalizability. Therefore: I am using oversamppling to retain as much data as possible.

Converting to a factor

df$RiskLevel <- as.factor(df$RiskLevel)
str(df)
## 'data.frame':    765 obs. of  7 variables:
##  $ Age        : int  25 35 29 30 35 23 23 32 23 19 ...
##  $ SystolicBP : int  130 140 90 140 120 140 130 120 90 120 ...
##  $ DiastolicBP: int  80 90 70 85 60 80 70 90 60 80 ...
##  $ BS         : num  15 13 8 7 6.1 7.01 7.01 6.9 7.01 7 ...
##  $ BodyTemp   : num  98 98 100 98 98 98 98 98 98 98 ...
##  $ HeartRate  : int  86 70 80 70 76 70 78 70 76 70 ...
##  $ RiskLevel  : Factor w/ 3 levels "high risk","low risk",..: 1 1 1 1 2 1 3 3 2 3 ...

Numeric variables

#Loading necessary library for visualization
library(ggplot2)

#Converting RiskLevel to a factor
df$RiskLevel <- as.factor(df$RiskLevel)

#Splitting the dataset into individual classes
high_risk <- df[df$RiskLevel == "high risk", ]
low_risk <- df[df$RiskLevel == "low risk", ]
mid_risk <- df[df$RiskLevel == "mid risk", ]

#Setting the target size for all classes (equal to the largest class size)
target_size <- max(nrow(low_risk), nrow(high_risk), nrow(mid_risk))

#Function to generate synthetic samples for balancing
generate_synthetic <- function(minority_class, target_size) {
  n_samples <- target_size - nrow(minority_class)
  if (n_samples <= 0) return(minority_class) #If already balanced, return original
  
  #Randomly sample from the minority class with replacement
  synthetic_samples <- minority_class[sample(1:nrow(minority_class), n_samples, replace = TRUE), ]
  
  #Adding small random noise to numeric columns for synthetic samples
  for (col in names(synthetic_samples)) {
    if (is.numeric(synthetic_samples[[col]])) {
      synthetic_samples[[col]] <- synthetic_samples[[col]] + runif(n_samples, -0.1, 0.1)
    }
  }
  
  #Combining original and synthetic samples
  return(rbind(minority_class, synthetic_samples))
}

#Generating synthetic samples for minority classes
high_risk_balanced <- generate_synthetic(high_risk, target_size)
mid_risk_balanced <- generate_synthetic(mid_risk, target_size)
low_risk_balanced <- low_risk # Low risk is already at the target size

#Combining all classes into a single balanced dataset
df_balanced <- rbind(high_risk_balanced, mid_risk_balanced, low_risk_balanced)

#Checking the new class distribution
print(table(df_balanced$RiskLevel))
## 
## high risk  low risk  mid risk 
##       367       367       367
#Visualizing the balanced dataset
ggplot(df_balanced, aes(x = RiskLevel, fill = RiskLevel)) +
  geom_bar() +
  labs(title = "Balanced Risk Level Distribution", x = "Risk Level", y = "Count") +
  theme_minimal()

#Summary statistics for numeric variables
df %>%
  select(Age, SystolicBP, DiastolicBP, BS, BodyTemp, HeartRate) %>%
  summary()
##       Age          SystolicBP     DiastolicBP           BS        
##  Min.   :10.00   Min.   : 70.0   Min.   : 49.00   Min.   : 6.000  
##  1st Qu.:20.00   1st Qu.:100.0   1st Qu.: 65.00   1st Qu.: 6.900  
##  Median :25.00   Median :120.0   Median : 80.00   Median : 7.200  
##  Mean   :30.13   Mean   :113.6   Mean   : 76.65   Mean   : 8.041  
##  3rd Qu.:36.00   3rd Qu.:120.0   3rd Qu.: 90.00   3rd Qu.: 7.800  
##  Max.   :66.00   Max.   :140.0   Max.   :100.00   Max.   :15.000  
##     BodyTemp        HeartRate    
##  Min.   : 98.00   Min.   :60.00  
##  1st Qu.: 98.00   1st Qu.:70.00  
##  Median : 98.00   Median :70.00  
##  Mean   : 98.07   Mean   :73.68  
##  3rd Qu.: 98.00   3rd Qu.:78.00  
##  Max.   :100.00   Max.   :90.00
#Visualizing numeric feature distributions
df %>%
  select(Age, SystolicBP, DiastolicBP, BS, BodyTemp, HeartRate) %>%
  gather(key = "Feature", value = "Value") %>%
  ggplot(aes(x = Value)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black") +
  facet_wrap(~ Feature, scales = "free") +
  theme_minimal() +
  labs(title = "Distribution of Numeric Features")

#Creating a data frame summarizing variables and comments
summary_table <- data.frame(
  Variable = c("Age", "SystolicBP", "DiastolicBP", "BS", "BodyTemp", "HeartRate"),
  Likely_Distribution = c(
    "Slight positive skew",
    "Slight negative skew",
    "Slight negative skew",
    "Positive skew",
    "Symmetrical",
    "Slight positive skew"
  ),
  Comments = c(
    "Younger ages dominate with a few older outliers.",
    "Concentration near median with lower outliers.",
    "Similar to SystolicBP with fewer low outliers.",
    "Higher outliers increase the mean.",
    "Likely normal distribution centered at 98.",
    "Most values around 70–78, with some high outliers."
  )
)

#Printting the table
print(summary_table)
##      Variable  Likely_Distribution
## 1         Age Slight positive skew
## 2  SystolicBP Slight negative skew
## 3 DiastolicBP Slight negative skew
## 4          BS        Positive skew
## 5    BodyTemp          Symmetrical
## 6   HeartRate Slight positive skew
##                                             Comments
## 1   Younger ages dominate with a few older outliers.
## 2     Concentration near median with lower outliers.
## 3     Similar to SystolicBP with fewer low outliers.
## 4                 Higher outliers increase the mean.
## 5         Likely normal distribution centered at 98.
## 6 Most values around 70–78, with some high outliers.
#Optionally display it in a more readable table format if using R Markdown or Viewer
library(knitr)
kable(summary_table, caption = "Variable Distributions and Comments")
Variable Distributions and Comments
Variable Likely_Distribution Comments
Age Slight positive skew Younger ages dominate with a few older outliers.
SystolicBP Slight negative skew Concentration near median with lower outliers.
DiastolicBP Slight negative skew Similar to SystolicBP with fewer low outliers.
BS Positive skew Higher outliers increase the mean.
BodyTemp Symmetrical Likely normal distribution centered at 98.
HeartRate Slight positive skew Most values around 70–78, with some high outliers.

Observations:

  1. Age Observation: The distribution is positively skewed (right-skewed), with most counts concentrated between 20 and 36. A small number of individuals are older (above 50), creating a long right tail. Insight: The population in this dataset is primarily young to middle-aged, with fewer older individuals.
  2. Body Temperature Observation: The distribution is highly symmetrical, with most values clustered exactly around 98°F. There are very few observations outside this value (98.5 to 100). Insight: This suggests that body temperature in the dataset remains consistent with the normal range for healthy individuals.
  3. Blood Sugar (BS) Observation: The distribution is positively skewed, with most values concentrated between 6.9 and 7.8. However, there are clear outliers at higher values (10 to 15), creating a long right tail. Insight: The majority of individuals have normal blood sugar levels, but some exhibit significantly elevated levels, which may warrant closer attention (e.g., potential diabetes cases).
  4. Diastolic Blood Pressure (DiastolicBP) Observation: The distribution is approximately symmetric, with the highest counts around 80 mmHg. There are slightly fewer observations at the extremes (50 and 100). Insight: Diastolic blood pressure values are largely concentrated around the normal range, with some variation at lower and higher values.
  5. Heart Rate Observation: The distribution is slightly positively skewed, with a peak around 70 bpm. There are some counts in the higher range (80–90 bpm) but very few lower than 60 bpm. Insight: Most individuals have a normal resting heart rate, with fewer outliers in the higher range.
  6. Systolic Blood Pressure (SystolicBP) Observation: The distribution shows a slight negative skew, with a strong peak at 120 mmHg. There are a few lower outliers (around 80) but fewer values exceeding 140. Insight: Systolic blood pressure is concentrated near the higher end of the normal range, with some low outliers potentially indicating hypotension. General Observations Most features exhibit expected physiological ranges (e.g., body temperature, heart rate). Skewness is evident in Age, BS, and Heart Rate, suggesting outliers or unbalanced distributions. Body Temperature and DiastolicBP are close to symmetrical, implying normal distributions.

Relationships Between Features

#Correlations
cor_matrix <- cor(df %>% select(Age, SystolicBP, DiastolicBP, BS, BodyTemp, HeartRate))

#Visualizing correlation matrix
library(corrplot)
## corrplot 0.95 loaded
corrplot(cor_matrix, method = "circle", type = "lower", tl.cex = 0.8, addCoef.col = "black")

Strongest Correlation: SystolicBP and DiastolicBP (0.74), which is expected since they are both measures of blood pressure. Moderate Correlations: Age and BS (0.45), and BS with both blood pressure measures (~0.33–0.34). Weak or No Correlation: Body temperature and heart rate show very little to no correlation with other variables.

#Boxplot for Age and RiskLevel
ggplot(df_balanced, aes(x = RiskLevel, y = Age, fill = RiskLevel)) +
  geom_boxplot() +
  labs(title = "Age Distribution by Risk Level", x = "Risk Level", y = "Age") +
  theme_minimal()

#Repeating for other features
features <- c("SystolicBP", "DiastolicBP", "BS", "BodyTemp", "HeartRate")
for (feature in features) {
  print(
    ggplot(df_balanced, aes_string(x = "RiskLevel", y = feature, fill = "RiskLevel")) +
      geom_boxplot() +
      labs(title = paste(feature, "Distribution by Risk Level"),
           x = "Risk Level", y = feature) +
      theme_minimal()
  )
}
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

#Scatter plot for SystolicBP vs. DiastolicBP by RiskLevel
ggplot(df_balanced, aes(x = SystolicBP, y = DiastolicBP, color = RiskLevel)) +
  geom_point(alpha = 0.7) +
  labs(title = "Systolic vs. Diastolic BP by Risk Level", x = "Systolic BP", y = "Diastolic BP") +
  theme_minimal()

Correlation between the continuous variables and the probability of being in the “high risk”

#Creating a binary variable for high risk
df_balanced$HighRisk <- ifelse(df_balanced$RiskLevel == "high risk", 1, 0)
#Selecting numeric features
numeric_features <- c("Age", "SystolicBP", "DiastolicBP", "BS", "BodyTemp", "HeartRate")

#Computing Pearson correlations
pearson_corr <- sapply(numeric_features, function(feature) {
  cor(df_balanced[[feature]], df_balanced$HighRisk, method = "pearson")
})

#Displaying Pearson correlations
pearson_corr
##          Age   SystolicBP  DiastolicBP           BS     BodyTemp    HeartRate 
##  0.380103117  0.484005994  0.482593784  0.662769454 -0.008962854  0.236358506
#Converting Pearson correlations to a data frame
correlation_df <- data.frame(
  Feature = names(pearson_corr),
  Correlation = pearson_corr
)

#Bar plot for correlations
library(ggplot2)
ggplot(correlation_df, aes(x = reorder(Feature, Correlation), y = Correlation, fill = Correlation)) +
  geom_bar(stat = "identity") +
  labs(title = "Correlation of Features with High Risk",
       x = "Feature",
       y = "Pearson Correlation") +
  theme_minimal() +
  coord_flip() + #Flip coordinates for horizontal bars
  scale_fill_gradient(low = "blue", high = "red")

For me, it can be surprising when age, which is often thought to be a key factor in health risks, does not turn out to be the strongest predictor. Instead, direct health indicators like blood sugar (BS), systolic blood pressure, and diastolic blood pressure emerge as more critical factors in predicting “high risk” outcomes.

library(ggplot2)

#List of variables to plot
variables <- c("BS", "SystolicBP", "DiastolicBP")

#Loop through each variable and create a trend plot
for (var in variables) {
  print(
    ggplot(df_balanced, aes_string(x = "Age", y = var, color = "HighRisk")) +
      geom_point(alpha = 0.6) +
      geom_smooth(method = "loess", se = FALSE) +
      labs(title = paste(var, "Trend Over Age"),
           x = "Age", y = var) +
      theme_minimal()
  )
}
## `geom_smooth()` using formula = 'y ~ x'
## Warning: The following aesthetics were dropped during statistical transformation:
## colour.
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?

## `geom_smooth()` using formula = 'y ~ x'
## Warning: The following aesthetics were dropped during statistical transformation:
## colour.
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?

## `geom_smooth()` using formula = 'y ~ x'
## Warning: The following aesthetics were dropped during statistical transformation:
## colour.
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?

#Filtering for younger individuals (e.g., Age < 40) who are High Risk
young_high_risk <- df_balanced %>%
  filter(Age < 40 & HighRisk == 1)

#Comparing younger high-risk individuals to others
young_non_high_risk <- df_balanced %>%
  filter(Age < 40 & HighRisk == 0)

#Summary statistics for young high-risk
summary(young_high_risk)
##       Age          SystolicBP     DiastolicBP           BS        
##  Min.   :21.93   Min.   : 89.9   Min.   : 59.98   Min.   : 6.710  
##  1st Qu.:25.00   1st Qu.:120.0   1st Qu.: 79.98   1st Qu.: 7.087  
##  Median :30.00   Median :140.0   Median : 90.00   Median : 8.000  
##  Mean   :30.24   Mean   :128.8   Mean   : 88.03   Mean   : 9.715  
##  3rd Qu.:35.00   3rd Qu.:140.0   3rd Qu.:100.00   3rd Qu.:11.956  
##  Max.   :39.99   Max.   :140.1   Max.   :100.09   Max.   :15.081  
##     BodyTemp        HeartRate         RiskLevel      HighRisk
##  Min.   : 97.90   Min.   :60.00   high risk:203   Min.   :1  
##  1st Qu.: 97.98   1st Qu.:70.00   low risk :  0   1st Qu.:1  
##  Median : 98.00   Median :78.03   mid risk :  0   Median :1  
##  Mean   : 98.09   Mean   :76.03                   Mean   :1  
##  3rd Qu.: 98.03   3rd Qu.:80.00                   3rd Qu.:1  
##  Max.   :100.00   Max.   :90.01                   Max.   :1
#Summary statistics for young non-high-risk
summary(young_non_high_risk)
##       Age          SystolicBP     DiastolicBP           BS        
##  Min.   :10.00   Min.   : 69.9   Min.   : 49.00   Min.   : 5.910  
##  1st Qu.:19.00   1st Qu.:100.0   1st Qu.: 60.00   1st Qu.: 6.800  
##  Median :22.00   Median :120.0   Median : 70.04   Median : 7.000  
##  Mean   :22.99   Mean   :109.8   Mean   : 72.73   Mean   : 7.104  
##  3rd Qu.:29.00   3rd Qu.:120.0   3rd Qu.: 80.00   3rd Qu.: 7.500  
##  Max.   :39.95   Max.   :130.1   Max.   :100.00   Max.   :10.994  
##     BodyTemp        HeartRate         RiskLevel      HighRisk
##  Min.   : 97.90   Min.   :59.90   high risk:  0   Min.   :0  
##  1st Qu.: 98.00   1st Qu.:70.00   low risk :299   1st Qu.:0  
##  Median : 98.00   Median :70.00   mid risk :306   Median :0  
##  Mean   : 98.08   Mean   :72.93                   Mean   :0  
##  3rd Qu.: 98.00   3rd Qu.:77.96                   3rd Qu.:0  
##  Max.   :100.09   Max.   :88.00                   Max.   :0

Insights: Age: High-risk individuals are generally older than non-high-risk individuals in this age range. However, young individuals (under 30) are still present in the high-risk group, emphasizing the need to explore other factors.

Systolic and Diastolic Blood presure: Blood pressure (both systolic and diastolic) is significantly higher in the high-risk group, even among younger individuals. This confirms that poor blood pressure control is a major risk factor.

Blood Sugar (BS) Blood sugar levels are markedly elevated in the high-risk group. The higher mean suggests some individuals in this group have extreme values.

Model

library(nnet)

#Fitting multinomial logistic regression
multinom_model <- multinom(RiskLevel ~ Age + SystolicBP + DiastolicBP + BS + BodyTemp + HeartRate, data = df_balanced)
## # weights:  24 (14 variable)
## initial  value 1209.572130 
## iter  10 value 871.126916
## iter  20 value 778.844664
## iter  30 value 771.175254
## iter  40 value 771.167253
## iter  50 value 764.888262
## iter  60 value 757.170932
## iter  70 value 757.166158
## iter  70 value 757.166155
## final  value 757.166055 
## converged
#Summary of the model
summary(multinom_model)
## Call:
## multinom(formula = RiskLevel ~ Age + SystolicBP + DiastolicBP + 
##     BS + BodyTemp + HeartRate, data = df_balanced)
## 
## Coefficients:
##          (Intercept)        Age  SystolicBP DiastolicBP         BS  BodyTemp
## low risk    226.0809 0.01944114 -0.12805405 -0.04257232 -0.9177363 -1.965944
## mid risk    127.2109 0.01825165 -0.07244766 -0.07573707 -0.6787368 -1.020532
##            HeartRate
## low risk -0.09894234
## mid risk -0.08889116
## 
## Std. Errors:
##           (Intercept)        Age SystolicBP DiastolicBP         BS   BodyTemp
## low risk 0.0002141865 0.01187866 0.01265363  0.01317042 0.09080277 0.02194777
## mid risk 0.0001805383 0.01143589 0.01191964  0.01217270 0.06406734 0.02020524
##           HeartRate
## low risk 0.01679366
## mid risk 0.01516674
## 
## Residual Deviance: 1514.332 
## AIC: 1542.332
#Predicting on the training set
predicted <- predict(multinom_model, df_balanced)

#Confusion matrix
table(Predicted = predicted, Actual = df_balanced$RiskLevel)
##            Actual
## Predicted   high risk low risk mid risk
##   high risk       297       13       37
##   low risk         15      229      114
##   mid risk         55      125      216
library(caret)
## Loading required package: lattice
## 
## Attaching package: 'lattice'
## The following objects are masked from 'package:openintro':
## 
##     ethanol, lsegments
## 
## Attaching package: 'caret'
## The following object is masked from 'package:openintro':
## 
##     dotPlot
## The following object is masked from 'package:purrr':
## 
##     lift
#Converting RiskLevel to a factor if not already
df_balanced$RiskLevel <- as.factor(df_balanced$RiskLevel)

#Splitting the dataset into training and testing sets
set.seed(42)
train_index <- createDataPartition(df_balanced$RiskLevel, p = 0.7, list = FALSE)
train_data <- df_balanced[train_index, ]
test_data <- df_balanced[-train_index, ]
library(randomForest)
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
## 
## Attaching package: 'randomForest'
## The following object is masked from 'package:dplyr':
## 
##     combine
## The following object is masked from 'package:ggplot2':
## 
##     margin
#Fitting the Random Forest model
rf_model <- randomForest(RiskLevel ~ Age + SystolicBP + DiastolicBP + BS + BodyTemp + HeartRate, 
                         data = train_data, ntree = 100, importance = TRUE)

# View the model
print(rf_model)
## 
## Call:
##  randomForest(formula = RiskLevel ~ Age + SystolicBP + DiastolicBP +      BS + BodyTemp + HeartRate, data = train_data, ntree = 100,      importance = TRUE) 
##                Type of random forest: classification
##                      Number of trees: 100
## No. of variables tried at each split: 2
## 
##         OOB estimate of  error rate: 14.14%
## Confusion matrix:
##           high risk low risk mid risk class.error
## high risk       244        6        7  0.05058366
## low risk          6      209       42  0.18677043
## mid risk         14       34      209  0.18677043
#Predicting on the test set
rf_predictions <- predict(rf_model, test_data)

#Confusion matrix
confusion_matrix <- confusionMatrix(rf_predictions, test_data$RiskLevel)
print(confusion_matrix)
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  high risk low risk mid risk
##   high risk       108        1       10
##   low risk          1       88       17
##   mid risk          1       21       83
## 
## Overall Statistics
##                                           
##                Accuracy : 0.8455          
##                  95% CI : (0.8019, 0.8827)
##     No Information Rate : 0.3333          
##     P-Value [Acc > NIR] : < 2e-16         
##                                           
##                   Kappa : 0.7682          
##                                           
##  Mcnemar's Test P-Value : 0.05068         
## 
## Statistics by Class:
## 
##                      Class: high risk Class: low risk Class: mid risk
## Sensitivity                    0.9818          0.8000          0.7545
## Specificity                    0.9500          0.9182          0.9000
## Pos Pred Value                 0.9076          0.8302          0.7905
## Neg Pred Value                 0.9905          0.9018          0.8800
## Prevalence                     0.3333          0.3333          0.3333
## Detection Rate                 0.3273          0.2667          0.2515
## Detection Prevalence           0.3606          0.3212          0.3182
## Balanced Accuracy              0.9659          0.8591          0.8273
#Plot feature importance
varImpPlot(rf_model, main = "Feature Importance")

Conclusions

In conclusion, this study underscores the persistent challenges of maternal health complications in rural, resource-limited settings like Bangladesh. By leveraging an IoT-based monitoring system, I identified critical risk factors such as elevated blood pressure and blood sugar levels, and complex age-related risk profiles. My findings reveal that high-risk individuals are generally older, but young individuals under 30 are also significantly represented in the high-risk group, highlighting the need to explore additional factors. Blood pressure, both systolic and diastolic, is significantly higher in the high-risk group, even among younger individuals, confirming poor blood pressure control as a major risk factor. Elevated blood sugar levels in the high-risk group further emphasize the importance of comprehensive metabolic monitoring.

Initially, I employed Multinomial Logistic Regression to identify key predictors such as blood sugar, blood pressure, and age. While this model provided interpretable coefficients, it struggled to separate low-risk and mid-risk categories due to overlapping feature distributions and its assumption of linearity between features and the log-odds of each class. These limitations necessitated the transition to a more robust model.

I chose Random Forest for its ability to handle non-linear relationships and capture feature interactions, such as the combined effect of high blood sugar and elevated blood pressure on maternal health risks. This model demonstrated superior performance, with an accuracy of 86.06% and high sensitivity across risk categories. Random Forest also identified blood sugar, systolic and diastolic blood pressure, and age as the most important predictors of maternal health risks.

The use of advanced machine learning techniques, particularly Random Forest classification, enabled the development of predictive models that offer nuanced insights into maternal health risks. These findings underscore the importance of comprehensive, individualized risk assessments and proactive intervention strategies. The scalable methodology presented in this research provides a valuable framework for early risk identification and targeted interventions, ultimately contributing to improved maternal health outcomes in vulnerable populations.

References

-Oversampling and undersampling in imbalanced data:

https://medium.com/metaor-artificial-intelligence/solving-the-class-imbalance-problem-58cb926b5a0f#:~:text=Resampling%20is%20a%20common%20technique,examples%20from%20the%20original%20dataset.

LS0tDQp0aXRsZTogIkRhdGEgNjA2IEZpbmFsIHByb2plY3QiDQphdXRob3I6ICJMYXVyYSBCIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiBvcGVuaW50cm86OmxhYl9yZXBvcnQNCi0tLQ0KIyMjIEFic3RyYWN0DQoNCk1hdGVybmFsIGhlYWx0aCBjb21wbGljYXRpb25zIHJlbWFpbiBhIGNyaXRpY2FsIGNoYWxsZW5nZSBpbiBydXJhbCwgcmVzb3VyY2UtbGltaXRlZCBzZXR0aW5ncyBsaWtlIEJhbmdsYWRlc2guIFRoaXMgc3R1ZHkgaW52ZXN0aWdhdGVzIHByaW1hcnkgcmlzayBmYWN0b3JzIGJ5IGFuYWx5emluZyAxLDAxNCBoZWFsdGggcmVjb3JkcyBjb2xsZWN0ZWQgdGhyb3VnaCBhbiBJb1QtYmFzZWQgbW9uaXRvcmluZyBzeXN0ZW0gYWNyb3NzIHJ1cmFsIGhlYWx0aGNhcmUgZmFjaWxpdGllcy4gRWxldmF0ZWQgc3lzdG9saWMgYW5kIGRpYXN0b2xpYyBibG9vZCBwcmVzc3VyZXMgaW4gdGhlIGhpZ2gtcmlzayBncm91cCwgZXZlbiBhbW9uZyB5b3VuZ2VyIGluZGl2aWR1YWxzLCBjb25maXJtIGJsb29kIHByZXNzdXJlIGNvbnRyb2wgYXMgYSBmdW5kYW1lbnRhbCByaXNrIGluZGljYXRvci4gTWFya2VkbHkgZWxldmF0ZWQgYmxvb2Qgc3VnYXIgbGV2ZWxzIGluIHRoZSBoaWdoLXJpc2sgZ3JvdXAgdW5kZXJzY29yZSB0aGUgaW1wb3J0YW5jZSBvZiBjb21wcmVoZW5zaXZlIG1ldGFib2xpYyBtb25pdG9yaW5nLiBBZ2UgcmV2ZWFsZWQgYSBjb21wbGV4IHJpc2sgcHJvZmlsZSwgd2l0aCBoaWdoLXJpc2sgaW5kaXZpZHVhbHMgZ2VuZXJhbGx5IG9sZGVyLCB5ZXQgeW91bmcgd29tZW4gdW5kZXIgMzAgYWxzbyBzaWduaWZpY2FudGx5IHJlcHJlc2VudGVkLiBUaGlzIGZpbmRpbmcgY2hhbGxlbmdlcyBzaW1wbGlzdGljIGFnZS1iYXNlZCByaXNrIGFzc3VtcHRpb25zLiBVc2luZyBhZHZhbmNlZCBtYWNoaW5lIGxlYXJuaW5nIHRlY2huaXF1ZXMsIHNwZWNpZmljYWxseSByYW5kb20gZm9yZXN0IGNsYXNzaWZpY2F0aW9uLCByZXNlYXJjaGVycyBkZXZlbG9wZWQgcHJlZGljdGl2ZSBtb2RlbHMgc3RyYXRpZnlpbmcgbWF0ZXJuYWwgaGVhbHRoIHJpc2tzIGludG8gbG93LCBtZWRpdW0sIGFuZCBoaWdoLXJpc2sgY2F0ZWdvcmllcy4gVGhlc2UgbW9kZWxzIHByb3ZpZGUgbnVhbmNlZCBpbnNpZ2h0cyBpbnRvIHRoZSBzaWduaWZpY2FuY2Ugb2YgdmFyaW91cyBoZWFsdGggaW5kaWNhdG9ycyBpbiBwcmVkaWN0aW5nIGFkdmVyc2Ugb3V0Y29tZXMuIFRoZSByZXNlYXJjaCBvZmZlcnMgYSBzY2FsYWJsZSBtZXRob2RvbG9neSBmb3IgZWFybHkgcmlzayBpZGVudGlmaWNhdGlvbiBhbmQgdGFyZ2V0ZWQgaW50ZXJ2ZW50aW9ucywgaWxsdW1pbmF0aW5nIHBvdGVudGlhbCBzdHJhdGVnaWVzIGZvciBhZGRyZXNzaW5nIG1hdGVybmFsIGhlYWx0aCBjaGFsbGVuZ2VzIGluIGxvdy1yZXNvdXJjZSBlbnZpcm9ubWVudHMuIFRoZSBmaW5kaW5ncyBjaGFsbGVuZ2UgdHJhZGl0aW9uYWwgYXBwcm9hY2hlcywgZW1waGFzaXppbmcgdGhlIG5lZWQgZm9yIGluZGl2aWR1YWxpemVkLCBjb21wcmVoZW5zaXZlIHJpc2sgYXNzZXNzbWVudCBhbmQgcHJvYWN0aXZlIGludGVydmVudGlvbiBzdHJhdGVnaWVzLiBVbHRpbWF0ZWx5LCB0aGlzIHJlc2VhcmNoIGNvbnRyaWJ1dGVzIGNydWNpYWwga25vd2xlZGdlIHRvIGltcHJvdmUgbWF0ZXJuYWwgaGVhbHRoIG91dGNvbWVzLCBvZmZlcmluZyBhIGRhdGEtZHJpdmVuIGFwcHJvYWNoIHRvIHVuZGVyc3RhbmRpbmcgYW5kIG1pdGlnYXRpbmcgcmlza3MgaW4gdnVsbmVyYWJsZSBwb3B1bGF0aW9ucy4NCg0KDQojIyMgSW50cm9kdWN0aW9uDQoNCkluIHJ1cmFsIEJhbmdsYWRlc2gsIG1hdGVybmFsIGhlYWx0aCBjb21wbGljYXRpb25zIHBvc2Ugc2lnbmlmaWNhbnQgY2hhbGxlbmdlcywgZXNwZWNpYWxseSBpbiByZXNvdXJjZS1saW1pdGVkIHNldHRpbmdzLiBUaGlzIHN0dWR5IGV4YW1pbmVzIGtleSByaXNrIGZhY3RvcnMgYnkgYW5hbHl6aW5nIDEsMDE0IGhlYWx0aCByZWNvcmRzIGZyb20gSW50ZXJuZXQgb2YgVGhpbmdzIChJb1QpLWJhc2VkIG1vbml0b3Jpbmcgc3lzdGVtIGFjcm9zcyBydXJhbCBoZWFsdGhjYXJlIGZhY2lsaXRpZXMuDQoNCiMjIyMgUmVzZWFyY2ggcXVlc3Rpb24NCg0KV2hhdCBhcmUgdGhlIHByaW1hcnkgcmlzayBmYWN0b3JzIGZvciBtYXRlcm5hbCBoZWFsdGggY29tcGxpY2F0aW9ucyBpbiBydXJhbCBCYW5nbGFkZXNoPw0KDQpCeSBhbmFseXppbmcgdGhlIGZlYXR1cmVzIGluIHRoZSBkYXRhc2V0LCByZXNlYXJjaGVycyBjb3VsZCBpZGVudGlmeSB0aGUgZmFjdG9ycyBtb3N0IHN0cm9uZ2x5IGFzc29jaWF0ZWQgd2l0aCBhZHZlcnNlIG1hdGVybmFsIG91dGNvbWVzLg0KDQpMb2FkaW5nIERhdGEgYW5kIHBhY2thZ2VzDQpgYGB7ciBsb2FkLXBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KG9wZW5pbnRybykNCiNVUkwgb2YgdGhlIGRhdGFzZXQNCnVybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xidXJlbmtvdi9tYXRlcm5hbHJpc2svcmVmcy9oZWFkcy9tYWluL01hdGVybmFsJTIwSGVhbHRoJTIwUmlzayUyMERhdGElMjBTZXQuY3N2Ig0KDQojTG9hZGluZyB0aGUgZGF0YXNldCBpbnRvIGEgZGF0YSBmcmFtZQ0KZGYgPC0gcmVhZC5jc3YodXJsKQ0KDQojRGlzcGxheWluZyB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIGRhdGFzZXQNCmhlYWQoZGYpDQpgYGANCg0KIyMjIERhdGEgZXhwbG9yYXRpb24gYW5kIGFuYWx5c2lzDQoNCkNoZWNraW5nIGZvciBtaXNzaW5nIHZhbHVlcw0KDQpgYGB7cn0NCiNKdXN0IGNoZWNraW5nIGZvciBtaXNzaW5nIHZhbHVlcw0KY29sU3Vtcyhpcy5uYShkZikpICNTaG91bGQgYWxsIGJlIDAgc2luY2Ugbm8gbWlzc2luZyB2YWx1ZXMgYXJlIG1lbnRpb25lZA0KYGBgDQojIyMjIERhdGFzZXQgU3VtbWFyeSBhbmQgU3RydWN0dXJlDQoNCmBgYHtyfQ0KI0NoZWNraW5nIHN0cnVjdHVyZSBhbmQgc3VtbWFyeQ0Kc3RyKGRmKSAgICAgICAgI0NoZWNraW5nIGRhdGEgdHlwZXMNCnN1bW1hcnkoZGYpICAgICNTdW1tYXJ5IG9mIGZlYXR1cmVzDQpgYGANClNvbWUgcmVsZXZhbnQgb2JzZXJ2YXRpb25zIGZyb20gdGhpcyBwcmV2aWV3IG9mIHRoaXMgZGF0YXNldDoNCg0KQWdlOg0KDQpUaGUgYWdlcyByYW5nZSBmcm9tIDEwIHRvIDcwIHllYXJzLg0KVGhlIGRpc3RyaWJ1dGlvbiBzZWVtcyByaWdodC1za2V3ZWQgd2l0aCBhIG1lYW4gaGlnaGVyIHRoYW4gdGhlIG1lZGlhbi4NCkEgY2xvc2VyIGxvb2sgYXQgYWdlIGdyb3VwcyBjb3VsZCBwcm92aWRlIGluc2lnaHRzIChlLmcuLCB0ZWVuYWdlcnMsIGFkdWx0cywgb2xkZXIgaW5kaXZpZHVhbHMpLg0KU3lzdG9saWNCUCBhbmQgRGlhc3RvbGljQlA6DQoNClRoZSByYW5nZXMgYXBwZWFyIHBsYXVzaWJsZSwgYnV0IHZhbHVlcyBhdCB0aGUgZXh0cmVtZXMgKGUuZy4sIDcwIGZvciBTeXN0b2xpY0JQIGFuZCA0OSBmb3IgRGlhc3RvbGljQlApIG1pZ2h0IG5lZWQgZnVydGhlciB2YWxpZGF0aW9uLg0KTWVkaWFuIFN5c3RvbGljQlAgKDEyMCkgYW5kIERpYXN0b2xpY0JQICg4MCkgYWxpZ24gd2l0aCBjb21tb24gaGVhbHRoeSByYW5nZXMuDQpCUyAoQmxvb2QgU3VnYXIpOg0KDQpBIHdpZGUgcmFuZ2UgZnJvbSA2IHRvIDE5IG1tb2wvTC4NCk1lYW4gKDguNzI2KSBhbmQgbWVkaWFuICg3LjUpIGluZGljYXRlIHBvc3NpYmxlIGhpZ2ggYmxvb2Qgc3VnYXIgY2FzZXMgc2tld2luZyB0aGUgZGF0YS4NCkJvZHlUZW1wOg0KDQpNb3N0IHZhbHVlcyBhcmUgY2x1c3RlcmVkIGFyb3VuZCA5OMKwRiwgYnV0IHRoZXJlIGFyZSBvdXRsaWVycyBsaWtlIDEwM8KwRi4NCkhlYXJ0UmF0ZToNCg0KUmVzdGluZyBoZWFydCByYXRlIHJhbmdlcyBmcm9tIDcgKGFub21hbG91cyBvciBlcnJvcj8pIHRvIDkwIGJwbS4NCk1lZGlhbiAoNzYgYnBtKSBpcyB3aXRoaW4gYSBub3JtYWwgcmFuZ2UgZm9yIGhlYWx0aHkgYWR1bHRzLg0KUmlza0xldmVsOg0KDQpUaGlzIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGlzIHRoZSB0YXJnZXQuIEFuYWx5emluZyBpdHMgZGlzdHJpYnV0aW9uIGlzIGNydWNpYWwgdG8gdW5kZXJzdGFuZCBjbGFzcyBiYWxhbmNlLg0KDQoNClZhbGlkYXRlIGFuZCBjbGVhbiBkYXRhDQpgYGB7cn0NCiNDaGVja2luZyBmb3IgdW5leHBlY3RlZCB2YWx1ZXMgKGUuZy4sIEhlYXJ0UmF0ZSA8IDQwIG9yID4gMjAwKQ0KZmlsdGVyKGRmLCBIZWFydFJhdGUgPCA0MCB8IEhlYXJ0UmF0ZSA+IDIwMCkNCg0KI0ludmVzdGlnYXRpbmcgdW51c3VhbGx5IGxvdyBvciBoaWdoIEJsb29kIFN1Z2FyIGFuZCBCb2R5VGVtcA0KZmlsdGVyKGRmLCBCUyA+IDE1IHwgQm9keVRlbXAgPiAxMDAgfCBCb2R5VGVtcCA8IDk3KQ0KDQojSGFuZGxpbmcgb3V0bGllcnMgYnkgY2FwcGluZyB2YWx1ZXMgb3IgcmVtb3ZpbmcgdGhlbQ0KZGYgPC0gZGYgJT4lDQogIGZpbHRlcihIZWFydFJhdGUgPj0gNDAgJiBIZWFydFJhdGUgPD0gMjAwKSAlPiUNCiAgZmlsdGVyKEJTIDw9IDE1ICYgQm9keVRlbXAgPD0gMTAwICYgQm9keVRlbXAgPj0gOTcpDQoNCmBgYA0KDQpUaGlzIGltYmFsYW5jZSBjb3VsZCBwb3NlIGEgY2hhbGxlbmdlIGZvciBjbGFzc2lmaWNhdGlvbiB0YXNrcyBhcyBtb2RlbHMgdGVuZCB0byBmYXZvciB0aGUgbWFqb3JpdHkgY2xhc3MgKExvdyBSaXNrKSBvdmVyIHRoZSBtaW5vcml0eSBjbGFzc2VzIChIaWdoIFJpc2sgYW5kIE1pZCBSaXNrKS4gVG8gYWRkcmVzcyB0aGlzLCBzZXZlcmFsIHRlY2huaXF1ZXMgY2FuIGJlIGVtcGxveWVkLCBzdWNoIGFzIHJlc2FtcGxpbmcsIGNsYXNzIHdlaWdodGluZywgb3IgdXNpbmcgc3BlY2lhbGl6ZWQgYWxnb3JpdGhtcy4NCmBgYHtyfQ0KI0NsYXNzIGRpc3RyaWJ1dGlvbg0KdGFibGUoZGYkUmlza0xldmVsKQ0KDQojQmFyIHBsb3QgdG8gdmlzdWFsaXplDQpnZ3Bsb3QoZGYsIGFlcyh4ID0gUmlza0xldmVsLCBmaWxsID0gUmlza0xldmVsKSkgKw0KICBnZW9tX2JhcigpICsNCiAgbGFicyh0aXRsZSA9ICJSaXNrIExldmVsIERpc3RyaWJ1dGlvbiIsIHggPSAiUmlzayBMZXZlbCIsIHkgPSAiQ291bnQiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KIyMjIyBPdmVyc2FtcGxpbmcNCg0KRGVjaXNpb24gZm9yIHRoaXMgRGF0YXNldA0KVGhlIGRhdGFzZXQgaGFzIDEwMTQgaW5zdGFuY2VzLCB3aGljaCBpcyByZWxhdGl2ZWx5IHNtYWxsLiBSZW1vdmluZyB0b28gbWFueSBzYW1wbGVzICh2aWEgdW5kZXJzYW1wbGluZykgY291bGQgaGFybSB0aGUgbW9kZWwncyBnZW5lcmFsaXphYmlsaXR5LiBUaGVyZWZvcmU6IEkgYW0gdXNpbmcgb3ZlcnNhbXBwbGluZyB0byByZXRhaW4gYXMgbXVjaCBkYXRhIGFzIHBvc3NpYmxlLg0KDQoNCkNvbnZlcnRpbmcgdG8gYSBmYWN0b3INCmBgYHtyfQ0KZGYkUmlza0xldmVsIDwtIGFzLmZhY3RvcihkZiRSaXNrTGV2ZWwpDQoNCmBgYA0KDQpgYGB7cn0NCnN0cihkZikNCg0KYGBgDQoNCiMjIyMgTnVtZXJpYyB2YXJpYWJsZXMNCmBgYHtyfQ0KI0xvYWRpbmcgbmVjZXNzYXJ5IGxpYnJhcnkgZm9yIHZpc3VhbGl6YXRpb24NCmxpYnJhcnkoZ2dwbG90MikNCg0KI0NvbnZlcnRpbmcgUmlza0xldmVsIHRvIGEgZmFjdG9yDQpkZiRSaXNrTGV2ZWwgPC0gYXMuZmFjdG9yKGRmJFJpc2tMZXZlbCkNCg0KI1NwbGl0dGluZyB0aGUgZGF0YXNldCBpbnRvIGluZGl2aWR1YWwgY2xhc3Nlcw0KaGlnaF9yaXNrIDwtIGRmW2RmJFJpc2tMZXZlbCA9PSAiaGlnaCByaXNrIiwgXQ0KbG93X3Jpc2sgPC0gZGZbZGYkUmlza0xldmVsID09ICJsb3cgcmlzayIsIF0NCm1pZF9yaXNrIDwtIGRmW2RmJFJpc2tMZXZlbCA9PSAibWlkIHJpc2siLCBdDQoNCiNTZXR0aW5nIHRoZSB0YXJnZXQgc2l6ZSBmb3IgYWxsIGNsYXNzZXMgKGVxdWFsIHRvIHRoZSBsYXJnZXN0IGNsYXNzIHNpemUpDQp0YXJnZXRfc2l6ZSA8LSBtYXgobnJvdyhsb3dfcmlzayksIG5yb3coaGlnaF9yaXNrKSwgbnJvdyhtaWRfcmlzaykpDQoNCiNGdW5jdGlvbiB0byBnZW5lcmF0ZSBzeW50aGV0aWMgc2FtcGxlcyBmb3IgYmFsYW5jaW5nDQpnZW5lcmF0ZV9zeW50aGV0aWMgPC0gZnVuY3Rpb24obWlub3JpdHlfY2xhc3MsIHRhcmdldF9zaXplKSB7DQogIG5fc2FtcGxlcyA8LSB0YXJnZXRfc2l6ZSAtIG5yb3cobWlub3JpdHlfY2xhc3MpDQogIGlmIChuX3NhbXBsZXMgPD0gMCkgcmV0dXJuKG1pbm9yaXR5X2NsYXNzKSAjSWYgYWxyZWFkeSBiYWxhbmNlZCwgcmV0dXJuIG9yaWdpbmFsDQogIA0KICAjUmFuZG9tbHkgc2FtcGxlIGZyb20gdGhlIG1pbm9yaXR5IGNsYXNzIHdpdGggcmVwbGFjZW1lbnQNCiAgc3ludGhldGljX3NhbXBsZXMgPC0gbWlub3JpdHlfY2xhc3Nbc2FtcGxlKDE6bnJvdyhtaW5vcml0eV9jbGFzcyksIG5fc2FtcGxlcywgcmVwbGFjZSA9IFRSVUUpLCBdDQogIA0KICAjQWRkaW5nIHNtYWxsIHJhbmRvbSBub2lzZSB0byBudW1lcmljIGNvbHVtbnMgZm9yIHN5bnRoZXRpYyBzYW1wbGVzDQogIGZvciAoY29sIGluIG5hbWVzKHN5bnRoZXRpY19zYW1wbGVzKSkgew0KICAgIGlmIChpcy5udW1lcmljKHN5bnRoZXRpY19zYW1wbGVzW1tjb2xdXSkpIHsNCiAgICAgIHN5bnRoZXRpY19zYW1wbGVzW1tjb2xdXSA8LSBzeW50aGV0aWNfc2FtcGxlc1tbY29sXV0gKyBydW5pZihuX3NhbXBsZXMsIC0wLjEsIDAuMSkNCiAgICB9DQogIH0NCiAgDQogICNDb21iaW5pbmcgb3JpZ2luYWwgYW5kIHN5bnRoZXRpYyBzYW1wbGVzDQogIHJldHVybihyYmluZChtaW5vcml0eV9jbGFzcywgc3ludGhldGljX3NhbXBsZXMpKQ0KfQ0KDQojR2VuZXJhdGluZyBzeW50aGV0aWMgc2FtcGxlcyBmb3IgbWlub3JpdHkgY2xhc3Nlcw0KaGlnaF9yaXNrX2JhbGFuY2VkIDwtIGdlbmVyYXRlX3N5bnRoZXRpYyhoaWdoX3Jpc2ssIHRhcmdldF9zaXplKQ0KbWlkX3Jpc2tfYmFsYW5jZWQgPC0gZ2VuZXJhdGVfc3ludGhldGljKG1pZF9yaXNrLCB0YXJnZXRfc2l6ZSkNCmxvd19yaXNrX2JhbGFuY2VkIDwtIGxvd19yaXNrICMgTG93IHJpc2sgaXMgYWxyZWFkeSBhdCB0aGUgdGFyZ2V0IHNpemUNCg0KI0NvbWJpbmluZyBhbGwgY2xhc3NlcyBpbnRvIGEgc2luZ2xlIGJhbGFuY2VkIGRhdGFzZXQNCmRmX2JhbGFuY2VkIDwtIHJiaW5kKGhpZ2hfcmlza19iYWxhbmNlZCwgbWlkX3Jpc2tfYmFsYW5jZWQsIGxvd19yaXNrX2JhbGFuY2VkKQ0KDQojQ2hlY2tpbmcgdGhlIG5ldyBjbGFzcyBkaXN0cmlidXRpb24NCnByaW50KHRhYmxlKGRmX2JhbGFuY2VkJFJpc2tMZXZlbCkpDQoNCiNWaXN1YWxpemluZyB0aGUgYmFsYW5jZWQgZGF0YXNldA0KZ2dwbG90KGRmX2JhbGFuY2VkLCBhZXMoeCA9IFJpc2tMZXZlbCwgZmlsbCA9IFJpc2tMZXZlbCkpICsNCiAgZ2VvbV9iYXIoKSArDQogIGxhYnModGl0bGUgPSAiQmFsYW5jZWQgUmlzayBMZXZlbCBEaXN0cmlidXRpb24iLCB4ID0gIlJpc2sgTGV2ZWwiLCB5ID0gIkNvdW50IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCg0KYGBge3J9DQojU3VtbWFyeSBzdGF0aXN0aWNzIGZvciBudW1lcmljIHZhcmlhYmxlcw0KZGYgJT4lDQogIHNlbGVjdChBZ2UsIFN5c3RvbGljQlAsIERpYXN0b2xpY0JQLCBCUywgQm9keVRlbXAsIEhlYXJ0UmF0ZSkgJT4lDQogIHN1bW1hcnkoKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCiNWaXN1YWxpemluZyBudW1lcmljIGZlYXR1cmUgZGlzdHJpYnV0aW9ucw0KZGYgJT4lDQogIHNlbGVjdChBZ2UsIFN5c3RvbGljQlAsIERpYXN0b2xpY0JQLCBCUywgQm9keVRlbXAsIEhlYXJ0UmF0ZSkgJT4lDQogIGdhdGhlcihrZXkgPSAiRmVhdHVyZSIsIHZhbHVlID0gIlZhbHVlIikgJT4lDQogIGdncGxvdChhZXMoeCA9IFZhbHVlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGZpbGwgPSAic2t5Ymx1ZSIsIGNvbG9yID0gImJsYWNrIikgKw0KICBmYWNldF93cmFwKH4gRmVhdHVyZSwgc2NhbGVzID0gImZyZWUiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIE51bWVyaWMgRmVhdHVyZXMiKQ0KDQpgYGANCg0KDQpgYGB7cn0NCiNDcmVhdGluZyBhIGRhdGEgZnJhbWUgc3VtbWFyaXppbmcgdmFyaWFibGVzIGFuZCBjb21tZW50cw0Kc3VtbWFyeV90YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBWYXJpYWJsZSA9IGMoIkFnZSIsICJTeXN0b2xpY0JQIiwgIkRpYXN0b2xpY0JQIiwgIkJTIiwgIkJvZHlUZW1wIiwgIkhlYXJ0UmF0ZSIpLA0KICBMaWtlbHlfRGlzdHJpYnV0aW9uID0gYygNCiAgICAiU2xpZ2h0IHBvc2l0aXZlIHNrZXciLA0KICAgICJTbGlnaHQgbmVnYXRpdmUgc2tldyIsDQogICAgIlNsaWdodCBuZWdhdGl2ZSBza2V3IiwNCiAgICAiUG9zaXRpdmUgc2tldyIsDQogICAgIlN5bW1ldHJpY2FsIiwNCiAgICAiU2xpZ2h0IHBvc2l0aXZlIHNrZXciDQogICksDQogIENvbW1lbnRzID0gYygNCiAgICAiWW91bmdlciBhZ2VzIGRvbWluYXRlIHdpdGggYSBmZXcgb2xkZXIgb3V0bGllcnMuIiwNCiAgICAiQ29uY2VudHJhdGlvbiBuZWFyIG1lZGlhbiB3aXRoIGxvd2VyIG91dGxpZXJzLiIsDQogICAgIlNpbWlsYXIgdG8gU3lzdG9saWNCUCB3aXRoIGZld2VyIGxvdyBvdXRsaWVycy4iLA0KICAgICJIaWdoZXIgb3V0bGllcnMgaW5jcmVhc2UgdGhlIG1lYW4uIiwNCiAgICAiTGlrZWx5IG5vcm1hbCBkaXN0cmlidXRpb24gY2VudGVyZWQgYXQgOTguIiwNCiAgICAiTW9zdCB2YWx1ZXMgYXJvdW5kIDcw4oCTNzgsIHdpdGggc29tZSBoaWdoIG91dGxpZXJzLiINCiAgKQ0KKQ0KDQojUHJpbnR0aW5nIHRoZSB0YWJsZQ0KcHJpbnQoc3VtbWFyeV90YWJsZSkNCg0KI09wdGlvbmFsbHkgZGlzcGxheSBpdCBpbiBhIG1vcmUgcmVhZGFibGUgdGFibGUgZm9ybWF0IGlmIHVzaW5nIFIgTWFya2Rvd24gb3IgVmlld2VyDQpsaWJyYXJ5KGtuaXRyKQ0Ka2FibGUoc3VtbWFyeV90YWJsZSwgY2FwdGlvbiA9ICJWYXJpYWJsZSBEaXN0cmlidXRpb25zIGFuZCBDb21tZW50cyIpDQoNCmBgYA0KDQpPYnNlcnZhdGlvbnM6DQoNCjEuIEFnZQ0KT2JzZXJ2YXRpb246IFRoZSBkaXN0cmlidXRpb24gaXMgcG9zaXRpdmVseSBza2V3ZWQgKHJpZ2h0LXNrZXdlZCksIHdpdGggbW9zdCBjb3VudHMgY29uY2VudHJhdGVkIGJldHdlZW4gMjAgYW5kIDM2LiBBIHNtYWxsIG51bWJlciBvZiBpbmRpdmlkdWFscyBhcmUgb2xkZXIgKGFib3ZlIDUwKSwgY3JlYXRpbmcgYSBsb25nIHJpZ2h0IHRhaWwuDQpJbnNpZ2h0OiBUaGUgcG9wdWxhdGlvbiBpbiB0aGlzIGRhdGFzZXQgaXMgcHJpbWFyaWx5IHlvdW5nIHRvIG1pZGRsZS1hZ2VkLCB3aXRoIGZld2VyIG9sZGVyIGluZGl2aWR1YWxzLg0KMi4gQm9keSBUZW1wZXJhdHVyZQ0KT2JzZXJ2YXRpb246IFRoZSBkaXN0cmlidXRpb24gaXMgaGlnaGx5IHN5bW1ldHJpY2FsLCB3aXRoIG1vc3QgdmFsdWVzIGNsdXN0ZXJlZCBleGFjdGx5IGFyb3VuZCA5OMKwRi4gVGhlcmUgYXJlIHZlcnkgZmV3IG9ic2VydmF0aW9ucyBvdXRzaWRlIHRoaXMgdmFsdWUgKDk4LjUgdG8gMTAwKS4NCkluc2lnaHQ6IFRoaXMgc3VnZ2VzdHMgdGhhdCBib2R5IHRlbXBlcmF0dXJlIGluIHRoZSBkYXRhc2V0IHJlbWFpbnMgY29uc2lzdGVudCB3aXRoIHRoZSBub3JtYWwgcmFuZ2UgZm9yIGhlYWx0aHkgaW5kaXZpZHVhbHMuDQozLiBCbG9vZCBTdWdhciAoQlMpDQpPYnNlcnZhdGlvbjogVGhlIGRpc3RyaWJ1dGlvbiBpcyBwb3NpdGl2ZWx5IHNrZXdlZCwgd2l0aCBtb3N0IHZhbHVlcyBjb25jZW50cmF0ZWQgYmV0d2VlbiA2LjkgYW5kIDcuOC4gSG93ZXZlciwgdGhlcmUgYXJlIGNsZWFyIG91dGxpZXJzIGF0IGhpZ2hlciB2YWx1ZXMgKDEwIHRvIDE1KSwgY3JlYXRpbmcgYSBsb25nIHJpZ2h0IHRhaWwuDQpJbnNpZ2h0OiBUaGUgbWFqb3JpdHkgb2YgaW5kaXZpZHVhbHMgaGF2ZSBub3JtYWwgYmxvb2Qgc3VnYXIgbGV2ZWxzLCBidXQgc29tZSBleGhpYml0IHNpZ25pZmljYW50bHkgZWxldmF0ZWQgbGV2ZWxzLCB3aGljaCBtYXkgd2FycmFudCBjbG9zZXIgYXR0ZW50aW9uIChlLmcuLCBwb3RlbnRpYWwgZGlhYmV0ZXMgY2FzZXMpLg0KNC4gRGlhc3RvbGljIEJsb29kIFByZXNzdXJlIChEaWFzdG9saWNCUCkNCk9ic2VydmF0aW9uOiBUaGUgZGlzdHJpYnV0aW9uIGlzIGFwcHJveGltYXRlbHkgc3ltbWV0cmljLCB3aXRoIHRoZSBoaWdoZXN0IGNvdW50cyBhcm91bmQgODAgbW1IZy4gVGhlcmUgYXJlIHNsaWdodGx5IGZld2VyIG9ic2VydmF0aW9ucyBhdCB0aGUgZXh0cmVtZXMgKDUwIGFuZCAxMDApLg0KSW5zaWdodDogRGlhc3RvbGljIGJsb29kIHByZXNzdXJlIHZhbHVlcyBhcmUgbGFyZ2VseSBjb25jZW50cmF0ZWQgYXJvdW5kIHRoZSBub3JtYWwgcmFuZ2UsIHdpdGggc29tZSB2YXJpYXRpb24gYXQgbG93ZXIgYW5kIGhpZ2hlciB2YWx1ZXMuDQo1LiBIZWFydCBSYXRlDQpPYnNlcnZhdGlvbjogVGhlIGRpc3RyaWJ1dGlvbiBpcyBzbGlnaHRseSBwb3NpdGl2ZWx5IHNrZXdlZCwgd2l0aCBhIHBlYWsgYXJvdW5kIDcwIGJwbS4gVGhlcmUgYXJlIHNvbWUgY291bnRzIGluIHRoZSBoaWdoZXIgcmFuZ2UgKDgw4oCTOTAgYnBtKSBidXQgdmVyeSBmZXcgbG93ZXIgdGhhbiA2MCBicG0uDQpJbnNpZ2h0OiBNb3N0IGluZGl2aWR1YWxzIGhhdmUgYSBub3JtYWwgcmVzdGluZyBoZWFydCByYXRlLCB3aXRoIGZld2VyIG91dGxpZXJzIGluIHRoZSBoaWdoZXIgcmFuZ2UuDQo2LiBTeXN0b2xpYyBCbG9vZCBQcmVzc3VyZSAoU3lzdG9saWNCUCkNCk9ic2VydmF0aW9uOiBUaGUgZGlzdHJpYnV0aW9uIHNob3dzIGEgc2xpZ2h0IG5lZ2F0aXZlIHNrZXcsIHdpdGggYSBzdHJvbmcgcGVhayBhdCAxMjAgbW1IZy4gVGhlcmUgYXJlIGEgZmV3IGxvd2VyIG91dGxpZXJzIChhcm91bmQgODApIGJ1dCBmZXdlciB2YWx1ZXMgZXhjZWVkaW5nIDE0MC4NCkluc2lnaHQ6IFN5c3RvbGljIGJsb29kIHByZXNzdXJlIGlzIGNvbmNlbnRyYXRlZCBuZWFyIHRoZSBoaWdoZXIgZW5kIG9mIHRoZSBub3JtYWwgcmFuZ2UsIHdpdGggc29tZSBsb3cgb3V0bGllcnMgcG90ZW50aWFsbHkgaW5kaWNhdGluZyBoeXBvdGVuc2lvbi4NCkdlbmVyYWwgT2JzZXJ2YXRpb25zDQpNb3N0IGZlYXR1cmVzIGV4aGliaXQgZXhwZWN0ZWQgcGh5c2lvbG9naWNhbCByYW5nZXMgKGUuZy4sIGJvZHkgdGVtcGVyYXR1cmUsIGhlYXJ0IHJhdGUpLg0KU2tld25lc3MgaXMgZXZpZGVudCBpbiBBZ2UsIEJTLCBhbmQgSGVhcnQgUmF0ZSwgc3VnZ2VzdGluZyBvdXRsaWVycyBvciB1bmJhbGFuY2VkIGRpc3RyaWJ1dGlvbnMuDQpCb2R5IFRlbXBlcmF0dXJlIGFuZCBEaWFzdG9saWNCUCBhcmUgY2xvc2UgdG8gc3ltbWV0cmljYWwsIGltcGx5aW5nIG5vcm1hbCBkaXN0cmlidXRpb25zLg0KDQojIyMjIFJlbGF0aW9uc2hpcHMgQmV0d2VlbiBGZWF0dXJlcw0KDQpgYGB7cn0NCiNDb3JyZWxhdGlvbnMNCmNvcl9tYXRyaXggPC0gY29yKGRmICU+JSBzZWxlY3QoQWdlLCBTeXN0b2xpY0JQLCBEaWFzdG9saWNCUCwgQlMsIEJvZHlUZW1wLCBIZWFydFJhdGUpKQ0KDQojVmlzdWFsaXppbmcgY29ycmVsYXRpb24gbWF0cml4DQpsaWJyYXJ5KGNvcnJwbG90KQ0KY29ycnBsb3QoY29yX21hdHJpeCwgbWV0aG9kID0gImNpcmNsZSIsIHR5cGUgPSAibG93ZXIiLCB0bC5jZXggPSAwLjgsIGFkZENvZWYuY29sID0gImJsYWNrIikNCg0KYGBgDQoNCg0KU3Ryb25nZXN0IENvcnJlbGF0aW9uOiBTeXN0b2xpY0JQIGFuZCBEaWFzdG9saWNCUCAoMC43NCksIHdoaWNoIGlzIGV4cGVjdGVkIHNpbmNlIHRoZXkgYXJlIGJvdGggbWVhc3VyZXMgb2YgYmxvb2QgcHJlc3N1cmUuDQpNb2RlcmF0ZSBDb3JyZWxhdGlvbnM6IEFnZSBhbmQgQlMgKDAuNDUpLCBhbmQgQlMgd2l0aCBib3RoIGJsb29kIHByZXNzdXJlIG1lYXN1cmVzICh+MC4zM+KAkzAuMzQpLg0KV2VhayBvciBObyBDb3JyZWxhdGlvbjogQm9keSB0ZW1wZXJhdHVyZSBhbmQgaGVhcnQgcmF0ZSBzaG93IHZlcnkgbGl0dGxlIHRvIG5vIGNvcnJlbGF0aW9uIHdpdGggb3RoZXIgdmFyaWFibGVzLg0KDQoNCmBgYHtyfQ0KI0JveHBsb3QgZm9yIEFnZSBhbmQgUmlza0xldmVsDQpnZ3Bsb3QoZGZfYmFsYW5jZWQsIGFlcyh4ID0gUmlza0xldmVsLCB5ID0gQWdlLCBmaWxsID0gUmlza0xldmVsKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGxhYnModGl0bGUgPSAiQWdlIERpc3RyaWJ1dGlvbiBieSBSaXNrIExldmVsIiwgeCA9ICJSaXNrIExldmVsIiwgeSA9ICJBZ2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojUmVwZWF0aW5nIGZvciBvdGhlciBmZWF0dXJlcw0KZmVhdHVyZXMgPC0gYygiU3lzdG9saWNCUCIsICJEaWFzdG9saWNCUCIsICJCUyIsICJCb2R5VGVtcCIsICJIZWFydFJhdGUiKQ0KZm9yIChmZWF0dXJlIGluIGZlYXR1cmVzKSB7DQogIHByaW50KA0KICAgIGdncGxvdChkZl9iYWxhbmNlZCwgYWVzX3N0cmluZyh4ID0gIlJpc2tMZXZlbCIsIHkgPSBmZWF0dXJlLCBmaWxsID0gIlJpc2tMZXZlbCIpKSArDQogICAgICBnZW9tX2JveHBsb3QoKSArDQogICAgICBsYWJzKHRpdGxlID0gcGFzdGUoZmVhdHVyZSwgIkRpc3RyaWJ1dGlvbiBieSBSaXNrIExldmVsIiksDQogICAgICAgICAgIHggPSAiUmlzayBMZXZlbCIsIHkgPSBmZWF0dXJlKSArDQogICAgICB0aGVtZV9taW5pbWFsKCkNCiAgKQ0KfQ0KDQpgYGANCg0KDQpgYGB7cn0NCiNTY2F0dGVyIHBsb3QgZm9yIFN5c3RvbGljQlAgdnMuIERpYXN0b2xpY0JQIGJ5IFJpc2tMZXZlbA0KZ2dwbG90KGRmX2JhbGFuY2VkLCBhZXMoeCA9IFN5c3RvbGljQlAsIHkgPSBEaWFzdG9saWNCUCwgY29sb3IgPSBSaXNrTGV2ZWwpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJTeXN0b2xpYyB2cy4gRGlhc3RvbGljIEJQIGJ5IFJpc2sgTGV2ZWwiLCB4ID0gIlN5c3RvbGljIEJQIiwgeSA9ICJEaWFzdG9saWMgQlAiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KDQpDb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBjb250aW51b3VzIHZhcmlhYmxlcyBhbmQgdGhlIHByb2JhYmlsaXR5IG9mIGJlaW5nIGluIHRoZSAiaGlnaCByaXNrIg0KDQpgYGB7cn0NCiNDcmVhdGluZyBhIGJpbmFyeSB2YXJpYWJsZSBmb3IgaGlnaCByaXNrDQpkZl9iYWxhbmNlZCRIaWdoUmlzayA8LSBpZmVsc2UoZGZfYmFsYW5jZWQkUmlza0xldmVsID09ICJoaWdoIHJpc2siLCAxLCAwKQ0KDQpgYGANCg0KDQpgYGB7cn0NCiNTZWxlY3RpbmcgbnVtZXJpYyBmZWF0dXJlcw0KbnVtZXJpY19mZWF0dXJlcyA8LSBjKCJBZ2UiLCAiU3lzdG9saWNCUCIsICJEaWFzdG9saWNCUCIsICJCUyIsICJCb2R5VGVtcCIsICJIZWFydFJhdGUiKQ0KDQojQ29tcHV0aW5nIFBlYXJzb24gY29ycmVsYXRpb25zDQpwZWFyc29uX2NvcnIgPC0gc2FwcGx5KG51bWVyaWNfZmVhdHVyZXMsIGZ1bmN0aW9uKGZlYXR1cmUpIHsNCiAgY29yKGRmX2JhbGFuY2VkW1tmZWF0dXJlXV0sIGRmX2JhbGFuY2VkJEhpZ2hSaXNrLCBtZXRob2QgPSAicGVhcnNvbiIpDQp9KQ0KDQojRGlzcGxheWluZyBQZWFyc29uIGNvcnJlbGF0aW9ucw0KcGVhcnNvbl9jb3JyDQoNCmBgYA0KDQpgYGB7cn0NCiNDb252ZXJ0aW5nIFBlYXJzb24gY29ycmVsYXRpb25zIHRvIGEgZGF0YSBmcmFtZQ0KY29ycmVsYXRpb25fZGYgPC0gZGF0YS5mcmFtZSgNCiAgRmVhdHVyZSA9IG5hbWVzKHBlYXJzb25fY29yciksDQogIENvcnJlbGF0aW9uID0gcGVhcnNvbl9jb3JyDQopDQoNCiNCYXIgcGxvdCBmb3IgY29ycmVsYXRpb25zDQpsaWJyYXJ5KGdncGxvdDIpDQpnZ3Bsb3QoY29ycmVsYXRpb25fZGYsIGFlcyh4ID0gcmVvcmRlcihGZWF0dXJlLCBDb3JyZWxhdGlvbiksIHkgPSBDb3JyZWxhdGlvbiwgZmlsbCA9IENvcnJlbGF0aW9uKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBsYWJzKHRpdGxlID0gIkNvcnJlbGF0aW9uIG9mIEZlYXR1cmVzIHdpdGggSGlnaCBSaXNrIiwNCiAgICAgICB4ID0gIkZlYXR1cmUiLA0KICAgICAgIHkgPSAiUGVhcnNvbiBDb3JyZWxhdGlvbiIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgY29vcmRfZmxpcCgpICsgI0ZsaXAgY29vcmRpbmF0ZXMgZm9yIGhvcml6b250YWwgYmFycw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJibHVlIiwgaGlnaCA9ICJyZWQiKQ0KDQpgYGANCg0KRm9yIG1lLCBpdCBjYW4gYmUgc3VycHJpc2luZyB3aGVuIGFnZSwgd2hpY2ggaXMgb2Z0ZW4gdGhvdWdodCB0byBiZSBhIGtleSBmYWN0b3IgaW4gaGVhbHRoIHJpc2tzLCBkb2VzIG5vdCB0dXJuIG91dCB0byBiZSB0aGUgc3Ryb25nZXN0IHByZWRpY3Rvci4gSW5zdGVhZCwgZGlyZWN0IGhlYWx0aCBpbmRpY2F0b3JzIGxpa2UgYmxvb2Qgc3VnYXIgKEJTKSwgc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUsIGFuZCBkaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgZW1lcmdlIGFzIG1vcmUgY3JpdGljYWwgZmFjdG9ycyBpbiBwcmVkaWN0aW5nICJoaWdoIHJpc2siIG91dGNvbWVzLg0KDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQojTGlzdCBvZiB2YXJpYWJsZXMgdG8gcGxvdA0KdmFyaWFibGVzIDwtIGMoIkJTIiwgIlN5c3RvbGljQlAiLCAiRGlhc3RvbGljQlAiKQ0KDQojTG9vcCB0aHJvdWdoIGVhY2ggdmFyaWFibGUgYW5kIGNyZWF0ZSBhIHRyZW5kIHBsb3QNCmZvciAodmFyIGluIHZhcmlhYmxlcykgew0KICBwcmludCgNCiAgICBnZ3Bsb3QoZGZfYmFsYW5jZWQsIGFlc19zdHJpbmcoeCA9ICJBZ2UiLCB5ID0gdmFyLCBjb2xvciA9ICJIaWdoUmlzayIpKSArDQogICAgICBnZW9tX3BvaW50KGFscGhhID0gMC42KSArDQogICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFKSArDQogICAgICBsYWJzKHRpdGxlID0gcGFzdGUodmFyLCAiVHJlbmQgT3ZlciBBZ2UiKSwNCiAgICAgICAgICAgeCA9ICJBZ2UiLCB5ID0gdmFyKSArDQogICAgICB0aGVtZV9taW5pbWFsKCkNCiAgKQ0KfQ0KDQpgYGANCg0KDQpgYGB7cn0NCiNGaWx0ZXJpbmcgZm9yIHlvdW5nZXIgaW5kaXZpZHVhbHMgKGUuZy4sIEFnZSA8IDQwKSB3aG8gYXJlIEhpZ2ggUmlzaw0KeW91bmdfaGlnaF9yaXNrIDwtIGRmX2JhbGFuY2VkICU+JQ0KICBmaWx0ZXIoQWdlIDwgNDAgJiBIaWdoUmlzayA9PSAxKQ0KDQojQ29tcGFyaW5nIHlvdW5nZXIgaGlnaC1yaXNrIGluZGl2aWR1YWxzIHRvIG90aGVycw0KeW91bmdfbm9uX2hpZ2hfcmlzayA8LSBkZl9iYWxhbmNlZCAlPiUNCiAgZmlsdGVyKEFnZSA8IDQwICYgSGlnaFJpc2sgPT0gMCkNCg0KI1N1bW1hcnkgc3RhdGlzdGljcyBmb3IgeW91bmcgaGlnaC1yaXNrDQpzdW1tYXJ5KHlvdW5nX2hpZ2hfcmlzaykNCg0KI1N1bW1hcnkgc3RhdGlzdGljcyBmb3IgeW91bmcgbm9uLWhpZ2gtcmlzaw0Kc3VtbWFyeSh5b3VuZ19ub25faGlnaF9yaXNrKQ0KDQpgYGANCkluc2lnaHRzOg0KQWdlOiBIaWdoLXJpc2sgaW5kaXZpZHVhbHMgYXJlIGdlbmVyYWxseSBvbGRlciB0aGFuIG5vbi1oaWdoLXJpc2sgaW5kaXZpZHVhbHMgaW4gdGhpcyBhZ2UgcmFuZ2UuIEhvd2V2ZXIsIHlvdW5nIGluZGl2aWR1YWxzICh1bmRlciAzMCkgYXJlIHN0aWxsIHByZXNlbnQgaW4gdGhlIGhpZ2gtcmlzayBncm91cCwgZW1waGFzaXppbmcgdGhlIG5lZWQgdG8gZXhwbG9yZSBvdGhlciBmYWN0b3JzLg0KDQpTeXN0b2xpYyBhbmQgRGlhc3RvbGljIEJsb29kIHByZXN1cmU6DQpCbG9vZCBwcmVzc3VyZSAoYm90aCBzeXN0b2xpYyBhbmQgZGlhc3RvbGljKSBpcyBzaWduaWZpY2FudGx5IGhpZ2hlciBpbiB0aGUgaGlnaC1yaXNrIGdyb3VwLCBldmVuIGFtb25nIHlvdW5nZXIgaW5kaXZpZHVhbHMuIFRoaXMgY29uZmlybXMgdGhhdCBwb29yIGJsb29kIHByZXNzdXJlIGNvbnRyb2wgaXMgYSBtYWpvciByaXNrIGZhY3Rvci4NCg0KDQpCbG9vZCBTdWdhciAoQlMpDQpCbG9vZCBzdWdhciBsZXZlbHMgYXJlIG1hcmtlZGx5IGVsZXZhdGVkIGluIHRoZSBoaWdoLXJpc2sgZ3JvdXAuIFRoZSBoaWdoZXIgbWVhbiBzdWdnZXN0cyBzb21lIGluZGl2aWR1YWxzIGluIHRoaXMgZ3JvdXAgaGF2ZSBleHRyZW1lIHZhbHVlcy4NCg0KIyMjIE1vZGVsDQoNCmBgYHtyfQ0KDQoNCmxpYnJhcnkobm5ldCkNCg0KI0ZpdHRpbmcgbXVsdGlub21pYWwgbG9naXN0aWMgcmVncmVzc2lvbg0KbXVsdGlub21fbW9kZWwgPC0gbXVsdGlub20oUmlza0xldmVsIH4gQWdlICsgU3lzdG9saWNCUCArIERpYXN0b2xpY0JQICsgQlMgKyBCb2R5VGVtcCArIEhlYXJ0UmF0ZSwgZGF0YSA9IGRmX2JhbGFuY2VkKQ0KDQojU3VtbWFyeSBvZiB0aGUgbW9kZWwNCnN1bW1hcnkobXVsdGlub21fbW9kZWwpDQoNCiNQcmVkaWN0aW5nIG9uIHRoZSB0cmFpbmluZyBzZXQNCnByZWRpY3RlZCA8LSBwcmVkaWN0KG11bHRpbm9tX21vZGVsLCBkZl9iYWxhbmNlZCkNCg0KI0NvbmZ1c2lvbiBtYXRyaXgNCnRhYmxlKFByZWRpY3RlZCA9IHByZWRpY3RlZCwgQWN0dWFsID0gZGZfYmFsYW5jZWQkUmlza0xldmVsKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpDQojQ29udmVydGluZyBSaXNrTGV2ZWwgdG8gYSBmYWN0b3IgaWYgbm90IGFscmVhZHkNCmRmX2JhbGFuY2VkJFJpc2tMZXZlbCA8LSBhcy5mYWN0b3IoZGZfYmFsYW5jZWQkUmlza0xldmVsKQ0KDQojU3BsaXR0aW5nIHRoZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cw0Kc2V0LnNlZWQoNDIpDQp0cmFpbl9pbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGRmX2JhbGFuY2VkJFJpc2tMZXZlbCwgcCA9IDAuNywgbGlzdCA9IEZBTFNFKQ0KdHJhaW5fZGF0YSA8LSBkZl9iYWxhbmNlZFt0cmFpbl9pbmRleCwgXQ0KdGVzdF9kYXRhIDwtIGRmX2JhbGFuY2VkWy10cmFpbl9pbmRleCwgXQ0KDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkocmFuZG9tRm9yZXN0KQ0KI0ZpdHRpbmcgdGhlIFJhbmRvbSBGb3Jlc3QgbW9kZWwNCnJmX21vZGVsIDwtIHJhbmRvbUZvcmVzdChSaXNrTGV2ZWwgfiBBZ2UgKyBTeXN0b2xpY0JQICsgRGlhc3RvbGljQlAgKyBCUyArIEJvZHlUZW1wICsgSGVhcnRSYXRlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGF0YSwgbnRyZWUgPSAxMDAsIGltcG9ydGFuY2UgPSBUUlVFKQ0KDQojIFZpZXcgdGhlIG1vZGVsDQpwcmludChyZl9tb2RlbCkNCg0KYGBgDQoNCg0KYGBge3J9DQojUHJlZGljdGluZyBvbiB0aGUgdGVzdCBzZXQNCnJmX3ByZWRpY3Rpb25zIDwtIHByZWRpY3QocmZfbW9kZWwsIHRlc3RfZGF0YSkNCg0KI0NvbmZ1c2lvbiBtYXRyaXgNCmNvbmZ1c2lvbl9tYXRyaXggPC0gY29uZnVzaW9uTWF0cml4KHJmX3ByZWRpY3Rpb25zLCB0ZXN0X2RhdGEkUmlza0xldmVsKQ0KcHJpbnQoY29uZnVzaW9uX21hdHJpeCkNCg0KYGBgDQoNCmBgYHtyfQ0KI1Bsb3QgZmVhdHVyZSBpbXBvcnRhbmNlDQp2YXJJbXBQbG90KHJmX21vZGVsLCBtYWluID0gIkZlYXR1cmUgSW1wb3J0YW5jZSIpDQoNCmBgYA0KDQoNCg0KDQojIyMgQ29uY2x1c2lvbnMNCg0KSW4gY29uY2x1c2lvbiwgdGhpcyBzdHVkeSB1bmRlcnNjb3JlcyB0aGUgcGVyc2lzdGVudCBjaGFsbGVuZ2VzIG9mIG1hdGVybmFsIGhlYWx0aCBjb21wbGljYXRpb25zIGluIHJ1cmFsLCByZXNvdXJjZS1saW1pdGVkIHNldHRpbmdzIGxpa2UgQmFuZ2xhZGVzaC4gQnkgbGV2ZXJhZ2luZyBhbiBJb1QtYmFzZWQgbW9uaXRvcmluZyBzeXN0ZW0sIEkgaWRlbnRpZmllZCBjcml0aWNhbCByaXNrIGZhY3RvcnMgc3VjaCBhcyBlbGV2YXRlZCBibG9vZCBwcmVzc3VyZSBhbmQgYmxvb2Qgc3VnYXIgbGV2ZWxzLCBhbmQgY29tcGxleCBhZ2UtcmVsYXRlZCByaXNrIHByb2ZpbGVzLiBNeSBmaW5kaW5ncyByZXZlYWwgdGhhdCBoaWdoLXJpc2sgaW5kaXZpZHVhbHMgYXJlIGdlbmVyYWxseSBvbGRlciwgYnV0IHlvdW5nIGluZGl2aWR1YWxzIHVuZGVyIDMwIGFyZSBhbHNvIHNpZ25pZmljYW50bHkgcmVwcmVzZW50ZWQgaW4gdGhlIGhpZ2gtcmlzayBncm91cCwgaGlnaGxpZ2h0aW5nIHRoZSBuZWVkIHRvIGV4cGxvcmUgYWRkaXRpb25hbCBmYWN0b3JzLiBCbG9vZCBwcmVzc3VyZSwgYm90aCBzeXN0b2xpYyBhbmQgZGlhc3RvbGljLCBpcyBzaWduaWZpY2FudGx5IGhpZ2hlciBpbiB0aGUgaGlnaC1yaXNrIGdyb3VwLCBldmVuIGFtb25nIHlvdW5nZXIgaW5kaXZpZHVhbHMsIGNvbmZpcm1pbmcgcG9vciBibG9vZCBwcmVzc3VyZSBjb250cm9sIGFzIGEgbWFqb3IgcmlzayBmYWN0b3IuIEVsZXZhdGVkIGJsb29kIHN1Z2FyIGxldmVscyBpbiB0aGUgaGlnaC1yaXNrIGdyb3VwIGZ1cnRoZXIgZW1waGFzaXplIHRoZSBpbXBvcnRhbmNlIG9mIGNvbXByZWhlbnNpdmUgbWV0YWJvbGljIG1vbml0b3JpbmcuDQoNCkluaXRpYWxseSwgSSBlbXBsb3llZCBNdWx0aW5vbWlhbCBMb2dpc3RpYyBSZWdyZXNzaW9uIHRvIGlkZW50aWZ5IGtleSBwcmVkaWN0b3JzIHN1Y2ggYXMgYmxvb2Qgc3VnYXIsIGJsb29kIHByZXNzdXJlLCBhbmQgYWdlLiBXaGlsZSB0aGlzIG1vZGVsIHByb3ZpZGVkIGludGVycHJldGFibGUgY29lZmZpY2llbnRzLCBpdCBzdHJ1Z2dsZWQgdG8gc2VwYXJhdGUgbG93LXJpc2sgYW5kIG1pZC1yaXNrIGNhdGVnb3JpZXMgZHVlIHRvIG92ZXJsYXBwaW5nIGZlYXR1cmUgZGlzdHJpYnV0aW9ucyBhbmQgaXRzIGFzc3VtcHRpb24gb2YgbGluZWFyaXR5IGJldHdlZW4gZmVhdHVyZXMgYW5kIHRoZSBsb2ctb2RkcyBvZiBlYWNoIGNsYXNzLiBUaGVzZSBsaW1pdGF0aW9ucyBuZWNlc3NpdGF0ZWQgdGhlIHRyYW5zaXRpb24gdG8gYSBtb3JlIHJvYnVzdCBtb2RlbC4NCg0KSSBjaG9zZSBSYW5kb20gRm9yZXN0IGZvciBpdHMgYWJpbGl0eSB0byBoYW5kbGUgbm9uLWxpbmVhciByZWxhdGlvbnNoaXBzIGFuZCBjYXB0dXJlIGZlYXR1cmUgaW50ZXJhY3Rpb25zLCBzdWNoIGFzIHRoZSBjb21iaW5lZCBlZmZlY3Qgb2YgaGlnaCBibG9vZCBzdWdhciBhbmQgZWxldmF0ZWQgYmxvb2QgcHJlc3N1cmUgb24gbWF0ZXJuYWwgaGVhbHRoIHJpc2tzLiBUaGlzIG1vZGVsIGRlbW9uc3RyYXRlZCBzdXBlcmlvciBwZXJmb3JtYW5jZSwgd2l0aCBhbiBhY2N1cmFjeSBvZiA4Ni4wNiUgYW5kIGhpZ2ggc2Vuc2l0aXZpdHkgYWNyb3NzIHJpc2sgY2F0ZWdvcmllcy4gUmFuZG9tIEZvcmVzdCBhbHNvIGlkZW50aWZpZWQgYmxvb2Qgc3VnYXIsIHN5c3RvbGljIGFuZCBkaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUsIGFuZCBhZ2UgYXMgdGhlIG1vc3QgaW1wb3J0YW50IHByZWRpY3RvcnMgb2YgbWF0ZXJuYWwgaGVhbHRoIHJpc2tzLg0KDQpUaGUgdXNlIG9mIGFkdmFuY2VkIG1hY2hpbmUgbGVhcm5pbmcgdGVjaG5pcXVlcywgcGFydGljdWxhcmx5IFJhbmRvbSBGb3Jlc3QgY2xhc3NpZmljYXRpb24sIGVuYWJsZWQgdGhlIGRldmVsb3BtZW50IG9mIHByZWRpY3RpdmUgbW9kZWxzIHRoYXQgb2ZmZXIgbnVhbmNlZCBpbnNpZ2h0cyBpbnRvIG1hdGVybmFsIGhlYWx0aCByaXNrcy4gVGhlc2UgZmluZGluZ3MgdW5kZXJzY29yZSB0aGUgaW1wb3J0YW5jZSBvZiBjb21wcmVoZW5zaXZlLCBpbmRpdmlkdWFsaXplZCByaXNrIGFzc2Vzc21lbnRzIGFuZCBwcm9hY3RpdmUgaW50ZXJ2ZW50aW9uIHN0cmF0ZWdpZXMuIFRoZSBzY2FsYWJsZSBtZXRob2RvbG9neSBwcmVzZW50ZWQgaW4gdGhpcyByZXNlYXJjaCBwcm92aWRlcyBhIHZhbHVhYmxlIGZyYW1ld29yayBmb3IgZWFybHkgcmlzayBpZGVudGlmaWNhdGlvbiBhbmQgdGFyZ2V0ZWQgaW50ZXJ2ZW50aW9ucywgdWx0aW1hdGVseSBjb250cmlidXRpbmcgdG8gaW1wcm92ZWQgbWF0ZXJuYWwgaGVhbHRoIG91dGNvbWVzIGluIHZ1bG5lcmFibGUgcG9wdWxhdGlvbnMuDQoNCg0KIyMjIFJlZmVyZW5jZXMNCg0KLU92ZXJzYW1wbGluZyBhbmQgdW5kZXJzYW1wbGluZyBpbiBpbWJhbGFuY2VkIGRhdGE6DQoNCmh0dHBzOi8vbWVkaXVtLmNvbS9tZXRhb3ItYXJ0aWZpY2lhbC1pbnRlbGxpZ2VuY2Uvc29sdmluZy10aGUtY2xhc3MtaW1iYWxhbmNlLXByb2JsZW0tNThjYjkyNmI1YTBmIzp+OnRleHQ9UmVzYW1wbGluZyUyMGlzJTIwYSUyMGNvbW1vbiUyMHRlY2huaXF1ZSxleGFtcGxlcyUyMGZyb20lMjB0aGUlMjBvcmlnaW5hbCUyMGRhdGFzZXQuDQo=