Step 1: Business Understanding
Purpose of the Dataset
We are using the Paris Airbnb dataset, which was likely collected to
provide insights into short-term rental properties in Paris, including
availability, pricing, and other property-related features. This dataset
helps us understand the rental market by analyzing factors such as
pricing and location. This data can be useful for Airbnb hosts to
optimize their listings, renters to find suitable properties, and Airbnb
itself to gain valuable market insights.
Defining Outcomes
We define our outcomes by predicting which factors influence rental
prices the most or by identifying what features lead to higher booking
rates. Success in mining this dataset will be determined by our ability
to accurately predict these outcomes using a machine learning model.
Specifically, we will focus on developing a prediction algorithm to
forecast the price of rentals based on various attributes like location,
room type, and availability.
Measuring Effectiveness
The effectiveness of our prediction algorithms will be measured using
metrics like Mean Absolute Error (MAE) or Root
Mean Squared Error (RMSE) for regression models (price
prediction). For classification tasks, such as predicting availability
or popularity, we will measure effectiveness using
Accuracy or AUC-ROC.
Step 2: Data Understanding
- Describe Data Attributes
Our dataset contains several attributes, which we describe as follows:
listing_id
: Unique identifier for each listing.
name
: Name of the Airbnb listing.
host_id
: Identifier for the host.
price
: The cost per night of the listing
(numerical).
room_type
: The type of room (categorical: e.g., Entire
home/apt, Private room, etc.).
neighbourhood
: The area in Paris (categorical).
reviews
: The number of reviews (numerical).
availability
: Number of days the listing is available
in a year (numerical).
- Verify Data Quality
We need to check the dataset for missing values, duplicates, and
outliers, and decide how to address these issues.
Data Quality
Missing Values
We will identify and handle any missing values using the following
approach:
# Check for missing values
missing_data = df.isnull().sum()
# Visualize missing data (optional)
import seaborn as sns
import matplotlib.pyplot as plt
sns.heatmap(df.isnull(), cbar=False)
plt.show()
Depending on the results, we will decide whether to drop rows with
missing values, impute missing values using the mean or median, or
ignore them if insignificant.
Duplicates
We will remove any duplicate entries using this code:
# Check for duplicates
duplicates = df[df.duplicated()]
df_cleaned = df.drop_duplicates()
Outliers
We will identify outliers using box plots for numerical features such as
price
. To detect and handle outliers, we might use the
interquartile range (IQR) method:
sns.boxplot(x=df['price'])
plt.show()
Simple Statistics
We will calculate basic statistics for the most important
attributes:
# Basic statistics
df.describe()
Step 3: Data Preprocessing and Visualization
Importing Libraries and Loading Data
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Load the dataset
df = pd.read_csv('paris_airbnb.csv')
# Initial overview
df.head()
Handling Missing Data
# Check for missing values
missing_values = df.isnull().sum()
print(missing_values)
# Fill missing values in 'price' column, if applicable
df['price'].fillna(df['price'].mean(), inplace=True)
Removing Duplicates
# Remove duplicate rows
df.drop_duplicates(inplace=True)
Descriptive Statistics
# Descriptive statistics for numerical columns
print(df.describe())
# Range, mode, mean, median, variance for price
price_range = df['price'].max() - df['price'].min()
price_mode = df['price'].mode()
price_mean = df['price'].mean()
price_median = df['price'].median()
price_variance = df['price'].var()
print(f"Price Range: {price_range}, Mode: {price_mode}, Mean: {price_mean}, Median: {price_median}, Variance: {price_variance}")
Step 4: Visualization of Important Attributes
We visualize the most important attributes like price
,
room_type
, and availability_365
.
- Price Distribution
plt.figure(figsize=(10,6))
sns.histplot(df['price'], bins=50, kde=True)
plt.title('Price Distribution of Airbnb Listings in Paris')
plt.xlabel('Price')
plt.ylabel('Frequency')
plt.show()
- Room Type Breakdown
plt.figure(figsize=(8,5))
sns.countplot(x='room_type', data=df)
plt.title('Distribution of Room Types')
plt.xlabel('Room Type')
plt.ylabel('Count')
plt.show()
- Availability of Listings
plt.figure(figsize=(10,6))
sns.histplot(df['availability'], bins=30, kde=True)
plt.title('Availability of Airbnb Listings in Paris')
plt.xlabel('Days Available')
plt.ylabel('Frequency')
plt.show()
Exploring Relationships Between Attributes
- Price vs. Room Type
plt.figure(figsize=(8,5))
sns.boxplot(x='room_type', y='price', data=df)
plt.title('Room Type vs. Price')
plt.xlabel('Room Type')
plt.ylabel('Price')
plt.show()
- Correlation Matrix
plt.figure(figsize=(10,6))
corr_matrix = df.corr()
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm')
plt.title('Correlation Matrix')
plt.show()
Step 5: Dimensionality Reduction
We use PCA (Principal Component Analysis) to reduce the
dimensionality of the dataset and visualize the results:
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# Standardize the features
features = ['price', 'number_of_reviews', 'availability_365']
x = df[features].values
x = StandardScaler().fit_transform(x)
# PCA
pca = PCA(n_components=2)
pca_result = pca.fit_transform(x)
# Visualize PCA result
plt.scatter(pca_result[:, 0], pca_result[:, 1])
plt.title('PCA of Airbnb Features')
plt.show()
Step 6: Exceptional Work - Creating Additional Features
We suggest adding new features to enhance our analysis:
- Price per person: Derived by dividing the price by
the number of guests a property accommodates.
- Luxury flag: Classify listings as luxury if their
price is in the top 10%.
# Example: Adding a feature for luxury listings
df['is_luxury'] = np.where(df['price'] > df['price'].quantile(0.9), 1, 0)
Final Step: Report
We will compile all of our findings and code into a well-documented
Jupyter notebook or a PDF/HTML report. The final report will include
explanations of each step, the Python code used, and conclusions drawn
from our analysis.
Would you like any further assistance in implementing specific
sections or preparing your final report?
LS0tDQp0aXRsZTogIkdyb3VwIFBhcmlzIC0gTGFiIDEgLSBNTDczMzEiICANCmF1dGhvcnM6ICJUcm95IE1jU2ltb3YsIEplc3NpY2EgTWNQaGF1bCwgVHJldm9yIEt1bnosIENocmlzdGlhbiBDYXN0cm8iICANCm91dHB1dDogaHRtbF9ub3RlYm9vayAgDQotLS0NCg0KIyMjIFN0ZXAgMTogQnVzaW5lc3MgVW5kZXJzdGFuZGluZw0KDQojIyMjIFB1cnBvc2Ugb2YgdGhlIERhdGFzZXQNCldlIGFyZSB1c2luZyB0aGUgUGFyaXMgQWlyYm5iIGRhdGFzZXQsIHdoaWNoIHdhcyBsaWtlbHkgY29sbGVjdGVkIHRvIHByb3ZpZGUgaW5zaWdodHMgaW50byBzaG9ydC10ZXJtIHJlbnRhbCBwcm9wZXJ0aWVzIGluIFBhcmlzLCBpbmNsdWRpbmcgYXZhaWxhYmlsaXR5LCBwcmljaW5nLCBhbmQgb3RoZXIgcHJvcGVydHktcmVsYXRlZCBmZWF0dXJlcy4gVGhpcyBkYXRhc2V0IGhlbHBzIHVzIHVuZGVyc3RhbmQgdGhlIHJlbnRhbCBtYXJrZXQgYnkgYW5hbHl6aW5nIGZhY3RvcnMgc3VjaCBhcyBwcmljaW5nIGFuZCBsb2NhdGlvbi4gVGhpcyBkYXRhIGNhbiBiZSB1c2VmdWwgZm9yIEFpcmJuYiBob3N0cyB0byBvcHRpbWl6ZSB0aGVpciBsaXN0aW5ncywgcmVudGVycyB0byBmaW5kIHN1aXRhYmxlIHByb3BlcnRpZXMsIGFuZCBBaXJibmIgaXRzZWxmIHRvIGdhaW4gdmFsdWFibGUgbWFya2V0IGluc2lnaHRzLg0KDQojIyMjIERlZmluaW5nIE91dGNvbWVzDQpXZSBkZWZpbmUgb3VyIG91dGNvbWVzIGJ5IHByZWRpY3Rpbmcgd2hpY2ggZmFjdG9ycyBpbmZsdWVuY2UgcmVudGFsIHByaWNlcyB0aGUgbW9zdCBvciBieSBpZGVudGlmeWluZyB3aGF0IGZlYXR1cmVzIGxlYWQgdG8gaGlnaGVyIGJvb2tpbmcgcmF0ZXMuIFN1Y2Nlc3MgaW4gbWluaW5nIHRoaXMgZGF0YXNldCB3aWxsIGJlIGRldGVybWluZWQgYnkgb3VyIGFiaWxpdHkgdG8gYWNjdXJhdGVseSBwcmVkaWN0IHRoZXNlIG91dGNvbWVzIHVzaW5nIGEgbWFjaGluZSBsZWFybmluZyBtb2RlbC4gU3BlY2lmaWNhbGx5LCB3ZSB3aWxsIGZvY3VzIG9uIGRldmVsb3BpbmcgYSBwcmVkaWN0aW9uIGFsZ29yaXRobSB0byBmb3JlY2FzdCB0aGUgcHJpY2Ugb2YgcmVudGFscyBiYXNlZCBvbiB2YXJpb3VzIGF0dHJpYnV0ZXMgbGlrZSBsb2NhdGlvbiwgcm9vbSB0eXBlLCBhbmQgYXZhaWxhYmlsaXR5Lg0KDQojIyMjIE1lYXN1cmluZyBFZmZlY3RpdmVuZXNzDQpUaGUgZWZmZWN0aXZlbmVzcyBvZiBvdXIgcHJlZGljdGlvbiBhbGdvcml0aG1zIHdpbGwgYmUgbWVhc3VyZWQgdXNpbmcgbWV0cmljcyBsaWtlICoqTWVhbiBBYnNvbHV0ZSBFcnJvciAoTUFFKSoqIG9yICoqUm9vdCBNZWFuIFNxdWFyZWQgRXJyb3IgKFJNU0UpKiogZm9yIHJlZ3Jlc3Npb24gbW9kZWxzIChwcmljZSBwcmVkaWN0aW9uKS4gRm9yIGNsYXNzaWZpY2F0aW9uIHRhc2tzLCBzdWNoIGFzIHByZWRpY3RpbmcgYXZhaWxhYmlsaXR5IG9yIHBvcHVsYXJpdHksIHdlIHdpbGwgbWVhc3VyZSBlZmZlY3RpdmVuZXNzIHVzaW5nICoqQWNjdXJhY3kqKiBvciAqKkFVQy1ST0MqKi4NCg0KLS0tDQoNCiMjIyBTdGVwIDI6IERhdGEgVW5kZXJzdGFuZGluZw0KDQoxLiAqKkRlc2NyaWJlIERhdGEgQXR0cmlidXRlcyoqICANCiAgIE91ciBkYXRhc2V0IGNvbnRhaW5zIHNldmVyYWwgYXR0cmlidXRlcywgd2hpY2ggd2UgZGVzY3JpYmUgYXMgZm9sbG93czoNCiAgIC0gYGxpc3RpbmdfaWRgOiBVbmlxdWUgaWRlbnRpZmllciBmb3IgZWFjaCBsaXN0aW5nLg0KICAgLSBgbmFtZWA6IE5hbWUgb2YgdGhlIEFpcmJuYiBsaXN0aW5nLg0KICAgLSBgaG9zdF9pZGA6IElkZW50aWZpZXIgZm9yIHRoZSBob3N0Lg0KICAgLSBgcHJpY2VgOiBUaGUgY29zdCBwZXIgbmlnaHQgb2YgdGhlIGxpc3RpbmcgKG51bWVyaWNhbCkuDQogICAtIGByb29tX3R5cGVgOiBUaGUgdHlwZSBvZiByb29tIChjYXRlZ29yaWNhbDogZS5nLiwgRW50aXJlIGhvbWUvYXB0LCBQcml2YXRlIHJvb20sIGV0Yy4pLg0KICAgLSBgbmVpZ2hib3VyaG9vZGA6IFRoZSBhcmVhIGluIFBhcmlzIChjYXRlZ29yaWNhbCkuDQogICAtIGByZXZpZXdzYDogVGhlIG51bWJlciBvZiByZXZpZXdzIChudW1lcmljYWwpLg0KICAgLSBgYXZhaWxhYmlsaXR5YDogTnVtYmVyIG9mIGRheXMgdGhlIGxpc3RpbmcgaXMgYXZhaWxhYmxlIGluIGEgeWVhciAobnVtZXJpY2FsKS4NCg0KMi4gKipWZXJpZnkgRGF0YSBRdWFsaXR5KiogIA0KICAgV2UgbmVlZCB0byBjaGVjayB0aGUgZGF0YXNldCBmb3IgbWlzc2luZyB2YWx1ZXMsIGR1cGxpY2F0ZXMsIGFuZCBvdXRsaWVycywgYW5kIGRlY2lkZSBob3cgdG8gYWRkcmVzcyB0aGVzZSBpc3N1ZXMuDQoNCiMjIyMgRGF0YSBRdWFsaXR5DQotICoqTWlzc2luZyBWYWx1ZXMqKiAgDQogIFdlIHdpbGwgaWRlbnRpZnkgYW5kIGhhbmRsZSBhbnkgbWlzc2luZyB2YWx1ZXMgdXNpbmcgdGhlIGZvbGxvd2luZyBhcHByb2FjaDoNCiAgDQogIGBgYHB5dGhvbg0KICAjIENoZWNrIGZvciBtaXNzaW5nIHZhbHVlcw0KICBtaXNzaW5nX2RhdGEgPSBkZi5pc251bGwoKS5zdW0oKQ0KICANCiAgIyBWaXN1YWxpemUgbWlzc2luZyBkYXRhIChvcHRpb25hbCkNCiAgaW1wb3J0IHNlYWJvcm4gYXMgc25zDQogIGltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCiAgDQogIHNucy5oZWF0bWFwKGRmLmlzbnVsbCgpLCBjYmFyPUZhbHNlKQ0KICBwbHQuc2hvdygpDQogIGBgYA0KDQogIERlcGVuZGluZyBvbiB0aGUgcmVzdWx0cywgd2Ugd2lsbCBkZWNpZGUgd2hldGhlciB0byBkcm9wIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcywgaW1wdXRlIG1pc3NpbmcgdmFsdWVzIHVzaW5nIHRoZSBtZWFuIG9yIG1lZGlhbiwgb3IgaWdub3JlIHRoZW0gaWYgaW5zaWduaWZpY2FudC4NCg0KLSAqKkR1cGxpY2F0ZXMqKiAgDQogIFdlIHdpbGwgcmVtb3ZlIGFueSBkdXBsaWNhdGUgZW50cmllcyB1c2luZyB0aGlzIGNvZGU6DQogIA0KICBgYGBweXRob24NCiAgIyBDaGVjayBmb3IgZHVwbGljYXRlcw0KICBkdXBsaWNhdGVzID0gZGZbZGYuZHVwbGljYXRlZCgpXQ0KICBkZl9jbGVhbmVkID0gZGYuZHJvcF9kdXBsaWNhdGVzKCkNCiAgYGBgDQoNCi0gKipPdXRsaWVycyoqICANCiAgV2Ugd2lsbCBpZGVudGlmeSBvdXRsaWVycyB1c2luZyBib3ggcGxvdHMgZm9yIG51bWVyaWNhbCBmZWF0dXJlcyBzdWNoIGFzIGBwcmljZWAuIFRvIGRldGVjdCBhbmQgaGFuZGxlIG91dGxpZXJzLCB3ZSBtaWdodCB1c2UgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UgKElRUikgbWV0aG9kOg0KICANCiAgYGBgcHl0aG9uDQogIHNucy5ib3hwbG90KHg9ZGZbJ3ByaWNlJ10pDQogIHBsdC5zaG93KCkNCiAgYGBgDQoNCiMjIyMgU2ltcGxlIFN0YXRpc3RpY3MNCldlIHdpbGwgY2FsY3VsYXRlIGJhc2ljIHN0YXRpc3RpY3MgZm9yIHRoZSBtb3N0IGltcG9ydGFudCBhdHRyaWJ1dGVzOg0KICANCmBgYHB5dGhvbg0KIyBCYXNpYyBzdGF0aXN0aWNzDQpkZi5kZXNjcmliZSgpDQpgYGANCg0KLS0tDQoNCiMjIyBTdGVwIDM6IERhdGEgUHJlcHJvY2Vzc2luZyBhbmQgVmlzdWFsaXphdGlvbg0KDQojIyMjIEltcG9ydGluZyBMaWJyYXJpZXMgYW5kIExvYWRpbmcgRGF0YQ0KDQpgYGBweXRob24NCmltcG9ydCBwYW5kYXMgYXMgcGQNCmltcG9ydCBudW1weSBhcyBucA0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KaW1wb3J0IHNlYWJvcm4gYXMgc25zDQoNCiMgTG9hZCB0aGUgZGF0YXNldA0KZGYgPSBwZC5yZWFkX2NzdigncGFyaXNfYWlyYm5iLmNzdicpDQoNCiMgSW5pdGlhbCBvdmVydmlldw0KZGYuaGVhZCgpDQpgYGANCg0KIyMjIyBIYW5kbGluZyBNaXNzaW5nIERhdGENCg0KYGBgcHl0aG9uDQojIENoZWNrIGZvciBtaXNzaW5nIHZhbHVlcw0KbWlzc2luZ192YWx1ZXMgPSBkZi5pc251bGwoKS5zdW0oKQ0KcHJpbnQobWlzc2luZ192YWx1ZXMpDQoNCiMgRmlsbCBtaXNzaW5nIHZhbHVlcyBpbiAncHJpY2UnIGNvbHVtbiwgaWYgYXBwbGljYWJsZQ0KZGZbJ3ByaWNlJ10uZmlsbG5hKGRmWydwcmljZSddLm1lYW4oKSwgaW5wbGFjZT1UcnVlKQ0KYGBgDQoNCiMjIyMgUmVtb3ZpbmcgRHVwbGljYXRlcw0KDQpgYGBweXRob24NCiMgUmVtb3ZlIGR1cGxpY2F0ZSByb3dzDQpkZi5kcm9wX2R1cGxpY2F0ZXMoaW5wbGFjZT1UcnVlKQ0KYGBgDQoNCiMjIyMgRGVzY3JpcHRpdmUgU3RhdGlzdGljcw0KDQpgYGBweXRob24NCiMgRGVzY3JpcHRpdmUgc3RhdGlzdGljcyBmb3IgbnVtZXJpY2FsIGNvbHVtbnMNCnByaW50KGRmLmRlc2NyaWJlKCkpDQoNCiMgUmFuZ2UsIG1vZGUsIG1lYW4sIG1lZGlhbiwgdmFyaWFuY2UgZm9yIHByaWNlDQpwcmljZV9yYW5nZSA9IGRmWydwcmljZSddLm1heCgpIC0gZGZbJ3ByaWNlJ10ubWluKCkNCnByaWNlX21vZGUgPSBkZlsncHJpY2UnXS5tb2RlKCkNCnByaWNlX21lYW4gPSBkZlsncHJpY2UnXS5tZWFuKCkNCnByaWNlX21lZGlhbiA9IGRmWydwcmljZSddLm1lZGlhbigpDQpwcmljZV92YXJpYW5jZSA9IGRmWydwcmljZSddLnZhcigpDQoNCnByaW50KGYiUHJpY2UgUmFuZ2U6IHtwcmljZV9yYW5nZX0sIE1vZGU6IHtwcmljZV9tb2RlfSwgTWVhbjoge3ByaWNlX21lYW59LCBNZWRpYW46IHtwcmljZV9tZWRpYW59LCBWYXJpYW5jZToge3ByaWNlX3ZhcmlhbmNlfSIpDQpgYGANCg0KLS0tDQoNCiMjIyBTdGVwIDQ6IFZpc3VhbGl6YXRpb24gb2YgSW1wb3J0YW50IEF0dHJpYnV0ZXMNCg0KV2UgdmlzdWFsaXplIHRoZSBtb3N0IGltcG9ydGFudCBhdHRyaWJ1dGVzIGxpa2UgYHByaWNlYCwgYHJvb21fdHlwZWAsIGFuZCBgYXZhaWxhYmlsaXR5XzM2NWAuDQoNCjEuICoqUHJpY2UgRGlzdHJpYnV0aW9uKioNCg0KYGBgcHl0aG9uDQpwbHQuZmlndXJlKGZpZ3NpemU9KDEwLDYpKQ0Kc25zLmhpc3RwbG90KGRmWydwcmljZSddLCBiaW5zPTUwLCBrZGU9VHJ1ZSkNCnBsdC50aXRsZSgnUHJpY2UgRGlzdHJpYnV0aW9uIG9mIEFpcmJuYiBMaXN0aW5ncyBpbiBQYXJpcycpDQpwbHQueGxhYmVsKCdQcmljZScpDQpwbHQueWxhYmVsKCdGcmVxdWVuY3knKQ0KcGx0LnNob3coKQ0KYGBgDQoNCjIuICoqUm9vbSBUeXBlIEJyZWFrZG93bioqDQoNCmBgYHB5dGhvbg0KcGx0LmZpZ3VyZShmaWdzaXplPSg4LDUpKQ0Kc25zLmNvdW50cGxvdCh4PSdyb29tX3R5cGUnLCBkYXRhPWRmKQ0KcGx0LnRpdGxlKCdEaXN0cmlidXRpb24gb2YgUm9vbSBUeXBlcycpDQpwbHQueGxhYmVsKCdSb29tIFR5cGUnKQ0KcGx0LnlsYWJlbCgnQ291bnQnKQ0KcGx0LnNob3coKQ0KYGBgDQoNCjMuICoqQXZhaWxhYmlsaXR5IG9mIExpc3RpbmdzKioNCg0KYGBgcHl0aG9uDQpwbHQuZmlndXJlKGZpZ3NpemU9KDEwLDYpKQ0Kc25zLmhpc3RwbG90KGRmWydhdmFpbGFiaWxpdHknXSwgYmlucz0zMCwga2RlPVRydWUpDQpwbHQudGl0bGUoJ0F2YWlsYWJpbGl0eSBvZiBBaXJibmIgTGlzdGluZ3MgaW4gUGFyaXMnKQ0KcGx0LnhsYWJlbCgnRGF5cyBBdmFpbGFibGUnKQ0KcGx0LnlsYWJlbCgnRnJlcXVlbmN5JykNCnBsdC5zaG93KCkNCmBgYA0KDQojIyMjIEV4cGxvcmluZyBSZWxhdGlvbnNoaXBzIEJldHdlZW4gQXR0cmlidXRlcw0KDQoxLiAqKlByaWNlIHZzLiBSb29tIFR5cGUqKg0KDQpgYGBweXRob24NCnBsdC5maWd1cmUoZmlnc2l6ZT0oOCw1KSkNCnNucy5ib3hwbG90KHg9J3Jvb21fdHlwZScsIHk9J3ByaWNlJywgZGF0YT1kZikNCnBsdC50aXRsZSgnUm9vbSBUeXBlIHZzLiBQcmljZScpDQpwbHQueGxhYmVsKCdSb29tIFR5cGUnKQ0KcGx0LnlsYWJlbCgnUHJpY2UnKQ0KcGx0LnNob3coKQ0KYGBgDQoNCjIuICoqQ29ycmVsYXRpb24gTWF0cml4KioNCg0KYGBgcHl0aG9uDQpwbHQuZmlndXJlKGZpZ3NpemU9KDEwLDYpKQ0KY29ycl9tYXRyaXggPSBkZi5jb3JyKCkNCnNucy5oZWF0bWFwKGNvcnJfbWF0cml4LCBhbm5vdD1UcnVlLCBjbWFwPSdjb29sd2FybScpDQpwbHQudGl0bGUoJ0NvcnJlbGF0aW9uIE1hdHJpeCcpDQpwbHQuc2hvdygpDQpgYGANCg0KLS0tDQoNCiMjIyBTdGVwIDU6IERpbWVuc2lvbmFsaXR5IFJlZHVjdGlvbg0KDQpXZSB1c2UgUENBIChQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzKSB0byByZWR1Y2UgdGhlIGRpbWVuc2lvbmFsaXR5IG9mIHRoZSBkYXRhc2V0IGFuZCB2aXN1YWxpemUgdGhlIHJlc3VsdHM6DQoNCmBgYHB5dGhvbg0KZnJvbSBza2xlYXJuLmRlY29tcG9zaXRpb24gaW1wb3J0IFBDQQ0KZnJvbSBza2xlYXJuLnByZXByb2Nlc3NpbmcgaW1wb3J0IFN0YW5kYXJkU2NhbGVyDQoNCiMgU3RhbmRhcmRpemUgdGhlIGZlYXR1cmVzDQpmZWF0dXJlcyA9IFsncHJpY2UnLCAnbnVtYmVyX29mX3Jldmlld3MnLCAnYXZhaWxhYmlsaXR5XzM2NSddDQp4ID0gZGZbZmVhdHVyZXNdLnZhbHVlcw0KeCA9IFN0YW5kYXJkU2NhbGVyKCkuZml0X3RyYW5zZm9ybSh4KQ0KDQojIFBDQQ0KcGNhID0gUENBKG5fY29tcG9uZW50cz0yKQ0KcGNhX3Jlc3VsdCA9IHBjYS5maXRfdHJhbnNmb3JtKHgpDQoNCiMgVmlzdWFsaXplIFBDQSByZXN1bHQNCnBsdC5zY2F0dGVyKHBjYV9yZXN1bHRbOiwgMF0sIHBjYV9yZXN1bHRbOiwgMV0pDQpwbHQudGl0bGUoJ1BDQSBvZiBBaXJibmIgRmVhdHVyZXMnKQ0KcGx0LnNob3coKQ0KYGBgDQoNCi0tLQ0KDQojIyMgU3RlcCA2OiBFeGNlcHRpb25hbCBXb3JrIC0gQ3JlYXRpbmcgQWRkaXRpb25hbCBGZWF0dXJlcw0KDQpXZSBzdWdnZXN0IGFkZGluZyBuZXcgZmVhdHVyZXMgdG8gZW5oYW5jZSBvdXIgYW5hbHlzaXM6DQogIA0KLSAqKlByaWNlIHBlciBwZXJzb246KiogRGVyaXZlZCBieSBkaXZpZGluZyB0aGUgcHJpY2UgYnkgdGhlIG51bWJlciBvZiBndWVzdHMgYSBwcm9wZXJ0eSBhY2NvbW1vZGF0ZXMuDQotICoqTHV4dXJ5IGZsYWc6KiogQ2xhc3NpZnkgbGlzdGluZ3MgYXMgbHV4dXJ5IGlmIHRoZWlyIHByaWNlIGlzIGluIHRoZSB0b3AgMTAlLg0KDQpgYGBweXRob24NCiMgRXhhbXBsZTogQWRkaW5nIGEgZmVhdHVyZSBmb3IgbHV4dXJ5IGxpc3RpbmdzDQpkZlsnaXNfbHV4dXJ5J10gPSBucC53aGVyZShkZlsncHJpY2UnXSA+IGRmWydwcmljZSddLnF1YW50aWxlKDAuOSksIDEsIDApDQpgYGANCg0KLS0tDQoNCiMjIyBGaW5hbCBTdGVwOiBSZXBvcnQNCldlIHdpbGwgY29tcGlsZSBhbGwgb2Ygb3VyIGZpbmRpbmdzIGFuZCBjb2RlIGludG8gYSB3ZWxsLWRvY3VtZW50ZWQgSnVweXRlciBub3RlYm9vayBvciBhIFBERi9IVE1MIHJlcG9ydC4gVGhlIGZpbmFsIHJlcG9ydCB3aWxsIGluY2x1ZGUgZXhwbGFuYXRpb25zIG9mIGVhY2ggc3RlcCwgdGhlIFB5dGhvbiBjb2RlIHVzZWQsIGFuZCBjb25jbHVzaW9ucyBkcmF3biBmcm9tIG91ciBhbmFseXNpcy4NCg0KV291bGQgeW91IGxpa2UgYW55IGZ1cnRoZXIgYXNzaXN0YW5jZSBpbiBpbXBsZW1lbnRpbmcgc3BlY2lmaWMgc2VjdGlvbnMgb3IgcHJlcGFyaW5nIHlvdXIgZmluYWwgcmVwb3J0Pw==