library(tidyverse)
library(ggplot2)
library(gghighlight)

#Importing the csv file to a books dataframe
books <- read.csv("bestsellers with categories.csv")

#Viewing the dataframe
head(books)

#Identifying the column names
names(books)
[1] "Name"        "Author"      "User.Rating" "Reviews"     "Price"      
[6] "Year"        "Genre"      

Correcting Formatting Issues

# Change the column name from User.Rating to User_Rating
names(books)[names(books) == "User.Rating"] <- "User_Rating"

Missing Values

#Identifying total number of missing values
sum(is.na(books))
[1] 0

There are no missing values in the data.

Statistics

#Identifying Number of books
total_books = count(books)

print(paste("There are",total_books,"books in total."))
[1] "There are 550 books in total."
#Identifying Number of books in each genre
count(books,Genre)
#Identifying the average user rating, rounded to 2 decimal places
avg_rating <- round(mean(books$User_Rating),digits =2)

#Identifying the average price, rounded to 2 decimal places
avg_price <- round(mean(books$Price),digits =2)

#Identifying the average number of reviews, rounded to 2 decimal places
avg_reviews <- round(mean(books$Reviews),digits =0)

#Identifying the highest user rating
max_rating <- max(books$User_Rating)

#Identifying the highest price
max_price <- max(books$Price)

#Identifying the highest price
max_reviews<- max(books$Reviews)

cat("For books in the Bestseller list: \nAverage user rating is",avg_rating,"and the highest user rating is",max_rating,"\nAverage price of $",avg_price,"and the highest price is $",max_price, "\nAverage number of reviews is",avg_reviews,"and the highest number of reviews is",max_reviews)
For books in the Bestseller list: 
Average user rating is 4.62 and the highest user rating is 4.9 
Average price of $ 13.1 and the highest price is $ 105 
Average number of reviews is 11953 and the highest number of reviews is 87841
#Grouping by genre
genre <- books %>% group_by(Genre)

#Summarizing average price for for books each year in each genre
genre_stats <- genre %>% summarise(Price = mean(Price))

#Renaming the price column to Average_price
colnames(genre_stats)[which(names(genre_stats) == "Price")] <- "Average_price"

#Summarizing average number of reviews for for books each year in each genre
genre_avg_reviews <- genre %>% summarise(Reviews = mean(Reviews))

#Adding the "Reviews" column from "genre_avg_reviews" to the genre dataframe as "average_reviews" 
genre_stats$Average_number_of_reviews <- genre_avg_reviews$Reviews

#Summarizing average user rating for for books each year in each genre
genre_avg_rating <- genre %>% summarise(User_Rating = mean(User_Rating))

genre_stats$Average_user_rating <- genre_avg_rating$User_Rating

genre_stats

On average,

Plots

User Rating

#plotting bar graph for user rating vs. number of reviews
ggplot(data=books,aes(x=User_Rating,y=Reviews)) + geom_bar(stat="identity") + labs(title="User Rating vs. Number of Reviews")

  • Books that receive a higher user rating recieve more reviews.
  • There is a significant increase in the number of reviews for books that have a rating of 4.5 and above.
#Grouping by user rating
by_rating_price <- books %>% group_by(User_Rating)

#Summarizing average price for each user rating value
by_rating_price <- by_rating_price %>% summarise(Price = mean(Price))

#plotting bar graph for user rating vs. price
ggplot(data=by_rating_price,aes(x=User_Rating,y=Price)) + geom_col() + labs(title="User Rating vs. Average Price")

On average, books with a user rating of 4.5 tend to be the most expensive.

Price

#plotting scatter plot for price vs. number of reviews
##geom_point() creates a scatter plot
#facet_wrap(~Genre) groups the data by Genre
#gghighlight(Reviews>25000) will highlight books with more than 25,000 reviews 
ggplot(data=books,aes(x=Price,y=Reviews)) + geom_point() + labs(title="Price vs. Number of Reviews") + gghighlight(Price<=25)

Generally, books priced at or under $25 receive the most reviews.

#plotting scatter plot for price vs. number of reviews
#Highlighting books with more than 25,000 reviews 
ggplot(data=books,aes(x=Price,y=Reviews)) + geom_point() + facet_wrap(~Genre) + labs(title="Price vs. Number of Reviews by Genre") + gghighlight(Reviews>25000)

