How to use matplotlib.pyplot in *.Rmd file?

Initiate a virtual environment.

pip install virtualenv

Have a test.

import matplotlib.pyplot as pylab
import numpy as np
pylab.figure(1) #create figure 1 
pylab.plot([1,2,3,4], [1,7,3,5]) #draw on figure 1 
pylab.show() #show figure on screen

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==