library(stringr)
library(ggplot2)
library(scales) # y axis
library(rgdal)
library(tidyverse)
library(lubridate) # convert time to 24 hours,convert days to weekdays
library(geojsonR) # read geojson file
library(geojsonio)

Analysis of Annual Financial Reports

  • Lyft Annual Report
  • Uber Annual Report
  • Revenue

    # in million dollars
    Year = c(2016:2021)
     
    Uber_Revenue = c(3338,7402,10433,13000,11139,17455)
    Lyft_Revenue = c(343,1060,2157,3616,2365,3208)
    Revenue = data.frame(Year,Uber_Revenue,Lyft_Revenue)
    

    Create revenue change percentage columns

    Revenue$Uber_Revenue_change_perc = c(NA,100*diff(Revenue$Uber_Revenue)/Revenue$Uber_Revenue[c(1:5)])
    Revenue$Lyft_Revenue_change_perc = c(NA,100*diff(Revenue$Lyft_Revenue)/Revenue$Lyft_Revenue[c(1:5)])
    Revenue_gather1 = gather(Revenue,key = "app_taxi",value = "Revenue",2:3)
    Revenue_gather2 = gather(Revenue, key = "percentage", value = "perc",4:5)
    Revenue_gather1$order = c(1:12)
    Revenue_gather2$order = c(1:12)
    Revenue_gather = merge(Revenue_gather1[,c(1,4,5,6)],Revenue_gather2[,c(4:6)],by = "order")
    
    ggplot(Revenue_gather) + 
      aes(x = Year, y = Revenue, fill = app_taxi) +
      geom_col() +
      scale_x_continuous(breaks = 2016:2021)+
      geom_text(aes(x = Year, 
                    y = Revenue, 
                    label = ifelse(is.na(perc), "",
                                   paste(round(perc,1),"%"))),
                vjust = -0.5) +
      facet_wrap(~app_taxi) +
      labs(x = "",
           y = "Total Revenue (In Million Dollars)",
           title = "Total Revenue and Percentage Change
           Worldwide")

    Active Riders

    Year = c(2016:2021)
    Uber_Active_Riders = c(45,68,91,111,93,118)
    Lyft_Active_Riders = c(6.6,12.6,18.6,22.905,12.552,18.728)
    consumers = data.frame(Year,Uber_Active_Riders,Lyft_Active_Riders)
    
    consumers$Uber_Rider_change_perc = c(NA,100*diff(consumers$Uber_Active_Riders)/consumers$Uber_Active_Riders[c(1:5)])
    consumers$Lyft_Rider_change_perc = c(NA,100*diff(consumers$Lyft_Active_Riders)/consumers$Lyft_Active_Riders[c(1:5)])
    
    consumers_gather1 = gather(consumers,key = "app_taxi",value = "Riders",2:3)
    consumers_gather2 = gather(consumers, key = "percentage", value = "perc",4:5)
    consumers_gather1$order = c(1:12)
    consumers_gather2$order = c(1:12)
    consumers_gather = merge(consumers_gather1[,c(1,4,5,6)],consumers_gather2[,c(4:6)],by = "order")
    
    ggplot(consumers_gather) + 
      aes(x = Year, y = Riders, fill = app_taxi) +
      geom_col() +
      scale_x_continuous(breaks = 2016:2021)+
      geom_text(aes(x = Year, 
                    y = Riders, 
                    label = ifelse(is.na(perc), "",paste(round(perc,1),"%"))),
                vjust = -0.5) +
      facet_wrap(~app_taxi) +
      labs(x = "",
           y = "Active Riders (In Millions)",
           title = "Uber and Lyft Active Riders and Percentage Change
           Worldwide")

    Trips

    Uber_trips = c(1818,3736,5220,6904,5025,6368)
    Lyft_trips = c(162.6,375.5,619.4,2100,NA,NA)
    Trips = data.frame(Year,Uber_tripsLyft_trips)
    
    Trips$Uber_Trip_change_perc = c(NA,100*diff(Trips$Uber_trips)/Trips$Uber_trips[c(1:5)])
    Trips$Lyft_Trip_change_perc = c(NA, 100*diff(Trips$Lyft_trips)/Trips$Lyft_trips[c(1:4)])
    
    Trips_gather1 = gather(Trips,key = "app_taxi",value = "Rides",2:3)
    Trips_gather2 = gather(Trips, key = "percentage", value = "perc",4:5)
    Trips_gather1$order = c(1:12)
    Trips_gather2$order = c(1:12)
    Trips_gather = merge(Trips_gather1[,c(1,4,5,6)],Trips_gather2[,c(4:6)],by = "order")
    
    ggplot(Trips_gather) + 
      aes(x = Year, y = Rides, fill = app_taxi) +
      geom_col() +
      scale_x_continuous(breaks = 2016:2021)+
      geom_text(aes(x = Year, 
                    y = Rides, 
                    label = ifelse(is.na(perc), "",paste(round(perc,1),"%"))),
                vjust = -0.5) +
      facet_wrap(~app_taxi) +
      labs(x = "",
           y = "Trips (In Millions)",
           title = "Uber and Lyft Trips and Percentage Change
           Worldwide")

    Analysis of 2020_High_Volume_FHV_Trip_Records dataset

  • 2020 High Volume FHV Trip Records
  • FHV = read.csv("2020_High_Volume_FHV_Trip_Records.csv")
    
    FHV_list = vector(mode = "list", length = 14)
    FHV_list
    #split the dataset into smaller datasets
    FHV_list[[1]] = FHV[1:10000000,]
    FHV_list[[2]] = FHV[10000001:20000000,]
    FHV_list[[3]] = FHV[20000001:30000000,]
    FHV_list[[4]] = FHV[30000001:40000000,]
    FHV_list[[5]] = FHV[40000001:50000000,]
    FHV_list[[6]] = FHV[50000001:60000000,]
    FHV_list[[7]] = FHV[60000001:70000000,]
    FHV_list[[8]] = FHV[70000001:80000000,]
    FHV_list[[9]] = FHV[80000001:90000000,]
    FHV_list[[10]] = FHV[90000001:100000000,]
    FHV_list[[11]] = FHV[100000001:110000000,]
    FHV_list[[12]] = FHV[110000001:120000000,]
    FHV_list[[13]] = FHV[120000001:130000000,]
    FHV_list[[14]] = FHV[130000001:nrow(FHV),]

    Each subset is about one gigabyte. It takes time to perform one single step. I just change FHV_list[[1]], to FHV_list[[2]], FHV_list[[3]]
FHV_list[[14]] manually, and run all the steps, then I got 14 subsets.

    FHV_list[[1]]$pickup_datetime = as.POSIXct(FHV_list[[1]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[1]]$dropoff_datetime = as.POSIXct(FHV_list[[1]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[1]]$Year = format(FHV_list[[1]]$pickup_datetime, format = "%Y")
    FHV_list[[1]]$Months = format(FHV_list[[1]]$pickup_datetime, format = "%m")
    FHV_list[[1]]$Time = format(FHV_list[[1]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[1]]$Trip_duration = difftime(FHV_list[[1]]$dropoff_datetime, FHV_list[[1]]$pickup_datetime, units = "hours")
    FHV_list[[1]]$Hours = format(FHV_list[[1]]$pickup_datetime, format = "%H")
    FHV_list[[1]]$Weekdays = weekdays(FHV_list[[1]]$pickup_datetime)
    FHV_list[[2]]$pickup_datetime = as.POSIXct(FHV_list[[2]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[2]]$dropoff_datetime = as.POSIXct(FHV_list[[2]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[2]]$Year = format(FHV_list[[2]]$pickup_datetime, format = "%Y")
    FHV_list[[2]]$Months = format(FHV_list[[2]]$pickup_datetime, format = "%m")
    FHV_list[[2]]$Time = format(FHV_list[[2]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[2]]$Trip_duration = difftime(FHV_list[[2]]$dropoff_datetime, FHV_list[[2]]$pickup_datetime, units = "hours")
    FHV_list[[2]]$Hours = format(FHV_list[[2]]$pickup_datetime, format = "%H")
    FHV_list[[2]]$Weekdays = weekdays(FHV_list[[2]]$pickup_datetime)
    FHV_list[[3]]$pickup_datetime = as.POSIXct(FHV_list[[3]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[3]]$dropoff_datetime = as.POSIXct(FHV_list[[3]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[3]]$Year = format(FHV_list[[3]]$pickup_datetime, format = "%Y")
    FHV_list[[3]]$Months = format(FHV_list[[3]]$pickup_datetime, format = "%m")
    FHV_list[[3]]$Time = format(FHV_list[[3]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[3]]$Trip_duration = difftime(FHV_list[[3]]$dropoff_datetime, FHV_list[[3]]$pickup_datetime, units = "hours")
    FHV_list[[3]]$Hours = format(FHV_list[[3]]$pickup_datetime, format = "%H")
    FHV_list[[3]]$Weekdays = weekdays(FHV_list[[3]]$pickup_datetime)
    FHV_list[[4]]$pickup_datetime = as.POSIXct(FHV_list[[4]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[4]]$dropoff_datetime = as.POSIXct(FHV_list[[4]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[4]]$Year = format(FHV_list[[4]]$pickup_datetime, format = "%Y")
    FHV_list[[4]]$Months = format(FHV_list[[4]]$pickup_datetime, format = "%m")
    FHV_list[[4]]$Time = format(FHV_list[[4]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[4]]$Trip_duration = difftime(FHV_list[[4]]$dropoff_datetime, FHV_list[[4]]$pickup_datetime, units = "hours")
    FHV_list[[4]]$Hours = format(FHV_list[[4]]$pickup_datetime, format = "%H")
    FHV_list[[4]]$Weekdays = weekdays(FHV_list[[4]]$pickup_datetime)
    FHV_list[[5]]$pickup_datetime = as.POSIXct(FHV_list[[5]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[5]]$dropoff_datetime = as.POSIXct(FHV_list[[5]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[5]]$Year = format(FHV_list[[5]]$pickup_datetime, format = "%Y")
    FHV_list[[5]]$Months = format(FHV_list[[5]]$pickup_datetime, format = "%m")
    FHV_list[[5]]$Time = format(FHV_list[[5]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[5]]$Trip_duration = difftime(FHV_list[[5]]$dropoff_datetime, FHV_list[[5]]$pickup_datetime, units = "hours")
    FHV_list[[5]]$Hours = format(FHV_list[[5]]$pickup_datetime, format = "%H")
    FHV_list[[5]]$Weekdays = weekdays(FHV_list[[5]]$pickup_datetime)
    FHV_list[[6]]$pickup_datetime = as.POSIXct(FHV_list[[6]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[6]]$dropoff_datetime = as.POSIXct(FHV_list[[6]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[6]]$Year = format(FHV_list[[6]]$pickup_datetime, format = "%Y")
    FHV_list[[6]]$Months = format(FHV_list[[6]]$pickup_datetime, format = "%m")
    FHV_list[[6]]$Time = format(FHV_list[[6]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[6]]$Trip_duration = difftime(FHV_list[[6]]$dropoff_datetime, FHV_list[[6]]$pickup_datetime, units = "hours")
    FHV_list[[6]]$Hours = format(FHV_list[[6]]$pickup_datetime, format = "%H")
    FHV_list[[6]]$Weekdays = weekdays(FHV_list[[6]]$pickup_datetime)
    FHV_list[[7]]$pickup_datetime = as.POSIXct(FHV_list[[7]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[7]]$dropoff_datetime = as.POSIXct(FHV_list[[7]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[7]]$Year = format(FHV_list[[7]]$pickup_datetime, format = "%Y")
    FHV_list[[7]]$Months = format(FHV_list[[7]]$pickup_datetime, format = "%m")
    FHV_list[[7]]$Time = format(FHV_list[[7]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[7]]$Trip_duration = difftime(FHV_list[[7]]$dropoff_datetime, FHV_list[[7]]$pickup_datetime, units = "hours")
    FHV_list[[7]]$Hours = format(FHV_list[[7]]$pickup_datetime, format = "%H")
    FHV_list[[7]]$Weekdays = weekdays(FHV_list[[7]]$pickup_datetime)
    FHV_list[[8]]$pickup_datetime = as.POSIXct(FHV_list[[8]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[8]]$dropoff_datetime = as.POSIXct(FHV_list[[8]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[8]]$Year = format(FHV_list[[8]]$pickup_datetime, format = "%Y")
    FHV_list[[8]]$Months = format(FHV_list[[8]]$pickup_datetime, format = "%m")
    FHV_list[[8]]$Time = format(FHV_list[[8]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[8]]$Trip_duration = difftime(FHV_list[[8]]$dropoff_datetime, FHV_list[[8]]$pickup_datetime, units = "hours")
    FHV_list[[8]]$Hours = format(FHV_list[[8]]$pickup_datetime, format = "%H")
    FHV_list[[8]]$Weekdays = weekdays(FHV_list[[8]]$pickup_datetime)
    FHV_list[[9]]$pickup_datetime = as.POSIXct(FHV_list[[9]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[9]]$dropoff_datetime = as.POSIXct(FHV_list[[9]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[9]]$Year = format(FHV_list[[9]]$pickup_datetime, format = "%Y")
    FHV_list[[9]]$Months = format(FHV_list[[9]]$pickup_datetime, format = "%m")
    FHV_list[[9]]$Time = format(FHV_list[[9]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[9]]$Trip_duration = difftime(FHV_list[[9]]$dropoff_datetime, FHV_list[[9]]$pickup_datetime, units = "hours")
    FHV_list[[9]]$Hours = format(FHV_list[[9]]$pickup_datetime, format = "%H")
    FHV_list[[9]]$Weekdays = weekdays(FHV_list[[9]]$pickup_datetime)
    FHV_list[[10]]$pickup_datetime = as.POSIXct(FHV_list[[10]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[10]]$dropoff_datetime = as.POSIXct(FHV_list[[10]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[10]]$Year = format(FHV_list[[10]]$pickup_datetime, format = "%Y")
    FHV_list[[10]]$Months = format(FHV_list[[10]]$pickup_datetime, format = "%m")
    FHV_list[[10]]$Time = format(FHV_list[[10]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[10]]$Trip_duration = difftime(FHV_list[[10]]$dropoff_datetime, FHV_list[[10]]$pickup_datetime, units = "hours")
    FHV_list[[10]]$Hours = format(FHV_list[[10]]$pickup_datetime, format = "%H")
    FHV_list[[10]]$Weekdays = weekdays(FHV_list[[10]]$pickup_datetime)
    FHV_list[[11]]$pickup_datetime = as.POSIXct(FHV_list[[11]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[11]]$dropoff_datetime = as.POSIXct(FHV_list[[11]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[11]]$Year = format(FHV_list[[11]]$pickup_datetime, format = "%Y")
    FHV_list[[11]]$Months = format(FHV_list[[11]]$pickup_datetime, format = "%m")
    FHV_list[[11]]$Time = format(FHV_list[[11]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[11]]$Trip_duration = difftime(FHV_list[[11]]$dropoff_datetime, FHV_list[[11]]$pickup_datetime, units = "hours")
    FHV_list[[11]]$Hours = format(FHV_list[[11]]$pickup_datetime, format = "%H")
    FHV_list[[11]]$Weekdays = weekdays(FHV_list[[11]]$pickup_datetime)
    FHV_list[[12]]$pickup_datetime = as.POSIXct(FHV_list[[12]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[12]]$dropoff_datetime = as.POSIXct(FHV_list[[12]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[12]]$Year = format(FHV_list[[12]]$pickup_datetime, format = "%Y")
    FHV_list[[12]]$Months = format(FHV_list[[12]]$pickup_datetime, format = "%m")
    FHV_list[[12]]$Time = format(FHV_list[[12]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[12]]$Trip_duration = difftime(FHV_list[[12]]$dropoff_datetime, FHV_list[[12]]$pickup_datetime, units = "hours")
    FHV_list[[12]]$Hours = format(FHV_list[[12]]$pickup_datetime, format = "%H")
    FHV_list[[12]]$Weekdays = weekdays(FHV_list[[12]]$pickup_datetime)
    FHV_list[[13]]$pickup_datetime = as.POSIXct(FHV_list[[13]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[13]]$dropoff_datetime = as.POSIXct(FHV_list[[13]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[13]]$Year = format(FHV_list[[13]]$pickup_datetime, format = "%Y")
    FHV_list[[13]]$Months = format(FHV_list[[13]]$pickup_datetime, format = "%m")
    FHV_list[[13]]$Time = format(FHV_list[[13]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[13]]$Trip_duration = difftime(FHV_list[[13]]$dropoff_datetime, FHV_list[[13]]$pickup_datetime, units = "hours")
    FHV_list[[13]]$Hours = format(FHV_list[[13]]$pickup_datetime, format = "%H")
    FHV_list[[13]]$Weekdays = weekdays(FHV_list[[13]]$pickup_datetime)
    FHV_list[[14]]$pickup_datetime = as.POSIXct(FHV_list[[14]]$pickup_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    FHV_list[[14]]$dropoff_datetime = as.POSIXct(FHV_list[[14]]$dropoff_datetime, format = "%m/%d/%Y %I:%M:%S %p",tz = Sys.timezone())
    # create Year, Months, Time, Trip_duration and Hours columns
    FHV_list[[14]]$Year = format(FHV_list[[14]]$pickup_datetime, format = "%Y")
    FHV_list[[14]]$Months = format(FHV_list[[14]]$pickup_datetime, format = "%m")
    FHV_list[[14]]$Time = format(FHV_list[[14]]$pickup_datetime, format = "%H:%M:%S")
    FHV_list[[14]]$Trip_duration = difftime(FHV_list[[14]]$dropoff_datetime, FHV_list[[14]]$pickup_datetime, units = "hours")
    FHV_list[[14]]$Hours = format(FHV_list[[14]]$pickup_datetime, format = "%H")
    FHV_list[[14]]$Weekdays = weekdays(FHV_list[[14]]$pickup_datetime)
    # modify global options
    options(scipen = 999) 
    #write.csv(FHV0, "FHV0.csv")

    Calculate sum of trip duration and number of trips of each subset

    FHV_group_list = vector(mode = "list",length = 14)
    for (i in 1:14){
      FHV_group_list[[i]] = FHV_list[[i]] %>%
      group_by(Year,Months,Hours,Weekdays,dispatching_base_num) %>%
      drop_na(Trip_duration) %>%
      summarize(Total_trip_duration = sum(Trip_duration),
                Total_number_of_trips = n())
    }

    Combine all the subsets

    FHV_group = do.call("rbind", list(FHV_group_list[[1]],FHV_group_list[[2]],FHV_group_list[[3]],
         FHV_group_list[[4]],FHV_group_list[[5]],FHV_group_list[[6]],                               FHV_group_list[[7]],FHV_group_list[[8]],FHV_group_list[[9]],
         FHV_group_list[[10]],FHV_group_list[[11]],FHV_group_list[[12]],
         FHV_group_list[[13]],FHV_group_list[[14]],))
    #write.csv(FHV_group,"FHV_group.csv")
    FHV_group = read.csv("FHV_group.csv")
  • A Listing of TLC Licensed Bases
  • find_a_ride = read.csv("find_a_ride1.csv")
    # trim whitespace of License_number
    find_a_ride$License_number = trimws(find_a_ride$License_number)
    
    FHV_ride = left_join(FHV_group,find_a_ride, by=(c("dispatching_base_num" = "License_number")))
    # extra WAV_Dispatcher from Name_of_Base
    FHV_ride$Name_of_Base = str_replace_all(FHV_ride$Name_of_Base, "-" , " ")
    FHV_ride$WAV_Dispatcher = word(FHV_ride$Name_of_Base, 1)
    FHV_ride$WAV_Dispatcher = str_replace_all(FHV_ride$WAV_Dispatcher, c("FLATIRON" = "Via","GREENPOINT" = "Via"))
    
    # remove "hours" in Total_trip_duration
    FHV_ride$Total_trip_duration = as.numeric(word(FHV_ride$Total_trip_duration,1))
    
    # filter out data of Via
    FHV_ride = FHV_ride[which(FHV_ride$WAV_Dispatcher != "Via"),]
    # convert Months, Hours, Weekdays to factors
    FHV_ride[,c(2:5)][]= lapply(FHV_ride[,c(2:5)],as.factor)
    #write.csv(FHV_ride[-1],"FHV_ride.csv")
    FHV_ride = read.csv("FHV_ride.csv")
    FHV_ride
    # Total Distance, Total Revenue and Total Trip Duration of NYC Green Taxi in 2020
    ggplot(FHV_ride) +
      aes(x = Months, y =  Total_number_of_trips, fill = WAV_Dispatcher) +
      geom_col() + 
      scale_x_continuous(breaks = 1:12) +
      scale_y_continuous(labels = format_format(big.mark = " ", 
                                                decimal.mark = ",", 
                                                scientific = FALSE)) +
     # geom_text(aes(label = Total_number_of_trips), vjust = -1) +
      labs(x = 'Months',
           y = "Number of Trips",
           title = "Number of Trips of NYC 
           Uber and Lyft in 2020") 
    
     
    #  Total Trip Duration in 24 hours
    ggplot(FHV_ride) +
      aes(x = Hours, y =  Total_trip_duration, fill = WAV_Dispatcher) +
      geom_col()+
      scale_x_continuous(breaks = 1:23) +
      scale_y_continuous(labels = format_format(big.mark = " ", 
                                                decimal.mark = ",", 
                                                scientific = FALSE)) +
    #  geom_text(aes(label = Total_number_of_trips), vjust = -1)+
    #  coord_flip()+
      labs(x = 'Hours',
           y = "Total Trip Duration (in hours)",
           title = "Total Trip Duration in 24 hours 
           of Uber and Lyft in 2020") 
    
    ggplot(FHV_ride) +
        aes(x = reorder(Weekdays,Total_trip_duration), y = Total_trip_duration, fill = WAV_Dispatcher) +
        geom_col() +
        scale_y_continuous(labels = format_format(big.mark = " ", 
                                                decimal.mark = ",", 
                                                scientific = FALSE)) +
        labs(x = 'Weekdays',
           y = "Total Trip Duration (in hours)",
           title = "Total Trip Duration in Weekdays 
           of NYC Uber and Lyft in 2020") 
    
    FHV6 = read.csv("FHV6.csv")
    FHV6

    Calculate trip duration base on location ID of each subset

    FHV_location6 = FHV6 %>%
      group_by(PULocationID, DOLocationID,) %>%
      summarize(Total_trip_duration = sum(Trip_duration),
                Total_trips = n()) 
    

    Combine all the FHV_location subsets

    FHV_location = rbind(FHV_location[,c(2:5)],FHV_location6)
    
    #write.csv(FHV_location, "FHV_location.csv")
    Merge with datasets taxi+_zone_lookup.csv and taxi zone map
  • Taxi Zone Lookup Table
  • taxi_zones = read.csv("taxi+_zone_lookup.csv")
    

    Calculate the trip duration and total trips base on pickup location

    pickUp = FHV_location %>%
      filter(PULocationID != 264 & PULocationID != 265 & !is.na(Total_trip_duration)) %>% # filter out unknown borough
      group_by(PULocationID) %>%
      summarise(Total_trip_duration = sum(Total_trip_duration),
                Total_trips = sum(Total_trips)) 
    

    Combine pickUp and taxi_zones

    pickUp = left_join(pickUp,taxi_zones, by = c("PULocationID" = "LocationID"))
    
    # read GeoJSON file of NYC Taxi zone
    js = FROM_GeoJson(url_file_string = "https://data.cityofnewyork.us/api/geospatial/d3c5-ddgc?method=export&format=GeoJSON")
    
    # create a datafram with longitude and latitude of NYC neighborhood 
    NYC_coord = lapply(1:length(js$features), 
             function(i){
               if(!is.list(js$features[[i]]$geometry$coordinates)){
               tmpdata <- js$features[[i]]$geometry$coordinates
             }else{
               tmpdata <- js$features[[i]]$geometry$coordinates[[1]]} 
               
    tmpdata %>%
       data.frame() %>% 
       tibble %>%
       mutate(zone = js$features[[i]]$properties$zone,
              borough = js$features[[i]]$properties$borough) %>% 
              rename("long" = 'X1', 'lat'='X2')}) %>%
       bind_rows()
    pickUp_coord = left_join(pickUp, NYC_coord, by = c("Zone"="zone"))
    
     
    #write.csv(pickUp_coord,"pickUp_coord.csv")
    pickUp_coord = read.csv("pickUp_coord.csv")
    

    Top 5 locations

    Top_LocationID = pickUp_coord %>%
      filter(Total_trip_duration > 480000) %>%
      count(Zone)
      

    Calculate the coordinates of the top 5 locations

    center_coord = NYC_coord %>%
      group_by(zone) %>%
      summarise(long = mean(long),lat = mean(lat)) %>%
      right_join(Top_LocationID, by = c("zone" = "Zone"))
    
    #write.csv(center_coord,"center_coord.csv")
    ggplot(pickUp_coord)+
      aes(x = long, y = lat,group = Zone, fill =Total_trip_duration) +
      geom_polygon() +
     with(center_coord, 
          annotate(geom="text", x = long, y=lat, 
                   label = zone, 
                   face="bold",
                   size = 5)) +
      scale_fill_gradient(low = "white", high = "blue") +
      labs(title = "Total Trip Duration Hours in Different Pick Up Locations 
           (Uber and Lyft, 2020)")
      
    dropOff = FHV_location %>%
      filter(DOLocationID != 264 & DOLocationID != 265 & !is.na(Total_trip_duration)) %>% # filter out unknown borough
      group_by(DOLocationID) %>%
      summarise(Total_trip_duration = sum(Total_trip_duration),
                Total_trips = sum(Total_trips)) 
    dropOff
    dropOff_coord = left_join(dropOff, NYC_coord, by = c("Zone"="zone"))
    
    Top_DLocationID = dropOff_coord %>%
      filter(!is.na(Zone)) %>%
      filter(Total_trip_duration > 390000) %>%
      count(Zone)
      
    Top_DLocationID  
    center_Dcoord = NYC_coord %>%
      group_by(zone) %>%
      summarise(long = mean(long),lat = mean(lat)) %>%
      right_join(Top_DLocationID, by = c("zone" = "Zone"))
    center_Dcoord
    #write.csv(center_Dcoord,"center_Dcoord.csv")
    ggplot(dropOff_coord)+
      aes(x = long, y = lat,group = Zone, fill =Total_trips) +
      geom_polygon() +
      with(center_Dcoord, 
          annotate(geom="text", x = long, y=lat, 
                   label = zone,
                   face="bold",
                   size = 5)) +
      scale_fill_gradient(low = "white", high = "red") +
      labs(title = "Total Trips in Different Pick Up Locations 
           (Uber and Lyft, 2020)")
      
    LS0tDQp0aXRsZTogIlViZXIgdnMuIEx5ZnQgSW4gTllDIg0KYXV0aG9yOiAiWmlxaSBQb2xpbWVyb3MiDQpkYXRlOiAiNS8xNy8yMDIyIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQpgYGB7cn0NCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkoc2NhbGVzKSAjIHkgYXhpcw0KbGlicmFyeShyZ2RhbCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpICMgY29udmVydCB0aW1lIHRvIDI0IGhvdXJzLGNvbnZlcnQgZGF5cyB0byB3ZWVrZGF5cw0KbGlicmFyeShnZW9qc29uUikgIyByZWFkIGdlb2pzb24gZmlsZQ0KbGlicmFyeShnZW9qc29uaW8pDQpsaWJyYXJ5KHNoaW55KQ0KDQpgYGANCg0KDQo8aDM+QW5hbHlzaXMgb2YgQW5udWFsIEZpbmFuY2lhbCBSZXBvcnRzPC9oMz4NCg0KPGxpPg0KPGEgaHJlZj0iaHR0cHM6Ly9pbnZlc3Rvci5seWZ0LmNvbS9maW5hbmNpYWxzLWFuZC1yZXBvcnRzL2FubnVhbC1yZXBvcnRzL2RlZmF1bHQuYXNweCI+THlmdCBBbm51YWwgUmVwb3J0PC9hPg0KPC9saT4NCjxsaT4NCjxhIGhyZWY9Imh0dHBzOi8vaW52ZXN0b3IudWJlci5jb20vZmluYW5jaWFscy9kZWZhdWx0LmFzcHgiPlViZXIgQW5udWFsIFJlcG9ydDwvYT4NCjwvbGk+DQoNCjxiPlJldmVudWU8L2I+DQoNCmBgYHtyfQ0KIyBpbiBtaWxsaW9uIGRvbGxhcnMNClllYXIgPSBjKDIwMTY6MjAyMSkNCiANClViZXJfUmV2ZW51ZSA9IGMoMzMzOCw3NDAyLDEwNDMzLDEzMDAwLDExMTM5LDE3NDU1KQ0KTHlmdF9SZXZlbnVlID0gYygzNDMsMTA2MCwyMTU3LDM2MTYsMjM2NSwzMjA4KQ0KUmV2ZW51ZSA9IGRhdGEuZnJhbWUoWWVhcixVYmVyX1JldmVudWUsTHlmdF9SZXZlbnVlKQ0KDQpgYGANCg0KDQpDcmVhdGUgcmV2ZW51ZSBjaGFuZ2UgcGVyY2VudGFnZSBjb2x1bW5zDQpgYGB7cn0NClJldmVudWUkVWJlcl9SZXZlbnVlX2NoYW5nZV9wZXJjID0gYyhOQSwxMDAqZGlmZihSZXZlbnVlJFViZXJfUmV2ZW51ZSkvUmV2ZW51ZSRVYmVyX1JldmVudWVbYygxOjUpXSkNClJldmVudWUkTHlmdF9SZXZlbnVlX2NoYW5nZV9wZXJjID0gYyhOQSwxMDAqZGlmZihSZXZlbnVlJEx5ZnRfUmV2ZW51ZSkvUmV2ZW51ZSRMeWZ0X1JldmVudWVbYygxOjUpXSkNCmBgYA0KDQoNCmBgYHtyfQ0KUmV2ZW51ZV9nYXRoZXIxID0gZ2F0aGVyKFJldmVudWUsa2V5ID0gImFwcF90YXhpIix2YWx1ZSA9ICJSZXZlbnVlIiwyOjMpDQpSZXZlbnVlX2dhdGhlcjIgPSBnYXRoZXIoUmV2ZW51ZSwga2V5ID0gInBlcmNlbnRhZ2UiLCB2YWx1ZSA9ICJwZXJjIiw0OjUpDQpSZXZlbnVlX2dhdGhlcjEkb3JkZXIgPSBjKDE6MTIpDQpSZXZlbnVlX2dhdGhlcjIkb3JkZXIgPSBjKDE6MTIpDQpSZXZlbnVlX2dhdGhlciA9IG1lcmdlKFJldmVudWVfZ2F0aGVyMVssYygxLDQsNSw2KV0sUmV2ZW51ZV9nYXRoZXIyWyxjKDQ6NildLGJ5ID0gIm9yZGVyIikNCg0KYGBgDQoNCg0KDQpgYGB7cn0NCmdncGxvdChSZXZlbnVlX2dhdGhlcikgKyANCiAgYWVzKHggPSBZZWFyLCB5ID0gUmV2ZW51ZSwgZmlsbCA9IGFwcF90YXhpKSArDQogIGdlb21fY29sKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMjAxNjoyMDIxKSsNCiAgZ2VvbV90ZXh0KGFlcyh4ID0gWWVhciwgDQogICAgICAgICAgICAgICAgeSA9IFJldmVudWUsIA0KICAgICAgICAgICAgICAgIGxhYmVsID0gaWZlbHNlKGlzLm5hKHBlcmMpLCAiIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZShyb3VuZChwZXJjLDEpLCIlIikpKSwNCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSkgKw0KICBmYWNldF93cmFwKH5hcHBfdGF4aSkgKw0KICBsYWJzKHggPSAiIiwNCiAgICAgICB5ID0gIlRvdGFsIFJldmVudWUgKEluIE1pbGxpb24gRG9sbGFycykiLA0KICAgICAgIHRpdGxlID0gIlRvdGFsIFJldmVudWUgYW5kIFBlcmNlbnRhZ2UgQ2hhbmdlDQogICAgICAgV29ybGR3aWRlIikNCmBgYA0KDQoNCjxiPiBBY3RpdmUgUmlkZXJzIDwvYj4NCg0KYGBge3J9DQpZZWFyID0gYygyMDE2OjIwMjEpDQpVYmVyX0FjdGl2ZV9SaWRlcnMgPSBjKDQ1LDY4LDkxLDExMSw5MywxMTgpDQpMeWZ0X0FjdGl2ZV9SaWRlcnMgPSBjKDYuNiwxMi42LDE4LjYsMjIuOTA1LDEyLjU1MiwxOC43MjgpDQpjb25zdW1lcnMgPSBkYXRhLmZyYW1lKFllYXIsVWJlcl9BY3RpdmVfUmlkZXJzLEx5ZnRfQWN0aXZlX1JpZGVycykNCg0KYGBgDQoNCg0KYGBge3J9DQpjb25zdW1lcnMkVWJlcl9SaWRlcl9jaGFuZ2VfcGVyYyA9IGMoTkEsMTAwKmRpZmYoY29uc3VtZXJzJFViZXJfQWN0aXZlX1JpZGVycykvY29uc3VtZXJzJFViZXJfQWN0aXZlX1JpZGVyc1tjKDE6NSldKQ0KY29uc3VtZXJzJEx5ZnRfUmlkZXJfY2hhbmdlX3BlcmMgPSBjKE5BLDEwMCpkaWZmKGNvbnN1bWVycyRMeWZ0X0FjdGl2ZV9SaWRlcnMpL2NvbnN1bWVycyRMeWZ0X0FjdGl2ZV9SaWRlcnNbYygxOjUpXSkNCg0KDQpgYGANCg0KDQpgYGB7cn0NCmNvbnN1bWVyc19nYXRoZXIxID0gZ2F0aGVyKGNvbnN1bWVycyxrZXkgPSAiYXBwX3RheGkiLHZhbHVlID0gIlJpZGVycyIsMjozKQ0KY29uc3VtZXJzX2dhdGhlcjIgPSBnYXRoZXIoY29uc3VtZXJzLCBrZXkgPSAicGVyY2VudGFnZSIsIHZhbHVlID0gInBlcmMiLDQ6NSkNCmNvbnN1bWVyc19nYXRoZXIxJG9yZGVyID0gYygxOjEyKQ0KY29uc3VtZXJzX2dhdGhlcjIkb3JkZXIgPSBjKDE6MTIpDQpjb25zdW1lcnNfZ2F0aGVyID0gbWVyZ2UoY29uc3VtZXJzX2dhdGhlcjFbLGMoMSw0LDUsNildLGNvbnN1bWVyc19nYXRoZXIyWyxjKDQ6NildLGJ5ID0gIm9yZGVyIikNCg0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoY29uc3VtZXJzX2dhdGhlcikgKyANCiAgYWVzKHggPSBZZWFyLCB5ID0gUmlkZXJzLCBmaWxsID0gYXBwX3RheGkpICsNCiAgZ2VvbV9jb2woKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAyMDE2OjIwMjEpKw0KICBnZW9tX3RleHQoYWVzKHggPSBZZWFyLCANCiAgICAgICAgICAgICAgICB5ID0gUmlkZXJzLCANCiAgICAgICAgICAgICAgICBsYWJlbCA9IGlmZWxzZShpcy5uYShwZXJjKSwgIiIscGFzdGUocm91bmQocGVyYywxKSwiJSIpKSksDQogICAgICAgICAgICB2anVzdCA9IC0wLjUpICsNCiAgZmFjZXRfd3JhcCh+YXBwX3RheGkpICsNCiAgbGFicyh4ID0gIiIsDQogICAgICAgeSA9ICJBY3RpdmUgUmlkZXJzIChJbiBNaWxsaW9ucykiLA0KICAgICAgIHRpdGxlID0gIlViZXIgYW5kIEx5ZnQgQWN0aXZlIFJpZGVycyBhbmQgUGVyY2VudGFnZSBDaGFuZ2UNCiAgICAgICBXb3JsZHdpZGUiKQ0KYGBgDQoNCg0KPGI+IFRyaXBzIDwvYj4NCmBgYHtyfQ0KVWJlcl90cmlwcyA9IGMoMTgxOCwzNzM2LDUyMjAsNjkwNCw1MDI1LDYzNjgpDQpMeWZ0X3RyaXBzID0gYygxNjIuNiwzNzUuNSw2MTkuNCwyMTAwLE5BLE5BKQ0KVHJpcHMgPSBkYXRhLmZyYW1lKFllYXIsVWJlcl90cmlwc++8jEx5ZnRfdHJpcHMpDQoNCmBgYA0KDQoNCg0KYGBge3J9DQpUcmlwcyRVYmVyX1RyaXBfY2hhbmdlX3BlcmMgPSBjKE5BLDEwMCpkaWZmKFRyaXBzJFViZXJfdHJpcHMpL1RyaXBzJFViZXJfdHJpcHNbYygxOjUpXSkNClRyaXBzJEx5ZnRfVHJpcF9jaGFuZ2VfcGVyYyA9IGMoTkEsIDEwMCpkaWZmKFRyaXBzJEx5ZnRfdHJpcHMpL1RyaXBzJEx5ZnRfdHJpcHNbYygxOjQpXSkNCg0KDQpgYGANCg0KYGBge3J9DQpUcmlwc19nYXRoZXIxID0gZ2F0aGVyKFRyaXBzLGtleSA9ICJhcHBfdGF4aSIsdmFsdWUgPSAiUmlkZXMiLDI6MykNClRyaXBzX2dhdGhlcjIgPSBnYXRoZXIoVHJpcHMsIGtleSA9ICJwZXJjZW50YWdlIiwgdmFsdWUgPSAicGVyYyIsNDo1KQ0KVHJpcHNfZ2F0aGVyMSRvcmRlciA9IGMoMToxMikNClRyaXBzX2dhdGhlcjIkb3JkZXIgPSBjKDE6MTIpDQpUcmlwc19nYXRoZXIgPSBtZXJnZShUcmlwc19nYXRoZXIxWyxjKDEsNCw1LDYpXSxUcmlwc19nYXRoZXIyWyxjKDQ6NildLGJ5ID0gIm9yZGVyIikNCg0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KFRyaXBzX2dhdGhlcikgKyANCiAgYWVzKHggPSBZZWFyLCB5ID0gUmlkZXMsIGZpbGwgPSBhcHBfdGF4aSkgKw0KICBnZW9tX2NvbCgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDIwMTY6MjAyMSkrDQogIGdlb21fdGV4dChhZXMoeCA9IFllYXIsIA0KICAgICAgICAgICAgICAgIHkgPSBSaWRlcywgDQogICAgICAgICAgICAgICAgbGFiZWwgPSBpZmVsc2UoaXMubmEocGVyYyksICIiLHBhc3RlKHJvdW5kKHBlcmMsMSksIiUiKSkpLA0KICAgICAgICAgICAgdmp1c3QgPSAtMC41KSArDQogIGZhY2V0X3dyYXAofmFwcF90YXhpKSArDQogIGxhYnMoeCA9ICIiLA0KICAgICAgIHkgPSAiVHJpcHMgKEluIE1pbGxpb25zKSIsDQogICAgICAgdGl0bGUgPSAiVWJlciBhbmQgTHlmdCBUcmlwcyBhbmQgUGVyY2VudGFnZSBDaGFuZ2UNCiAgICAgICBXb3JsZHdpZGUiKQ0KYGBgDQoNCg0KDQoNCkFuYWx5c2lzIG9mIDIwMjBfSGlnaF9Wb2x1bWVfRkhWX1RyaXBfUmVjb3JkcyBkYXRhc2V0DQoNCjxsaT4NCjxhIGhyZWY9Imh0dHBzOi8vZGF0YS5jaXR5b2ZuZXd5b3JrLnVzL1RyYW5zcG9ydGF0aW9uLzIwMjAtSGlnaC1Wb2x1bWUtRkhWLVRyaXAtUmVjb3Jkcy95cnQ5LTU4ZzgiPjIwMjAgSGlnaCBWb2x1bWUgRkhWIFRyaXAgUmVjb3JkczwvYT4NCjwvbGk+DQoNCmBgYHtyfQ0KRkhWID0gcmVhZC5jc3YoIjIwMjBfSGlnaF9Wb2x1bWVfRkhWX1RyaXBfUmVjb3Jkcy5jc3YiKQ0KDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdCA9IHZlY3Rvcihtb2RlID0gImxpc3QiLCBsZW5ndGggPSAxNCkNCkZIVl9saXN0DQpgYGANCg0KYGBge3J9DQojc3BsaXQgdGhlIGRhdGFzZXQgaW50byBzbWFsbGVyIGRhdGFzZXRzDQpGSFZfbGlzdFtbMV1dID0gRkhWWzE6MTAwMDAwMDAsXQ0KRkhWX2xpc3RbWzJdXSA9IEZIVlsxMDAwMDAwMToyMDAwMDAwMCxdDQpGSFZfbGlzdFtbM11dID0gRkhWWzIwMDAwMDAxOjMwMDAwMDAwLF0NCkZIVl9saXN0W1s0XV0gPSBGSFZbMzAwMDAwMDE6NDAwMDAwMDAsXQ0KRkhWX2xpc3RbWzVdXSA9IEZIVls0MDAwMDAwMTo1MDAwMDAwMCxdDQpGSFZfbGlzdFtbNl1dID0gRkhWWzUwMDAwMDAxOjYwMDAwMDAwLF0NCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s3XV0gPSBGSFZbNjAwMDAwMDE6NzAwMDAwMDAsXQ0KRkhWX2xpc3RbWzhdXSA9IEZIVls3MDAwMDAwMTo4MDAwMDAwMCxdDQpGSFZfbGlzdFtbOV1dID0gRkhWWzgwMDAwMDAxOjkwMDAwMDAwLF0NCkZIVl9saXN0W1sxMF1dID0gRkhWWzkwMDAwMDAxOjEwMDAwMDAwMCxdDQpGSFZfbGlzdFtbMTFdXSA9IEZIVlsxMDAwMDAwMDE6MTEwMDAwMDAwLF0NCkZIVl9saXN0W1sxMl1dID0gRkhWWzExMDAwMDAwMToxMjAwMDAwMDAsXQ0KRkhWX2xpc3RbWzEzXV0gPSBGSFZbMTIwMDAwMDAxOjEzMDAwMDAwMCxdDQpGSFZfbGlzdFtbMTRdXSA9IEZIVlsxMzAwMDAwMDE6bnJvdyhGSFYpLF0NCmBgYA0KDQpFYWNoIHN1YnNldCBpcyBhYm91dCBvbmUgZ2lnYWJ5dGUuIEl0IHRha2VzIHRpbWUgdG8gcGVyZm9ybSBvbmUgc2luZ2xlIHN0ZXAuIA0KSSBqdXN0IGNoYW5nZSBGSFZfbGlzdFtbMV1dLCB0byBGSFZfbGlzdFtbMl1dLCBGSFZfbGlzdFtbM11dLi4uRkhWX2xpc3RbWzE0XV0gbWFudWFsbHksIGFuZCBydW4gYWxsIHRoZSBzdGVwcywgdGhlbiBJIGdvdCAxNCBzdWJzZXRzLg0KDQpgYGB7cn0NCkZIVl9saXN0W1sxXV0kcGlja3VwX2RhdGV0aW1lID0gYXMuUE9TSVhjdChGSFZfbGlzdFtbMV1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIix0eiA9IFN5cy50aW1lem9uZSgpKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzFdXSRkcm9wb2ZmX2RhdGV0aW1lID0gYXMuUE9TSVhjdChGSFZfbGlzdFtbMV1dJGRyb3BvZmZfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIsdHogPSBTeXMudGltZXpvbmUoKSkNCmBgYA0KDQpgYGB7cn0NCiMgY3JlYXRlIFllYXIsIE1vbnRocywgVGltZSwgVHJpcF9kdXJhdGlvbiBhbmQgSG91cnMgY29sdW1ucw0KRkhWX2xpc3RbWzFdXSRZZWFyID0gZm9ybWF0KEZIVl9saXN0W1sxXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJVkiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzFdXSRNb250aHMgPSBmb3JtYXQoRkhWX2xpc3RbWzFdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbSIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMV1dJFRpbWUgPSBmb3JtYXQoRkhWX2xpc3RbWzFdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlSDolTTolUyIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMV1dJFRyaXBfZHVyYXRpb24gPSBkaWZmdGltZShGSFZfbGlzdFtbMV1dJGRyb3BvZmZfZGF0ZXRpbWUsIEZIVl9saXN0W1sxXV0kcGlja3VwX2RhdGV0aW1lLCB1bml0cyA9ICJob3VycyIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMV1dJEhvdXJzID0gZm9ybWF0KEZIVl9saXN0W1sxXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJUgiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzFdXSRXZWVrZGF5cyA9IHdlZWtkYXlzKEZIVl9saXN0W1sxXV0kcGlja3VwX2RhdGV0aW1lKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzJdXSRwaWNrdXBfZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KEZIVl9saXN0W1syXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiLHR6ID0gU3lzLnRpbWV6b25lKCkpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMl1dJGRyb3BvZmZfZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KEZIVl9saXN0W1syXV0kZHJvcG9mZl9kYXRldGltZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIix0eiA9IFN5cy50aW1lem9uZSgpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjcmVhdGUgWWVhciwgTW9udGhzLCBUaW1lLCBUcmlwX2R1cmF0aW9uIGFuZCBIb3VycyBjb2x1bW5zDQpGSFZfbGlzdFtbMl1dJFllYXIgPSBmb3JtYXQoRkhWX2xpc3RbWzJdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlWSIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMl1dJE1vbnRocyA9IGZvcm1hdChGSFZfbGlzdFtbMl1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVtIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1syXV0kVGltZSA9IGZvcm1hdChGSFZfbGlzdFtbMl1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVIOiVNOiVTIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1syXV0kVHJpcF9kdXJhdGlvbiA9IGRpZmZ0aW1lKEZIVl9saXN0W1syXV0kZHJvcG9mZl9kYXRldGltZSwgRkhWX2xpc3RbWzJdXSRwaWNrdXBfZGF0ZXRpbWUsIHVuaXRzID0gImhvdXJzIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1syXV0kSG91cnMgPSBmb3JtYXQoRkhWX2xpc3RbWzJdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlSCIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMl1dJFdlZWtkYXlzID0gd2Vla2RheXMoRkhWX2xpc3RbWzJdXSRwaWNrdXBfZGF0ZXRpbWUpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbM11dJHBpY2t1cF9kYXRldGltZSA9IGFzLlBPU0lYY3QoRkhWX2xpc3RbWzNdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIsdHogPSBTeXMudGltZXpvbmUoKSkNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1szXV0kZHJvcG9mZl9kYXRldGltZSA9IGFzLlBPU0lYY3QoRkhWX2xpc3RbWzNdXSRkcm9wb2ZmX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiLHR6ID0gU3lzLnRpbWV6b25lKCkpDQpgYGANCg0KYGBge3J9DQojIGNyZWF0ZSBZZWFyLCBNb250aHMsIFRpbWUsIFRyaXBfZHVyYXRpb24gYW5kIEhvdXJzIGNvbHVtbnMNCkZIVl9saXN0W1szXV0kWWVhciA9IGZvcm1hdChGSFZfbGlzdFtbM11dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVZIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1szXV0kTW9udGhzID0gZm9ybWF0KEZIVl9saXN0W1szXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0iKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzNdXSRUaW1lID0gZm9ybWF0KEZIVl9saXN0W1szXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJUg6JU06JVMiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzNdXSRUcmlwX2R1cmF0aW9uID0gZGlmZnRpbWUoRkhWX2xpc3RbWzNdXSRkcm9wb2ZmX2RhdGV0aW1lLCBGSFZfbGlzdFtbM11dJHBpY2t1cF9kYXRldGltZSwgdW5pdHMgPSAiaG91cnMiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzNdXSRIb3VycyA9IGZvcm1hdChGSFZfbGlzdFtbM11dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVIIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1szXV0kV2Vla2RheXMgPSB3ZWVrZGF5cyhGSFZfbGlzdFtbM11dJHBpY2t1cF9kYXRldGltZSkNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s0XV0kcGlja3VwX2RhdGV0aW1lID0gYXMuUE9TSVhjdChGSFZfbGlzdFtbNF1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIix0eiA9IFN5cy50aW1lem9uZSgpKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzRdXSRkcm9wb2ZmX2RhdGV0aW1lID0gYXMuUE9TSVhjdChGSFZfbGlzdFtbNF1dJGRyb3BvZmZfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIsdHogPSBTeXMudGltZXpvbmUoKSkNCmBgYA0KDQpgYGB7cn0NCiMgY3JlYXRlIFllYXIsIE1vbnRocywgVGltZSwgVHJpcF9kdXJhdGlvbiBhbmQgSG91cnMgY29sdW1ucw0KRkhWX2xpc3RbWzRdXSRZZWFyID0gZm9ybWF0KEZIVl9saXN0W1s0XV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJVkiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzRdXSRNb250aHMgPSBmb3JtYXQoRkhWX2xpc3RbWzRdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbSIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbNF1dJFRpbWUgPSBmb3JtYXQoRkhWX2xpc3RbWzRdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlSDolTTolUyIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbNF1dJFRyaXBfZHVyYXRpb24gPSBkaWZmdGltZShGSFZfbGlzdFtbNF1dJGRyb3BvZmZfZGF0ZXRpbWUsIEZIVl9saXN0W1s0XV0kcGlja3VwX2RhdGV0aW1lLCB1bml0cyA9ICJob3VycyIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbNF1dJEhvdXJzID0gZm9ybWF0KEZIVl9saXN0W1s0XV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJUgiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzRdXSRXZWVrZGF5cyA9IHdlZWtkYXlzKEZIVl9saXN0W1s0XV0kcGlja3VwX2RhdGV0aW1lKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzVdXSRwaWNrdXBfZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KEZIVl9saXN0W1s1XV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiLHR6ID0gU3lzLnRpbWV6b25lKCkpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbNV1dJGRyb3BvZmZfZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KEZIVl9saXN0W1s1XV0kZHJvcG9mZl9kYXRldGltZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIix0eiA9IFN5cy50aW1lem9uZSgpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjcmVhdGUgWWVhciwgTW9udGhzLCBUaW1lLCBUcmlwX2R1cmF0aW9uIGFuZCBIb3VycyBjb2x1bW5zDQpGSFZfbGlzdFtbNV1dJFllYXIgPSBmb3JtYXQoRkhWX2xpc3RbWzVdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlWSIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbNV1dJE1vbnRocyA9IGZvcm1hdChGSFZfbGlzdFtbNV1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVtIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s1XV0kVGltZSA9IGZvcm1hdChGSFZfbGlzdFtbNV1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVIOiVNOiVTIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s1XV0kVHJpcF9kdXJhdGlvbiA9IGRpZmZ0aW1lKEZIVl9saXN0W1s1XV0kZHJvcG9mZl9kYXRldGltZSwgRkhWX2xpc3RbWzVdXSRwaWNrdXBfZGF0ZXRpbWUsIHVuaXRzID0gImhvdXJzIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s1XV0kSG91cnMgPSBmb3JtYXQoRkhWX2xpc3RbWzVdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlSCIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbNV1dJFdlZWtkYXlzID0gd2Vla2RheXMoRkhWX2xpc3RbWzVdXSRwaWNrdXBfZGF0ZXRpbWUpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbNl1dJHBpY2t1cF9kYXRldGltZSA9IGFzLlBPU0lYY3QoRkhWX2xpc3RbWzZdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIsdHogPSBTeXMudGltZXpvbmUoKSkNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s2XV0kZHJvcG9mZl9kYXRldGltZSA9IGFzLlBPU0lYY3QoRkhWX2xpc3RbWzZdXSRkcm9wb2ZmX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiLHR6ID0gU3lzLnRpbWV6b25lKCkpDQpgYGANCg0KYGBge3J9DQojIGNyZWF0ZSBZZWFyLCBNb250aHMsIFRpbWUsIFRyaXBfZHVyYXRpb24gYW5kIEhvdXJzIGNvbHVtbnMNCkZIVl9saXN0W1s2XV0kWWVhciA9IGZvcm1hdChGSFZfbGlzdFtbNl1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVZIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s2XV0kTW9udGhzID0gZm9ybWF0KEZIVl9saXN0W1s2XV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0iKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzZdXSRUaW1lID0gZm9ybWF0KEZIVl9saXN0W1s2XV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJUg6JU06JVMiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzZdXSRUcmlwX2R1cmF0aW9uID0gZGlmZnRpbWUoRkhWX2xpc3RbWzZdXSRkcm9wb2ZmX2RhdGV0aW1lLCBGSFZfbGlzdFtbNl1dJHBpY2t1cF9kYXRldGltZSwgdW5pdHMgPSAiaG91cnMiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzZdXSRIb3VycyA9IGZvcm1hdChGSFZfbGlzdFtbNl1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVIIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s2XV0kV2Vla2RheXMgPSB3ZWVrZGF5cyhGSFZfbGlzdFtbNl1dJHBpY2t1cF9kYXRldGltZSkNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s3XV0kcGlja3VwX2RhdGV0aW1lID0gYXMuUE9TSVhjdChGSFZfbGlzdFtbN11dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIix0eiA9IFN5cy50aW1lem9uZSgpKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzddXSRkcm9wb2ZmX2RhdGV0aW1lID0gYXMuUE9TSVhjdChGSFZfbGlzdFtbN11dJGRyb3BvZmZfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIsdHogPSBTeXMudGltZXpvbmUoKSkNCmBgYA0KDQpgYGB7cn0NCiMgY3JlYXRlIFllYXIsIE1vbnRocywgVGltZSwgVHJpcF9kdXJhdGlvbiBhbmQgSG91cnMgY29sdW1ucw0KRkhWX2xpc3RbWzddXSRZZWFyID0gZm9ybWF0KEZIVl9saXN0W1s3XV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJVkiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzddXSRNb250aHMgPSBmb3JtYXQoRkhWX2xpc3RbWzddXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbSIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbN11dJFRpbWUgPSBmb3JtYXQoRkhWX2xpc3RbWzddXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlSDolTTolUyIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbN11dJFRyaXBfZHVyYXRpb24gPSBkaWZmdGltZShGSFZfbGlzdFtbN11dJGRyb3BvZmZfZGF0ZXRpbWUsIEZIVl9saXN0W1s3XV0kcGlja3VwX2RhdGV0aW1lLCB1bml0cyA9ICJob3VycyIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbN11dJEhvdXJzID0gZm9ybWF0KEZIVl9saXN0W1s3XV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJUgiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzddXSRXZWVrZGF5cyA9IHdlZWtkYXlzKEZIVl9saXN0W1s3XV0kcGlja3VwX2RhdGV0aW1lKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzhdXSRwaWNrdXBfZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KEZIVl9saXN0W1s4XV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiLHR6ID0gU3lzLnRpbWV6b25lKCkpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbOF1dJGRyb3BvZmZfZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KEZIVl9saXN0W1s4XV0kZHJvcG9mZl9kYXRldGltZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIix0eiA9IFN5cy50aW1lem9uZSgpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjcmVhdGUgWWVhciwgTW9udGhzLCBUaW1lLCBUcmlwX2R1cmF0aW9uIGFuZCBIb3VycyBjb2x1bW5zDQpGSFZfbGlzdFtbOF1dJFllYXIgPSBmb3JtYXQoRkhWX2xpc3RbWzhdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlWSIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbOF1dJE1vbnRocyA9IGZvcm1hdChGSFZfbGlzdFtbOF1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVtIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s4XV0kVGltZSA9IGZvcm1hdChGSFZfbGlzdFtbOF1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVIOiVNOiVTIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s4XV0kVHJpcF9kdXJhdGlvbiA9IGRpZmZ0aW1lKEZIVl9saXN0W1s4XV0kZHJvcG9mZl9kYXRldGltZSwgRkhWX2xpc3RbWzhdXSRwaWNrdXBfZGF0ZXRpbWUsIHVuaXRzID0gImhvdXJzIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s4XV0kSG91cnMgPSBmb3JtYXQoRkhWX2xpc3RbWzhdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlSCIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbOF1dJFdlZWtkYXlzID0gd2Vla2RheXMoRkhWX2xpc3RbWzhdXSRwaWNrdXBfZGF0ZXRpbWUpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbOV1dJHBpY2t1cF9kYXRldGltZSA9IGFzLlBPU0lYY3QoRkhWX2xpc3RbWzldXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIsdHogPSBTeXMudGltZXpvbmUoKSkNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s5XV0kZHJvcG9mZl9kYXRldGltZSA9IGFzLlBPU0lYY3QoRkhWX2xpc3RbWzldXSRkcm9wb2ZmX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiLHR6ID0gU3lzLnRpbWV6b25lKCkpDQpgYGANCg0KYGBge3J9DQojIGNyZWF0ZSBZZWFyLCBNb250aHMsIFRpbWUsIFRyaXBfZHVyYXRpb24gYW5kIEhvdXJzIGNvbHVtbnMNCkZIVl9saXN0W1s5XV0kWWVhciA9IGZvcm1hdChGSFZfbGlzdFtbOV1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVZIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s5XV0kTW9udGhzID0gZm9ybWF0KEZIVl9saXN0W1s5XV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0iKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzldXSRUaW1lID0gZm9ybWF0KEZIVl9saXN0W1s5XV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJUg6JU06JVMiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzldXSRUcmlwX2R1cmF0aW9uID0gZGlmZnRpbWUoRkhWX2xpc3RbWzldXSRkcm9wb2ZmX2RhdGV0aW1lLCBGSFZfbGlzdFtbOV1dJHBpY2t1cF9kYXRldGltZSwgdW5pdHMgPSAiaG91cnMiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzldXSRIb3VycyA9IGZvcm1hdChGSFZfbGlzdFtbOV1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVIIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1s5XV0kV2Vla2RheXMgPSB3ZWVrZGF5cyhGSFZfbGlzdFtbOV1dJHBpY2t1cF9kYXRldGltZSkNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxMF1dJHBpY2t1cF9kYXRldGltZSA9IGFzLlBPU0lYY3QoRkhWX2xpc3RbWzEwXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiLHR6ID0gU3lzLnRpbWV6b25lKCkpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMTBdXSRkcm9wb2ZmX2RhdGV0aW1lID0gYXMuUE9TSVhjdChGSFZfbGlzdFtbMTBdXSRkcm9wb2ZmX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiLHR6ID0gU3lzLnRpbWV6b25lKCkpDQpgYGANCg0KYGBge3J9DQojIGNyZWF0ZSBZZWFyLCBNb250aHMsIFRpbWUsIFRyaXBfZHVyYXRpb24gYW5kIEhvdXJzIGNvbHVtbnMNCkZIVl9saXN0W1sxMF1dJFllYXIgPSBmb3JtYXQoRkhWX2xpc3RbWzEwXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJVkiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzEwXV0kTW9udGhzID0gZm9ybWF0KEZIVl9saXN0W1sxMF1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVtIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxMF1dJFRpbWUgPSBmb3JtYXQoRkhWX2xpc3RbWzEwXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJUg6JU06JVMiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzEwXV0kVHJpcF9kdXJhdGlvbiA9IGRpZmZ0aW1lKEZIVl9saXN0W1sxMF1dJGRyb3BvZmZfZGF0ZXRpbWUsIEZIVl9saXN0W1sxMF1dJHBpY2t1cF9kYXRldGltZSwgdW5pdHMgPSAiaG91cnMiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzEwXV0kSG91cnMgPSBmb3JtYXQoRkhWX2xpc3RbWzEwXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJUgiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzEwXV0kV2Vla2RheXMgPSB3ZWVrZGF5cyhGSFZfbGlzdFtbMTBdXSRwaWNrdXBfZGF0ZXRpbWUpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMTFdXSRwaWNrdXBfZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KEZIVl9saXN0W1sxMV1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIix0eiA9IFN5cy50aW1lem9uZSgpKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzExXV0kZHJvcG9mZl9kYXRldGltZSA9IGFzLlBPU0lYY3QoRkhWX2xpc3RbWzExXV0kZHJvcG9mZl9kYXRldGltZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIix0eiA9IFN5cy50aW1lem9uZSgpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjcmVhdGUgWWVhciwgTW9udGhzLCBUaW1lLCBUcmlwX2R1cmF0aW9uIGFuZCBIb3VycyBjb2x1bW5zDQpGSFZfbGlzdFtbMTFdXSRZZWFyID0gZm9ybWF0KEZIVl9saXN0W1sxMV1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVZIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxMV1dJE1vbnRocyA9IGZvcm1hdChGSFZfbGlzdFtbMTFdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbSIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMTFdXSRUaW1lID0gZm9ybWF0KEZIVl9saXN0W1sxMV1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVIOiVNOiVTIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxMV1dJFRyaXBfZHVyYXRpb24gPSBkaWZmdGltZShGSFZfbGlzdFtbMTFdXSRkcm9wb2ZmX2RhdGV0aW1lLCBGSFZfbGlzdFtbMTFdXSRwaWNrdXBfZGF0ZXRpbWUsIHVuaXRzID0gImhvdXJzIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxMV1dJEhvdXJzID0gZm9ybWF0KEZIVl9saXN0W1sxMV1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVIIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxMV1dJFdlZWtkYXlzID0gd2Vla2RheXMoRkhWX2xpc3RbWzExXV0kcGlja3VwX2RhdGV0aW1lKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzEyXV0kcGlja3VwX2RhdGV0aW1lID0gYXMuUE9TSVhjdChGSFZfbGlzdFtbMTJdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIsdHogPSBTeXMudGltZXpvbmUoKSkNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxMl1dJGRyb3BvZmZfZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KEZIVl9saXN0W1sxMl1dJGRyb3BvZmZfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIsdHogPSBTeXMudGltZXpvbmUoKSkNCmBgYA0KDQpgYGB7cn0NCiMgY3JlYXRlIFllYXIsIE1vbnRocywgVGltZSwgVHJpcF9kdXJhdGlvbiBhbmQgSG91cnMgY29sdW1ucw0KRkhWX2xpc3RbWzEyXV0kWWVhciA9IGZvcm1hdChGSFZfbGlzdFtbMTJdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlWSIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMTJdXSRNb250aHMgPSBmb3JtYXQoRkhWX2xpc3RbWzEyXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0iKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzEyXV0kVGltZSA9IGZvcm1hdChGSFZfbGlzdFtbMTJdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlSDolTTolUyIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMTJdXSRUcmlwX2R1cmF0aW9uID0gZGlmZnRpbWUoRkhWX2xpc3RbWzEyXV0kZHJvcG9mZl9kYXRldGltZSwgRkhWX2xpc3RbWzEyXV0kcGlja3VwX2RhdGV0aW1lLCB1bml0cyA9ICJob3VycyIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMTJdXSRIb3VycyA9IGZvcm1hdChGSFZfbGlzdFtbMTJdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlSCIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMTJdXSRXZWVrZGF5cyA9IHdlZWtkYXlzKEZIVl9saXN0W1sxMl1dJHBpY2t1cF9kYXRldGltZSkNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxM11dJHBpY2t1cF9kYXRldGltZSA9IGFzLlBPU0lYY3QoRkhWX2xpc3RbWzEzXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiLHR6ID0gU3lzLnRpbWV6b25lKCkpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMTNdXSRkcm9wb2ZmX2RhdGV0aW1lID0gYXMuUE9TSVhjdChGSFZfbGlzdFtbMTNdXSRkcm9wb2ZmX2RhdGV0aW1lLCBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiLHR6ID0gU3lzLnRpbWV6b25lKCkpDQpgYGANCg0KYGBge3J9DQojIGNyZWF0ZSBZZWFyLCBNb250aHMsIFRpbWUsIFRyaXBfZHVyYXRpb24gYW5kIEhvdXJzIGNvbHVtbnMNCkZIVl9saXN0W1sxM11dJFllYXIgPSBmb3JtYXQoRkhWX2xpc3RbWzEzXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJVkiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzEzXV0kTW9udGhzID0gZm9ybWF0KEZIVl9saXN0W1sxM11dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVtIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxM11dJFRpbWUgPSBmb3JtYXQoRkhWX2xpc3RbWzEzXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJUg6JU06JVMiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzEzXV0kVHJpcF9kdXJhdGlvbiA9IGRpZmZ0aW1lKEZIVl9saXN0W1sxM11dJGRyb3BvZmZfZGF0ZXRpbWUsIEZIVl9saXN0W1sxM11dJHBpY2t1cF9kYXRldGltZSwgdW5pdHMgPSAiaG91cnMiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzEzXV0kSG91cnMgPSBmb3JtYXQoRkhWX2xpc3RbWzEzXV0kcGlja3VwX2RhdGV0aW1lLCBmb3JtYXQgPSAiJUgiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzEzXV0kV2Vla2RheXMgPSB3ZWVrZGF5cyhGSFZfbGlzdFtbMTNdXSRwaWNrdXBfZGF0ZXRpbWUpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMTRdXSRwaWNrdXBfZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KEZIVl9saXN0W1sxNF1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIix0eiA9IFN5cy50aW1lem9uZSgpKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2xpc3RbWzE0XV0kZHJvcG9mZl9kYXRldGltZSA9IGFzLlBPU0lYY3QoRkhWX2xpc3RbWzE0XV0kZHJvcG9mZl9kYXRldGltZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIix0eiA9IFN5cy50aW1lem9uZSgpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjcmVhdGUgWWVhciwgTW9udGhzLCBUaW1lLCBUcmlwX2R1cmF0aW9uIGFuZCBIb3VycyBjb2x1bW5zDQpGSFZfbGlzdFtbMTRdXSRZZWFyID0gZm9ybWF0KEZIVl9saXN0W1sxNF1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVZIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxNF1dJE1vbnRocyA9IGZvcm1hdChGSFZfbGlzdFtbMTRdXSRwaWNrdXBfZGF0ZXRpbWUsIGZvcm1hdCA9ICIlbSIpDQpgYGANCg0KYGBge3J9DQpGSFZfbGlzdFtbMTRdXSRUaW1lID0gZm9ybWF0KEZIVl9saXN0W1sxNF1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVIOiVNOiVTIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxNF1dJFRyaXBfZHVyYXRpb24gPSBkaWZmdGltZShGSFZfbGlzdFtbMTRdXSRkcm9wb2ZmX2RhdGV0aW1lLCBGSFZfbGlzdFtbMTRdXSRwaWNrdXBfZGF0ZXRpbWUsIHVuaXRzID0gImhvdXJzIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxNF1dJEhvdXJzID0gZm9ybWF0KEZIVl9saXN0W1sxNF1dJHBpY2t1cF9kYXRldGltZSwgZm9ybWF0ID0gIiVIIikNCmBgYA0KDQpgYGB7cn0NCkZIVl9saXN0W1sxNF1dJFdlZWtkYXlzID0gd2Vla2RheXMoRkhWX2xpc3RbWzE0XV0kcGlja3VwX2RhdGV0aW1lKQ0KYGBgDQoNCg0KYGBge3J9DQojIG1vZGlmeSBnbG9iYWwgb3B0aW9ucw0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpIA0KYGBgDQoNCmBgYHtyfQ0KI3dyaXRlLmNzdihGSFYwLCAiRkhWMC5jc3YiKQ0KYGBgDQoNCkNhbGN1bGF0ZSBzdW0gb2YgdHJpcCBkdXJhdGlvbiBhbmQgbnVtYmVyIG9mIHRyaXBzIG9mIGVhY2ggc3Vic2V0DQoNCg0KYGBge3J9DQpGSFZfZ3JvdXBfbGlzdCA9IHZlY3Rvcihtb2RlID0gImxpc3QiLGxlbmd0aCA9IDE0KQ0KYGBgDQoNCg0KYGBge3J9DQpmb3IgKGkgaW4gMToxNCl7DQogIEZIVl9ncm91cF9saXN0W1tpXV0gPSBGSFZfbGlzdFtbaV1dICU+JQ0KICBncm91cF9ieShZZWFyLE1vbnRocyxIb3VycyxXZWVrZGF5cyxkaXNwYXRjaGluZ19iYXNlX251bSkgJT4lDQogIGRyb3BfbmEoVHJpcF9kdXJhdGlvbikgJT4lDQogIHN1bW1hcml6ZShUb3RhbF90cmlwX2R1cmF0aW9uID0gc3VtKFRyaXBfZHVyYXRpb24pLA0KICAgICAgICAgICAgVG90YWxfbnVtYmVyX29mX3RyaXBzID0gbigpKQ0KfQ0KYGBgDQoNCg0KDQpDb21iaW5lIGFsbCB0aGUgc3Vic2V0cw0KDQpgYGB7cn0NCkZIVl9ncm91cCA9IGRvLmNhbGwoInJiaW5kIiwgbGlzdChGSFZfZ3JvdXBfbGlzdFtbMV1dLEZIVl9ncm91cF9saXN0W1syXV0sRkhWX2dyb3VwX2xpc3RbWzNdXSwNCiAgICAgRkhWX2dyb3VwX2xpc3RbWzRdXSxGSFZfZ3JvdXBfbGlzdFtbNV1dLEZIVl9ncm91cF9saXN0W1s2XV0sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZIVl9ncm91cF9saXN0W1s3XV0sRkhWX2dyb3VwX2xpc3RbWzhdXSxGSFZfZ3JvdXBfbGlzdFtbOV1dLA0KICAgICBGSFZfZ3JvdXBfbGlzdFtbMTBdXSxGSFZfZ3JvdXBfbGlzdFtbMTFdXSxGSFZfZ3JvdXBfbGlzdFtbMTJdXSwNCiAgICAgRkhWX2dyb3VwX2xpc3RbWzEzXV0sRkhWX2dyb3VwX2xpc3RbWzE0XV0sKSkNCmBgYA0KDQoNCmBgYHtyfQ0KI3dyaXRlLmNzdihGSFZfZ3JvdXAsIkZIVl9ncm91cC5jc3YiKQ0KYGBgDQoNCmBgYHtyfQ0KRkhWX2dyb3VwID0gcmVhZC5jc3YoIkZIVl9ncm91cC5jc3YiKQ0KYGBgDQoNCjxsaT4NCjxhIGhyZWY9Imh0dHBzOi8vd3d3MS5ueWMuZ292L2Fzc2V0cy90bGMvZG93bmxvYWRzL3BkZi9maW5kX2FfcmlkZS5wZGYiPkEgTGlzdGluZyBvZiBUTEMgTGljZW5zZWQgQmFzZXM8L2E+DQo8L2xpPg0KDQpgYGB7cn0NCmZpbmRfYV9yaWRlID0gcmVhZC5jc3YoImZpbmRfYV9yaWRlMS5jc3YiKQ0KIyB0cmltIHdoaXRlc3BhY2Ugb2YgTGljZW5zZV9udW1iZXINCmZpbmRfYV9yaWRlJExpY2Vuc2VfbnVtYmVyID0gdHJpbXdzKGZpbmRfYV9yaWRlJExpY2Vuc2VfbnVtYmVyKQ0KDQpgYGANCg0KYGBge3J9DQpGSFZfcmlkZSA9IGxlZnRfam9pbihGSFZfZ3JvdXAsZmluZF9hX3JpZGUsIGJ5PShjKCJkaXNwYXRjaGluZ19iYXNlX251bSIgPSAiTGljZW5zZV9udW1iZXIiKSkpDQpgYGANCg0KYGBge3J9DQojIGV4dHJhIFdBVl9EaXNwYXRjaGVyIGZyb20gTmFtZV9vZl9CYXNlDQpGSFZfcmlkZSROYW1lX29mX0Jhc2UgPSBzdHJfcmVwbGFjZV9hbGwoRkhWX3JpZGUkTmFtZV9vZl9CYXNlLCAiLSIgLCAiICIpDQpGSFZfcmlkZSRXQVZfRGlzcGF0Y2hlciA9IHdvcmQoRkhWX3JpZGUkTmFtZV9vZl9CYXNlLCAxKQ0KRkhWX3JpZGUkV0FWX0Rpc3BhdGNoZXIgPSBzdHJfcmVwbGFjZV9hbGwoRkhWX3JpZGUkV0FWX0Rpc3BhdGNoZXIsIGMoIkZMQVRJUk9OIiA9ICJWaWEiLCJHUkVFTlBPSU5UIiA9ICJWaWEiKSkNCg0KIyByZW1vdmUgImhvdXJzIiBpbiBUb3RhbF90cmlwX2R1cmF0aW9uDQpGSFZfcmlkZSRUb3RhbF90cmlwX2R1cmF0aW9uID0gYXMubnVtZXJpYyh3b3JkKEZIVl9yaWRlJFRvdGFsX3RyaXBfZHVyYXRpb24sMSkpDQoNCiMgZmlsdGVyIG91dCBkYXRhIG9mIFZpYQ0KRkhWX3JpZGUgPSBGSFZfcmlkZVt3aGljaChGSFZfcmlkZSRXQVZfRGlzcGF0Y2hlciAhPSAiVmlhIiksXQ0KYGBgDQoNCmBgYHtyfQ0KIyBjb252ZXJ0IE1vbnRocywgSG91cnMsIFdlZWtkYXlzIHRvIGZhY3RvcnMNCkZIVl9yaWRlWyxjKDI6NSldW109IGxhcHBseShGSFZfcmlkZVssYygyOjUpXSxhcy5mYWN0b3IpDQpgYGANCg0KDQpgYGB7cn0NCiN3cml0ZS5jc3YoRkhWX3JpZGVbLTFdLCJGSFZfcmlkZS5jc3YiKQ0KYGBgDQoNCg0KYGBge3J9DQpGSFZfcmlkZSA9IHJlYWQuY3N2KCJGSFZfcmlkZS5jc3YiKQ0KRkhWX3JpZGUNCmBgYA0KDQpgYGB7cn0NCiMgVG90YWwgRGlzdGFuY2UsIFRvdGFsIFJldmVudWUgYW5kIFRvdGFsIFRyaXAgRHVyYXRpb24gb2YgTllDIEdyZWVuIFRheGkgaW4gMjAyMA0KZ2dwbG90KEZIVl9yaWRlKSArDQogIGFlcyh4ID0gTW9udGhzLCB5ID0gIFRvdGFsX251bWJlcl9vZl90cmlwcywgZmlsbCA9IFdBVl9EaXNwYXRjaGVyKSArDQogIGdlb21fY29sKCkgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6MTIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGZvcm1hdF9mb3JtYXQoYmlnLm1hcmsgPSAiICIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNpbWFsLm1hcmsgPSAiLCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2llbnRpZmljID0gRkFMU0UpKSArDQogIyBnZW9tX3RleHQoYWVzKGxhYmVsID0gVG90YWxfbnVtYmVyX29mX3RyaXBzKSwgdmp1c3QgPSAtMSkgKw0KICBsYWJzKHggPSAnTW9udGhzJywNCiAgICAgICB5ID0gIk51bWJlciBvZiBUcmlwcyIsDQogICAgICAgdGl0bGUgPSAiTnVtYmVyIG9mIFRyaXBzIG9mIE5ZQyANCiAgICAgICBVYmVyIGFuZCBMeWZ0IGluIDIwMjAiKSANCg0KIA0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgIFRvdGFsIFRyaXAgRHVyYXRpb24gaW4gMjQgaG91cnMNCmdncGxvdChGSFZfcmlkZSkgKw0KICBhZXMoeCA9IEhvdXJzLCB5ID0gIFRvdGFsX3RyaXBfZHVyYXRpb24sIGZpbGwgPSBXQVZfRGlzcGF0Y2hlcikgKw0KICBnZW9tX2NvbCgpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMToyMykgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gZm9ybWF0X2Zvcm1hdChiaWcubWFyayA9ICIgIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY2ltYWwubWFyayA9ICIsIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjaWVudGlmaWMgPSBGQUxTRSkpICsNCiMgIGdlb21fdGV4dChhZXMobGFiZWwgPSBUb3RhbF9udW1iZXJfb2ZfdHJpcHMpLCB2anVzdCA9IC0xKSsNCiMgIGNvb3JkX2ZsaXAoKSsNCiAgbGFicyh4ID0gJ0hvdXJzJywNCiAgICAgICB5ID0gIlRvdGFsIFRyaXAgRHVyYXRpb24gKGluIGhvdXJzKSIsDQogICAgICAgdGl0bGUgPSAiVG90YWwgVHJpcCBEdXJhdGlvbiBpbiAyNCBob3VycyANCiAgICAgICBvZiBVYmVyIGFuZCBMeWZ0IGluIDIwMjAiKSANCg0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KEZIVl9yaWRlKSArDQogICAgYWVzKHggPSByZW9yZGVyKFdlZWtkYXlzLFRvdGFsX3RyaXBfZHVyYXRpb24pLCB5ID0gVG90YWxfdHJpcF9kdXJhdGlvbiwgZmlsbCA9IFdBVl9EaXNwYXRjaGVyKSArDQogICAgZ2VvbV9jb2woKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGZvcm1hdF9mb3JtYXQoYmlnLm1hcmsgPSAiICIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNpbWFsLm1hcmsgPSAiLCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2llbnRpZmljID0gRkFMU0UpKSArDQogICAgbGFicyh4ID0gJ1dlZWtkYXlzJywNCiAgICAgICB5ID0gIlRvdGFsIFRyaXAgRHVyYXRpb24gKGluIGhvdXJzKSIsDQogICAgICAgdGl0bGUgPSAiVG90YWwgVHJpcCBEdXJhdGlvbiBpbiBXZWVrZGF5cyANCiAgICAgICBvZiBOWUMgVWJlciBhbmQgTHlmdCBpbiAyMDIwIikgDQoNCg0KYGBgDQoNCg0KYGBge3J9DQpGSFY2ID0gcmVhZC5jc3YoIkZIVjYuY3N2IikNCkZIVjYNCmBgYA0KDQoNCkNhbGN1bGF0ZSB0cmlwIGR1cmF0aW9uIGJhc2Ugb24gbG9jYXRpb24gSUQgb2YgZWFjaCBzdWJzZXQNCmBgYHtyfQ0KRkhWX2xvY2F0aW9uNiA9IEZIVjYgJT4lDQogIGdyb3VwX2J5KFBVTG9jYXRpb25JRCwgRE9Mb2NhdGlvbklELCkgJT4lDQogIHN1bW1hcml6ZShUb3RhbF90cmlwX2R1cmF0aW9uID0gc3VtKFRyaXBfZHVyYXRpb24pLA0KICAgICAgICAgICAgVG90YWxfdHJpcHMgPSBuKCkpIA0KDQpgYGANCg0KDQoNCkNvbWJpbmUgYWxsIHRoZSBGSFZfbG9jYXRpb24gc3Vic2V0cw0KYGBge3J9DQpGSFZfbG9jYXRpb24gPSByYmluZChGSFZfbG9jYXRpb25bLGMoMjo1KV0sRkhWX2xvY2F0aW9uNikNCg0KDQpgYGANCg0KYGBge3J9DQojd3JpdGUuY3N2KEZIVl9sb2NhdGlvbiwgIkZIVl9sb2NhdGlvbi5jc3YiKQ0KYGBgDQoNCg0KDQoNCk1lcmdlIHdpdGggZGF0YXNldHMgdGF4aStfem9uZV9sb29rdXAuY3N2IGFuZCB0YXhpIHpvbmUgbWFwDQo8bGk+DQo8YSBocmVmPSJodHRwczovL3MzLmFtYXpvbmF3cy5jb20vbnljLXRsYy9taXNjL3RheGkrX3pvbmVfbG9va3VwLmNzdiI+VGF4aSBab25lIExvb2t1cCBUYWJsZTwvYT4NCjwvbGk+DQoNCmBgYHtyfQ0KdGF4aV96b25lcyA9IHJlYWQuY3N2KCJ0YXhpK196b25lX2xvb2t1cC5jc3YiKQ0KDQpgYGANCg0KDQoNCkNhbGN1bGF0ZSB0aGUgdHJpcCBkdXJhdGlvbiBhbmQgdG90YWwgdHJpcHMgYmFzZSBvbiBwaWNrdXAgbG9jYXRpb24NCmBgYHtyfQ0KcGlja1VwID0gRkhWX2xvY2F0aW9uICU+JQ0KICBmaWx0ZXIoUFVMb2NhdGlvbklEICE9IDI2NCAmIFBVTG9jYXRpb25JRCAhPSAyNjUgJiAhaXMubmEoVG90YWxfdHJpcF9kdXJhdGlvbikpICU+JSAjIGZpbHRlciBvdXQgdW5rbm93biBib3JvdWdoDQogIGdyb3VwX2J5KFBVTG9jYXRpb25JRCkgJT4lDQogIHN1bW1hcmlzZShUb3RhbF90cmlwX2R1cmF0aW9uID0gc3VtKFRvdGFsX3RyaXBfZHVyYXRpb24pLA0KICAgICAgICAgICAgVG90YWxfdHJpcHMgPSBzdW0oVG90YWxfdHJpcHMpKSANCg0KYGBgDQoNCkNvbWJpbmUgcGlja1VwIGFuZCB0YXhpX3pvbmVzDQpgYGB7cn0NCnBpY2tVcCA9IGxlZnRfam9pbihwaWNrVXAsdGF4aV96b25lcywgYnkgPSBjKCJQVUxvY2F0aW9uSUQiID0gIkxvY2F0aW9uSUQiKSkNCg0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgcmVhZCBHZW9KU09OIGZpbGUgb2YgTllDIFRheGkgem9uZQ0KanMgPSBGUk9NX0dlb0pzb24odXJsX2ZpbGVfc3RyaW5nID0gImh0dHBzOi8vZGF0YS5jaXR5b2ZuZXd5b3JrLnVzL2FwaS9nZW9zcGF0aWFsL2QzYzUtZGRnYz9tZXRob2Q9ZXhwb3J0JmZvcm1hdD1HZW9KU09OIikNCg0KYGBgDQoNCmBgYHtyfQ0KIyBjcmVhdGUgYSBkYXRhZnJhbSB3aXRoIGxvbmdpdHVkZSBhbmQgbGF0aXR1ZGUgb2YgTllDIG5laWdoYm9yaG9vZCANCk5ZQ19jb29yZCA9IGxhcHBseSgxOmxlbmd0aChqcyRmZWF0dXJlcyksIA0KICAgICAgICAgZnVuY3Rpb24oaSl7DQogICAgICAgICAgIGlmKCFpcy5saXN0KGpzJGZlYXR1cmVzW1tpXV0kZ2VvbWV0cnkkY29vcmRpbmF0ZXMpKXsNCiAgICAgICAgICAgdG1wZGF0YSA8LSBqcyRmZWF0dXJlc1tbaV1dJGdlb21ldHJ5JGNvb3JkaW5hdGVzDQogICAgICAgICB9ZWxzZXsNCiAgICAgICAgICAgdG1wZGF0YSA8LSBqcyRmZWF0dXJlc1tbaV1dJGdlb21ldHJ5JGNvb3JkaW5hdGVzW1sxXV19IA0KICAgICAgICAgICANCnRtcGRhdGEgJT4lDQogICBkYXRhLmZyYW1lKCkgJT4lIA0KICAgdGliYmxlICU+JQ0KICAgbXV0YXRlKHpvbmUgPSBqcyRmZWF0dXJlc1tbaV1dJHByb3BlcnRpZXMkem9uZSwNCiAgICAgICAgICBib3JvdWdoID0ganMkZmVhdHVyZXNbW2ldXSRwcm9wZXJ0aWVzJGJvcm91Z2gpICU+JSANCiAgICAgICAgICByZW5hbWUoImxvbmciID0gJ1gxJywgJ2xhdCc9J1gyJyl9KSAlPiUNCiAgIGJpbmRfcm93cygpDQpgYGANCg0KDQpgYGB7cn0NCnBpY2tVcF9jb29yZCA9IGxlZnRfam9pbihwaWNrVXAsIE5ZQ19jb29yZCwgYnkgPSBjKCJab25lIj0iem9uZSIpKQ0KDQogDQpgYGANCg0KDQpgYGB7cn0NCiN3cml0ZS5jc3YocGlja1VwX2Nvb3JkLCJwaWNrVXBfY29vcmQuY3N2IikNCmBgYA0KDQpgYGB7cn0NCnBpY2tVcF9jb29yZCA9IHJlYWQuY3N2KCJwaWNrVXBfY29vcmQuY3N2IikNCg0KYGBgDQpUb3AgNSBsb2NhdGlvbnMNCmBgYHtyfQ0KVG9wX0xvY2F0aW9uSUQgPSBwaWNrVXBfY29vcmQgJT4lDQogIGZpbHRlcihUb3RhbF90cmlwX2R1cmF0aW9uID4gNDgwMDAwKSAlPiUNCiAgY291bnQoWm9uZSkNCiAgDQpgYGANCg0KQ2FsY3VsYXRlIHRoZSBjb29yZGluYXRlcyBvZiB0aGUgdG9wIDUgbG9jYXRpb25zDQpgYGB7cn0NCmNlbnRlcl9jb29yZCA9IE5ZQ19jb29yZCAlPiUNCiAgZ3JvdXBfYnkoem9uZSkgJT4lDQogIHN1bW1hcmlzZShsb25nID0gbWVhbihsb25nKSxsYXQgPSBtZWFuKGxhdCkpICU+JQ0KICByaWdodF9qb2luKFRvcF9Mb2NhdGlvbklELCBieSA9IGMoInpvbmUiID0gIlpvbmUiKSkNCg0KYGBgDQoNCmBgYHtyfQ0KI3dyaXRlLmNzdihjZW50ZXJfY29vcmQsImNlbnRlcl9jb29yZC5jc3YiKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmdncGxvdChwaWNrVXBfY29vcmQpKw0KICBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsZ3JvdXAgPSBab25lLCBmaWxsID1Ub3RhbF90cmlwX2R1cmF0aW9uKSArDQogIGdlb21fcG9seWdvbigpICsNCiB3aXRoKGNlbnRlcl9jb29yZCwgDQogICAgICBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgeCA9IGxvbmcsIHk9bGF0LCANCiAgICAgICAgICAgICAgIGxhYmVsID0gem9uZSwgDQogICAgICAgICAgICAgICBmYWNlPSJib2xkIiwNCiAgICAgICAgICAgICAgIHNpemUgPSA1KSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAiYmx1ZSIpICsNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBUcmlwIER1cmF0aW9uIEhvdXJzIGluIERpZmZlcmVudCBQaWNrIFVwIExvY2F0aW9ucyANCiAgICAgICAoVWJlciBhbmQgTHlmdCwgMjAyMCkiKQ0KICANCmBgYA0KDQpgYGB7cn0NCmRyb3BPZmYgPSBGSFZfbG9jYXRpb24gJT4lDQogIGZpbHRlcihET0xvY2F0aW9uSUQgIT0gMjY0ICYgRE9Mb2NhdGlvbklEICE9IDI2NSAmICFpcy5uYShUb3RhbF90cmlwX2R1cmF0aW9uKSkgJT4lICMgZmlsdGVyIG91dCB1bmtub3duIGJvcm91Z2gNCiAgZ3JvdXBfYnkoRE9Mb2NhdGlvbklEKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsX3RyaXBfZHVyYXRpb24gPSBzdW0oVG90YWxfdHJpcF9kdXJhdGlvbiksDQogICAgICAgICAgICBUb3RhbF90cmlwcyA9IHN1bShUb3RhbF90cmlwcykpIA0KZHJvcE9mZg0KYGBgDQoNCmBgYHtyfQ0KZHJvcE9mZl9jb29yZCA9IGxlZnRfam9pbihkcm9wT2ZmLCBOWUNfY29vcmQsIGJ5ID0gYygiWm9uZSI9InpvbmUiKSkNCg0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KVG9wX0RMb2NhdGlvbklEID0gZHJvcE9mZl9jb29yZCAlPiUNCiAgZmlsdGVyKCFpcy5uYShab25lKSkgJT4lDQogIGZpbHRlcihUb3RhbF90cmlwX2R1cmF0aW9uID4gMzkwMDAwKSAlPiUNCiAgY291bnQoWm9uZSkNCiAgDQpUb3BfRExvY2F0aW9uSUQgIA0KYGBgDQoNCmBgYHtyfQ0KY2VudGVyX0Rjb29yZCA9IE5ZQ19jb29yZCAlPiUNCiAgZ3JvdXBfYnkoem9uZSkgJT4lDQogIHN1bW1hcmlzZShsb25nID0gbWVhbihsb25nKSxsYXQgPSBtZWFuKGxhdCkpICU+JQ0KICByaWdodF9qb2luKFRvcF9ETG9jYXRpb25JRCwgYnkgPSBjKCJ6b25lIiA9ICJab25lIikpDQpjZW50ZXJfRGNvb3JkDQpgYGANCg0KDQpgYGB7cn0NCiN3cml0ZS5jc3YoY2VudGVyX0Rjb29yZCwiY2VudGVyX0Rjb29yZC5jc3YiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRyb3BPZmZfY29vcmQpKw0KICBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsZ3JvdXAgPSBab25lLCBmaWxsID1Ub3RhbF90cmlwcykgKw0KICBnZW9tX3BvbHlnb24oKSArDQogIHdpdGgoY2VudGVyX0Rjb29yZCwgDQogICAgICBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgeCA9IGxvbmcsIHk9bGF0LCANCiAgICAgICAgICAgICAgIGxhYmVsID0gem9uZSwNCiAgICAgICAgICAgICAgIGZhY2U9ImJvbGQiLA0KICAgICAgICAgICAgICAgc2l6ZSA9IDUpKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgVHJpcHMgaW4gRGlmZmVyZW50IFBpY2sgVXAgTG9jYXRpb25zIA0KICAgICAgIChVYmVyIGFuZCBMeWZ0LCAyMDIwKSIpDQogIA0KYGBgDQoNCg0K