Download data here. For how to construct the data objects, see Tutorial.

Quick facts about the datasets:


Load library and data.

library(FRmatch)
load("sce-mouse-M1-10X-snRNA-subclass.rda")
load("sce-mouse-M1-10X-scRNA-subclass.rda")

Rename the data objects for simplification.

## query data
sce.query <- sce.mouse.M1.10X.scRNA.subclass
## reference data
sce.ref <- sce.mouse.M1.10X.snRNA.subclass

Take a look at the gene expression data distribution. We preprocessed the 10X data by taking the log1p() transformation of the raw count data, which was stored in the @assays$logcounts slot of the data object.

par(mfrow=c(1,2))
hist(logcounts(sce.query))
hist(logcounts(sce.ref))

Since both datasets follow similar distribution, just take the scaling option in the normalization step.

sce.query.scale <- normalization(sce.query, scale=TRUE)
sce.ref.scale <- normalization(sce.ref, scale=TRUE)

Check again the scaled data distribution, ranging from 0 to 1.

par(mfrow=c(1,2))
hist(logcounts(sce.query.scale))
hist(logcounts(sce.ref.scale))

Take an overview of the cell type clusters in both datasets. In FR-Match, we are going to ignore the clusters with very few cells.

## cluster size
plot_clusterSize(sce.query, sce.ref, name.E1 = "Mouse M1 10X scRNA", name.E2 = "Mouse M1 10X snRNA")

The following codes are suggested to plot all cell type barcode plots in an assigned folder. NOT RUN HERE.

ref.clusters <- unique(colData(sce.ref)$cluster_membership)
for(cl in ref.clusters){
  plot_cluster_by_markers(sce.ref.scale, cluster.name = cl, name.E1 = "M1_10X_snRNA_",
                          filename = paste0("plot_barcodes_M1_10X_snRNA/scale_",cl,".pdf"))
}

query.clusters <- unique(colData(sce.query)$cluster_membership)
for(cl in query.clusters){
  plot_cluster_by_markers(sce.ref.scale, sce.query.scale, cluster.name = cl, name.E2 = "M1_10X_scRNA_",
                          filename = paste0("plot_barcodes_M1_10X_scRNA/scale_",cl,".pdf"))
}

Run FR-Match

Cell-to-cluster matching

Below we provide some guidance on parameter configuration.

  • We ignore small clusters that have less than 5 cells (filter.size=5)
  • For cell2cluster matching, it is recommended to use subsamp.size=10. Your best choice depends on your data. The choice of this parameter will impact the matching score (i.e. p-value), which is used to determine a match or unassigned match.
  • use.cosine=TRUE indicates to use the cosine distance instead of the Euclidean distance in the calculation, which will bring more robustness in matching between different types of samples.
  • prefix=c("scRNAseq.","snRNAseq.") is the customizable names of your query and reference data.
rst.cell2cluster <- FRmatch_cell2cluster(sce.query.scale, sce.ref.scale, 
                                         filter.size=5, subsamp.size=10, use.cosine=TRUE,
                                         prefix=c("scRNAseq.","snRNAseq."))

The easiest way to look at the results is to use its plot function, which shows the proportion of query cells that are matched to the reference cluster. Note that the column sum is 1.

plot_FRmatch_cell2cluster(rst.cell2cluster, type="match.prop")

The under-partitioned SMC_Peri cluster

In this example, there is an under-partitioned query cluster (SMC_Peri), which is matched in part to two reference clusters (SMC and Peri). We can look at the specific barcodes of the under-partitioned query cluster and the matched reference clusters for more insights.

plot_cluster_by_markers(sce.ref.scale, sce.query.scale, cluster.name = "SMC_Peri", name.E2 = "scRNA.")

plot_cluster_by_markers(sce.ref.scale, cluster.name = "SMC", name.E1 = "snRNA.")

plot_cluster_by_markers(sce.ref.scale, cluster.name = "Peri", name.E1 = "snRNA.")

Available cell2cluster results

Matching results of each query cell can be accessed below.

rst.cell2cluster$cell2cluster

Cluster-to-cluster matching

