The dataset provided has 4 fields relating to several kinds of products in diverse categories. The task at hand is to realise the top trending targets/products for each category.

#Loading necessary libraries
library(ggplot2)
library(dplyr)
library(lubridate)
setwd("C:/Users/ankit/Desktop/Pyprojects/Trends-Assignment.tar")
trend <- read.csv("trendz.csv", na.strings = c(' ', '', 'NA', 'NaN'), stringsAsFactors = F)

Since we are dealing with just 9 days, I have decided to refer to them as Days (1 to 9) for the purpose of simplicity.

trend$day <- ymd_hms(trend$day)
trend$day <- day(trend$day) - 15
head(trend)

We need to also remove the rows that have missing target name since they do not add much value to our analysis

ind <- which(is.na(trend$target))
trend <- trend[-ind,]
summary(trend)
   activity             target            day            city               count        
 Length:963548      Min.   :     0   Min.   :1.000   Length:963548      Min.   :   1.00  
 Class :character   1st Qu.:134485   1st Qu.:3.000   Class :character   1st Qu.:   1.00  
 Mode  :character   Median :232545   Median :5.000   Mode  :character   Median :   1.00  
                    Mean   :210099   Mean   :5.001                      Mean   :   3.14  
                    3rd Qu.:314840   3rd Qu.:7.000                      3rd Qu.:   2.00  
                    Max.   :715573   Max.   :9.000                      Max.   :1137.00  

Next, we need to seperate out the entities that can be added to cart and others. The categories corresponding to ‘viewed sku’, ‘viewed drug’ and ‘viewed OTC’ have targets common with ‘added to cart’ category. Hence, these are the 3 categories for which we can calculate conversion rates and define a metric that compares trend using data.

For the other three 2 categories - ‘viewed article’ & ‘viewed OTC categories’ the outcome that can be used to determine trend is the no of views itself.

First, we will work out a function that determines the top trends for the last 2 categories

#Funtion for determining the top trending entities within 'viewed article' and 'viewed OTC categories'
top_trend_finder_view_cor <- function(act, df, name_city){
  ifelse(name_city != 'All',
    z <- df %>% filter(activity == as.name(act), city == as.name(name_city)) %>%
      group_by(target, day) %>% 
      summarise(Total_Count = sum(count)) %>% 
      arrange(target,day),
  z <- df %>% filter(activity == as.name(act)) %>%
      group_by(target, day) %>% 
      summarise(Total_Count = sum(count)) %>% 
      arrange(target,day)
  )
  d <- ungroup(z) %>% group_by(target) %>%
    summarise(series_size = n(), sum.target = sum(Total_Count)) %>%
    filter(series_size > 2, sum.target > 30)
  
  z <- subset(z, target %in% unique(d$target))
  a <- unique(z[['target']])
  cor_estimate = c()
  b <- z %>% filter(target == a[1])
  for (i in 1:length(a)){
    b <- z %>% filter(target == a[i])
    model <- cor.test(~Total_Count + day,data=b,method="kendall")
    cor_estimate <- append(cor_estimate,model$estimate)
    }
  cor_rank <- data.frame(target = a, score = cor_estimate)
  
  e <- (head(arrange(cor_rank,desc(score)), n = 5))
  f <- subset(z, target %in% e$target)
  num_plot <- ggplot(f, aes(day,Total_Count, colour=as.factor(target))) + 
    #geom_line(size = 2) + 
    scale_x_continuous(breaks = c(1:9)) +
    ggtitle(cat(as.name(act), "for", as.name(name_city))) + xlab('Day') + theme(
    plot.title = element_text(color="red", size=14, face="bold.italic", hjust = 0.5),
    axis.title.x = element_text(size=14, face="bold"),
    axis.title.y = element_text(size=14, face="bold")) +
    geom_smooth(se = F, size = 2)
  return(num_plot)
}
# This function plots it for all cities, the name of city could be mentioned if required
# 'viewed article category'
top_trend_finder_view_cor('viewed article', trend, 'All')
viewed article for All

# 'viewed otc categories' activity
top_trend_finder_view_cor('viewed otc categories', trend, 'All')
viewed otc categories for All

Turns out that only around 8 targets have added to cart value associated with it in ‘viewed drug’ and hence the more reliable metric for observation of trend is the no of views and hence we will use the view function for ‘viewed drug’ category as well.

# 'viewed frug' activity
top_trend_finder_view_cor('viewed drug', trend, 'All')
viewed drug for All

