text:
import sys
import shutil
import subprocess
import os
import time
import tempfile
import argparse
import tarfile
import re
import shutil
import math
import fileinput
from os.path import abspath

progname = os.path.split(sys.argv[0])[1]
myversion = 'V001.1 March 2014'
verbose = False
debug = False
toolFactoryURL = 'https://bitbucket.org/fubar/galaxytoolfactory'

# if we do html we need these dependencies specified in a tool_dependencies.xml file and referred to in the generated
# tool xml
toolhtmldepskel = """<?xml version="1.0"?>
<tool_dependency>
    <package name="ghostscript" version="9.10">
        <repository name="package_ghostscript_9_10" owner="devteam" prior_installation_required="True" />
    </package>
    <package name="graphicsmagick" version="1.3.18">
        <repository name="package_graphicsmagick_1_3" owner="iuc" prior_installation_required="True" />
    </package>
        <readme>
           %s
       </readme>
</tool_dependency>
"""

protorequirements = """<requirements>
      <requirement type="package" version="9.10">ghostscript</requirement>
      <requirement type="package" version="1.3.18">graphicsmagick</requirement>
      <container type="docker">toolfactory/custombuild:%s</container>
</requirements>"""

def timenow():
    """return current time as a string
    """
    return time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(time.time()))

html_escape_table = {
     "&": "&",
     ">": ">",
     "<": "<",
     "$": "\$"
     }

def html_escape(text):
     """Produce entities within text."""
     return "".join(html_escape_table.get(c,c) for c in text)