Though it is suggested to use slightly larger subsampling size (subsamp.size=20 by default) for the cluster-to-cluster matching. Here, we use subsamp.size=10 again in this use case. Your best setting may depend on your data, therefore we recommend to check the cluster sizes in the beginning.

rst <- FRmatch(sce.query.scale, sce.ref.scale, 
               filter.size=5, subsamp.size=10, use.cosine=TRUE,
               prefix=c("scRNAseq.","snRNAseq."))

Visualize the results. Since larger subsampling size is used and the nature of taking a cluster as a whole, the cluster-level matching is more conserved, resulting to some unassigned matches including the under-partitioned cluster.

plot_FRmatch(rst, sig.level=.1)

Other presentations of the results

We may use the following plot to look at the adjusted p-values, and choose a reasonable sig.level (red dashed line) to determine matches (above the red line) and unassigned matches (below the red line).

plot_FRmatch(rst, type="padj", sig.level = .1)

We can also use the prediction function to list the most similar reference cluster (i.e. highest adjusted p-value) to each query cluster, regardless the sig.level threshold.

## top match
predict_most_similar_cluster(rst)

Minimum spanning tree plots

Lastly, we can also use the minimum spanning tree (MST) plot to diagnose the under-partitioned cluster and its partial matches.

plot_MST(sce.query.scale, sce.ref.scale, "SMC_Peri", "SMC", use.cosine=T)
        runs   runs.samp1   runs.samp2         stat      p.value 
15.000000000  8.000000000  7.000000000 -2.540459701  0.005535343 

plot_MST(sce.query.scale, sce.ref.scale, "SMC_Peri", "Peri", use.cosine=T)
         runs    runs.samp1    runs.samp2          stat       p.value 
17.0000000000 11.0000000000  6.0000000000 -3.3707719915  0.0003747893 