The next function is exclusively for the 2 categories (‘viewed sku’ & ‘viewed OTC’). These 2 categories have large number of targets that feature well in the added to target category as well. Hence, It would be interesting to determine the conversion rate for realising the trends for these 2 categories.

library(gridExtra)
top_trend_finder_cor <- function(act, df, name_city){
  if(name_city != 'All'){
    z <- df %>% filter(activity == as.name(act), city == name_city) %>%
    group_by(target, day) %>% 
    summarise(Total_Count = sum(count))
  
    y <- z %>% group_by(target) %>% 
      summarise(n = n(), sum = sum(Total_Count)) %>%
      filter(n > 1)
    
    z <- ungroup(z) %>% filter(target %in% unique(y$target))
    
    c <- df %>% filter(activity == 'added to cart', city == name_city) %>%
      group_by(target) %>%
      summarise(Total_Added_To_Cart = sum(count)) %>%
      filter(Total_Added_To_Cart > 10)
    
    z <- z %>% filter(target %in% unique(c$target))
    a <- df %>% filter(activity == 'added to cart', city == name_city) %>%
      group_by(target, day) %>%
      summarise(Total_Added_To_Cart = sum(count))
  
  } else {
    z <- df %>% filter(activity == as.name(act)) %>%
      group_by(target, day) %>% 
      summarise(Total_Count = sum(count))
    
    y <- z %>% group_by(target) %>% 
      summarise(n = n(), sum = sum(Total_Count)) %>%
      filter(n > 1)
    
    z <- ungroup(z) %>% filter(target %in% unique(y$target))
    
    c <- df %>% filter(activity == 'added to cart') %>%
      group_by(target) %>%
      summarise(Total_Added_To_Cart = sum(count)) %>%
      filter(Total_Added_To_Cart > 15)
    
    z <- z %>% filter(target %in% unique(c$target))
    a <- df %>% filter(activity == 'added to cart') %>%
      group_by(target, day) %>%
      summarise(Total_Added_To_Cart = sum(count))
  }
  b <- left_join(z,a,by = c('target', 'day'))
  b[which(is.na(b$Total_Added_To_Cart)),'Total_Added_To_Cart'] <- 0
  b[which(b$Total_Added_To_Cart > b$Total_Count),'Total_Added_To_Cart'] <- 0
  b <- b %>% mutate(conversion = (Total_Added_To_Cart/Total_Count)*100) %>%
     arrange(target,day)
  e <- unique(b$target)
  cor_estimate = c()
  for (i in 1:length(e)){
    d <- b %>% filter(target == e[i])
    model <- cor.test(~conversion + day,data=d,method="kendall")
    cor_estimate <- append(cor_estimate,model$estimate)
  }
  cor_rank <- data.frame(target = e, score = cor_estimate)
  
  g <- (head(arrange(cor_rank,desc(score)), n = 5))
  f <- subset(b, target %in% g$target)
  
  num_plot <- ggplot(f, aes(day,Total_Count, colour=as.factor(target))) + 
    #geom_line(size = 2) + 
    scale_x_continuous(breaks = c(1:9)) +
    ggtitle(as.name(act)) + theme(
    plot.title = element_text(color="red", size=14, face="bold.italic", hjust = 0.5),
    axis.title.x = element_text(size=14, face="bold"),
    axis.title.y = element_text(size=14, face="bold")) +
    geom_smooth(se = F, size = 1.5)
  num_plot_2 <- ggplot(f, aes(day,Total_Added_To_Cart, colour=as.factor(target))) + 
    #geom_line(size = 2) + 
    scale_x_continuous(breaks = c(1:9)) +
    ggtitle(as.name(act)) + theme(
    plot.title = element_text(color="red", size=14, face="bold.italic", hjust = 0.5),
    axis.title.x = element_text(size=14, face="bold"),
    axis.title.y = element_text(size=14, face="bold")) +
    geom_smooth(se = F, size = 1.5)
  finplot <- grid.arrange(num_plot, num_plot_2, nrow=2)
  suppressWarnings(return(finplot))
}
top_trend_finder_cor('viewed OTC',trend, 'New Delhi')
TableGrob (2 x 1) "arrange": 2 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (2-2,1-1) arrange gtable[layout]

top_trend_finder_cor('viewed sku',trend, 'New Delhi')
TableGrob (2 x 1) "arrange": 2 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (2-2,1-1) arrange gtable[layout]

