suppressPackageStartupMessages({
    library(dplyr)
    library(tidyr)
    library(ggplot2)
    library(htmltools)
})
options(digits = 4)

Scope

A rough engineering analysis of vehicle throttle behavior relating acceleration, speed, acceleration, and losses.

As an example, key design parameters for typical Toyota Corollas have been collected here:

g <- 9.81 # N/m^2
rho_air_std <- 1.225 # kg/m^3, https://en.wikipedia.org/wiki/Density_of_air
hp_to_W <- 745.7 # W/hp
lbm_to_kg <- 0.4536
in_to_m <- 0.0254
mm_to_m <- 0.001
mph_to_ms <- 0.447 # meters-per-second per miles-per-hour
corolla_tbl <- read.table( text =
'Variable   Value   Unit     URL
mass        2910    "lbm"    "https://www.caranddriver.com/toyota/corolla/specs"
c_d         0.3     "-"      "https://en.wikipedia.org/wiki/Automobile_drag_coefficient"
c_d_A_cs    0.631   "m^2"    "https://en.wikipedia.org/wiki/Automobile_drag_coefficient"
c_rr        0.01    "-"      "https://en.wikipedia.org/wiki/Rolling_resistance"
tire_id     15      "in"     "https://www.lesschwab.com/article/tire-size-explained-reading-the-sidewall.html"
tire_width  200     "mm"     "https://www.lesschwab.com/article/tire-size-explained-reading-the-sidewall.html"
tire_aspect 65      "%"      "https://www.lesschwab.com/article/tire-size-explained-reading-the-sidewall.html"
P_rated     139     "hp"     "https://www.caranddriver.com/toyota/corolla/specs"
R_G         40      "RPM-hr/mi" "http://www.corollaforum.com/threads/2012-le-high-rpm-at-80-mph.1305/"
omega_max   6100    "RPM"    "https://www.caranddriver.com/toyota/corolla/specs"
', header = TRUE, as.is=TRUE )
# corolla <- list( mass = 2910 * lbm_to_kg # kg, 
#                , c_d = 0.3 # 
#                , A_cs = 0.631 / 0.3 # m^2, ditto
#                , c_rr = 0.01 # unitless
#                , r = 15/2*in_to_m + 200 * mm_to_m * 65/100 # 200/65R15, 
#                , P_rated = 130 * hp_to_W # W,  says 139
#                )
corolla_tbl

Making the units consistent:

corolla <- (   corolla_tbl
           %>% select( Variable, Value )
           %>% spread( Variable, Value )
           %>% mutate( mass = mass * lbm_to_kg
                     , A_cs = c_d_A_cs / c_d
                     , r = tire_id/2*in_to_m + tire_width * mm_to_m * tire_aspect / 100
                     , P_rated = P_rated * hp_to_W
                     , R_G = R_G / mph_to_ms
                     )
           %>% select( mass, c_d, A_cs, c_rr, r, P_rated, R_G, omega_max )
           )
knitr::kable( corolla
            , col.names = c( "Mass (kg)", "$C_d$ (unitless)", "$A_{cs}$ ($m^2$)", "$C_{rr}$ (unitless)", "Tire radius (m)", "$P_{rated}$ (kW)", "Gearing (RPM-s/m)", "Rated $\\omega$ (RPM)" )
            )
Mass (kg) Cd (unitless) Acs (m2) Crr (unitless) Tire radius (m) Prated (kW) Gearing (RPM-s/m) Rated ω (RPM)
1320 0.3 2.103 0.01 0.3205 103652 89.49 6100

Equations

Weight

From force is mass times acceleration:

\[ W = m \cdot g \]

W <- corolla$mass * g

For the Corolla, \(W = (1319.976)(9.81)=1.2949\times 10^{4}~N\).

Rolling resistance

From physics1, rolling resistance is a fraction of the weight:

\[ F_r = c_{rr} \cdot W \]

F_r_corolla <- corolla$c_rr * W

For the Corolla, \(F_{r,Corolla} = (0.01)(1.2949\times 10^{4}) = 129.4896~N\).

Air drag

Density, coefficient of drag, and cross-sectional area modify the relative air speed to obtain air drag.

\[ F_d = \frac{1}{2} \cdot c_d \rho A_{cs} u^2 \]

F_d <- function( u, rho = rho_air_std, car_info = corolla ) {
    0.5 * car_info$c_d * rho * car_info$A_cs * u^2
}
F_d_corolla_70 <- F_d( 70 * mph_to_ms )

For the Corolla at 70mph, \(Fd = \frac{1}{2} \cdot (0.3)(1.225)(2.1033)(70 \cdot 0.447)^2 = 378.396~N\)

Gravity

If you intend to maintain speed up a hill then you will need to be able to overcome the force of gravity:

\[ F_g = W \cdot \sin{\theta} \]

where a slope of \(3.4336~{}^\circ\) is the usual limit2.

F_g_corolla <- W * sin( atan2( 6, 100 ) )

which is \(F_{g,Corolla} = 775.5432~N\)

Opposition power

Since work is force times distance, power is force times velocity:

\[ P = F \cdot u \]

If we add the opposition forces and multiply by velocity, we can compute the power being absorbed by these mechanisms at different velocities:

\[ P_{opposition} = (F_r + F_d + F_g) \cdot u \]

P_opposition_corolla_70 = (F_r_corolla + F_d_corolla_70 + F_g_corolla) * (70 * mph_to_ms)

For the Corolla, this is \(P = \left((129.4896) + (378.396) + (775.5432) \right) \cdot (31.3) = 4.0158\times 10^{4}~W\)

Pedal power

The torque produced in response to throttle pedal position is not exactly constant over the variation of engine speed (subject to transmission gear setting, corresponding to velocity). Some example curves from Ford3 support the idea that torque is approximately constant4 at full throttle though the torque does reduce with speed when throttle position is only partially-engaged. However, of more interest is the torque vs. throttle position at a mid-speed condition, which shows that while not-quite-linear, the throttle position can be used to obtain a range of desired torque levels.

knitr::include_graphics( "Engine-map-engine-torque-vs-throttle-and-engine-RPM.png" )

So for a moment, let’s approximate this behavior at mid-RPM conditions as a linear mapping between throttle position p and torque T:

\[ T = {p} \cdot T_{max} \]

Since power is torque times angular speed, and the gearing between the wheel and the engine (\(R_G\) in \(RPM/\frac{m}{s}\)) links angular speed with velocity:

\[ P_{engine} = T \cdot \omega = (p \cdot T_{max})(R_G \cdot u) = p \cdot \left(T_{max} \frac{\omega_{max}}{\omega_{max}}\right) \cdot R_G \cdot u = p \cdot P_{engine,max} \frac{R_G u}{\omega_{max}} \]

Here we assume \(P_{engine,max}=1.0365\times 10^{5}~W\).

If the power out of the engine equals the power being absorbed by air drag and rolling resistance then the forces involved will also be balanced and the velocity will be constant (vehicle steady speed).

pedal_levels <- seq( 10, 70, 10 )
(   expand.grid( MPH = 10:80
               , p = pedal_levels
               )
%>% mutate( p_f = factor( p, levels = pedal_levels )
          , u = MPH * mph_to_ms
          , F_d_corolla = F_d( u )
          , P_opposition_corolla = ( F_d_corolla + F_r_corolla + F_g_corolla ) * u
          , P_engine_corolla = p/100 * corolla$P_rated * corolla$R_G * u / corolla$omega_max
          )
%>% select( MPH, p_f, P_opposition_corolla, P_engine_corolla )
%>% gather( variable, value, -c( MPH, p_f ) )
%>% ggplot( aes( x = MPH, y = value, colour = variable ) ) ) +
    geom_line() +
    facet_wrap( ~p_f ) +
    scale_colour_discrete( name = "Power Flow" ) +
    ylab( "Power (W)" ) +
    xlab( "Speed (miles/hour)" )

When the blue line is below the red engine output (e.g. at low speed and high pedal position) the car can accelerate. But when the blue line catches up to the red line (e.g. at \(45~mph\)/\(70\%~\) pedal), you have no ability to accelerate. And when the blue line is above the red line (e.g. at \(45~mph\)/\(50\%~\) pedal) the car will be decelerating.

This value of \(R_G\) represents high gear, and the fact that it is so difficult to overcome the losses in high gear is why cars have low gears.

\(\require{cancel}\)

Fuel

For reference, 1 kilogram of gasoline releases approximately \(46.4~MJ\) of heat5 and the density of gasoline is about \(0.755~kg/L\)6, so a car traveling at \(70~mph\) with a fuel efficiency of \(35~mpg\) is converting \(\frac{46.4~\cancel{MJ}}{1~\cancel{kg}} \frac{1000~kJ}{1~\cancel{MJ}} \frac{0.755~\cancel{kg}}{1~\cancel{L}} \frac{3.785~\cancel{L}}{1~\cancel{gal}} \frac{1~\cancel{gal}}{35~\cancel{mi}} \frac{70~\cancel{mi}}{1~\cancel{h}} \frac{1~\cancel{h}}{3600~s}=73.6645~kW\) of power from chemical form to heat. Since only about 20-30% of the thermal power typically makes it mechanically to the wheels where the above analysis applies7, one would expect that this means only \(15-25~kW\) is available at the wheels, which seems only sort of consistent with the above analysis.

In fact you probably cannot achieve \(35~mpg\) while traveling up a 6% slope at \(45~mph\) in a \(1300~kg\) vehicle… under those conditions you are likely running at a lower efficiency such as \(20~mpg\) or lower. Some combination of slowing down to reduce opposing power and burning more fuel to increase power availability can increase the power excess (red minus blue). The available mechanical power at \(20~mpg\) would be on the order of \(\frac{35}{20} \cdot 25~kW \approx 44~kW\). Reducing weight can also affect the rolling resistance and gravity forces and therefore reduce power losses that need to be overcome. Driving a Toyota Prius with the Eco indicator8 enabled can give you some intuitive insight into just how much driving conditions affect instantaneous efficiency.

Assuming the road slope is level (\(0~{}^\circ\)), we get:

pedal_levels <- seq( 10, 70, 10 )
(   expand.grid( MPH = 10:80
               , p = pedal_levels
               )
%>% mutate( p_f = factor( p, levels = pedal_levels )
          , u = MPH * mph_to_ms
          , F_d_corolla = F_d( u )
          , P_opposition_corolla = ( F_d_corolla + F_r_corolla + 0 ) * u
          , P_engine_corolla = p/100 * corolla$P_rated * corolla$R_G * u / corolla$omega_max
          )
%>% select( MPH, p_f, P_opposition_corolla, P_engine_corolla )
%>% gather( variable, value, -c( MPH, p_f ) )
%>% ggplot( aes( x = MPH, y = value, colour = variable ) ) ) +
    geom_line() +
    facet_wrap( ~p_f ) +
    scale_colour_discrete( name = "Power Flow" ) +
    ylab( "Power (W)" ) +
    xlab( "Speed (miles/hour)" )

which has quite a lot of potential to accelerate the car (red above blue) in high gear even at medium pedal positions (well below engine maximum power rating).



  1. https://en.wikipedia.org/wiki/Rolling_resistance↩

  2. https://en.wikipedia.org/wiki/Grade_(slope)↩

  3. Laila, D.S., P. Shakouri, A. Ordys, and M. Askari. “Longitudinal Vehicle Dynamics Using Simulink/Matlab.” In UKACC International Conference on CONTROL 2010, 955–60. Coventry, UK: Institution of Engineering and Technology, 2010. https://doi.org/10.1049/ic.2010.0410. Figure from https://www.researchgate.net/figure/Engine-map-engine-torque-vs-throttle-and-engine-RPM_fig7_261390259↩

  4. https://www.toyotanation.com/threads/torque-curves-for-new-engines-vs-previous-gen.1623010/#post-13768106 djoyce101 posted some example dynamometer curves↩

  5. https://en.wikipedia.org/wiki/Energy_density↩

  6. https://en.wikipedia.org/wiki/Gasoline↩

  7. https://www.fueleconomy.gov/FEG/atv.shtml↩

  8. https://www.toyotaguru.us/prius-2010-manual/i-hybrid-system-indicator.html↩

LS0tDQp0aXRsZTogIkNhciBhY2NlbGVyYXRpb24iDQphdXRob3I6ICJKZWZmIE5ld21pbGxlciINCmRhdGU6IDIwMjEtMDEtMzENCm91dHB1dDoNCiAgICBodG1sX25vdGVib29rOg0KICAgICAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICAgICAgZmlnX2NhcHRpb246IFRSVUUNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyfQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsNCiAgICBsaWJyYXJ5KGRwbHlyKQ0KICAgIGxpYnJhcnkodGlkeXIpDQogICAgbGlicmFyeShnZ3Bsb3QyKQ0KICAgIGxpYnJhcnkoaHRtbHRvb2xzKQ0KfSkNCm9wdGlvbnMoZGlnaXRzID0gNCkNCmBgYA0KDQojIFNjb3BlDQoNCkEgcm91Z2ggZW5naW5lZXJpbmcgYW5hbHlzaXMgb2YgdmVoaWNsZSB0aHJvdHRsZSBiZWhhdmlvciByZWxhdGluZyBhY2NlbGVyYXRpb24sIHNwZWVkLCBhY2NlbGVyYXRpb24sIGFuZCBsb3NzZXMuDQoNCkFzIGFuIGV4YW1wbGUsIGtleSBkZXNpZ24gcGFyYW1ldGVycyBmb3IgdHlwaWNhbCBUb3lvdGEgQ29yb2xsYXMgaGF2ZSBiZWVuIGNvbGxlY3RlZCBoZXJlOg0KDQpgYGB7cn0NCmcgPC0gOS44MSAjIE4vbV4yDQpyaG9fYWlyX3N0ZCA8LSAxLjIyNSAjIGtnL21eMywgaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGVuc2l0eV9vZl9haXINCmhwX3RvX1cgPC0gNzQ1LjcgIyBXL2hwDQpsYm1fdG9fa2cgPC0gMC40NTM2DQppbl90b19tIDwtIDAuMDI1NA0KbW1fdG9fbSA8LSAwLjAwMQ0KbXBoX3RvX21zIDwtIDAuNDQ3ICMgbWV0ZXJzLXBlci1zZWNvbmQgcGVyIG1pbGVzLXBlci1ob3VyDQpjb3JvbGxhX3RibCA8LSByZWFkLnRhYmxlKCB0ZXh0ID0NCidWYXJpYWJsZSAgIFZhbHVlICAgVW5pdCAgICAgVVJMDQptYXNzICAgICAgICAyOTEwICAgICJsYm0iICAgICJodHRwczovL3d3dy5jYXJhbmRkcml2ZXIuY29tL3RveW90YS9jb3JvbGxhL3NwZWNzIg0KY19kICAgICAgICAgMC4zICAgICAiLSIgICAgICAiaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQXV0b21vYmlsZV9kcmFnX2NvZWZmaWNpZW50Ig0KY19kX0FfY3MgICAgMC42MzEgICAibV4yIiAgICAiaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQXV0b21vYmlsZV9kcmFnX2NvZWZmaWNpZW50Ig0KY19yciAgICAgICAgMC4wMSAgICAiLSIgICAgICAiaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUm9sbGluZ19yZXNpc3RhbmNlIg0KdGlyZV9pZCAgICAgMTUgICAgICAiaW4iICAgICAiaHR0cHM6Ly93d3cubGVzc2Nod2FiLmNvbS9hcnRpY2xlL3RpcmUtc2l6ZS1leHBsYWluZWQtcmVhZGluZy10aGUtc2lkZXdhbGwuaHRtbCINCnRpcmVfd2lkdGggIDIwMCAgICAgIm1tIiAgICAgImh0dHBzOi8vd3d3Lmxlc3NjaHdhYi5jb20vYXJ0aWNsZS90aXJlLXNpemUtZXhwbGFpbmVkLXJlYWRpbmctdGhlLXNpZGV3YWxsLmh0bWwiDQp0aXJlX2FzcGVjdCA2NSAgICAgICIlIiAgICAgICJodHRwczovL3d3dy5sZXNzY2h3YWIuY29tL2FydGljbGUvdGlyZS1zaXplLWV4cGxhaW5lZC1yZWFkaW5nLXRoZS1zaWRld2FsbC5odG1sIg0KUF9yYXRlZCAgICAgMTM5ICAgICAiaHAiICAgICAiaHR0cHM6Ly93d3cuY2FyYW5kZHJpdmVyLmNvbS90b3lvdGEvY29yb2xsYS9zcGVjcyINClJfRyAgICAgICAgIDQwICAgICAgIlJQTS1oci9taSIgImh0dHA6Ly93d3cuY29yb2xsYWZvcnVtLmNvbS90aHJlYWRzLzIwMTItbGUtaGlnaC1ycG0tYXQtODAtbXBoLjEzMDUvIg0Kb21lZ2FfbWF4ICAgNjEwMCAgICAiUlBNIiAgICAiaHR0cHM6Ly93d3cuY2FyYW5kZHJpdmVyLmNvbS90b3lvdGEvY29yb2xsYS9zcGVjcyINCicsIGhlYWRlciA9IFRSVUUsIGFzLmlzPVRSVUUgKQ0KIyBjb3JvbGxhIDwtIGxpc3QoIG1hc3MgPSAyOTEwICogbGJtX3RvX2tnICMga2csIA0KIyAgICAgICAgICAgICAgICAsIGNfZCA9IDAuMyAjIA0KIyAgICAgICAgICAgICAgICAsIEFfY3MgPSAwLjYzMSAvIDAuMyAjIG1eMiwgZGl0dG8NCiMgICAgICAgICAgICAgICAgLCBjX3JyID0gMC4wMSAjIHVuaXRsZXNzDQojICAgICAgICAgICAgICAgICwgciA9IDE1LzIqaW5fdG9fbSArIDIwMCAqIG1tX3RvX20gKiA2NS8xMDAgIyAyMDAvNjVSMTUsIA0KIyAgICAgICAgICAgICAgICAsIFBfcmF0ZWQgPSAxMzAgKiBocF90b19XICMgVywgIHNheXMgMTM5DQojICAgICAgICAgICAgICAgICkNCmNvcm9sbGFfdGJsDQpgYGANCg0KTWFraW5nIHRoZSB1bml0cyBjb25zaXN0ZW50Og0KDQpgYGB7cn0NCmNvcm9sbGEgPC0gKCAgIGNvcm9sbGFfdGJsDQogICAgICAgICAgICU+JSBzZWxlY3QoIFZhcmlhYmxlLCBWYWx1ZSApDQogICAgICAgICAgICU+JSBzcHJlYWQoIFZhcmlhYmxlLCBWYWx1ZSApDQogICAgICAgICAgICU+JSBtdXRhdGUoIG1hc3MgPSBtYXNzICogbGJtX3RvX2tnDQogICAgICAgICAgICAgICAgICAgICAsIEFfY3MgPSBjX2RfQV9jcyAvIGNfZA0KICAgICAgICAgICAgICAgICAgICAgLCByID0gdGlyZV9pZC8yKmluX3RvX20gKyB0aXJlX3dpZHRoICogbW1fdG9fbSAqIHRpcmVfYXNwZWN0IC8gMTAwDQogICAgICAgICAgICAgICAgICAgICAsIFBfcmF0ZWQgPSBQX3JhdGVkICogaHBfdG9fVw0KICAgICAgICAgICAgICAgICAgICAgLCBSX0cgPSBSX0cgLyBtcGhfdG9fbXMNCiAgICAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgICAgJT4lIHNlbGVjdCggbWFzcywgY19kLCBBX2NzLCBjX3JyLCByLCBQX3JhdGVkLCBSX0csIG9tZWdhX21heCApDQogICAgICAgICAgICkNCmtuaXRyOjprYWJsZSggY29yb2xsYQ0KICAgICAgICAgICAgLCBjb2wubmFtZXMgPSBjKCAiTWFzcyAoa2cpIiwgIiRDX2QkICh1bml0bGVzcykiLCAiJEFfe2NzfSQgKCRtXjIkKSIsICIkQ197cnJ9JCAodW5pdGxlc3MpIiwgIlRpcmUgcmFkaXVzIChtKSIsICIkUF97cmF0ZWR9JCAoa1cpIiwgIkdlYXJpbmcgKFJQTS1zL20pIiwgIlJhdGVkICRcXG9tZWdhJCAoUlBNKSIgKQ0KICAgICAgICAgICAgKQ0KYGBgDQoNCg0KIyBFcXVhdGlvbnMNCg0KIyMgV2VpZ2h0DQoNCkZyb20gZm9yY2UgaXMgbWFzcyB0aW1lcyBhY2NlbGVyYXRpb246DQoNCiQkDQpXID0gbSBcY2RvdCBnDQokJA0KDQpgYGB7cn0NClcgPC0gY29yb2xsYSRtYXNzICogZw0KYGBgDQoNCkZvciB0aGUgQ29yb2xsYSwgJFcgPSAoYHIgY29yb2xsYSRtYXNzYCkoYHIgZ2ApPWByIFdgfk4kLg0KDQojIyBSb2xsaW5nIHJlc2lzdGFuY2UNCg0KRnJvbSBwaHlzaWNzW14zXSwgcm9sbGluZyByZXNpc3RhbmNlIGlzIGEgZnJhY3Rpb24gb2YgdGhlIHdlaWdodDoNCg0KJCQNCkZfciA9IGNfe3JyfSBcY2RvdCBXDQokJA0KDQpgYGB7cn0NCkZfcl9jb3JvbGxhIDwtIGNvcm9sbGEkY19yciAqIFcNCmBgYA0KDQoNCkZvciB0aGUgQ29yb2xsYSwgJEZfe3IsQ29yb2xsYX0gPSAoYHIgY29yb2xsYSRjX3JyYCkoYHIgV2ApID0gYHIgRl9yX2Nvcm9sbGFgfk4kLg0KDQojIyBBaXIgZHJhZw0KDQpEZW5zaXR5LCBjb2VmZmljaWVudCBvZiBkcmFnLCBhbmQgY3Jvc3Mtc2VjdGlvbmFsIGFyZWEgbW9kaWZ5IHRoZSByZWxhdGl2ZSBhaXIgc3BlZWQgdG8gb2J0YWluIGFpciBkcmFnLg0KDQokJA0KRl9kID0gXGZyYWN7MX17Mn0gXGNkb3QgY19kIFxyaG8gQV97Y3N9IHVeMg0KJCQNCg0KYGBge3J9DQpGX2QgPC0gZnVuY3Rpb24oIHUsIHJobyA9IHJob19haXJfc3RkLCBjYXJfaW5mbyA9IGNvcm9sbGEgKSB7DQogICAgMC41ICogY2FyX2luZm8kY19kICogcmhvICogY2FyX2luZm8kQV9jcyAqIHVeMg0KfQ0KYGBgDQoNCg0KYGBge3J9DQpGX2RfY29yb2xsYV83MCA8LSBGX2QoIDcwICogbXBoX3RvX21zICkNCmBgYA0KDQpGb3IgdGhlIENvcm9sbGEgYXQgNzBtcGgsICRGZCA9IFxmcmFjezF9ezJ9IFxjZG90IChgciBjb3JvbGxhJGNfZGApKGByIHJob19haXJfc3RkYCkoYHIgY29yb2xsYSRBX2NzYCkoNzAgXGNkb3QgYHIgbXBoX3RvX21zYCleMiA9IGByIEZfZF9jb3JvbGxhXzcwYH5OJA0KDQojIyBHcmF2aXR5DQoNCklmIHlvdSBpbnRlbmQgdG8gbWFpbnRhaW4gc3BlZWQgdXAgYSBoaWxsIHRoZW4geW91IHdpbGwgbmVlZCB0byBiZSBhYmxlIHRvIG92ZXJjb21lIHRoZSBmb3JjZSBvZiBncmF2aXR5Og0KDQokJA0KRl9nID0gVyBcY2RvdCBcc2lue1x0aGV0YX0NCiQkDQoNCndoZXJlIGEgc2xvcGUgb2YgJGByIDE4MC9waSphdGFuMig2LDEwMClgfnt9XlxjaXJjJCBpcyB0aGUgdXN1YWwgbGltaXRbXjVdLg0KDQpgYGB7cn0NCkZfZ19jb3JvbGxhIDwtIFcgKiBzaW4oIGF0YW4yKCA2LCAxMDAgKSApDQpgYGANCg0Kd2hpY2ggaXMgJEZfe2csQ29yb2xsYX0gPSBgciBGX2dfY29yb2xsYWB+TiQNCg0KIyMgT3Bwb3NpdGlvbiBwb3dlcg0KDQpTaW5jZSB3b3JrIGlzIGZvcmNlIHRpbWVzIGRpc3RhbmNlLCBwb3dlciBpcyBmb3JjZSB0aW1lcyB2ZWxvY2l0eToNCg0KJCQNClAgPSBGIFxjZG90IHUNCiQkDQoNCklmIHdlIGFkZCB0aGUgb3Bwb3NpdGlvbiBmb3JjZXMgYW5kIG11bHRpcGx5IGJ5IHZlbG9jaXR5LCB3ZSBjYW4gY29tcHV0ZSB0aGUgcG93ZXIgYmVpbmcgYWJzb3JiZWQgYnkgdGhlc2UgbWVjaGFuaXNtcyBhdCBkaWZmZXJlbnQgdmVsb2NpdGllczoNCg0KJCQNClBfe29wcG9zaXRpb259ID0gKEZfciArIEZfZCArIEZfZykgXGNkb3QgdQ0KJCQNCg0KYGBge3J9DQpQX29wcG9zaXRpb25fY29yb2xsYV83MCA9IChGX3JfY29yb2xsYSArIEZfZF9jb3JvbGxhXzcwICsgRl9nX2Nvcm9sbGEpICogKDcwICogbXBoX3RvX21zKQ0KYGBgDQoNCkZvciB0aGUgQ29yb2xsYSwgdGhpcyBpcyAkUCA9IFxsZWZ0KChgciBGX3JfY29yb2xsYWApICsgKGByIEZfZF9jb3JvbGxhXzcwYCkgKyAoYHIgRl9nX2Nvcm9sbGFgKSBccmlnaHQpIFxjZG90ICgzMS4zKSA9IGByIFBfb3Bwb3NpdGlvbl9jb3JvbGxhXzcwYH5XJA0KDQojIyBQZWRhbCBwb3dlcg0KDQpUaGUgdG9ycXVlIHByb2R1Y2VkIGluIHJlc3BvbnNlIHRvIHRocm90dGxlIHBlZGFsIHBvc2l0aW9uIGlzIG5vdCBleGFjdGx5IGNvbnN0YW50IG92ZXIgdGhlIHZhcmlhdGlvbiBvZiBlbmdpbmUgc3BlZWQgKHN1YmplY3QgdG8gdHJhbnNtaXNzaW9uIGdlYXIgc2V0dGluZywgY29ycmVzcG9uZGluZyB0byB2ZWxvY2l0eSkuIFNvbWUgZXhhbXBsZSBjdXJ2ZXMgZnJvbSBGb3JkW14yXSBzdXBwb3J0IHRoZSBpZGVhIHRoYXQgdG9ycXVlIGlzIGFwcHJveGltYXRlbHkgY29uc3RhbnRbXjFdIGF0IGZ1bGwgdGhyb3R0bGUgdGhvdWdoIHRoZSB0b3JxdWUgZG9lcyByZWR1Y2Ugd2l0aCBzcGVlZCB3aGVuIHRocm90dGxlIHBvc2l0aW9uIGlzIG9ubHkgcGFydGlhbGx5LWVuZ2FnZWQuIEhvd2V2ZXIsIG9mIG1vcmUgaW50ZXJlc3QgaXMgdGhlIHRvcnF1ZSB2cy4gdGhyb3R0bGUgcG9zaXRpb24gYXQgYSBtaWQtc3BlZWQgY29uZGl0aW9uLCB3aGljaCBzaG93cyB0aGF0IHdoaWxlIG5vdC1xdWl0ZS1saW5lYXIsIHRoZSB0aHJvdHRsZSBwb3NpdGlvbiBjYW4gYmUgdXNlZCB0byBvYnRhaW4gYSByYW5nZSBvZiBkZXNpcmVkIHRvcnF1ZSBsZXZlbHMuDQoNCmBgYHtyLGZpZy5jYXA9IkV4YW1wbGUgRW5naW5lIFJQTSB2cyBUaHJvdHRsZSBhbmQgVG9ycXVlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCAiRW5naW5lLW1hcC1lbmdpbmUtdG9ycXVlLXZzLXRocm90dGxlLWFuZC1lbmdpbmUtUlBNLnBuZyIgKQ0KYGBgDQoNClNvIGZvciBhIG1vbWVudCwgbGV0J3MgYXBwcm94aW1hdGUgdGhpcyBiZWhhdmlvciBhdCBtaWQtUlBNIGNvbmRpdGlvbnMgYXMgYSBsaW5lYXIgbWFwcGluZyBiZXR3ZWVuIHRocm90dGxlIHBvc2l0aW9uIHAgYW5kIHRvcnF1ZSBUOg0KDQokJA0KVCA9IHtwfSBcY2RvdCBUX3ttYXh9DQokJA0KDQpTaW5jZSBwb3dlciBpcyB0b3JxdWUgdGltZXMgYW5ndWxhciBzcGVlZCwgYW5kIHRoZSBnZWFyaW5nIGJldHdlZW4gdGhlIHdoZWVsIGFuZCB0aGUgZW5naW5lICgkUl9HJCBpbiAkUlBNL1xmcmFje219e3N9JCkgbGlua3MgYW5ndWxhciBzcGVlZCB3aXRoIHZlbG9jaXR5Og0KDQokJA0KUF97ZW5naW5lfSA9IFQgXGNkb3QgXG9tZWdhID0gKHAgXGNkb3QgVF97bWF4fSkoUl9HIFxjZG90IHUpID0gcCBcY2RvdCBcbGVmdChUX3ttYXh9IFxmcmFje1xvbWVnYV97bWF4fX17XG9tZWdhX3ttYXh9fVxyaWdodCkgXGNkb3QgUl9HIFxjZG90IHUgPSBwIFxjZG90IFBfe2VuZ2luZSxtYXh9IFxmcmFje1JfRyB1fXtcb21lZ2Ffe21heH19DQokJA0KDQpIZXJlIHdlIGFzc3VtZSAkUF97ZW5naW5lLG1heH09YHIgY29yb2xsYSRQX3JhdGVkYH5XJC4NCg0KSWYgdGhlIHBvd2VyIG91dCBvZiB0aGUgZW5naW5lIGVxdWFscyB0aGUgcG93ZXIgYmVpbmcgYWJzb3JiZWQgYnkgYWlyIGRyYWcgYW5kIHJvbGxpbmcgcmVzaXN0YW5jZSB0aGVuIHRoZSBmb3JjZXMgaW52b2x2ZWQgd2lsbCBhbHNvIGJlIGJhbGFuY2VkIGFuZCB0aGUgdmVsb2NpdHkgd2lsbCBiZSBjb25zdGFudCAodmVoaWNsZSBzdGVhZHkgc3BlZWQpLg0KDQpgYGB7cixmaWcuY2FwPSJQb3dlciB2cyBzcGVlZCBhdCB2YXJpb3VzIHBlZGFsIHBvc2l0aW9ucyAoJSkifQ0KcGVkYWxfbGV2ZWxzIDwtIHNlcSggMTAsIDcwLCAxMCApDQooICAgZXhwYW5kLmdyaWQoIE1QSCA9IDEwOjgwDQogICAgICAgICAgICAgICAsIHAgPSBwZWRhbF9sZXZlbHMNCiAgICAgICAgICAgICAgICkNCiU+JSBtdXRhdGUoIHBfZiA9IGZhY3RvciggcCwgbGV2ZWxzID0gcGVkYWxfbGV2ZWxzICkNCiAgICAgICAgICAsIHUgPSBNUEggKiBtcGhfdG9fbXMNCiAgICAgICAgICAsIEZfZF9jb3JvbGxhID0gRl9kKCB1ICkNCiAgICAgICAgICAsIFBfb3Bwb3NpdGlvbl9jb3JvbGxhID0gKCBGX2RfY29yb2xsYSArIEZfcl9jb3JvbGxhICsgRl9nX2Nvcm9sbGEgKSAqIHUNCiAgICAgICAgICAsIFBfZW5naW5lX2Nvcm9sbGEgPSBwLzEwMCAqIGNvcm9sbGEkUF9yYXRlZCAqIGNvcm9sbGEkUl9HICogdSAvIGNvcm9sbGEkb21lZ2FfbWF4DQogICAgICAgICAgKQ0KJT4lIHNlbGVjdCggTVBILCBwX2YsIFBfb3Bwb3NpdGlvbl9jb3JvbGxhLCBQX2VuZ2luZV9jb3JvbGxhICkNCiU+JSBnYXRoZXIoIHZhcmlhYmxlLCB2YWx1ZSwgLWMoIE1QSCwgcF9mICkgKQ0KJT4lIGdncGxvdCggYWVzKCB4ID0gTVBILCB5ID0gdmFsdWUsIGNvbG91ciA9IHZhcmlhYmxlICkgKSApICsNCiAgICBnZW9tX2xpbmUoKSArDQogICAgZmFjZXRfd3JhcCggfnBfZiApICsNCiAgICBzY2FsZV9jb2xvdXJfZGlzY3JldGUoIG5hbWUgPSAiUG93ZXIgRmxvdyIgKSArDQogICAgeWxhYiggIlBvd2VyIChXKSIgKSArDQogICAgeGxhYiggIlNwZWVkIChtaWxlcy9ob3VyKSIgKQ0KYGBgDQoNCldoZW4gdGhlIGJsdWUgbGluZSBpcyBiZWxvdyB0aGUgcmVkIGVuZ2luZSBvdXRwdXQgKGUuZy4gYXQgbG93IHNwZWVkIGFuZCBoaWdoIHBlZGFsIHBvc2l0aW9uKSB0aGUgY2FyIGNhbiBhY2NlbGVyYXRlLiBCdXQgd2hlbiB0aGUgYmx1ZSBsaW5lIGNhdGNoZXMgdXAgdG8gdGhlIHJlZCBsaW5lIChlLmcuIGF0ICQ0NX5tcGgkLyQ3MFwlfiQgcGVkYWwpLCB5b3UgaGF2ZSBubyBhYmlsaXR5IHRvIGFjY2VsZXJhdGUuIEFuZCB3aGVuIHRoZSBibHVlIGxpbmUgaXMgYWJvdmUgdGhlIHJlZCBsaW5lIChlLmcuIGF0ICQ0NX5tcGgkLyQ1MFwlfiQgcGVkYWwpIHRoZSBjYXIgd2lsbCBiZSBkZWNlbGVyYXRpbmcuDQoNClRoaXMgdmFsdWUgb2YgJFJfRyQgcmVwcmVzZW50cyBoaWdoIGdlYXIsIGFuZCB0aGUgZmFjdCB0aGF0IGl0IGlzIHNvIGRpZmZpY3VsdCB0byBvdmVyY29tZSB0aGUgbG9zc2VzIGluIGhpZ2ggZ2VhciBpcyB3aHkgY2FycyBoYXZlIGxvdyBnZWFycy4NCg0KICRccmVxdWlyZXtjYW5jZWx9JA0KDQojIEZ1ZWwNCg0KRm9yIHJlZmVyZW5jZSwgMSBraWxvZ3JhbSBvZiBnYXNvbGluZSByZWxlYXNlcyBhcHByb3hpbWF0ZWx5ICQ0Ni40fk1KJCBvZiBoZWF0W140XSBhbmQgdGhlIGRlbnNpdHkgb2YgZ2Fzb2xpbmUgaXMgYWJvdXQgJDAuNzU1fmtnL0wkW142XSwgc28gYSBjYXIgdHJhdmVsaW5nIGF0ICQ3MH5tcGgkIHdpdGggYSBmdWVsIGVmZmljaWVuY3kgb2YgJDM1fm1wZyQgaXMgY29udmVydGluZyAkXGZyYWN7NDYuNH5cY2FuY2Vse01KfX17MX5cY2FuY2Vse2tnfX0gXGZyYWN7MTAwMH5rSn17MX5cY2FuY2Vse01KfX0gXGZyYWN7MC43NTV+XGNhbmNlbHtrZ319ezF+XGNhbmNlbHtMfX0gXGZyYWN7My43ODV+XGNhbmNlbHtMfX17MX5cY2FuY2Vse2dhbH19IFxmcmFjezF+XGNhbmNlbHtnYWx9fXszNX5cY2FuY2Vse21pfX0gXGZyYWN7NzB+XGNhbmNlbHttaX19ezF+XGNhbmNlbHtofX0gXGZyYWN7MX5cY2FuY2Vse2h9fXszNjAwfnN9PWByIDQ2LjQgKiAxMDAwICogMC43NTUgKiAzLjc4NSAvIDM1ICogNzAgLyAzNjAwYH5rVyQgb2YgcG93ZXIgZnJvbSBjaGVtaWNhbCBmb3JtIHRvIGhlYXQuIFNpbmNlIG9ubHkgYWJvdXQgMjAtMzAlIG9mIHRoZSB0aGVybWFsIHBvd2VyIHR5cGljYWxseSBtYWtlcyBpdCBtZWNoYW5pY2FsbHkgdG8gdGhlIHdoZWVscyB3aGVyZSB0aGUgYWJvdmUgYW5hbHlzaXMgYXBwbGllc1teN10sIG9uZSB3b3VsZCBleHBlY3QgdGhhdCB0aGlzIG1lYW5zIG9ubHkgJDE1LTI1fmtXJCBpcyBhdmFpbGFibGUgYXQgdGhlIHdoZWVscywgd2hpY2ggc2VlbXMgb25seSBzb3J0IG9mIGNvbnNpc3RlbnQgd2l0aCB0aGUgYWJvdmUgYW5hbHlzaXMuDQoNCkluIGZhY3QgeW91IHByb2JhYmx5IGNhbm5vdCBhY2hpZXZlICQzNX5tcGckIHdoaWxlIHRyYXZlbGluZyB1cCBhIDYlIHNsb3BlIGF0ICQ0NX5tcGgkIGluIGEgJDEzMDB+a2ckIHZlaGljbGUuLi4gdW5kZXIgdGhvc2UgY29uZGl0aW9ucyB5b3UgYXJlIGxpa2VseSBydW5uaW5nIGF0IGEgbG93ZXIgZWZmaWNpZW5jeSBzdWNoIGFzICQyMH5tcGckIG9yIGxvd2VyLiBTb21lIGNvbWJpbmF0aW9uIG9mIHNsb3dpbmcgZG93biB0byByZWR1Y2Ugb3Bwb3NpbmcgcG93ZXIgYW5kIGJ1cm5pbmcgbW9yZSBmdWVsIHRvIGluY3JlYXNlIHBvd2VyIGF2YWlsYWJpbGl0eSBjYW4gaW5jcmVhc2UgdGhlIHBvd2VyIGV4Y2VzcyAocmVkIG1pbnVzIGJsdWUpLiBUaGUgYXZhaWxhYmxlIG1lY2hhbmljYWwgcG93ZXIgYXQgJDIwfm1wZyQgd291bGQgYmUgb24gdGhlIG9yZGVyIG9mICRcZnJhY3szNX17MjB9IFxjZG90IDI1fmtXIFxhcHByb3ggNDR+a1ckLiBSZWR1Y2luZyB3ZWlnaHQgY2FuIGFsc28gYWZmZWN0IHRoZSByb2xsaW5nIHJlc2lzdGFuY2UgYW5kIGdyYXZpdHkgZm9yY2VzIGFuZCB0aGVyZWZvcmUgcmVkdWNlIHBvd2VyIGxvc3NlcyB0aGF0IG5lZWQgdG8gYmUgb3ZlcmNvbWUuIERyaXZpbmcgYSBUb3lvdGEgUHJpdXMgd2l0aCB0aGUgRWNvIGluZGljYXRvclteOF0gZW5hYmxlZCBjYW4gZ2l2ZSB5b3Ugc29tZSBpbnR1aXRpdmUgaW5zaWdodCBpbnRvIGp1c3QgaG93IG11Y2ggZHJpdmluZyBjb25kaXRpb25zIGFmZmVjdCBpbnN0YW50YW5lb3VzIGVmZmljaWVuY3kuDQoNCkFzc3VtaW5nIHRoZSByb2FkIHNsb3BlIGlzIGxldmVsICgkMH57fV5cY2lyYyQpLCB3ZSBnZXQ6DQoNCmBgYHtyLGZpZy5jYXA9IlBvd2VyIHZzIHNwZWVkIGF0IHZhcmlvdXMgcGVkYWwgcG9zaXRpb25zIG9uIGxldmVsIGdyb3VuZCAoJSkifQ0KcGVkYWxfbGV2ZWxzIDwtIHNlcSggMTAsIDcwLCAxMCApDQooICAgZXhwYW5kLmdyaWQoIE1QSCA9IDEwOjgwDQogICAgICAgICAgICAgICAsIHAgPSBwZWRhbF9sZXZlbHMNCiAgICAgICAgICAgICAgICkNCiU+JSBtdXRhdGUoIHBfZiA9IGZhY3RvciggcCwgbGV2ZWxzID0gcGVkYWxfbGV2ZWxzICkNCiAgICAgICAgICAsIHUgPSBNUEggKiBtcGhfdG9fbXMNCiAgICAgICAgICAsIEZfZF9jb3JvbGxhID0gRl9kKCB1ICkNCiAgICAgICAgICAsIFBfb3Bwb3NpdGlvbl9jb3JvbGxhID0gKCBGX2RfY29yb2xsYSArIEZfcl9jb3JvbGxhICsgMCApICogdQ0KICAgICAgICAgICwgUF9lbmdpbmVfY29yb2xsYSA9IHAvMTAwICogY29yb2xsYSRQX3JhdGVkICogY29yb2xsYSRSX0cgKiB1IC8gY29yb2xsYSRvbWVnYV9tYXgNCiAgICAgICAgICApDQolPiUgc2VsZWN0KCBNUEgsIHBfZiwgUF9vcHBvc2l0aW9uX2Nvcm9sbGEsIFBfZW5naW5lX2Nvcm9sbGEgKQ0KJT4lIGdhdGhlciggdmFyaWFibGUsIHZhbHVlLCAtYyggTVBILCBwX2YgKSApDQolPiUgZ2dwbG90KCBhZXMoIHggPSBNUEgsIHkgPSB2YWx1ZSwgY29sb3VyID0gdmFyaWFibGUgKSApICkgKw0KICAgIGdlb21fbGluZSgpICsNCiAgICBmYWNldF93cmFwKCB+cF9mICkgKw0KICAgIHNjYWxlX2NvbG91cl9kaXNjcmV0ZSggbmFtZSA9ICJQb3dlciBGbG93IiApICsNCiAgICB5bGFiKCAiUG93ZXIgKFcpIiApICsNCiAgICB4bGFiKCAiU3BlZWQgKG1pbGVzL2hvdXIpIiApDQpgYGANCg0Kd2hpY2ggaGFzIHF1aXRlIGEgbG90IG9mIHBvdGVudGlhbCB0byBhY2NlbGVyYXRlIHRoZSBjYXIgKHJlZCBhYm92ZSBibHVlKSBpbiBoaWdoIGdlYXIgZXZlbiBhdCBtZWRpdW0gcGVkYWwgcG9zaXRpb25zICh3ZWxsIGJlbG93IGVuZ2luZSBtYXhpbXVtIHBvd2VyIHJhdGluZykuDQoNCi0tLQ0KW14xXTogaHR0cHM6Ly93d3cudG95b3RhbmF0aW9uLmNvbS90aHJlYWRzL3RvcnF1ZS1jdXJ2ZXMtZm9yLW5ldy1lbmdpbmVzLXZzLXByZXZpb3VzLWdlbi4xNjIzMDEwLyNwb3N0LTEzNzY4MTA2IGRqb3ljZTEwMSBwb3N0ZWQgc29tZSBleGFtcGxlIGR5bmFtb21ldGVyIGN1cnZlcw0KW14yXTogTGFpbGEsIEQuUy4sIFAuIFNoYWtvdXJpLCBBLiBPcmR5cywgYW5kIE0uIEFza2FyaS4g4oCcTG9uZ2l0dWRpbmFsIFZlaGljbGUgRHluYW1pY3MgVXNpbmcgU2ltdWxpbmsvTWF0bGFiLuKAnSBJbiBVS0FDQyBJbnRlcm5hdGlvbmFsIENvbmZlcmVuY2Ugb24gQ09OVFJPTCAyMDEwLCA5NTXigJM2MC4gQ292ZW50cnksIFVLOiBJbnN0aXR1dGlvbiBvZiBFbmdpbmVlcmluZyBhbmQgVGVjaG5vbG9neSwgMjAxMC4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwNDkvaWMuMjAxMC4wNDEwLiBGaWd1cmUgZnJvbSBodHRwczovL3d3dy5yZXNlYXJjaGdhdGUubmV0L2ZpZ3VyZS9FbmdpbmUtbWFwLWVuZ2luZS10b3JxdWUtdnMtdGhyb3R0bGUtYW5kLWVuZ2luZS1SUE1fZmlnN18yNjEzOTAyNTkNClteM106IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1JvbGxpbmdfcmVzaXN0YW5jZQ0KW140XTogaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRW5lcmd5X2RlbnNpdHkNClteNV06IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dyYWRlXyhzbG9wZSkNClteNl06IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dhc29saW5lDQpbXjddOiBodHRwczovL3d3dy5mdWVsZWNvbm9teS5nb3YvRkVHL2F0di5zaHRtbA0KW144XTogaHR0cHM6Ly93d3cudG95b3RhZ3VydS51cy9wcml1cy0yMDEwLW1hbnVhbC9pLWh5YnJpZC1zeXN0ZW0taW5kaWNhdG9yLmh0bWw=