Highlighted above are the books that received over 25,000 reviews.

Year

#Grouping by year
by_year <- books %>% group_by(Year)

#Summarizing average price for for books each year
by_year_price <- by_year %>% summarise(Price = mean(Price))

#Summarizing average price for for books each year
by_year_reviews <- by_year %>% summarise(Reviews = mean(Reviews))

#plotting bar graph for year vs. average number of reviews
#You have to specify stat="identity" for this kind of dataset.
#labs(title=" ") sets the title
ggplot(data=by_year_reviews,aes(x=Year,y=Reviews)) + geom_col() + labs(title="Average Number of Reviews Per Year")

After mid-2011 the average number of reviews received increased significantly.

#plotting bar graph for year vs. average price
#You have to specify stat="identity" for this kind of dataset.
#labs(title=" ") sets the title
ggplot(data=by_year_price,aes(x=Year,y=Price)) + geom_col() + labs(title="Average Price Per Year")

The average price for an Amazon Top 50 Bestseller has remained between $10 to $15 between 2009-2019.

Summary of Data Analysis

There are 550 books in total.

For books on the Bestseller list in the period from 2009-2019,

Relationship between Price, User Rating, Number of Reviews and Year of Release

  • The average price for an Amazon Top 50 Bestseller has remained between $10 to $15 between 2009-2019.
  • Books priced at or under $25 receive the most reviews, regarless of genre.
  • After mid-2011 the average number of reviews received increased significantly.
  • Books that have a rating of 4.5 and above receive significantly higher reviews.
  • The majority of books receive less than 25,000 reviews.
  • Books that receive a higher user rating received more reviews.
  • Books with a user rating of 4.5 tend to have the highest average price.
