Repository: mwouts/jupytext
Pull request #90 (2018-09-30 22:23:44)

V0.7.2

codecov[bot]: # [Codecov](https://codecov.io/gh/mwouts/jupytext/pull/90?src=pr&el=h1) Report
> Merging [#90](https://codecov.io/gh/mwouts/jupytext/pull/90?src=pr&el=desc) into [master](https://codecov.io/gh/mwouts/jupytext/commit/8bbfd1e87b0609cda8bf9bd7f8bc1efbca943e1b?src=pr&el=desc) will **increase** coverage by `0.08%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/mwouts/jupytext/pull/90/graphs/tree.svg?width=650&token=a1pNjsiILi&height=150&src=pr)](https://codecov.io/gh/mwouts/jupytext/pull/90?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master      #90      +/-   ##
==========================================
+ Coverage    97.6%   97.69%   +0.08%     
==========================================
  Files          48       48              
  Lines        2886     2990     +104     
==========================================
+ Hits         2817     2921     +104     
  Misses         69       69
```


| [Impacted Files](https://codecov.io/gh/mwouts/jupytext/pull/90?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [tests/test\_read\_simple\_python.py](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9yZWFkX3NpbXBsZV9weXRob24ucHk=) | `100% <ø> (ø)` | :arrow_up: |
| [setup.py](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree#diff-c2V0dXAucHk=) | `0% <ø> (ø)` | :arrow_up: |
| [tests/test\_cli.py](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9jbGkucHk=) | `100% <100%> (ø)` | :arrow_up: |
| [tests/test\_escape\_magics.py](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9lc2NhcGVfbWFnaWNzLnB5) | `100% <100%> (ø)` | :arrow_up: |
| [jupytext/contentsmanager.py](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree#diff-anVweXRleHQvY29udGVudHNtYW5hZ2VyLnB5) | `95.67% <100%> (+0.05%)` | :arrow_up: |
| [jupytext/magics.py](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree#diff-anVweXRleHQvbWFnaWNzLnB5) | `100% <100%> (ø)` | :arrow_up: |
| [jupytext/header.py](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree#diff-anVweXRleHQvaGVhZGVyLnB5) | `99.17% <100%> (+0.02%)` | :arrow_up: |
| [jupytext/jupytext.py](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree#diff-anVweXRleHQvanVweXRleHQucHk=) | `96.07% <100%> (+0.03%)` | :arrow_up: |
| [jupytext/languages.py](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree#diff-anVweXRleHQvbGFuZ3VhZ2VzLnB5) | `100% <100%> (ø)` | :arrow_up: |
| [jupytext/formats.py](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree#diff-anVweXRleHQvZm9ybWF0cy5weQ==) | `96.85% <100%> (+0.12%)` | :arrow_up: |
| ... and [6 more](https://codecov.io/gh/mwouts/jupytext/pull/90/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mwouts/jupytext/pull/90?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mwouts/jupytext/pull/90?src=pr&el=footer). Last update [8bbfd1e...6feb7eb](https://codecov.io/gh/mwouts/jupytext/pull/90?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index bca58ca1..fcc63de7 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,6 +3,21 @@
 Release History
 ---------------
 
+0.7.2 (2018-10-01)
+++++++++++++++++++++++
+
+**Improvements**
+
+- `light` and `percent` formats made available for scheme and cpp notebooks. Adding more formats
+is straightforward - just add a new entry to _SCRIPT_EXTENSIONS in languages.py, a sample notebook
+and a mirror test (#61)
+- Format name is automatically appended to extension in `jupytext_formats` when notebook is loaded/saved.
+
+**BugFixes**
+
+- Notebooks extensions can only be prefixed with `.nb` (#87)
+
+
 0.7.1 (2018-09-24)
 ++++++++++++++++++++++
 
diff --git a/README.md b/README.md
index 8cc8aaca..99d5a5a8 100755
--- a/README.md
+++ b/README.md
@@ -192,19 +192,20 @@ The `light` format has:
 
 ### Julia, Python and R scripts in the double percent format
 
-Many Python editors recognize cells delimited with a commented double percent sign `# %%`, including
-- [Spyder IDE](https://pythonhosted.org/spyder/editor.html),
+The _double percent_ format is a representation of Jupyter notebooks as scripts, in which cells are delimited with a commented double percent sign `# %%`. The format was introduced by Spyder a few years ago, and is now supported by many editors, including
+- [Spyder IDE](https://docs.spyder-ide.org/editor.html#defining-code-cells),
 - [Hydrogen](https://atom.io/packages/hydrogen), a package for Atom,
-- [vscodeJupyter](https://marketplace.visualstudio.com/items?itemName=donjayamanne.jupyter), an extension for Visual Studio Code,
-- and PyCharm Professional.
+- [VS Code](https://code.visualstudio.com/) with the [vscodeJupyter](https://marketplace.visualstudio.com/items?itemName=donjayamanne.jupyter) extension,
+- [Python Tools for Visual Studio](https://github.com/Microsoft/PTVS),
+- and [PyCharm Professional](https://www.jetbrains.com/pycharm/).
 
-Our implementation of the `percent` format is compatible with the above editors. Cell headers have the following structure:
+Our implementation of the `percent` format is compatible with the original specifications by Spyder. We extended the format to allow markdown cells and cell metadata. Cell headers have the following structure:
 ```python
 # %% Optional text [cell type] {optional JSON metadata}
 ```
 where cell type is either omitted (code cells), or `[markdown]` or  `[raw]`. The content of markdown and raw cells is commented in the resulting script.
 
-Note that the double percent scripts you have written outside of Jupytext will be opened as such by Jupytext, provided that they contain at least two cells.
+Double percent scripts created by Jupytext have a header with an explicit format information. The format of scripts with no header is inferred automatically: scripts with at least two double percent cells are identified as double percent scripts.
 
 ### Sphinx-gallery scripts
 
diff --git a/jupytext/cell_reader.py b/jupytext/cell_reader.py
index 802caacf..c7f9894c 100644
--- a/jupytext/cell_reader.py
+++ b/jupytext/cell_reader.py
@@ -2,6 +2,7 @@
 
 import re
 from nbformat.v4.nbbase import new_code_cell, new_raw_cell, new_markdown_cell
+from .languages import _SCRIPT_EXTENSIONS
 
 try:
     from sphinx_gallery.notebook import rst2md
@@ -12,10 +13,8 @@ from .cell_metadata import is_active, json_options_to_metadata, \
     md_options_to_metadata, rmd_options_to_metadata, \
     double_percent_options_to_metadata
 from .stringparser import StringParser
-from .magics import unescape_magic, is_magic, unescape_code_start
+from .magics import uncomment_magic, is_magic, unescape_code_start
 
-_CODE_OPTION_PY = re.compile(r"^(#|# )\+(\s*){(.*)}\s*$")
-_SIMPLE_START_CODE_PY = re.compile(r"^(#|# )\+(\s*)$")
 _BLANK_LINE = re.compile(r"^\s*$")
 _PY_COMMENT = re.compile(r"^\s*#")
 _PY_INDENTED = re.compile(r"^\s")
@@ -88,6 +87,7 @@ class BaseCellReader(object):
     lines_to_next_cell = 1
 
     start_code_re = None
+    simple_start_code_re = None
     end_code_re = None
 
     # How to make code inactive
@@ -122,6 +122,9 @@ class BaseCellReader(object):
         else:
             new_cell = new_raw_cell
 
+        if not self.metadata:
+            self.metadata = {}
+
         if self.lines_to_next_cell != 1:
             self.metadata['lines_to_next_cell'] = self.lines_to_next_cell
 
@@ -171,9 +174,9 @@ class BaseCellReader(object):
 
             # Simple code pattern in LightScripts must be preceded with
             # a blank line
-            if i > 0 and self.start_code_re == _CODE_OPTION_PY and \
+            if i > 0 and self.simple_start_code_re and \
                     _BLANK_LINE.match(lines[i - 1]) and \
-                    _SIMPLE_START_CODE_PY.match(line):
+                    self.simple_start_code_re.match(line):
                 return i - 1, i, False
 
             if self.end_code_re:
@@ -307,7 +310,7 @@ class RMarkdownCellReader(MarkdownCellReader):
     def uncomment_code_and_magics(self, lines):
         if self.cell_type == 'code':
             if is_active(self.ext, self.metadata):
-                unescape_magic(lines, self.language or self.default_language)
+                uncomment_magic(lines, self.language or self.default_language)
 
         unescape_code_start(lines, self.ext, self.language or
                             self.default_language)
@@ -325,7 +328,7 @@ class ScriptCellReader(BaseCellReader):
     def uncomment_code_and_magics(self, lines):
         if self.cell_type == 'code':
             if is_active(self.ext, self.metadata):
-                unescape_magic(lines, self.language or self.default_language)
+                uncomment_magic(lines, self.language or self.default_language)
             else:
                 lines = uncomment(lines)
 
@@ -371,21 +374,25 @@ class LightScriptCellReader(ScriptCellReader):
     """Read notebook cells from plain Python or Julia files. Cells
     are identified by line breaks, unless they start with an
     explicit marker '# +' """
-    comment = '#'
-    start_code_re = _CODE_OPTION_PY
 
     def __init__(self, ext):
         super(LightScriptCellReader, self).__init__(ext)
-        self.default_language = 'julia' if ext == '.jl' else 'python'
+        script = _SCRIPT_EXTENSIONS[ext]
+        self.default_language = script['language']
+        self.comment = script['comment']
+        self.start_code_re = re.compile("^({0}|{0} )".format(self.comment) +
+                                        r"\+(\s*){(.*)}\s*$")
+        self.simple_start_code_re = re.compile(
+            r"^({0}|{0} )\+(\s*)$".format(self.comment))
 
     def options_to_metadata(self, options):
         return json_options_to_metadata(options)
 
     def metadata_and_language_from_option_line(self, line):
-        if _CODE_OPTION_PY.match(line):
+        if self.start_code_re.match(line):
             self.metadata = self.options_to_metadata(
-                _CODE_OPTION_PY.match(line).group(3))
-        elif _SIMPLE_START_CODE_PY.match(line):
+                self.start_code_re.match(line).group(3))
+        elif self.simple_start_code_re.match(line):
             self.metadata = {}
 
         if self.metadata is not None:
@@ -413,9 +420,12 @@ class LightScriptCellReader(ScriptCellReader):
 class DoublePercentScriptCellReader(ScriptCellReader):
     """Read notebook cells from Hydrogen/Spyder/VScode scripts (#59)"""
 
-    comment = '#'
-    default_language = 'python'
-    start_code_re = re.compile(r"^#\s+%%(.*)$")
+    def __init__(self, ext):
+        ScriptCellReader.__init__(self, ext)
+        script = _SCRIPT_EXTENSIONS[ext]
+        self.default_language = script['language']
+        self.comment = script['comment']
+        self.start_code_re = re.compile(r"^{}\s+%%(.*)$".format(self.comment))
 
     def options_to_metadata(self, options):
         return None, double_percent_options_to_metadata(options)
@@ -471,7 +481,7 @@ class SphinxGalleryScriptCellReader(ScriptCellReader):
 
     comment = '#'
     default_language = 'python'
-    twenty_hash = re.compile('^#( |)#{19,}\s*$')
+    twenty_hash = re.compile(r'^#( |)#{19,}\s*$')
     markdown_marker = None
     rst2md = False
 
diff --git a/jupytext/cell_to_text.py b/jupytext/cell_to_text.py
index 7abec897..8af59721 100644
--- a/jupytext/cell_to_text.py
+++ b/jupytext/cell_to_text.py
@@ -6,8 +6,9 @@ from .languages import cell_language
 from .cell_metadata import filter_metadata, is_active, \
     metadata_to_rmd_options, metadata_to_json_options, \
     metadata_to_double_percent_options
-from .magics import escape_magic, escape_code_start
+from .magics import comment_magic, escape_code_start
 from .cell_reader import LightScriptCellReader
+from .languages import _SCRIPT_EXTENSIONS
 
 
 def cell_source(cell):
@@ -20,7 +21,7 @@ def cell_source(cell):
     return source.splitlines()
 
 
-def comment(lines, prefix):
+def comment_lines(lines, prefix):
     """Return commented lines"""
     if not prefix:
         return lines
@@ -29,7 +30,6 @@ def comment(lines, prefix):
 
 class BaseCellExporter(object):
     """A class that represent a notebook cell as text"""
-    prefix = None
 
     def __init__(self, cell, default_language, ext):
         self.ext = ext
@@ -38,6 +38,7 @@ class BaseCellExporter(object):
         self.metadata = filter_metadata(cell.metadata)
         self.language = cell_language(self.source) or default_language
         self.default_language = default_language
+        self.comment = _SCRIPT_EXTENSIONS.get(ext, {}).get('comment', '#')
 
         # how many blank lines before next cell
         self.lines_to_next_cell = cell.metadata.get('lines_to_next_cell', 1)
@@ -67,13 +68,13 @@ class BaseCellExporter(object):
             return self.code_to_text()
 
         source = copy(self.source)
-        if not self.prefix:
+        if not self.comment:
             escape_code_start(source, self.ext, None)
         return self.markdown_to_text(source)
 
     def markdown_to_text(self, source):
         """Escape the given source, for a markdown cell"""
-        return comment(source, self.prefix)
+        return comment_lines(source, self.comment)
 
     def code_to_text(self):
         """Return the text representation of this cell as a code cell"""
@@ -88,7 +89,10 @@ class BaseCellExporter(object):
 
 class MarkdownCellExporter(BaseCellExporter):
     """A class that represent a notebook cell as Markdown"""
-    prefix = ''
+
+    def __init__(self, cell, default_language, ext):
+        BaseCellExporter.__init__(self, cell, default_language, ext)
+        self.comment = ''
 
     def code_to_text(self):
         """Return the text representation of a code cell"""
@@ -106,7 +110,10 @@ class MarkdownCellExporter(BaseCellExporter):
 
 class RMarkdownCellExporter(BaseCellExporter):
     """A class that represent a notebook cell as Markdown"""
-    prefix = ''
+
+    def __init__(self, cell, default_language, ext):
+        BaseCellExporter.__init__(self, cell, default_language, ext)
+        self.comment = ''
 
     def code_to_text(self):
         """Return the text representation of a code cell"""
@@ -115,7 +122,7 @@ class RMarkdownCellExporter(BaseCellExporter):
         escape_code_start(source, self.ext, self.language)
 
         if active:
-            escape_magic(source, self.language)
+            comment_magic(source, self.language)
 
         lines = []
         if not is_active('Rmd', self.metadata):
@@ -127,12 +134,12 @@ class RMarkdownCellExporter(BaseCellExporter):
         return lines
 
 
-def endofcell_marker(source):
+def endofcell_marker(source, comment):
     """Issues #31 #38:  does the cell contain a blank line? In that case
     we add an end-of-cell marker"""
     endofcell = '-'
     while True:
-        endofcell_re = re.compile(r'^#( )' + endofcell + r'\s*$')
+        endofcell_re = re.compile(r'^{}( )'.format(comment) + endofcell + r'\s*$')
         if list(filter(endofcell_re.match, source)):
             endofcell = endofcell + '-'
         else:
@@ -141,21 +148,23 @@ def endofcell_marker(source):
 
 class LightScriptCellExporter(BaseCellExporter):
     """A class that represent a notebook cell as a Python or Julia script"""
-    prefix = '#'
+
+    def __init__(self, cell, default_language, ext):
+        BaseCellExporter.__init__(self, cell, default_language, ext)
+        self.comment = _SCRIPT_EXTENSIONS[ext]['comment']
 
     def is_code(self):
         # Treat markdown cells with metadata as code cells (#66)
         if self.cell_type == 'markdown' and self.metadata:
             self.metadata['cell_type'] = self.cell_type
-            self.source = comment(self.source, self.prefix)
+            self.source = comment_lines(self.source, self.comment)
             return True
         return super(LightScriptCellExporter, self).is_code()
 
     def code_to_text(self):
         """Return the text representation of a code cell"""
         active = is_active(self.ext, self.metadata)
-        if active and self.language != (
-                'python' if self.ext == '.py' else 'julia'):
+        if active and self.language != self.default_language:
             active = False
             self.metadata['active'] = 'ipynb'
             self.metadata['language'] = self.language
@@ -164,12 +173,12 @@ class LightScriptCellExporter(BaseCellExporter):
         escape_code_start(source, self.ext, self.language)
 
         if active:
-            escape_magic(source, self.language)
+            comment_magic(source, self.language)
         else:
-            source = ['# ' + line if line else '#' for line in source]
+            source = [self.comment + ' ' + line if line else self.comment for line in source]
 
         if self.explicit_start_marker(source):
-            self.metadata['endofcell'] = endofcell_marker(source)
+            self.metadata['endofcell'] = endofcell_marker(source, self.comment)
 
         if not self.metadata:
             return source
@@ -179,9 +188,9 @@ class LightScriptCellExporter(BaseCellExporter):
         if endofcell == '-':
             del self.metadata['endofcell']
         options = metadata_to_json_options(self.metadata)
-        lines.append('# + {}'.format(options))
+        lines.append(self.comment + ' + {}'.format(options))
         lines.extend(source)
-        lines.append('# {}'.format(endofcell))
+        lines.append(self.comment + ' {}'.format(endofcell))
         return lines
 
     def explicit_start_marker(self, source):
@@ -189,7 +198,7 @@ class LightScriptCellExporter(BaseCellExporter):
         start of cell marker?"""
         if self.metadata:
             return True
-        if all([line.startswith('#') for line in self.source]):
+        if all([line.startswith(self.comment) for line in self.source]):
             return True
         if LightScriptCellReader(self.ext).read(source)[1] < len(source):
             return True
@@ -199,16 +208,16 @@ class LightScriptCellExporter(BaseCellExporter):
     def simplify_code_markers(self, text, next_text, lines):
         """Simplify cell marker when previous line is blank, remove end
         of cell marker when next cell has an explicit marker"""
-        if text[0] == '# + {}' and (not lines or not lines[-1]):
-            text[0] = '# +'
+        if text[0] == '{0} + {{}}'.format(self.comment) and (not lines or not lines[-1]):
+            text[0] = self.comment + ' +'
 
         # remove end of cell marker when redundant
         # with next explicit marker
-        if self.is_code() and text[-1] == '# -':
+        if self.is_code() and text[-1] == self.comment + ' -':
             if self.lines_to_end_of_cell_marker:
                 text = text[:-1] + \
-                       [''] * self.lines_to_end_of_cell_marker + ['# -']
-            elif not next_text or next_text[0].startswith('# + {'):
+                       [''] * self.lines_to_end_of_cell_marker + [self.comment + ' -']
+            elif not next_text or next_text[0].startswith(self.comment + ' + {'):
                 text = text[:-1]
 
         return text
@@ -216,7 +225,10 @@ class LightScriptCellExporter(BaseCellExporter):
 
 class RScriptCellExporter(BaseCellExporter):
     """A class that can represent a notebook cell as a R script"""
-    prefix = "#'"
+
+    def __init__(self, cell, default_language, ext):
+        BaseCellExporter.__init__(self, cell, default_language, ext)
+        self.comment = "#'"
 
     def code_to_text(self):
         """Return the text representation of a code cell"""
@@ -230,7 +242,7 @@ class RScriptCellExporter(BaseCellExporter):
         escape_code_start(source, self.ext, self.language)
 
         if active:
-            escape_magic(source, self.language)
+            comment_magic(source, self.language)
 
         if not active:
             source = ['# ' + line if line else '#' for line in source]
@@ -248,7 +260,10 @@ class RScriptCellExporter(BaseCellExporter):
 class DoublePercentCellExporter(BaseCellExporter):
     """A class that can represent a notebook cell as an
     Hydrogen/Spyder/VScode script (#59)"""
-    prefix = '#'
+
+    def __init__(self, cell, default_language, ext):
+        BaseCellExporter.__init__(self, cell, default_language, ext)
+        self.comment = _SCRIPT_EXTENSIONS[ext]['comment']
 
     def code_to_text(self):
         """Not used"""
@@ -265,22 +280,26 @@ class DoublePercentCellExporter(BaseCellExporter):
 
         options = metadata_to_double_percent_options(self.metadata)
         if options.startswith('%') or not options:
-            lines = ['# %%' + options]
+            lines = [self.comment + ' %%' + options]
         else:
-            lines = ['# %% ' + options]
+            lines = [self.comment + ' %% ' + options]
 
         if self.cell_type == 'code':
             return lines + self.source
 
-        return lines + comment(self.source, self.prefix)
+        return lines + comment_lines(self.source, self.comment)
 
 
 class SphinxGalleryCellExporter(BaseCellExporter):
     """A class that can represent a notebook cell as a
     Sphinx Gallery script (#80)"""
-    prefix = '#'
+
     default_cell_marker = '#' * 79
 
+    def __init__(self, cell, default_language, ext):
+        BaseCellExporter.__init__(self, cell, default_language, ext)
+        self.comment = '#'
+
     def code_to_text(self):
         """Not used"""
         pass
@@ -304,5 +323,4 @@ class SphinxGalleryCellExporter(BaseCellExporter):
             return [cell_marker] + self.source + [cell_marker]
 
         return [cell_marker if cell_marker.startswith('#' * 20)
-                else self.default_cell_marker] + comment(self.source,
-                                                         self.prefix)
+                else self.default_cell_marker] + comment_lines(self.source, self.comment)
diff --git a/jupytext/cli.py b/jupytext/cli.py
index 724d9177..a12ea979 100644
--- a/jupytext/cli.py
+++ b/jupytext/cli.py
@@ -9,6 +9,7 @@ from .formats import NOTEBOOK_EXTENSIONS, JUPYTEXT_FORMATS, \
     check_file_version, one_format_as_string, parse_one_format
 from .combine import combine_inputs_with_outputs
 from .compare import test_round_trip_conversion
+from .languages import _SCRIPT_EXTENSIONS
 
 
 def convert_notebook_files(nb_files, fmt, input_format=None, output=None,
@@ -117,12 +118,11 @@ def canonize_format(format_or_ext, file_path=None):
 
         raise ValueError('Please specificy either --to or --output')
 
-    return {'notebook': 'ipynb',
-            'python': 'py',
-            'julia': 'jl',
-            'r': 'R',
-            'markdown': 'md',
-            'rmarkdown': 'Rmd'}[format_or_ext]
+    for ext in _SCRIPT_EXTENSIONS:
+        if _SCRIPT_EXTENSIONS[ext]['language'] == format_or_ext:
+            return ext.replace('.', '')
+
+    return {'notebook': 'ipynb', 'markdown': 'md', 'rmarkdown': 'Rmd'}[format_or_ext]
 
 
 def cli_jupytext(args=None):
@@ -131,8 +131,8 @@ def cli_jupytext(args=None):
         description='Jupyter notebooks as markdown documents, '
                     'Julia, Python or R scripts')
 
-    notebook_formats = (['notebook', 'python', 'julia', 'r', 'markdown',
-                         'rmarkdown'] +
+    notebook_formats = (['notebook', 'rmarkdown', 'markdown'] +
+                        [_SCRIPT_EXTENSIONS[ext]['language'] for ext in _SCRIPT_EXTENSIONS] +
                         [ext.replace('.', '') for ext in NOTEBOOK_EXTENSIONS] +
                         [one_format_as_string(fmt.extension, fmt.format_name)
                          for fmt in JUPYTEXT_FORMATS])
diff --git a/jupytext/contentsmanager.py b/jupytext/contentsmanager.py
index f96acd34..83ceb694 100644
--- a/jupytext/contentsmanager.py
+++ b/jupytext/contentsmanager.py
@@ -105,7 +105,9 @@ def file_fmt_ext(path):
     """
     file, ext = os.path.splitext(path)
     file, intermediate_ext = os.path.splitext(file)
-    return file, intermediate_ext + ext, ext
+    if intermediate_ext in ['.nb']:
+        return file, intermediate_ext + ext, ext
+    return file + intermediate_ext, ext, ext
 
 
 class TextFileContentsManager(FileContentsManager, Configurable):
@@ -222,10 +224,8 @@ class TextFileContentsManager(FileContentsManager, Configurable):
             alt_ext = '.' + alt_fmt.split('.')[-1]
 
             if alt_ext in self.nb_extensions:
-                format_name = format_name_for_ext(nb.metadata, alt_ext) or \
-                              self.preferred_format(
-                                  alt_ext,
-                                  self.preferred_jupytext_formats_save)
+                format_name = format_name_for_ext(nb.metadata, alt_ext, explicit_default=False) or \
+                              self.preferred_format(alt_ext, self.preferred_jupytext_formats_save)
                 with mock.patch('nbformat.writes',
                                 _jupytext_writes(alt_ext, format_name)):
                     super(TextFileContentsManager, self) \
diff --git a/jupytext/formats.py b/jupytext/formats.py
index 6bf50518..d6a3f925 100644
--- a/jupytext/formats.py
+++ b/jupytext/formats.py
@@ -4,7 +4,6 @@ new formats here!
 """
 
 import os
-from copy import copy
 from .header import header_to_metadata_and_cell, insert_or_test_version_number
 from .cell_reader import MarkdownCellReader, RMarkdownCellReader, \
     LightScriptCellReader, RScriptCellReader, DoublePercentScriptCellReader, \
@@ -13,6 +12,7 @@ from .cell_to_text import MarkdownCellExporter, RMarkdownCellExporter, \
     LightScriptCellExporter, RScriptCellExporter, DoublePercentCellExporter, \
     SphinxGalleryCellExporter
 from .stringparser import StringParser
+from .languages import _SCRIPT_EXTENSIONS
 
 
 class NotebookFormatDescription:
@@ -56,7 +56,7 @@ JUPYTEXT_FORMATS = \
             current_version_number='1.0'),
 
         NotebookFormatDescription(
-            format_name='rscript',
+            format_name='spin',
             extension='.R',
             header_prefix="#'",
             cell_reader_class=RScriptCellReader,
@@ -67,7 +67,7 @@ JUPYTEXT_FORMATS = \
         NotebookFormatDescription(
             format_name='light',
             extension=ext,
-            header_prefix='#',
+            header_prefix=_SCRIPT_EXTENSIONS[ext]['comment'],
             cell_reader_class=LightScriptCellReader,
             cell_exporter_class=LightScriptCellExporter,
             # Version 1.3 on 2018-09-22 - jupytext v0.7.0rc0 : Metadata are
@@ -78,20 +78,18 @@ JUPYTEXT_FORMATS = \
             # with one blank line #38
             # Version 1.0 on 2018-08-22 - jupytext v0.5.2 : Initial version
             current_version_number='1.3',
-            min_readable_version_number='1.1') for ext in ['.jl', '.py']] + \
+            min_readable_version_number='1.1') for ext in _SCRIPT_EXTENSIONS] + \
     [
         NotebookFormatDescription(
             format_name='percent',
             extension=ext,
-            header_prefix='#',
+            header_prefix=_SCRIPT_EXTENSIONS[ext]['comment'],
             cell_reader_class=DoublePercentScriptCellReader,
             cell_exporter_class=DoublePercentCellExporter,
             # Version 1.1 on 2018-09-23 - jupytext v0.7.0rc1 : [markdown] and
             # [raw] for markdown and raw cells.
             # Version 1.0 on 2018-09-22 - jupytext v0.7.0rc0 : Initial version
-            current_version_number='1.1')
-        for ext in
-        ['.jl', '.py', '.R']] + \
+            current_version_number='1.1') for ext in _SCRIPT_EXTENSIONS] + \
     [
         NotebookFormatDescription(
             format_name='sphinx',
@@ -123,11 +121,7 @@ def get_format(ext, format_name=None):
     formats_for_extension = []
     for fmt in JUPYTEXT_FORMATS:
         if fmt.extension == ext:
-            if fmt.format_name == format_name:
-                return fmt
-            if not format_name:
-                fmt = copy(fmt)
-                fmt.format_name = None
+            if fmt.format_name == format_name or not format_name:
                 return fmt
             formats_for_extension.append(fmt.format_name)
 
@@ -139,6 +133,19 @@ def get_format(ext, format_name=None):
     raise TypeError("Not format associated to extension '{}'".format(ext))
 
 
+def read_format_from_metadata(text, ext):
+    """Return the format of the file, when that information is available from the metadata"""
+    lines = text.splitlines()
+
+    metadata, _, _ = header_to_metadata_and_cell(
+        lines, "#'" if ext == '.R' else '#')
+
+    if set(metadata).difference(['encoding', 'main_language']):
+        return format_name_for_ext(metadata, ext)
+
+    return None
+
+
 def guess_format(text, ext):
     """Guess the format of the file, given its extension and content"""
     lines = text.splitlines()
@@ -179,7 +186,7 @@ def guess_format(text, ext):
             return 'sphinx'
 
     # Default format
-    return None
+    return get_format(ext).format_name
 
 
 def check_file_version(notebook, source_path, outputs_path):
@@ -277,15 +284,19 @@ def formats_as_string(formats):
                      for ext, format_name in formats])
 
 
-def format_name_for_ext(metadata, ext):
+def format_name_for_ext(metadata, ext, explicit_default=True):
     """Return the format name for that extension"""
     formats = metadata.get('jupytext_formats', '')
     formats = parse_formats(formats)
     for fmt_ext, ext_format_name in formats:
         if fmt_ext.endswith(ext):
-            return ext_format_name
+            if (not explicit_default) or ext_format_name:
+                return ext_format_name
 
-    return None
+    if (not explicit_default) or ext in ['.Rmd', 'md']:
+        return None
+
+    return get_format(ext).format_name
 
 
 def update_jupytext_formats_metadata(notebook, ext, format_name):
diff --git a/jupytext/header.py b/jupytext/header.py
index c49eb15a..16d0c71d 100644
--- a/jupytext/header.py
+++ b/jupytext/header.py
@@ -5,14 +5,14 @@ import re
 import yaml
 import nbformat
 from nbformat.v4.nbbase import new_raw_cell
-from .cell_to_text import comment
+from .cell_to_text import comment_lines
+from .languages import _SCRIPT_EXTENSIONS
 
 _HEADER_RE = re.compile(r"^---\s*$")
 _BLANK_RE = re.compile(r"^\s*$")
 _JUPYTER_RE = re.compile(r"^jupyter\s*:\s*$")
 _LEFTSPACE_RE = re.compile(r"^\s")
-_ENCODING_RE = re.compile('^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)')
-_UTF8_HEADER = '# -*- coding: utf-8 -*-'
+_UTF8_HEADER = ' -*- coding: utf-8 -*-'
 
 # Change this to False in tests
 INSERT_AND_CHECK_VERSION_NUMBER = True
@@ -50,9 +50,10 @@ def encoding_and_executable(notebook, ext):
     """
     lines = []
     metadata = notebook.get('metadata', {})
+    comment = _SCRIPT_EXTENSIONS.get(ext, {}).get('comment')
 
     if ext not in ['.Rmd', '.md'] and 'executable' in metadata:
-        lines.append('#!' + metadata['executable'])
+        lines.append(comment + '!' + metadata['executable'])
         del metadata['executable']
 
     if 'encoding' in metadata:
@@ -63,7 +64,7 @@ def encoding_and_executable(notebook, ext):
             try:
                 cell.source.encode('ascii')
             except (UnicodeEncodeError, UnicodeDecodeError):
-                lines.append(_UTF8_HEADER)
+                lines.append(comment + _UTF8_HEADER)
                 break
 
     return lines
@@ -102,7 +103,7 @@ def metadata_and_cell_to_header(notebook, text_format):
     if header:
         header = ['---'] + header + ['---']
 
-    header = comment(header, text_format.header_prefix)
+    header = comment_lines(header, text_format.header_prefix)
 
     if header and skipline:
         header += ['']
@@ -123,13 +124,17 @@ def header_to_metadata_and_cell(lines, header_prefix):
     start = 0
     i = -1
 
+    comment = '#' if header_prefix == "#'" else header_prefix
+
+    encoding_re = re.compile(r'^[ \t\f]*{}.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)'.format(comment))
+
     for i, line in enumerate(lines):
-        if i == 0 and line.startswith('#!'):
+        if i == 0 and line.startswith(comment + '!'):
             metadata['executable'] = line[2:]
             start = i + 1
             continue
-        if i == 0 or (i == 1 and not _ENCODING_RE.match(lines[0])):
-            encoding = _ENCODING_RE.match(line)
+        if i == 0 or (i == 1 and not encoding_re.match(lines[0])):
+            encoding = encoding_re.match(line)
             if encoding:
                 if encoding.group(1) != 'utf-8':
                     raise ValueError('Encodings other than utf-8 '
diff --git a/jupytext/jupytext.py b/jupytext/jupytext.py
index c3c6789a..6f5d5705 100644
--- a/jupytext/jupytext.py
+++ b/jupytext/jupytext.py
@@ -15,7 +15,7 @@ from copy import deepcopy
 from nbformat.v4.rwbase import NotebookReader, NotebookWriter
 from nbformat.v4.nbbase import new_notebook, new_code_cell
 import nbformat
-from .formats import get_format, guess_format, \
+from .formats import get_format, read_format_from_metadata, guess_format, \
     update_jupytext_formats_metadata, format_name_for_ext
 from .header import header_to_metadata_and_cell, metadata_and_cell_to_header, \
     encoding_and_executable, insert_or_test_version_number
@@ -116,6 +116,8 @@ def reads(text, ext, format_name=None,
     if ext == '.ipynb':
         return nbformat.reads(text, as_version, **kwargs)
 
+    format_name = read_format_from_metadata(text, ext) or format_name
+
     if not format_name:
         format_name = guess_format(text, ext)
         if format_name == 'sphinx' and rst2md:
diff --git a/jupytext/languages.py b/jupytext/languages.py
index 2b9baadf..2030a87d 100644
--- a/jupytext/languages.py
+++ b/jupytext/languages.py
@@ -7,12 +7,17 @@ _JUPYTER_LANGUAGES = ['R', 'bash', 'sh', 'python', 'python2', 'python3',
 _JUPYTER_LANGUAGES_RE = [re.compile(r"^%%{}\s*".format(lang))
                          for lang in _JUPYTER_LANGUAGES]
 
+_SCRIPT_EXTENSIONS = {'.py': {'language': 'python', 'comment': '#'},
+                      '.R': {'language': 'R', 'comment': '#'},
+                      '.jl': {'language': 'julia', 'comment': '#'},
+                      '.cpp': {'language': 'c++', 'comment': '//'},
+                      '.ss': {'language': 'scheme', 'comment': ';;'}}
+
 
 def default_language_from_metadata_and_ext(notebook, ext):
     """Return the default language for a notebook that was read
     from the given file extension"""
-    default_from_ext = 'R' if ext == '.R' \
-        else 'julia' if ext == '.jl' else 'python'
+    default_from_ext = _SCRIPT_EXTENSIONS.get(ext, {}).get('language', 'python')
 
     return (notebook.metadata.get('language_info', {}).get('name')
             or notebook.metadata.get('main_language') or default_from_ext)
@@ -21,8 +26,7 @@ def default_language_from_metadata_and_ext(notebook, ext):
 def set_main_and_cell_language(metadata, cells, ext):
     """Set main language for the given collection of cells, and
     use magics for cells that use other languages"""
-    default_from_ext = 'R' if ext == '.R' \
-        else 'julia' if ext == '.jl' else 'python'
+    default_from_ext = _SCRIPT_EXTENSIONS.get(ext, {}).get('language', 'python')
     main_language = (metadata.get('language_info', {}).get('name') or
                      metadata.get('main_language'))
     if main_language is None:
diff --git a/jupytext/magics.py b/jupytext/magics.py
index 0057b51b..1dfc9d92 100644
--- a/jupytext/magics.py
+++ b/jupytext/magics.py
@@ -2,6 +2,7 @@
 
 import re
 from .stringparser import StringParser
+from .languages import _SCRIPT_EXTENSIONS
 
 # Line magics retrieved manually (Aug 18) with %lsmagic
 _LINE_MAGICS = '%alias  %alias_magic  %autocall  %automagic  %autosave  ' \
@@ -30,9 +31,13 @@ _LINE_MAGICS += ('%autoreload %aimport '  # autoreload
 _LINE_MAGICS = [magic for magic in _LINE_MAGICS if magic.startswith('%')]
 
 # A magic expression is a line or cell magic escaped zero, or multiple times
-_FORCE_ESC_RE = re.compile(r"^(# |#)*%(.*)(#| )escape")
-_FORCE_NOT_ESC_RE = re.compile(r"^(# |#)*%(.*)noescape")
-_MAGIC_RE = re.compile(r"^(# |#)*(%%|{})".format('|'.join(_LINE_MAGICS)))
+_FORCE_ESC_RE = {_SCRIPT_EXTENSIONS[ext]['language']: re.compile(
+    r"^({0} |{0})*%(.*)({0}| )escape".format(_SCRIPT_EXTENSIONS[ext]['comment'])) for ext in _SCRIPT_EXTENSIONS}
+_FORCE_NOT_ESC_RE = {_SCRIPT_EXTENSIONS[ext]['language']: re.compile(
+    r"^({0} |{0})*%(.*)({0}| )noescape".format(_SCRIPT_EXTENSIONS[ext]['comment'])) for ext in _SCRIPT_EXTENSIONS}
+_MAGIC_RE = {_SCRIPT_EXTENSIONS[ext]['language']: re.compile(
+    r"^({0} |{0})*(%%|{1})".format(_SCRIPT_EXTENSIONS[ext]['comment'], '|'.join(_LINE_MAGICS))) for ext in _SCRIPT_EXTENSIONS}
+_COMMENT = {_SCRIPT_EXTENSIONS[ext]['language']: _SCRIPT_EXTENSIONS[ext]['comment'] for ext in _SCRIPT_EXTENSIONS}
 
 # Commands starting with a question marks have to be escaped
 _HELP_RE = re.compile(r"^(# |#)*\?")
@@ -40,16 +45,17 @@ _HELP_RE = re.compile(r"^(# |#)*\?")
 
 def is_magic(line, language):
     """Is the current line a (possibly escaped) Jupyter magic?"""
-    if _FORCE_ESC_RE.match(line):
+    if _FORCE_ESC_RE.get(language, _FORCE_ESC_RE['python']).match(line):
         return True
-    if not _FORCE_NOT_ESC_RE.match(line) and _MAGIC_RE.match(line):
+    if not _FORCE_NOT_ESC_RE.get(language, _FORCE_ESC_RE['python']).match(line) and \
+            _MAGIC_RE.get(language, _FORCE_ESC_RE['python']).match(line):
         return True
-    if language == 'R':
-        return False
-    return _HELP_RE.match(line)
+    if language == 'python':
+        return _HELP_RE.match(line)
+    return False
 
 
-def escape_magic(source, language='python'):
+def comment_magic(source, language='python'):
     """Escape Jupyter magics with '# '"""
     parser = StringParser(language)
     for pos, line in enumerate(source):
@@ -59,30 +65,30 @@ def escape_magic(source, language='python'):
     return source
 
 
-def unesc(line):
+def unesc(line, language):
     """Uncomment once a commented line"""
-    if line.startswith('# '):
-        return line[2:]
-    if line.startswith('#'):
-        return line[1:]
+    comment = _COMMENT[language]
+    if line.startswith(comment + ' '):
+        return line[len(comment) + 1:]
+    if line.startswith(comment):
+        return line[len(comment):]
     return line
 
 
-def unescape_magic(source, language='python'):
+def uncomment_magic(source, language='python'):
     """Unescape Jupyter magics"""
     parser = StringParser(language)
     for pos, line in enumerate(source):
         if not parser.is_quoted() and is_magic(line, language):
-            source[pos] = unesc(line)
+            source[pos] = unesc(line, language)
         parser.read_line(line)
     return source
 
 
-_ESCAPED_CODE_START = {'.R': re.compile(r"^(# |#)*#\+"),
-                       '.Rmd': re.compile(r"^(# |#)*```{.*}"),
-                       '.md': re.compile(r"^(# |#)*```"),
-                       '.py': re.compile(r"^(# |#)*(#|# )\+(\s*){.*}")}
-_ESCAPED_CODE_START['.jl'] = _ESCAPED_CODE_START['.py']
+_ESCAPED_CODE_START = {'.Rmd': re.compile(r"^(# |#)*```{.*}"),
+                       '.md': re.compile(r"^(# |#)*```")}
+_ESCAPED_CODE_START.update({ext: re.compile(
+    r"^({0} |{0})*({0}|{0} )\+".format(_SCRIPT_EXTENSIONS[ext]['comment'])) for ext in _SCRIPT_EXTENSIONS})
 
 
 def is_escaped_code_start(line, ext):
@@ -105,7 +111,7 @@ def unescape_code_start(source, ext, language='python'):
     parser = StringParser(language)
     for pos, line in enumerate(source):
         if not parser.is_quoted() and is_escaped_code_start(line, ext):
-            unescaped = unesc(line)
+            unescaped = unesc(line, language)
             # don't remove comment char if we break the code start...
             if is_escaped_code_start(unescaped, ext):
                 source[pos] = unescaped
diff --git a/setup.py b/setup.py
index ecd25a6c..f44617a4 100644
--- a/setup.py
+++ b/setup.py
@@ -8,7 +8,7 @@ with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f:
 
 setup(
     name='jupytext',
-    version='0.7.1',
+    version='0.7.2',
     author='Marc Wouts',
     author_email='marc.wouts@gmail.com',
     description='Jupyter notebooks as Markdown documents, '


Test patch:
diff --git a/tests/notebooks/ipynb_cpp/xcpp_by_quantstack.ipynb b/tests/notebooks/ipynb_cpp/xcpp_by_quantstack.ipynb
new file mode 100644
index 00000000..5af16659
--- /dev/null
+++ b/tests/notebooks/ipynb_cpp/xcpp_by_quantstack.ipynb
@@ -0,0 +1,767 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "[![xeus-cling](images/xeus-cling.png)](https://github.com/QuantStack/xeus-cling/)\n",
+    "\n",
+    "A Jupyter kernel for C++ based on the `cling` C++ interpreter and the `xeus` native implementation of the Jupyter protocol, xeus.\n",
+    "\n",
+    "- GitHub repository: https://github.com/QuantStack/xeus-cling/\n",
+    "- Online documentation: https://xeus-cling.readthedocs.io/"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Usage\n",
+    "\n",
+    "<div style=\"background: #efffed;\n",
+    "            border: 1px solid grey;\n",
+    "            margin: 8px 0 8px 0;\n",
+    "            text-align: center;\n",
+    "            padding: 8px; \">\n",
+    "    <i class=\"fa-play fa\" \n",
+    "       style=\"font-size: 40px;\n",
+    "              line-height: 40px;\n",
+    "              margin: 8px;\n",
+    "              color: #444;\">\n",
+    "    </i>\n",
+    "    <div>\n",
+    "    To run the selected code cell, hit <pre style=\"background: #efffed\">Shift + Enter</pre>\n",
+    "    </div>\n",
+    "</div>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Output and error streams\n",
+    "\n",
+    "`std::cout` and `std::cerr` are redirected to the notebook frontend."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "\n",
+    "std::cout << \"some output\" << std::endl;"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "std::cerr << \"some error\" << std::endl;"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <stdexcept>"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "throw std::runtime_error(\"Unknown exception\");"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Omitting the `;` in the last statement of a cell results in an output being printed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "int j = 5;"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "j"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Interpreting the C++ programming language\n",
+    "\n",
+    "`cling` has a broad support of the features of C++. You can define functions, classes, templates, etc ..."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Functions"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "double sqr(double a)\n",
+    "{\n",
+    "    return a * a;\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "double a = 2.5;\n",
+    "double asqr = sqr(a);\n",
+    "asqr"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Classes"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class Foo\n",
+    "{\n",
+    "public:\n",
+    "\n",
+    "    virtual ~Foo() {}\n",
+    "    \n",
+    "    virtual void print(double value) const\n",
+    "    {\n",
+    "        std::cout << \"Foo value = \" << value << std::endl;\n",
+    "    }\n",
+    "};"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Foo bar;\n",
+    "bar.print(1.2);"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Polymorphism"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class Bar : public Foo\n",
+    "{\n",
+    "public:\n",
+    "\n",
+    "    virtual ~Bar() {}\n",
+    "    \n",
+    "    virtual void print(double value) const\n",
+    "    {\n",
+    "        std::cout << \"Bar value = \" << 2 * value << std::endl;\n",
+    "    }\n",
+    "};"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Foo* bar2 = new Bar;\n",
+    "bar2->print(1.2);\n",
+    "delete bar2;"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Templates"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <typeinfo>\n",
+    "\n",
+    "template <class T>\n",
+    "class FooT\n",
+    "{\n",
+    "public:\n",
+    "    \n",
+    "    explicit FooT(const T& t) : m_t(t) {}\n",
+    "    \n",
+    "    void print() const\n",
+    "    {\n",
+    "        std::cout << typeid(T).name() << \" m_t = \" << m_t << std::endl;\n",
+    "    }\n",
+    "    \n",
+    "private:\n",
+    "    \n",
+    "    T m_t;\n",
+    "};\n",
+    "\n",
+    "template <>\n",
+    "class FooT<int>\n",
+    "{\n",
+    "public:\n",
+    "    \n",
+    "    explicit FooT(const int& t) : m_t(t) {}\n",
+    "    \n",
+    "    void print() const\n",
+    "    {\n",
+    "        std::cout << \"m_t = \" << m_t << std::endl;\n",
+    "    }\n",
+    "    \n",
+    "private:\n",
+    "    \n",
+    "    int m_t;\n",
+    "};"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "FooT<double> foot1(1.2);\n",
+    "foot1.print();"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "FooT<int> foot2(4);\n",
+    "foot2.print();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## C++11 / C++14 support"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class Foo11\n",
+    "{\n",
+    "public:\n",
+    "    \n",
+    "    Foo11() { std::cout << \"Foo11 default constructor\" << std::endl; }\n",
+    "    Foo11(const Foo11&) { std::cout << \"Foo11 copy constructor\" << std::endl; }\n",
+    "    Foo11(Foo11&&) { std::cout << \"Foo11 move constructor\" << std::endl; }\n",
+    "};"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Foo11 f1;\n",
+    "Foo11 f2(f1);\n",
+    "Foo11 f3(std::move(f1));"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <vector>\n",
+    "\n",
+    "std::vector<int> v = { 1, 2, 3};\n",
+    "auto iter = ++v.begin();\n",
+    "v"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "*iter"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "... and also lambda, universal references, `decltype`, etc ..."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Documentation and completion\n",
+    "\n",
+    " - Documentation for types of the standard library is retrieved on cppreference.com.\n",
+    " - The quick-help feature can also be enabled for user-defined types and third-party libraries. More documentation on this feature is available at https://xeus-cling.readthedocs.io/en/latest/inline_help.html.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "?std::vector"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Using the `display_data` mechanism"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "For a user-defined type `T`, the rich rendering in the notebook and JupyterLab can be enabled by by implementing the function `xeus::xjson mime_bundle_repr(const T& im)`, which returns the JSON mime bundle for that type.\n",
+    "\n",
+    "More documentation on the rich display system of Jupyter and Xeus-cling is available at https://xeus-cling.readthedocs.io/en/latest/rich_display.html"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Image example"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <string>\n",
+    "#include <fstream>\n",
+    "\n",
+    "#include \"xtl/xbase64.hpp\"\n",
+    "#include \"xeus/xjson.hpp\"\n",
+    "\n",
+    "namespace im\n",
+    "{\n",
+    "    struct image\n",
+    "    {   \n",
+    "        inline image(const std::string& filename)\n",
+    "        {\n",
+    "            std::ifstream fin(filename, std::ios::binary);   \n",
+    "            m_buffer << fin.rdbuf();\n",
+    "        }\n",
+    "        \n",
+    "        std::stringstream m_buffer;\n",
+    "    };\n",
+    "    \n",
+    "    xeus::xjson mime_bundle_repr(const image& i)\n",
+    "    {\n",
+    "        auto bundle = xeus::xjson::object();\n",
+    "        bundle[\"image/png\"] = xtl::base64encode(i.m_buffer.str());\n",
+    "        return bundle;\n",
+    "    }\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "im::image marie(\"images/marie.png\");\n",
+    "marie"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Audio example"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <string>\n",
+    "#include <fstream>\n",
+    "\n",
+    "#include \"xtl/xbase64.hpp\"\n",
+    "#include \"xeus/xjson.hpp\"\n",
+    "\n",
+    "namespace au\n",
+    "{\n",
+    "    struct audio\n",
+    "    {   \n",
+    "        inline audio(const std::string& filename)\n",
+    "        {\n",
+    "            std::ifstream fin(filename, std::ios::binary);   \n",
+    "            m_buffer << fin.rdbuf();\n",
+    "        }\n",
+    "        \n",
+    "        std::stringstream m_buffer;\n",
+    "    };\n",
+    "    \n",
+    "    xeus::xjson mime_bundle_repr(const audio& a)\n",
+    "    {\n",
+    "        auto bundle = xeus::xjson::object();\n",
+    "        bundle[\"text/html\"] =\n",
+    "           std::string(\"<audio controls=\\\"controls\\\"><source src=\\\"data:audio/wav;base64,\")\n",
+    "           + xtl::base64encode(a.m_buffer.str()) +\n",
+    "            \"\\\" type=\\\"audio/wav\\\" /></audio>\";\n",
+    "        return bundle;\n",
+    "    }\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "au::audio drums(\"audio/audio.wav\");\n",
+    "drums"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Display"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include \"xcpp/xdisplay.hpp\""
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "xcpp::display(drums);"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Update-display"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <string>\n",
+    "#include \"xcpp/xdisplay.hpp\"\n",
+    "\n",
+    "namespace ht\n",
+    "{\n",
+    "    struct html\n",
+    "    {   \n",
+    "        inline html(const std::string& content)\n",
+    "        {\n",
+    "            m_content = content;\n",
+    "        }\n",
+    "        std::string m_content;\n",
+    "    };\n",
+    "\n",
+    "    xeus::xjson mime_bundle_repr(const html& a)\n",
+    "    {\n",
+    "        auto bundle = xeus::xjson::object();\n",
+    "        bundle[\"text/html\"] = a.m_content;\n",
+    "        return bundle;\n",
+    "    }\n",
+    "}\n",
+    "\n",
+    "// A red rectangle\n",
+    "ht::html rect(R\"(\n",
+    "<div style='\n",
+    "    width: 90px;\n",
+    "    height: 50px;\n",
+    "    line-height: 50px;\n",
+    "    background-color: blue;\n",
+    "    color: white;\n",
+    "    text-align: center;'>\n",
+    "Original\n",
+    "</div>)\");"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "xcpp::display(rect, \"some_display_id\");"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "// Update the rectangle to be blue\n",
+    "rect.m_content = R\"(\n",
+    "<div style='\n",
+    "    width: 90px;\n",
+    "    height: 50px;\n",
+    "    line-height: 50px;\n",
+    "    background-color: red;\n",
+    "    color: white;\n",
+    "    text-align: center;'>\n",
+    "Updated\n",
+    "</div>)\";\n",
+    "\n",
+    "xcpp::display(rect, \"some_display_id\", true);"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Magics\n",
+    "\n",
+    "Magics are special commands for the kernel that are not part of the C++ language.\n",
+    "\n",
+    "They are defined with the symbol `%` for a line magic and `%%` for a cell magic.\n",
+    "\n",
+    "More documentation for magics is available at https://xeus-cling.readthedocs.io/en/latest/magics.html."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <algorithm>\n",
+    "#include <vector>"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "std::vector<double> to_shuffle = {1, 2, 3, 4};"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%timeit std::random_shuffle(to_shuffle.begin(), to_shuffle.end());"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "[![xtensor](images/xtensor.png)](https://github.com/QuantStack/xtensor/)\n",
+    "\n",
+    "- GitHub repository: https://github.com/QuantStack/xtensor/\n",
+    "- Online documentation: https://xtensor.readthedocs.io/\n",
+    "- NumPy to xtensor cheat sheet: http://xtensor.readthedocs.io/en/latest/numpy.html\n",
+    "\n",
+    "`xtensor` is a C++ library for manipulating N-D arrays with an API very similar to that of numpy."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "\n",
+    "#include \"xtensor/xarray.hpp\"\n",
+    "#include \"xtensor/xio.hpp\"\n",
+    "#include \"xtensor/xview.hpp\"\n",
+    "\n",
+    "xt::xarray<double> arr1\n",
+    "  {{1.0, 2.0, 3.0},\n",
+    "   {2.0, 5.0, 7.0},\n",
+    "   {2.0, 5.0, 7.0}};\n",
+    "\n",
+    "xt::xarray<double> arr2\n",
+    "  {5.0, 6.0, 7.0};\n",
+    "\n",
+    "xt::view(arr1, 1) + arr2"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Together with the C++ Jupyter kernel, `xtensor` offers a similar experience as `NumPy` in the Python Jupyter kernel, including broadcasting and universal functions."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "#include \"xtensor/xarray.hpp\"\n",
+    "#include \"xtensor/xio.hpp\""
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "xt::xarray<int> arr\n",
+    "  {1, 2, 3, 4, 5, 6, 7, 8, 9};\n",
+    "\n",
+    "arr.reshape({3, 3});\n",
+    "\n",
+    "std::cout << arr;"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include \"xtensor-blas/xlinalg.hpp\""
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "xt::xtensor<double, 2> m = {{1.5, 0.5}, {0.7, 1.0}};\n",
+    "std::cout << \"Matrix rank: \" << std::endl << xt::linalg::matrix_rank(m) << std::endl;\n",
+    "std::cout << \"Matrix inverse: \" << std::endl << xt::linalg::inv(m) << std::endl;\n",
+    "std::cout << \"Eigen values: \" << std::endl << xt::linalg::eigvals(m) << std::endl;"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "xt::xarray<double> arg1 = xt::arange<double>(9);\n",
+    "xt::xarray<double> arg2 = xt::arange<double>(18);\n",
+    "\n",
+    "arg1.reshape({3, 3});\n",
+    "arg2.reshape({2, 3, 3});\n",
+    "\n",
+    "std::cout << xt::linalg::dot(arg1, arg2) << std::endl;"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "C++14",
+   "language": "C++14",
+   "name": "xeus-cling-cpp14"
+  },
+  "language_info": {
+   "codemirror_mode": "text/x-c++src",
+   "file_extension": ".cpp",
+   "mimetype": "text/x-c++src",
+   "name": "c++",
+   "version": "-std=c++14"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/tests/notebooks/ipynb_scheme/Reference Guide for Calysto Scheme.ipynb b/tests/notebooks/ipynb_scheme/Reference Guide for Calysto Scheme.ipynb
new file mode 100644
index 00000000..78a826c1
--- /dev/null
+++ b/tests/notebooks/ipynb_scheme/Reference Guide for Calysto Scheme.ipynb
@@ -0,0 +1,1208 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<img src=\"images/logo-64x64.png\"/>\n",
+    "<h1>Reference Guide for Calysto Scheme</h1>\n",
+    "\n",
+    "[Calysto Scheme](https://github.com/Calysto/calysto_scheme) is a real Scheme programming language, with full support for continuations, including call/cc. It can also use all Python libraries. Also has some extensions that make it more useful (stepper-debugger, choose/fail, stack traces), or make it better integrated with Python.\n",
+    "\n",
+    "In Jupyter notebooks, because Calysto Scheme uses [MetaKernel](https://github.com/Calysto/metakernel/blob/master/README.rst), it has a fully-supported set of \"magics\"---meta-commands for additional functionality. This includes running Scheme in parallel. See all of the [MetaKernel Magics](https://github.com/Calysto/metakernel/blob/master/metakernel/magics/README.md).\n",
+    "\n",
+    "Calysto Scheme is written in Scheme, and then translated into Python (and other backends). The entire functionality lies in a single Python file: https://github.com/Calysto/calysto_scheme/blob/master/calysto_scheme/scheme.py However, you can easily install it (see below).\n",
+    "\n",
+    "Please see [Calysto Scheme Language](Calysto%20Scheme%20Language.ipynb) for more details on the Calysto Scheme language.\n",
+    "\n",
+    "## Installation\n",
+    "\n",
+    "You can install Calysto Scheme with Python3:\n",
+    "\n",
+    "```\n",
+    "pip3 install --upgrade calysto-scheme --user -U\n",
+    "python3 -m calysto_kernel install --user\n",
+    "```\n",
+    "\n",
+    "or in the system kernel folder with:\n",
+    "\n",
+    "```\n",
+    "sudo pip3 install --upgrade calysto-scheme -U\n",
+    "sudo python3 -m calysto_kernel install\n",
+    "```\n",
+    "\n",
+    "Change pip3/python3 to use a different pip or Python. The version of Python used will determine how Calysto Scheme is run.\n",
+    "\n",
+    "Use it in the console, qtconsole, or notebook with IPython 3:\n",
+    "\n",
+    "```\n",
+    "ipython console --kernel calysto_scheme\n",
+    "ipython qtconsole --kernel calysto_scheme\n",
+    "ipython notebook --kernel calysto_scheme\n",
+    "```\n",
+    "\n",
+    "In addition to all of the following items, Calysto Scheme also has access to all of Python's builtin functions, and all of Python's libraries. For example, you can use `(complex 3 2)` to create a complex number by calling Python's complex function.\n",
+    "\n",
+    "## Jupyter Enhancements\n",
+    "\n",
+    "When you run Calysto Scheme in Jupyter (console, notebook, qtconsole, etc.) you get:\n",
+    "\n",
+    "* TAB completions of Scheme functions and variable names\n",
+    "* display of rich media\n",
+    "* stepper/debugger\n",
+    "* magics (% macros)\n",
+    "* shell commands (! command)\n",
+    "* LaTeX equations\n",
+    "* LaTeX-style variables\n",
+    "* Python integration"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### LaTeX-style variables\n",
+    "\n",
+    "Calysto Scheme allows you to use LaTeX-style variables in code. For example, if you type:\n",
+    "\n",
+    "```\n",
+    "\\beta\n",
+    "```\n",
+    "\n",
+    "with the cursor right after the 'a' in beta, and then press TAB, it will turn into the unicode character:\n",
+    "\n",
+    "```\n",
+    "β\n",
+    "```\n",
+    "\n",
+    "There are nearly 1300 different symbols defined (thanks to the Julia language) and documented here:\n",
+    "\n",
+    "http://docs.julialang.org/en/release-0.4/manual/unicode-input/#man-unicode-input\n",
+    "\n",
+    "Calysto Scheme may not implement all of those. Some useful and suggestive ones:\n",
+    "\n",
+    "* \\pi - π\n",
+    "* \\Pi - Π\n",
+    "* \\Sigma - Σ\n",
+    "* \\_i - subscript i, such as vectorᵢ"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "(define α 67)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "67"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "α"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(define i 2)\n",
+    "(define vectorᵢ (vector-ref (vector 0 6 3 2) i))\n",
+    "vectorᵢ"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Rich media"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(calysto)"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(import \"calysto.display\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "This is <b>bold</b>, <i>italics</i>, <u>underlined</u>."
+      ],
+      "text/plain": [
+       "<IPython.core.display.HTML object>"
+      ]
+     },
+     "execution_count": 5,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(calysto.display.HTML \"This is <b>bold</b>, <i>italics</i>, <u>underlined</u>.\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(calysto)"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(import \"calysto.graphics\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "(define canvas (calysto.graphics.Canvas))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "(define ball (calysto.graphics.Circle '(150 150) 100))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/svg+xml": [
+       "<svg baseProfile=\"full\" height=\"300\" version=\"1.1\" width=\"300\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:ev=\"http://www.w3.org/2001/xml-events\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><defs /><circle cx=\"150\" cy=\"150\" fill=\"#800080\" r=\"100\" stroke=\"#000000\" stroke-width=\"1\" /></svg>"
+      ],
+      "text/plain": [
+       "<Canvas (300, 300)>"
+      ]
+     },
+     "execution_count": 9,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(ball.draw canvas)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Shell commands"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      " config-err-xKLxlo\n",
+      " data.in\n",
+      " \u001b[0m\u001b[01;34msnap.0_canonical-livepatch_T3qk8v\u001b[0m\n",
+      " \u001b[01;34msnap.1000_gimp_sWagwQ\u001b[0m\n",
+      " \u001b[01;34mssh-CZzILsgWt9Zv\u001b[0m\n",
+      " \u001b[01;34msystemd-private-42d628edc3bb4937ba24e08da9a82866-bolt.service-MRDb9u\u001b[0m\n",
+      " \u001b[01;34msystemd-private-42d628edc3bb4937ba24e08da9a82866-colord.service-GxIyqz\u001b[0m\n",
+      " \u001b[01;34msystemd-private-42d628edc3bb4937ba24e08da9a82866-fwupd.service-uHFZyL\u001b[0m\n",
+      " \u001b[01;34msystemd-private-42d628edc3bb4937ba24e08da9a82866-iio-sensor-proxy.service-MgIq5B\u001b[0m\u001b[K\n",
+      " \u001b[01;34msystemd-private-42d628edc3bb4937ba24e08da9a82866-redis-server.service-eDX0r5\u001b[0m\n",
+      " \u001b[01;34msystemd-private-42d628edc3bb4937ba24e08da9a82866-rtkit-daemon.service-KzoHbL\u001b[0m\n",
+      " \u001b[01;34msystemd-private-42d628edc3bb4937ba24e08da9a82866-systemd-resolved.service-M9b7Fe\u001b[0m\u001b[K\n",
+      " \u001b[01;34msystemd-private-42d628edc3bb4937ba24e08da9a82866-systemd-timesyncd.service-wCU0NV\u001b[0m\u001b[K\n",
+      "\u001b[01;34m'Test Dir'\u001b[0m\n",
+      " \u001b[01;34mwww-data-temp-aspnet-0\u001b[0m\n"
+     ]
+    }
+   ],
+   "source": [
+    "! ls /tmp"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Stepper/Debugger\n",
+    "\n",
+    "Here is what the debugger looks like:\n",
+    "\n",
+    "<img src=\"images/stepper_debugger.png\">\n",
+    "\n",
+    "It has breakpoints (click in left margin). You must press Stop to exit the debugger."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "```scheme\n",
+    "%%debug\n",
+    "\n",
+    "(begin\n",
+    " (define x 1)\n",
+    " (set! x 2)\n",
+    ")\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Python Integration\n",
+    "\n",
+    "You can import and use any Python library in Calysto Scheme.\n",
+    "\n",
+    "In addition, if you wish, you can execute expressions and statements in a Python environment:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(python-eval \"1 + 2\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "(python-exec \n",
+    "\"\n",
+    "def mypyfunc(a, b):\n",
+    "    return a * b\n",
+    "\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "This is a shared environment with Scheme:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "20"
+      ]
+     },
+     "execution_count": 13,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(mypyfunc 4 5)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "You can use `func` to turn a Scheme procedure into a Python function, and `define!` to put it into the shared enviornment with Python:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "(define! mypyfunc2 (func (lambda (n) n)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "34"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(python-eval \"mypyfunc2(34)\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Differences Between Languages\n",
+    "\n",
+    "## Major differences between Scheme and Python\n",
+    "\n",
+    "1. In Scheme, double quotes are used for strings and may contain newlines\n",
+    "1. In Scheme, a single quote is short for (quote ...) and means \"literal\"\n",
+    "1. In Scheme, everything is an expression and has a return value\n",
+    "1. Python does not support macros (e.g., extending syntax)\n",
+    "1. In Python, \"if X\" is false if X is None, False, [], (,) or 0. In Scheme, \"if X\" is only false if X is #f or 0\n",
+    "1. Calysto Scheme uses continuations, not the call stack. However, for debugging there is a pseudo-stack when an error is raised. You can trun that off with (use-stack-trace #f)\n",
+    "1. Scheme procedures are not Python functions, but there are means to use one as the other.\n",
+    "\n",
+    "## Major Differences Between Calysto Scheme and other Schemes\n",
+    "\n",
+    "1. define-syntax works slightly differently\n",
+    "1. In Calysto Scheme, #(...) is short for '#(...)\n",
+    "1. Calysto Scheme is missing many standard functions (see list at bottom)\n",
+    "1. Calysto Scheme has a built-in amb operator called `choose`\n",
+    "1. For debugging there is a pseudo-stack when errors are raised in Calysto Scheme. You can trun that off with (use-stack-trace #f)\n",
+    "\n",
+    "### Stack Trace\n",
+    "\n",
+    "Calysto Scheme acts as if it has a call stack, for easier debugging. For example:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "(define fact\n",
+    "    (lambda (n)\n",
+    "      (if (= n 1)\n",
+    "           q\n",
+    "           (* n (fact (- n 1))))))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "\u001b[0;31m\n",
+      "Traceback (most recent call last):\n",
+      "  File \"In [17]\", line 1, col 1, in 'fact'\n",
+      "  File \"In [16]\", line 5, col 17, in 'fact'\n",
+      "  File \"In [16]\", line 5, col 17, in 'fact'\n",
+      "  File \"In [16]\", line 5, col 17, in 'fact'\n",
+      "  File \"In [16]\", line 5, col 17, in 'fact'\n",
+      "  File \"In [16]\", line 4, col 12\n",
+      "RunTimeError: unbound variable 'q'\n",
+      "\n",
+      "\u001b[0m"
+     ]
+    }
+   ],
+   "source": [
+    "(fact 5)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "To turn off the stack trace on error:\n",
+    "\n",
+    "```scheme\n",
+    "(use-stack-trace #f)\n",
+    "```\n",
+    "That will allow infinite recursive loops without keeping track of the \"stack\"."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Calysto Scheme Variables\n",
+    "\n",
+    "## SCHEMEPATH\n",
+    "SCHEMEPATH is a list of search directories used with (load NAME). This is a reference, so you should append to it rather than attempting to redefine it."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(\".\" \"/home/dblank/.local/lib/python3.6/site-packages/calysto_scheme/modules\")"
+      ]
+     },
+     "execution_count": 18,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "SCHEMEPATH"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "(set-cdr! (cdr SCHEMEPATH) (list \"/var/modules\"))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(\".\" \"/home/dblank/.local/lib/python3.6/site-packages/calysto_scheme/modules\" \"/var/modules\")"
+      ]
+     },
+     "execution_count": 20,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "SCHEMEPATH"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Getting Started\n",
+    "\n",
+    "Note that you can use the word `lambda` or \\lambda and then press [TAB]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "(define factorial\n",
+    "  (λ (n)\n",
+    "     (cond\n",
+    "      ((zero? n) 1)\n",
+    "      (else (* n (factorial (- n 1)))))))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "120"
+      ]
+     },
+     "execution_count": 22,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(factorial 5)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## define-syntax\n",
+    "(define-syntax NAME RULES): a method for creating macros"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "(define-syntax time \n",
+    "  [(time ?exp) (let ((start (current-time)))\n",
+    "                 ?exp\n",
+    "                 (- (current-time) start))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "0.0012819766998291016"
+      ]
+     },
+     "execution_count": 24,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(time (car '(1 2 3 4)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 25,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    ";;---------------------------------------------------------------------\n",
+    ";; collect is like list comprehension in Python\n",
+    "\n",
+    "(define-syntax collect\n",
+    "  [(collect ?exp for ?var in ?list)\n",
+    "   (filter-map (lambda (?var) ?exp) (lambda (?var) #t) ?list)]\n",
+    "  [(collect ?exp for ?var in ?list if ?condition)\n",
+    "   (filter-map (lambda (?var) ?exp) (lambda (?var) ?condition) ?list)])\n",
+    "\n",
+    "(define filter-map\n",
+    "  (lambda (f pred? values)\n",
+    "    (if (null? values)\n",
+    "      '()\n",
+    "      (if (pred? (car values))\n",
+    "          (cons (f (car values)) (filter-map f pred? (cdr values)))\n",
+    "          (filter-map f pred? (cdr values))))))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 26,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(0 1 4 9 16 25 36 49 64 81)"
+      ]
+     },
+     "execution_count": 26,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(collect (* n n) for n in (range 10))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(25 64 121 196 289)"
+      ]
+     },
+     "execution_count": 27,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(collect (* n n) for n in (range 5 20 3))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 28,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(36 49 64 81)"
+      ]
+     },
+     "execution_count": 28,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(collect (* n n) for n in (range 10) if (> n 5))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    ";;---------------------------------------------------------------------\n",
+    ";; for loops\n",
+    "\n",
+    "(define-syntax for\n",
+    "  [(for ?exp times do . ?bodies)\n",
+    "   (for-repeat ?exp (lambda () . ?bodies))]\n",
+    "  [(for ?var in ?exp do . ?bodies)\n",
+    "   (for-iterate1 ?exp (lambda (?var) . ?bodies))]\n",
+    "  [(for ?var at (?i) in ?exp do . ?bodies)\n",
+    "   (for-iterate2 0 ?exp (lambda (?var ?i) . ?bodies))]\n",
+    "  [(for ?var at (?i ?j . ?rest) in ?exp do . ?bodies)\n",
+    "   (for ?var at (?i) in ?exp do\n",
+    "     (for ?var at (?j . ?rest) in ?var do . ?bodies))])\n",
+    "\n",
+    "(define for-repeat\n",
+    "  (lambda (n f)\n",
+    "    (if (< n 1)\n",
+    "      'done\n",
+    "      (begin\n",
+    "        (f)\n",
+    "        (for-repeat (- n 1) f)))))\n",
+    "\n",
+    "(define for-iterate1\n",
+    "  (lambda (values f)\n",
+    "    (if (null? values)\n",
+    "      'done\n",
+    "      (begin\n",
+    "        (f (car values))\n",
+    "        (for-iterate1 (cdr values) f)))))\n",
+    "\n",
+    "(define for-iterate2\n",
+    "  (lambda (i values f)\n",
+    "    (if (null? values)\n",
+    "      'done\n",
+    "      (begin\n",
+    "        (f (car values) i)\n",
+    "        (for-iterate2 (+ i 1) (cdr values) f)))))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 30,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "(define matrix2d\n",
+    "  '((10 20)\n",
+    "    (30 40)\n",
+    "    (50 60)\n",
+    "    (70 80)))\n",
+    "\n",
+    "(define matrix3d\n",
+    "  '(((10 20 30) (40 50 60))\n",
+    "    ((70 80 90) (100 110 120))\n",
+    "    ((130 140 150) (160 170 180))\n",
+    "    ((190 200 210) (220 230 240))))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "5"
+      ]
+     },
+     "execution_count": 31,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(begin \n",
+    " (define hello 0)\n",
+    " (for 5 times do (set! hello (+ hello 1)))\n",
+    " hello\n",
+    " )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 32,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "a\n",
+      "b\n",
+      "c\n",
+      "d\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "done"
+      ]
+     },
+     "execution_count": 32,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(for sym in '(a b c d) do (define x 1) (set! x sym) (print x))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 33,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "10\n",
+      "12\n",
+      "14\n",
+      "16\n",
+      "18\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "done"
+      ]
+     },
+     "execution_count": 33,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(for n in (range 10 20 2) do (print n))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 34,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "(10 coords: 0 0)\n",
+      "(20 coords: 0 1)\n",
+      "(30 coords: 1 0)\n",
+      "(40 coords: 1 1)\n",
+      "(50 coords: 2 0)\n",
+      "(60 coords: 2 1)\n",
+      "(70 coords: 3 0)\n",
+      "(80 coords: 3 1)\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "done"
+      ]
+     },
+     "execution_count": 34,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(for n at (i j) in matrix2d do (print (list n 'coords: i j)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 35,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "(10 coords: 0 0 0)\n",
+      "(20 coords: 0 0 1)\n",
+      "(30 coords: 0 0 2)\n",
+      "(40 coords: 0 1 0)\n",
+      "(50 coords: 0 1 1)\n",
+      "(60 coords: 0 1 2)\n",
+      "(70 coords: 1 0 0)\n",
+      "(80 coords: 1 0 1)\n",
+      "(90 coords: 1 0 2)\n",
+      "(100 coords: 1 1 0)\n",
+      "(110 coords: 1 1 1)\n",
+      "(120 coords: 1 1 2)\n",
+      "(130 coords: 2 0 0)\n",
+      "(140 coords: 2 0 1)\n",
+      "(150 coords: 2 0 2)\n",
+      "(160 coords: 2 1 0)\n",
+      "(170 coords: 2 1 1)\n",
+      "(180 coords: 2 1 2)\n",
+      "(190 coords: 3 0 0)\n",
+      "(200 coords: 3 0 1)\n",
+      "(210 coords: 3 0 2)\n",
+      "(220 coords: 3 1 0)\n",
+      "(230 coords: 3 1 1)\n",
+      "(240 coords: 3 1 2)\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "done"
+      ]
+     },
+     "execution_count": 35,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(for n at (i j k) in matrix3d do (print (list n 'coords: i j k)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 36,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "(define-syntax scons\n",
+    "  [(scons ?x ?y) (cons ?x (lambda () ?y))])\n",
+    "\n",
+    "(define scar car)\n",
+    "\n",
+    "(define scdr\n",
+    "  (lambda (s)\n",
+    "    (let ((result ((cdr s))))\n",
+    "      (set-cdr! s (lambda () result))\n",
+    "      result)))\n",
+    "\n",
+    "(define first\n",
+    "  (lambda (n s)\n",
+    "    (if (= n 0)\n",
+    "      '()\n",
+    "      (cons (scar s) (first (- n 1) (scdr s))))))\n",
+    "\n",
+    "(define nth\n",
+    "  (lambda (n s)\n",
+    "    (if (= n 0)\n",
+    "      (scar s)\n",
+    "      (nth (- n 1) (scdr s)))))\n",
+    "\n",
+    "(define smap\n",
+    "  (lambda (f s)\n",
+    "    (scons (f (scar s)) (smap f (scdr s)))))\n",
+    "\n",
+    "(define ones (scons 1 ones))\n",
+    "\n",
+    "(define nats (scons 0 (combine nats + ones)))\n",
+    "\n",
+    "(define combine\n",
+    "  (lambda (s1 op s2)\n",
+    "    (scons (op (scar s1) (scar s2)) (combine (scdr s1) op (scdr s2)))))\n",
+    "\n",
+    "(define fibs (scons 1 (scons 1 (combine fibs + (scdr fibs)))))\n",
+    "\n",
+    "(define facts (scons 1 (combine facts * (scdr nats))))\n",
+    "\n",
+    "(define ! (lambda (n) (nth n facts)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 37,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "120"
+      ]
+     },
+     "execution_count": 37,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(! 5)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 38,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3628800"
+      ]
+     },
+     "execution_count": 38,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(nth 10 facts)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 39,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "10946"
+      ]
+     },
+     "execution_count": 39,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(nth 20 fibs)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 40,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040)"
+      ]
+     },
+     "execution_count": 40,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(first 30 fibs)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## for-each\n",
+    "(for-each PROCEDURE LIST): apply PROCEDURE to each item in LIST; like `map` but don't return results"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 41,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "3\n",
+      "4\n",
+      "5\n"
+     ]
+    }
+   ],
+   "source": [
+    "(for-each (lambda (n) (print n)) '(3 4 5))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## format\n",
+    "(format STRING ITEM ...): format the string with ITEMS as arguments"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 42,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\"This uses formatting apple apple \\n\""
+      ]
+     },
+     "execution_count": 42,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(format \"This uses formatting ~a ~s ~%\" 'apple 'apple)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## func\n",
+    "\n",
+    "Turns a lambda into a Python function.\n",
+    "\n",
+    "(func (lambda ...))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 43,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<function dlr_func.<locals>.f at 0x7fc499d3c2f0>"
+      ]
+     },
+     "execution_count": 43,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(func (lambda (n) n))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## There's more!\n",
+    "\n",
+    "Please see [Calysto Scheme Language](Calysto%20Scheme%20Language.ipynb) for more details on the Calysto Scheme language."
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Calysto Scheme (Python)",
+   "language": "scheme",
+   "name": "calysto_scheme"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "scheme"
+   },
+   "mimetype": "text/x-scheme",
+   "name": "scheme",
+   "pygments_lexer": "scheme"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/tests/notebooks/mirror/ipynb_to_percent/Reference Guide for Calysto Scheme.ss b/tests/notebooks/mirror/ipynb_to_percent/Reference Guide for Calysto Scheme.ss
new file mode 100644
index 00000000..bfa24fbf
--- /dev/null
+++ b/tests/notebooks/mirror/ipynb_to_percent/Reference Guide for Calysto Scheme.ss
@@ -0,0 +1,449 @@
+;; -*- coding: utf-8 -*-
+;; ---
+;; jupyter:
+;;   kernelspec:
+;;     display_name: Calysto Scheme (Python)
+;;     language: scheme
+;;     name: calysto_scheme
+;;   language_info:
+;;     codemirror_mode:
+;;       name: scheme
+;;     mimetype: text/x-scheme
+;;     name: scheme
+;;     pygments_lexer: scheme
+;; ---
+
+;; %% [markdown]
+;; <img src="images/logo-64x64.png"/>
+;; <h1>Reference Guide for Calysto Scheme</h1>
+;;
+;; [Calysto Scheme](https://github.com/Calysto/calysto_scheme) is a real Scheme programming language, with full support for continuations, including call/cc. It can also use all Python libraries. Also has some extensions that make it more useful (stepper-debugger, choose/fail, stack traces), or make it better integrated with Python.
+;;
+;; In Jupyter notebooks, because Calysto Scheme uses [MetaKernel](https://github.com/Calysto/metakernel/blob/master/README.rst), it has a fully-supported set of "magics"---meta-commands for additional functionality. This includes running Scheme in parallel. See all of the [MetaKernel Magics](https://github.com/Calysto/metakernel/blob/master/metakernel/magics/README.md).
+;;
+;; Calysto Scheme is written in Scheme, and then translated into Python (and other backends). The entire functionality lies in a single Python file: https://github.com/Calysto/calysto_scheme/blob/master/calysto_scheme/scheme.py However, you can easily install it (see below).
+;;
+;; Please see [Calysto Scheme Language](Calysto%20Scheme%20Language.ipynb) for more details on the Calysto Scheme language.
+;;
+;; ## Installation
+;;
+;; You can install Calysto Scheme with Python3:
+;;
+;; ```
+;; pip3 install --upgrade calysto-scheme --user -U
+;; python3 -m calysto_kernel install --user
+;; ```
+;;
+;; or in the system kernel folder with:
+;;
+;; ```
+;; sudo pip3 install --upgrade calysto-scheme -U
+;; sudo python3 -m calysto_kernel install
+;; ```
+;;
+;; Change pip3/python3 to use a different pip or Python. The version of Python used will determine how Calysto Scheme is run.
+;;
+;; Use it in the console, qtconsole, or notebook with IPython 3:
+;;
+;; ```
+;; ipython console --kernel calysto_scheme
+;; ipython qtconsole --kernel calysto_scheme
+;; ipython notebook --kernel calysto_scheme
+;; ```
+;;
+;; In addition to all of the following items, Calysto Scheme also has access to all of Python's builtin functions, and all of Python's libraries. For example, you can use `(complex 3 2)` to create a complex number by calling Python's complex function.
+;;
+;; ## Jupyter Enhancements
+;;
+;; When you run Calysto Scheme in Jupyter (console, notebook, qtconsole, etc.) you get:
+;;
+;; * TAB completions of Scheme functions and variable names
+;; * display of rich media
+;; * stepper/debugger
+;; * magics (% macros)
+;; * shell commands (! command)
+;; * LaTeX equations
+;; * LaTeX-style variables
+;; * Python integration
+
+;; %% [markdown]
+;; ### LaTeX-style variables
+;;
+;; Calysto Scheme allows you to use LaTeX-style variables in code. For example, if you type:
+;;
+;; ```
+;; \beta
+;; ```
+;;
+;; with the cursor right after the 'a' in beta, and then press TAB, it will turn into the unicode character:
+;;
+;; ```
+;; β
+;; ```
+;;
+;; There are nearly 1300 different symbols defined (thanks to the Julia language) and documented here:
+;;
+;; http://docs.julialang.org/en/release-0.4/manual/unicode-input/#man-unicode-input
+;;
+;; Calysto Scheme may not implement all of those. Some useful and suggestive ones:
+;;
+;; * \pi - π
+;; * \Pi - Π
+;; * \Sigma - Σ
+;; * \_i - subscript i, such as vectorᵢ
+
+;; %%
+(define α 67)
+
+;; %%
+α
+
+;; %%
+(define i 2)
+(define vectorᵢ (vector-ref (vector 0 6 3 2) i))
+vectorᵢ
+
+;; %% [markdown]
+;; ### Rich media
+
+;; %%
+(import "calysto.display")
+
+;; %%
+(calysto.display.HTML "This is <b>bold</b>, <i>italics</i>, <u>underlined</u>.")
+
+;; %%
+(import "calysto.graphics")
+
+;; %%
+(define canvas (calysto.graphics.Canvas))
+
+;; %%
+(define ball (calysto.graphics.Circle '(150 150) 100))
+
+;; %%
+(ball.draw canvas)
+
+;; %% [markdown]
+;; ### Shell commands
+
+;; %%
+! ls /tmp
+
+;; %% [markdown]
+;; ### Stepper/Debugger
+;;
+;; Here is what the debugger looks like:
+;;
+;; <img src="images/stepper_debugger.png">
+;;
+;; It has breakpoints (click in left margin). You must press Stop to exit the debugger.
+
+;; %% [markdown]
+;; ```scheme
+;; %%debug
+;;
+;; (begin
+;;  (define x 1)
+;;  (set! x 2)
+;; )
+;; ```
+
+;; %% [markdown]
+;; ### Python Integration
+;;
+;; You can import and use any Python library in Calysto Scheme.
+;;
+;; In addition, if you wish, you can execute expressions and statements in a Python environment:
+
+;; %%
+(python-eval "1 + 2")
+
+;; %%
+(python-exec 
+"
+def mypyfunc(a, b):
+    return a * b
+")
+
+;; %% [markdown]
+;; This is a shared environment with Scheme:
+
+;; %%
+(mypyfunc 4 5)
+
+;; %% [markdown]
+;; You can use `func` to turn a Scheme procedure into a Python function, and `define!` to put it into the shared enviornment with Python:
+
+;; %%
+(define! mypyfunc2 (func (lambda (n) n)))
+
+;; %%
+(python-eval "mypyfunc2(34)")
+
+;; %% [markdown]
+;; # Differences Between Languages
+;;
+;; ## Major differences between Scheme and Python
+;;
+;; 1. In Scheme, double quotes are used for strings and may contain newlines
+;; 1. In Scheme, a single quote is short for (quote ...) and means "literal"
+;; 1. In Scheme, everything is an expression and has a return value
+;; 1. Python does not support macros (e.g., extending syntax)
+;; 1. In Python, "if X" is false if X is None, False, [], (,) or 0. In Scheme, "if X" is only false if X is #f or 0
+;; 1. Calysto Scheme uses continuations, not the call stack. However, for debugging there is a pseudo-stack when an error is raised. You can trun that off with (use-stack-trace #f)
+;; 1. Scheme procedures are not Python functions, but there are means to use one as the other.
+;;
+;; ## Major Differences Between Calysto Scheme and other Schemes
+;;
+;; 1. define-syntax works slightly differently
+;; 1. In Calysto Scheme, #(...) is short for '#(...)
+;; 1. Calysto Scheme is missing many standard functions (see list at bottom)
+;; 1. Calysto Scheme has a built-in amb operator called `choose`
+;; 1. For debugging there is a pseudo-stack when errors are raised in Calysto Scheme. You can trun that off with (use-stack-trace #f)
+;;
+;; ### Stack Trace
+;;
+;; Calysto Scheme acts as if it has a call stack, for easier debugging. For example:
+
+;; %%
+(define fact
+    (lambda (n)
+      (if (= n 1)
+           q
+           (* n (fact (- n 1))))))
+
+;; %%
+(fact 5)
+
+;; %% [markdown]
+;; To turn off the stack trace on error:
+;;
+;; ```scheme
+;; (use-stack-trace #f)
+;; ```
+;; That will allow infinite recursive loops without keeping track of the "stack".
+
+;; %% [markdown]
+;; # Calysto Scheme Variables
+;;
+;; ## SCHEMEPATH
+;; SCHEMEPATH is a list of search directories used with (load NAME). This is a reference, so you should append to it rather than attempting to redefine it.
+
+;; %%
+SCHEMEPATH
+
+;; %%
+(set-cdr! (cdr SCHEMEPATH) (list "/var/modules"))
+
+;; %%
+SCHEMEPATH
+
+;; %% [markdown]
+;; ## Getting Started
+;;
+;; Note that you can use the word `lambda` or \lambda and then press [TAB]
+
+;; %%
+(define factorial
+  (λ (n)
+     (cond
+      ((zero? n) 1)
+      (else (* n (factorial (- n 1)))))))
+
+;; %%
+(factorial 5)
+
+;; %% [markdown]
+;; ## define-syntax
+;; (define-syntax NAME RULES): a method for creating macros
+
+;; %%
+(define-syntax time 
+  [(time ?exp) (let ((start (current-time)))
+                 ?exp
+                 (- (current-time) start))])
+
+;; %%
+(time (car '(1 2 3 4)))
+
+;; %%
+;;---------------------------------------------------------------------
+;; collect is like list comprehension in Python
+
+(define-syntax collect
+  [(collect ?exp for ?var in ?list)
+   (filter-map (lambda (?var) ?exp) (lambda (?var) #t) ?list)]
+  [(collect ?exp for ?var in ?list if ?condition)
+   (filter-map (lambda (?var) ?exp) (lambda (?var) ?condition) ?list)])
+
+(define filter-map
+  (lambda (f pred? values)
+    (if (null? values)
+      '()
+      (if (pred? (car values))
+          (cons (f (car values)) (filter-map f pred? (cdr values)))
+          (filter-map f pred? (cdr values))))))
+
+;; %%
+(collect (* n n) for n in (range 10))
+
+;; %%
+(collect (* n n) for n in (range 5 20 3))
+
+;; %%
+(collect (* n n) for n in (range 10) if (> n 5))
+
+;; %%
+;;---------------------------------------------------------------------
+;; for loops
+
+(define-syntax for
+  [(for ?exp times do . ?bodies)
+   (for-repeat ?exp (lambda () . ?bodies))]
+  [(for ?var in ?exp do . ?bodies)
+   (for-iterate1 ?exp (lambda (?var) . ?bodies))]
+  [(for ?var at (?i) in ?exp do . ?bodies)
+   (for-iterate2 0 ?exp (lambda (?var ?i) . ?bodies))]
+  [(for ?var at (?i ?j . ?rest) in ?exp do . ?bodies)
+   (for ?var at (?i) in ?exp do
+     (for ?var at (?j . ?rest) in ?var do . ?bodies))])
+
+(define for-repeat
+  (lambda (n f)
+    (if (< n 1)
+      'done
+      (begin
+        (f)
+        (for-repeat (- n 1) f)))))
+
+(define for-iterate1
+  (lambda (values f)
+    (if (null? values)
+      'done
+      (begin
+        (f (car values))
+        (for-iterate1 (cdr values) f)))))
+
+(define for-iterate2
+  (lambda (i values f)
+    (if (null? values)
+      'done
+      (begin
+        (f (car values) i)
+        (for-iterate2 (+ i 1) (cdr values) f)))))
+
+;; %%
+(define matrix2d
+  '((10 20)
+    (30 40)
+    (50 60)
+    (70 80)))
+
+(define matrix3d
+  '(((10 20 30) (40 50 60))
+    ((70 80 90) (100 110 120))
+    ((130 140 150) (160 170 180))
+    ((190 200 210) (220 230 240))))
+
+;; %%
+(begin 
+ (define hello 0)
+ (for 5 times do (set! hello (+ hello 1)))
+ hello
+ )
+
+;; %%
+(for sym in '(a b c d) do (define x 1) (set! x sym) (print x))
+
+;; %%
+(for n in (range 10 20 2) do (print n))
+
+;; %%
+(for n at (i j) in matrix2d do (print (list n 'coords: i j)))
+
+;; %%
+(for n at (i j k) in matrix3d do (print (list n 'coords: i j k)))
+
+;; %%
+(define-syntax scons
+  [(scons ?x ?y) (cons ?x (lambda () ?y))])
+
+(define scar car)
+
+(define scdr
+  (lambda (s)
+    (let ((result ((cdr s))))
+      (set-cdr! s (lambda () result))
+      result)))
+
+(define first
+  (lambda (n s)
+    (if (= n 0)
+      '()
+      (cons (scar s) (first (- n 1) (scdr s))))))
+
+(define nth
+  (lambda (n s)
+    (if (= n 0)
+      (scar s)
+      (nth (- n 1) (scdr s)))))
+
+(define smap
+  (lambda (f s)
+    (scons (f (scar s)) (smap f (scdr s)))))
+
+(define ones (scons 1 ones))
+
+(define nats (scons 0 (combine nats + ones)))
+
+(define combine
+  (lambda (s1 op s2)
+    (scons (op (scar s1) (scar s2)) (combine (scdr s1) op (scdr s2)))))
+
+(define fibs (scons 1 (scons 1 (combine fibs + (scdr fibs)))))
+
+(define facts (scons 1 (combine facts * (scdr nats))))
+
+(define ! (lambda (n) (nth n facts)))
+
+;; %%
+(! 5)
+
+;; %%
+(nth 10 facts)
+
+;; %%
+(nth 20 fibs)
+
+;; %%
+(first 30 fibs)
+
+;; %% [markdown]
+;; ## for-each
+;; (for-each PROCEDURE LIST): apply PROCEDURE to each item in LIST; like `map` but don't return results
+
+;; %%
+(for-each (lambda (n) (print n)) '(3 4 5))
+
+;; %% [markdown]
+;; ## format
+;; (format STRING ITEM ...): format the string with ITEMS as arguments
+
+;; %%
+(format "This uses formatting ~a ~s ~%" 'apple 'apple)
+
+;; %% [markdown]
+;; ## func
+;;
+;; Turns a lambda into a Python function.
+;;
+;; (func (lambda ...))
+
+;; %%
+(func (lambda (n) n))
+
+;; %% [markdown]
+;; ## There's more!
+;;
+;; Please see [Calysto Scheme Language](Calysto%20Scheme%20Language.ipynb) for more details on the Calysto Scheme language.
diff --git a/tests/notebooks/mirror/ipynb_to_percent/xcpp_by_quantstack.cpp b/tests/notebooks/mirror/ipynb_to_percent/xcpp_by_quantstack.cpp
new file mode 100644
index 00000000..1862841d
--- /dev/null
+++ b/tests/notebooks/mirror/ipynb_to_percent/xcpp_by_quantstack.cpp
@@ -0,0 +1,445 @@
+// ---
+// jupyter:
+//   kernelspec:
+//     display_name: C++14
+//     language: C++14
+//     name: xeus-cling-cpp14
+//   language_info:
+//     codemirror_mode: text/x-c++src
+//     file_extension: .cpp
+//     mimetype: text/x-c++src
+//     name: c++
+//     version: -std=c++14
+// ---
+
+// %% [markdown]
+// [![xeus-cling](images/xeus-cling.png)](https://github.com/QuantStack/xeus-cling/)
+//
+// A Jupyter kernel for C++ based on the `cling` C++ interpreter and the `xeus` native implementation of the Jupyter protocol, xeus.
+//
+// - GitHub repository: https://github.com/QuantStack/xeus-cling/
+// - Online documentation: https://xeus-cling.readthedocs.io/
+
+// %% [markdown]
+// ## Usage
+//
+// <div style="background: #efffed;
+//             border: 1px solid grey;
+//             margin: 8px 0 8px 0;
+//             text-align: center;
+//             padding: 8px; ">
+//     <i class="fa-play fa" 
+//        style="font-size: 40px;
+//               line-height: 40px;
+//               margin: 8px;
+//               color: #444;">
+//     </i>
+//     <div>
+//     To run the selected code cell, hit <pre style="background: #efffed">Shift + Enter</pre>
+//     </div>
+// </div>
+
+// %% [markdown]
+// ## Output and error streams
+//
+// `std::cout` and `std::cerr` are redirected to the notebook frontend.
+
+// %%
+#include <iostream>
+
+std::cout << "some output" << std::endl;
+
+// %%
+std::cerr << "some error" << std::endl;
+
+// %%
+#include <stdexcept>
+
+// %%
+throw std::runtime_error("Unknown exception");
+
+// %% [markdown]
+// Omitting the `;` in the last statement of a cell results in an output being printed
+
+// %%
+int j = 5;
+
+// %%
+j
+
+// %% [markdown]
+// # Interpreting the C++ programming language
+//
+// `cling` has a broad support of the features of C++. You can define functions, classes, templates, etc ...
+
+// %% [markdown]
+// ## Functions
+
+// %%
+double sqr(double a)
+{
+    return a * a;
+}
+
+// %%
+double a = 2.5;
+double asqr = sqr(a);
+asqr
+
+// %% [markdown]
+// ## Classes
+
+// %%
+class Foo
+{
+public:
+
+    virtual ~Foo() {}
+    
+    virtual void print(double value) const
+    {
+        std::cout << "Foo value = " << value << std::endl;
+    }
+};
+
+// %%
+Foo bar;
+bar.print(1.2);
+
+// %% [markdown]
+// ## Polymorphism
+
+// %%
+class Bar : public Foo
+{
+public:
+
+    virtual ~Bar() {}
+    
+    virtual void print(double value) const
+    {
+        std::cout << "Bar value = " << 2 * value << std::endl;
+    }
+};
+
+// %%
+Foo* bar2 = new Bar;
+bar2->print(1.2);
+delete bar2;
+
+// %% [markdown]
+// ## Templates
+
+// %%
+#include <typeinfo>
+
+template <class T>
+class FooT
+{
+public:
+    
+    explicit FooT(const T& t) : m_t(t) {}
+    
+    void print() const
+    {
+        std::cout << typeid(T).name() << " m_t = " << m_t << std::endl;
+    }
+    
+private:
+    
+    T m_t;
+};
+
+template <>
+class FooT<int>
+{
+public:
+    
+    explicit FooT(const int& t) : m_t(t) {}
+    
+    void print() const
+    {
+        std::cout << "m_t = " << m_t << std::endl;
+    }
+    
+private:
+    
+    int m_t;
+};
+
+// %%
+FooT<double> foot1(1.2);
+foot1.print();
+
+// %%
+FooT<int> foot2(4);
+foot2.print();
+
+// %% [markdown]
+// ## C++11 / C++14 support
+
+// %%
+class Foo11
+{
+public:
+    
+    Foo11() { std::cout << "Foo11 default constructor" << std::endl; }
+    Foo11(const Foo11&) { std::cout << "Foo11 copy constructor" << std::endl; }
+    Foo11(Foo11&&) { std::cout << "Foo11 move constructor" << std::endl; }
+};
+
+// %%
+Foo11 f1;
+Foo11 f2(f1);
+Foo11 f3(std::move(f1));
+
+// %%
+#include <vector>
+
+std::vector<int> v = { 1, 2, 3};
+auto iter = ++v.begin();
+v
+
+// %%
+*iter
+
+// %% [markdown]
+// ... and also lambda, universal references, `decltype`, etc ...
+
+// %% [markdown]
+// ## Documentation and completion
+//
+//  - Documentation for types of the standard library is retrieved on cppreference.com.
+//  - The quick-help feature can also be enabled for user-defined types and third-party libraries. More documentation on this feature is available at https://xeus-cling.readthedocs.io/en/latest/inline_help.html.
+//
+
+// %%
+?std::vector
+
+// %% [markdown]
+// ## Using the `display_data` mechanism
+
+// %% [markdown]
+// For a user-defined type `T`, the rich rendering in the notebook and JupyterLab can be enabled by by implementing the function `xeus::xjson mime_bundle_repr(const T& im)`, which returns the JSON mime bundle for that type.
+//
+// More documentation on the rich display system of Jupyter and Xeus-cling is available at https://xeus-cling.readthedocs.io/en/latest/rich_display.html
+
+// %% [markdown]
+// ### Image example
+
+// %%
+#include <string>
+#include <fstream>
+
+#include "xtl/xbase64.hpp"
+#include "xeus/xjson.hpp"
+
+namespace im
+{
+    struct image
+    {   
+        inline image(const std::string& filename)
+        {
+            std::ifstream fin(filename, std::ios::binary);   
+            m_buffer << fin.rdbuf();
+        }
+        
+        std::stringstream m_buffer;
+    };
+    
+    xeus::xjson mime_bundle_repr(const image& i)
+    {
+        auto bundle = xeus::xjson::object();
+        bundle["image/png"] = xtl::base64encode(i.m_buffer.str());
+        return bundle;
+    }
+}
+
+// %%
+im::image marie("images/marie.png");
+marie
+
+// %% [markdown]
+// ### Audio example
+
+// %%
+#include <string>
+#include <fstream>
+
+#include "xtl/xbase64.hpp"
+#include "xeus/xjson.hpp"
+
+namespace au
+{
+    struct audio
+    {   
+        inline audio(const std::string& filename)
+        {
+            std::ifstream fin(filename, std::ios::binary);   
+            m_buffer << fin.rdbuf();
+        }
+        
+        std::stringstream m_buffer;
+    };
+    
+    xeus::xjson mime_bundle_repr(const audio& a)
+    {
+        auto bundle = xeus::xjson::object();
+        bundle["text/html"] =
+           std::string("<audio controls=\"controls\"><source src=\"data:audio/wav;base64,")
+           + xtl::base64encode(a.m_buffer.str()) +
+            "\" type=\"audio/wav\" /></audio>";
+        return bundle;
+    }
+}
+
+// %%
+au::audio drums("audio/audio.wav");
+drums
+
+// %% [markdown]
+// ### Display
+
+// %%
+#include "xcpp/xdisplay.hpp"
+
+// %%
+xcpp::display(drums);
+
+// %% [markdown]
+// ### Update-display
+
+// %%
+#include <string>
+#include "xcpp/xdisplay.hpp"
+
+namespace ht
+{
+    struct html
+    {   
+        inline html(const std::string& content)
+        {
+            m_content = content;
+        }
+        std::string m_content;
+    };
+
+    xeus::xjson mime_bundle_repr(const html& a)
+    {
+        auto bundle = xeus::xjson::object();
+        bundle["text/html"] = a.m_content;
+        return bundle;
+    }
+}
+
+// A red rectangle
+ht::html rect(R"(
+<div style='
+    width: 90px;
+    height: 50px;
+    line-height: 50px;
+    background-color: blue;
+    color: white;
+    text-align: center;'>
+Original
+</div>)");
+
+// %%
+xcpp::display(rect, "some_display_id");
+
+// %%
+// Update the rectangle to be blue
+rect.m_content = R"(
+<div style='
+    width: 90px;
+    height: 50px;
+    line-height: 50px;
+    background-color: red;
+    color: white;
+    text-align: center;'>
+Updated
+</div>)";
+
+xcpp::display(rect, "some_display_id", true);
+
+// %% [markdown]
+// ## Magics
+//
+// Magics are special commands for the kernel that are not part of the C++ language.
+//
+// They are defined with the symbol `%` for a line magic and `%%` for a cell magic.
+//
+// More documentation for magics is available at https://xeus-cling.readthedocs.io/en/latest/magics.html.
+
+// %%
+#include <algorithm>
+#include <vector>
+
+// %%
+std::vector<double> to_shuffle = {1, 2, 3, 4};
+
+// %%
+%timeit std::random_shuffle(to_shuffle.begin(), to_shuffle.end());
+
+// %% [markdown]
+// [![xtensor](images/xtensor.png)](https://github.com/QuantStack/xtensor/)
+//
+// - GitHub repository: https://github.com/QuantStack/xtensor/
+// - Online documentation: https://xtensor.readthedocs.io/
+// - NumPy to xtensor cheat sheet: http://xtensor.readthedocs.io/en/latest/numpy.html
+//
+// `xtensor` is a C++ library for manipulating N-D arrays with an API very similar to that of numpy.
+
+// %%
+#include <iostream>
+
+#include "xtensor/xarray.hpp"
+#include "xtensor/xio.hpp"
+#include "xtensor/xview.hpp"
+
+xt::xarray<double> arr1
+  {{1.0, 2.0, 3.0},
+   {2.0, 5.0, 7.0},
+   {2.0, 5.0, 7.0}};
+
+xt::xarray<double> arr2
+  {5.0, 6.0, 7.0};
+
+xt::view(arr1, 1) + arr2
+
+// %% [markdown]
+// Together with the C++ Jupyter kernel, `xtensor` offers a similar experience as `NumPy` in the Python Jupyter kernel, including broadcasting and universal functions.
+
+// %%
+#include <iostream>
+#include "xtensor/xarray.hpp"
+#include "xtensor/xio.hpp"
+
+// %%
+xt::xarray<int> arr
+  {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+arr.reshape({3, 3});
+
+std::cout << arr;
+
+// %%
+#include "xtensor-blas/xlinalg.hpp"
+
+// %%
+xt::xtensor<double, 2> m = {{1.5, 0.5}, {0.7, 1.0}};
+std::cout << "Matrix rank: " << std::endl << xt::linalg::matrix_rank(m) << std::endl;
+std::cout << "Matrix inverse: " << std::endl << xt::linalg::inv(m) << std::endl;
+std::cout << "Eigen values: " << std::endl << xt::linalg::eigvals(m) << std::endl;
+
+// %%
+xt::xarray<double> arg1 = xt::arange<double>(9);
+xt::xarray<double> arg2 = xt::arange<double>(18);
+
+arg1.reshape({3, 3});
+arg2.reshape({2, 3, 3});
+
+std::cout << xt::linalg::dot(arg1, arg2) << std::endl;
+
+// %%
+
diff --git a/tests/notebooks/mirror/ipynb_to_script/Reference Guide for Calysto Scheme.ss b/tests/notebooks/mirror/ipynb_to_script/Reference Guide for Calysto Scheme.ss
new file mode 100644
index 00000000..57461d60
--- /dev/null
+++ b/tests/notebooks/mirror/ipynb_to_script/Reference Guide for Calysto Scheme.ss
@@ -0,0 +1,395 @@
+;; -*- coding: utf-8 -*-
+;; ---
+;; jupyter:
+;;   kernelspec:
+;;     display_name: Calysto Scheme (Python)
+;;     language: scheme
+;;     name: calysto_scheme
+;;   language_info:
+;;     codemirror_mode:
+;;       name: scheme
+;;     mimetype: text/x-scheme
+;;     name: scheme
+;;     pygments_lexer: scheme
+;; ---
+
+;; <img src="images/logo-64x64.png"/>
+;; <h1>Reference Guide for Calysto Scheme</h1>
+;;
+;; [Calysto Scheme](https://github.com/Calysto/calysto_scheme) is a real Scheme programming language, with full support for continuations, including call/cc. It can also use all Python libraries. Also has some extensions that make it more useful (stepper-debugger, choose/fail, stack traces), or make it better integrated with Python.
+;;
+;; In Jupyter notebooks, because Calysto Scheme uses [MetaKernel](https://github.com/Calysto/metakernel/blob/master/README.rst), it has a fully-supported set of "magics"---meta-commands for additional functionality. This includes running Scheme in parallel. See all of the [MetaKernel Magics](https://github.com/Calysto/metakernel/blob/master/metakernel/magics/README.md).
+;;
+;; Calysto Scheme is written in Scheme, and then translated into Python (and other backends). The entire functionality lies in a single Python file: https://github.com/Calysto/calysto_scheme/blob/master/calysto_scheme/scheme.py However, you can easily install it (see below).
+;;
+;; Please see [Calysto Scheme Language](Calysto%20Scheme%20Language.ipynb) for more details on the Calysto Scheme language.
+;;
+;; ## Installation
+;;
+;; You can install Calysto Scheme with Python3:
+;;
+;; ```
+;; pip3 install --upgrade calysto-scheme --user -U
+;; python3 -m calysto_kernel install --user
+;; ```
+;;
+;; or in the system kernel folder with:
+;;
+;; ```
+;; sudo pip3 install --upgrade calysto-scheme -U
+;; sudo python3 -m calysto_kernel install
+;; ```
+;;
+;; Change pip3/python3 to use a different pip or Python. The version of Python used will determine how Calysto Scheme is run.
+;;
+;; Use it in the console, qtconsole, or notebook with IPython 3:
+;;
+;; ```
+;; ipython console --kernel calysto_scheme
+;; ipython qtconsole --kernel calysto_scheme
+;; ipython notebook --kernel calysto_scheme
+;; ```
+;;
+;; In addition to all of the following items, Calysto Scheme also has access to all of Python's builtin functions, and all of Python's libraries. For example, you can use `(complex 3 2)` to create a complex number by calling Python's complex function.
+;;
+;; ## Jupyter Enhancements
+;;
+;; When you run Calysto Scheme in Jupyter (console, notebook, qtconsole, etc.) you get:
+;;
+;; * TAB completions of Scheme functions and variable names
+;; * display of rich media
+;; * stepper/debugger
+;; * magics (% macros)
+;; * shell commands (! command)
+;; * LaTeX equations
+;; * LaTeX-style variables
+;; * Python integration
+
+;; ### LaTeX-style variables
+;;
+;; Calysto Scheme allows you to use LaTeX-style variables in code. For example, if you type:
+;;
+;; ```
+;; \beta
+;; ```
+;;
+;; with the cursor right after the 'a' in beta, and then press TAB, it will turn into the unicode character:
+;;
+;; ```
+;; β
+;; ```
+;;
+;; There are nearly 1300 different symbols defined (thanks to the Julia language) and documented here:
+;;
+;; http://docs.julialang.org/en/release-0.4/manual/unicode-input/#man-unicode-input
+;;
+;; Calysto Scheme may not implement all of those. Some useful and suggestive ones:
+;;
+;; * \pi - π
+;; * \Pi - Π
+;; * \Sigma - Σ
+;; * \_i - subscript i, such as vectorᵢ
+
+(define α 67)
+
+α
+
+(define i 2)
+(define vectorᵢ (vector-ref (vector 0 6 3 2) i))
+vectorᵢ
+
+;; ### Rich media
+
+(import "calysto.display")
+
+(calysto.display.HTML "This is <b>bold</b>, <i>italics</i>, <u>underlined</u>.")
+
+(import "calysto.graphics")
+
+(define canvas (calysto.graphics.Canvas))
+
+(define ball (calysto.graphics.Circle '(150 150) 100))
+
+(ball.draw canvas)
+
+;; ### Shell commands
+
+! ls /tmp
+
+;; ### Stepper/Debugger
+;;
+;; Here is what the debugger looks like:
+;;
+;; <img src="images/stepper_debugger.png">
+;;
+;; It has breakpoints (click in left margin). You must press Stop to exit the debugger.
+
+;; ```scheme
+;; %%debug
+;;
+;; (begin
+;;  (define x 1)
+;;  (set! x 2)
+;; )
+;; ```
+
+;; ### Python Integration
+;;
+;; You can import and use any Python library in Calysto Scheme.
+;;
+;; In addition, if you wish, you can execute expressions and statements in a Python environment:
+
+(python-eval "1 + 2")
+
+(python-exec 
+"
+def mypyfunc(a, b):
+    return a * b
+")
+
+;; This is a shared environment with Scheme:
+
+(mypyfunc 4 5)
+
+;; You can use `func` to turn a Scheme procedure into a Python function, and `define!` to put it into the shared enviornment with Python:
+
+(define! mypyfunc2 (func (lambda (n) n)))
+
+(python-eval "mypyfunc2(34)")
+
+;; # Differences Between Languages
+;;
+;; ## Major differences between Scheme and Python
+;;
+;; 1. In Scheme, double quotes are used for strings and may contain newlines
+;; 1. In Scheme, a single quote is short for (quote ...) and means "literal"
+;; 1. In Scheme, everything is an expression and has a return value
+;; 1. Python does not support macros (e.g., extending syntax)
+;; 1. In Python, "if X" is false if X is None, False, [], (,) or 0. In Scheme, "if X" is only false if X is #f or 0
+;; 1. Calysto Scheme uses continuations, not the call stack. However, for debugging there is a pseudo-stack when an error is raised. You can trun that off with (use-stack-trace #f)
+;; 1. Scheme procedures are not Python functions, but there are means to use one as the other.
+;;
+;; ## Major Differences Between Calysto Scheme and other Schemes
+;;
+;; 1. define-syntax works slightly differently
+;; 1. In Calysto Scheme, #(...) is short for '#(...)
+;; 1. Calysto Scheme is missing many standard functions (see list at bottom)
+;; 1. Calysto Scheme has a built-in amb operator called `choose`
+;; 1. For debugging there is a pseudo-stack when errors are raised in Calysto Scheme. You can trun that off with (use-stack-trace #f)
+;;
+;; ### Stack Trace
+;;
+;; Calysto Scheme acts as if it has a call stack, for easier debugging. For example:
+
+(define fact
+    (lambda (n)
+      (if (= n 1)
+           q
+           (* n (fact (- n 1))))))
+
+(fact 5)
+
+;; To turn off the stack trace on error:
+;;
+;; ```scheme
+;; (use-stack-trace #f)
+;; ```
+;; That will allow infinite recursive loops without keeping track of the "stack".
+
+;; # Calysto Scheme Variables
+;;
+;; ## SCHEMEPATH
+;; SCHEMEPATH is a list of search directories used with (load NAME). This is a reference, so you should append to it rather than attempting to redefine it.
+
+SCHEMEPATH
+
+(set-cdr! (cdr SCHEMEPATH) (list "/var/modules"))
+
+SCHEMEPATH
+
+;; ## Getting Started
+;;
+;; Note that you can use the word `lambda` or \lambda and then press [TAB]
+
+(define factorial
+  (λ (n)
+     (cond
+      ((zero? n) 1)
+      (else (* n (factorial (- n 1)))))))
+
+(factorial 5)
+
+;; ## define-syntax
+;; (define-syntax NAME RULES): a method for creating macros
+
+(define-syntax time 
+  [(time ?exp) (let ((start (current-time)))
+                 ?exp
+                 (- (current-time) start))])
+
+(time (car '(1 2 3 4)))
+
+;; +
+;;---------------------------------------------------------------------
+;; collect is like list comprehension in Python
+
+(define-syntax collect
+  [(collect ?exp for ?var in ?list)
+   (filter-map (lambda (?var) ?exp) (lambda (?var) #t) ?list)]
+  [(collect ?exp for ?var in ?list if ?condition)
+   (filter-map (lambda (?var) ?exp) (lambda (?var) ?condition) ?list)])
+
+(define filter-map
+  (lambda (f pred? values)
+    (if (null? values)
+      '()
+      (if (pred? (car values))
+          (cons (f (car values)) (filter-map f pred? (cdr values)))
+          (filter-map f pred? (cdr values))))))
+;; -
+
+(collect (* n n) for n in (range 10))
+
+(collect (* n n) for n in (range 5 20 3))
+
+(collect (* n n) for n in (range 10) if (> n 5))
+
+;; +
+;;---------------------------------------------------------------------
+;; for loops
+
+(define-syntax for
+  [(for ?exp times do . ?bodies)
+   (for-repeat ?exp (lambda () . ?bodies))]
+  [(for ?var in ?exp do . ?bodies)
+   (for-iterate1 ?exp (lambda (?var) . ?bodies))]
+  [(for ?var at (?i) in ?exp do . ?bodies)
+   (for-iterate2 0 ?exp (lambda (?var ?i) . ?bodies))]
+  [(for ?var at (?i ?j . ?rest) in ?exp do . ?bodies)
+   (for ?var at (?i) in ?exp do
+     (for ?var at (?j . ?rest) in ?var do . ?bodies))])
+
+(define for-repeat
+  (lambda (n f)
+    (if (< n 1)
+      'done
+      (begin
+        (f)
+        (for-repeat (- n 1) f)))))
+
+(define for-iterate1
+  (lambda (values f)
+    (if (null? values)
+      'done
+      (begin
+        (f (car values))
+        (for-iterate1 (cdr values) f)))))
+
+(define for-iterate2
+  (lambda (i values f)
+    (if (null? values)
+      'done
+      (begin
+        (f (car values) i)
+        (for-iterate2 (+ i 1) (cdr values) f)))))
+
+;; +
+(define matrix2d
+  '((10 20)
+    (30 40)
+    (50 60)
+    (70 80)))
+
+(define matrix3d
+  '(((10 20 30) (40 50 60))
+    ((70 80 90) (100 110 120))
+    ((130 140 150) (160 170 180))
+    ((190 200 210) (220 230 240))))
+;; -
+
+(begin 
+ (define hello 0)
+ (for 5 times do (set! hello (+ hello 1)))
+ hello
+ )
+
+(for sym in '(a b c d) do (define x 1) (set! x sym) (print x))
+
+(for n in (range 10 20 2) do (print n))
+
+(for n at (i j) in matrix2d do (print (list n 'coords: i j)))
+
+(for n at (i j k) in matrix3d do (print (list n 'coords: i j k)))
+
+;; +
+(define-syntax scons
+  [(scons ?x ?y) (cons ?x (lambda () ?y))])
+
+(define scar car)
+
+(define scdr
+  (lambda (s)
+    (let ((result ((cdr s))))
+      (set-cdr! s (lambda () result))
+      result)))
+
+(define first
+  (lambda (n s)
+    (if (= n 0)
+      '()
+      (cons (scar s) (first (- n 1) (scdr s))))))
+
+(define nth
+  (lambda (n s)
+    (if (= n 0)
+      (scar s)
+      (nth (- n 1) (scdr s)))))
+
+(define smap
+  (lambda (f s)
+    (scons (f (scar s)) (smap f (scdr s)))))
+
+(define ones (scons 1 ones))
+
+(define nats (scons 0 (combine nats + ones)))
+
+(define combine
+  (lambda (s1 op s2)
+    (scons (op (scar s1) (scar s2)) (combine (scdr s1) op (scdr s2)))))
+
+(define fibs (scons 1 (scons 1 (combine fibs + (scdr fibs)))))
+
+(define facts (scons 1 (combine facts * (scdr nats))))
+
+(define ! (lambda (n) (nth n facts)))
+;; -
+
+(! 5)
+
+(nth 10 facts)
+
+(nth 20 fibs)
+
+(first 30 fibs)
+
+;; ## for-each
+;; (for-each PROCEDURE LIST): apply PROCEDURE to each item in LIST; like `map` but don't return results
+
+(for-each (lambda (n) (print n)) '(3 4 5))
+
+;; ## format
+;; (format STRING ITEM ...): format the string with ITEMS as arguments
+
+(format "This uses formatting ~a ~s ~%" 'apple 'apple)
+
+;; ## func
+;;
+;; Turns a lambda into a Python function.
+;;
+;; (func (lambda ...))
+
+(func (lambda (n) n))
+
+;; ## There's more!
+;;
+;; Please see [Calysto Scheme Language](Calysto%20Scheme%20Language.ipynb) for more details on the Calysto Scheme language.
diff --git a/tests/notebooks/mirror/ipynb_to_script/julia_benchmark_plotly_barchart.jl b/tests/notebooks/mirror/ipynb_to_script/julia_benchmark_plotly_barchart.jl
index c608ed1b..e67f1849 100644
--- a/tests/notebooks/mirror/ipynb_to_script/julia_benchmark_plotly_barchart.jl
+++ b/tests/notebooks/mirror/ipynb_to_script/julia_benchmark_plotly_barchart.jl
@@ -16,97 +16,97 @@
 #     version: 3.6.6
 # ---
 
-# + {"active": "ipynb", "language": "python"}
-# # IJulia rocks! So does Plotly. Check it out
-#
-# using Plotly
-# api_key = "" # visit https://plot.ly/api to generate an API username and password
-# username = ""
-#
-# Plotly.signin(username, api_key)
+# +
+# IJulia rocks! So does Plotly. Check it out
 
-# + {"active": "ipynb", "language": "python"}
-# # Following data taken from http://julialang.org/ frontpage 
-# benchmarks = ["fib", "parse_int", "quicksort3", "mandel", "pi_sum", "rand_mat_stat", "rand_mat_mul"]
-# platforms = ["Fortran", "Julia", "Python", "R", "Matlab", "Mathematica", "Javascript", "Go"]
-#
-# data = {
-#         platforms[1] => [0.26, 5.03, 1.11, 0.86, 0.80, 0.64, 0.96],
-#         platforms[2] => [0.91, 1.60, 1.14, 0.85, 1.00, 1.66, 1.01],
-#         platforms[3] => [30.37, 13.95, 31.98, 14.19, 16.33, 13.52, 3.41 ],
-#         platforms[4] => [411.36, 59.40, 524.29, 106.97, 15.42, 10.84, 3.98  ],
-#         platforms[5] => [1992.00, 1463.16, 101.84, 64.58, 1.29, 6.61, 1.10   ],
-#         platforms[6] => [64.46, 29.54, 35.74, 6.07, 1.32, 4.52, 1.16 ],
-#         platforms[7] => [2.18, 2.43, 3.51, 3.49, 0.84, 3.28, 14.60],
-#         platforms[8] => [1.03, 4.79, 1.25, 2.36, 1.41, 8.12, 8.51]
-#         }
-#
-# pdata = [ {"x"=>benchmarks,"y"=>data[k],"bardir"=>"h","type"=>"bar","name"=>k} for k = platforms ]
-#
-# layout = {
-#           "title"=> "Julia benchmark comparison (smaller is better, C performance = 1.0)",
-#           "barmode"=> "group",
-#           "autosize"=> false,
-#           "width"=> 900,
-#           "height"=> 900,
-#           "titlefont"=>
-#           {
-#            "family"=> "Open Sans",
-#            "size"=> 18,
-#            "color"=> "rgb(84, 39, 143)"
-#            },
-#           "margin"=> {"l"=>160, "pad"=>0},
-#           "xaxis"=> {
-#                      "title"=> "Benchmark log-time",
-#                      "type"=> "log"
-#                      },
-#           "yaxis"=> {"title"=> "Benchmark Name"}
-#           }
-#
-# response = Plotly.plot(pdata,["layout"=>layout])
-#
-# # Embed in an iframe within IJulia
-# s = string("<iframe height='750' id='igraph' scrolling='no' seamless='seamless' src='",
-#             response["url"],
-#             "/700/700' width='750'></iframe>")
-# display("text/html", s)
+using Plotly
+api_key = "" # visit https://plot.ly/api to generate an API username and password
+username = ""
 
-# + {"active": "ipynb", "language": "python"}
-# # checkout https://plot.ly/api/ for more Julia examples!
-# # But to show off some other Plotly features:
-# x = 1:1500
-# y1 = sin(2*pi*x/1500.) + rand(1500)-0.5
-# y2 = sin(2*pi*x/1500.)
-#
-# fish = {"x"=>x,"y"=> y1,
-# 	"type"=>"scatter","mode"=>"markers",
-# 	"marker"=>{"color"=>"rgb(0, 0, 255)","opacity"=>0.5 } }
-#
-# fit = {"x"=> x,"y"=> y2,
-# 	"type"=>"scatter", "mode"=>"markers", "opacity"=>0.8,
-# 	"marker"=>{"color"=>"rgb(255, 0, 0)"} }
-#
-# layout = {"autosize"=> false,
-#     "width"=> 650, "height"=> 550,
-#     "title"=>"Fish School",
-# 	"xaxis"=>{ "ticks"=> "",
-#         "gridcolor"=> "white",
-#         "zerolinecolor"=> "white",    
-#         "linecolor"=> "white",
-#         "autorange"=> false,
-#         "range"=>[0,1500] },
-# 	"yaxis"=>{ "ticks"=> "",
-#         "gridcolor"=> "white",
-#         "zerolinecolor"=> "white",
-# 		"linecolor"=> "white",
-#         "autorange"=> false,
-#         "range"=>[-2.2,2.2] },
-# 	"plot_bgcolor"=> "rgb(245,245,247)",
-#     "showlegend"=> false,
-#     "hovermode"=> "closest"}
-#
-# response = Plotly.plot([fish, fit],["layout"=>layout])
-# s = string("<iframe height='750' id='igraph' scrolling='no' seamless='seamless' src='",
-#             response["url"],
-#             "/700/700' width='750'></iframe>")
-# display("text/html", s)
+Plotly.signin(username, api_key)
+
+# +
+# Following data taken from http://julialang.org/ frontpage 
+benchmarks = ["fib", "parse_int", "quicksort3", "mandel", "pi_sum", "rand_mat_stat", "rand_mat_mul"]
+platforms = ["Fortran", "Julia", "Python", "R", "Matlab", "Mathematica", "Javascript", "Go"]
+
+data = {
+        platforms[1] => [0.26, 5.03, 1.11, 0.86, 0.80, 0.64, 0.96],
+        platforms[2] => [0.91, 1.60, 1.14, 0.85, 1.00, 1.66, 1.01],
+        platforms[3] => [30.37, 13.95, 31.98, 14.19, 16.33, 13.52, 3.41 ],
+        platforms[4] => [411.36, 59.40, 524.29, 106.97, 15.42, 10.84, 3.98  ],
+        platforms[5] => [1992.00, 1463.16, 101.84, 64.58, 1.29, 6.61, 1.10   ],
+        platforms[6] => [64.46, 29.54, 35.74, 6.07, 1.32, 4.52, 1.16 ],
+        platforms[7] => [2.18, 2.43, 3.51, 3.49, 0.84, 3.28, 14.60],
+        platforms[8] => [1.03, 4.79, 1.25, 2.36, 1.41, 8.12, 8.51]
+        }
+
+pdata = [ {"x"=>benchmarks,"y"=>data[k],"bardir"=>"h","type"=>"bar","name"=>k} for k = platforms ]
+
+layout = {
+          "title"=> "Julia benchmark comparison (smaller is better, C performance = 1.0)",
+          "barmode"=> "group",
+          "autosize"=> false,
+          "width"=> 900,
+          "height"=> 900,
+          "titlefont"=>
+          {
+           "family"=> "Open Sans",
+           "size"=> 18,
+           "color"=> "rgb(84, 39, 143)"
+           },
+          "margin"=> {"l"=>160, "pad"=>0},
+          "xaxis"=> {
+                     "title"=> "Benchmark log-time",
+                     "type"=> "log"
+                     },
+          "yaxis"=> {"title"=> "Benchmark Name"}
+          }
+
+response = Plotly.plot(pdata,["layout"=>layout])
+
+# Embed in an iframe within IJulia
+s = string("<iframe height='750' id='igraph' scrolling='no' seamless='seamless' src='",
+            response["url"],
+            "/700/700' width='750'></iframe>")
+display("text/html", s)
+
+# +
+# checkout https://plot.ly/api/ for more Julia examples!
+# But to show off some other Plotly features:
+x = 1:1500
+y1 = sin(2*pi*x/1500.) + rand(1500)-0.5
+y2 = sin(2*pi*x/1500.)
+
+fish = {"x"=>x,"y"=> y1,
+	"type"=>"scatter","mode"=>"markers",
+	"marker"=>{"color"=>"rgb(0, 0, 255)","opacity"=>0.5 } }
+
+fit = {"x"=> x,"y"=> y2,
+	"type"=>"scatter", "mode"=>"markers", "opacity"=>0.8,
+	"marker"=>{"color"=>"rgb(255, 0, 0)"} }
+
+layout = {"autosize"=> false,
+    "width"=> 650, "height"=> 550,
+    "title"=>"Fish School",
+	"xaxis"=>{ "ticks"=> "",
+        "gridcolor"=> "white",
+        "zerolinecolor"=> "white",    
+        "linecolor"=> "white",
+        "autorange"=> false,
+        "range"=>[0,1500] },
+	"yaxis"=>{ "ticks"=> "",
+        "gridcolor"=> "white",
+        "zerolinecolor"=> "white",
+		"linecolor"=> "white",
+        "autorange"=> false,
+        "range"=>[-2.2,2.2] },
+	"plot_bgcolor"=> "rgb(245,245,247)",
+    "showlegend"=> false,
+    "hovermode"=> "closest"}
+
+response = Plotly.plot([fish, fit],["layout"=>layout])
+s = string("<iframe height='750' id='igraph' scrolling='no' seamless='seamless' src='",
+            response["url"],
+            "/700/700' width='750'></iframe>")
+display("text/html", s)
diff --git a/tests/notebooks/mirror/ipynb_to_script/xcpp_by_quantstack.cpp b/tests/notebooks/mirror/ipynb_to_script/xcpp_by_quantstack.cpp
new file mode 100644
index 00000000..1eda116b
--- /dev/null
+++ b/tests/notebooks/mirror/ipynb_to_script/xcpp_by_quantstack.cpp
@@ -0,0 +1,405 @@
+// ---
+// jupyter:
+//   kernelspec:
+//     display_name: C++14
+//     language: C++14
+//     name: xeus-cling-cpp14
+//   language_info:
+//     codemirror_mode: text/x-c++src
+//     file_extension: .cpp
+//     mimetype: text/x-c++src
+//     name: c++
+//     version: -std=c++14
+// ---
+
+// [![xeus-cling](images/xeus-cling.png)](https://github.com/QuantStack/xeus-cling/)
+//
+// A Jupyter kernel for C++ based on the `cling` C++ interpreter and the `xeus` native implementation of the Jupyter protocol, xeus.
+//
+// - GitHub repository: https://github.com/QuantStack/xeus-cling/
+// - Online documentation: https://xeus-cling.readthedocs.io/
+
+// ## Usage
+//
+// <div style="background: #efffed;
+//             border: 1px solid grey;
+//             margin: 8px 0 8px 0;
+//             text-align: center;
+//             padding: 8px; ">
+//     <i class="fa-play fa" 
+//        style="font-size: 40px;
+//               line-height: 40px;
+//               margin: 8px;
+//               color: #444;">
+//     </i>
+//     <div>
+//     To run the selected code cell, hit <pre style="background: #efffed">Shift + Enter</pre>
+//     </div>
+// </div>
+
+// ## Output and error streams
+//
+// `std::cout` and `std::cerr` are redirected to the notebook frontend.
+
+// +
+#include <iostream>
+
+std::cout << "some output" << std::endl;
+// -
+
+std::cerr << "some error" << std::endl;
+
+#include <stdexcept>
+
+throw std::runtime_error("Unknown exception");
+
+// Omitting the `;` in the last statement of a cell results in an output being printed
+
+int j = 5;
+
+j
+
+// # Interpreting the C++ programming language
+//
+// `cling` has a broad support of the features of C++. You can define functions, classes, templates, etc ...
+
+// ## Functions
+
+double sqr(double a)
+{
+    return a * a;
+}
+
+double a = 2.5;
+double asqr = sqr(a);
+asqr
+
+// ## Classes
+
+class Foo
+{
+public:
+
+    virtual ~Foo() {}
+    
+    virtual void print(double value) const
+    {
+        std::cout << "Foo value = " << value << std::endl;
+    }
+};
+
+Foo bar;
+bar.print(1.2);
+
+// ## Polymorphism
+
+class Bar : public Foo
+{
+public:
+
+    virtual ~Bar() {}
+    
+    virtual void print(double value) const
+    {
+        std::cout << "Bar value = " << 2 * value << std::endl;
+    }
+};
+
+Foo* bar2 = new Bar;
+bar2->print(1.2);
+delete bar2;
+
+// ## Templates
+
+// +
+#include <typeinfo>
+
+template <class T>
+class FooT
+{
+public:
+    
+    explicit FooT(const T& t) : m_t(t) {}
+    
+    void print() const
+    {
+        std::cout << typeid(T).name() << " m_t = " << m_t << std::endl;
+    }
+    
+private:
+    
+    T m_t;
+};
+
+template <>
+class FooT<int>
+{
+public:
+    
+    explicit FooT(const int& t) : m_t(t) {}
+    
+    void print() const
+    {
+        std::cout << "m_t = " << m_t << std::endl;
+    }
+    
+private:
+    
+    int m_t;
+};
+// -
+
+FooT<double> foot1(1.2);
+foot1.print();
+
+FooT<int> foot2(4);
+foot2.print();
+
+// ## C++11 / C++14 support
+
+class Foo11
+{
+public:
+    
+    Foo11() { std::cout << "Foo11 default constructor" << std::endl; }
+    Foo11(const Foo11&) { std::cout << "Foo11 copy constructor" << std::endl; }
+    Foo11(Foo11&&) { std::cout << "Foo11 move constructor" << std::endl; }
+};
+
+Foo11 f1;
+Foo11 f2(f1);
+Foo11 f3(std::move(f1));
+
+// +
+#include <vector>
+
+std::vector<int> v = { 1, 2, 3};
+auto iter = ++v.begin();
+v
+// -
+
+*iter
+
+// ... and also lambda, universal references, `decltype`, etc ...
+
+// ## Documentation and completion
+//
+//  - Documentation for types of the standard library is retrieved on cppreference.com.
+//  - The quick-help feature can also be enabled for user-defined types and third-party libraries. More documentation on this feature is available at https://xeus-cling.readthedocs.io/en/latest/inline_help.html.
+//
+
+?std::vector
+
+// ## Using the `display_data` mechanism
+
+// For a user-defined type `T`, the rich rendering in the notebook and JupyterLab can be enabled by by implementing the function `xeus::xjson mime_bundle_repr(const T& im)`, which returns the JSON mime bundle for that type.
+//
+// More documentation on the rich display system of Jupyter and Xeus-cling is available at https://xeus-cling.readthedocs.io/en/latest/rich_display.html
+
+// ### Image example
+
+// +
+#include <string>
+#include <fstream>
+
+#include "xtl/xbase64.hpp"
+#include "xeus/xjson.hpp"
+
+namespace im
+{
+    struct image
+    {   
+        inline image(const std::string& filename)
+        {
+            std::ifstream fin(filename, std::ios::binary);   
+            m_buffer << fin.rdbuf();
+        }
+        
+        std::stringstream m_buffer;
+    };
+    
+    xeus::xjson mime_bundle_repr(const image& i)
+    {
+        auto bundle = xeus::xjson::object();
+        bundle["image/png"] = xtl::base64encode(i.m_buffer.str());
+        return bundle;
+    }
+}
+// -
+
+im::image marie("images/marie.png");
+marie
+
+// ### Audio example
+
+// +
+#include <string>
+#include <fstream>
+
+#include "xtl/xbase64.hpp"
+#include "xeus/xjson.hpp"
+
+namespace au
+{
+    struct audio
+    {   
+        inline audio(const std::string& filename)
+        {
+            std::ifstream fin(filename, std::ios::binary);   
+            m_buffer << fin.rdbuf();
+        }
+        
+        std::stringstream m_buffer;
+    };
+    
+    xeus::xjson mime_bundle_repr(const audio& a)
+    {
+        auto bundle = xeus::xjson::object();
+        bundle["text/html"] =
+           std::string("<audio controls=\"controls\"><source src=\"data:audio/wav;base64,")
+           + xtl::base64encode(a.m_buffer.str()) +
+            "\" type=\"audio/wav\" /></audio>";
+        return bundle;
+    }
+}
+// -
+
+au::audio drums("audio/audio.wav");
+drums
+
+// ### Display
+
+#include "xcpp/xdisplay.hpp"
+
+xcpp::display(drums);
+
+// ### Update-display
+
+// +
+#include <string>
+#include "xcpp/xdisplay.hpp"
+
+namespace ht
+{
+    struct html
+    {   
+        inline html(const std::string& content)
+        {
+            m_content = content;
+        }
+        std::string m_content;
+    };
+
+    xeus::xjson mime_bundle_repr(const html& a)
+    {
+        auto bundle = xeus::xjson::object();
+        bundle["text/html"] = a.m_content;
+        return bundle;
+    }
+}
+
+// A red rectangle
+ht::html rect(R"(
+<div style='
+    width: 90px;
+    height: 50px;
+    line-height: 50px;
+    background-color: blue;
+    color: white;
+    text-align: center;'>
+Original
+</div>)");
+// -
+
+xcpp::display(rect, "some_display_id");
+
+// +
+// Update the rectangle to be blue
+rect.m_content = R"(
+<div style='
+    width: 90px;
+    height: 50px;
+    line-height: 50px;
+    background-color: red;
+    color: white;
+    text-align: center;'>
+Updated
+</div>)";
+
+xcpp::display(rect, "some_display_id", true);
+// -
+
+// ## Magics
+//
+// Magics are special commands for the kernel that are not part of the C++ language.
+//
+// They are defined with the symbol `%` for a line magic and `%%` for a cell magic.
+//
+// More documentation for magics is available at https://xeus-cling.readthedocs.io/en/latest/magics.html.
+
+#include <algorithm>
+#include <vector>
+
+std::vector<double> to_shuffle = {1, 2, 3, 4};
+
+# %timeit std::random_shuffle(to_shuffle.begin(), to_shuffle.end());
+
+// [![xtensor](images/xtensor.png)](https://github.com/QuantStack/xtensor/)
+//
+// - GitHub repository: https://github.com/QuantStack/xtensor/
+// - Online documentation: https://xtensor.readthedocs.io/
+// - NumPy to xtensor cheat sheet: http://xtensor.readthedocs.io/en/latest/numpy.html
+//
+// `xtensor` is a C++ library for manipulating N-D arrays with an API very similar to that of numpy.
+
+// +
+#include <iostream>
+
+#include "xtensor/xarray.hpp"
+#include "xtensor/xio.hpp"
+#include "xtensor/xview.hpp"
+
+xt::xarray<double> arr1
+  {{1.0, 2.0, 3.0},
+   {2.0, 5.0, 7.0},
+   {2.0, 5.0, 7.0}};
+
+xt::xarray<double> arr2
+  {5.0, 6.0, 7.0};
+
+xt::view(arr1, 1) + arr2
+// -
+
+// Together with the C++ Jupyter kernel, `xtensor` offers a similar experience as `NumPy` in the Python Jupyter kernel, including broadcasting and universal functions.
+
+#include <iostream>
+#include "xtensor/xarray.hpp"
+#include "xtensor/xio.hpp"
+
+// +
+xt::xarray<int> arr
+  {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+arr.reshape({3, 3});
+
+std::cout << arr;
+// -
+
+#include "xtensor-blas/xlinalg.hpp"
+
+xt::xtensor<double, 2> m = {{1.5, 0.5}, {0.7, 1.0}};
+std::cout << "Matrix rank: " << std::endl << xt::linalg::matrix_rank(m) << std::endl;
+std::cout << "Matrix inverse: " << std::endl << xt::linalg::inv(m) << std::endl;
+std::cout << "Eigen values: " << std::endl << xt::linalg::eigvals(m) << std::endl;
+
+// +
+xt::xarray<double> arg1 = xt::arange<double>(9);
+xt::xarray<double> arg2 = xt::arange<double>(18);
+
+arg1.reshape({3, 3});
+arg2.reshape({2, 3, 3});
+
+std::cout << xt::linalg::dot(arg1, arg2) << std::endl;
+// -
+
+
diff --git a/tests/notebooks/mirror/script_to_ipynb/light_sample.ipynb b/tests/notebooks/mirror/script_to_ipynb/light_sample.ipynb
new file mode 100644
index 00000000..b3866a10
--- /dev/null
+++ b/tests/notebooks/mirror/script_to_ipynb/light_sample.ipynb
@@ -0,0 +1,29 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Sample notebook"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "a = 1\n",
+    "b = 2\n",
+    "a + b"
+   ]
+  }
+ ],
+ "metadata": {
+  "jupytext_format_version": "1.2",
+  "jupytext_formats": "ipynb,py",
+  "main_language": "python"
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/tests/notebooks/python/light_sample.py b/tests/notebooks/python/light_sample.py
new file mode 100644
index 00000000..f64e8f2d
--- /dev/null
+++ b/tests/notebooks/python/light_sample.py
@@ -0,0 +1,11 @@
+# ---
+# jupyter:
+#   jupytext_format_version: '1.2'
+#   jupytext_formats: ipynb,py
+# ---
+
+# # Sample notebook
+
+a = 1
+b = 2
+a + b
diff --git a/tests/test_cli.py b/tests/test_cli.py
index ba246dee..aaaa1cd3 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -50,6 +50,17 @@ def test_convert_single_file(nb_file, capsys):
     compare(out, pynb)
 
 
+@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_cpp'))
+def test_to_cpluplus(nb_file, capsys):
+    nb1 = readf(nb_file)
+    text_cpp = writes(nb1, ext='.cpp')
+    jupytext([nb_file, '--to', 'c++', '--output', '-'])
+
+    out, err = capsys.readouterr()
+    assert err == ''
+    compare(out, text_cpp)
+
+
 @pytest.mark.parametrize('nb_files', [list_notebooks('ipynb_py')])
 def test_convert_multiple_file(nb_files, tmpdir):
     nb_orgs = []
diff --git a/tests/test_contentsmanager.py b/tests/test_contentsmanager.py
index 5b89e2d5..31e346bc 100644
--- a/tests/test_contentsmanager.py
+++ b/tests/test_contentsmanager.py
@@ -1,6 +1,7 @@
 # coding: utf-8
 
 import os
+import shutil
 import time
 import pytest
 import mock
@@ -75,6 +76,34 @@ def test_load_save_rename_nbpy(nb_file, tmpdir):
     assert os.path.isfile(str(tmpdir.join('new.nb.py')))
 
 
+@skip_if_dict_is_not_ordered
+@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_py'))
+def test_load_save_rename_notebook_with_dot(nb_file, tmpdir):
+    tmp_ipynb = '1.notebook.ipynb'
+    tmp_nbpy = '1.notebook.py'
+
+    cm = jupytext.TextFileContentsManager()
+    cm.default_jupytext_formats = 'ipynb,py'
+    cm.root_dir = str(tmpdir)
+
+    # open ipynb, save nb.py, reopen
+    nb = jupytext.readf(nb_file)
+    cm.save(model=dict(type='notebook', content=nb), path=tmp_nbpy)
+    nbpy = cm.get(tmp_nbpy)
+    compare_notebooks(nb, nbpy['content'])
+
+    # save ipynb
+    cm.save(model=dict(type='notebook', content=nb), path=tmp_ipynb)
+
+    # rename py
+    cm.rename(tmp_nbpy, '2.new.py')
+    assert not os.path.isfile(str(tmpdir.join(tmp_ipynb)))
+    assert not os.path.isfile(str(tmpdir.join(tmp_nbpy)))
+
+    assert os.path.isfile(str(tmpdir.join('2.new.ipynb')))
+    assert os.path.isfile(str(tmpdir.join('2.new.py')))
+
+
 @skip_if_dict_is_not_ordered
 @pytest.mark.parametrize('nb_file', list_notebooks('ipynb_py'))
 def test_load_save_rename_nbpy_default_config(nb_file, tmpdir):
@@ -231,15 +260,15 @@ def test_load_save_percent_format(nb_file, tmpdir):
 def test_save_to_percent_format(nb_file, tmpdir):
     tmp_ipynb = 'notebook.ipynb'
     tmp_jl = 'notebook.jl'
-    nb = jupytext.readf(nb_file)
 
     cm = jupytext.TextFileContentsManager()
     cm.root_dir = str(tmpdir)
     cm.preferred_jupytext_formats_save = 'jl:percent'
 
+    nb = jupytext.readf(nb_file)
     nb['metadata']['jupytext_formats'] = 'ipynb,jl'
 
-    # open python, save
+    # save to ipynb and jl
     with mock.patch('jupytext.header.INSERT_AND_CHECK_VERSION_NUMBER', True):
         cm.save(model=dict(type='notebook', content=nb), path=tmp_ipynb)
 
@@ -250,3 +279,69 @@ def test_save_to_percent_format(nb_file, tmpdir):
     # Parse the YAML header
     metadata, _, _ = header_to_metadata_and_cell(text_jl.splitlines(), '#')
     assert metadata['jupytext_formats'] == 'ipynb,jl:percent'
+
+
+@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_py')[:1])
+def test_preferred_format_allows_to_read_others_format(nb_file, tmpdir):
+    # 1. write py ipynb
+    tmp_ipynb = u'notebook.ipynb'
+    tmp_nbpy = u'notebook.py'
+
+    cm = jupytext.TextFileContentsManager()
+    cm.preferred_jupytext_formats_save = 'py:light'
+    cm.root_dir = str(tmpdir)
+
+    # load notebook and save it using the cm
+    nb = jupytext.readf(nb_file)
+    nb['metadata']['jupytext_formats'] = 'ipynb,py'
+    with mock.patch('jupytext.header.INSERT_AND_CHECK_VERSION_NUMBER', True):
+        cm.save(model=dict(type='notebook', content=nb), path=tmp_ipynb)
+
+    # Saving does not update the metadata, as 'save' makes a copy of the notebook
+    # assert nb['metadata']['jupytext_formats'] == 'ipynb,py:light'
+
+    # Set preferred format for reading
+    cm.preferred_jupytext_formats_read = 'py:percent'
+
+    # Read notebook
+    with mock.patch('jupytext.header.INSERT_AND_CHECK_VERSION_NUMBER', True):
+        model = cm.get(tmp_nbpy)
+
+    # Check that format is explicit
+    assert model['content']['metadata']['jupytext_formats'] == 'ipynb,py:light'
+
+    # Check contents
+    compare_notebooks(nb, model['content'])
+
+    # Change save format and save
+    model['content']['metadata']['jupytext_formats'] == 'ipynb,py'
+    cm.preferred_jupytext_formats_save = 'py:percent'
+    with mock.patch('jupytext.header.INSERT_AND_CHECK_VERSION_NUMBER', True):
+        cm.save(model=dict(type='notebook', content=nb), path=tmp_ipynb)
+
+    # Read notebook
+    with mock.patch('jupytext.header.INSERT_AND_CHECK_VERSION_NUMBER', True):
+        model = cm.get(tmp_nbpy)
+    compare_notebooks(nb, model['content'])
+
+    # Check that format is explicit
+    assert model['content']['metadata']['jupytext_formats'] == 'ipynb,py:percent'
+
+
+@pytest.mark.parametrize('nb_file', list_notebooks('python'))
+def test_preferred_format_allows_to_read_implicit_light_format(nb_file, tmpdir):
+    # copy content to notebook.py
+    tmp_nbpy = u'notebook.py'
+    shutil.copy(nb_file, str(tmpdir.join(tmp_nbpy)))
+
+    # create contents manager with default load format as percent
+    cm = jupytext.TextFileContentsManager()
+    cm.preferred_jupytext_formats_load = 'py:percent'
+    cm.root_dir = str(tmpdir)
+
+    # load notebook
+    with mock.patch('jupytext.header.INSERT_AND_CHECK_VERSION_NUMBER', True):
+        model = cm.get(tmp_nbpy)
+
+    # check that format (missing) is recognized as light
+    assert 'py:light' in model['content']['metadata']['jupytext_formats']
diff --git a/tests/test_escape_magics.py b/tests/test_escape_magics.py
index cd778e89..f7a896e0 100644
--- a/tests/test_escape_magics.py
+++ b/tests/test_escape_magics.py
@@ -1,31 +1,31 @@
 import pytest
-from jupytext.magics import escape_magic, unescape_magic, unesc
+from jupytext.magics import comment_magic, uncomment_magic, unesc
 
 
 def test_unesc():
-    assert unesc('# comment') == 'comment'
-    assert unesc('#comment') == 'comment'
-    assert unesc('comment') == 'comment'
+    assert unesc('# comment', 'python') == 'comment'
+    assert unesc('#comment', 'python') == 'comment'
+    assert unesc('comment', 'python') == 'comment'
 
 
 @pytest.mark.parametrize('line', ['%matplotlib inline', '#%matplotlib inline',
                                   '##%matplotlib inline', '%%HTML',
                                   '%autoreload', '%store'])
 def test_escape(line):
-    assert escape_magic([line]) == ['# ' + line]
-    assert unescape_magic(escape_magic([line])) == [line]
+    assert comment_magic([line]) == ['# ' + line]
+    assert uncomment_magic(comment_magic([line])) == [line]
 
 
 @pytest.mark.parametrize('line', ['%pytest.fixture'])
 def test_escape_magic_only(line):
-    assert escape_magic([line]) == [line]
+    assert comment_magic([line]) == [line]
 
 
 @pytest.mark.parametrize('line', ['%matplotlib inline #noescape'])
 def test_force_noescape(line):
-    assert escape_magic([line]) == [line]
+    assert comment_magic([line]) == [line]
 
 
 @pytest.mark.parametrize('line', ['%pytest.fixture #escape'])
 def test_force_escape(line):
-    assert escape_magic([line]) == ['# ' + line]
+    assert comment_magic([line]) == ['# ' + line]
diff --git a/tests/test_formats.py b/tests/test_formats.py
index 8f722ea2..ecf37a99 100644
--- a/tests/test_formats.py
+++ b/tests/test_formats.py
@@ -6,7 +6,7 @@ from .utils import list_notebooks
 @pytest.mark.parametrize('nb_file', list_notebooks('python'))
 def test_guess_format_light(nb_file):
     with open(nb_file) as stream:
-        assert guess_format(stream.read(), ext='.py') is None
+        assert guess_format(stream.read(), ext='.py') == 'light'
 
 
 @pytest.mark.parametrize('nb_file', list_notebooks('percent'))
diff --git a/tests/test_mirror.py b/tests/test_mirror.py
index 79655a28..c248564c 100644
--- a/tests/test_mirror.py
+++ b/tests/test_mirror.py
@@ -83,6 +83,16 @@ def test_ipynb_to_R(nb_file):
     assert_conversion_same_as_mirror(nb_file, '.R', 'ipynb_to_script')
 
 
+@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_scheme'))
+def test_ipynb_to_scheme(nb_file):
+    assert_conversion_same_as_mirror(nb_file, '.ss', 'ipynb_to_script')
+
+
+@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_cpp'))
+def test_ipynb_to_cpp(nb_file):
+    assert_conversion_same_as_mirror(nb_file, '.cpp', 'ipynb_to_script')
+
+
 @pytest.mark.parametrize('nb_file', list_notebooks('ipynb_julia'))
 def test_ipynb_to_julia_percent(nb_file):
     assert_conversion_same_as_mirror(nb_file, '.jl', 'ipynb_to_percent',
@@ -101,6 +111,18 @@ def test_ipynb_to_R_percent(nb_file):
                                      format_name='percent')
 
 
+@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_cpp'))
+def test_ipynb_to_cpp_percent(nb_file):
+    assert_conversion_same_as_mirror(nb_file, '.cpp', 'ipynb_to_percent',
+                                     format_name='percent')
+
+
+@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_scheme'))
+def test_ipynb_to_scheme_percent(nb_file):
+    assert_conversion_same_as_mirror(nb_file, '.ss', 'ipynb_to_percent',
+                                     format_name='percent')
+
+
 @pytest.mark.parametrize('nb_file', list_notebooks('percent'))
 def test_percent_to_ipynb(nb_file):
     assert_conversion_same_as_mirror(nb_file, '.ipynb', 'script_to_ipynb',
diff --git a/tests/test_read_simple_python.py b/tests/test_read_simple_python.py
index 636a27f8..b05318df 100644
--- a/tests/test_read_simple_python.py
+++ b/tests/test_read_simple_python.py
@@ -131,7 +131,7 @@ a + 2
 
 def test_read_cell_explicit_start(pynb='''
 import pandas as pd
-# +
+# + {}
 def data():
     return pd.DataFrame({'A': [0, 1]})