The dataset could be really interesting with a longer time series and trends observed would be more authentic. The correlation function would still provide a good estimate for the trend strength comparison.

Also more historical day could provide a metric like historical mean of count that can be used for weeding out unpopular products/items from the analysis.

LS0tDQp0aXRsZTogIjFNRyBUcmVuZHMgQXNzaWdubWVudCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNClRoZSBkYXRhc2V0IHByb3ZpZGVkIGhhcyA0IGZpZWxkcyByZWxhdGluZyB0byBzZXZlcmFsIGtpbmRzIG9mIHByb2R1Y3RzIGluIGRpdmVyc2UgY2F0ZWdvcmllcy4gVGhlIHRhc2sgYXQgaGFuZCBpcyB0byByZWFsaXNlIHRoZSB0b3AgdHJlbmRpbmcgdGFyZ2V0cy9wcm9kdWN0cyBmb3IgZWFjaCBjYXRlZ29yeS4gDQoNCmBgYHtyfQ0KI0xvYWRpbmcgbmVjZXNzYXJ5IGxpYnJhcmllcw0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0Kc2V0d2QoIkM6L1VzZXJzL2Fua2l0L0Rlc2t0b3AvUHlwcm9qZWN0cy9UcmVuZHMtQXNzaWdubWVudC50YXIiKQ0KdHJlbmQgPC0gcmVhZC5jc3YoInRyZW5kei5jc3YiLCBuYS5zdHJpbmdzID0gYygnICcsICcnLCAnTkEnLCAnTmFOJyksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQ0KYGBgDQpTaW5jZSB3ZSBhcmUgZGVhbGluZyB3aXRoIGp1c3QgOSBkYXlzLCBJIGhhdmUgZGVjaWRlZCB0byByZWZlciB0byB0aGVtIGFzIERheXMgKDEgdG8gOSkgZm9yIHRoZSBwdXJwb3NlIG9mIHNpbXBsaWNpdHkuDQoNCmBgYHtyfQ0KI0NvbnZlcnRpbmcgZGF5IGludG8gUG9zaXhzdCBmb3JtYXQNCnRyZW5kJGRheSA8LSB5bWRfaG1zKHRyZW5kJGRheSkNCnRyZW5kJGRheSA8LSBkYXkodHJlbmQkZGF5KSAtIDE1DQpoZWFkKHRyZW5kKQ0KYGBgDQoNCldlIG5lZWQgdG8gYWxzbyByZW1vdmUgdGhlIHJvd3MgdGhhdCBoYXZlIG1pc3NpbmcgdGFyZ2V0IG5hbWUgc2luY2UgdGhleSBkbyBub3QgYWRkIG11Y2ggdmFsdWUgdG8gb3VyIGFuYWx5c2lzDQoNCmBgYHtyfQ0KaW5kIDwtIHdoaWNoKGlzLm5hKHRyZW5kJHRhcmdldCkpDQp0cmVuZCA8LSB0cmVuZFstaW5kLF0NCnN1bW1hcnkodHJlbmQpDQpgYGANCg0KTmV4dCwgd2UgbmVlZCB0byBzZXBlcmF0ZSBvdXQgdGhlIGVudGl0aWVzIHRoYXQgY2FuIGJlIGFkZGVkIHRvIGNhcnQgYW5kIG90aGVycy4gVGhlIGNhdGVnb3JpZXMgY29ycmVzcG9uZGluZyB0byAndmlld2VkIHNrdScsICd2aWV3ZWQgZHJ1ZycgYW5kICd2aWV3ZWQgT1RDJyBoYXZlIHRhcmdldHMgY29tbW9uIHdpdGggJ2FkZGVkIHRvIGNhcnQnIGNhdGVnb3J5LiBIZW5jZSwgdGhlc2UgYXJlIHRoZSAzIGNhdGVnb3JpZXMgZm9yIHdoaWNoIHdlIGNhbiBjYWxjdWxhdGUgY29udmVyc2lvbiByYXRlcyBhbmQgZGVmaW5lIGEgbWV0cmljIHRoYXQgY29tcGFyZXMgdHJlbmQgdXNpbmcgZGF0YS4gDQoNCkZvciB0aGUgb3RoZXIgdGhyZWUgMiBjYXRlZ29yaWVzIC0gJ3ZpZXdlZCBhcnRpY2xlJyAmICd2aWV3ZWQgT1RDIGNhdGVnb3JpZXMnIHRoZSBvdXRjb21lIHRoYXQgY2FuIGJlIHVzZWQgdG8gZGV0ZXJtaW5lIHRyZW5kIGlzIHRoZSBubyBvZiB2aWV3cyBpdHNlbGYuDQoNCkZpcnN0LCB3ZSB3aWxsIHdvcmsgb3V0IGEgZnVuY3Rpb24gdGhhdCBkZXRlcm1pbmVzIHRoZSB0b3AgdHJlbmRzIGZvciB0aGUgbGFzdCAyIGNhdGVnb3JpZXMNCg0KDQpgYGB7cn0NCiNGdW50aW9uIGZvciBkZXRlcm1pbmluZyB0aGUgdG9wIHRyZW5kaW5nIGVudGl0aWVzIHdpdGhpbiAndmlld2VkIGFydGljbGUnIGFuZCAndmlld2VkIE9UQyBjYXRlZ29yaWVzJw0KdG9wX3RyZW5kX2ZpbmRlcl92aWV3X2NvciA8LSBmdW5jdGlvbihhY3QsIGRmLCBuYW1lX2NpdHkpew0KICAjIGZpbHRlcmluZyBkYXRhZnJhbWUgb24gdGhlIGJhc2lzIG9mIGNpdHkgaW5wdXQgJiB0eXBlIG9mIGFjdGl2aXR5DQogIGlmZWxzZShuYW1lX2NpdHkgIT0gJ0FsbCcsDQogICAgeiA8LSBkZiAlPiUgZmlsdGVyKGFjdGl2aXR5ID09IGFzLm5hbWUoYWN0KSwgY2l0eSA9PSBhcy5uYW1lKG5hbWVfY2l0eSkpICU+JQ0KICAgICAgZ3JvdXBfYnkodGFyZ2V0LCBkYXkpICU+JSANCiAgICAgIHN1bW1hcmlzZShUb3RhbF9Db3VudCA9IHN1bShjb3VudCkpICU+JSANCiAgICAgIGFycmFuZ2UodGFyZ2V0LGRheSksDQogICAgeiA8LSBkZiAlPiUgZmlsdGVyKGFjdGl2aXR5ID09IGFzLm5hbWUoYWN0KSkgJT4lDQogICAgICBncm91cF9ieSh0YXJnZXQsIGRheSkgJT4lIA0KICAgICAgc3VtbWFyaXNlKFRvdGFsX0NvdW50ID0gc3VtKGNvdW50KSkgJT4lIA0KICAgICAgYXJyYW5nZSh0YXJnZXQsZGF5KQ0KICApDQogIA0KICAjIGZpbHRlcmluZyBvdXQgdW5wb3B1bGFyIHRhcmdldHMgdGhhdCBtaWdodCBjcmVlcCBpbiB0byB0aGUgdG9wIGNhdGVnb3JpZXMgd2l0aG91dCBoYXZpbmcgYQ0KICAjIHJlbGV2ZW50IGNvdW50LCBhbHNvIGZpbHRlcmluZyBvdXQgdGltZSBzZXJpZXMgd2l0aCBsZXNzIHRoYW4gMyBlbnRyaWVzL29ic2VydmF0aW9ucw0KICBkIDwtIHVuZ3JvdXAoeikgJT4lIGdyb3VwX2J5KHRhcmdldCkgJT4lDQogICAgc3VtbWFyaXNlKHNlcmllc19zaXplID0gbigpLCBzdW0udGFyZ2V0ID0gc3VtKFRvdGFsX0NvdW50KSkgJT4lDQogICAgZmlsdGVyKHNlcmllc19zaXplID4gMiwgc3VtLnRhcmdldCA+IDMwKQ0KICANCiAgeiA8LSBzdWJzZXQoeiwgdGFyZ2V0ICVpbiUgdW5pcXVlKGQkdGFyZ2V0KSkNCiAgYSA8LSB1bmlxdWUoeltbJ3RhcmdldCddXSkNCiAgDQogICNjYWxjdWxhdGUgY29ycmVsYXRpb24gd2l0aCB0aW1lIGZvciBlYWNoIHRhcmdldA0KICBjb3JfZXN0aW1hdGUgPSBjKCkNCiAgYiA8LSB6ICU+JSBmaWx0ZXIodGFyZ2V0ID09IGFbMV0pDQogIGZvciAoaSBpbiAxOmxlbmd0aChhKSl7DQogICAgYiA8LSB6ICU+JSBmaWx0ZXIodGFyZ2V0ID09IGFbaV0pDQogICAgbW9kZWwgPC0gY29yLnRlc3QoflRvdGFsX0NvdW50ICsgZGF5LGRhdGE9YixtZXRob2Q9ImtlbmRhbGwiKQ0KICAgIGNvcl9lc3RpbWF0ZSA8LSBhcHBlbmQoY29yX2VzdGltYXRlLG1vZGVsJGVzdGltYXRlKQ0KICAgIH0NCiAgY29yX3JhbmsgPC0gZGF0YS5mcmFtZSh0YXJnZXQgPSBhLCBzY29yZSA9IGNvcl9lc3RpbWF0ZSkNCiAgDQogICNFeHRyYWN0aW5nIHRvcCA1IHRyZW5kcw0KICBlIDwtIChoZWFkKGFycmFuZ2UoY29yX3JhbmssZGVzYyhzY29yZSkpLCBuID0gNSkpDQogIGYgPC0gc3Vic2V0KHosIHRhcmdldCAlaW4lIGUkdGFyZ2V0KQ0KICANCiAgI1Bsb3QgdGhlIHRvcCA1IHRyZW5kaW5nIHRhcmdldHMgZm9yIHRoZSByZXF1ZXN0ZWQgYWN0aXZpdHkNCiAgbnVtX3Bsb3QgPC0gZ2dwbG90KGYsIGFlcyhkYXksVG90YWxfQ291bnQsIGNvbG91cj1hcy5mYWN0b3IodGFyZ2V0KSkpICsgDQogICAgI2dlb21fbGluZShzaXplID0gMikgKyANCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygxOjkpKSArDQogICAgZ2d0aXRsZShjYXQoYXMubmFtZShhY3QpLCAiZm9yIiwgYXMubmFtZShuYW1lX2NpdHkpKSkgKyB4bGFiKCdEYXknKSArIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3I9InJlZCIsIHNpemU9MTQsIGZhY2U9ImJvbGQuaXRhbGljIiwgaGp1c3QgPSAwLjUpLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCBmYWNlPSJib2xkIiksDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGZhY2U9ImJvbGQiKSkgKw0KICAgIGdlb21fc21vb3RoKHNlID0gRiwgc2l6ZSA9IDIpDQoNCiAgcmV0dXJuKG51bV9wbG90KQ0KfQ0KYGBgDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCiMgVGhpcyBmdW5jdGlvbiBwbG90cyBpdCBmb3IgYWxsIGNpdGllcywgdGhlIG5hbWUgb2YgY2l0eSBjb3VsZCBiZSBtZW50aW9uZWQgaWYgcmVxdWlyZWQNCiMgJ3ZpZXdlZCBhcnRpY2xlIGNhdGVnb3J5Jw0KdG9wX3RyZW5kX2ZpbmRlcl92aWV3X2Nvcigndmlld2VkIGFydGljbGUnLCB0cmVuZCwgJ0FsbCcpDQoNCmBgYA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQojICd2aWV3ZWQgb3RjIGNhdGVnb3JpZXMnIGFjdGl2aXR5DQp0b3BfdHJlbmRfZmluZGVyX3ZpZXdfY29yKCd2aWV3ZWQgb3RjIGNhdGVnb3JpZXMnLCB0cmVuZCwgJ0FsbCcpDQpgYGANClR1cm5zIG91dCB0aGF0IG9ubHkgYXJvdW5kIDggdGFyZ2V0cyBoYXZlIGFkZGVkIHRvIGNhcnQgdmFsdWUgYXNzb2NpYXRlZCB3aXRoIGl0IGluICd2aWV3ZWQgZHJ1ZycgYW5kIGhlbmNlIHRoZSBtb3JlIHJlbGlhYmxlIG1ldHJpYyBmb3Igb2JzZXJ2YXRpb24gb2YgdHJlbmQgaXMgdGhlIG5vIG9mIHZpZXdzIGFuZCBoZW5jZSB3ZSB3aWxsIHVzZSB0aGUgdmlldyBmdW5jdGlvbiBmb3IgJ3ZpZXdlZCBkcnVnJyBjYXRlZ29yeSBhcyB3ZWxsLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQojICd2aWV3ZWQgZnJ1ZycgYWN0aXZpdHkNCnRvcF90cmVuZF9maW5kZXJfdmlld19jb3IoJ3ZpZXdlZCBkcnVnJywgdHJlbmQsICdBbGwnKQ0KYGBgDQoNClRoZSBuZXh0IGZ1bmN0aW9uIGlzIGV4Y2x1c2l2ZWx5IGZvciB0aGUgMiBjYXRlZ29yaWVzICgndmlld2VkIHNrdScgJiAndmlld2VkIE9UQycpLiBUaGVzZSAyIGNhdGVnb3JpZXMgaGF2ZSBsYXJnZSBudW1iZXIgb2YgdGFyZ2V0cyB0aGF0IGZlYXR1cmUgd2VsbCBpbiB0aGUgYWRkZWQgdG8gdGFyZ2V0IGNhdGVnb3J5IGFzIHdlbGwuIEhlbmNlLCBJdCB3b3VsZCBiZSBpbnRlcmVzdGluZyB0byBkZXRlcm1pbmUgdGhlIGNvbnZlcnNpb24gcmF0ZSBmb3IgcmVhbGlzaW5nIHRoZSB0cmVuZHMgZm9yIHRoZXNlIDIgY2F0ZWdvcmllcy4NCg0KYGBge3J9DQpsaWJyYXJ5KGdyaWRFeHRyYSkNCnRvcF90cmVuZF9maW5kZXJfY29yIDwtIGZ1bmN0aW9uKGFjdCwgZGYsIG5hbWVfY2l0eSl7DQogIA0KICAjIGZpbHRlcmluZyBkYXRhZnJhbWUgb24gdGhlIGJhc2lzIG9mIGNpdHkgaW5wdXQgJiB0eXBlIG9mIGFjdGl2aXR5DQogICMgJiBpbXBsZW1lbnRpbmcgcmVxdWlyZWQgY29uZGl0aW9ucyBhcyB3YXMgZG9uZSBpbiB0aGUgcHJldmlvdXMgZnVuY3Rpb24NCiAgaWYobmFtZV9jaXR5ICE9ICdBbGwnKXsNCiAgICB6IDwtIGRmICU+JSBmaWx0ZXIoYWN0aXZpdHkgPT0gYXMubmFtZShhY3QpLCBjaXR5ID09IG5hbWVfY2l0eSkgJT4lDQogICAgICBncm91cF9ieSh0YXJnZXQsIGRheSkgJT4lIA0KICAgICAgc3VtbWFyaXNlKFRvdGFsX0NvdW50ID0gc3VtKGNvdW50KSkNCiAgICB5IDwtIHogJT4lIGdyb3VwX2J5KHRhcmdldCkgJT4lIA0KICAgICAgc3VtbWFyaXNlKG4gPSBuKCksIHN1bSA9IHN1bShUb3RhbF9Db3VudCkpICU+JQ0KICAgICAgZmlsdGVyKG4gPiAxKQ0KICAgIHogPC0gdW5ncm91cCh6KSAlPiUgZmlsdGVyKHRhcmdldCAlaW4lIHVuaXF1ZSh5JHRhcmdldCkpDQogICAgYyA8LSBkZiAlPiUgZmlsdGVyKGFjdGl2aXR5ID09ICdhZGRlZCB0byBjYXJ0JywgY2l0eSA9PSBuYW1lX2NpdHkpICU+JQ0KICAgICAgZ3JvdXBfYnkodGFyZ2V0KSAlPiUNCiAgICAgIHN1bW1hcmlzZShUb3RhbF9BZGRlZF9Ub19DYXJ0ID0gc3VtKGNvdW50KSkgJT4lDQogICAgICBmaWx0ZXIoVG90YWxfQWRkZWRfVG9fQ2FydCA+IDEwKQ0KICAgIHogPC0geiAlPiUgZmlsdGVyKHRhcmdldCAlaW4lIHVuaXF1ZShjJHRhcmdldCkpDQogICAgYSA8LSBkZiAlPiUgZmlsdGVyKGFjdGl2aXR5ID09ICdhZGRlZCB0byBjYXJ0JywgY2l0eSA9PSBuYW1lX2NpdHkpICU+JQ0KICAgICAgZ3JvdXBfYnkodGFyZ2V0LCBkYXkpICU+JQ0KICAgICAgc3VtbWFyaXNlKFRvdGFsX0FkZGVkX1RvX0NhcnQgPSBzdW0oY291bnQpKQ0KICB9IGVsc2Ugew0KICAgIHogPC0gZGYgJT4lIGZpbHRlcihhY3Rpdml0eSA9PSBhcy5uYW1lKGFjdCkpICU+JQ0KICAgICAgZ3JvdXBfYnkodGFyZ2V0LCBkYXkpICU+JSANCiAgICAgIHN1bW1hcmlzZShUb3RhbF9Db3VudCA9IHN1bShjb3VudCkpDQogICAgeSA8LSB6ICU+JSBncm91cF9ieSh0YXJnZXQpICU+JSANCiAgICAgIHN1bW1hcmlzZShuID0gbigpLCBzdW0gPSBzdW0oVG90YWxfQ291bnQpKSAlPiUNCiAgICAgIGZpbHRlcihuID4gMSkNCiAgICB6IDwtIHVuZ3JvdXAoeikgJT4lIGZpbHRlcih0YXJnZXQgJWluJSB1bmlxdWUoeSR0YXJnZXQpKQ0KICAgIGMgPC0gZGYgJT4lIGZpbHRlcihhY3Rpdml0eSA9PSAnYWRkZWQgdG8gY2FydCcpICU+JQ0KICAgICAgZ3JvdXBfYnkodGFyZ2V0KSAlPiUNCiAgICAgIHN1bW1hcmlzZShUb3RhbF9BZGRlZF9Ub19DYXJ0ID0gc3VtKGNvdW50KSkgJT4lDQogICAgICBmaWx0ZXIoVG90YWxfQWRkZWRfVG9fQ2FydCA+IDE1KQ0KICAgIHogPC0geiAlPiUgZmlsdGVyKHRhcmdldCAlaW4lIHVuaXF1ZShjJHRhcmdldCkpDQogICAgYSA8LSBkZiAlPiUgZmlsdGVyKGFjdGl2aXR5ID09ICdhZGRlZCB0byBjYXJ0JykgJT4lDQogICAgICBncm91cF9ieSh0YXJnZXQsIGRheSkgJT4lDQogICAgICBzdW1tYXJpc2UoVG90YWxfQWRkZWRfVG9fQ2FydCA9IHN1bShjb3VudCkpDQogIH0NCiAgDQogICNKb2luaW5nIHRoZSAnQWRkZWQgdG8gQ2FydCcgbnVtYmVycyB3aXRoIHRoZSBtYWluIHZpZXcgY291bnQgdGFibGUNCiAgYiA8LSBsZWZ0X2pvaW4oeixhLGJ5ID0gYygndGFyZ2V0JywgJ2RheScpKQ0KICBiW3doaWNoKGlzLm5hKGIkVG90YWxfQWRkZWRfVG9fQ2FydCkpLCdUb3RhbF9BZGRlZF9Ub19DYXJ0J10gPC0gMA0KICANCiAgI1JlbW92aW5nIGFuYW1vbGllcyB3aGVyZSBhZGRlZCB0byBjYXJ0IGlzIGdyZWF0ZXIgdGhhbiB0aGUgdmlldyBjb3VudCB3aGljaCBpcyBpbXByYWN0aWNhbA0KICBiW3doaWNoKGIkVG90YWxfQWRkZWRfVG9fQ2FydCA+IGIkVG90YWxfQ291bnQpLCdUb3RhbF9BZGRlZF9Ub19DYXJ0J10gPC0gMA0KICBiIDwtIGIgJT4lIG11dGF0ZShjb252ZXJzaW9uID0gKFRvdGFsX0FkZGVkX1RvX0NhcnQvVG90YWxfQ291bnQpKjEwMCkgJT4lDQogICAgIGFycmFuZ2UodGFyZ2V0LGRheSkNCg0KICBlIDwtIHVuaXF1ZShiJHRhcmdldCkNCiAgDQogICNFc3RpbWF0aW5nIGNvcnJlbGF0aW9uIG9mIHRoZSBjb252ZXJzaW9uIHJhdGUgZm9yIGVhY2ggdGFyZ2V0IHdpdGggdGltZQ0KICBjb3JfZXN0aW1hdGUgPSBjKCkNCiAgZm9yIChpIGluIDE6bGVuZ3RoKGUpKXsNCiAgICBkIDwtIGIgJT4lIGZpbHRlcih0YXJnZXQgPT0gZVtpXSkNCiAgICBtb2RlbCA8LSBjb3IudGVzdCh+Y29udmVyc2lvbiArIGRheSxkYXRhPWQsbWV0aG9kPSJrZW5kYWxsIikNCiAgICBjb3JfZXN0aW1hdGUgPC0gYXBwZW5kKGNvcl9lc3RpbWF0ZSxtb2RlbCRlc3RpbWF0ZSkNCiAgfQ0KICBjb3JfcmFuayA8LSBkYXRhLmZyYW1lKHRhcmdldCA9IGUsIHNjb3JlID0gY29yX2VzdGltYXRlKQ0KICBnIDwtIChoZWFkKGFycmFuZ2UoY29yX3JhbmssZGVzYyhzY29yZSkpLCBuID0gNSkpDQogIGYgPC0gc3Vic2V0KGIsIHRhcmdldCAlaW4lIGckdGFyZ2V0KQ0KICANCiAgI1Bsb3QgdGhlIHZpZXdlZCBhbmQgYWRkZWQgdG8gY2FydCBhY3Rpdml0eSBpbiBzZXBlcmF0ZSBwbG90cyANCiAgbnVtX3Bsb3QgPC0gZ2dwbG90KGYsIGFlcyhkYXksVG90YWxfQ291bnQsIGNvbG91cj1hcy5mYWN0b3IodGFyZ2V0KSkpICsgDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMTo5KSkgKw0KICAgIGdndGl0bGUoYXMubmFtZShhY3QpKSArIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3I9InJlZCIsIHNpemU9MTQsIGZhY2U9ImJvbGQuaXRhbGljIiwgaGp1c3QgPSAwLjUpLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCBmYWNlPSJib2xkIiksDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGZhY2U9ImJvbGQiKSkgKw0KICAgIGdlb21fc21vb3RoKHNlID0gRiwgc2l6ZSA9IDEuNSkNCiAgbnVtX3Bsb3RfMiA8LSBnZ3Bsb3QoZiwgYWVzKGRheSxUb3RhbF9BZGRlZF9Ub19DYXJ0LCBjb2xvdXI9YXMuZmFjdG9yKHRhcmdldCkpKSArIA0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKDE6OSkpICsNCiAgICBnZ3RpdGxlKGFzLm5hbWUoYWN0KSkgKyB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yPSJyZWQiLCBzaXplPTE0LCBmYWNlPSJib2xkLml0YWxpYyIsIGhqdXN0ID0gMC41KSwNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFjZT0iYm9sZCIpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCBmYWNlPSJib2xkIikpICsNCiAgICBnZW9tX3Ntb290aChzZSA9IEYsIHNpemUgPSAxLjUpDQogIGZpbnBsb3QgPC0gZ3JpZC5hcnJhbmdlKG51bV9wbG90LCBudW1fcGxvdF8yLCBucm93PTIpDQogIHN1cHByZXNzV2FybmluZ3MocmV0dXJuKGZpbnBsb3QpKQ0KfQ0KYGBgDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KdG9wX3RyZW5kX2ZpbmRlcl9jb3IoJ3ZpZXdlZCBPVEMnLHRyZW5kLCAnTmV3IERlbGhpJykNCmBgYA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQp0b3BfdHJlbmRfZmluZGVyX2Nvcigndmlld2VkIHNrdScsdHJlbmQsICdOZXcgRGVsaGknKQ0KYGBgDQoNClRoZSBkYXRhc2V0IGNvdWxkIGJlIHJlYWxseSBpbnRlcmVzdGluZyB3aXRoIGEgbG9uZ2VyIHRpbWUgc2VyaWVzIGFuZCB0cmVuZHMgb2JzZXJ2ZWQgd291bGQgYmUgbW9yZSBhdXRoZW50aWMuIFRoZSBjb3JyZWxhdGlvbiBmdW5jdGlvbiB3b3VsZCBzdGlsbCBwcm92aWRlIGEgZ29vZCBlc3RpbWF0ZSBmb3IgdGhlIHRyZW5kIHN0cmVuZ3RoIGNvbXBhcmlzb24uDQoNCkFsc28gbW9yZSBoaXN0b3JpY2FsIGRheSBjb3VsZCBwcm92aWRlIGEgbWV0cmljIGxpa2UgaGlzdG9yaWNhbCBtZWFuIG9mIGNvdW50IHRoYXQgY2FuIGJlIHVzZWQgZm9yIHdlZWRpbmcgb3V0IHVucG9wdWxhciBwcm9kdWN0cy9pdGVtcyBmcm9tIHRoZSBhbmFseXNpcy4NCg0K