LS0tCnRpdGxlOiAiQW1hem9uIEJlc3RzZWxsZXJzIERhdGEgQW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdnaGlnaGxpZ2h0KQoKI0ltcG9ydGluZyB0aGUgY3N2IGZpbGUgdG8gYSBib29rcyBkYXRhZnJhbWUKYm9va3MgPC0gcmVhZC5jc3YoImJlc3RzZWxsZXJzIHdpdGggY2F0ZWdvcmllcy5jc3YiKQoKI1ZpZXdpbmcgdGhlIGRhdGFmcmFtZQpoZWFkKGJvb2tzKQoKI0lkZW50aWZ5aW5nIHRoZSBjb2x1bW4gbmFtZXMKbmFtZXMoYm9va3MpCmBgYAojIyMgQ29ycmVjdGluZyBGb3JtYXR0aW5nIElzc3VlcwpgYGB7cn0KIyBDaGFuZ2UgdGhlIGNvbHVtbiBuYW1lIGZyb20gVXNlci5SYXRpbmcgdG8gVXNlcl9SYXRpbmcKbmFtZXMoYm9va3MpW25hbWVzKGJvb2tzKSA9PSAiVXNlci5SYXRpbmciXSA8LSAiVXNlcl9SYXRpbmciCmBgYAoKIyMjIE1pc3NpbmcgVmFsdWVzCmBgYHtyfQojSWRlbnRpZnlpbmcgdG90YWwgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzCnN1bShpcy5uYShib29rcykpCmBgYApUaGVyZSBhcmUgbm8gbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGRhdGEuCgoKIyMgU3RhdGlzdGljcwpgYGB7cn0KI0lkZW50aWZ5aW5nIE51bWJlciBvZiBib29rcwp0b3RhbF9ib29rcyA9IGNvdW50KGJvb2tzKQoKcHJpbnQocGFzdGUoIlRoZXJlIGFyZSIsdG90YWxfYm9va3MsImJvb2tzIGluIHRvdGFsLiIpKQoKI0lkZW50aWZ5aW5nIE51bWJlciBvZiBib29rcyBpbiBlYWNoIGdlbnJlCmNvdW50KGJvb2tzLEdlbnJlKQpgYGAKYGBge3J9CiNJZGVudGlmeWluZyB0aGUgYXZlcmFnZSB1c2VyIHJhdGluZywgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzCmF2Z19yYXRpbmcgPC0gcm91bmQobWVhbihib29rcyRVc2VyX1JhdGluZyksZGlnaXRzID0yKQoKI0lkZW50aWZ5aW5nIHRoZSBhdmVyYWdlIHByaWNlLCByb3VuZGVkIHRvIDIgZGVjaW1hbCBwbGFjZXMKYXZnX3ByaWNlIDwtIHJvdW5kKG1lYW4oYm9va3MkUHJpY2UpLGRpZ2l0cyA9MikKCiNJZGVudGlmeWluZyB0aGUgYXZlcmFnZSBudW1iZXIgb2YgcmV2aWV3cywgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzCmF2Z19yZXZpZXdzIDwtIHJvdW5kKG1lYW4oYm9va3MkUmV2aWV3cyksZGlnaXRzID0wKQoKI0lkZW50aWZ5aW5nIHRoZSBoaWdoZXN0IHVzZXIgcmF0aW5nCm1heF9yYXRpbmcgPC0gbWF4KGJvb2tzJFVzZXJfUmF0aW5nKQoKI0lkZW50aWZ5aW5nIHRoZSBoaWdoZXN0IHByaWNlCm1heF9wcmljZSA8LSBtYXgoYm9va3MkUHJpY2UpCgojSWRlbnRpZnlpbmcgdGhlIGhpZ2hlc3QgcHJpY2UKbWF4X3Jldmlld3M8LSBtYXgoYm9va3MkUmV2aWV3cykKCmNhdCgiRm9yIGJvb2tzIGluIHRoZSBCZXN0c2VsbGVyIGxpc3Q6IFxuQXZlcmFnZSB1c2VyIHJhdGluZyBpcyIsYXZnX3JhdGluZywiYW5kIHRoZSBoaWdoZXN0IHVzZXIgcmF0aW5nIGlzIixtYXhfcmF0aW5nLCJcbkF2ZXJhZ2UgcHJpY2Ugb2YgJCIsYXZnX3ByaWNlLCJhbmQgdGhlIGhpZ2hlc3QgcHJpY2UgaXMgJCIsbWF4X3ByaWNlLCAiXG5BdmVyYWdlIG51bWJlciBvZiByZXZpZXdzIGlzIixhdmdfcmV2aWV3cywiYW5kIHRoZSBoaWdoZXN0IG51bWJlciBvZiByZXZpZXdzIGlzIixtYXhfcmV2aWV3cykKYGBgCgpgYGB7cn0KI0dyb3VwaW5nIGJ5IGdlbnJlCmdlbnJlIDwtIGJvb2tzICU+JSBncm91cF9ieShHZW5yZSkKCiNTdW1tYXJpemluZyBhdmVyYWdlIHByaWNlIGZvciBmb3IgYm9va3MgZWFjaCB5ZWFyIGluIGVhY2ggZ2VucmUKZ2VucmVfc3RhdHMgPC0gZ2VucmUgJT4lIHN1bW1hcmlzZShQcmljZSA9IG1lYW4oUHJpY2UpKQoKI1JlbmFtaW5nIHRoZSBwcmljZSBjb2x1bW4gdG8gQXZlcmFnZV9wcmljZQpjb2xuYW1lcyhnZW5yZV9zdGF0cylbd2hpY2gobmFtZXMoZ2VucmVfc3RhdHMpID09ICJQcmljZSIpXSA8LSAiQXZlcmFnZV9wcmljZSIKCiNTdW1tYXJpemluZyBhdmVyYWdlIG51bWJlciBvZiByZXZpZXdzIGZvciBmb3IgYm9va3MgZWFjaCB5ZWFyIGluIGVhY2ggZ2VucmUKZ2VucmVfYXZnX3Jldmlld3MgPC0gZ2VucmUgJT4lIHN1bW1hcmlzZShSZXZpZXdzID0gbWVhbihSZXZpZXdzKSkKCiNBZGRpbmcgdGhlICJSZXZpZXdzIiBjb2x1bW4gZnJvbSAiZ2VucmVfYXZnX3Jldmlld3MiIHRvIHRoZSBnZW5yZSBkYXRhZnJhbWUgYXMgImF2ZXJhZ2VfcmV2aWV3cyIgCmdlbnJlX3N0YXRzJEF2ZXJhZ2VfbnVtYmVyX29mX3Jldmlld3MgPC0gZ2VucmVfYXZnX3Jldmlld3MkUmV2aWV3cwoKI1N1bW1hcml6aW5nIGF2ZXJhZ2UgdXNlciByYXRpbmcgZm9yIGZvciBib29rcyBlYWNoIHllYXIgaW4gZWFjaCBnZW5yZQpnZW5yZV9hdmdfcmF0aW5nIDwtIGdlbnJlICU+JSBzdW1tYXJpc2UoVXNlcl9SYXRpbmcgPSBtZWFuKFVzZXJfUmF0aW5nKSkKCmdlbnJlX3N0YXRzJEF2ZXJhZ2VfdXNlcl9yYXRpbmcgPC0gZ2VucmVfYXZnX3JhdGluZyRVc2VyX1JhdGluZwoKZ2VucmVfc3RhdHMKYGBgCk9uIGF2ZXJhZ2UsCgotIEZpY3Rpb24gYm9va3MgcmVjZWl2ZSBtb3JlIHJldmlld3MgYW5kIGhpZ2hlciB1c2VyIHJhdGluZ3MgdGhhbiBOb24tRmljdGlvbiBib29rcy4KLSBOb24tRmljdGlvbiBib29rcyBhcmUgbW9yZSBleHBlbnNpdmUgdGhhbiBGaWN0aW9uIGJvb2tzLgoKCiMjIFBsb3RzCgojIyMgVXNlciBSYXRpbmcKCmBgYHtyfQojcGxvdHRpbmcgYmFyIGdyYXBoIGZvciB1c2VyIHJhdGluZyB2cy4gbnVtYmVyIG9mIHJldmlld3MKZ2dwbG90KGRhdGE9Ym9va3MsYWVzKHg9VXNlcl9SYXRpbmcseT1SZXZpZXdzKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsgbGFicyh0aXRsZT0iVXNlciBSYXRpbmcgdnMuIE51bWJlciBvZiBSZXZpZXdzIikKYGBgCgotIEJvb2tzIHRoYXQgcmVjZWl2ZSBhIGhpZ2hlciB1c2VyIHJhdGluZyByZWNpZXZlIG1vcmUgcmV2aWV3cy4KLSBUaGVyZSBpcyBhIHNpZ25pZmljYW50IGluY3JlYXNlIGluIHRoZSBudW1iZXIgb2YgcmV2aWV3cyBmb3IgYm9va3MgdGhhdCBoYXZlIGEgcmF0aW5nIG9mIDxiPiA0LjUgYW5kIGFib3ZlLjwvYj4gCgpgYGB7cn0KI0dyb3VwaW5nIGJ5IHVzZXIgcmF0aW5nCmJ5X3JhdGluZ19wcmljZSA8LSBib29rcyAlPiUgZ3JvdXBfYnkoVXNlcl9SYXRpbmcpCgojU3VtbWFyaXppbmcgYXZlcmFnZSBwcmljZSBmb3IgZWFjaCB1c2VyIHJhdGluZyB2YWx1ZQpieV9yYXRpbmdfcHJpY2UgPC0gYnlfcmF0aW5nX3ByaWNlICU+JSBzdW1tYXJpc2UoUHJpY2UgPSBtZWFuKFByaWNlKSkKCiNwbG90dGluZyBiYXIgZ3JhcGggZm9yIHVzZXIgcmF0aW5nIHZzLiBwcmljZQpnZ3Bsb3QoZGF0YT1ieV9yYXRpbmdfcHJpY2UsYWVzKHg9VXNlcl9SYXRpbmcseT1QcmljZSkpICsgZ2VvbV9jb2woKSArIGxhYnModGl0bGU9IlVzZXIgUmF0aW5nIHZzLiBBdmVyYWdlIFByaWNlIikKYGBgCgpPbiBhdmVyYWdlLCBib29rcyB3aXRoIGEgdXNlciByYXRpbmcgb2YgPGI+NC41PC9iPiB0ZW5kIHRvIGJlIHRoZSBtb3N0IGV4cGVuc2l2ZS4KCiMjIyBQcmljZQpgYGB7cn0KI3Bsb3R0aW5nIHNjYXR0ZXIgcGxvdCBmb3IgcHJpY2UgdnMuIG51bWJlciBvZiByZXZpZXdzCmdncGxvdChkYXRhPWJvb2tzLGFlcyh4PVByaWNlLHk9UmV2aWV3cykpICsgZ2VvbV9wb2ludCgpICsgbGFicyh0aXRsZT0iUHJpY2UgdnMuIE51bWJlciBvZiBSZXZpZXdzIikgKyBnZ2hpZ2hsaWdodChQcmljZTw9MjUpCmBgYApHZW5lcmFsbHksIGJvb2tzIHByaWNlZCBhdCBvciB1bmRlciA8Yj4kMjU8L2I+IHJlY2VpdmUgdGhlIG1vc3QgcmV2aWV3cy4KCgpgYGB7cn0KI3Bsb3R0aW5nIHNjYXR0ZXIgcGxvdCBmb3IgcHJpY2UgdnMuIG51bWJlciBvZiByZXZpZXdzCiNIaWdobGlnaHRpbmcgYm9va3Mgd2l0aCBtb3JlIHRoYW4gMjUsMDAwIHJldmlld3MgCmdncGxvdChkYXRhPWJvb2tzLGFlcyh4PVByaWNlLHk9UmV2aWV3cykpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfd3JhcCh+R2VucmUpICsgbGFicyh0aXRsZT0iUHJpY2UgdnMuIE51bWJlciBvZiBSZXZpZXdzIGJ5IEdlbnJlIikgKyBnZ2hpZ2hsaWdodChSZXZpZXdzPjI1MDAwKQoKYGBgCgpIaWdobGlnaHRlZCBhYm92ZSBhcmUgdGhlIGJvb2tzIHRoYXQgcmVjZWl2ZWQgb3ZlciAyNSwwMDAgcmV2aWV3cy4gCgojIyBZZWFyCmBgYHtyfQojR3JvdXBpbmcgYnkgeWVhcgpieV95ZWFyIDwtIGJvb2tzICU+JSBncm91cF9ieShZZWFyKQoKI1N1bW1hcml6aW5nIGF2ZXJhZ2UgcHJpY2UgZm9yIGZvciBib29rcyBlYWNoIHllYXIKYnlfeWVhcl9wcmljZSA8LSBieV95ZWFyICU+JSBzdW1tYXJpc2UoUHJpY2UgPSBtZWFuKFByaWNlKSkKCiNTdW1tYXJpemluZyBhdmVyYWdlIHByaWNlIGZvciBmb3IgYm9va3MgZWFjaCB5ZWFyCmJ5X3llYXJfcmV2aWV3cyA8LSBieV95ZWFyICU+JSBzdW1tYXJpc2UoUmV2aWV3cyA9IG1lYW4oUmV2aWV3cykpCgojcGxvdHRpbmcgYmFyIGdyYXBoIGZvciB5ZWFyIHZzLiBhdmVyYWdlIG51bWJlciBvZiByZXZpZXdzCmdncGxvdChkYXRhPWJ5X3llYXJfcmV2aWV3cyxhZXMoeD1ZZWFyLHk9UmV2aWV3cykpICsgZ2VvbV9jb2woKSArIGxhYnModGl0bGU9IkF2ZXJhZ2UgTnVtYmVyIG9mIFJldmlld3MgUGVyIFllYXIiKQpgYGAKPGI+QWZ0ZXIgbWlkLTIwMTE8L2I+IHRoZSBhdmVyYWdlIG51bWJlciBvZiByZXZpZXdzIHJlY2VpdmVkIGluY3JlYXNlZCBzaWduaWZpY2FudGx5LiAKYGBge3J9CiNwbG90dGluZyBiYXIgZ3JhcGggZm9yIHllYXIgdnMuIGF2ZXJhZ2UgcHJpY2UKZ2dwbG90KGRhdGE9YnlfeWVhcl9wcmljZSxhZXMoeD1ZZWFyLHk9UHJpY2UpKSArIGdlb21fY29sKCkgKyBsYWJzKHRpdGxlPSJBdmVyYWdlIFByaWNlIFBlciBZZWFyIikKYGBgClRoZSBhdmVyYWdlIHByaWNlIGZvciBhbiBBbWF6b24gVG9wIDUwIEJlc3RzZWxsZXIgaGFzIHJlbWFpbmVkIDxiPiBiZXR3ZWVuICQxMCB0byAkMTU8L2I+IGJldHdlZW4gMjAwOS0yMDE5LgoKIyBTdW1tYXJ5IG9mIERhdGEgQW5hbHlzaXMKClRoZXJlIGFyZSA8Yj41NTA8L2I+IGJvb2tzIGluIHRvdGFsLgoKLSBGaWN0aW9uOiAyNDAKLSBOb24tRmljdGlvbjogMzEwCgpGb3IgYm9va3Mgb24gdGhlIEJlc3RzZWxsZXIgbGlzdCBpbiB0aGUgcGVyaW9kIGZyb20gMjAwOS0yMDE5LAoKLSBBdmVyYWdlIHVzZXIgcmF0aW5nIGlzIDQuNjIgYW5kIHRoZSBoaWdoZXN0IHVzZXIgcmF0aW5nIGlzIDQuOSAKLSBBdmVyYWdlIHByaWNlIG9mICQxMy4xMCBhbmQgdGhlIGhpZ2hlc3QgcHJpY2UgaXMgJDEwNS4wMCAKLSBBdmVyYWdlIG51bWJlciBvZiByZXZpZXdzIGlzIDExOTUzIGFuZCB0aGUgaGlnaGVzdCBudW1iZXIgb2YgcmV2aWV3cyBpcyA4Nzg0MQotIEZpY3Rpb24gYm9va3MgcmVjZWl2ZSBtb3JlIHJldmlld3MgYW5kIGhpZ2hlciB1c2VyIHJhdGluZ3MgdGhhbiBOb24tRmljdGlvbiBib29rcy4KLSBOb24tRmljdGlvbiBib29rcyBhcmUgbW9yZSBleHBlbnNpdmUgdGhhbiBGaWN0aW9uIGJvb2tzLgoKIyMjIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIFByaWNlLCBVc2VyIFJhdGluZywgTnVtYmVyIG9mIFJldmlld3MgYW5kIFllYXIgb2YgUmVsZWFzZQoKLSBUaGUgYXZlcmFnZSBwcmljZSBmb3IgYW4gQW1hem9uIFRvcCA1MCBCZXN0c2VsbGVyIGhhcyByZW1haW5lZCA8Yj4gYmV0d2VlbiAkMTAgdG8gJDE1PC9iPiBiZXR3ZWVuIDIwMDktMjAxOS4KLSBCb29rcyBwcmljZWQgYXQgb3IgdW5kZXIgPGI+JDI1PC9iPiByZWNlaXZlIHRoZSBtb3N0IHJldmlld3MsIHJlZ2FybGVzcyBvZiBnZW5yZS4KLSA8Yj5BZnRlciBtaWQtMjAxMTwvYj4gdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHJldmlld3MgcmVjZWl2ZWQgaW5jcmVhc2VkIHNpZ25pZmljYW50bHkuIAotIEJvb2tzIHRoYXQgaGF2ZSBhIHJhdGluZyBvZiA8Yj4gNC41IGFuZCBhYm92ZTwvYj4gcmVjZWl2ZSBzaWduaWZpY2FudGx5IGhpZ2hlciByZXZpZXdzLgotIFRoZSBtYWpvcml0eSBvZiBib29rcyByZWNlaXZlIGxlc3MgdGhhbiAyNSwwMDAgcmV2aWV3cy4KLSBCb29rcyB0aGF0IHJlY2VpdmUgYSBoaWdoZXIgdXNlciByYXRpbmcgcmVjZWl2ZWQgbW9yZSByZXZpZXdzLgotIEJvb2tzIHdpdGggYSB1c2VyIHJhdGluZyBvZiA8Yj40LjU8L2I+IHRlbmQgdG8gaGF2ZSB0aGUgaGlnaGVzdCBhdmVyYWdlIHByaWNlLiAKCgoKCgoK