What does rc
mean in rcParams
?
runtime configuration.
PyLab 绘图能力的完整用户指南参见 https://matplotlib.org/users/index.html
完整的颜色和线型标识符列表,参见 http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot
rcParams 中有很多设置项目,完整的列表参见 http://matplotlib.org/users/customizing.html
如果你不想花费精力对这些参数进行单独设置,可以使用一个预定义的样式表,具体介绍参见 http://matplotlib.org/users/style_sheets.html#style-sheets
def findPayment (loan, r, m):
""" Assumes: 'loan' and 'r' are floats, 'm' an int
Returns the monthly payment for a mortgage of size
'loan' at a monthly rate of 'r' for 'm' months """
return loan * ((r * (1 + r) ** m) / ((1 + r) ** m - 1))
class Mortgage (object):
""" Abstract class for building different kinds of mortgages """
def __init__(self, loan, annRate, months):
""" Assumes: loan and annRate are floats, months an int
Creates a new mortgage of size loan, duration months, and annual rate annRate """
# Here are three instance initial values
self.loan = loan
self.rate = annRate / 12 # Is it an common agreement that annual rate is 12 times of monthly rate?
self.months = months
# Here are two lists extended monthly
self.paid = [0.0] # At the first day of loan you don't need to pay loan. Note this list is extended monthly
self.outstanding = [loan] # the m th month 本金额 remained to be paid
# Here is the monthly payment for a mortgage of size 'loan' at monthly rate of 'self.rate' for 'months' months
self.payment = findPayment (loan, self.rate, months)
self.legend = None # description of certain kind of mortgage
def makePayment (self):
""" Make a monthly payment"""
# Calculate monthly payments then append them to two lists:
# monthly payments and monthly outstanding loan balance
self.paid.append (self.payment)
# reduction used to reduce the loan balance = monthly payment - the amount of interest due on the outstanding loan balance
reduction = self.payment - self.outstanding [-1] * self.rate
self.outstanding.append (self.outstanding [-1] - reduction)
def getTotalPaid (self):
""" Return the total amount paid so far """
return sum (self.paid)
def __str__(self):
return self.legend
def plotPayments(self, style):
pylab.plot(self.paid[1:], style, label = self.legend)
def plotBalance(self, style):
pylab.plot(self.outstanding, style, label = self.legend)
def plotTotPd(self, style):
totPd = [self.paid[0]]
for i in range(1, len(self.paid)):
totPd.append(totPd[-1] + self.paid[i])
pylab.plot(totPd, style, label = self.legend)
def plotNet(self, style):
totPd = [self.paid[0]]
for i in range(1, len(self.paid)):
totPd.append(totPd[-1] + self.paid[i])
equityAcquired = np.array([self.loan] * \
len(self.outstanding))
equityAcquired = equityAcquired - \
np.array(self.outstanding)
net = np.array(totPd) - equityAcquired
pylab.plot(net, style, label = self.legend)
class Fixed(Mortgage):
def __init__(self, loan, r, months):
Mortgage.__init__(self, loan, r, months)
self.legend = 'Fixed, ' + str(r*100) + '%'
class FixedWithPts(Mortgage):
def __init__(self, loan, r, months, pts):
Mortgage.__init__(self, loan, r, months)
self.pts = pts
self.paid = [loan*(pts/100.0)]
self.legend = 'Fixed, ' + str(r*100) + '%, '\
+ str(pts) + ' points'
class TwoRate(Mortgage):
def __init__(self, loan, r, months, teaserRate, teaserMonths):
Mortgage.__init__(self, loan, teaserRate, months)
self.teaserMonths = teaserMonths
self.teaserRate = teaserRate
self.nextRate = r/12.0
self.legend = (str(teaserRate*100)
+ '% for ' + str(self.teaserMonths)
+ ' months, then ' + str(r*100) + '%')
def makePayment(self):
if len(self.paid) == self.teaserMonths + 1:
self.rate = self.nextRate
self.payment = findPayment(self.outstanding[-1],
self.rate,
self.months - self.teaserMonths)
Mortgage.makePayment(self)
def plotMortgages(morts, amt):
def labelPlot(figure, title, xLabel, yLabel):
pylab.figure(figure)
pylab.title(title)
pylab.xlabel(xLabel)
pylab.ylabel(yLabel)
pylab.legend(loc = 'best')
styles = ['k-', 'k-.', 'k:']
#Give names to figure numbers
payments, cost, balance, netCost = 0, 1, 2, 3
for i in range(len(morts)):
pylab.figure(payments)
morts[i].plotPayments(styles[i])
pylab.figure(cost)
morts[i].plotTotPd(styles[i])
pylab.figure(balance)
morts[i].plotBalance(styles[i])
pylab.figure(netCost)
morts[i].plotNet(styles[i])
labelPlot(payments, 'Monthly Payments of $' + str(amt) +
' Mortgages', 'Months', 'Monthly Payments')
pylab.show(payments)
labelPlot(cost, 'Cash Outlay of $' + str(amt) +
' Mortgages', 'Months', 'Total Payments')
pylab.show(cost)
labelPlot(balance, 'Balance Remaining of $' + str(amt) +
' Mortgages', 'Months', 'Remaining Loan Balance of $')
pylab.show(balance)
labelPlot(netCost, 'Net Cost of $' + str(amt) + ' Mortgages',
'Months', 'Payments - Equity $')
pylab.show(balance)
def compareMortgages(amt, years, fixedRate, pts, ptsRate,
varRate1, varRate2, varMonths):
totMonths = years*12
fixed1 = Fixed(amt, fixedRate, totMonths)
fixed2 = FixedWithPts(amt, ptsRate, totMonths, pts)
twoRate = TwoRate(amt, varRate2, totMonths, varRate1, varMonths)
morts = [fixed1, fixed2, twoRate]
for m in range(totMonths):
for mort in morts:
mort.makePayment()
plotMortgages(morts, amt)
compareMortgages(amt=200000, years=30, fixedRate=0.07,
pts = 3.25, ptsRate=0.05,
varRate1=0.045, varRate2=0.095, varMonths=48)
LS0tCnRpdGxlOiAxMSBQTE9UVElORyBBTkQgTU9SRSBBQk9VVCBDTEFTU0VTCnN1YnRpdGxlOiAKYXV0aG9yOiBKb2huIFF1CmRhdGU6IDIwMjAtMDYtMTIKc2x1ZzogcGxvdHRpbmctYW5kLW1vcmUtYWJvdXQtY2xhc3Nlcwp0YWdzOgotIApjYXRlZ29yaWVzOgotIE1JVDYwMAotIElDUFAgYm9va25vdGVzCnR5cG9yYS1yb290LXVybDogLi4vLi4vc3RhdGljCnNob3dfdG9jOiB5ZXMKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIyMgSG93IHRvIHVzZSBgbWF0cGxvdGxpYi5weXBsb3RgIGluIGAqLlJtZGAgZmlsZT8KCkluaXRpYXRlIGEgdmlydHVhbCBlbnZpcm9ubWVudC4KCmBgYGNvbW1hbmRsaW5lCnBpcCBpbnN0YWxsIHZpcnR1YWxlbnYKYGBgCgoKCkhhdmUgYSB0ZXN0LgoKYGBge3B5dGhvbn0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHB5bGFiCmltcG9ydCBudW1weSBhcyBucApweWxhYi5maWd1cmUoMSkgI2NyZWF0ZSBmaWd1cmUgMSAKcHlsYWIucGxvdChbMSwyLDMsNF0sIFsxLDcsMyw1XSkgI2RyYXcgb24gZmlndXJlIDEgCnB5bGFiLnNob3coKSAjc2hvdyBmaWd1cmUgb24gc2NyZWVuCmBgYAoKIyMjIFdoYXQgZG9lcyBgcmNgIG1lYW4gaW4gYHJjUGFyYW1zYD8KCnJ1bnRpbWUgY29uZmlndXJhdGlvbi4KCgoKUHlMYWIg57uY5Zu+6IO95Yqb55qE5a6M5pW055So5oi35oyH5Y2X5Y+C6KeBCmh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvdXNlcnMvaW5kZXguaHRtbAoK5a6M5pW055qE6aKc6Imy5ZKM57q/5Z6L5qCH6K+G56ym5YiX6KGo77yM5Y+C6KeBCmh0dHA6Ly9tYXRwbG90bGliLm9yZy9hcGkvcHlwbG90X2FwaS5odG1sI21hdHBsb3RsaWIucHlwbG90LnBsb3QKCnJjUGFyYW1zIOS4reacieW+iOWkmuiuvue9rumhueebru+8jOWujOaVtOeahOWIl+ihqOWPguingQpodHRwOi8vbWF0cGxvdGxpYi5vcmcvdXNlcnMvY3VzdG9taXppbmcuaHRtbAoK5aaC5p6c5L2g5LiN5oOz6Iqx6LS557K+5Yqb5a+56L+Z5Lqb5Y+C5pWw6L+b6KGM5Y2V54us6K6+572u77yM5Y+v5Lul5L2/55So5LiA5Liq6aKE5a6a5LmJ55qE5qC35byP6KGo77yM5YW35L2T5LuL57uN5Y+C6KeBCmh0dHA6Ly9tYXRwbG90bGliLm9yZy91c2Vycy9zdHlsZV9zaGVldHMuaHRtbCNzdHlsZS1zaGVldHMKCmBgYHtweXRob259CmRlZiBmaW5kUGF5bWVudCAobG9hbiwgciwgbSk6CiAgICAiIiIgQXNzdW1lczogJ2xvYW4nIGFuZCAncicgYXJlIGZsb2F0cywgJ20nIGFuIGludAogICAgICAgIFJldHVybnMgdGhlIG1vbnRobHkgcGF5bWVudCBmb3IgYSBtb3J0Z2FnZSBvZiBzaXplCiAgICAgICAgJ2xvYW4nIGF0IGEgbW9udGhseSByYXRlIG9mICdyJyBmb3IgJ20nIG1vbnRocyAiIiIKICAgIHJldHVybiBsb2FuICogKChyICogKDEgKyByKSAqKiBtKSAvICgoMSArIHIpICoqIG0gLSAxKSkKCmNsYXNzIE1vcnRnYWdlIChvYmplY3QpOgogICAgIiIiIEFic3RyYWN0IGNsYXNzIGZvciBidWlsZGluZyBkaWZmZXJlbnQga2luZHMgb2YgbW9ydGdhZ2VzICIiIgoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBsb2FuLCBhbm5SYXRlLCBtb250aHMpOgogICAgICAgICIiIiBBc3N1bWVzOiBsb2FuIGFuZCBhbm5SYXRlIGFyZSBmbG9hdHMsIG1vbnRocyBhbiBpbnQgCiAgICAgICAgICAgIENyZWF0ZXMgYSBuZXcgbW9ydGdhZ2Ugb2Ygc2l6ZSBsb2FuLCBkdXJhdGlvbiBtb250aHMsIGFuZCBhbm51YWwgcmF0ZSBhbm5SYXRlICIiIgogICAgICAgICMgSGVyZSBhcmUgdGhyZWUgaW5zdGFuY2UgaW5pdGlhbCB2YWx1ZXMKICAgICAgICBzZWxmLmxvYW4gPSBsb2FuCiAgICAgICAgc2VsZi5yYXRlID0gYW5uUmF0ZSAvIDEyICMgSXMgaXQgYW4gY29tbW9uIGFncmVlbWVudCB0aGF0IGFubnVhbCByYXRlIGlzIDEyIHRpbWVzIG9mIG1vbnRobHkgcmF0ZT8KICAgICAgICBzZWxmLm1vbnRocyA9IG1vbnRocwogICAgICAgICMgSGVyZSBhcmUgdHdvIGxpc3RzIGV4dGVuZGVkIG1vbnRobHkKICAgICAgICBzZWxmLnBhaWQgPSBbMC4wXSAjIEF0IHRoZSBmaXJzdCBkYXkgb2YgbG9hbiB5b3UgZG9uJ3QgbmVlZCB0byBwYXkgbG9hbi4gTm90ZSB0aGlzIGxpc3QgaXMgZXh0ZW5kZWQgbW9udGhseQogICAgICAgIHNlbGYub3V0c3RhbmRpbmcgPSBbbG9hbl0gIyB0aGUgbSB0aCBtb250aCDmnKzph5Hpop0gcmVtYWluZWQgdG8gYmUgcGFpZAogICAgICAgICMgSGVyZSBpcyB0aGUgbW9udGhseSBwYXltZW50IGZvciBhIG1vcnRnYWdlIG9mIHNpemUgJ2xvYW4nIGF0IG1vbnRobHkgcmF0ZSBvZiAnc2VsZi5yYXRlJyBmb3IgJ21vbnRocycgbW9udGhzCiAgICAgICAgc2VsZi5wYXltZW50ID0gZmluZFBheW1lbnQgKGxvYW4sIHNlbGYucmF0ZSwgbW9udGhzKSAKICAgICAgICBzZWxmLmxlZ2VuZCA9IE5vbmUgICMgZGVzY3JpcHRpb24gb2YgY2VydGFpbiBraW5kIG9mIG1vcnRnYWdlCgogICAgZGVmIG1ha2VQYXltZW50IChzZWxmKToKICAgICAgICAiIiIgTWFrZSBhIG1vbnRobHkgcGF5bWVudCIiIgogICAgICAgICMgQ2FsY3VsYXRlIG1vbnRobHkgcGF5bWVudHMgdGhlbiBhcHBlbmQgdGhlbSB0byB0d28gbGlzdHM6CiAgICAgICAgIyBtb250aGx5IHBheW1lbnRzIGFuZCBtb250aGx5IG91dHN0YW5kaW5nIGxvYW4gYmFsYW5jZQogICAgICAgIHNlbGYucGFpZC5hcHBlbmQgKHNlbGYucGF5bWVudCkgCiAgICAgICAgIyByZWR1Y3Rpb24gdXNlZCB0byByZWR1Y2UgdGhlIGxvYW4gYmFsYW5jZSA9IG1vbnRobHkgcGF5bWVudCAtIHRoZSBhbW91bnQgb2YgaW50ZXJlc3QgZHVlIG9uIHRoZSBvdXRzdGFuZGluZyBsb2FuIGJhbGFuY2UKICAgICAgICByZWR1Y3Rpb24gPSBzZWxmLnBheW1lbnQgLSBzZWxmLm91dHN0YW5kaW5nIFstMV0gKiBzZWxmLnJhdGUgCiAgICAgICAgc2VsZi5vdXRzdGFuZGluZy5hcHBlbmQgKHNlbGYub3V0c3RhbmRpbmcgWy0xXSAtIHJlZHVjdGlvbikgCgogICAgZGVmIGdldFRvdGFsUGFpZCAoc2VsZik6CiAgICAgICAgIiIiIFJldHVybiB0aGUgdG90YWwgYW1vdW50IHBhaWQgc28gZmFyICIiIgogICAgICAgIHJldHVybiBzdW0gKHNlbGYucGFpZCkKCiAgICBkZWYgX19zdHJfXyhzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5sZWdlbmQKICAgICAgICAKICAgIGRlZiBwbG90UGF5bWVudHMoc2VsZiwgc3R5bGUpOgogICAgICAgIHB5bGFiLnBsb3Qoc2VsZi5wYWlkWzE6XSwgc3R5bGUsIGxhYmVsID0gc2VsZi5sZWdlbmQpCiAgICAgICAgCiAgICBkZWYgcGxvdEJhbGFuY2Uoc2VsZiwgc3R5bGUpOgogICAgICAgIHB5bGFiLnBsb3Qoc2VsZi5vdXRzdGFuZGluZywgc3R5bGUsIGxhYmVsID0gc2VsZi5sZWdlbmQpCiAgICAgICAgCiAgICBkZWYgcGxvdFRvdFBkKHNlbGYsIHN0eWxlKToKICAgICAgICB0b3RQZCA9IFtzZWxmLnBhaWRbMF1dCiAgICAgICAgZm9yIGkgaW4gcmFuZ2UoMSwgbGVuKHNlbGYucGFpZCkpOgogICAgICAgICAgICB0b3RQZC5hcHBlbmQodG90UGRbLTFdICsgc2VsZi5wYWlkW2ldKSAKICAgICAgICBweWxhYi5wbG90KHRvdFBkLCBzdHlsZSwgbGFiZWwgPSBzZWxmLmxlZ2VuZCkKICAgICAgICAKICAgIGRlZiBwbG90TmV0KHNlbGYsIHN0eWxlKToKICAgICAgICB0b3RQZCA9IFtzZWxmLnBhaWRbMF1dCiAgICAgICAgZm9yIGkgaW4gcmFuZ2UoMSwgbGVuKHNlbGYucGFpZCkpOgogICAgICAgICAgICB0b3RQZC5hcHBlbmQodG90UGRbLTFdICsgc2VsZi5wYWlkW2ldKSAKICAgICAgICBlcXVpdHlBY3F1aXJlZCA9IG5wLmFycmF5KFtzZWxmLmxvYW5dICogXAogICAgICAgICAgICAgICAgICAgICAgICAgbGVuKHNlbGYub3V0c3RhbmRpbmcpKSAKICAgICAgICBlcXVpdHlBY3F1aXJlZCA9IGVxdWl0eUFjcXVpcmVkIC0gXAogICAgICAgICAgICAgICAgICAgICAgICAgbnAuYXJyYXkoc2VsZi5vdXRzdGFuZGluZykgCiAgICAgICAgbmV0ID0gbnAuYXJyYXkodG90UGQpIC0gZXF1aXR5QWNxdWlyZWQKICAgICAgICBweWxhYi5wbG90KG5ldCwgc3R5bGUsIGxhYmVsID0gc2VsZi5sZWdlbmQpCmBgYAoKCmBgYHtweXRob259CmNsYXNzIEZpeGVkKE1vcnRnYWdlKToKCiAgICBkZWYgX19pbml0X18oc2VsZiwgbG9hbiwgciwgbW9udGhzKToKICAgICAgICBNb3J0Z2FnZS5fX2luaXRfXyhzZWxmLCBsb2FuLCByLCBtb250aHMpCiAgICAgICAgc2VsZi5sZWdlbmQgPSAnRml4ZWQsICcgKyBzdHIocioxMDApICsgJyUnCiAgICAgICAgCiAgICAgICAgCmNsYXNzIEZpeGVkV2l0aFB0cyhNb3J0Z2FnZSk6CgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGxvYW4sIHIsIG1vbnRocywgcHRzKToKICAgICAgICBNb3J0Z2FnZS5fX2luaXRfXyhzZWxmLCBsb2FuLCByLCBtb250aHMpCiAgICAgICAgc2VsZi5wdHMgPSBwdHMKICAgICAgICBzZWxmLnBhaWQgPSBbbG9hbioocHRzLzEwMC4wKV0KICAgICAgICBzZWxmLmxlZ2VuZCA9ICdGaXhlZCwgJyArIHN0cihyKjEwMCkgKyAnJSwgJ1wKICAgICAgICAgICAgICAgICAgICAgICsgc3RyKHB0cykgKyAnIHBvaW50cycKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgCmNsYXNzIFR3b1JhdGUoTW9ydGdhZ2UpOgoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBsb2FuLCByLCBtb250aHMsIHRlYXNlclJhdGUsIHRlYXNlck1vbnRocyk6CiAgICAgICAgTW9ydGdhZ2UuX19pbml0X18oc2VsZiwgbG9hbiwgdGVhc2VyUmF0ZSwgbW9udGhzKQogICAgICAgIHNlbGYudGVhc2VyTW9udGhzID0gdGVhc2VyTW9udGhzIAogICAgICAgIHNlbGYudGVhc2VyUmF0ZSA9IHRlYXNlclJhdGUgCiAgICAgICAgc2VsZi5uZXh0UmF0ZSA9IHIvMTIuMCAKICAgICAgICBzZWxmLmxlZ2VuZCA9IChzdHIodGVhc2VyUmF0ZSoxMDApICAKICAgICAgICAgICAgICAgICAgICAgICsgJyUgZm9yICcgKyBzdHIoc2VsZi50ZWFzZXJNb250aHMpICAKICAgICAgICAgICAgICAgICAgICAgICsgJyBtb250aHMsIHRoZW4gJyArIHN0cihyKjEwMCkgKyAnJScpCiAgICAgICAgICAgICAgICAgICAgICAKICAgIGRlZiBtYWtlUGF5bWVudChzZWxmKToKICAgICAgICBpZiBsZW4oc2VsZi5wYWlkKSA9PSBzZWxmLnRlYXNlck1vbnRocyArIDE6CiAgICAgICAgICAgIHNlbGYucmF0ZSA9IHNlbGYubmV4dFJhdGUgCiAgICAgICAgICAgIHNlbGYucGF5bWVudCA9IGZpbmRQYXltZW50KHNlbGYub3V0c3RhbmRpbmdbLTFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5yYXRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5tb250aHMgLSBzZWxmLnRlYXNlck1vbnRocykKICAgICAgICBNb3J0Z2FnZS5tYWtlUGF5bWVudChzZWxmKQpgYGAKCgoKYGBge3B5dGhvbn0KZGVmIHBsb3RNb3J0Z2FnZXMobW9ydHMsIGFtdCk6CgogICAgZGVmIGxhYmVsUGxvdChmaWd1cmUsIHRpdGxlLCB4TGFiZWwsIHlMYWJlbCk6CiAgICAgICAgcHlsYWIuZmlndXJlKGZpZ3VyZSkgCiAgICAgICAgcHlsYWIudGl0bGUodGl0bGUpIAogICAgICAgIHB5bGFiLnhsYWJlbCh4TGFiZWwpIAogICAgICAgIHB5bGFiLnlsYWJlbCh5TGFiZWwpIAogICAgICAgIHB5bGFiLmxlZ2VuZChsb2MgPSAnYmVzdCcpCiAgICAgICAgCiAgICBzdHlsZXMgPSBbJ2stJywgJ2stLicsICdrOiddCiAgICAjR2l2ZSBuYW1lcyB0byBmaWd1cmUgbnVtYmVycwogICAgcGF5bWVudHMsIGNvc3QsIGJhbGFuY2UsIG5ldENvc3QgPSAwLCAxLCAyLCAzIAogICAgZm9yIGkgaW4gcmFuZ2UobGVuKG1vcnRzKSk6CiAgICAgICAgcHlsYWIuZmlndXJlKHBheW1lbnRzKSAKICAgICAgICBtb3J0c1tpXS5wbG90UGF5bWVudHMoc3R5bGVzW2ldKSAKICAgICAgICBweWxhYi5maWd1cmUoY29zdCkgCiAgICAgICAgbW9ydHNbaV0ucGxvdFRvdFBkKHN0eWxlc1tpXSkgCiAgICAgICAgcHlsYWIuZmlndXJlKGJhbGFuY2UpIAogICAgICAgIG1vcnRzW2ldLnBsb3RCYWxhbmNlKHN0eWxlc1tpXSkgCiAgICAgICAgcHlsYWIuZmlndXJlKG5ldENvc3QpIAogICAgICAgIG1vcnRzW2ldLnBsb3ROZXQoc3R5bGVzW2ldKQogICAgbGFiZWxQbG90KHBheW1lbnRzLCAnTW9udGhseSBQYXltZW50cyBvZiAkJyArIHN0cihhbXQpICsKICAgICAgICAgICAgICAnIE1vcnRnYWdlcycsICdNb250aHMnLCAnTW9udGhseSBQYXltZW50cycpCiAgICBweWxhYi5zaG93KHBheW1lbnRzKQogICAgbGFiZWxQbG90KGNvc3QsICdDYXNoIE91dGxheSBvZiAkJyArIHN0cihhbXQpICsKICAgICAgICAgICAgICAnIE1vcnRnYWdlcycsICdNb250aHMnLCAnVG90YWwgUGF5bWVudHMnKSAKICAgIHB5bGFiLnNob3coY29zdCkKICAgIGxhYmVsUGxvdChiYWxhbmNlLCAnQmFsYW5jZSBSZW1haW5pbmcgb2YgJCcgKyBzdHIoYW10KSArCiAgICAgICAgICAgICAgJyBNb3J0Z2FnZXMnLCAnTW9udGhzJywgJ1JlbWFpbmluZyBMb2FuIEJhbGFuY2Ugb2YgJCcpIAogICAgcHlsYWIuc2hvdyhiYWxhbmNlKQogICAgbGFiZWxQbG90KG5ldENvc3QsICdOZXQgQ29zdCBvZiAkJyArIHN0cihhbXQpICsgJyBNb3J0Z2FnZXMnLAogICAgICAgICAgICAgICdNb250aHMnLCAnUGF5bWVudHMgLSBFcXVpdHkgJCcpCiAgICBweWxhYi5zaG93KGJhbGFuY2UpICAgICAgICAgIApgYGAKCgoKCmBgYHtweXRob259CmRlZiBjb21wYXJlTW9ydGdhZ2VzKGFtdCwgeWVhcnMsIGZpeGVkUmF0ZSwgcHRzLCBwdHNSYXRlLCAKICAgICAgICAgICAgICAgICAgICAgdmFyUmF0ZTEsIHZhclJhdGUyLCB2YXJNb250aHMpOgogICAgdG90TW9udGhzID0geWVhcnMqMTIgCiAgICBmaXhlZDEgPSBGaXhlZChhbXQsIGZpeGVkUmF0ZSwgdG90TW9udGhzKSAKICAgIGZpeGVkMiA9IEZpeGVkV2l0aFB0cyhhbXQsIHB0c1JhdGUsIHRvdE1vbnRocywgcHRzKSAKICAgIHR3b1JhdGUgPSBUd29SYXRlKGFtdCwgdmFyUmF0ZTIsIHRvdE1vbnRocywgdmFyUmF0ZTEsIHZhck1vbnRocykgCiAgICBtb3J0cyA9IFtmaXhlZDEsIGZpeGVkMiwgdHdvUmF0ZV0gCiAgICBmb3IgbSBpbiByYW5nZSh0b3RNb250aHMpOiAKICAgICAgICBmb3IgbW9ydCBpbiBtb3J0czogCiAgICAgICAgICAgIG1vcnQubWFrZVBheW1lbnQoKQogICAgcGxvdE1vcnRnYWdlcyhtb3J0cywgYW10KQpgYGAKCgoKYGBge3B5dGhvbn0KY29tcGFyZU1vcnRnYWdlcyhhbXQ9MjAwMDAwLCB5ZWFycz0zMCwgZml4ZWRSYXRlPTAuMDcsIAogICAgICAgICAgICAgICAgIHB0cyA9IDMuMjUsIHB0c1JhdGU9MC4wNSwgCiAgICAgICAgICAgICAgICAgdmFyUmF0ZTE9MC4wNDUsIHZhclJhdGUyPTAuMDk1LCB2YXJNb250aHM9NDgpCmBgYA==