def cmd_exists(cmd):
     return subprocess.call("type " + cmd, shell=True,
           stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0

def edit_dockerfile(dockerfile):
    '''we have to change the userid of galaxy inside the container to the id with which the tool is run,
       otherwise we have a mismatch in the file permissions inside the container'''
    uid=os.getuid()
    for line in fileinput.FileInput(dockerfile, inplace=1):
        sys.stdout.write(re.sub("RUN adduser galaxy.*",  "RUN adduser galaxy -u {0}\n".format(uid), line))

def build_docker(dockerfile, docker_client, image_tag='base'):
    '''Given the path to a dockerfile, and a docker_client, build the image, if it does not
    exist yet.'''
    image_id='toolfactory/custombuild:'+image_tag
    existing_images=", ".join(["".join(d['RepoTags']) for d in docker_client.images()])
    if image_id in existing_images:
        print 'docker container exists, skipping build'
        return image_id
    print "Building Docker image, using Dockerfile:{0}".format(dockerfile)
    build_process=docker_client.build(fileobj=open(dockerfile, 'r'), tag=image_id)
    print "succesfully dispatched docker build process, building now"
    build_log=[line for line in build_process] #will block until image is built.
    return image_id

def construct_bind(host_path, container_path=False, binds=None, ro=True):
    #TODO remove container_path if it's alwyas going to be the same as host_path
    '''build or extend binds dictionary with container path. binds is used
    to mount all files using the docker-py client.'''
    if not binds:
        binds={}
    if isinstance(host_path, list):
        for k,v in enumerate(host_path):
            if not container_path:
                container_path=host_path[k]
            binds[host_path[k]]={'bind':container_path, 'ro':ro}
            container_path=False #could be more elegant
        return binds
    else:
        if not container_path:
            container_path=host_path
        binds[host_path]={'bind':container_path, 'ro':ro}
        return binds

def switch_to_docker(opts):
    import docker #need local import, as container does not have docker-py
    docker_client=docker.Client()
    toolfactory_path=abspath(sys.argv[0])
    dockerfile=os.path.dirname(toolfactory_path)+'/Dockerfile'
    edit_dockerfile(dockerfile)
    image_id=build_docker(dockerfile, docker_client)
    binds=construct_bind(host_path=opts.script_path, ro=False)
    binds=construct_bind(binds=binds, host_path=abspath(opts.output_dir), ro=False)
    if len(opts.input_tab)>0:
        binds=construct_bind(binds=binds, host_path=opts.input_tab, ro=True)
    if not opts.output_tab == 'None':
        binds=construct_bind(binds=binds, host_path=opts.output_tab, ro=False)
    if opts.make_HTML:
        binds=construct_bind(binds=binds, host_path=opts.output_html, ro=False)
    if opts.make_Tool:
        binds=construct_bind(binds=binds, host_path=opts.new_tool, ro=False)
        binds=construct_bind(binds=binds, host_path=opts.help_text, ro=True)
    binds=construct_bind(binds=binds, host_path=toolfactory_path)
    volumes=binds.keys()
    sys.argv=[abspath(opts.output_dir) if sys.argv[i-1]=='--output_dir' else arg for i,arg in enumerate(sys.argv)] ##inject absolute path of working_dir
    cmd=['python', '-u']+sys.argv+['--dockerized', '1']
    container=docker_client.create_container(
        image=image_id,
        user='galaxy',
        volumes=volumes,
        command=cmd
        )
    docker_client.start(container=container[u'Id'], binds=binds)
    docker_client.wait(container=container[u'Id'])
    logs=docker_client.logs(container=container[u'Id'])
    print "".join([log for log in logs])

class ScriptRunner:
    """class is a wrapper for an arbitrary script
    """

    def __init__(self,opts=None,treatbashSpecial=True, image_tag='base'):
        """
        cleanup inputs, setup some outputs

        """
        self.opts = opts
        self.useGM = cmd_exists('gm')
        self.useIM = cmd_exists('convert')
        self.useGS = cmd_exists('gs')
        self.temp_warned = False # we want only one warning if $TMP not set
        self.treatbashSpecial = treatbashSpecial
        self.image_tag = image_tag
        os.chdir(abspath(opts.output_dir))
        self.thumbformat = 'png'
        self.toolname_sanitized = re.sub('[^a-zA-Z0-9_]+', '_', opts.tool_name) # a sanitizer now does this but..
        self.toolname = opts.tool_name
        self.toolid = self.toolname
        self.myname = sys.argv[0] # get our name because we write ourselves out as a tool later
        self.pyfile = self.myname # crude but efficient - the cruft won't hurt much
        self.xmlfile = '%s.xml' % self.toolname_sanitized
        s = open(self.opts.script_path,'r').readlines()
        s = [x.rstrip() for x in s] # remove pesky dos line endings if needed
        self.script = '\n'.join(s)
        fhandle,self.sfile = tempfile.mkstemp(prefix=self.toolname_sanitized,suffix=".%s" % (opts.interpreter))
        tscript = open(self.sfile,'w') # use self.sfile as script source for Popen
        tscript.write(self.script)
        tscript.close()
        self.indentedScript = '\n'.join([' %s' % html_escape(x) for x in s]) # for restructured text in help
        self.escapedScript = '\n'.join([html_escape(x) for x in s])
        self.elog = os.path.join(self.opts.output_dir,"%s_error.log" % self.toolname_sanitized)
        if opts.output_dir: # may not want these complexities
            self.tlog = os.path.join(self.opts.output_dir,"%s_runner.log" % self.toolname_sanitized)
            art = '%s.%s' % (self.toolname_sanitized,opts.interpreter)
            artpath = os.path.join(self.opts.output_dir,art) # need full path
            artifact = open(artpath,'w') # use self.sfile as script source for Popen
            artifact.write(self.script)
            artifact.close()
        self.cl = []
        self.html = []
        a = self.cl.append
        a(opts.interpreter)
        if self.treatbashSpecial and opts.interpreter in ['bash','sh']:
            a(self.sfile)
        else:
            a('-') # stdin
	for input in opts.input_tab:
	  a(input)
        if opts.output_tab == 'None': #If tool generates only HTML, set output name to toolname
            a(str(self.toolname_sanitized)+'.out')
        a(opts.output_tab)
	for param in opts.additional_parameters:
          param, value=param.split(',')
          a('--'+param)
          a(value)
        #print self.cl
        self.outFormats = opts.output_format
        self.inputFormats = [formats for formats in opts.input_formats]
        self.test1Input = '%s_test1_input.xls' % self.toolname_sanitized
        self.test1Output = '%s_test1_output.xls' % self.toolname_sanitized
        self.test1HTML = '%s_test1_output.html' % self.toolname_sanitized

    def makeXML(self):
        """
        Create a Galaxy xml tool wrapper for the new script as a string to write out
        fixme - use templating or something less fugly than this example of what we produce

        <tool id="reverse" name="reverse" version="0.01">
            <description>a tabular file</description>
            <command interpreter="python">
            reverse.py --script_path "$runMe" --interpreter "python"
            --tool_name "reverse" --input_tab "$input1" --output_tab "$tab_file"
            </command>
            <inputs>
            <param name="input1"  type="data" format="tabular" label="Select a suitable input file from your history"/>

            </inputs>
            <outputs>
            <data format=opts.output_format name="tab_file"/>

            </outputs>
            <help>

**What it Does**

Reverse the columns in a tabular file

            </help>
            <configfiles>
            <configfile name="runMe">

# reverse order of columns in a tabular file
import sys
inp = sys.argv[1]
outp = sys.argv[2]
i = open(inp,'r')
o = open(outp,'w')
for row in i:
     rs = row.rstrip().split('\t')
     rs.reverse()
     o.write('\t'.join(rs))
     o.write('\n')
i.close()
o.close()

            </configfile>
            </configfiles>
            </tool>

        """
        newXML="""<tool id="%(toolid)s" name="%(toolname)s" version="%(tool_version)s">
%(tooldesc)s
%(requirements)s
<command interpreter="python">
%(command)s
</command>
<inputs>
%(inputs)s
</inputs>
<outputs>
%(outputs)s
</outputs>
<configfiles>
<configfile name="runMe">
%(script)s
</configfile>
</configfiles>

%(tooltests)s

<help>

%(help)s

</help>
</tool>""" # needs a dict with toolname, toolname_sanitized, toolid, interpreter, scriptname, command, inputs as a multi line string ready to write, outputs ditto, help ditto

        newCommand="""
        %(toolname_sanitized)s.py --script_path "$runMe" --interpreter "%(interpreter)s"
            --tool_name "%(toolname)s" %(command_inputs)s %(command_outputs)s """
        # may NOT be an input or htmlout - appended later
        tooltestsTabOnly = """
        <tests>
        <test>
        <param name="input1" value="%(test1Input)s" ftype="tabular"/>
        <param name="runMe" value="$runMe"/>
        <output name="tab_file" file="%(test1Output)s" ftype="tabular"/>
        </test>
        </tests>
        """
        tooltestsHTMLOnly = """
        <tests>
        <test>
        <param name="input1" value="%(test1Input)s" ftype="tabular"/>
        <param name="runMe" value="$runMe"/>
        <output name="html_file" file="%(test1HTML)s" ftype="html" lines_diff="5"/>
        </test>
        </tests>
        """
        tooltestsBoth = """<tests>
        <test>
        <param name="input1" value="%(test1Input)s" ftype="tabular"/>
        <param name="runMe" value="$runMe"/>
        <output name="tab_file" file="%(test1Output)s" ftype="tabular" />
        <output name="html_file" file="%(test1HTML)s" ftype="html" lines_diff="10"/>
        </test>
        </tests>
        """
        xdict = {}
        #xdict['requirements'] = ''
        #if self.opts.make_HTML:
        xdict['requirements'] = protorequirements % self.image_tag
        xdict['tool_version'] = self.opts.tool_version
        xdict['test1Input'] = self.test1Input
        xdict['test1HTML'] = self.test1HTML
        xdict['test1Output'] = self.test1Output
        if self.opts.make_HTML and self.opts.output_tab <> 'None':
            xdict['tooltests'] = tooltestsBoth % xdict
        elif self.opts.make_HTML:
            xdict['tooltests'] = tooltestsHTMLOnly % xdict
        else:
            xdict['tooltests'] = tooltestsTabOnly % xdict
        xdict['script'] = self.escapedScript
        # configfile is least painful way to embed script to avoid external dependencies
        # but requires escaping of <, > and $ to avoid Mako parsing
        if self.opts.help_text:
            helptext = open(self.opts.help_text,'r').readlines()
            helptext = [html_escape(x) for x in helptext] # must html escape here too - thanks to Marius van den Beek
            xdict['help'] = ''.join([x for x in helptext])
        else:
            xdict['help'] = 'Please ask the tool author (%s) for help as none was supplied at tool generation\n' % (self.opts.user_email)
        coda = ['**Script**','Pressing execute will run the following code over your input file and generate some outputs in your history::']
        coda.append('\n')
        coda.append(self.indentedScript)
        coda.append('\n**Attribution**\nThis Galaxy tool was created by %s at %s\nusing the Galaxy Tool Factory.\n' % (self.opts.user_email,timenow()))
        coda.append('See %s for details of that project' % (toolFactoryURL))
        coda.append('Please cite: Creating re-usable tools from scripts: The Galaxy Tool Factory. Ross Lazarus; Antony Kaspi; Mark Ziemann; The Galaxy Team. ')
        coda.append('Bioinformatics 2012; doi: 10.1093/bioinformatics/bts573\n')
        xdict['help'] = '%s\n%s' % (xdict['help'],'\n'.join(coda))
        if self.opts.tool_desc:
            xdict['tooldesc'] = '<description>%s</description>' % self.opts.tool_desc
        else:
            xdict['tooldesc'] = ''
        xdict['command_outputs'] = ''
        xdict['outputs'] = ''
        if self.opts.input_tab <> 'None':
            xdict['command_inputs'] = '--input_tab'
            xdict['inputs']=''
            for i,input in enumerate(self.inputFormats):
                xdict['inputs' ]+='<param name="input{0}"  type="data" format="{1}" label="Select a suitable input file from your history"/> \n'.format(i+1, input)
                xdict['command_inputs'] += ' $input{0}'.format(i+1)
        else:
            xdict['command_inputs'] = '' # assume no input - eg a random data generator
            xdict['inputs'] = ''
        # I find setting the job name not very logical. can be changed in workflows anyway. xdict['inputs'] += '<param name="job_name" type="text" label="Supply a name for the outputs to remind you what they contain" value="%s"/> \n' % self.toolname
        xdict['toolname'] = self.toolname
        xdict['toolname_sanitized'] = self.toolname_sanitized
        xdict['toolid'] = self.toolid
        xdict['interpreter'] = self.opts.interpreter
        xdict['scriptname'] = self.sfile
        if self.opts.make_HTML:
            xdict['command_outputs'] += ' --output_dir "$html_file.files_path" --output_html "$html_file" --make_HTML "yes"'
            xdict['outputs'] +=  ' <data format="html" name="html_file"/>\n'
        else:
            xdict['command_outputs'] += ' --output_dir "./"'
        #print self.opts.output_tab
        if self.opts.output_tab!="None":
            xdict['command_outputs'] += ' --output_tab "$tab_file"'
            xdict['outputs'] += ' <data format="%s" name="tab_file"/>\n' % self.outFormats
        xdict['command'] = newCommand % xdict
        #print xdict['outputs']
        xmls = newXML % xdict
        xf = open(self.xmlfile,'w')
        xf.write(xmls)
        xf.write('\n')
        xf.close()
        # ready for the tarball

    def makeTooltar(self):
        """
        a tool is a gz tarball with eg
        /toolname_sanitized/tool.xml /toolname_sanitized/tool.py /toolname_sanitized/test-data/test1_in.foo ...
        """
        retval = self.run()
        if retval:
            print >> sys.stderr,'## Run failed. Cannot build yet. Please fix and retry'
            sys.exit(1)
        tdir = self.toolname_sanitized
        os.mkdir(tdir)
        self.makeXML()
        if self.opts.make_HTML:
            if self.opts.help_text:
                hlp = open(self.opts.help_text,'r').read()
            else:
                hlp = 'Please ask the tool author for help as none was supplied at tool generation\n'
            if self.opts.include_dependencies:
                tooldepcontent = toolhtmldepskel  % hlp
                depf = open(os.path.join(tdir,'tool_dependencies.xml'),'w')
                depf.write(tooldepcontent)
                depf.write('\n')
                depf.close()
        if self.opts.input_tab <> 'None': # no reproducible test otherwise? TODO: maybe..
            testdir = os.path.join(tdir,'test-data')
            os.mkdir(testdir) # make tests directory
	    for i in self.opts.input_tab:
		  #print i
	          shutil.copyfile(i,os.path.join(testdir,self.test1Input))
            if not self.opts.output_tab:
                shutil.copyfile(self.opts.output_tab,os.path.join(testdir,self.test1Output))
            if self.opts.make_HTML:
                shutil.copyfile(self.opts.output_html,os.path.join(testdir,self.test1HTML))
            if self.opts.output_dir:
                shutil.copyfile(self.tlog,os.path.join(testdir,'test1_out.log'))
        outpif = '%s.py' % self.toolname_sanitized # new name
        outpiname = os.path.join(tdir,outpif) # path for the tool tarball
        pyin = os.path.basename(self.pyfile) # our name - we rewrite ourselves (TM)
        notes = ['# %s - a self annotated version of %s generated by running %s\n' % (outpiname,pyin,pyin),]
        notes.append('# to make a new Galaxy tool called %s\n' % self.toolname)
        notes.append('# User %s at %s\n' % (self.opts.user_email,timenow()))
        pi=[line.replace('if False:', 'if False:') for line in open(self.pyfile)] #do not run docker in the generated tool
        notes += pi
        outpi = open(outpiname,'w')
        outpi.write(''.join(notes))
        outpi.write('\n')
        outpi.close()
        stname = os.path.join(tdir,self.sfile)
        if not os.path.exists(stname):
            shutil.copyfile(self.sfile, stname)
        xtname = os.path.join(tdir,self.xmlfile)
        if not os.path.exists(xtname):
            shutil.copyfile(self.xmlfile,xtname)
        tarpath = "%s.gz" % self.toolname_sanitized
        tar = tarfile.open(tarpath, "w:gz")
        tar.add(tdir,arcname=self.toolname_sanitized)
        tar.close()
        shutil.copyfile(tarpath,self.opts.new_tool)
        shutil.rmtree(tdir)
        ## TODO: replace with optional direct upload to local toolshed?
        return retval

    def compressPDF(self,inpdf=None,thumbformat='png'):
        """need absolute path to pdf
           note that GS gets confoozled if no $TMP or $TEMP
           so we set it
        """
        assert os.path.isfile(inpdf), "## Input %s supplied to %s compressPDF not found" % (inpdf,self.myName)
        hlog = os.path.join(self.opts.output_dir,"compress_%s.txt" % os.path.basename(inpdf))
        sto = open(hlog,'a')
        our_env = os.environ.copy()
        our_tmp = our_env.get('TMP',None)
        if not our_tmp:
            our_tmp = our_env.get('TEMP',None)
        if not (our_tmp and os.path.exists(our_tmp)):
            newtmp = os.path.join(self.opts.output_dir,'tmp')
            try:
                os.mkdir(newtmp)
            except:
                sto.write('## WARNING - cannot make %s - it may exist or permissions need fixing\n' % newtmp)
            our_env['TEMP'] = newtmp
            if not self.temp_warned:
               sto.write('## WARNING - no $TMP or $TEMP!!! Please fix - using %s temporarily\n' % newtmp)
               self.temp_warned = True
        outpdf = '%s_compressed' % inpdf
        cl = ["gs", "-sDEVICE=pdfwrite", "-dNOPAUSE", "-dUseCIEColor", "-dBATCH","-dPDFSETTINGS=/printer", "-sOutputFile=%s" % outpdf,inpdf]
        x = subprocess.Popen(cl,stdout=sto,stderr=sto,cwd=self.opts.output_dir,env=our_env)
        retval1 = x.wait()
        sto.close()
        if retval1 == 0:
            os.unlink(inpdf)
            shutil.move(outpdf,inpdf)
            os.unlink(hlog)
        hlog = os.path.join(self.opts.output_dir,"thumbnail_%s.txt" % os.path.basename(inpdf))
        sto = open(hlog,'w')
        outpng = '%s.%s' % (os.path.splitext(inpdf)[0],thumbformat)
        if self.useGM:
            cl2 = ['gm', 'convert', inpdf, outpng]
        else: # assume imagemagick
            cl2 = ['convert', inpdf, outpng]
        x = subprocess.Popen(cl2,stdout=sto,stderr=sto,cwd=self.opts.output_dir,env=our_env)
        retval2 = x.wait()
        sto.close()
        if retval2 == 0:
             os.unlink(hlog)
        retval = retval1 or retval2
        return retval

    def getfSize(self,fpath,outpath):
        """
        format a nice file size string
        """
        size = ''
        fp = os.path.join(outpath,fpath)
        if os.path.isfile(fp):
            size = '0 B'
            n = float(os.path.getsize(fp))
            if n > 2**20:
                size = '%1.1f MB' % (n/2**20)
            elif n > 2**10:
                size = '%1.1f KB' % (n/2**10)
            elif n > 0:
                size = '%d B' % (int(n))
        return size

    def makeHtml(self):
        """ Create an HTML file content to list all the artifacts found in the output_dir
        """

        galhtmlprefix = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
        <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" />
        <title></title>
        <link rel="stylesheet" href="/static/style/base.css" type="text/css" />
        </head>
        <body>
        <div class="toolFormBody">
        """
        galhtmlattr = """<hr/><div class="infomessage">This tool (%s) was generated by the <a href="https://bitbucket.org/fubar/galaxytoolfactory/overview">Galaxy Tool Factory</a></div><br/>"""
        galhtmlpostfix = """</div></body></html>\n"""

        flist = os.listdir(self.opts.output_dir)
        flist = [x for x in flist if x <> 'Rplots.pdf']
        flist.sort()
        html = []
        html.append(galhtmlprefix % progname)
        html.append('<div class="infomessage">Galaxy Tool "%s" run at %s</div><br/>' % (self.toolname,timenow()))
        fhtml = []
        if len(flist) > 0:
            logfiles = [x for x in flist if x.lower().endswith('.log')] # log file names determine sections
            logfiles.sort()
            logfiles = [x for x in logfiles if abspath(x) <> abspath(self.tlog)]
            logfiles.append(abspath(self.tlog)) # make it the last one
            pdflist = []
            npdf = len([x for x in flist if os.path.splitext(x)[-1].lower() == '.pdf'])
            for rownum,fname in enumerate(flist):
                dname,e = os.path.splitext(fname)
                sfsize = self.getfSize(fname,self.opts.output_dir)
                if e.lower() == '.pdf' : # compress and make a thumbnail
                    thumb = '%s.%s' % (dname,self.thumbformat)
                    pdff = os.path.join(self.opts.output_dir,fname)
                    retval = self.compressPDF(inpdf=pdff,thumbformat=self.thumbformat)
                    if retval == 0:
                        pdflist.append((fname,thumb))
                    else:
                        pdflist.append((fname,fname))
                if (rownum+1) % 2 == 0:
                    fhtml.append('<tr class="odd_row"><td><a href="%s">%s</a></td><td>%s</td></tr>' % (fname,fname,sfsize))
                else:
                    fhtml.append('<tr><td><a href="%s">%s</a></td><td>%s</td></tr>' % (fname,fname,sfsize))
            for logfname in logfiles: # expect at least tlog - if more
                if abspath(logfname) == abspath(self.tlog): # handled later
                    sectionname = 'All tool run'
                    if (len(logfiles) > 1):
                        sectionname = 'Other'
                    ourpdfs = pdflist
                else:
                    realname = os.path.basename(logfname)
                    sectionname = os.path.splitext(realname)[0].split('_')[0] # break in case _ added to log
                    ourpdfs = [x for x in pdflist if os.path.basename(x[0]).split('_')[0] == sectionname]
                    pdflist = [x for x in pdflist if os.path.basename(x[0]).split('_')[0] <> sectionname] # remove
                nacross = 1
                npdf = len(ourpdfs)

                if npdf > 0:
                    nacross = math.sqrt(npdf) ## int(round(math.log(npdf,2)))
                    if int(nacross)**2 != npdf:
                        nacross += 1
                    nacross = int(nacross)
                    width = min(400,int(1200/nacross))
                    html.append('<div class="toolFormTitle">%s images and outputs</div>' % sectionname)
                    html.append('(Click on a thumbnail image to download the corresponding original PDF image)<br/>')
                    ntogo = nacross # counter for table row padding with empty cells
                    html.append('<div><table class="simple" cellpadding="2" cellspacing="2">\n<tr>')
                    for i,paths in enumerate(ourpdfs):
                        fname,thumb = paths
                        s= """<td><a href="%s"><img src="%s" title="Click to download a PDF of %s" hspace="5" width="%d"
                           alt="Image called %s"/></a></td>\n""" % (fname,thumb,fname,width,fname)
                        if ((i+1) % nacross == 0):
                            s += '</tr>\n'
                            ntogo = 0
                            if i < (npdf - 1): # more to come
                               s += '<tr>'
                               ntogo = nacross
                        else:
                            ntogo -= 1
                        html.append(s)
                    if html[-1].strip().endswith('</tr>'):
                        html.append('</table></div>\n')
                    else:
                        if ntogo > 0: # pad
                           html.append('<td> </td>'*ntogo)
                        html.append('</tr></table></div>\n')
                logt = open(logfname,'r').readlines()
                logtext = [x for x in logt if x.strip() > '']
                html.append('<div class="toolFormTitle">%s log output</div>' % sectionname)
                if len(logtext) > 1:
                    html.append('\n<pre>\n')
                    html += logtext
                    html.append('\n</pre>\n')
                else:
                    html.append('%s is empty<br/>' % logfname)
        if len(fhtml) > 0:
           fhtml.insert(0,'<div><table class="colored" cellpadding="3" cellspacing="3"><tr><th>Output File Name (click to view)</th><th>Size</th></tr>\n')
           fhtml.append('</table></div><br/>')
           html.append('<div class="toolFormTitle">All output files available for downloading</div>\n')
           html += fhtml # add all non-pdf files to the end of the display
        else:
            html.append('<div class="warningmessagelarge">### Error - %s returned no files - please confirm that parameters are sane</div>' % self.opts.interpreter)
        html.append(galhtmlpostfix)
        htmlf = file(self.opts.output_html,'w')
        htmlf.write('\n'.join(html))
        htmlf.write('\n')
        htmlf.close()
        self.html = html

    def run(self):
        """
        scripts must be small enough not to fill the pipe!
        """
        if self.treatbashSpecial and self.opts.interpreter in ['bash','sh']:
          retval = self.runBash()
        else:
            if self.opts.output_dir:
                ste = open(self.elog,'w')
                sto = open(self.tlog,'w')
                sto.write('## Toolfactory generated command line = %s\n' % ' '.join(self.cl))
                sto.flush()
                p = subprocess.Popen(self.cl,shell=False,stdout=sto,stderr=ste,stdin=subprocess.PIPE,cwd=self.opts.output_dir)
            else:
                p = subprocess.Popen(self.cl,shell=False,stdin=subprocess.PIPE)
            p.stdin.write(self.script)
            p.stdin.close()
            retval = p.wait()
            if self.opts.output_dir:
                sto.close()
                ste.close()
                err = open(self.elog,'r').readlines()
                if retval <> 0 and err: # problem
                    print >> sys.stderr,err #same problem, need to capture docker stdin/stdout
            if self.opts.make_HTML:
                self.makeHtml()
        return retval

    def runBash(self):
        """
        cannot use - for bash so use self.sfile
        """
        if self.opts.output_dir:
            s = '## Toolfactory generated command line = %s\n' % ' '.join(self.cl)
            sto = open(self.tlog,'w')
            sto.write(s)
            sto.flush()
            p = subprocess.Popen(self.cl,shell=False,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
        else:
            p = subprocess.Popen(self.cl,shell=False)
        retval = p.wait()
        if self.opts.output_dir:
            sto.close()
        if self.opts.make_HTML:
            self.makeHtml()
        return retval

def main():
    u = """
    This is a Galaxy wrapper. It expects to be called by a special purpose tool.xml as:
    <command interpreter="python">rgBaseScriptWrapper.py --script_path "$scriptPath" --tool_name "foo" --interpreter "Rscript"
    </command>
    """
    op = argparse.ArgumentParser()
    a = op.add_argument
    a('--script_path',default=None)
    a('--tool_name',default=None)
    a('--interpreter',default=None)
    a('--output_dir',default='./')
    a('--output_html',default=None)
    a('--input_tab',default='None', nargs='*')
    a('--output_tab',default='None')
    a('--user_email',default='Unknown')
    a('--bad_user',default=None)
    a('--make_Tool',default=None)
    a('--make_HTML',default=None)
    a('--help_text',default=None)
    a('--tool_desc',default=None)
    a('--new_tool',default=None)
    a('--tool_version',default=None)
    a('--include_dependencies',default=None)
    a('--dockerized',default=0)
    a('--output_format', default='tabular')
    a('--input_format', dest='input_formats', action='append', default=[])
    a('--additional_parameters', dest='additional_parameters', action='append', default=[])
    opts = op.parse_args()
    assert not opts.bad_user,'UNAUTHORISED: %s is NOT authorized to use this tool until Galaxy admin adds %s to admin_users in universe_wsgi.ini' % (opts.bad_user,opts.bad_user)
    assert opts.tool_name,'## Tool Factory expects a tool name - eg --tool_name=DESeq'
    assert opts.interpreter,'## Tool Factory wrapper expects an interpreter - eg --interpreter=Rscript'
    assert os.path.isfile(opts.script_path),'## Tool Factory wrapper expects a script path - eg --script_path=foo.R'
    if opts.output_dir:
        try:
            os.makedirs(opts.output_dir)
        except:
            pass
    if False:
      switch_to_docker(opts)
      return
    r = ScriptRunner(opts)
    if opts.make_Tool:
        retcode = r.makeTooltar()
    else:
        retcode = r.run()
    os.unlink(r.sfile)
    if retcode:
        sys.exit(retcode) # indicate failure to job runner

if __name__ == "__main__":
    main()


text:
angular.module('App').constant('CONFIG', {

});

redpajama_set_name: RedPajamaGithub


text:
using System;
using Shouldly;
using Xunit;

namespace TestStack.BDDfy.Tests.Exceptions.OtherExceptions
{
    public class WhenThenThrowsException : OtherExceptionBase
    {
        private void ExecuteUsingReflectingScanners()
        {
            Should.Throw<Exception>(() => Sut.Execute(ThrowingMethods.Then, false));
        }

        private void ExecuteUsingFluentScanner()
        {
            Should.Throw<Exception>(() => Sut.Execute(ThrowingMethods.Then, true));
        }

        [Fact]
        public void GivenIsReportedAsSuccessfulWhenUsingReflectingScanners()
        {
            ExecuteUsingReflectingScanners();
            Sut.AssertGivenStepResult(Result.Passed);
        }

        [Fact]
        public void WhenIsReportedAsSuccessfulWhenUsingReflectingScanners()
        {
            ExecuteUsingReflectingScanners();
            Sut.AssertWhenStepResult(Result.Passed);
        }

        [Fact]
        public void ThenIsReportedAsFailedWhenUsingReflectingScanners()
        {
            ExecuteUsingReflectingScanners();
            Sut.AssertThenStepResult(Result.Failed);
        }

        [Fact]
        public void ScenarioResultReturnsFailedWhenUsingReflectingScanners()
        {
            ExecuteUsingReflectingScanners();
            Sut.AssertScenarioResult(Result.Failed);
        }

        [Fact]
        public void StoryResultReturnsFailedWhenUsingReflectingScanners()
        {
            ExecuteUsingReflectingScanners();
            Sut.AssertStoryResult(Result.Failed);
        }

        [Fact]
        public void TearDownMethodIsExecutedWhenUsingReflectingScanners()
        {
            ExecuteUsingReflectingScanners();
            Sut.AssertTearDownMethodIsExecuted();
        }

        [Fact]
        public void GivenIsReportedAsSuccessfulWhenUsingFluentScanners()
        {
            ExecuteUsingFluentScanner();
            Sut.AssertGivenStepResult(Result.Passed);
        }

        [Fact]
        public void WhenIsReportedAsSuccessfulWhenUsingFluentScanners()
        {
            ExecuteUsingFluentScanner();
            Sut.AssertWhenStepResult(Result.Passed);
        }

        [Fact]
        public void ThenIsReportedAsFailedWhenUsingFluentScanners()
        {
            ExecuteUsingFluentScanner();
            Sut.AssertThenStepResult(Result.Failed);
        }

        [Fact]
        public void ScenarioResultReturnsFailedWhenUsingFluentScanners()
        {
            ExecuteUsingFluentScanner();
            Sut.AssertScenarioResult(Result.Failed);
        }

        [Fact]
        public void StoryResultReturnsFailedWhenUsingFluentScanners()
        {
            ExecuteUsingFluentScanner();
            Sut.AssertStoryResult(Result.Failed);
        }

        [Fact]
        public void TearDownMethodIsExecutedWhenUsingFluentScanners()
        {
            ExecuteUsingFluentScanner();
            Sut.AssertTearDownMethodIsExecuted();
        }
    }
}


text:
/*

// based on Apple's Reachability.h
// this needs to be refactor - this is piece of poo

*/

#if 0
#import "FishLampMinimum.h"

#import "FLCocoaRequired.h"
#import <SystemConfiguration/SystemConfiguration.h>

// use with NSNotificationCenter
extern NSString* const FLReachabilityDidChangeNotification;
extern NSString* const FLReachabilityNotificationObjectKey;

typedef enum {
	FLInternetReachability,
	FLHostReachability,
	FLAddressReachability,
	FLWifiReachability
} FLReachabilityType;

#define FLReachabilityDefaultBroadcastMask kSCNetworkReachabilityFlagsReachable

#define FLReachabilityErrorDomain @"FLReachabilityErrorDomain"

typedef enum {
	FLReachabilityErrorDomainCodeNoConnection		= -2000,
	FLReachabilityErrorDomainCodeHostNotReachable	= -2001
} FLReachabilityErrorDomainCode;

@protocol FLReachablityListener;

@interface FLReachability: NSObject {
@private
	SCNetworkReachabilityRef _reachabilityRef;
	NSString* _hostName;
	FLReachabilityType _type;
	SCNetworkReachabilityFlags _lastFlags;
	SCNetworkReachabilityFlags _broadcastMask;
}

- (id) initWithHostName:(NSString*) hostName;
- (id) initWithAddress:(const struct sockaddr_in*) address;
- (id) initWithInternetConnection;
- (id) initWithWiFi;

@property (readonly, assign, nonatomic) FLReachabilityType reachabilityType;

@property (readonly, retain, nonatomic) NSString* hostName;
@property (readonly, assign, nonatomic) SCNetworkReachabilityRef reachabilityRef;

@property (readonly, assign, nonatomic) BOOL isReachable;
@property (readonly, assign, nonatomic) BOOL connectionRequired;

@property (readonly, assign, nonatomic) SCNetworkReachabilityFlags currentReachabilityFlags;
@property (readonly, assign, nonatomic) SCNetworkReachabilityFlags broadcastMask; //defaults to FLReachabilityDefaultBroadcastMask

//Start listening for reachability notifications on the current run loop
- (BOOL) startNotifer;
- (void) stopNotifer;

+ (FLReachability*) defaultReachability;
+ (BOOL) isConnectedToNetwork; // checks default reachability

@end

#endif


text:
@interface DummyCPPEnable : NSObject

@end


text:
from __future__ import unicode_literals

from ...utils import try_import_or_runtime_error

try_import_or_runtime_error('django_redis',
    'Trying to use rabbitmq client without pika installed. Install pika to use '
    'this package.'
)


text:
set -e

SRC="`df | grep -m 1 ' /mnt/sd[a-z][0-9]' | awk '{ print $6 }' | sed 's/\/var.*//'`"
MYBOOT2DOCKER=$SRC/myboot2docker
echo "Removing $MYBOOT2DOCKER"
if [[ -d $MYBOOT2DOCKER ]]; then
  sudo rm -rf $MYBOOT2DOCKER
fi

BOOTLOCAL=$SRC/var/lib/boot2docker/bootlocal.sh
echo "Thanks for trying out My Boot2Docker. It's been uninstalled."
echo "please remove configuration in $BOOTLOCAL"


text:
paper-radio-button {
  display: block;
}

.checkbox-container {
  display: flex;
  flex-flow: column;
}

.checkbox-container-row {
  display: flex;
}

.checkbox-container-row > cr-checkbox {
  display: flex;
  flex-basis: 0;
  flex-grow: 1;
}


text:
using System.Reflection;

[assembly: AssemblyTitle("Google.Apis.Auth.PlatformServices")]


text:
layout: page
title: About
permalink: /about/
---

Physics PhD from the University of Michigan (2016); Bachelor of Science from the Massachusetts Institute of Technology (2004)

### More Information

Born and raised in San Juan, Puerto Rico, I now live in Manhattan.

I enjoy learning new challenging things, singing, reading, and walking my dog, Luna.

### Contact me

[lauradelmar@gmail.com](mailto:lauradelmar@gmail.com)


text:
import {NgModule}     from '@angular/core';
import {RouterModule} from '@angular/router'
import {ListboxDemo} from './listboxdemo';

@NgModule({
	imports: [
		RouterModule.forChild([
			{path:'',component: ListboxDemo}
		])
	],
	exports: [
		RouterModule
	]
})
export class ListboxDemoRoutingModule {}


text:
require_relative '../../git_aliases'
require 'git_aliases/installation'

installation = GitAliases::Installation.this

if ARGV.length < 1
  puts 'Usage: git uninstall <alias> [<alias>...]'
  exit false
end

ARGV.each do |new_alias|
  installation.uninstall_alias(new_alias)
end


text:
'use strict';

angular.module('dashingApp.dashboard').
controller('dashingJsSlackCtrl', function($scope, $http, $interval) {
    $scope.title = $scope.item.params.title;
    $scope.items = [];
    $scope.loading = true;
    $scope.showing = null;
    $scope.hashtag = $scope.item.params.hashtag;
    $scope.showDate = $scope.item.params.showDate;

    if($scope.item.params.token === '_YOUR_SLACK_TOKEN_'){
        $scope.error = 'please replace "_YOUR_SLACK_TOKEN_" in config file';
        return;
    }
    if($scope.item.params.channel === '_YOUR_SLACK_CHANNEL_ID_'){
        $scope.error = 'please replace "_YOUR_SLACK_CHANNEL_ID_" in config file';
        return;
    }

    $scope.getMessage = function(){
        let hastag = $scope.item.params.hastag;
        $http({
            method: 'GET',
            url: 'https://slack.com/api/channels.history?pretty=1&token='+$scope.item.params.token+'&channel='+$scope.item.params.channel+'&pretty=1'
        }).then(function(response) {
            var items = response.data.messages;
            for(var i in items){
                if($scope.length >= $scope.item.params.maxItems){
                    break;
                }
                let item = items[i],
                    patt = new RegExp($scope.hashtag);

                if(item.type === 'message' && patt.test(item.text)) {
                    item.text = item.text.replace(/(<([^>]+)>)/ig, '').replace($scope.hashtag, '');
                    // tmp let message = item;
                    let message = item;
                    $http({
                        method: 'GET',
                        url: 'https://slack.com/api/users.info?pretty=1&token=' + $scope.item.params.token + '&user=' + item.user
                    }).then(function (response) {
                        message.userData = response.data.user;
                        $scope.items.push(message);
                        if($scope.item.params.shuffle){
                            $scope.shuffle($scope.items);
                        }
                    });
                }
            }
            $scope.loading = false;
        });
    };

    $scope.shuffle = function(a) {
        for (let i = a.length; i; i--) {
            let j = parseInt(Math.random() * i, 10);
            [a[i - 1], a[j]] = [a[j], a[i - 1]];
        }
    };

    $interval($scope.getMessage, 1000*60*5);
    $scope.getMessage();

    function setShowing(){
        if($scope.showing +1  >= $scope.items.length || $scope.showing === null){
            $scope.showing = 0;
        }else{
            $scope.showing++;
        }

        if($scope.items[$scope.showing]){
            $scope.at = new Date($scope.items[$scope.showing].ts.split('.')[0] * 1000);
        }
    }

    var stop = $interval(setShowing, $scope.item.params.interval);
    setShowing();
});


text:
const Rule = require('./Rule')
const {slugValidator} = require('./validators/slugValidator')
const {blockValidator} = require('./validators/blockValidator')

// eslint-disable-next-line complexity
function inferFromSchemaType(typeDef) {
  if (typeDef.validation === false) {
    typeDef.validation = []
    return typeDef
  }

  const isInitialized =
    Array.isArray(typeDef.validation) &&
    typeDef.validation.every(item => typeof item.validate === 'function')

  if (isInitialized) {
    return typeDef
  }

  const type = typeDef.type
  const typed = Rule[typeDef.jsonType]
  let base = typed ? typed() : new Rule()

  if (type && type.name === 'datetime') {
    base = base.type('Date')
  }

  if (type && type.name === 'url') {
    base = base.uri()
  }

  if (type && type.name === 'slug') {
    base = base.custom(slugValidator)
  }

  if (type && type.name === 'reference') {
    base = base.reference()
  }

  if (type && type.name === 'email') {
    base = base.email()
  }

  if (type && type.name === 'block') {
    base = base.custom(blockValidator)
  }

  if (typeDef.annotations) {
    typeDef.annotations.forEach(annotation => inferFromSchemaType(annotation))
  }

  if (typeDef.options && typeDef.options.list) {
    base = base.valid(typeDef.options.list.map(extractValueFromListOption))
  }

  typeDef.validation = inferValidation(typeDef, base)

  if (typeDef.fields) {
    typeDef.fields.forEach(field => inferFromSchemaType(field.type))
  }

  if (typeDef.of && typeDef.jsonType === 'array') {
    typeDef.of.forEach(candidate => inferFromSchemaType(candidate))
  }

  return typeDef
}

function extractValueFromListOption(option) {
  return option.value || option
}

function inferValidation(field, baseRule) {
  if (!field.validation) {
    return [baseRule]
  }

  const isLazy = typeof field.validation === 'function'
  const validation = isLazy ? field.validation(baseRule) : field.validation
  return Array.isArray(validation) ? validation : [validation]
}

module.exports = inferFromSchemaType


text:
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Game.Overlays;

namespace osu.Game.Screens.Edit
{
    /// <summary>
    /// TODO: eventually make this inherit Screen and add a local screen stack inside the Editor.
    /// </summary>
    public abstract class EditorScreen : VisibilityContainer
    {
        [Resolved]
        protected EditorBeatmap EditorBeatmap { get; private set; }

        [Cached]
        protected readonly OverlayColourProvider ColourProvider;

        protected override Container<Drawable> Content => content;
        private readonly Container content;

        public readonly EditorScreenMode Type;

        protected EditorScreen(EditorScreenMode type)
        {
            Type = type;

            Anchor = Anchor.Centre;
            Origin = Anchor.Centre;
            RelativeSizeAxes = Axes.Both;

            ColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);

            InternalChild = content = new PopoverContainer { RelativeSizeAxes = Axes.Both };
        }

        protected override void PopIn()
        {
            this.ScaleTo(1f, 200, Easing.OutQuint)
                .FadeIn(200, Easing.OutQuint);
        }

        protected override void PopOut()
        {
            this.ScaleTo(0.98f, 200, Easing.OutQuint)
                .FadeOut(200, Easing.OutQuint);
        }
    }
}


text:
<?php
//
// +----------------------------------------------------------------------+
// | PEAR :: Benchmark                                                    |
// +----------------------------------------------------------------------+
// | Copyright (c) 2002-2006 Matthias Englert <Matthias.Englert@gmx.de>.  |
// +----------------------------------------------------------------------+
// | This source file is subject to the New BSD license, That is bundled  |
// | with this package in the file LICENSE, and is available through      |
// | the world-wide-web at                                                |
// | http://www.opensource.org/licenses/bsd-license.php                   |
// | If you did not receive a copy of the new BSDlicense and are unable   |
// | to obtain it through the world-wide-web, please send a note to       |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
//
// $Id: Profiler.php,v 1.2 2007/05/24 05:23:20 anant Exp $
//

require_once 'PEAR.php';

class Benchmark_Profiler extends PEAR {
    /**
     * Contains the total ex. time of each section
     *
     * @var    array
     * @access private
     */
    var $_sections = array();

    /**
     * Calling stack
     *
     * @var    array
     * @access private
     */
    var $_stack = array();

    /**
     * Notes how often a section was entered
     *
     * @var    array
     * @access private
     */
    var $_numberOfCalls = array();

    /**
     * Notes for each section how much time is spend in sub-sections
     *
     * @var    array
     * @access private
     */
    var $_subSectionsTime = array();

    /**
     * Notes for each section how often it calls which section
     *
     * @var    array
     * @access private
     */
    var $_calls = array();

    /**
     * Notes for each section how often it was called by which section
     *
     * @var    array
     * @access private
     */
    var $_callers = array();

    /**
     * Auto-starts and stops profiler
     *
     * @var    boolean
     * @access private
     */
    var $_auto = FALSE;

    /**
     * Max marker name length for non-html output
     *
     * @var    integer
     * @access private
     */
    var $_maxStringLength = 0;

    /**
     * Constructor, starts profiling recording
     *
     * @access public
     */
    function Benchmark_Profiler($auto = FALSE) {
        $this->_auto = $auto;

        if ($this->_auto) {
            $this->start();
        }

        $this->PEAR();
    }

    /**
     * Close method, stop profiling recording and display output.
     *
     * @access public
     */
    function close() {
        if (isset($this->_auto) && $this->_auto) {
            $this->stop();
            $this->display();
        }
    }

    /**
     * Returns profiling informations for a given section.
     *
     * @param  string $section
     * @return array
     * @access public
     */
    function getSectionInformations($section = 'Global') {
        if (isset($this->_sections[$section])) {
            $calls = array();

            if (isset($this->_calls[$section])) {
                $calls = $this->_calls[$section];
            }

            $callers = array();

            if (isset($this->_callers[$section])) {
                $callers = $this->_callers[$section];
            }

            $informations = array();

            $informations['time']       = $this->_sections[$section];
            if (isset($this->_sections['Global'])) {
                $informations['percentage'] = number_format(100 * $this->_sections[$section] / $this->_sections['Global'], 2, '.', '');
            } else {
                $informations['percentage'] = 'N/A';
            }
            $informations['calls']      = $calls;
            $informations['num_calls']  = $this->_numberOfCalls[$section];
            $informations['callers']    = $callers;

      	    if (isset($this->_subSectionsTime[$section])) {
                $informations['netto_time'] = $this->_sections[$section] - $this->_subSectionsTime[$section];
      	    } else {
                $informations['netto_time'] = $this->_sections[$section];
      	    }

            return $informations;
        } else {
            $this->raiseError("The section '$section' does not exists.\n", NULL, PEAR_ERROR_TRIGGER, E_USER_WARNING);
        }
    }

    /**
     * Returns profiling informations for all sections.
     *
     * @return array
     * @access public
     */
    function getAllSectionsInformations() {
        $informations = array();

        foreach($this->_sections as $section => $time) {
            $informations[$section] = $this->getSectionInformations($section);
        }

        return $informations;
    }

    /**
     * Returns formatted profiling information.
     *
     * @param  string output format (auto, plain or html), default auto
     * @see    display()
     * @access private
     */
    function _getOutput($format) {

        /* Quickly find out the maximun length: Ineffecient, but will do for now! */
        $informations = $this->getAllSectionsInformations();
        $names = array_keys($informations);

        $maxLength = 0;
        foreach ($names as $name)
        {
            if ($maxLength < strlen($name)) {
                $maxLength = strlen($name);
            }
        }
        $this->_maxStringLength = $maxLength;

        if ($format == 'auto') {
            if (function_exists('version_compare') &&
                version_compare(phpversion(), '4.1', 'ge')) {
                $format = isset($_SERVER['SERVER_PROTOCOL']) ? 'html' : 'plain';
            } else {
                global $HTTP_SERVER_VARS;
                $format = isset($HTTP_SERVER_VARS['SERVER_PROTOCOL']) ? 'html' : 'plain';
            }
        }

        if ($format == 'html') {
            $out = '
'."\n";
            $out .=
                '
 	total ex. time	'.
                'netto ex. time	'.
                '#calls	%	'.
                'calls	callers
'.
                "\n";
        } else {
            $dashes = $out = str_pad("\n", ($this->_maxStringLength + 75), '-', STR_PAD_LEFT);
            $out .= str_pad('Section', $this->_maxStringLength + 10);
            $out .= str_pad("Total Ex Time", 22);
            $out .= str_pad("Netto Ex Time", 22);
            $out .= str_pad("#Calls", 10);
            $out .= "Percentage\n";
            $out .= $dashes;
        }

        foreach($informations as $name => $values) {
            $percentage = $values['percentage'];
            $calls_str = "";

            foreach($values['calls'] as $key => $val) {
                if ($calls_str) {
                    $calls_str .= ", ";
                }

                $calls_str .= "$key ($val)";
            }

            $callers_str = "";

            foreach($values['callers'] as $key => $val) {
                if ($callers_str) {
                    $callers_str .= ", ";
    		        }

                $callers_str .= "$key ($val)";
            }

            if ($format == 'html') {
                $out .= "
$name	{$values['time']}	{$values['netto_time']}	{$values['num_calls']}	";
                if (is_numeric($values['percentage'])) {
                    $out .= "{$values['percentage']}%	\n";
                } else {
                    $out .= "{$values['percentage']}	\n";
                }
                $out .= "$calls_str	$callers_str
";
            } else {
                $out .= str_pad($name, $this->_maxStringLength + 10);
                $out .= str_pad($values['time'], 22);
                $out .= str_pad($values['netto_time'], 22);
                $out .= str_pad($values['num_calls'], 10);
                if (is_numeric($values['percentage'])) {
                    $out .= str_pad($values['percentage']."%\n", 8, ' ', STR_PAD_LEFT);
                } else {
                    $out .= str_pad($values['percentage']."\n", 8, ' ', STR_PAD_LEFT);
                }
            }
        }

        if ($format == 'html') {
            return $out . '
';
        } else {
            return $out;
        }
    }

    /**
     * Returns formatted profiling information.
     *
     * @param  string output format (auto, plain or html), default auto
     * @access public
     */
    function display($format = 'auto') {
        echo $this->_getOutput($format);
    }

    /**
     * Enters "Global" section.
     *
     * @see    enterSection(), stop()
     * @access public
     */
    function start() {
        $this->enterSection('Global');
    }

    /**
     * Leaves "Global" section.
     *
     * @see    leaveSection(), start()
     * @access public
     */
    function stop() {
        $this->leaveSection('Global');
    }

    /**
     * Enters code section.
     *
     * @param  string  name of the code section
     * @see    start(), leaveSection()
     * @access public
     */
    function enterSection($name) {
        if (count($this->_stack)) {
            if (isset($this->_callers[$name][$this->_stack[count($this->_stack) - 1]["name"]])) {
                $this->_callers[$name][$this->_stack[count($this->_stack) - 1]["name"]]++;
            } else {
                $this->_callers[$name][$this->_stack[count($this->_stack) - 1]["name"]] = 1;
            }

            if (isset($this->_calls[$this->_stack[count($this->_stack) - 1]["name"]][$name])) {
                $this->_calls[$this->_stack[count($this->_stack) - 1]["name"]][$name]++;
            } else {
                $this->_calls[$this->_stack[count($this->_stack) - 1]["name"]][$name] = 1;
            }
        } else {
            if ($name != 'Global') {
                $this->raiseError("tried to enter section ".$name." but profiling was not started\n", NULL, PEAR_ERROR_DIE);
            }
        }

        if (isset($this->_numberOfCalls[$name])) {
            $this->_numberOfCalls[$name]++;
        } else {
            $this->_numberOfCalls[$name] = 1;
        }

        array_push($this->_stack, array("name" => $name, "time" => $this->_getMicrotime()));
    }

    /**
     * Leaves code section.
     *
     * @param  string  name of the marker to be set
     * @see     stop(), enterSection()
     * @access public
     */
    function leaveSection($name) {
        $microtime = $this->_getMicrotime();

        if (!count($this->_stack)) {
            $this->raiseError("tried to leave section ".$name." but profiling was not started\n", NULL, PEAR_ERROR_DIE);
        }

        $x = array_pop($this->_stack);

        if ($x["name"] != $name) {
            $this->raiseError("reached end of section $name but expecting end of " . $x["name"]."\n", NULL, PEAR_ERROR_DIE);
        }

        if (isset($this->_sections[$name])) {
            $this->_sections[$name] += $microtime - $x["time"];
        } else {
            $this->_sections[$name] = $microtime - $x["time"];
        }

        $parent = array_pop($this->_stack);

      	if (isset($parent)) {
            if (isset($this->_subSectionsTime[$parent['name']])) {
                $this->_subSectionsTime[$parent['name']] += $microtime - $x['time'];
            } else {
                $this->_subSectionsTime[$parent['name']] = $microtime - $x['time'];
            }

            array_push($this->_stack, $parent);
        }
    }

    /**
     * Wrapper for microtime().
     *
     * @return float
     * @access private
     * @since  1.3.0
     */
    function _getMicrotime() {
        $microtime = explode(' ', microtime());
        return $microtime[1] . substr($microtime[0], 1);
    }
}


text:
package rdf

import (
	"bytes"
	"errors"
	"fmt"
	"hash"
	"sort"
)

// See "Canonical Forms for Isomorphic and Equivalent RDF Graphs: Algorithms
// for Leaning and Labelling Blank Nodes" by Aiden Hogan for description of
// the algorithm, https://doi.org/10.1145/3068333 and available free from
// the author's web page http://aidanhogan.com/docs/rdf-canonicalisation.pdf.
//
// Aspects of implementation from discussion in v1.0 of the readme of the PoC
// at https://doi.org/10.5281/zenodo.3154322

// Isomorphic returns whether the RDF graph datasets a and b are isomorphic,
// where there is a bijective mapping between blank nodes in a and b using
// the given hash function. If decomp is true, the graphs are decomposed
// before canonicalization.
func Isomorphic(a, b []*Statement, decomp bool, h hash.Hash) bool {
	if len(a) != len(b) {
		return false
	}

	zero := make([]byte, h.Size())
	ah, _ := IsoCanonicalHashes(a, decomp, true, h, zero)
	bh, _ := IsoCanonicalHashes(b, decomp, true, h, zero)
	if len(ah) != len(bh) {
		return false
	}

	work := make([][]byte, 2*len(ah))
	lexicalHashes(work[:len(ah)], ah)
	lexicalHashes(work[len(ah):], bh)
	for i := range work[:len(ah)] {
		if !bytes.Equal(work[i], work[i+len(ah)]) {
			return false
		}
	}
	return true
}

func lexicalHashes(dst [][]byte, hashes map[string][]byte) {
	i := 0
	for _, s := range hashes {
		dst[i] = s
		i++
	}
	sort.Sort(lexical(dst))
}

// IsoCanonicalHashes returns a mapping between the nodes of the RDF graph
// dataset described by the given statements using the provided hash
// function. If decomp is true, the graphs are decomposed before hashing.
// If dist is true the input graph is decomposed into identical splits, the
// entire graph will be hashed to distinguish nodes. If decomp is false,
// dist has no effect.
// Blank node hashes are initially set to the value of zero. Hash values
// are provided for literal and IRI nodes as well as for blank node. The
// hash input for literal nodes includes the quotes and the input for IRI
// nodes first removes the angle quotes around the IRI, although these are
// included in the map keys.
//
// Note that hashes returned by IsoCanonicalHashes with decomp=true are not
// comparable with hashes returned by IsoCanonicalHashes with decomp=false.
//
// See http://aidanhogan.com/docs/rdf-canonicalisation.pdf for details of
// the hashing algorithm.
func IsoCanonicalHashes(statements []*Statement, decomp, dist bool, h hash.Hash, zero []byte) (hashes map[string][]byte, terms map[string]map[string]bool) {
	if len(statements) == 0 {
		return nil, nil
	}

	if debug {
		debug.log(0, "Statements:")
		for _, s := range statements {
			debug.log(0, s)
		}
		debug.log(0)
	}

	hash, parts, ok := hashBNodesPerSplit(statements, decomp, h, zero)

	if debug {
		debug.log(0, "Blanks:")
		if len(hash.blanks) != 0 {
			for _, b := range hash.blanks {
				debug.log(0, b)
			}
		} else {
			debug.log(0, "none")
		}
		debug.log(0)

		debug.log(0, "Parts:")
		debug.logParts(0, parts)

		debug.logf(0, "Hashes from hashBNodesPerSplit (splitting=%t):\n", decomp)
		debug.logHashes(0, hash.hashOf, h.Size())
	}

	if ok {
		return hash.hashOf, hash.termsFor
	}

	// TODO: remove the triviality exception in distinguish and return
	// the original hashes if this result is nil. Make the triviality
	// exception optional.
	hashes = distinguish(statements, dist, h, zero, hash, parts, nil, 0)

	if hashes == nil {
		// distinguish was given trivial parts and
		// we did not ask it to try to merge them.
		return hash.hashOf, hash.termsFor
	}

	if debug {
		debug.log(0, "Final resolved Hashes:")
		debug.logHashes(0, hashes, h.Size())
	}

	terms = make(map[string]map[string]bool, len(hashes))
	for k, h := range hashes {
		terms[string(h)] = map[string]bool{k: true}
	}

	return hashes, terms
}

// C14n performs a relabeling of the statements in src based on the terms
// obtained from IsoCanonicalHashes, placing the results in dst and returning
// them. The relabeling scheme is the same as for the Universal RDF Dataset
// Normalization Algorithm, blank terms are ordered lexically by their hash
// value and then given a blank label with the prefix "_:c14n" and an
// identifier counter corresponding to the label's sort rank.
//
// If dst is nil, it is allocated, otherwise the length of dst must match the
// length of src.
func C14n(dst, src []*Statement, terms map[string]map[string]bool) ([]*Statement, error) {
	if dst == nil {
		dst = make([]*Statement, len(src))
	}

	if len(dst) != len(src) {
		return dst, errors.New("rdf: slice length mismatch")
	}

	need := make(map[string]bool)
	for _, s := range src {
		for _, t := range []string{
			s.Subject.Value,
			s.Object.Value,
			s.Label.Value,
		} {
			if !isBlank(t) {
				continue
			}
			need[t] = true
		}
	}

	blanks := make([]string, len(need))
	i := 0
	for h, m := range terms {
		var ok bool
		for t := range m {
			if isBlank(t) {
				ok = true
				break
			}
		}
		if !ok {
			continue
		}
		if i == len(blanks) {
			return dst, errors.New("rdf: too many blanks in terms")
		}
		blanks[i] = h
		i++
	}
	sort.Strings(blanks)

	c14n := make(map[string]string)
	for i, b := range blanks {
		if len(terms[b]) == 0 {
			return nil, fmt.Errorf("rdf: no term for blank with hash %x", b)
		}
		for t := range terms[b] {
			if !isBlank(t) {
				continue
			}
			if _, exists := c14n[t]; exists {
				continue
			}
			delete(need, t)
			c14n[t] = fmt.Sprintf("_:c14n%d", i)
		}
	}

	if len(need) != 0 {
		return dst, fmt.Errorf("rdf: missing term hashes for %d terms", len(need))
	}

	for i, s := range src {
		if dst[i] == nil {
			dst[i] = &Statement{}
		}
		n := dst[i]
		n.Subject = Term{Value: translate(s.Subject.Value, c14n)}
		n.Predicate = s.Predicate
		n.Object = Term{Value: translate(s.Object.Value, c14n)}
		n.Label = Term{Value: translate(s.Label.Value, c14n)}
	}
	sort.Sort(c14nStatements(dst))

	return dst, nil
}

func translate(term string, mapping map[string]string) string {
	if term, ok := mapping[term]; ok {
		return term
	}
	return term
}

type c14nStatements []*Statement

func (s c14nStatements) Len() int { return len(s) }
func (s c14nStatements) Less(i, j int) bool {
	si := s[i]
	sj := s[j]
	switch {
	case si.Subject.Value < sj.Subject.Value:
		return true
	case si.Subject.Value > sj.Subject.Value:
		return false
	}
	switch { // Always IRI.
	case si.Predicate.Value < sj.Predicate.Value:
		return true
	case si.Predicate.Value > sj.Predicate.Value:
		return false
	}
	switch {
	case si.Object.Value < sj.Object.Value:
		return true
	case si.Object.Value > sj.Object.Value:
		return false
	}
	return si.Label.Value < sj.Label.Value
}
func (s c14nStatements) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// hashBNodes returns the hashed blank nodes of the graph described by statements
// using the provided hash function. Hashes are initialised with zero.
//
// This is algorithm 1 in doi:10.1145/3068333.
func hashBNodes(statements []*Statement, h hash.Hash, zero []byte, hash0 map[string][]byte) (hash *table, disjoint bool) {
	curr := newTable()
	for _, s := range statements {
		for i, t := range []string{
			s.Subject.Value,
			s.Predicate.Value,
			s.Object.Value,
			s.Label.Value,
		} {
			switch {
			case i == 3 && t == "":
				continue
			case isBlank(t):
				if hash0 == nil {
					curr.set(t, zero)
				} else {
					curr.set(t, hash0[t])
				}
			case isIRI(t):
				h.Reset()
				h.Write([]byte(t[1 : len(t)-1]))
				curr.set(t, h.Sum(nil))
			default:
				h.Reset()
				h.Write([]byte(t))
				curr.set(t, h.Sum(nil))
			}
		}
	}

	bag := newHashBag(h, curr)
	last := curr.clone()
	for {
		curr, last = last, curr
		for _, s := range statements {
			if isBlank(s.Subject.Value) {
				var lab []byte
				if s.Label.Value != "" {
					lab = last.hashOf[s.Label.Value]
				}
				c := hashTuple(h, last.hashOf[s.Object.Value], last.hashOf[s.Predicate.Value], lab, []byte{'+'})
				bag.add(s.Subject.Value, c)
			}

			if isBlank(s.Object.Value) {
				var lab []byte
				if s.Label.Value != "" {
					lab = last.hashOf[s.Label.Value]
				}
				c := hashTuple(h, last.hashOf[s.Subject.Value], last.hashOf[s.Predicate.Value], lab, []byte{'-'})
				bag.add(s.Object.Value, c)
			}

			// This and the lab value above implement the label hashing
			// required for RDF dataset hashing as described in
			// https://doi.org/10.5281/zenodo.3154322 v1.0
			// Readme.md#adaptation-of-the-algorithms-to-handle-datasets.
			if isBlank(s.Label.Value) {
				c := hashTuple(h, last.hashOf[s.Subject.Value], last.hashOf[s.Predicate.Value], last.hashOf[s.Object.Value], []byte{'.'})
				bag.add(s.Label.Value, c)
			}
		}

		for t := range bag.hashesFor {
			curr.set(t, bag.sum(t))
		}

		disjoint = curr.allUnique()
		if disjoint || !curr.changedFrom(last) {
			return curr, disjoint
		}
	}
}

// table is a collision aware hash collection for RDF terms.
type table struct {
	// hashOf holds the hash for each term.
	hashOf map[string][]byte
	// termsFor holds the set of nodes in
	// the second key for terms that share
	// the hash in the first key.
	termsFor map[string]map[string]bool

	// isBlank and blanks are the set of blank
	// nodes.
	// isBlank is nil for cloned tables.
	isBlank map[string]bool
	// blanks is nil for tables created
	// with newTable.
	blanks []string
}

// newTable returns a new hash table.
func newTable() *table {
	return &table{
		hashOf:   make(map[string][]byte),
		termsFor: make(map[string]map[string]bool),
		isBlank:  make(map[string]bool),
	}
}

// wasCloned returns whether t is a parent or child of a cloning operation.
func (t *table) wasCloned() bool { return t.isBlank == nil }

// isNew returns whether t is a new table.
func (t *table) isNew() bool { return t.blanks == nil }

// clone returns a clone of the receiver.
func (t *table) clone() *table {
	new := &table{
		hashOf:   make(map[string][]byte),
		termsFor: make(map[string]map[string]bool),
	}
	for term, hash := range t.hashOf {
		new.hashOf[term] = hash
	}
	for hash, coll := range t.termsFor {
		if len(coll) == 0 {
			continue
		}
		terms := make(map[string]bool)
		for term := range coll {
			terms[term] = true
		}
		new.termsFor[hash] = terms
	}
	if t.isNew() {
		t.blanks = make([]string, len(t.isBlank))
		i := 0
		for n := range t.isBlank {
			t.blanks[i] = n
			i++
		}
		t.isBlank = nil
	}
	new.blanks = t.blanks
	return new
}

// TODO(kortschak): Make hash table in table.hashOf reuse the []byte on update.
// This is not trivial since we need to check for changes, so we can't just get
// the current hash buffer and write into it. So if this is done we probably
// a pair of buffers, a current and a waiting.

// set sets the hash of the term, removing any previously set hash.
func (t *table) set(term string, hash []byte) {
	prev := t.hashOf[term]
	if bytes.Equal(prev, hash) {
		return
	}
	t.hashOf[term] = hash

	// Delete any existing hashes for this term.
	switch terms := t.termsFor[string(prev)]; {
	case len(terms) == 1:
		delete(t.termsFor, string(prev))
	case len(terms) > 1:
		delete(terms, term)
	}

	terms, ok := t.termsFor[string(hash)]
	if ok {
		terms[term] = true
	} else {
		t.termsFor[string(hash)] = map[string]bool{term: true}
	}

	if !t.wasCloned() && isBlank(term) {
		// We are in the original table, so note
		// any blank node label that we see.
		t.isBlank[term] = true
	}
}

// allUnique returns whether every term has an unique hash. allUnique
// can only be called on a table that was returned by clone.
func (t *table) allUnique() bool {
	if t.isNew() {
		panic("checked hash bag from uncloned table")
	}
	for _, term := range t.blanks {
		if len(t.termsFor[string(t.hashOf[term])]) > 1 {
			return false
		}
	}
	return true
}

// changedFrom returns whether the receiver has been updated from last.
// changedFrom can only be called on a table that was returned by clone.
func (t *table) changedFrom(last *table) bool {
	if t.isNew() {
		panic("checked hash bag from uncloned table")
	}
	for i, x := range t.blanks {
		for _, y := range t.blanks[i+1:] {
			if bytes.Equal(t.hashOf[x], t.hashOf[y]) != bytes.Equal(last.hashOf[x], last.hashOf[y]) {
				return true
			}
		}
	}
	return false
}

// hashBag implements a commutative and associative hash.
// See notes in https://doi.org/10.5281/zenodo.3154322 v1.0
// Readme.md#what-is-the-precise-specification-of-hashbag.
type hashBag struct {
	hash      hash.Hash
	hashesFor map[string][][]byte
}

// newHashBag returns a new hashBag using the provided hash function for
// the given hash table. newHashBag can only take a table parameter that
// was returned by newTable.
func newHashBag(h hash.Hash, t *table) hashBag {
	if t.wasCloned() {
		panic("made hash bag from cloned table")
	}
	b := hashBag{hash: h, hashesFor: make(map[string][][]byte, len(t.isBlank))}
	for n := range t.isBlank {
		b.hashesFor[n] = [][]byte{t.hashOf[n]}
	}
	return b
}

// add adds the hash to the hash bag for the term.
func (b hashBag) add(term string, hash []byte) {
	b.hashesFor[term] = append(b.hashesFor[term], hash)
}

// sum calculates the hash sum for the given term, updates the hash bag
// state and returns the hash.
func (b hashBag) sum(term string) []byte {
	p := b.hashesFor[term]
	sort.Sort(lexical(p))
	h := hashTuple(b.hash, p...)
	b.hashesFor[term] = b.hashesFor[term][:1]
	b.hashesFor[term][0] = h
	return h
}

// lexical implements lexical sorting of [][]byte.
type lexical [][]byte

func (b lexical) Len() int           { return len(b) }
func (b lexical) Less(i, j int) bool { return string(b[i]) < string(b[j]) }
func (b lexical) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }

// hashTuple returns the h hash of the concatenation of t.
func hashTuple(h hash.Hash, t ...[]byte) []byte {
	h.Reset()
	for _, b := range t {
		h.Write(b)
	}
	return h.Sum(nil)
}

// hashBNodesPerSplit returns the independently hashed blank nodes of the
// graph described by statements using the provided hash function. Hashes
// are initialised with zero.
//
// This is algorithm 2 in doi:10.1145/3068333.
func hashBNodesPerSplit(statements []*Statement, decomp bool, h hash.Hash, zero []byte) (hash *table, parts byLengthHash, disjoint bool) {
	if !decomp {
		hash, ok := hashBNodes(statements, h, zero, nil)
		parts = appendOrdered(byLengthHash{}, hash.termsFor)
		sort.Sort(parts)
		return hash, parts, ok
	}

	splits := split(statements)

	// Avoid recombination work if there is only one split.
	if len(splits) == 1 {
		hash, ok := hashBNodes(statements, h, zero, nil)
		parts = appendOrdered(byLengthHash{}, hash.termsFor)
		sort.Sort(parts)
		return hash, parts, ok
	}

	hash = &table{hashOf: make(map[string][]byte)}
	disjoint = true
	for _, g := range splits {
		part, ok := hashBNodes(g, h, zero, nil)
		// Each split is guaranteed to be disjoint in its
		// set of blank nodes, so we can just append to our
		// collection of blanks.
		hash.blanks = append(hash.blanks, part.blanks...)
		if !ok {
			// Allow a short-circuit of the allUnique check.
			disjoint = false
		}
		for k, v := range part.hashOf {
			hash.hashOf[k] = v
		}
		parts = appendOrdered(parts, part.termsFor)
	}
	sort.Sort(parts)
	return hash, parts, disjoint && allUnique(hash.hashOf)
}

// appendOrdered adds parts (labels stored in the second key) for each
// hash (stored in the first key) to parts.
func appendOrdered(parts byLengthHash, partSets map[string]map[string]bool) byLengthHash {
	for h, s := range partSets {
		var p []string
		for e := range s {
			if isBlank(e) {
				p = append(p, e)
			}
		}
		if p != nil {
			parts.nodes = append(parts.nodes, p)
			parts.hashes = append(parts.hashes, h)
		}
	}
	return parts
}

// byLengthHash implements ascending length sort of a set of blank RDF
// term partitions with ties broken by lexical ordering of the partitions'
// hashes.
type byLengthHash struct {
	// nodes holds the blank nodes of a part.
	nodes [][]string
	// hashes holds the hashes corresponding
	// to the nodes in the nodes field, using
	// the same index.
	hashes []string
}

func (s byLengthHash) Len() int { return len(s.nodes) }
func (s byLengthHash) Less(i, j int) bool {
	switch {
	case len(s.nodes[i]) < len(s.nodes[j]):
		return true
	case len(s.nodes[i]) > len(s.nodes[j]):
		return false
	}
	return s.hashes[i] < s.hashes[j]
}
func (s byLengthHash) Swap(i, j int) {
	s.nodes[i], s.nodes[j] = s.nodes[j], s.nodes[i]
	s.hashes[i], s.hashes[j] = s.hashes[j], s.hashes[i]
}

// allUnique returns whether the []byte hash values in hashes are all unique.
func allUnique(hashes map[string][]byte) bool {
	set := make(map[string]bool)
	for _, h := range hashes {
		if set[string(h)] {
			return false
		}
		set[string(h)] = true
	}
	return true
}

// split returns the statements forming connected components in the graph
// described by statements.
//
// This is split in algorithm 2 in doi:10.1145/3068333.
func split(statements []*Statement) [][]*Statement {
	ds := make(djSet)
	for _, s := range statements {
		ds.add(s.Subject.Value)
		ds.add(s.Object.Value)
		if isBlank(s.Subject.Value) && isBlank(s.Object.Value) {
			ds.union(ds.find(s.Subject.Value), ds.find(s.Object.Value))
		}
	}

	var (
		splits [][]*Statement
		ground []*Statement
	)
	idxOf := make(map[*dsNode]int)
	for _, s := range statements {
		var t string
		switch {
		case isBlank(s.Subject.Value):
			t = s.Subject.Value
		case isBlank(s.Object.Value):
			t = s.Object.Value
		default:
			ground = append(ground, s)
			continue
		}
		r := ds.find(t)
		if r == nil {
			panic(fmt.Sprintf("term not found: %q", t))
		}
		i, ok := idxOf[r]
		if !ok {
			i = len(splits)
			idxOf[r] = i
			splits = append(splits, []*Statement{s})
		} else {
			splits[i] = append(splits[i], s)
		}
	}
	if ground != nil {
		splits = append(splits, ground)
	}

	if debug {
		debug.log(0, "Splits:")
		for i, s := range splits {
			for j, t := range s {
				if j == 0 {
					debug.logf(0, "%d.\t%s\n", i+1, t)
				} else {
					debug.logf(0, "\t%s\n", t)
				}
			}
			debug.log(0)
		}
	}

	return splits
}

// distinguish returns G⊥: smallest hash-labelled graph found thus far.
// The graph is returned as a node to hash lookup.
//
// This is part of algorithm 3 in doi:10.1145/3068333.
//
// The correspondence between the parameters for the function in the paper
// with the implementation here is as follows:
//   - G = statements
//   - hash = hash
//   - P = parts (already sorted by hashBNodesPerSplit)
//   - G⊥ = lowest
//   - B = hash.blanks
//
// The additional parameter dist specifies that distinguish should treat
// coequal trivial parts as a coarse of intermediate part and distinguish
// the nodes in that merged part.
func distinguish(statements []*Statement, dist bool, h hash.Hash, zero []byte, hash *table, parts byLengthHash, lowest map[string][]byte, depth int) map[string][]byte {
	if debug {
		debug.log(depth, "Running Distinguish")
	}

	var small []string
	var k int
	for k, small = range parts.nodes {
		if len(small) > 1 {
			break
		}
	}
	if len(small) < 2 {
		if lowest != nil || !dist {
			if debug {
				debug.log(depth, "Return lowest (no non-trivial parts):")
				debug.logHashes(depth, lowest, h.Size())
			}

			return lowest
		}

		// We have been given a set of fine parts,
		// but to reach here they must have been
		// non-uniquely labeled, so treat them
		// as a single coarse part.
		k, small = 0, parts.nodes[0]
	}

	if debug {
		debug.logf(depth, "Part: %v %x\n\n", small, parts.hashes[k])
		debug.log(depth, "Orig hash:")
		debug.logHashes(depth, hash.hashOf, h.Size())
	}

	smallHash := hash.hashOf[small[0]]
	for _, p := range parts.nodes[k:] {
		if !bytes.Equal(smallHash, hash.hashOf[p[0]]) {

			if debug {
				debug.logf(depth, "End of co-equal hashes: %x != %x\n\n", smallHash, hash.hashOf[p[0]])
			}

			break
		}
		for i, b := range p {

			if debug {
				debug.logf(depth, "Iter: %d — B = %q\n\n", i, b)

				if depth == 0 {
					debug.log(depth, "Current lowest:\n")
					debug.logHashes(depth, lowest, h.Size())
				}
			}

			hashP := hash.clone()
			hashP.set(b, hashTuple(h, hashP.hashOf[b], []byte{'@'}))
			hashPP, ok := hashBNodes(statements, h, zero, hashP.hashOf)
			if ok {

				if debug {
					debug.log(depth, "hashPP is trivial")
					debug.log(depth, "comparing hashPP\n")
					debug.logHashes(depth, hashPP.hashOf, h.Size())
					debug.log(depth, "with previous\n")
					debug.logHashes(depth, lowest, h.Size())
				}

				if lowest == nil || graphLess(statements, hashPP.hashOf, lowest) {
					lowest = hashPP.hashOf
					debug.log(depth, "choose hashPP\n")
				}
			} else {
				partsP := appendOrdered(byLengthHash{}, hashPP.termsFor)
				sort.Sort(partsP)

				if debug {
					debug.log(depth, "Parts':")
					debug.logParts(depth, partsP)
					debug.log(depth, "Recursive distinguish")
					debug.log(depth, "Called with current lowest:\n")
					debug.logHashes(depth, lowest, h.Size())
				}

				lowest = distinguish(statements, dist, h, zero, hashPP, partsP, lowest, depth+1)
			}
		}
	}

	if debug {
		debug.log(depth, "Return lowest:")
		debug.logHashes(depth, lowest, h.Size())
	}

	return lowest
}

// terms ordered syntactically, triples ordered lexicographically, and graphs
// ordered such that G < H if and only if G ⊂ H or there exists a triple
// t ∈ G \ H such that no triple t' ∈ H \ G exists where t' < t.
// p9 https://doi.org/10.1145/3068333
func graphLess(statements []*Statement, a, b map[string][]byte) bool {
	g := newLexicalStatements(statements, a)
	sort.Sort(g)
	h := newLexicalStatements(statements, b)
	sort.Sort(h)

	gSubH := sub(g, h, len(g.statements))
	if len(gSubH) == 0 {
		return true
	}

	hSubG := sub(h, g, 1)
	if len(hSubG) == 0 {
		return true
	}
	lowestH := relabeledStatement{hSubG[0], h.hashes}

	for _, s := range gSubH {
		rs := relabeledStatement{s, g.hashes}
		if rs.less(lowestH) {
			return true
		}
	}
	return false
}

// lexicalStatements is a sort implementation for Statements with blank
// node labels replaced with their hash.
type lexicalStatements struct {
	statements []*Statement
	hashes     map[string][]byte
}

func newLexicalStatements(statements []*Statement, hash map[string][]byte) lexicalStatements {
	s := lexicalStatements{
		statements: make([]*Statement, len(statements)),
		hashes:     hash,
	}
	copy(s.statements, statements)
	return s
}

// sub returns the difference between a and b up to max elements long.
func sub(a, b lexicalStatements, max int) []*Statement {
	var d []*Statement
	var i, j int
	for i < len(a.statements) && j < len(b.statements) && len(d) < max {
		ra := relabeledStatement{a.statements[i], a.hashes}
		rb := relabeledStatement{b.statements[j], b.hashes}
		switch {
		case ra.less(rb):
			d = append(d, a.statements[i])
			i++
		case rb.less(ra):
			j++
		default:
			i++
		}
	}
	if len(d) < max {
		d = append(d, a.statements[i:min(len(a.statements), i+max-len(d))]...)
	}
	return d
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func (s lexicalStatements) Len() int { return len(s.statements) }
func (s lexicalStatements) Less(i, j int) bool {
	return relabeledStatement{s.statements[i], s.hashes}.less(relabeledStatement{s.statements[j], s.hashes})
}
func (s lexicalStatements) Swap(i, j int) {
	s.statements[i], s.statements[j] = s.statements[j], s.statements[i]
}

// relabeledStatement is a statement that is orderable by its blank node
// hash relabeling.
type relabeledStatement struct {
	statement *Statement
	labels    map[string][]byte
}

func (a relabeledStatement) less(b relabeledStatement) bool {
	switch {
	case relabeledTerm{a.statement.Subject, a.labels}.less(relabeledTerm{b.statement.Subject, b.labels}):
		return true
	case relabeledTerm{b.statement.Subject, b.labels}.less(relabeledTerm{a.statement.Subject, a.labels}):
		return false
	}
	switch { // Always IRI.
	case a.statement.Predicate.Value < b.statement.Predicate.Value:
		return true
	case a.statement.Predicate.Value > b.statement.Predicate.Value:
		return false
	}
	switch {
	case relabeledTerm{a.statement.Object, a.labels}.less(relabeledTerm{b.statement.Object, b.labels}):
		return true
	case relabeledTerm{b.statement.Object, b.labels}.less(relabeledTerm{a.statement.Object, a.labels}):
		return false
	}
	return relabeledTerm{a.statement.Label, a.labels}.less(relabeledTerm{b.statement.Label, b.labels})
}

func (s relabeledStatement) String() string {
	subj := relabeledTerm{term: s.statement.Subject, labels: s.labels}
	obj := relabeledTerm{term: s.statement.Object, labels: s.labels}
	if s.statement.Label.Value == "" {
		return fmt.Sprintf("%s %s %s .", subj, s.statement.Predicate.Value, obj)
	}
	lab := relabeledTerm{term: s.statement.Label, labels: s.labels}
	return fmt.Sprintf("%s %s %s %s .", subj, s.statement.Predicate.Value, obj, lab)
}

// relabeledTerm is a term that is orderable by its blank node hash relabeling.
type relabeledTerm struct {
	term   Term
	labels map[string][]byte
}

func (a relabeledTerm) less(b relabeledTerm) bool {
	aIsBlank := isBlank(a.term.Value)
	bIsBlank := isBlank(b.term.Value)
	switch {
	case aIsBlank && bIsBlank:
		return bytes.Compare(a.labels[a.term.Value], b.labels[b.term.Value]) < 0
	case aIsBlank:
		return blankPrefix < unquoteIRI(b.term.Value)
	case bIsBlank:
		return unquoteIRI(a.term.Value) < blankPrefix
	default:
		return unquoteIRI(a.term.Value) < unquoteIRI(b.term.Value)
	}
}

func unquoteIRI(s string) string {
	if len(s) > 1 && s[0] == '<' && s[len(s)-1] == '>' {
		s = s[1 : len(s)-1]
	}
	return s
}

func (t relabeledTerm) String() string {
	if !isBlank(t.term.Value) {
		return t.term.Value
	}
	h, ok := t.labels[t.term.Value]
	if !ok {
		return t.term.Value + "_missing_hash"
	}
	return fmt.Sprintf("_:%0x", h)
}


text:
package slack

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"time"
)

func fileUploadReq(path, fpath string, values url.Values) (*http.Request, error) {
	fullpath, err := filepath.Abs(fpath)
	if err != nil {
		return nil, err
	}
	file, err := os.Open(fullpath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	wr := multipart.NewWriter(body)

	ioWriter, err := wr.CreateFormFile("file", filepath.Base(fullpath))
	if err != nil {
		wr.Close()
		return nil, err
	}
	bytes, err := io.Copy(ioWriter, file)
	if err != nil {
		wr.Close()
		return nil, err
	}
	// Close the multipart writer or the footer won't be written
	wr.Close()
	stat, err := file.Stat()
	if err != nil {
		return nil, err
	}
	if bytes != stat.Size() {
		return nil, errors.New("could not read the whole file")
	}
	req, err := http.NewRequest("POST", path, body)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", wr.FormDataContentType())
	req.URL.RawQuery = (values).Encode()
	return req, nil
}

func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error {
	var decoder *json.Decoder
	if debug {
		response, err := ioutil.ReadAll(body)
		if err != nil {
			return err
		}
		log.Println(string(response))
		decoder = json.NewDecoder(bytes.NewReader(response))
	} else {
		decoder = json.NewDecoder(body)
	}
	if err := decoder.Decode(&intf); err != nil {
		return err
	}
	return nil

}

func parseResponseMultipart(path string, filepath string, values url.Values, intf interface{}, debug bool) error {
	req, err := fileUploadReq(SLACK_API+path, filepath, values)
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return parseResponseBody(resp.Body, &intf, debug)
}

func postForm(endpoint string, values url.Values, intf interface{}, debug bool) error {
	resp, err := http.PostForm(endpoint, values)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return parseResponseBody(resp.Body, &intf, debug)
}

func parseResponse(path string, values url.Values, intf interface{}, debug bool) error {
	return postForm(SLACK_API+path, values, intf, debug)
}

func parseAdminResponse(method string, teamName string, values url.Values, intf interface{}, debug bool) error {
	endpoint := fmt.Sprintf(SLACK_WEB_API_FORMAT, teamName, method, time.Now().Unix())
	return postForm(endpoint, values, intf, debug)
}


text:
/**
 * Autogenerated by Thrift Compiler (0.9.0)
 *
 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
 *  @generated
 */
package com.cloudera.impala.thrift;

import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.thrift.scheme.IScheme;
import org.apache.thrift.scheme.SchemeFactory;
import org.apache.thrift.scheme.StandardScheme;

import org.apache.thrift.scheme.TupleScheme;
import org.apache.thrift.protocol.TTupleProtocol;
import org.apache.thrift.protocol.TProtocolException;
import org.apache.thrift.EncodingUtils;
import org.apache.thrift.TException;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.Set;
import java.util.HashSet;
import java.util.EnumSet;
import java.util.Collections;
import java.util.BitSet;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TResetMetadataResponse implements org.apache.thrift.TBase<TResetMetadataResponse, TResetMetadataResponse._Fields>, java.io.Serializable, Cloneable {
  private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("TResetMetadataResponse");

  private static final org.apache.thrift.protocol.TField RESULT_FIELD_DESC = new org.apache.thrift.protocol.TField("result", org.apache.thrift.protocol.TType.STRUCT, (short)1);

  private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
  static {
    schemes.put(StandardScheme.class, new TResetMetadataResponseStandardSchemeFactory());
    schemes.put(TupleScheme.class, new TResetMetadataResponseTupleSchemeFactory());
  }

  public TCatalogUpdateResult result; // required

  /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
  public enum _Fields implements org.apache.thrift.TFieldIdEnum {
    RESULT((short)1, "result");

    private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();

    static {
      for (_Fields field : EnumSet.allOf(_Fields.class)) {
        byName.put(field.getFieldName(), field);
      }
    }

    /**
     * Find the _Fields constant that matches fieldId, or null if its not found.
     */
    public static _Fields findByThriftId(int fieldId) {
      switch(fieldId) {
        case 1: // RESULT
          return RESULT;
        default:
          return null;
      }
    }

    /**
     * Find the _Fields constant that matches fieldId, throwing an exception
     * if it is not found.
     */
    public static _Fields findByThriftIdOrThrow(int fieldId) {
      _Fields fields = findByThriftId(fieldId);
      if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
      return fields;
    }

    /**
     * Find the _Fields constant that matches name, or null if its not found.
     */
    public static _Fields findByName(String name) {
      return byName.get(name);
    }

    private final short _thriftId;
    private final String _fieldName;

    _Fields(short thriftId, String fieldName) {
      _thriftId = thriftId;
      _fieldName = fieldName;
    }

    public short getThriftFieldId() {
      return _thriftId;
    }

    public String getFieldName() {
      return _fieldName;
    }
  }

  // isset id assignments
  public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
  static {
    Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
    tmpMap.put(_Fields.RESULT, new org.apache.thrift.meta_data.FieldMetaData("result", org.apache.thrift.TFieldRequirementType.REQUIRED,
        new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, TCatalogUpdateResult.class)));
    metaDataMap = Collections.unmodifiableMap(tmpMap);
    org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(TResetMetadataResponse.class, metaDataMap);
  }

  public TResetMetadataResponse() {
  }

  public TResetMetadataResponse(
    TCatalogUpdateResult result)
  {
    this();
    this.result = result;
  }

  /**
   * Performs a deep copy on <i>other</i>.
   */
  public TResetMetadataResponse(TResetMetadataResponse other) {
    if (other.isSetResult()) {
      this.result = new TCatalogUpdateResult(other.result);
    }
  }

  public TResetMetadataResponse deepCopy() {
    return new TResetMetadataResponse(this);
  }

  @Override
  public void clear() {
    this.result = null;
  }

  public TCatalogUpdateResult getResult() {
    return this.result;
  }

  public TResetMetadataResponse setResult(TCatalogUpdateResult result) {
    this.result = result;
    return this;
  }

  public void unsetResult() {
    this.result = null;
  }

  /** Returns true if field result is set (has been assigned a value) and false otherwise */
  public boolean isSetResult() {
    return this.result != null;
  }

  public void setResultIsSet(boolean value) {
    if (!value) {
      this.result = null;
    }
  }

  public void setFieldValue(_Fields field, Object value) {
    switch (field) {
    case RESULT:
      if (value == null) {
        unsetResult();
      } else {
        setResult((TCatalogUpdateResult)value);
      }
      break;

    }
  }

  public Object getFieldValue(_Fields field) {
    switch (field) {
    case RESULT:
      return getResult();

    }
    throw new IllegalStateException();
  }

  /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
  public boolean isSet(_Fields field) {
    if (field == null) {
      throw new IllegalArgumentException();
    }

    switch (field) {
    case RESULT:
      return isSetResult();
    }
    throw new IllegalStateException();
  }

  @Override
  public boolean equals(Object that) {
    if (that == null)
      return false;
    if (that instanceof TResetMetadataResponse)
      return this.equals((TResetMetadataResponse)that);
    return false;
  }

  public boolean equals(TResetMetadataResponse that) {
    if (that == null)
      return false;

    boolean this_present_result = true && this.isSetResult();
    boolean that_present_result = true && that.isSetResult();
    if (this_present_result || that_present_result) {
      if (!(this_present_result && that_present_result))
        return false;
      if (!this.result.equals(that.result))
        return false;
    }

    return true;
  }

  @Override
  public int hashCode() {
    HashCodeBuilder builder = new HashCodeBuilder();

    boolean present_result = true && (isSetResult());
    builder.append(present_result);
    if (present_result)
      builder.append(result);

    return builder.toHashCode();
  }

  public int compareTo(TResetMetadataResponse other) {
    if (!getClass().equals(other.getClass())) {
      return getClass().getName().compareTo(other.getClass().getName());
    }

    int lastComparison = 0;
    TResetMetadataResponse typedOther = (TResetMetadataResponse)other;

    lastComparison = Boolean.valueOf(isSetResult()).compareTo(typedOther.isSetResult());
    if (lastComparison != 0) {
      return lastComparison;
    }
    if (isSetResult()) {
      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.result, typedOther.result);
      if (lastComparison != 0) {
        return lastComparison;
      }
    }
    return 0;
  }

  public _Fields fieldForId(int fieldId) {
    return _Fields.findByThriftId(fieldId);
  }

  public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
    schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
  }

  public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
    schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder("TResetMetadataResponse(");
    boolean first = true;

    sb.append("result:");
    if (this.result == null) {
      sb.append("null");
    } else {
      sb.append(this.result);
    }
    first = false;
    sb.append(")");
    return sb.toString();
  }

  public void validate() throws org.apache.thrift.TException {
    // check for required fields
    if (result == null) {
      throw new org.apache.thrift.protocol.TProtocolException("Required field 'result' was not present! Struct: " + toString());
    }
    // check for sub-struct validity
    if (result != null) {
      result.validate();
    }
  }

  private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
    try {
      write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
    } catch (org.apache.thrift.TException te) {
      throw new java.io.IOException(te);
    }
  }

  private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
    try {
      read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
    } catch (org.apache.thrift.TException te) {
      throw new java.io.IOException(te);
    }
  }

  private static class TResetMetadataResponseStandardSchemeFactory implements SchemeFactory {
    public TResetMetadataResponseStandardScheme getScheme() {
      return new TResetMetadataResponseStandardScheme();
    }
  }

  private static class TResetMetadataResponseStandardScheme extends StandardScheme<TResetMetadataResponse> {

    public void read(org.apache.thrift.protocol.TProtocol iprot, TResetMetadataResponse struct) throws org.apache.thrift.TException {
      org.apache.thrift.protocol.TField schemeField;
      iprot.readStructBegin();
      while (true)
      {
        schemeField = iprot.readFieldBegin();
        if (schemeField.type == org.apache.thrift.protocol.TType.STOP) {
          break;
        }
        switch (schemeField.id) {
          case 1: // RESULT
            if (schemeField.type == org.apache.thrift.protocol.TType.STRUCT) {
              struct.result = new TCatalogUpdateResult();
              struct.result.read(iprot);
              struct.setResultIsSet(true);
            } else {
              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
            }
            break;
          default:
            org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
        }
        iprot.readFieldEnd();
      }
      iprot.readStructEnd();

      // check for required fields of primitive type, which can't be checked in the validate method
      struct.validate();
    }

    public void write(org.apache.thrift.protocol.TProtocol oprot, TResetMetadataResponse struct) throws org.apache.thrift.TException {
      struct.validate();

      oprot.writeStructBegin(STRUCT_DESC);
      if (struct.result != null) {
        oprot.writeFieldBegin(RESULT_FIELD_DESC);
        struct.result.write(oprot);
        oprot.writeFieldEnd();
      }
      oprot.writeFieldStop();
      oprot.writeStructEnd();
    }

  }

  private static class TResetMetadataResponseTupleSchemeFactory implements SchemeFactory {
    public TResetMetadataResponseTupleScheme getScheme() {
      return new TResetMetadataResponseTupleScheme();
    }
  }

  private static class TResetMetadataResponseTupleScheme extends TupleScheme<TResetMetadataResponse> {

    @Override
    public void write(org.apache.thrift.protocol.TProtocol prot, TResetMetadataResponse struct) throws org.apache.thrift.TException {
      TTupleProtocol oprot = (TTupleProtocol) prot;
      struct.result.write(oprot);
    }

    @Override
    public void read(org.apache.thrift.protocol.TProtocol prot, TResetMetadataResponse struct) throws org.apache.thrift.TException {
      TTupleProtocol iprot = (TTupleProtocol) prot;
      struct.result = new TCatalogUpdateResult();
      struct.result.read(iprot);
      struct.setResultIsSet(true);
    }
  }

}


text: mvn compile install


text:
export { of } from 'rxjs';
//# sourceMappingURL=of.js.map