LS0tCnRpdGxlOiAiTWF0Y2hpbmcgbW91c2UgTTEgY2VsbCBzdWJjbGFzc2VzIGFjcm9zcyBkaWZmZXJlbnQgc2FtcGxlIHR5cGVzIC0gc2NSTkFzZXEgYW5kIHNuUk5Bc2VxIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgotLS0KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChjYWNoZSA9IFRSVUUpCnNldHdkKCJ+L0RvY3VtZW50cy9CSUNDTi1CcmFpblN0YW5kYXJkcy0yMDIwL0FubGF5c2VzL01hcHBpbmcvTW91c2UtTTEvRlJtYXRjaC90dXRvcmlhbC8iKQpgYGAKCi0tLQoKRG93bmxvYWQgZGF0YSBbaGVyZV0oaHR0cHM6Ly9qY3Zpb2ZmaWNlMzY1LW15LnNoYXJlcG9pbnQuY29tLzpmOi9yL3BlcnNvbmFsL3poYW5neV9qY3ZpX29yZy9Eb2N1bWVudHMvRlItTWF0Y2glMjBkZW1vJTIwZGF0YS9UdXRvcmlhbF9tb3VzZV9NMV8xMFhfc2NSTkFzZXFfc25STkFzZXE/Y3NmPTEmd2ViPTEmZT1iUG5hMXIpLiAKRm9yIGhvdyB0byBjb25zdHJ1Y3QgdGhlIGRhdGEgb2JqZWN0cywgc2VlIFtUdXRvcmlhbF0oaHR0cHM6Ly9qY3ZlbnRlcmluc3RpdHV0ZS5naXRodWIuaW8vY2VsbGlncmF0ZS9GUm1hdGNoLXZpZ25ldHRlLmh0bWwpLgoKUXVpY2sgZmFjdHMgYWJvdXQgdGhlIGRhdGFzZXRzOgoKKyBSZWZlcmVuY2UgZGF0YQogICsgQW5hdG9taWMgcmVnaW9uOiBoZWFsdGh5IG1vdXNlIHByaW1hcnkgbW90b3IgY29ydGV4IChNMSkKICArIEV4cGVyaW1lbnRhbCBwbGF0Zm9ybTogMTBYIHYzCiAgKyBTYW1wbGUgdHlwZTogc2luZ2xlLW51Y2xldXMKICArIENpdGF0aW9uOiBbQW4gaW50ZWdyYXRlZCB0cmFuc2NyaXB0b21pYyBhbmQgZXBpZ2Vub21pYyBhdGxhcyBvZiBtb3VzZSBwcmltYXJ5IG1vdG9yIGNvcnRleCBjZWxsIHR5cGVzXShodHRwczovL3d3dy5iaW9yeGl2Lm9yZy9jb250ZW50LzEwLjExMDEvMjAyMC4wMi4yOS45NzA1NTh2MikuIERhdGEgdXNlZCBoZXJlIGlzIHBhcnQgb2YgdGhlIGludGVncmF0ZWQgdHJhbnNjcmlwdG9taWMgYW5kIGVwaWdlbm9taWMgYXRsYXMgaW4gYWJvdmUgcHVibGljYXRpb24uIEFsc28gdXNlZCBhcyB0aGUgcmVmZXJlbmNlIGRhdGFzZXQgaW4gdGhlIFtBemltdXRoIGFwcF0oaHR0cHM6Ly9hcHAuYXppbXV0aC5odWJtYXBjb25zb3J0aXVtLm9yZy9hcHAvbW91c2UtbW90b3Jjb3J0ZXgpLgorIFF1ZXJ5IGRhdGEKICArIEFuYXRvbWljIHJlZ2lvbjogaGVhbHRoeSBtb3VzZSBwcmltYXJ5IG1vdG9yIGNvcnRleCAoTU9wLCBhLmsuYS4gTTEpCiAgKyBFeHBlcmltZW50YWwgcGxhdGZvcm06IDEwWCB2MgogICsgU2FtcGxlIHR5cGU6IHNpbmdsZS1jZWxsCiAgKyBDaXRhdGlvbjogW0EgdGF4b25vbXkgb2YgdHJhbnNjcmlwdG9taWMgY2VsbCB0eXBlcyBhY3Jvc3MgdGhlIGlzb2NvcnRleCBhbmQgaGlwcG9jYW1wYWwgZm9ybWF0aW9uXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmNlbGwuMjAyMS4wNC4wMjEpLiBEYXRhIHVzZWQgaGVyZSBpcyBwYXJ0IG9mIHRoZSBtb3VzZSB3aG9sZSBjb3J0ZXggdGF4b25vbXkgaW4gdGhlIGFib3ZlIHB1YmxpY2F0aW9uLiBBbHNvIHVzZWQgYXMgdGhlIGRlbW8gZGF0YXNldCBpbiB0aGUgW0F6aW11dGggYXBwXShodHRwczovL2FwcC5hemltdXRoLmh1Ym1hcGNvbnNvcnRpdW0ub3JnL2FwcC9tb3VzZS1tb3RvcmNvcnRleCkuCgotLS0KCkxvYWQgbGlicmFyeSBhbmQgZGF0YS4KYGBge3J9CmxpYnJhcnkoRlJtYXRjaCkKbG9hZCgic2NlLW1vdXNlLU0xLTEwWC1zblJOQS1zdWJjbGFzcy5yZGEiKQpsb2FkKCJzY2UtbW91c2UtTTEtMTBYLXNjUk5BLXN1YmNsYXNzLnJkYSIpCmBgYAoKClJlbmFtZSB0aGUgZGF0YSBvYmplY3RzIGZvciBzaW1wbGlmaWNhdGlvbi4KYGBge3J9CiMjIHF1ZXJ5IGRhdGEKc2NlLnF1ZXJ5IDwtIHNjZS5tb3VzZS5NMS4xMFguc2NSTkEuc3ViY2xhc3MKIyMgcmVmZXJlbmNlIGRhdGEKc2NlLnJlZiA8LSBzY2UubW91c2UuTTEuMTBYLnNuUk5BLnN1YmNsYXNzCmBgYAoKClRha2UgYSBsb29rIGF0IHRoZSBnZW5lIGV4cHJlc3Npb24gZGF0YSBkaXN0cmlidXRpb24uIF9XZSBwcmVwcm9jZXNzZWQgdGhlIDEwWCBkYXRhIGJ5IHRha2luZyB0aGUgYGxvZzFwKClgIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSByYXcgY291bnQgZGF0YSwgd2hpY2ggd2FzIHN0b3JlZCBpbiB0aGUgYEBhc3NheXMkbG9nY291bnRzYCBzbG90IG9mIHRoZSBkYXRhIG9iamVjdC5fIApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CnBhcihtZnJvdz1jKDEsMikpCmhpc3QobG9nY291bnRzKHNjZS5xdWVyeSkpCmhpc3QobG9nY291bnRzKHNjZS5yZWYpKQpgYGAKClNpbmNlIGJvdGggZGF0YXNldHMgZm9sbG93IHNpbWlsYXIgZGlzdHJpYnV0aW9uLCBqdXN0IHRha2UgdGhlIHNjYWxpbmcgb3B0aW9uIGluIHRoZSBub3JtYWxpemF0aW9uIHN0ZXAuCmBgYHtyfQpzY2UucXVlcnkuc2NhbGUgPC0gbm9ybWFsaXphdGlvbihzY2UucXVlcnksIHNjYWxlPVRSVUUpCnNjZS5yZWYuc2NhbGUgPC0gbm9ybWFsaXphdGlvbihzY2UucmVmLCBzY2FsZT1UUlVFKQpgYGAKCkNoZWNrIGFnYWluIHRoZSBzY2FsZWQgZGF0YSBkaXN0cmlidXRpb24sIHJhbmdpbmcgZnJvbSAwIHRvIDEuCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KcGFyKG1mcm93PWMoMSwyKSkKaGlzdChsb2djb3VudHMoc2NlLnF1ZXJ5LnNjYWxlKSkKaGlzdChsb2djb3VudHMoc2NlLnJlZi5zY2FsZSkpCmBgYAoKClRha2UgYW4gb3ZlcnZpZXcgb2YgdGhlIGNlbGwgdHlwZSBjbHVzdGVycyBpbiBib3RoIGRhdGFzZXRzLiBfSW4gRlItTWF0Y2gsIHdlIGFyZSBnb2luZyB0byBpZ25vcmUgdGhlIGNsdXN0ZXJzIHdpdGggdmVyeSBmZXcgY2VsbHMuXwpgYGB7ciwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9MTB9CiMjIGNsdXN0ZXIgc2l6ZQpwbG90X2NsdXN0ZXJTaXplKHNjZS5xdWVyeSwgc2NlLnJlZiwgbmFtZS5FMSA9ICJNb3VzZSBNMSAxMFggc2NSTkEiLCBuYW1lLkUyID0gIk1vdXNlIE0xIDEwWCBzblJOQSIpCmBgYAoKClRoZSBmb2xsb3dpbmcgY29kZXMgYXJlIHN1Z2dlc3RlZCB0byBwbG90IGFsbCBjZWxsIHR5cGUgYmFyY29kZSBwbG90cyBpbiBhbiBhc3NpZ25lZCBmb2xkZXIuIE5PVCBSVU4gSEVSRS4KYGBge3IsIGV2YWw9RkFMU0V9CnJlZi5jbHVzdGVycyA8LSB1bmlxdWUoY29sRGF0YShzY2UucmVmKSRjbHVzdGVyX21lbWJlcnNoaXApCmZvcihjbCBpbiByZWYuY2x1c3RlcnMpewogIHBsb3RfY2x1c3Rlcl9ieV9tYXJrZXJzKHNjZS5yZWYuc2NhbGUsIGNsdXN0ZXIubmFtZSA9IGNsLCBuYW1lLkUxID0gIk0xXzEwWF9zblJOQV8iLAogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKCJwbG90X2JhcmNvZGVzX00xXzEwWF9zblJOQS9zY2FsZV8iLGNsLCIucGRmIikpCn0KCnF1ZXJ5LmNsdXN0ZXJzIDwtIHVuaXF1ZShjb2xEYXRhKHNjZS5xdWVyeSkkY2x1c3Rlcl9tZW1iZXJzaGlwKQpmb3IoY2wgaW4gcXVlcnkuY2x1c3RlcnMpewogIHBsb3RfY2x1c3Rlcl9ieV9tYXJrZXJzKHNjZS5yZWYuc2NhbGUsIHNjZS5xdWVyeS5zY2FsZSwgY2x1c3Rlci5uYW1lID0gY2wsIG5hbWUuRTIgPSAiTTFfMTBYX3NjUk5BXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoInBsb3RfYmFyY29kZXNfTTFfMTBYX3NjUk5BL3NjYWxlXyIsY2wsIi5wZGYiKSkKfQpgYGAKCgojIFJ1biBGUi1NYXRjaAoKIyMgQ2VsbC10by1jbHVzdGVyIG1hdGNoaW5nCgpCZWxvdyB3ZSBwcm92aWRlIHNvbWUgZ3VpZGFuY2Ugb24gcGFyYW1ldGVyIGNvbmZpZ3VyYXRpb24uCgogICsgV2UgaWdub3JlIHNtYWxsIGNsdXN0ZXJzIHRoYXQgaGF2ZSBsZXNzIHRoYW4gNSBjZWxscyAoYGZpbHRlci5zaXplPTVgKQogICsgRm9yIGNlbGwyY2x1c3RlciBtYXRjaGluZywgaXQgaXMgcmVjb21tZW5kZWQgdG8gdXNlIGBzdWJzYW1wLnNpemU9MTBgLiBZb3VyIGJlc3QgY2hvaWNlIGRlcGVuZHMgb24geW91ciBkYXRhLiBUaGUgY2hvaWNlIG9mIHRoaXMgcGFyYW1ldGVyIHdpbGwgaW1wYWN0IHRoZSBtYXRjaGluZyBzY29yZSAoaS5lLiBwLXZhbHVlKSwgd2hpY2ggaXMgdXNlZCB0byBkZXRlcm1pbmUgYSBtYXRjaCBvciB1bmFzc2lnbmVkIG1hdGNoLgogICsgYHVzZS5jb3NpbmU9VFJVRWAgaW5kaWNhdGVzIHRvIHVzZSB0aGUgY29zaW5lIGRpc3RhbmNlIGluc3RlYWQgb2YgdGhlIEV1Y2xpZGVhbiBkaXN0YW5jZSBpbiB0aGUgY2FsY3VsYXRpb24sIHdoaWNoIHdpbGwgYnJpbmcgbW9yZSByb2J1c3RuZXNzIGluIG1hdGNoaW5nIGJldHdlZW4gZGlmZmVyZW50IHR5cGVzIG9mIHNhbXBsZXMuCiAgKyBgcHJlZml4PWMoInNjUk5Bc2VxLiIsInNuUk5Bc2VxLiIpYCBpcyB0aGUgY3VzdG9taXphYmxlIG5hbWVzIG9mIHlvdXIgcXVlcnkgYW5kIHJlZmVyZW5jZSBkYXRhLgoKYGBge3IsIGV2YWw9RkFMU0V9CnJzdC5jZWxsMmNsdXN0ZXIgPC0gRlJtYXRjaF9jZWxsMmNsdXN0ZXIoc2NlLnF1ZXJ5LnNjYWxlLCBzY2UucmVmLnNjYWxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIuc2l6ZT01LCBzdWJzYW1wLnNpemU9MTAsIHVzZS5jb3NpbmU9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmVmaXg9Yygic2NSTkFzZXEuIiwic25STkFzZXEuIikpCmBgYAoKVGhlIGVhc2llc3Qgd2F5IHRvIGxvb2sgYXQgdGhlIHJlc3VsdHMgaXMgdG8gdXNlIGl0cyBwbG90IGZ1bmN0aW9uLCB3aGljaCBzaG93cyB0aGUgcHJvcG9ydGlvbiBvZiBxdWVyeSBjZWxscyB0aGF0IGFyZSBtYXRjaGVkIHRvIHRoZSByZWZlcmVuY2UgY2x1c3Rlci4gTm90ZSB0aGF0IHRoZSBjb2x1bW4gc3VtIGlzIDEuCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OX0KcGxvdF9GUm1hdGNoX2NlbGwyY2x1c3Rlcihyc3QuY2VsbDJjbHVzdGVyLCB0eXBlPSJtYXRjaC5wcm9wIikKYGBgCgojIyBUaGUgdW5kZXItcGFydGl0aW9uZWQgU01DX1BlcmkgY2x1c3RlcgoKSW4gdGhpcyBleGFtcGxlLCB0aGVyZSBpcyBhbiB1bmRlci1wYXJ0aXRpb25lZCBxdWVyeSBjbHVzdGVyIChTTUNfUGVyaSksIHdoaWNoIGlzIG1hdGNoZWQgaW4gcGFydCB0byB0d28gcmVmZXJlbmNlIGNsdXN0ZXJzIChTTUMgYW5kIFBlcmkpLiBXZSBjYW4gbG9vayBhdCB0aGUgc3BlY2lmaWMgYmFyY29kZXMgb2YgdGhlIHVuZGVyLXBhcnRpdGlvbmVkIHF1ZXJ5IGNsdXN0ZXIgYW5kIHRoZSBtYXRjaGVkIHJlZmVyZW5jZSBjbHVzdGVycyBmb3IgbW9yZSBpbnNpZ2h0cy4KYGBge3IsIGZpZy5oZWlnaHQ9N30KcGxvdF9jbHVzdGVyX2J5X21hcmtlcnMoc2NlLnJlZi5zY2FsZSwgc2NlLnF1ZXJ5LnNjYWxlLCBjbHVzdGVyLm5hbWUgPSAiU01DX1BlcmkiLCBuYW1lLkUyID0gInNjUk5BLiIpCnBsb3RfY2x1c3Rlcl9ieV9tYXJrZXJzKHNjZS5yZWYuc2NhbGUsIGNsdXN0ZXIubmFtZSA9ICJTTUMiLCBuYW1lLkUxID0gInNuUk5BLiIpCnBsb3RfY2x1c3Rlcl9ieV9tYXJrZXJzKHNjZS5yZWYuc2NhbGUsIGNsdXN0ZXIubmFtZSA9ICJQZXJpIiwgbmFtZS5FMSA9ICJzblJOQS4iKQpgYGAKCgojIyBBdmFpbGFibGUgY2VsbDJjbHVzdGVyIHJlc3VsdHMKCk1hdGNoaW5nIHJlc3VsdHMgb2YgZWFjaCBxdWVyeSBjZWxsIGNhbiBiZSBhY2Nlc3NlZCBiZWxvdy4KYGBge3J9CnJzdC5jZWxsMmNsdXN0ZXIkY2VsbDJjbHVzdGVyCmBgYAoKIyMgQ2x1c3Rlci10by1jbHVzdGVyIG1hdGNoaW5nCgpUaG91Z2ggaXQgaXMgc3VnZ2VzdGVkIHRvIHVzZSBzbGlnaHRseSBsYXJnZXIgc3Vic2FtcGxpbmcgc2l6ZSAoYHN1YnNhbXAuc2l6ZT0yMGAgYnkgZGVmYXVsdCkgZm9yIHRoZSBjbHVzdGVyLXRvLWNsdXN0ZXIgbWF0Y2hpbmcuIEhlcmUsIHdlIHVzZSBgc3Vic2FtcC5zaXplPTEwYCBhZ2FpbiBpbiB0aGlzIHVzZSBjYXNlLiBZb3VyIGJlc3Qgc2V0dGluZyBtYXkgZGVwZW5kIG9uIHlvdXIgZGF0YSwgdGhlcmVmb3JlIHdlIHJlY29tbWVuZCB0byBjaGVjayB0aGUgY2x1c3RlciBzaXplcyBpbiB0aGUgYmVnaW5uaW5nLgpgYGB7ciwgZXZhbD1GQUxTRX0KcnN0IDwtIEZSbWF0Y2goc2NlLnF1ZXJ5LnNjYWxlLCBzY2UucmVmLnNjYWxlLCAKICAgICAgICAgICAgICAgZmlsdGVyLnNpemU9NSwgc3Vic2FtcC5zaXplPTEwLCB1c2UuY29zaW5lPVRSVUUsCiAgICAgICAgICAgICAgIHByZWZpeD1jKCJzY1JOQXNlcS4iLCJzblJOQXNlcS4iKSkKYGBgCgpWaXN1YWxpemUgdGhlIHJlc3VsdHMuIFNpbmNlIGxhcmdlciBzdWJzYW1wbGluZyBzaXplIGlzIHVzZWQgYW5kIHRoZSBuYXR1cmUgb2YgdGFraW5nIGEgY2x1c3RlciBhcyBhIHdob2xlLCB0aGUgY2x1c3Rlci1sZXZlbCBtYXRjaGluZyBpcyBtb3JlIGNvbnNlcnZlZCwgcmVzdWx0aW5nIHRvIHNvbWUgdW5hc3NpZ25lZCBtYXRjaGVzIGluY2x1ZGluZyB0aGUgdW5kZXItcGFydGl0aW9uZWQgY2x1c3Rlci4KYGBge3IsIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTN9CnBsb3RfRlJtYXRjaChyc3QsIHNpZy5sZXZlbD0uMSkKYGBgCgojIyBPdGhlciBwcmVzZW50YXRpb25zIG9mIHRoZSByZXN1bHRzCgpXZSBtYXkgdXNlIHRoZSBmb2xsb3dpbmcgcGxvdCB0byBsb29rIGF0IHRoZSBhZGp1c3RlZCBwLXZhbHVlcywgYW5kIGNob29zZSBhIHJlYXNvbmFibGUgYHNpZy5sZXZlbGAgKHJlZCBkYXNoZWQgbGluZSkgdG8gZGV0ZXJtaW5lIG1hdGNoZXMgKGFib3ZlIHRoZSByZWQgbGluZSkgYW5kIHVuYXNzaWduZWQgbWF0Y2hlcyAoYmVsb3cgdGhlIHJlZCBsaW5lKS4KYGBge3IsIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTJ9CnBsb3RfRlJtYXRjaChyc3QsIHR5cGU9InBhZGoiLCBzaWcubGV2ZWwgPSAuMSkKYGBgCgpXZSBjYW4gYWxzbyB1c2UgdGhlIHByZWRpY3Rpb24gZnVuY3Rpb24gdG8gbGlzdCB0aGUgbW9zdCBzaW1pbGFyIHJlZmVyZW5jZSBjbHVzdGVyIChpLmUuIGhpZ2hlc3QgYWRqdXN0ZWQgcC12YWx1ZSkgdG8gZWFjaCBxdWVyeSBjbHVzdGVyLCByZWdhcmRsZXNzIHRoZSBgc2lnLmxldmVsYCB0aHJlc2hvbGQuCmBgYHtyfQojIyB0b3AgbWF0Y2gKcHJlZGljdF9tb3N0X3NpbWlsYXJfY2x1c3Rlcihyc3QpCmBgYAoKCiMjIE1pbmltdW0gc3Bhbm5pbmcgdHJlZSBwbG90cwoKTGFzdGx5LCB3ZSBjYW4gYWxzbyB1c2UgdGhlIG1pbmltdW0gc3Bhbm5pbmcgdHJlZSAoTVNUKSBwbG90IHRvIGRpYWdub3NlIHRoZSB1bmRlci1wYXJ0aXRpb25lZCBjbHVzdGVyIGFuZCBpdHMgcGFydGlhbCBtYXRjaGVzLiAKCmBgYHtyLCBmaWcud2lkdGg9NSxmaWcuaGVpZ2h0PTV9CnBsb3RfTVNUKHNjZS5xdWVyeS5zY2FsZSwgc2NlLnJlZi5zY2FsZSwgIlNNQ19QZXJpIiwgIlNNQyIsIHVzZS5jb3NpbmU9VCkKcGxvdF9NU1Qoc2NlLnF1ZXJ5LnNjYWxlLCBzY2UucmVmLnNjYWxlLCAiU01DX1BlcmkiLCAiUGVyaSIsIHVzZS5jb3NpbmU9VCkKYGBgCgo=