diff --git a/README.md b/README.md index 49660a0..6673eb3 100644 --- a/README.md +++ b/README.md @@ -482,9 +482,9 @@ Features supported by cheat.sh plugins for different editors: |-------------------|-----|-------|---|------|----| |Command queries |✓ |✓ |✓ |✓ |✓ | |Queries from buffer| | |✓ |✓ | | -|Toggle comments | | |✓ | | | -|Prev/next answer | | |✓ | | | -|Multiple answers | |✓ | | | | +|Toggle comments | | |✓ | |✓ | +|Prev/next answer | | |✓ | |✓ | +|Multiple answers | |✓ | | |✓ | |Warnings as queries| | |✓ | | | |Queries history | | |✓ |✓ | | |Session id | | |✓ | | | diff --git a/bin/srv.py b/bin/srv.py index 40270d0..0d850f4 100644 --- a/bin/srv.py +++ b/bin/srv.py @@ -247,7 +247,11 @@ def answer(topic=None): return "429 %s\n" % not_allowed, 429 html_is_needed = is_html_needed(user_agent) and not is_result_a_script(topic) - result, found = cheat_wrapper(topic, request_options=options, html=html_is_needed) + if html_is_needed: + output_format='html' + else: + output_format='ansi' + result, found = cheat_wrapper(topic, request_options=options, output_format=output_format) if 'Please come back in several hours' in result and html_is_needed: return MALFORMED_RESPONSE_HTML_PAGE diff --git a/lib/adapter/adapter.py b/lib/adapter/adapter.py index 0143740..69957dc 100644 --- a/lib/adapter/adapter.py +++ b/lib/adapter/adapter.py @@ -1,19 +1,68 @@ import abc class Adapter(object): + + _adapter_name = None + _output_format = 'code' + _cache_needed = False + def __init__(self): - self._list = self._get_list() + self._list = {None: self._get_list()} @abc.abstractmethod - def _get_list(self): + def _get_list(self, prefix=None): return [] - def get_list(self): - return self._list + def get_list(self, prefix=None): + """ + Return available pages for `prefix` + """ + + if prefix in self._list: + return self._list[prefix] + + self._list[prefix] = set(self._get_list(prefix=prefix)) + return self._list[prefix] def is_found(self, topic): - return topic in self._list + """ + check if `topic` is available + CAUTION: only root is checked + """ + return topic in self._list[None] + + def is_cache_needed(self): + """ + Return True if answers should be cached. + Return False if answers should not be cached. + """ + return self._cache_needed @abc.abstractmethod - def get_page(self, topic, request_options=None): + def _get_page(self, topic, request_options=None): + """ + Return page for `topic` + """ pass + + def _get_output_format(self, topic): + if '/' in topic: + subquery = topic.split('/')[-1] + else: + subquery = topic + + if subquery in [':list']: + return 'text' + return self._output_format + + def get_page_dict(self, topic, request_options=None): + """ + Return page dict for `topic` + """ + answer_dict = { + 'topic': topic, + 'topic_type': self._adapter_name, + 'answer': self._get_page(topic, request_options=request_options), + 'format': self._get_output_format(topic), + } + return answer_dict diff --git a/lib/adapter/cheat_sheets.py b/lib/adapter/cheat_sheets.py index be14f3d..e2cb1db 100644 --- a/lib/adapter/cheat_sheets.py +++ b/lib/adapter/cheat_sheets.py @@ -5,6 +5,8 @@ import glob sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from globals import PATH_CHEAT_SHEETS +from adapter import Adapter + def _remove_initial_underscore(filename): if filename.startswith('_'): filename = filename[1:] @@ -31,41 +33,51 @@ def _get_answers_and_dirs(): answers = [os.path.split(topic)[1] for topic in topics if not _isdir(topic)] return answers, answer_dirs -def _update_cheat_sheets_topics(): - answers = _get_answer_files_from_folder() - cheatsheet_answers, cheatsheet_dirs = _get_answers_and_dirs() - return answers+cheatsheet_answers, cheatsheet_dirs +class CheatSheets(Adapter): -def get_list(): - return _update_cheat_sheets_topics()[0] + _adapter_name = "cheat.sheets" + _output_format = "code" -def get_dirs_list(): - return _update_cheat_sheets_topics()[1] + def __init__(self): + self._answers = [] + self._cheatsheet_answers = [] + self._cheatsheet_dirs = [] + Adapter.__init__(self) -_CHEAT_SHEETS_LIST = get_list() -def is_found(topic): - return topic in _CHEAT_SHEETS_LIST + def _update_cheat_sheets_topics(self): + self._answers = _get_answer_files_from_folder() + self._cheatsheet_answers, self._cheatsheet_dirs = _get_answers_and_dirs() + return self._answers + self._cheatsheet_answers, self._cheatsheet_dirs -_CHEAT_SHEETS_DIRS = get_dirs_list() -def is_dir_found(topic): - return topic in _CHEAT_SHEETS_DIRS + def _get_list(self, prefix=None): + return self._update_cheat_sheets_topics()[0] -def get_page(topic, request_options=None): - """ - Get the cheat sheet topic from the own repository (cheat.sheets). - It's possible that topic directory starts with omitted underscore - """ - filename = PATH_CHEAT_SHEETS + "%s" % topic - if not os.path.exists(filename): - filename = PATH_CHEAT_SHEETS + "_%s" % topic - if os.path.isdir(filename): - return "" - else: + def _get_page(self, topic, request_options=None): + """ + Get the cheat sheet topic from the own repository (cheat.sheets). + It's possible that topic directory starts with omitted underscore + """ + filename = PATH_CHEAT_SHEETS + "%s" % topic + if not os.path.exists(filename): + filename = PATH_CHEAT_SHEETS + "_%s" % topic + if os.path.isdir(filename): + return "" return open(filename, "r").read().decode('utf-8') -def get_dir(topic, request_options=None): - answer = [] - for f_name in glob.glob(PATH_CHEAT_SHEETS + "%s/*" % topic.rstrip('/')): - answer.append(os.path.basename(f_name)) - topics = sorted(answer) - return "\n".join(topics) + "\n" +class CheatSheetsDir(CheatSheets): + + _adapter_name = "cheat.sheets dir" + _output_format = "text" + + def _get_list(self, prefix=None): + return self._update_cheat_sheets_topics()[1] + + def _get_page(self, topic, request_options=None): + answer = [] + for f_name in glob.glob(PATH_CHEAT_SHEETS + "%s/*" % topic.rstrip('/')): + answer.append(os.path.basename(f_name)) + topics = sorted(answer) + return "\n".join(topics) + "\n" + + def is_found(self, topic): + return CheatSheets.is_found(self, topic.rstrip('/')) diff --git a/lib/adapter/cmd.py b/lib/adapter/cmd.py index 3c61368..4a0d929 100644 --- a/lib/adapter/cmd.py +++ b/lib/adapter/cmd.py @@ -15,11 +15,16 @@ def _get_filenames(path): return [os.path.split(topic)[1] for topic in glob.glob(path)] class Tldr(Adapter): - def _get_list(self): + + _adapter_name = "tldr" + _output_format = "code" + _cache_needed = True + + def _get_list(self, prefix=None): return [filename[:-3] for filename in _get_filenames(PATH_TLDR_PAGES) if filename.endswith('.md')] - def get_page(self, topic, request_options=None): + def _get_page(self, topic, request_options=None): cmd = ["tldr", topic] proc = Popen(cmd, stdout=PIPE, stderr=PIPE) answer = proc.communicate()[0] @@ -40,32 +45,44 @@ class Tldr(Adapter): return answer.decode('utf-8') class Cheat(Adapter): - def _get_list(self): + + _adapter_name = "cheat" + _output_format = "code" + _cache_needed = True + + def _get_list(self, prefix=None): return _get_filenames(PATH_CHEAT_PAGES) - def get_page(self, topic, request_options=None): + def _get_page(self, topic, request_options=None): cmd = ["/usr/local/bin/cheat", topic] - print(cmd) proc = Popen(cmd, stdout=PIPE, stderr=PIPE) answer = proc.communicate()[0].decode('utf-8') - print("answer=%s"% answer) return answer class Fosdem(Adapter): - def _get_list(self): + + _adapter_name = "fosdem" + _output_format = "ansi" + + def _get_list(self, prefix=None): return ['fosdem'] - def get_page(self, topic, request_options=None): + def _get_page(self, topic, request_options=None): cmd = ["sudo", "/usr/local/bin/current-fosdem-slide"] proc = Popen(cmd, stdout=PIPE, stderr=PIPE) answer = proc.communicate()[0].decode('utf-8') return answer class Translation(Adapter): - def _get_list(self): + + _adapter_name = "translation" + _output_format = "text" + _cache_needed = True + + def _get_list(self, prefix=None): return [] - def get_page(self, topic, request_options=None): + def _get_page(self, topic, request_options=None): from_, topic = topic.split('/', 1) to_ = request_options.get('lang', 'en') if '-' in from_: diff --git a/lib/adapter/internal.py b/lib/adapter/internal.py index 3b3dc37..f3f9c74 100644 --- a/lib/adapter/internal.py +++ b/lib/adapter/internal.py @@ -7,7 +7,8 @@ from fuzzywuzzy import process, fuzz sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from globals import MYDIR, COLOR_STYLES -from colorize_internal import colorize_internal +from adapter import Adapter +from fmt.internal import colorize_internal _INTERNAL_TOPICS = [ ":cht.sh", @@ -33,9 +34,13 @@ _COLORIZED_INTERNAL_TOPICS = [ ':intro', ] -class InternalPages(object): +class InternalPages(Adapter): + + _adapter_name = 'internal' + _output_format = 'ansi' def __init__(self, get_topic_type=None, get_topics_list=None): + Adapter.__init__(self) self.get_topic_type = get_topic_type self.get_topics_list = get_topics_list @@ -50,9 +55,8 @@ class InternalPages(object): answer += "%s %s\n" % (key, val) return answer - @staticmethod - def get_list(): + def get_list(prefix=None): return _INTERNAL_TOPICS def _get_list_answer(self, topic, request_options=None): @@ -70,7 +74,7 @@ class InternalPages(object): return answer - def get_page(self, topic, request_options=None): + def _get_page(self, topic, request_options=None): if topic.endswith('/:list') or topic.lstrip('/') == ':list': return self._get_list_answer(topic) @@ -87,19 +91,25 @@ class InternalPages(object): return answer def is_found(self, topic): - return topic in self.get_list() + return ( + topic in self.get_list() + or topic.endswith('/:list') + ) class UnknownPages(InternalPages): + _adapter_name = 'unknown' + _output_format = 'text' + @staticmethod - def get_list(): + def get_list(prefix=None): return [] @staticmethod def is_found(topic): - return False + return True - def get_page(self, topic, request_options=None): + def _get_page(self, topic, request_options=None): topics_list = self.get_topics_list() if topic.startswith(':'): topics_list = [x for x in topics_list if x.startswith(':')] @@ -114,3 +124,16 @@ Do you mean one of these topics maybe? %s """ % possible_topics_text + +class Search(Adapter): + + _adapter_name = 'search' + _output_format = 'text' + _cache_needed = False + + @staticmethod + def get_list(prefix=None): + return [] + + def is_found(topic): + return True diff --git a/lib/adapter/latenz.py b/lib/adapter/latenz.py index 02358db..f9a25b0 100644 --- a/lib/adapter/latenz.py +++ b/lib/adapter/latenz.py @@ -1,16 +1,21 @@ import sys import os sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) - from globals import PATH_LATENZ +from adapter import Adapter -def get_answer(topic, request_options=None): - sys.path.append(PATH_LATENZ) - import latencies - return latencies.render() +class Latenz(Adapter): -def get_list(): - return ['latencies'] + _adapter_name = "late.nz" + _output_format = "ansi" -def is_found(topic): - return topic.lower() in ['latencies', 'late.nz', 'latency'] + def _get_page(self, topic, request_options=None): + sys.path.append(PATH_LATENZ) + import latencies + return latencies.render() + + def _get_list(self, prefix=None): + return ['latencies'] + + def is_found(self, topic): + return topic.lower() in ['latencies', 'late.nz', 'latency'] diff --git a/lib/adapter/learnxiny.py b/lib/adapter/learnxiny.py index 1924998..2286325 100644 --- a/lib/adapter/learnxiny.py +++ b/lib/adapter/learnxiny.py @@ -7,6 +7,8 @@ import os import re from globals import PATH_LEARNXINY +from adapter import Adapter + class LearnXYAdapter(object): """ @@ -23,14 +25,13 @@ class LearnXYAdapter(object): _block_cut_end = 0 def __init__(self): - self._whole_cheatsheet = self._read_cheatsheet() self._blocks = self._extract_blocks() self._topics_list = [x for x, _ in self._blocks] if "Comments" in self._topics_list: self._topics_list = [x for x in self._topics_list if x != "Comments"] + ["Comments"] - self._topics_list += [":learn"] + self._topics_list += [":learn", ":list"] print(self.prefix, self._topics_list) def _is_block_separator(self, before, now, after): @@ -111,7 +112,7 @@ class LearnXYAdapter(object): return True return False - def get_list(self, prefix=False): + def get_list(self, prefix=None): """ Get list of topics for `prefix` """ @@ -119,7 +120,7 @@ class LearnXYAdapter(object): return ["%s/%s" % (self.prefix, x) for x in self._topics_list] return self._topics_list - def get_cheat_sheet(self, name, partial=False): + def get_page(self, name, partial=False): """ Return specified cheat sheet `name` for the language. If `partial`, cheat sheet name may be shortened @@ -142,9 +143,6 @@ class LearnXYAdapter(object): for block_name, block_contents in self._blocks: if block_name == name: - - print("\n".join(block_contents)) - print(name) return "\n".join(block_contents) return None @@ -792,34 +790,46 @@ class LearnVisualBasicAdapter(LearnXYAdapter): _filename = "visualbasic.html.markdown" _splitted = False -ADAPTERS = {cls.prefix: cls() for cls in vars()['LearnXYAdapter'].__subclasses__()} +_ADAPTERS = {cls.prefix: cls() for cls in vars()['LearnXYAdapter'].__subclasses__()} -def get_learnxiny(topic, request_options=None): - """ - Return cheat sheet for `topic` - or empty string if nothing found - """ - lang, topic = topic.split('/', 1) - if lang not in ADAPTERS: - return '' - return ADAPTERS[lang].get_cheat_sheet(topic) +class LearnXinY(Adapter): -def get_learnxiny_list(): - """ - Return list of all learnxiny topics - """ - answer = [] - for language_adapter in ADAPTERS.values(): - answer += language_adapter.get_list(prefix=True) - return answer + _output_format = 'code' + _cache_needed = True -def is_valid_learnxy(topic): - """ - Return whether `topic` is a valid learnxiny topic - """ + def __init__(self): + self.adapters = _ADAPTERS + Adapter.__init__(self) - lang, topic = topic.split('/', 1) - if lang not in ADAPTERS: - return False + def _get_page(self, topic, request_options=None): + """ + Return cheat sheet for `topic` + or empty string if nothing found + """ + lang, topic = topic.split('/', 1) + if lang not in self.adapters: + return '' + return self.adapters[lang].get_page(topic) - return ADAPTERS[lang].is_valid(topic) + def _get_list(self, prefix=None): + """ + Return list of all learnxiny topics + """ + answer = [] + for language_adapter in self.adapters.values(): + answer += language_adapter.get_list(prefix=True) + return answer + + def is_found(self, topic): + """ + Return whether `topic` is a valid learnxiny topic + """ + + if '/' not in topic: + return False + + lang, topic = topic.split('/', 1) + if lang not in self.adapters: + return False + + return self.adapters[lang].is_valid(topic) diff --git a/lib/adapter/question.py b/lib/adapter/question.py index 4473358..823370a 100644 --- a/lib/adapter/question.py +++ b/lib/adapter/question.py @@ -12,52 +12,69 @@ from polyglot.detect.base import UnknownLanguage sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from globals import MYDIR -def get_page(topic, request_options=None): - """ - Find answer for the `topic` question. - """ +from adapter import Adapter +from languages_data import SO_NAME - # if there is a language name in the section name, - # cut it off (de:python => python) - if '/' in topic: - section_name, topic = topic.split('/', 1) - if ':' in section_name: - _, section_name = section_name.split(':', 1) - topic = "%s/%s" % (section_name, topic) +class Question(Adapter): - # some clients send queries with - instead of + so we have to rewrite them to - topic = re.sub(r"(? python) + if '/' in topic: + section_name, topic = topic.split('/', 1) + if ':' in section_name: + _, section_name = section_name.split(':', 1) + section_name = SO_NAME.get(section_name, section_name) + topic = "%s/%s" % (section_name, topic) - lang = 'en' - try: - query_text = topic # " ".join(topic) - query_text = re.sub('^[^/]*/+', '', query_text.rstrip('/')) - query_text = re.sub('/[0-9]+$', '', query_text) - query_text = re.sub('/[0-9]+$', '', query_text) - detector = Detector(query_text) - supposed_lang = detector.languages[0].code - if len(topic_words) > 2 or supposed_lang in ['az', 'ru', 'uk', 'de', 'fr', 'es', 'it', 'nl']: - lang = supposed_lang - if supposed_lang.startswith('zh_') or supposed_lang == 'zh': - lang = 'zh' - elif supposed_lang.startswith('pt_'): - lang = 'pt' - if supposed_lang in ['ja', 'ko']: - lang = supposed_lang - except UnknownLanguage: - print("Unknown language (%s)" % query_text) + # some clients send queries with - instead of + so we have to rewrite them to + topic = re.sub(r"(? 2 or supposed_lang in ['az', 'ru', 'uk', 'de', 'fr', 'es', 'it', 'nl']: + lang = supposed_lang + if supposed_lang.startswith('zh_') or supposed_lang == 'zh': + lang = 'zh' + elif supposed_lang.startswith('pt_'): + lang = 'pt' + if supposed_lang in ['ja', 'ko']: + lang = supposed_lang + + except UnknownLanguage: + print("Unknown language (%s)" % query_text) + + if lang != 'en': + topic = ['--human-language', lang, topic] + else: + topic = [topic] + + cmd = [os.path.join(MYDIR, "bin/get-answer-for-question")] + topic + proc = Popen(cmd, stdout=PIPE, stderr=PIPE) + answer = proc.communicate()[0].decode('utf-8') + return answer + + def get_list(self, prefix=None): + return [] + + def is_found(self, topic): + return True diff --git a/lib/adapter/rosetta.py b/lib/adapter/rosetta.py index 8bdcc17..b56b0ea 100644 --- a/lib/adapter/rosetta.py +++ b/lib/adapter/rosetta.py @@ -23,7 +23,9 @@ class Rosetta(Adapter): Adapter for RosettaCode """ - __section_name = 'rosetta' + __section_name = "rosetta" + _adapter_name = "rosetta" + _output_format = "code" @staticmethod def _load_rosetta_code_names(): @@ -57,14 +59,19 @@ class Rosetta(Adapter): answer = "".join("%s\n" % x for x in sorted(answer)) return answer - def _get_task(self, lang, query): - if lang not in self._rosetta_code_name: - return "" - + @staticmethod + def _parse_query(query): if '/' in query: task, subquery = query.split('/', 1) else: task, subquery = query, None + return task, subquery + + def _get_task(self, lang, query): + if lang not in self._rosetta_code_name: + return "" + + task, subquery = self._parse_query(query) if task == ':list': return self._rosetta_get_list(lang) @@ -101,7 +108,7 @@ class Rosetta(Adapter): ) % number_of_pages return answer - def get_page(self, topic, request_options=None): + def _get_page(self, topic, request_options=None): if '/' not in topic: return self._rosetta_get_list(topic) @@ -118,18 +125,17 @@ class Rosetta(Adapter): return self._get_task(lang, topic) - def _get_list(self): + def _get_list(self, prefix=None): return [] - def get_list(self): - # return self._get_list() + def get_list(self, prefix=None): answer = [self.__section_name] for i in self._rosetta_code_name: answer.append('%s/%s/' % (i, self.__section_name)) return answer def is_found(self, _): - return False + return True def __init__(self): Adapter.__init__(self) diff --git a/lib/cache.py b/lib/cache.py new file mode 100644 index 0000000..78b339d --- /dev/null +++ b/lib/cache.py @@ -0,0 +1,45 @@ +import os +import json +import redis +from globals import REDISHOST + +if os.environ.get('REDIS_HOST', '').lower() != 'none': + _REDIS = redis.StrictRedis(host=REDISHOST, port=6379, db=0) +else: + _REDIS = None + +if os.environ.get('REDIS_PREFIX', '').lower() != 'none': + _REDIS_PREFIX = os.environ.get('REDIS_PREFIX', '') + ':' +else: + _REDIS_PREFIX = '' + +def put(key, value): + """ + Save `value` with `key`, and serialize it if needed + """ + + if _REDIS_PREFIX: + key = _REDIS_PREFIX + key + + if _REDIS: + if isinstance(value, (dict, list)): + value = json.dumps(value) + + _REDIS.set(key, value) + +def get(key): + """ + Read `value` by `key`, and deserialize it if needed + """ + + if _REDIS_PREFIX: + key = _REDIS_PREFIX + key + + if _REDIS: + value = _REDIS.get(key) + try: + value = json.loads(value) + except (ValueError, TypeError): + pass + return value + return None diff --git a/lib/cheat_wrapper.py b/lib/cheat_wrapper.py index 40d6b62..9f66efb 100644 --- a/lib/cheat_wrapper.py +++ b/lib/cheat_wrapper.py @@ -1,274 +1,56 @@ """ Main cheat.sh wrapper. -Get answers from getters (in get_answer), adds syntax highlighting -or html markup and returns the result. +Parse the query, get answers from getters (using get_answer), +visualize it using frontends and return the result. + +Exports: + + cheat_wrapper() """ -from gevent.monkey import patch_all -from gevent.subprocess import Popen, PIPE -patch_all() - -# pylint: disable=wrong-import-position,wrong-import-order -import sys -import os import re +import json -import colored -from pygments import highlight as pygments_highlight -from pygments.formatters import Terminal256Formatter # pylint: disable=no-name-in-module +from routing import get_answer_dict, get_topics_list +from search import find_answers_by_keyword +from languages_data import LANGUAGE_ALIAS, rewrite_editor_section_name +import postprocessing -MYDIR = os.path.abspath(os.path.join(__file__, '..', '..')) -sys.path.append("%s/lib/" % MYDIR) -from globals import error, ANSI2HTML, COLOR_STYLES, GITHUB_REPOSITORY -from buttons import TWITTER_BUTTON, GITHUB_BUTTON, GITHUB_BUTTON_FOOTER -from languages_data import LEXER, get_lexer_name -from get_answer import get_topic_type, get_topics_list, get_answer, find_answer_by_keyword -from beautifier import code_blocks -# import beautifier -# pylint: disable=wrong-import-position,wrong-import-order +import frontend.html +import frontend.ansi -ANSI_ESCAPE = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]') -def remove_ansi(sometext): +def cheat_wrapper(query, request_options=None, output_format='ansi'): """ - Remove ANSI sequences from `sometext` and convert it into plaintext. - """ - return ANSI_ESCAPE.sub('', sometext) - -def html_wrapper(data): - """ - Convert ANSI text `data` to HTML - """ - proc = Popen( - ["bash", ANSI2HTML, "--palette=solarized", "--bg=dark"], - stdin=PIPE, stdout=PIPE, stderr=PIPE) - data = data.encode('utf-8') - stdout, stderr = proc.communicate(data) - if proc.returncode != 0: - error(stdout + stderr) - return stdout.decode('utf-8') - -def _colorize_internal(topic, answer, html_needed): - - def _colorize_line(line): - if line.startswith('T'): - line = colored.fg("grey_62") + line + colored.attr('reset') - line = re.sub(r"\{(.*?)\}", colored.fg("orange_3") + r"\1"+colored.fg('grey_35'), line) - return line - - line = re.sub(r"\[(F.*?)\]", - colored.bg("black") + colored.fg("cyan") + r"[\1]"+colored.attr('reset'), - line) - line = re.sub(r"\[(g.*?)\]", - colored.bg("dark_gray") \ - + colored.fg("grey_0") \ - + r"[\1]"+colored.attr('reset'), - line) - line = re.sub(r"\{(.*?)\}", - colored.fg("orange_3") + r"\1"+colored.attr('reset'), - line) - line = re.sub(r"<(.*?)>", - colored.fg("cyan") + r"\1"+colored.attr('reset'), - line) - return line - - if topic in [':list', ':bash_completion']: - return answer - - if topic == ':firstpage-v1': - lines = answer.splitlines() - answer_lines = lines[:9] - answer_lines.append(colored.fg('grey_35')+lines[9]+colored.attr('reset')) - for line in lines[10:]: - answer_lines.append(_colorize_line(line)) - if html_needed: - answer_lines = answer_lines[:-2] - answer = "\n".join(answer_lines) + "\n" - - return answer - -def _colorize_ansi_answer(topic, answer, color_style, # pylint: disable=too-many-arguments - highlight_all=True, highlight_code=False, - unindent_code=False): - - color_style = color_style or "native" - lexer_class = LEXER['bash'] - if '/' in topic: - section_name = topic.split('/', 1)[0].lower() - section_name = get_lexer_name(section_name) - lexer_class = LEXER.get(section_name, lexer_class) - if section_name == 'php': - answer = "\n%s?>\n" % answer - - if highlight_all: - highlight = lambda answer: pygments_highlight( - answer, lexer_class(), Terminal256Formatter(style=color_style)).strip('\n')+'\n' - else: - highlight = lambda x: x - - if highlight_code: - blocks = code_blocks(answer, wrap_lines=True, unindent_code=(4 if unindent_code else False)) - highlighted_blocks = [] - for block in blocks: - if block[0] == 1: - this_block = highlight(block[1]) - else: - this_block = block[1].strip('\n')+'\n' - highlighted_blocks.append(this_block) - - result = "\n".join(highlighted_blocks) - else: - result = highlight(answer).lstrip('\n') - return result - -def _github_button(topic_type): - - full_name = GITHUB_REPOSITORY.get(topic_type, '') - if not full_name: - return '' - - short_name = full_name.split('/', 1)[1] # pylint: disable=unused-variable - - button = ( - "" - '%(short_name)s' - ) % locals() - return button - -def _render_html(query, result, editable, repository_button, request_options): - - result = result + "\n$" - result = html_wrapper(result) - title = "
' - '[edit]' - '') % edit_page_link - result = re.sub("
", edit_button + form_html + "", result)
- result = re.sub("", "" + title, result)
- if not request_options.get('quiet'):
- result = result.replace('',
- TWITTER_BUTTON \
- + GITHUB_BUTTON \
- + repository_button \
- + GITHUB_BUTTON_FOOTER \
- + '')
- return result
-
-def _visualize(query, keyword, answers, request_options, html=None): # pylint: disable=too-many-locals
-
- search_mode = bool(keyword)
-
- highlight = not bool(request_options and request_options.get('no-terminal'))
- color_style = request_options.get('style', '')
- if color_style not in COLOR_STYLES:
- color_style = ''
-
- found = True # if the page was found in the database
- editable = False # can generated page be edited on github (only cheat.sheets pages can)
- result = ""
- for topic, answer in answers: # pylint: disable=too-many-nested-blocks
-
- if topic == 'LIMITED':
- result += colored.bg('dark_goldenrod') \
- + colored.fg('yellow_1') \
- + ' ' + answer + ' ' \
- + colored.attr('reset') + "\n"
- break
-
- topic_type = get_topic_type(topic)
- highlight = (highlight
- and not topic.endswith('/:list')
- and topic not in [":list", ":bash_completion"]
- and topic_type not in ["unknown"]
- )
- found = found and not topic_type == 'unknown'
- editable = editable or topic_type == "cheat.sheets"
-
- if topic_type == "internal" and highlight:
- answer = _colorize_internal(topic, answer, html)
- elif topic_type in ["late.nz", "fosdem"]:
- pass
- else:
- answer = _colorize_ansi_answer(
- topic, answer, color_style,
- highlight_all=highlight,
- highlight_code=(topic_type == 'question'
- and not request_options.get('add_comments')
- and not request_options.get('remove_text')),
- unindent_code=request_options.get('unindent_code')
- )
-
- if search_mode:
- if not highlight:
- result += "\n[%s]\n" % topic
- else:
- result += "\n%s%s %s %s%s\n" % (colored.bg('dark_gray'),
- colored.attr("res_underlined"),
- topic,
- colored.attr("res_underlined"),
- colored.attr('reset'))
- result += answer
-
- result = result.strip('\n') + "\n"
-
- if search_mode:
- editable = False
- repository_button = ''
- else:
- repository_button = _github_button(topic_type)
-
- if html and query:
- result = _render_html(
- query, result, editable, repository_button, request_options)
-
-
- return result, found
-
-def _sanitize_query(query):
- return re.sub('[<>"]', '', query)
-
-def cheat_wrapper(query, request_options=None, html=False):
- """
- Giant megafunction that delivers cheat sheet for `query`.
+ Function that delivers cheat sheet for `query`.
If `html` is True, the answer is formatted as HTML.
Additional request options specified in `request_options`.
-
- This function is really really bad, and should be rewritten
- as soon as possible.
"""
+ def _rewrite_aliases(word):
+ if word == ':bash.completion':
+ return ':bash_completion'
+ return word
+
+ def _rewrite_section_name(query):
+ """
+ Rewriting special section names:
+ * EDITOR:NAME => emacs:go-mode
+ """
+
+ if '/' not in query:
+ return query
+
+ section_name, rest = query.split('/', 1)
+
+ if ':' in section_name:
+ section_name = rewrite_editor_section_name(section_name)
+ section_name = LANGUAGE_ALIAS.get(section_name, section_name)
+
+ return "%s/%s" % (section_name, rest)
+
+ def _sanitize_query(query):
+ return re.sub('[<>"]', '', query)
+
def _strip_hyperlink(query):
return re.sub('(,[0-9]+)+$', '', query)
@@ -292,6 +74,9 @@ def cheat_wrapper(query, request_options=None, html=False):
return topic, keyword, search_options
query = _sanitize_query(query)
+ query = _rewrite_aliases(query)
+ query = _rewrite_section_name(query)
+
# at the moment, we just remove trailing slashes
# so queries python/ and python are equal
@@ -299,9 +84,26 @@ def cheat_wrapper(query, request_options=None, html=False):
topic, keyword, search_options = _parse_query(query)
if keyword:
- answers = find_answer_by_keyword(
+ answers = find_answers_by_keyword(
topic, keyword, options=search_options, request_options=request_options)
else:
- answers = [(topic, get_answer(topic, keyword, request_options=request_options))]
+ answers = [get_answer_dict(topic, request_options=request_options)]
- return _visualize(query, keyword, answers, request_options, html=html)
+ answers = [
+ postprocessing.postprocess(
+ answer, keyword, search_options, request_options=request_options)
+ for answer in answers
+ ]
+
+ answer_data = {
+ 'query': query,
+ 'keyword': keyword,
+ 'answers': answers,
+ }
+
+ if output_format == 'html':
+ answer_data['topics_list'] = get_topics_list()
+ return frontend.html.visualize(answer_data, request_options)
+ elif output_format == 'json':
+ return json.dumps(answer_data, indent=4)
+ return frontend.ansi.visualize(answer_data, request_options)
diff --git a/lib/fmt/__init__.py b/lib/fmt/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/lib/beautifier.py b/lib/fmt/comments.py
similarity index 98%
rename from lib/beautifier.py
rename to lib/fmt/comments.py
index 22eb33f..49c766b 100644
--- a/lib/beautifier.py
+++ b/lib/fmt/comments.py
@@ -31,15 +31,14 @@ import re
from itertools import groupby, chain
from tempfile import NamedTemporaryFile
-import redis
+import cache
MYDIR = os.path.abspath(os.path.join(__file__, '..', '..'))
sys.path.append("%s/lib/" % MYDIR)
from languages_data import VIM_NAME
-from globals import PATH_VIM_ENVIRONMENT, REDISHOST
+from globals import PATH_VIM_ENVIRONMENT
# pylint: enable=wrong-import-position,wrong-import-order
-REDIS = redis.StrictRedis(host=REDISHOST, port=6379, db=1)
FNULL = open(os.devnull, 'w')
TEXT = 0
CODE = 1
@@ -298,13 +297,12 @@ def beautify(text, lang, options):
return text
digest = "t:%s:%s:%s" % (hashlib.md5(text).hexdigest(), lang, mode)
- answer = REDIS.get(digest)
+ answer = cache.get(digest)
if answer:
return answer
-
answer = _beautify(text, lang, **beauty_options)
+ cache.put(digest, answer)
- REDIS.set(digest, answer)
return answer
def __main__():
diff --git a/lib/colorize_internal.py b/lib/fmt/internal.py
similarity index 65%
rename from lib/colorize_internal.py
rename to lib/fmt/internal.py
index 2d709ac..a30ab95 100644
--- a/lib/colorize_internal.py
+++ b/lib/fmt/internal.py
@@ -93,3 +93,38 @@ def colorize_internal(text, palette_number=1):
text = re.sub("{.*?}", _colorize_curlies_block, text)
text = re.sub("#(.*?)\n", _colorize_headers, text)
return text
+
+def colorize_internal_firstpage_v1(answer):
+ """
+ Colorize "/:firstpage-v1".
+ Legacy.
+ """
+
+ def _colorize_line(line):
+ if line.startswith('T'):
+ line = colored.fg("grey_62") + line + colored.attr('reset')
+ line = re.sub(r"\{(.*?)\}", colored.fg("orange_3") + r"\1"+colored.fg('grey_35'), line)
+ return line
+
+ line = re.sub(r"\[(F.*?)\]",
+ colored.bg("black") + colored.fg("cyan") + r"[\1]"+colored.attr('reset'),
+ line)
+ line = re.sub(r"\[(g.*?)\]",
+ colored.bg("dark_gray")+colored.fg("grey_0")+r"[\1]"+colored.attr('reset'),
+ line)
+ line = re.sub(r"\{(.*?)\}",
+ colored.fg("orange_3") + r"\1"+colored.attr('reset'),
+ line)
+ line = re.sub(r"<(.*?)>",
+ colored.fg("cyan") + r"\1"+colored.attr('reset'),
+ line)
+ return line
+
+ lines = answer.splitlines()
+ answer_lines = lines[:9]
+ answer_lines.append(colored.fg('grey_35')+lines[9]+colored.attr('reset'))
+ for line in lines[10:]:
+ answer_lines.append(_colorize_line(line))
+ answer = "\n".join(answer_lines) + "\n"
+
+ return answer
diff --git a/lib/fmt/markdown.py b/lib/fmt/markdown.py
new file mode 100644
index 0000000..e120a8c
--- /dev/null
+++ b/lib/fmt/markdown.py
@@ -0,0 +1,89 @@
+"""
+Markdown support.
+
+Exports:
+ format_text(text, config=None, highlighter=None):
+
+Uses external pygments formatters for highlighting (passed as an argument).
+"""
+
+import re
+import ansiwrap
+import colored
+
+def format_text(text, config=None, highlighter=None):
+ """
+ Renders `text` according to markdown rules.
+ Uses `highlighter` for syntax highlighting.
+ Returns a dictionary with "output" and "links".
+ """
+ return _format_section(text, config=config, highlighter=highlighter)
+
+def _split_into_paragraphs(text):
+ return re.split('\n\n+', text)
+
+def _colorize(text):
+ return \
+ re.sub(
+ r"`(.*?)`",
+ colored.bg("dark_gray") \
+ + colored.fg("white") \
+ + " " + r"\1" + " " \
+ + colored.attr('reset'),
+ re.sub(
+ r"\*\*(.*?)\*\*",
+ colored.attr('bold') \
+ + colored.fg("white") \
+ + r"\1" \
+ + colored.attr('reset'),
+ text))
+
+def _format_section(section_text, config=None, highlighter=None):
+
+ answer = ''
+
+ # cut code blocks
+ block_number = 0
+ while True:
+ section_text, replacements = re.subn(
+ '^```.*?^```',
+ 'MULTILINE_BLOCK_%s' % block_number,
+ section_text,
+ 1,
+ flags=re.S | re.MULTILINE)
+ block_number += 1
+ if not replacements:
+ break
+
+ # cut links
+ links = []
+ while True:
+ regexp = re.compile(r'\[(.*?)\]\((.*?)\)')
+ match = regexp.search(section_text)
+ if match:
+ links.append(match.group(0))
+ text = match.group(1)
+ # links are not yet supported
+ #
+ text = '\x1B]8;;%s\x1B\\\\%s\x1B]8;;\x1B\\\\' % (match.group(2), match.group(1))
+ else:
+ break
+
+
+ section_text, replacements = regexp.subn(
+ text, # 'LINK_%s' % len(links),
+ section_text,
+ 1)
+ block_number += 1
+ if not replacements:
+ break
+
+ for paragraph in _split_into_paragraphs(section_text):
+ answer += "\n".join(
+ ansiwrap.fill(_colorize(line)) + "\n"
+ for line in paragraph.splitlines()) + "\n"
+
+ return {
+ 'ansi': answer,
+ 'links': links
+ }
diff --git a/lib/frontend/__init__.py b/lib/frontend/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/lib/frontend/ansi.py b/lib/frontend/ansi.py
new file mode 100644
index 0000000..7b89c3d
--- /dev/null
+++ b/lib/frontend/ansi.py
@@ -0,0 +1,130 @@
+"""
+ANSI frontend.
+
+Exports:
+ visualize(answer_data, request_options)
+
+Format:
+ answer_data = {
+ 'answers': '...',}
+
+ answers = [answer,...]
+
+ answer = {
+ 'topic': '...',
+ 'topic_type': '...',
+ 'answer': '...',
+ 'format': 'ansi|code|markdown|text...',
+ }
+"""
+
+import os
+import sys
+import re
+
+import colored
+from pygments import highlight as pygments_highlight
+from pygments.formatters import Terminal256Formatter # pylint: disable=no-name-in-module
+ # pylint: disable=wrong-import-position
+sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
+from globals import COLOR_STYLES
+import languages_data
+import beautifier # pylint: enable=wrong-import-position
+
+import fmt.internal
+
+def visualize(answer_data, request_options):
+ """
+ Renders `answer_data` as ANSI output.
+ """
+ answers = answer_data['answers']
+ return _visualize(answers, request_options, search_mode=bool(answer_data['keyword']))
+
+ANSI_ESCAPE = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
+def remove_ansi(sometext):
+ """
+ Remove ANSI sequences from `sometext` and convert it into plaintext.
+ """
+ return ANSI_ESCAPE.sub('', sometext)
+
+def _limited_answer(answer):
+ return colored.bg('dark_goldenrod') + colored.fg('yellow_1') \
+ + ' ' + answer + ' ' \
+ + colored.attr('reset') + "\n"
+
+def _colorize_ansi_answer(topic, answer, color_style, # pylint: disable=too-many-arguments
+ highlight_all=True, highlight_code=False,
+ unindent_code=False):
+
+ color_style = color_style or "native"
+ lexer_class = languages_data.LEXER['bash']
+ if '/' in topic:
+ section_name = topic.split('/', 1)[0].lower()
+ section_name = languages_data.get_lexer_name(section_name)
+ lexer_class = languages_data.LEXER.get(section_name, lexer_class)
+ if section_name == 'php':
+ answer = "\n%s?>\n" % answer
+
+ if highlight_all:
+ highlight = lambda answer: pygments_highlight(
+ answer, lexer_class(), Terminal256Formatter(style=color_style)).strip('\n')+'\n'
+ else:
+ highlight = lambda x: x
+
+ if highlight_code:
+ blocks = beautifier.code_blocks(
+ answer, wrap_lines=True, unindent_code=(4 if unindent_code else False))
+ highlighted_blocks = []
+ for block in blocks:
+ if block[0] == 1:
+ this_block = highlight(block[1])
+ else:
+ this_block = block[1].strip('\n')+'\n'
+ highlighted_blocks.append(this_block)
+
+ result = "\n".join(highlighted_blocks)
+ else:
+ result = highlight(answer).lstrip('\n')
+ return result
+
+def _visualize(answers, request_options, search_mode=False):
+
+ highlight = not bool(request_options and request_options.get('no-terminal'))
+ color_style = request_options.get('style', '')
+ if color_style not in COLOR_STYLES:
+ color_style = ''
+
+ found = True
+ result = ""
+ for answer_dict in answers:
+ topic = answer_dict['topic']
+ topic_type = answer_dict['topic_type']
+ answer = answer_dict['answer']
+ found = found and not topic_type == 'unknown'
+
+ if search_mode and topic != 'LIMITED':
+ if not highlight:
+ result += "\n[%s]\n" % topic
+ else:
+ result += "\n%s%s %s %s%s\n" % (
+ colored.bg('dark_gray'), colored.attr("res_underlined"),
+ topic,
+ colored.attr("res_underlined"), colored.attr('reset'))
+
+ if answer_dict['format'] in ['ansi', 'text']:
+ result += answer
+ elif topic == ':firstpage-v1':
+ result += fmt.internal.colorize_internal_firstpage_v1(answer)
+ elif topic == 'LIMITED':
+ result += _limited_answer(topic)
+ else:
+ result += _colorize_ansi_answer(
+ topic, answer, color_style,
+ highlight_all=highlight,
+ highlight_code=(topic_type == 'question'
+ and not request_options.get('add_comments')
+ and not request_options.get('remove_text')))
+
+
+ result = result.strip('\n') + "\n"
+ return result, found
diff --git a/lib/frontend/html.py b/lib/frontend/html.py
new file mode 100644
index 0000000..c2a88bd
--- /dev/null
+++ b/lib/frontend/html.py
@@ -0,0 +1,106 @@
+from gevent.monkey import patch_all
+from gevent.subprocess import Popen, PIPE
+patch_all()
+
+# pylint: disable=wrong-import-position,wrong-import-order
+import sys
+import os
+import re
+
+MYDIR = os.path.abspath(os.path.join(__file__, '..', '..'))
+sys.path.append("%s/lib/" % MYDIR)
+
+from globals import error, ANSI2HTML, GITHUB_REPOSITORY
+from buttons import TWITTER_BUTTON, GITHUB_BUTTON, GITHUB_BUTTON_FOOTER
+import frontend.ansi
+# pylint: disable=wrong-import-position,wrong-import-order
+
+def visualize(answer_data, request_options):
+ query = answer_data['query']
+ answers = answer_data['answers']
+ topics_list = answer_data['topics_list']
+ editable = (len(answers) == 1 and answers[0]['topic_type'] == 'cheat.sheets')
+
+ repository_button = ''
+ if len(answers) == 1:
+ repository_button = _github_button(answers[0]['topic_type'])
+
+ result, found = frontend.ansi.visualize(answer_data, request_options)
+ return _render_html(query, result, editable, repository_button, topics_list, request_options), found
+
+def _github_button(topic_type):
+
+ full_name = GITHUB_REPOSITORY.get(topic_type, '')
+ if not full_name:
+ return ''
+
+ short_name = full_name.split('/', 1)[1] # pylint: disable=unused-variable
+
+ button = (
+ ""
+ '%(short_name)s'
+ ) % locals()
+ return button
+
+def _render_html(query, result, editable, repository_button, topics_list, request_options):
+
+ def _html_wrapper(data):
+ """
+ Convert ANSI text `data` to HTML
+ """
+ proc = Popen(
+ ["bash", ANSI2HTML, "--palette=solarized", "--bg=dark"],
+ stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ data = data.encode('utf-8')
+ stdout, stderr = proc.communicate(data)
+ if proc.returncode != 0:
+ error(stdout + stderr)
+ return stdout.decode('utf-8')
+
+
+ result = result + "\n$"
+ result = _html_wrapper(result)
+ title = "cheat.sh/%s " % query
+ submit_button = ('')
+ topic_list = (''
+ % ("\n".join("" % x for x in topics_list)))
+
+ curl_line = "$ curl cheat.sh/"
+ if query == ':firstpage':
+ query = ""
+ form_html = (''
+ '%s%s'
+ ''
+ '%s'
+ '') \
+ % (submit_button, curl_line, query, topic_list)
+
+ edit_button = ''
+ if editable:
+ # It's possible that topic directory starts with omitted underscore
+ if '/' in query:
+ query = '_' + query
+ edit_page_link = 'https://github.com/chubin/cheat.sheets/edit/master/sheets/' + query
+ edit_button = (
+ '' + '[edit]' + '') % edit_page_link + result = re.sub("
", edit_button + form_html + "", result)
+ result = re.sub("", "" + title, result)
+ if not request_options.get('quiet'):
+ result = result.replace('',
+ TWITTER_BUTTON \
+ + GITHUB_BUTTON \
+ + repository_button \
+ + GITHUB_BUTTON_FOOTER \
+ + '')
+ return result
diff --git a/lib/get_answer.py b/lib/get_answer.py
deleted file mode 100644
index 6b35a54..0000000
--- a/lib/get_answer.py
+++ /dev/null
@@ -1,389 +0,0 @@
-"""
-Main module, answers hub.
-
-Exports:
-
- get_topics_list()
- get_topic_type()
- get_answer()
- find_answer_by_keyword()
-"""
-from __future__ import print_function
-
-import os
-import re
-import redis
-
-import beautifier
-from globals import REDISHOST, MAX_SEARCH_LEN
-from languages_data import LANGUAGE_ALIAS, SO_NAME, rewrite_editor_section_name
-
-import adapter.cheat_sheets
-import adapter.cmd
-import adapter.latenz
-import adapter.question
-import adapter.internal
-import adapter.rosetta
-import adapter.learnxiny
-
-class Router(object):
-
- """
- Implementation of query routing.
- Routing is done basing on the data exported by the adapters.
- (mainly by functions get_list() and is_found()).
-
- Function get_topics_list() returns available topics
- (that are accessible at /:list).
-
- Function get_topic_type() delivers name of the adapter,
- that will process the query.
-
- Refactoring of the class is almost done.
- The next steps to do:
- * _topic_list, _topic_found and topic_getters should be merged together.
- """
-
- def __init__(self):
-
- self._cached_topics_list = []
- self._cached_topic_type = {}
-
- self._adapter = {
- "internal": adapter.internal.InternalPages(
- get_topic_type=self.get_topic_type,
- get_topics_list=self.get_topics_list),
- "unknown": adapter.internal.UnknownPages(
- get_topic_type=self.get_topic_type,
- get_topics_list=self.get_topics_list),
- "tldr": adapter.cmd.Tldr(),
- "cheat": adapter.cmd.Cheat(),
- "fosdem": adapter.cmd.Fosdem(),
- "translation": adapter.cmd.Translation(),
- "rosetta": adapter.rosetta.Rosetta(),
- }
-
- self._topic_list = {
- "late.nz": adapter.latenz.get_list(),
- "cheat.sheets": adapter.cheat_sheets.get_list(),
- "cheat.sheets dir": adapter.cheat_sheets.get_dirs_list(),
- "learnxiny": adapter.learnxiny.get_learnxiny_list(),
- }
- for key, obj in self._adapter.items():
- self._topic_list[key] = obj.get_list()
-
- self._topic_found = {
- "late.nz": adapter.latenz.is_found,
- "cheat.sheets": adapter.cheat_sheets.is_found,
- "cheat.sheets dir": adapter.cheat_sheets.is_dir_found,
- "learnxiny": adapter.learnxiny.is_valid_learnxy,
- }
- for key, obj in self._adapter.items():
- self._topic_found[key] = obj.is_found
-
-# topic_type, function_getter
-# should be replaced with a decorator
-# pylint: disable=bad-whitespace
- self.topic_getters = (
- ("late.nz", adapter.latenz.get_answer),
- ("cheat.sheets", adapter.cheat_sheets.get_page),
- ("cheat.sheets dir", adapter.cheat_sheets.get_dir),
- ("learnxiny", adapter.learnxiny.get_learnxiny),
- ("question", adapter.question.get_page),
- ("fosdem", self._adapter["fosdem"].get_page),
- ("rosetta", self._adapter["rosetta"].get_page),
- ("tldr", self._adapter["tldr"].get_page),
- ("internal", self._adapter["internal"].get_page),
- ("cheat", self._adapter["cheat"].get_page),
- ("translation", self._adapter["translation"].get_page),
- ("unknown", self._adapter["unknown"].get_page),
- )
-# pylint: enable=bad-whitespace
-
- def get_topics_list(self, skip_dirs=False, skip_internal=False):
- """
- List of topics returned on /:list
- """
-
- if self._cached_topics_list:
- return self._cached_topics_list
-
- # merging all top level lists
- sources_to_merge = ['tldr', 'cheat', 'cheat.sheets', 'learnxiny', 'rosetta']
- if not skip_dirs:
- sources_to_merge.append("cheat.sheets dir")
- if not skip_internal:
- sources_to_merge.append("internal")
-
- answer = {}
- for key in sources_to_merge:
- answer.update({name:key for name in self._topic_list[key]})
- answer = sorted(set(answer.keys()))
-
- # doing it in this strange way to save the order of the topics
- for topic in adapter.learnxiny.get_learnxiny_list():
- if topic not in answer:
- answer.append(topic)
-
- self._cached_topics_list = answer
- return answer
-
- def get_topic_type(self, topic): # pylint: disable=too-many-locals,too-many-branches,too-many-statements
- """
- Return topic type for `topic` or "unknown" if topic can't be determined.
- """
-
- def __get_topic_type(topic):
-
- if topic == "":
- return "search"
- if re.match('[^/]*/rosetta(/|$)', topic):
- return "rosetta"
- if topic.startswith(":"):
- return "internal"
- if topic.endswith("/:list"):
- return "internal"
- if topic.endswith('/'):
- topic = topic.rstrip('/')
- if self._topic_found['cheat.sheets dir'](topic):
- return "cheat.sheets dir"
-
- for source in ['cheat.sheets', 'cheat', 'tldr', 'late.nz', 'fosdem']:
- if self._topic_found[source](topic):
- return source
-
- if '/' not in topic:
- #if '+' in topic_name:
- # return 'question'
- return "unknown"
-
- # topic contains '/'
- #
- if adapter.learnxiny.is_valid_learnxy(topic):
- return 'learnxiny'
- topic_type = topic.split('/', 1)[0]
- if topic_type in ['ru', 'fr'] or re.match(r'[a-z][a-z]-[a-z][a-z]$', topic_type):
- return 'translation'
- return 'question'
-
- if topic not in self._cached_topic_type:
- self._cached_topic_type[topic] = __get_topic_type(topic)
- print("%s %s" % (topic, self._cached_topic_type[topic]))
- return self._cached_topic_type[topic]
-
-if os.environ.get('REDIS_HOST', '').lower() != 'none':
- REDIS = redis.StrictRedis(host=REDISHOST, port=6379, db=0)
-else:
- REDIS = None
-
-_ROUTER = Router()
-
-get_topic_type = _ROUTER.get_topic_type
-get_topics_list = _ROUTER.get_topics_list
-TOPIC_GETTERS = _ROUTER.topic_getters
-
-def get_answer(topic, keyword, options="", request_options=None): # pylint: disable=too-many-locals,too-many-branches,too-many-statements
- """
- Find cheat sheet for the topic.
- If `keyword` is None or rempty, return the whole answer.
- Otherwise cut the paragraphs containing keywords.
-
- Args:
- topic (str): the name of the topic of the cheat sheet
- keyword (str): the name of the keywords to search in the cheat sheets
-
- Returns:
- string: the cheat sheet
- """
-
- def _join_paragraphs(paragraphs):
- answer = "\n".join(paragraphs)
- return answer
-
- def _split_paragraphs(text):
- answer = []
- paragraph = ""
- for line in text.splitlines():
- if line == "":
- answer.append(paragraph)
- paragraph = ""
- else:
- paragraph += line+"\n"
- answer.append(paragraph)
- return answer
-
- def _paragraph_contains(paragraph, keyword, insensitive=False, word_boundaries=True):
- """
- Check if `paragraph` contains `keyword`.
- Several keywords can be joined together using ~
- For example: ~ssh~passphrase
- """
- answer = True
-
- if '~' in keyword:
- keywords = keyword.split('~')
- else:
- keywords = [keyword]
-
- for kwrd in keywords:
- regex = re.escape(kwrd)
- if not word_boundaries:
- regex = r"\b%s\b" % kwrd
-
- if insensitive:
- answer = answer and bool(re.search(regex, paragraph, re.IGNORECASE))
- else:
- answer = answer and bool(re.search(regex, paragraph))
-
- return answer
-
- def _rewrite_aliases(word):
- if word == ':bash.completion':
- return ':bash_completion'
- return word
-
- def _rewrite_section_name(query):
- """
- """
- if '/' not in query:
- return query
-
- section_name, rest = query.split('/', 1)
-
- if ':' in section_name:
- # if ':' is in section_name, it means, that we want to
- # translate the answer in the specified human language
- # (experimental)
- language, section_name = section_name.split(':', 1)
- else:
- language = ""
-
- section_name = LANGUAGE_ALIAS.get(section_name, section_name)
-
- if language:
- section_name = language + ":" + section_name
-
- return "%s/%s" % (section_name, rest)
-
- def _rewrite_section_name_for_q(query):
- """
- FIXME: we rewrite the section name too earlier,
- what means that we have to use SO names everywhere,
- where actually canonified internal names shoud be used.
- After this thing is fixed, we should:
- * fix naming in cache
- * fix VIM_NAMES
- """
- if '/' not in query:
- return query
-
- section_name, rest = query.split('/', 1)
- if ':' in section_name:
- section_name = rewrite_editor_section_name(section_name)
-
- section_name = SO_NAME.get(section_name, section_name)
- return "%s/%s" % (section_name, rest)
-
-
- answer = None
- needs_beautification = False
-
- topic = _rewrite_aliases(topic)
- topic = _rewrite_section_name(topic)
-
- # this is pretty unoptimal
- # so this part should be rewritten
- # for the most queries we could say immediately
- # what type the query has
- topic_type = get_topic_type(topic)
-
- # checking if the answer is in the cache
- if topic != "":
- # temporary hack for "questions":
- # the topic name has to be prefixed with q:
- # so we can later delete them from redis
- # and we known that they need beautification
- #if '/' in topic and '+' in topic:
- if topic_type == 'question': #'/' in topic and '+' in topic:
- topic = _rewrite_section_name_for_q(topic)
- topic = "q:" + topic
- needs_beautification = True
-
- if REDIS:
- answer = REDIS.get(topic)
- if answer:
- answer = answer.decode('utf-8')
-
- # if answer was not found in the cache
- # try to find it in one of the repositories
- if not answer:
-
- for topic_getter_type, topic_getter in TOPIC_GETTERS:
- if topic_type == topic_getter_type:
- answer = topic_getter(topic, request_options=request_options)
- break
- if not answer:
- topic_type = "unknown"
- answer = dict(TOPIC_GETTERS)['unknown'](topic)
-
- # saving answers in the cache
- if REDIS:
- if topic_type not in ["search", "internal", "unknown"]:
- REDIS.set(topic, answer)
-
- if needs_beautification:
- filetype = 'bash'
- if '/' in topic:
- filetype = topic.split('/', 1)[0]
- if filetype.startswith('q:'):
- filetype = filetype[2:]
-
- answer = beautifier.beautify(answer.encode('utf-8'), filetype, request_options)
-
- if not keyword:
- return answer
-
- #
- # shorten the answer, because keyword is specified
- #
- insensitive = 'i' in options
- word_boundaries = 'b' in options
-
- paragraphs = _split_paragraphs(answer)
- paragraphs = [p for p in paragraphs
- if _paragraph_contains(p, keyword,
- insensitive=insensitive,
- word_boundaries=word_boundaries)]
- if paragraphs == []:
- return ""
-
- answer = _join_paragraphs(paragraphs)
- return answer
-
-def find_answer_by_keyword(directory, keyword, options="", request_options=None):
- """
- Search in the whole tree of all cheatsheets or in its subtree `directory`
- by `keyword`
- """
-
- recursive = 'r' in options
-
- answer_paragraphs = []
- for topic in get_topics_list(skip_internal=True, skip_dirs=True):
-
- if not topic.startswith(directory):
- continue
-
- subtopic = topic[len(directory):]
- if not recursive and '/' in subtopic:
- continue
-
- answer = get_answer(topic, keyword, options=options, request_options=request_options)
- if answer:
- answer_paragraphs.append((topic, answer))
-
- if len(answer_paragraphs) > MAX_SEARCH_LEN:
- answer_paragraphs.append(("LIMITED", "LIMITED TO %s ANSWERS" % MAX_SEARCH_LEN))
- break
-
- return answer_paragraphs
diff --git a/lib/postprocessing.py b/lib/postprocessing.py
new file mode 100644
index 0000000..d0b85e6
--- /dev/null
+++ b/lib/postprocessing.py
@@ -0,0 +1,87 @@
+import re
+import fmt.comments
+
+def postprocess(answer, keyword, options, request_options=None):
+ answer = _answer_add_comments(answer, request_options=request_options)
+ answer = _answer_filter_by_keyword(answer, keyword, options, request_options=request_options)
+ return answer
+
+def _answer_add_comments(answer, request_options=None):
+
+ if answer['format'] != 'text+code':
+ return answer
+
+ topic = answer['topic']
+ filetype = 'bash'
+ if '/' in topic:
+ filetype = topic.split('/', 1)[0]
+ if filetype.startswith('q:'):
+ filetype = filetype[2:]
+
+ answer['answer'] = fmt.comments.beautify(
+ answer['answer'].encode('utf-8'), filetype, request_options)
+ answer['format'] = 'code'
+ return answer
+
+def _answer_filter_by_keyword(answer, keyword, options, request_options=None):
+ answer['answer'] = _filter_by_keyword(answer['answer'], keyword, options)
+ return answer
+
+def _filter_by_keyword(answer, keyword, options):
+
+ def _join_paragraphs(paragraphs):
+ answer = "\n".join(paragraphs)
+ return answer
+
+ def _split_paragraphs(text):
+ answer = []
+ paragraph = ""
+ for line in text.splitlines():
+ if line == "":
+ answer.append(paragraph)
+ paragraph = ""
+ else:
+ paragraph += line+"\n"
+ answer.append(paragraph)
+ return answer
+
+ def _paragraph_contains(paragraph, keyword, insensitive=False, word_boundaries=True):
+ """
+ Check if `paragraph` contains `keyword`.
+ Several keywords can be joined together using ~
+ For example: ~ssh~passphrase
+ """
+ answer = True
+
+ if '~' in keyword:
+ keywords = keyword.split('~')
+ else:
+ keywords = [keyword]
+
+ for kwrd in keywords:
+ regex = re.escape(kwrd)
+ if not word_boundaries:
+ regex = r"\b%s\b" % kwrd
+
+ if insensitive:
+ answer = answer and bool(re.search(regex, paragraph, re.IGNORECASE))
+ else:
+ answer = answer and bool(re.search(regex, paragraph))
+
+ return answer
+
+
+ if not keyword:
+ return answer
+
+ search_options = {
+ 'insensitive': 'i' in options,
+ 'word_boundaries': 'b' in options
+ }
+
+ paragraphs = [p for p in _split_paragraphs(answer)
+ if _paragraph_contains(p, keyword, **search_options)]
+ if not paragraphs:
+ return ""
+
+ return _join_paragraphs(paragraphs)
diff --git a/lib/routing.py b/lib/routing.py
new file mode 100644
index 0000000..ebc70a8
--- /dev/null
+++ b/lib/routing.py
@@ -0,0 +1,180 @@
+"""
+Queries routing and caching.
+
+Exports:
+
+ get_topics_list()
+ get_answer_dict()
+"""
+from __future__ import print_function
+
+import re
+
+import cache
+import adapter.cheat_sheets
+import adapter.cmd
+import adapter.internal
+import adapter.latenz
+import adapter.learnxiny
+import adapter.question
+import adapter.rosetta
+
+class Router(object):
+
+ """
+ Implementation of query routing. Routing is based on `routing_table`
+ and the data exported by the adapters (functions `get_list()` and `is_found()`).
+
+ `get_topics_list()` returns available topics (accessible at /:list).
+ `get_answer_dict()` return answer for the query.
+ """
+
+ routing_table = [
+ ("^$", "search"),
+ ("^[^/]*/rosetta(/|$)", "rosetta"),
+ ("^:", "internal"),
+ ("/:list$", "internal"),
+ ("/$", "cheat.sheets dir"),
+ ("", "cheat.sheets"),
+ ("", "cheat"),
+ ("", "tldr"),
+ ("", "late.nz"),
+ ("", "fosdem"),
+ ("^[^/]*$", "unknown"),
+ ("", "learnxiny"),
+ ("^[a-z][a-z]-[a-z][a-z]$", "translation"),
+ ]
+
+ def __init__(self):
+
+ self._cached_topics_list = []
+ self._cached_topic_type = {}
+
+ self._adapter = {
+ "internal": adapter.internal.InternalPages(
+ get_topic_type=self.get_topic_type,
+ get_topics_list=self.get_topics_list),
+ "unknown": adapter.internal.UnknownPages(
+ get_topic_type=self.get_topic_type,
+ get_topics_list=self.get_topics_list),
+ "search": adapter.internal.Search(),
+ "tldr": adapter.cmd.Tldr(),
+ "cheat": adapter.cmd.Cheat(),
+ "fosdem": adapter.cmd.Fosdem(),
+ "translation": adapter.cmd.Translation(),
+ "rosetta": adapter.rosetta.Rosetta(),
+ "late.nz": adapter.latenz.Latenz(),
+ "question": adapter.question.Question(),
+ "cheat.sheets": adapter.cheat_sheets.CheatSheets(),
+ "cheat.sheets dir": adapter.cheat_sheets.CheatSheetsDir(),
+ "learnxiny": adapter.learnxiny.LearnXinY(),
+ }
+
+ self._topic_list = {
+ key: obj.get_list()
+ for key, obj in self._adapter.items()
+ }
+
+ def get_topics_list(self, skip_dirs=False, skip_internal=False):
+ """
+ List of topics returned on /:list
+ """
+
+ if self._cached_topics_list:
+ return self._cached_topics_list
+
+ sources_to_merge = ['tldr', 'cheat', 'cheat.sheets', 'learnxiny', 'rosetta']
+ if not skip_dirs:
+ sources_to_merge.append("cheat.sheets dir")
+ if not skip_internal:
+ sources_to_merge.append("internal")
+
+ answer = {}
+ for key in sources_to_merge:
+ answer.update({name:key for name in self._topic_list[key]})
+ answer = sorted(set(answer.keys()))
+
+ self._cached_topics_list = answer
+ return answer
+
+ def get_topic_type(self, topic):
+ """
+ Return topic type for `topic` or "unknown" if topic can't be determined.
+ """
+
+ def __get_topic_type(topic):
+
+ for regexp, route in self.routing_table:
+ if re.search(regexp, topic):
+ if route in self._adapter:
+ if self._adapter[route].is_found(topic):
+ return route
+ else:
+ return route
+ return 'question'
+
+ if topic not in self._cached_topic_type:
+ self._cached_topic_type[topic] = __get_topic_type(topic)
+ return self._cached_topic_type[topic]
+
+ def _get_page_dict(self, query, request_options=None):
+ """
+ Return answer_dict for the `query`.
+ """
+
+ topic_type = self.get_topic_type(query)
+ return self._adapter[topic_type]\
+ .get_page_dict(query, request_options=request_options)
+
+ def get_answer_dict(self, topic, request_options=None):
+ """
+ Find cheat sheet for the topic.
+
+ Args:
+ `topic` (str): the name of the topic of the cheat sheet
+
+ Returns:
+ answer_dict: the answer dictionary
+ """
+
+ topic_type = self.get_topic_type(topic)
+
+ # 'question' queries are pretty expensive, that's why they should be handled
+ # in a special way:
+ # we do not drop the old style cache entries and try to reuse them if possible
+ if topic_type == 'question':
+ answer = cache.get('q:' + topic)
+ if answer:
+ if isinstance(answer, dict):
+ return answer
+ return {
+ 'topic': topic,
+ 'topic_type': 'question',
+ 'answer': answer,
+ 'format': 'text+code',
+ }
+
+ answer = self._get_page_dict(topic, request_options=request_options)
+ cache.put('q:' + topic, answer)
+ return answer
+
+ # Try to find cacheable queries in the cache.
+ # If answer was not found in the cache, resolve it in a normal way and save in the cache
+ cache_needed = self._adapter[topic_type].is_cache_needed()
+ if cache_needed:
+ answer = cache.get(topic)
+ if not isinstance(answer, dict):
+ answer = None
+ if answer:
+ return answer
+
+ answer = self._get_page_dict(topic, request_options=request_options)
+
+ if cache_needed and answer:
+ cache.put(topic, answer)
+ return answer
+
+# pylint: disable=invalid-name
+_ROUTER = Router()
+get_topics_list = _ROUTER.get_topics_list
+get_answer_dict = _ROUTER.get_answer_dict
diff --git a/lib/search.py b/lib/search.py
new file mode 100644
index 0000000..981596f
--- /dev/null
+++ b/lib/search.py
@@ -0,0 +1,59 @@
+"""
+Very naive search implementation. Just a placeholder.
+
+Exports:
+
+ find_answer_by_keyword()
+
+It should be implemented on the adapter basis:
+
+ 1. adapter.search(keyword) returns list of matching answers
+ * maybe with some initial weight
+ 2. ranking is done
+ 3. sorted results are returned
+ 4. eage page are cut by keyword
+ 5. results are paginated
+
+"""
+
+from globals import MAX_SEARCH_LEN
+from routing import get_answer_dict, get_topics_list
+
+def _limited_entry():
+ return {
+ 'topic_type': 'LIMITED',
+ "topic": "LIMITED",
+ 'answer': "LIMITED TO %s ANSWERS" % MAX_SEARCH_LEN,
+ 'format': "code",
+ }
+
+def find_answers_by_keyword(directory, keyword, options="", request_options=None):
+ """
+ Search in the whole tree of all cheatsheets or in its subtree `directory`
+ by `keyword`
+ """
+
+ recursive = 'r' in options
+
+ answers_found = []
+ for topic in get_topics_list(skip_internal=True, skip_dirs=True):
+
+ if not topic.startswith(directory):
+ continue
+
+ subtopic = topic[len(directory):]
+ if not recursive and '/' in subtopic:
+ continue
+
+ answer = get_answer_dict(topic, request_options=request_options)
+
+ if answer and answer.get('answer') and keyword.lower() in answer.get('answer', '').lower():
+ answers_found.append(answer)
+
+ if len(answers_found) > MAX_SEARCH_LEN:
+ answers_found.append(
+ _limited_entry()
+ )
+ break
+
+ return answers_found
diff --git a/tests/results/0 b/tests/results/0
deleted file mode 100644
index 928091f..0000000
--- a/tests/results/0
+++ /dev/null
@@ -1,17 +0,0 @@
-1line
-:learn
-Advanced
-Classes
-Comments
-Control_Flow
-Functions
-Modules
-Primitive_Datatypes_and_Operators
-Variables_and_Collections
-doc
-func
-hello
-lambda
-loops
-recursion
-rosetta/
diff --git a/tests/results/1 b/tests/results/1
index 7b1f52f..b83ed07 100644
--- a/tests/results/1
+++ b/tests/results/1
@@ -1,17 +1,18 @@
-[38;5;246m# Displays everything in the target directory[39m
-[38;5;252mls[39m[38;5;252m [39m[38;5;252mpath/to/the/target/directory[39m
-
-[38;5;246m# Displays everything including hidden files[39m
-[38;5;252mls[39m[38;5;252m [39m[38;5;252m-a[39m
-
-[38;5;246m# Displays all files, along with the size (with unit suffixes) and timestamp[39m
-[38;5;252mls[39m[38;5;252m [39m[38;5;252m-lh[39m[38;5;252m [39m
-
-[38;5;246m# Display files, sorted by size[39m
-[38;5;252mls[39m[38;5;252m [39m[38;5;252m-S[39m
-
-[38;5;246m# Display directories only[39m
-[38;5;252mls[39m[38;5;252m [39m[38;5;252m-d[39m[38;5;252m [39m[38;5;252m*/[39m
-
-[38;5;246m# Display directories only, include hidden[39m
-[38;5;252mls[39m[38;5;252m [39m[38;5;252m-d[39m[38;5;252m [39m[38;5;252m.*/[39m[38;5;252m [39m[38;5;252m*/[39m
+1line
+:learn
+:list
+Advanced
+Classes
+Comments
+Control_Flow
+Functions
+Modules
+Primitive_Datatypes_and_Operators
+Variables_and_Collections
+doc
+func
+hello
+lambda
+loops
+recursion
+rosetta/
diff --git a/tests/results/10 b/tests/results/10
index d0db3d4..cc8bdf8 100644
--- a/tests/results/10
+++ b/tests/results/10
@@ -1,3 +1,16 @@
+[38;5;246m# How do I copy a file in Python?[39m
+[38;5;246m# [39m
+[38;5;246m# shutil (http://docs.python.org/2/library/shutil.html) has many methods[39m
+[38;5;246m# you can use. One of which is:[39m
+
[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mshutil[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252mcopyfile[39m
[38;5;252mcopyfile[39m[38;5;252m([39m[38;5;252msrc[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mdst[39m[38;5;252m)[39m
+
+[38;5;246m# Copy the contents of the file named src to a file named dst. The[39m
+[38;5;246m# destination location must be writable; otherwise, an IOError exception[39m
+[38;5;246m# will be raised. If dst already exists, it will be replaced. Special[39m
+[38;5;246m# files such as character or block devices and pipes cannot be copied[39m
+[38;5;246m# with this function. src and dst are path names given as strings.[39m
+[38;5;246m# [39m
+[38;5;246m# [Swati] [so/q/123198] [cc by-sa 3.0][39m
diff --git a/tests/results/11 b/tests/results/11
index 7c53265..d0db3d4 100644
--- a/tests/results/11
+++ b/tests/results/11
@@ -1,3 +1,3 @@
-from shutil import copyfile
+[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mshutil[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252mcopyfile[39m
-copyfile(src, dst)
+[38;5;252mcopyfile[39m[38;5;252m([39m[38;5;252msrc[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mdst[39m[38;5;252m)[39m
diff --git a/tests/results/12 b/tests/results/12
index 8794df4..7c53265 100644
--- a/tests/results/12
+++ b/tests/results/12
@@ -1,32 +1,3 @@
- _ _ _ [38;2;0;204;0m_[0m[38;2;0;204;0m_[0m[38;2;0;204;0m [0m[38;2;0;204;0m [0m[38;2;0;204;0m [0m
- ___| |__ ___ __ _| |_ ___| |__ [38;2;0;204;0m\[0m[38;2;0;204;0m [0m[38;2;0;204;0m\[0m[38;2;0;204;0m [0m[38;2;0;204;0m [0m [48;2;85;85;85m [0m[48;2;85;85;85m [0m[48;2;85;85;85m [0m[48;2;85;85;85mT[0m[48;2;85;85;85mh[0m[48;2;85;85;85me[0m[48;2;85;85;85m [0m[48;2;85;85;85mo[0m[48;2;85;85;85mn[0m[48;2;85;85;85ml[0m[48;2;85;85;85my[0m[48;2;85;85;85m [0m[48;2;85;85;85mc[0m[48;2;85;85;85mh[0m[48;2;85;85;85me[0m[48;2;85;85;85ma[0m[48;2;85;85;85mt[0m[48;2;85;85;85m [0m[48;2;85;85;85ms[0m[48;2;85;85;85mh[0m[48;2;85;85;85me[0m[48;2;85;85;85me[0m[48;2;85;85;85mt[0m[48;2;85;85;85m [0m[48;2;85;85;85my[0m[48;2;85;85;85mo[0m[48;2;85;85;85mu[0m[48;2;85;85;85m [0m[48;2;85;85;85mn[0m[48;2;85;85;85me[0m[48;2;85;85;85me[0m[48;2;85;85;85md[0m[48;2;85;85;85m [0m[48;2;85;85;85m [0m[48;2;85;85;85m [0m
- / __| '_ \ / _ \/ _` | __| / __| '_ \ [38;2;0;204;0m\[0m[38;2;0;204;0m [0m[38;2;0;204;0m\[0m[38;2;0;204;0m [0m [38;2;136;136;136mU[0m[38;2;136;136;136mn[0m[38;2;136;136;136mi[0m[38;2;136;136;136mf[0m[38;2;136;136;136mi[0m[38;2;136;136;136me[0m[38;2;136;136;136md[0m[38;2;136;136;136m [0m[38;2;136;136;136ma[0m[38;2;136;136;136mc[0m[38;2;136;136;136mc[0m[38;2;136;136;136me[0m[38;2;136;136;136ms[0m[38;2;136;136;136ms[0m[38;2;136;136;136m [0m[38;2;136;136;136mt[0m[38;2;136;136;136mo[0m[38;2;136;136;136m [0m[38;2;136;136;136mt[0m[38;2;136;136;136mh[0m[38;2;136;136;136me[0m[38;2;136;136;136m [0m[38;2;136;136;136mb[0m[38;2;136;136;136me[0m[38;2;136;136;136ms[0m[38;2;136;136;136mt[0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m
-| (__| | | | __/ (_| | |_ _\__ \ | | |[38;2;0;204;0m/[0m[38;2;0;204;0m [0m[38;2;0;204;0m/[0m[38;2;0;204;0m [0m [38;2;136;136;136mc[0m[38;2;136;136;136mo[0m[38;2;136;136;136mm[0m[38;2;136;136;136mm[0m[38;2;136;136;136mu[0m[38;2;136;136;136mn[0m[38;2;136;136;136mi[0m[38;2;136;136;136mt[0m[38;2;136;136;136my[0m[38;2;136;136;136m [0m[38;2;136;136;136md[0m[38;2;136;136;136mr[0m[38;2;136;136;136mi[0m[38;2;136;136;136mv[0m[38;2;136;136;136me[0m[38;2;136;136;136mn[0m[38;2;136;136;136m [0m[38;2;136;136;136md[0m[38;2;136;136;136mo[0m[38;2;136;136;136mc[0m[38;2;136;136;136mu[0m[38;2;136;136;136mm[0m[38;2;136;136;136me[0m[38;2;136;136;136mn[0m[38;2;136;136;136mt[0m[38;2;136;136;136ma[0m[38;2;136;136;136mt[0m[38;2;136;136;136mi[0m[38;2;136;136;136mo[0m[38;2;136;136;136mn[0m
- \___|_| |_|\___|\__,_|\__(_)___/_| |_[38;2;0;204;0m/[0m[38;2;0;204;0m_[0m[38;2;0;204;0m/[0m[38;2;0;204;0m [0m[38;2;0;204;0m [0m [38;2;136;136;136mr[0m[38;2;136;136;136me[0m[38;2;136;136;136mp[0m[38;2;136;136;136mo[0m[38;2;136;136;136ms[0m[38;2;136;136;136mi[0m[38;2;136;136;136mt[0m[38;2;136;136;136mo[0m[38;2;136;136;136mr[0m[38;2;136;136;136mi[0m[38;2;136;136;136me[0m[38;2;136;136;136ms[0m[38;2;136;136;136m [0m[38;2;136;136;136mo[0m[38;2;136;136;136mf[0m[38;2;136;136;136m [0m[38;2;136;136;136mt[0m[38;2;136;136;136mh[0m[38;2;136;136;136me[0m[38;2;136;136;136m [0m[38;2;136;136;136mw[0m[38;2;136;136;136mo[0m[38;2;136;136;136mr[0m[38;2;136;136;136ml[0m[38;2;136;136;136md[0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m
-
-[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
-[38;2;136;136;136m|[0m $ [38;2;0;204;0mc[0m[38;2;0;204;0mu[0m[38;2;0;204;0mr[0m[38;2;0;204;0ml[0m [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204me[0m[38;2;0;170;204ma[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m/[0m[38;2;0;170;204ml[0m[38;2;0;170;204ms[0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;204;0mc[0m[38;2;0;204;0mh[0m[38;2;0;204;0mt[0m[38;2;0;204;0m.[0m[38;2;0;204;0ms[0m[38;2;0;204;0mh[0m [38;2;0;170;204mb[0m[38;2;0;170;204mt[0m[38;2;0;170;204mr[0m[38;2;0;170;204mf[0m[38;2;0;170;204ms[0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m [0m[38;2;0;170;204ml[0m[38;2;0;170;204mu[0m[38;2;0;170;204ma[0m[38;2;0;170;204m/[0m[38;2;0;204;0m:[0m[38;2;0;204;0ml[0m[38;2;0;204;0me[0m[38;2;0;204;0ma[0m[38;2;0;204;0mr[0m[38;2;0;204;0mn[0m [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m $ [38;2;0;204;0mc[0m[38;2;0;204;0mu[0m[38;2;0;204;0mr[0m[38;2;0;204;0ml[0m [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m/[0m[38;2;0;170;204mb[0m[38;2;0;170;204mt[0m[38;2;0;170;204mr[0m[38;2;0;170;204mf[0m[38;2;0;170;204ms[0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;204;0mc[0m[38;2;0;204;0mh[0m[38;2;0;204;0mt[0m[38;2;0;204;0m.[0m[38;2;0;204;0ms[0m[38;2;0;204;0mh[0m [38;2;0;170;204mt[0m[38;2;0;170;204ma[0m[38;2;0;170;204mr[0m[38;2;0;170;204m~[0m[38;2;0;170;204ml[0m[38;2;0;170;204mi[0m[38;2;0;170;204ms[0m[38;2;0;170;204mt[0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m Learn any[38;2;255;0;0m*[0m programming [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m $ [38;2;0;204;0mc[0m[38;2;0;204;0mu[0m[38;2;0;204;0mr[0m[38;2;0;204;0ml[0m [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m/[0m[38;2;0;170;204mt[0m[38;2;0;170;204ma[0m[38;2;0;170;204mr[0m[38;2;0;170;204m~[0m[38;2;0;170;204ml[0m[38;2;0;170;204mi[0m[38;2;0;170;204ms[0m[38;2;0;170;204mt[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m language not leaving [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mu[0m[38;2;0;170;204mr[0m[38;2;0;170;204ml[0m [38;2;0;204;0mh[0m[38;2;0;204;0mt[0m[38;2;0;204;0mt[0m[38;2;0;204;0mp[0m[38;2;0;204;0ms[0m[38;2;0;204;0m:[0m[38;2;0;170;204m/[0m[38;2;0;170;204m/[0m[38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m [0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m your shell [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;255;0;0m*[0m[38;2;255;0;0m)[0m any of 60 [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m
-[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;204;204;0m [0m[38;2;204;204;0mq[0m[38;2;204;204;0mu[0m[38;2;204;204;0me[0m[38;2;204;204;0mr[0m[38;2;204;204;0mi[0m[38;2;204;204;0me[0m[38;2;204;204;0ms[0m[38;2;204;204;0m [0m[38;2;204;204;0mw[0m[38;2;204;204;0mi[0m[38;2;204;204;0mt[0m[38;2;204;204;0mh[0m[38;2;204;204;0m [0m[38;2;204;204;0mc[0m[38;2;204;204;0mu[0m[38;2;204;204;0mr[0m[38;2;204;204;0ml[0m[38;2;204;204;0m [0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m [38;2;204;204;0mo[0m[38;2;204;204;0mw[0m[38;2;204;204;0mn[0m[38;2;204;204;0m [0m[38;2;204;204;0mo[0m[38;2;204;204;0mp[0m[38;2;204;204;0mt[0m[38;2;204;204;0mi[0m[38;2;204;204;0mo[0m[38;2;204;204;0mn[0m[38;2;204;204;0ma[0m[38;2;204;204;0ml[0m[38;2;204;204;0m [0m[38;2;204;204;0mc[0m[38;2;204;204;0ml[0m[38;2;204;204;0mi[0m[38;2;204;204;0me[0m[38;2;204;204;0mn[0m[38;2;204;204;0mt[0m [38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m [38;2;204;204;0ml[0m[38;2;204;204;0me[0m[38;2;204;204;0ma[0m[38;2;204;204;0mr[0m[38;2;204;204;0mn[0m[38;2;204;204;0m,[0m[38;2;204;204;0m [0m[38;2;204;204;0ml[0m[38;2;204;204;0me[0m[38;2;204;204;0ma[0m[38;2;204;204;0mr[0m[38;2;204;204;0mn[0m[38;2;204;204;0m,[0m[38;2;204;204;0m [0m[38;2;204;204;0ml[0m[38;2;204;204;0me[0m[38;2;204;204;0ma[0m[38;2;204;204;0mr[0m[38;2;204;204;0mn[0m[38;2;204;204;0m![0m [38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
-[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
-[38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m [0m[38;2;0;170;204mg[0m[38;2;0;170;204mo[0m[38;2;0;170;204m/[0m[38;2;0;170;204mf[0m[38;2;0;204;0m<[0m[38;2;0;204;0mt[0m[38;2;0;204;0ma[0m[38;2;0;204;0mb[0m[38;2;0;204;0m>[0m[38;2;0;204;0m<[0m[38;2;0;204;0mt[0m[38;2;0;204;0ma[0m[38;2;0;204;0mb[0m[38;2;0;204;0m>[0m[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m [38;2;0;204;0m-[0m[38;2;0;204;0m-[0m[38;2;0;204;0ms[0m[38;2;0;204;0mh[0m[38;2;0;204;0me[0m[38;2;0;204;0ml[0m[38;2;0;204;0ml[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m [38;2;0;204;0mg[0m[38;2;0;204;0mo[0m[38;2;0;204;0m [0m[38;2;0;204;0mz[0m[38;2;0;204;0mi[0m[38;2;0;204;0mp[0m[38;2;0;204;0m [0m[38;2;0;204;0ml[0m[38;2;0;204;0mi[0m[38;2;0;204;0ms[0m[38;2;0;204;0mt[0m[38;2;0;204;0ms[0m [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m go/for go/func [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;34;170;34mc[0m[38;2;34;170;34mh[0m[38;2;34;170;34mt[0m[38;2;34;170;34m.[0m[38;2;34;170;34ms[0m[38;2;34;170;34mh[0m[38;2;34;170;34m>[0m help [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m Ask any question using [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m $ cht.sh go/for [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m ... [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m cht.sh or curl cht.sh: [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m ... [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m /go[38;2;34;170;34m/[0mzip[38;2;34;170;34m+[0mlists [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m (use [38;2;34;170;34m/[0m,[38;2;34;170;34m+[0m when curling) [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m
-[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;204;204;0m [0m[38;2;204;204;0mT[0m[38;2;204;204;0mA[0m[38;2;204;204;0mB[0m[38;2;204;204;0m-[0m[38;2;204;204;0mc[0m[38;2;204;204;0mo[0m[38;2;204;204;0mm[0m[38;2;204;204;0mp[0m[38;2;204;204;0ml[0m[38;2;204;204;0me[0m[38;2;204;204;0mt[0m[38;2;204;204;0mi[0m[38;2;204;204;0mo[0m[38;2;204;204;0mn[0m[38;2;204;204;0m [0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;204;204;0m [0m[38;2;204;204;0mi[0m[38;2;204;204;0mn[0m[38;2;204;204;0mt[0m[38;2;204;204;0me[0m[38;2;204;204;0mr[0m[38;2;204;204;0ma[0m[38;2;204;204;0mc[0m[38;2;204;204;0mt[0m[38;2;204;204;0mi[0m[38;2;204;204;0mv[0m[38;2;204;204;0me[0m[38;2;204;204;0m [0m[38;2;204;204;0ms[0m[38;2;204;204;0mh[0m[38;2;204;204;0me[0m[38;2;204;204;0ml[0m[38;2;204;204;0ml[0m[38;2;204;204;0m [0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;204;204;0m [0m[38;2;204;204;0mp[0m[38;2;204;204;0mr[0m[38;2;204;204;0mo[0m[38;2;204;204;0mg[0m[38;2;204;204;0mr[0m[38;2;204;204;0ma[0m[38;2;204;204;0mm[0m[38;2;204;204;0mm[0m[38;2;204;204;0mi[0m[38;2;204;204;0mn[0m[38;2;204;204;0mg[0m[38;2;204;204;0m [0m[38;2;204;204;0mq[0m[38;2;204;204;0mu[0m[38;2;204;204;0me[0m[38;2;204;204;0ms[0m[38;2;204;204;0mt[0m[38;2;204;204;0mi[0m[38;2;204;204;0mo[0m[38;2;204;204;0mn[0m[38;2;204;204;0ms[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
-[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
-[38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mu[0m[38;2;0;170;204mr[0m[38;2;0;170;204ml[0m[38;2;0;170;204m [0m[38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m/[0m[38;2;0;204;0m:[0m[38;2;0;204;0mh[0m[38;2;0;204;0me[0m[38;2;0;204;0ml[0m[38;2;0;204;0mp[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;204;0mv[0m[38;2;0;204;0mi[0m[38;2;0;204;0mm[0m [38;2;0;170;204mp[0m[38;2;0;170;204mr[0m[38;2;0;170;204mg[0m[38;2;0;170;204m.[0m[38;2;0;170;204mp[0m[38;2;0;170;204my[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;204;0mt[0m[38;2;0;204;0mi[0m[38;2;0;204;0mm[0m[38;2;0;204;0me[0m curl cht.sh/ [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m see [38;2;34;170;34m/[0m[38;2;34;170;34m:[0m[38;2;34;170;34mh[0m[38;2;34;170;34me[0m[38;2;34;170;34ml[0m[38;2;34;170;34mp[0m and [38;2;34;170;34m/[0m[38;2;34;170;34m:[0m[38;2;34;170;34mi[0m[38;2;34;170;34mn[0m[38;2;34;170;34mt[0m[38;2;34;170;34mr[0m[38;2;34;170;34mo[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m ... [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m ... [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m for usage information [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m zip lists _ [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m real [38;2;34;170;34m0[0m[38;2;34;170;34mm[0m[38;2;34;170;34m0[0m[38;2;34;170;34m.[0m[38;2;34;170;34m0[0m[38;2;34;170;34m7[0m[38;2;34;170;34m5[0m[38;2;34;170;34ms[0m [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m and README.md on [38;2;0;170;204mG[0m[38;2;0;170;204mi[0m[38;2;0;170;204mt[0m[38;2;0;170;204mH[0m[38;2;0;170;204mu[0m[38;2;0;170;204mb[0m[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;34;170;34m<[0m[38;2;34;170;34ml[0m[38;2;34;170;34me[0m[38;2;34;170;34ma[0m[38;2;34;170;34md[0m[38;2;34;170;34me[0m[38;2;34;170;34mr[0m[38;2;34;170;34m>[0m[38;2;34;170;34mK[0m[38;2;34;170;34mK[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m for the details [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m *awesome* [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m
-[38;2;136;136;136m|[0m [38;2;255;0;0m*[0m[38;2;255;0;0ms[0m[38;2;255;0;0mt[0m[38;2;255;0;0ma[0m[38;2;255;0;0mr[0m[38;2;255;0;0mt[0m[38;2;255;0;0m [0m[38;2;255;0;0mh[0m[38;2;255;0;0me[0m[38;2;255;0;0mr[0m[38;2;255;0;0me[0m[38;2;255;0;0m*[0m[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m
-[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m [38;2;204;204;0ms[0m[38;2;204;204;0me[0m[38;2;204;204;0ml[0m[38;2;204;204;0mf[0m[38;2;204;204;0m-[0m[38;2;204;204;0md[0m[38;2;204;204;0mo[0m[38;2;204;204;0mc[0m[38;2;204;204;0mu[0m[38;2;204;204;0mm[0m[38;2;204;204;0me[0m[38;2;204;204;0mn[0m[38;2;204;204;0mt[0m[38;2;204;204;0me[0m[38;2;204;204;0md[0m [38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m [38;2;204;204;0mq[0m[38;2;204;204;0mu[0m[38;2;204;204;0me[0m[38;2;204;204;0mr[0m[38;2;204;204;0mi[0m[38;2;204;204;0me[0m[38;2;204;204;0ms[0m[38;2;204;204;0m [0m[38;2;204;204;0mf[0m[38;2;204;204;0mr[0m[38;2;204;204;0mo[0m[38;2;204;204;0mm[0m[38;2;204;204;0m [0m[38;2;204;204;0me[0m[38;2;204;204;0md[0m[38;2;204;204;0mi[0m[38;2;204;204;0mt[0m[38;2;204;204;0mo[0m[38;2;204;204;0mr[0m[38;2;204;204;0m![0m [38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m [38;2;204;204;0mi[0m[38;2;204;204;0mn[0m[38;2;204;204;0ms[0m[38;2;204;204;0mt[0m[38;2;204;204;0ma[0m[38;2;204;204;0mn[0m[38;2;204;204;0mt[0m[38;2;204;204;0m [0m[38;2;204;204;0ma[0m[38;2;204;204;0mn[0m[38;2;204;204;0ms[0m[38;2;204;204;0mw[0m[38;2;204;204;0me[0m[38;2;204;204;0mr[0m[38;2;204;204;0ms[0m [38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
-
-[38;2;136;136;136m[[0m[38;2;136;136;136mF[0m[38;2;136;136;136mo[0m[38;2;136;136;136ml[0m[38;2;136;136;136ml[0m[38;2;136;136;136mo[0m[38;2;136;136;136mw[0m[38;2;136;136;136m [0m[38;2;136;136;136m@[0m[38;2;136;136;136mi[0m[38;2;136;136;136mg[0m[38;2;136;136;136mo[0m[38;2;136;136;136mr[0m[38;2;136;136;136m_[0m[38;2;136;136;136mc[0m[38;2;136;136;136mh[0m[38;2;136;136;136mu[0m[38;2;136;136;136mb[0m[38;2;136;136;136mi[0m[38;2;136;136;136mn[0m[38;2;136;136;136m [0m[38;2;136;136;136mf[0m[38;2;136;136;136mo[0m[38;2;136;136;136mr[0m[38;2;136;136;136m [0m[38;2;136;136;136mu[0m[38;2;136;136;136mp[0m[38;2;136;136;136md[0m[38;2;136;136;136ma[0m[38;2;136;136;136mt[0m[38;2;136;136;136me[0m[38;2;136;136;136ms[0m[38;2;136;136;136m][0m[38;2;136;136;136m[[0m[38;2;136;136;136mg[0m[38;2;136;136;136mi[0m[38;2;136;136;136mt[0m[38;2;136;136;136mh[0m[38;2;136;136;136mu[0m[38;2;136;136;136mb[0m[38;2;136;136;136m.[0m[38;2;136;136;136mc[0m[38;2;136;136;136mo[0m[38;2;136;136;136mm[0m[38;2;136;136;136m/[0m[38;2;136;136;136mc[0m[38;2;136;136;136mh[0m[38;2;136;136;136mu[0m[38;2;136;136;136mb[0m[38;2;136;136;136mi[0m[38;2;136;136;136mn[0m[38;2;136;136;136m/[0m[38;2;136;136;136mc[0m[38;2;136;136;136mh[0m[38;2;136;136;136me[0m[38;2;136;136;136ma[0m[38;2;136;136;136mt[0m[38;2;136;136;136m.[0m[38;2;136;136;136ms[0m[38;2;136;136;136mh[0m[38;2;136;136;136m][0m
+from shutil import copyfile
+
+copyfile(src, dst)
diff --git a/tests/results/14 b/tests/results/14
index 12fb820..8794df4 100644
--- a/tests/results/14
+++ b/tests/results/14
@@ -1,772 +1,32 @@
-[38;5;246m# Single line comments start with a number symbol.[39m
-
-[38;5;214m""" Multiline strings can be written[39m
-[38;5;214m using three "s, and are often used[39m
-[38;5;214m as comments[39m
-[38;5;214m"""[39m
-
-[38;5;246m####################################################[39m
-[38;5;246m# 1. Primitive Datatypes and Operators[39m
-[38;5;246m####################################################[39m
-
-[38;5;246m# You have numbers[39m
-[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => 3[39m
-
-[38;5;246m# Math is what you would expect[39m
-[38;5;67m1[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => 2[39m
-[38;5;67m8[39m[38;5;252m [39m[38;5;252m-[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => 7[39m
-[38;5;67m10[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => 20[39m
-[38;5;67m35[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m [39m[38;5;246m# => 7[39m
-
-[38;5;246m# Division is a bit tricky. It is integer division and floors the results[39m
-[38;5;246m# automatically.[39m
-[38;5;67m5[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => 2[39m
-
-[38;5;246m# To fix division we need to learn about floats.[39m
-[38;5;67m2.0[39m[38;5;252m [39m[38;5;246m# This is a float[39m
-[38;5;67m11.0[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m4.0[39m[38;5;252m [39m[38;5;246m# => 2.75 ahhh...much better[39m
-
-[38;5;246m# Result of integer division truncated down both for positive and negative.[39m
-[38;5;67m5[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => 1[39m
-[38;5;67m5.0[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m3.0[39m[38;5;252m [39m[38;5;246m# => 1.0 # works on floats too[39m
-[38;5;252m-[39m[38;5;67m5[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => -2[39m
-[38;5;252m-[39m[38;5;67m5.0[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m3.0[39m[38;5;252m [39m[38;5;246m# => -2.0[39m
-
-[38;5;246m# Note that we can also import division module(Section 6 Modules)[39m
-[38;5;246m# to carry out normal division with just one '/'.[39m
-[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04m__future__[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252mdivision[39m
-
-[38;5;67m11[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m [39m[38;5;246m# => 2.75 ...normal division[39m
-[38;5;67m11[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m [39m[38;5;246m# => 2 ...floored division[39m
-
-[38;5;246m# Modulo operation[39m
-[38;5;67m7[39m[38;5;252m [39m[38;5;252m%[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => 1[39m
-
-[38;5;246m# Exponentiation (x to the yth power)[39m
-[38;5;67m2[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m [39m[38;5;246m# => 16[39m
-
-[38;5;246m# Enforce precedence with parentheses[39m
-[38;5;252m([39m[38;5;67m1[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => 8[39m
-
-[38;5;246m# Boolean Operators[39m
-[38;5;246m# Note "and" and "or" are case-sensitive[39m
-[38;5;31mTrue[39m[38;5;252m [39m[38;5;70;01mand[39;00m[38;5;252m [39m[38;5;31mFalse[39m[38;5;252m [39m[38;5;246m# => False[39m
-[38;5;31mFalse[39m[38;5;252m [39m[38;5;70;01mor[39;00m[38;5;252m [39m[38;5;31mTrue[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# Note using Bool operators with ints[39m
-[38;5;67m0[39m[38;5;252m [39m[38;5;70;01mand[39;00m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => 0[39m
-[38;5;252m-[39m[38;5;67m5[39m[38;5;252m [39m[38;5;70;01mor[39;00m[38;5;252m [39m[38;5;67m0[39m[38;5;252m [39m[38;5;246m# => -5[39m
-[38;5;67m0[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;31mFalse[39m[38;5;252m [39m[38;5;246m# => True[39m
-[38;5;67m2[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;31mTrue[39m[38;5;252m [39m[38;5;246m# => False[39m
-[38;5;67m1[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;31mTrue[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# negate with not[39m
-[38;5;70;01mnot[39;00m[38;5;252m [39m[38;5;31mTrue[39m[38;5;252m [39m[38;5;246m# => False[39m
-[38;5;70;01mnot[39;00m[38;5;252m [39m[38;5;31mFalse[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# Equality is ==[39m
-[38;5;67m1[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => True[39m
-[38;5;67m2[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => False[39m
-
-[38;5;246m# Inequality is !=[39m
-[38;5;67m1[39m[38;5;252m [39m[38;5;252m!=[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => False[39m
-[38;5;67m2[39m[38;5;252m [39m[38;5;252m!=[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# More comparisons[39m
-[38;5;67m1[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m [39m[38;5;246m# => True[39m
-[38;5;67m1[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m [39m[38;5;246m# => False[39m
-[38;5;67m2[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => True[39m
-[38;5;67m2[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# Comparisons can be chained![39m
-[38;5;67m1[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => True[39m
-[38;5;67m2[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => False[39m
-
-[38;5;246m# Strings are created with " or '[39m
-[38;5;214m"[39m[38;5;214mThis is a string.[39m[38;5;214m"[39m
-[38;5;214m'[39m[38;5;214mThis is also a string.[39m[38;5;214m'[39m
-
-[38;5;246m# Strings can be added too![39m
-[38;5;214m"[39m[38;5;214mHello [39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mworld![39m[38;5;214m"[39m[38;5;252m [39m[38;5;246m# => "Hello world!"[39m
-[38;5;246m# Strings can be added without using '+'[39m
-[38;5;214m"[39m[38;5;214mHello [39m[38;5;214m"[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mworld![39m[38;5;214m"[39m[38;5;252m [39m[38;5;246m# => "Hello world!"[39m
-
-[38;5;246m# ... or multiplied[39m
-[38;5;214m"[39m[38;5;214mHello[39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => "HelloHelloHello"[39m
-
-[38;5;246m# A string can be treated like a list of characters[39m
-[38;5;214m"[39m[38;5;214mThis is a string[39m[38;5;214m"[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 'T'[39m
-
-[38;5;246m# You can find the length of a string[39m
-[38;5;31mlen[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mThis is a string[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 16[39m
-
-[38;5;246m# String formatting with %[39m
-[38;5;246m# Even though the % string operator will be deprecated on Python 3.1 and removed[39m
-[38;5;246m# later at some time, it may still be good to know how it works.[39m
-[38;5;252mx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mapple[39m[38;5;214m'[39m
-[38;5;252my[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mlemon[39m[38;5;214m'[39m
-[38;5;252mz[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mThe items in the basket are [39m[38;5;214m%s[39m[38;5;214m and [39m[38;5;214m%s[39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m%[39m[38;5;252m [39m[38;5;252m([39m[38;5;252mx[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m)[39m
-
-[38;5;246m# A newer way to format strings is the format method.[39m
-[38;5;246m# This method is the preferred way[39m
-[38;5;214m"[39m[38;5;214m{} is a {}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mThis[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mplaceholder[39m[38;5;214m"[39m[38;5;252m)[39m
-[38;5;214m"[39m[38;5;214m{0} can be {1}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mstrings[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mformatted[39m[38;5;214m"[39m[38;5;252m)[39m
-[38;5;246m# You can use keywords if you don't want to count.[39m
-[38;5;214m"[39m[38;5;214m{name} wants to eat {food}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;252mname[39m[38;5;252m=[39m[38;5;214m"[39m[38;5;214mBob[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mfood[39m[38;5;252m=[39m[38;5;214m"[39m[38;5;214mlasagna[39m[38;5;214m"[39m[38;5;252m)[39m
-
-[38;5;246m# None is an object[39m
-[38;5;31mNone[39m[38;5;252m [39m[38;5;246m# => None[39m
-
-[38;5;246m# Don't use the equality "==" symbol to compare objects to None[39m
-[38;5;246m# Use "is" instead[39m
-[38;5;214m"[39m[38;5;214metc[39m[38;5;214m"[39m[38;5;252m [39m[38;5;70;01mis[39;00m[38;5;252m [39m[38;5;31mNone[39m[38;5;252m [39m[38;5;246m# => False[39m
-[38;5;31mNone[39m[38;5;252m [39m[38;5;70;01mis[39;00m[38;5;252m [39m[38;5;31mNone[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# The 'is' operator tests for object identity. This isn't[39m
-[38;5;246m# very useful when dealing with primitive values, but is[39m
-[38;5;246m# very useful when dealing with objects.[39m
-
-[38;5;246m# Any object can be used in a Boolean context.[39m
-[38;5;246m# The following values are considered falsey:[39m
-[38;5;246m# - None[39m
-[38;5;246m# - zero of any numeric type (e.g., 0, 0L, 0.0, 0j)[39m
-[38;5;246m# - empty sequences (e.g., '', (), [])[39m
-[38;5;246m# - empty containers (e.g., {}, set())[39m
-[38;5;246m# - instances of user-defined classes meeting certain conditions[39m
-[38;5;246m# see: https://docs.python.org/2/reference/datamodel.html#object.__nonzero__[39m
-[38;5;246m#[39m
-[38;5;246m# All other values are truthy (using the bool() function on them returns True).[39m
-[38;5;31mbool[39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => False[39m
-[38;5;31mbool[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => False[39m
-
-
-[38;5;246m####################################################[39m
-[38;5;246m# 2. Variables and Collections[39m
-[38;5;246m####################################################[39m
-
-[38;5;246m# Python has a print statement[39m
-[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214mI[39m[38;5;214m'[39m[38;5;214mm Python. Nice to meet you![39m[38;5;214m"[39m[38;5;252m [39m[38;5;246m# => I'm Python. Nice to meet you![39m
-
-[38;5;246m# Simple way to get input data from console[39m
-[38;5;252minput_string_var[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mraw_input[39m[38;5;252m([39m
-[38;5;252m [39m[38;5;214m"[39m[38;5;214mEnter some data: [39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Returns the data as a string[39m
-[38;5;252minput_var[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31minput[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mEnter some data: [39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Evaluates the data as python code[39m
-[38;5;246m# Warning: Caution is recommended for input() method usage[39m
-[38;5;246m# Note: In python 3, input() is deprecated and raw_input() is renamed to input()[39m
-
-[38;5;246m# No need to declare variables before assigning to them.[39m
-[38;5;252msome_var[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m [39m[38;5;246m# Convention is to use lower_case_with_underscores[39m
-[38;5;252msome_var[39m[38;5;252m [39m[38;5;246m# => 5[39m
-
-[38;5;246m# Accessing a previously unassigned variable is an exception.[39m
-[38;5;246m# See Control Flow to learn more about exception handling.[39m
-[38;5;252msome_other_var[39m[38;5;252m [39m[38;5;246m# Raises a name error[39m
-
-[38;5;246m# if can be used as an expression[39m
-[38;5;246m# Equivalent of C's '?:' ternary operator[39m
-[38;5;214m"[39m[38;5;214myahoo![39m[38;5;214m"[39m[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;70;01melse[39;00m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => "yahoo!"[39m
-
-[38;5;246m# Lists store sequences[39m
-[38;5;252mli[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;252m][39m
-[38;5;246m# You can start with a prefilled list[39m
-[38;5;252mother_li[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m][39m
-
-[38;5;246m# Add stuff to the end of a list with append[39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1][39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 2][39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 2, 4][39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 2, 4, 3][39m
-[38;5;246m# Remove from the end with pop[39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mpop[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 3 and li is now [1, 2, 4][39m
-[38;5;246m# Let's put it back[39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 2, 4, 3] again.[39m
-
-[38;5;246m# Access a list like you would any array[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 1[39m
-[38;5;246m# Assign new values to indexes that have already been initialized with =[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m42[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 42[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# Note: setting it back to the original value[39m
-[38;5;246m# Look at the last element[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 3[39m
-
-[38;5;246m# Looking out of bounds is an IndexError[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;67m4[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# Raises an IndexError[39m
-
-[38;5;246m# You can look at ranges with slice syntax.[39m
-[38;5;246m# (It's a closed/open range for you mathy types.)[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m:[39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [2, 4][39m
-[38;5;246m# Omit the beginning[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;67m2[39m[38;5;252m:[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [4, 3][39m
-[38;5;246m# Omit the end[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;252m:[39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [1, 2, 4][39m
-[38;5;246m# Select every second entry[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;252m:[39m[38;5;252m:[39m[38;5;67m2[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# =>[1, 4][39m
-[38;5;246m# Reverse a copy of the list[39m
-[38;5;252mli[39m[38;5;252m[[39m[38;5;252m:[39m[38;5;252m:[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [3, 4, 2, 1][39m
-[38;5;246m# Use any combination of these to make advanced slices[39m
-[38;5;246m# li[start:end:step][39m
-
-[38;5;246m# Remove arbitrary elements from a list with "del"[39m
-[38;5;70;01mdel[39;00m[38;5;252m [39m[38;5;252mli[39m[38;5;252m[[39m[38;5;67m2[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# li is now [1, 2, 3][39m
-
-[38;5;246m# You can add lists[39m
-[38;5;252mli[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252mother_li[39m[38;5;252m [39m[38;5;246m# => [1, 2, 3, 4, 5, 6][39m
-[38;5;246m# Note: values for li and for other_li are not modified.[39m
-
-[38;5;246m# Concatenate lists with "extend()"[39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mextend[39m[38;5;252m([39m[38;5;252mother_li[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Now li is [1, 2, 3, 4, 5, 6][39m
-
-[38;5;246m# Remove first occurrence of a value[39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mremove[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 3, 4, 5, 6][39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mremove[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Raises a ValueError as 2 is not in the list[39m
-
-[38;5;246m# Insert an element at a specific index[39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252minsert[39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 2, 3, 4, 5, 6] again[39m
-
-[38;5;246m# Get the index of the first item found[39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mindex[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 1[39m
-[38;5;252mli[39m[38;5;252m.[39m[38;5;252mindex[39m[38;5;252m([39m[38;5;67m7[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Raises a ValueError as 7 is not in the list[39m
-
-[38;5;246m# Check for existence in a list with "in"[39m
-[38;5;67m1[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mli[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# Examine the length with "len()"[39m
-[38;5;31mlen[39m[38;5;252m([39m[38;5;252mli[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 6[39m
-
-[38;5;246m# Tuples are like lists but are immutable.[39m
-[38;5;252mtup[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m
-[38;5;252mtup[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 1[39m
-[38;5;252mtup[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# Raises a TypeError[39m
-
-[38;5;246m# You can do all those list thingies on tuples too[39m
-[38;5;31mlen[39m[38;5;252m([39m[38;5;252mtup[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 3[39m
-[38;5;252mtup[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252m([39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => (1, 2, 3, 4, 5, 6)[39m
-[38;5;252mtup[39m[38;5;252m[[39m[38;5;252m:[39m[38;5;67m2[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => (1, 2)[39m
-[38;5;67m2[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mtup[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# You can unpack tuples (or lists) into variables[39m
-[38;5;252ma[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mb[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# a is now 1, b is now 2 and c is now 3[39m
-[38;5;252md[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252me[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mf[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m [39m[38;5;246m# you can leave out the parentheses[39m
-[38;5;246m# Tuples are created by default if you leave out the parentheses[39m
-[38;5;252mg[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m [39m[38;5;246m# => (4, 5, 6)[39m
-[38;5;246m# Now look how easy it is to swap two values[39m
-[38;5;252me[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252md[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252md[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252me[39m[38;5;252m [39m[38;5;246m# d is now 5 and e is now 4[39m
-
-[38;5;246m# Dictionaries store mappings[39m
-[38;5;252mempty_dict[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;252m}[39m
-[38;5;246m# Here is a prefilled dictionary[39m
-[38;5;252mfilled_dict[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;214m"[39m[38;5;214mone[39m[38;5;214m"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mtwo[39m[38;5;214m"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mthree[39m[38;5;214m"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m}[39m
-
-[38;5;246m# Look up values with [][39m
-[38;5;252mfilled_dict[39m[38;5;252m[[39m[38;5;214m"[39m[38;5;214mone[39m[38;5;214m"[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 1[39m
-
-[38;5;246m# Get all keys as a list with "keys()"[39m
-[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mkeys[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => ["three", "two", "one"][39m
-[38;5;246m# Note - Dictionary key ordering is not guaranteed.[39m
-[38;5;246m# Your results might not match this exactly.[39m
-
-[38;5;246m# Get all values as a list with "values()"[39m
-[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mvalues[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [3, 2, 1][39m
-[38;5;246m# Note - Same as above regarding key ordering.[39m
-
-[38;5;246m# Get all key-value pairs as a list of tuples with "items()"[39m
-[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mitems[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [("one", 1), ("two", 2), ("three", 3)][39m
-
-[38;5;246m# Check for existence of keys in a dictionary with "in"[39m
-[38;5;214m"[39m[38;5;214mone[39m[38;5;214m"[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mfilled_dict[39m[38;5;252m [39m[38;5;246m# => True[39m
-[38;5;67m1[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mfilled_dict[39m[38;5;252m [39m[38;5;246m# => False[39m
-
-[38;5;246m# Looking up a non-existing key is a KeyError[39m
-[38;5;252mfilled_dict[39m[38;5;252m[[39m[38;5;214m"[39m[38;5;214mfour[39m[38;5;214m"[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# KeyError[39m
-
-[38;5;246m# Use "get()" method to avoid the KeyError[39m
-[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mget[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mone[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 1[39m
-[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mget[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mfour[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => None[39m
-[38;5;246m# The get method supports a default argument when the value is missing[39m
-[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mget[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mone[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 1[39m
-[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mget[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mfour[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 4[39m
-[38;5;246m# note that filled_dict.get("four") is still => None[39m
-[38;5;246m# (get doesn't set the value in the dictionary)[39m
-
-[38;5;246m# set the value of a key with a syntax similar to lists[39m
-[38;5;252mfilled_dict[39m[38;5;252m[[39m[38;5;214m"[39m[38;5;214mfour[39m[38;5;214m"[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m [39m[38;5;246m# now, filled_dict["four"] => 4[39m
-
-[38;5;246m# "setdefault()" inserts into a dictionary only if the given key isn't present[39m
-[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252msetdefault[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mfive[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# filled_dict["five"] is set to 5[39m
-[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252msetdefault[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mfive[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# filled_dict["five"] is still 5[39m
-
-[38;5;246m# Sets store ... well sets (which are like lists but can contain no duplicates)[39m
-[38;5;252mempty_set[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mset[39m[38;5;252m([39m[38;5;252m)[39m
-[38;5;246m# Initialize a "set()" with a bunch of values[39m
-[38;5;252msome_set[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mset[39m[38;5;252m([39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# some_set is now set([1, 2, 3, 4])[39m
-
-[38;5;246m# order is not guaranteed, even though it may sometimes look sorted[39m
-[38;5;252manother_set[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mset[39m[38;5;252m([39m[38;5;252m[[39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# another_set is now set([1, 2, 3, 4])[39m
-
-[38;5;246m# Since Python 2.7, {} can be used to declare a set[39m
-[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => {1, 2, 3, 4}[39m
-
-[38;5;246m# Add more items to a set[39m
-[38;5;252mfilled_set[39m[38;5;252m.[39m[38;5;252madd[39m[38;5;252m([39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# filled_set is now {1, 2, 3, 4, 5}[39m
-
-[38;5;246m# Do set intersection with &[39m
-[38;5;252mother_set[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m}[39m
-[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;252m&[39m[38;5;252m [39m[38;5;252mother_set[39m[38;5;252m [39m[38;5;246m# => {3, 4, 5}[39m
-
-[38;5;246m# Do set union with |[39m
-[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;252m|[39m[38;5;252m [39m[38;5;252mother_set[39m[38;5;252m [39m[38;5;246m# => {1, 2, 3, 4, 5, 6}[39m
-
-[38;5;246m# Do set difference with -[39m
-[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m}[39m[38;5;252m [39m[38;5;252m-[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => {1, 4}[39m
-
-[38;5;246m# Do set symmetric difference with ^[39m
-[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m}[39m[38;5;252m [39m[38;5;252m^[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => {1, 4, 5}[39m
-
-[38;5;246m# Check if set on the left is a superset of set on the right[39m
-[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m}[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => False[39m
-
-[38;5;246m# Check if set on the left is a subset of set on the right[39m
-[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m}[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# Check for existence in a set with in[39m
-[38;5;67m2[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;246m# => True[39m
-[38;5;67m10[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;246m# => False[39m
-[38;5;67m10[39m[38;5;252m [39m[38;5;70;01mnot[39;00m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# Check data type of variable[39m
-[38;5;31mtype[39m[38;5;252m([39m[38;5;252mli[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => list[39m
-[38;5;31mtype[39m[38;5;252m([39m[38;5;252mfilled_dict[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => dict[39m
-[38;5;31mtype[39m[38;5;252m([39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => int[39m
-
-
-[38;5;246m####################################################[39m
-[38;5;246m# 3. Control Flow[39m
-[38;5;246m####################################################[39m
-
-[38;5;246m# Let's just make a variable[39m
-[38;5;252msome_var[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m
-
-[38;5;246m# Here is an if statement. Indentation is significant in python![39m
-[38;5;246m# prints "some_var is smaller than 10"[39m
-[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252msome_var[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214msome_var is totally bigger than 10.[39m[38;5;214m"[39m
-[38;5;70;01melif[39;00m[38;5;252m [39m[38;5;252msome_var[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# This elif clause is optional.[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214msome_var is smaller than 10.[39m[38;5;214m"[39m
-[38;5;70;01melse[39;00m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# This is optional too.[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214msome_var is indeed 10.[39m[38;5;214m"[39m
-
-[38;5;214m"""[39m
-[38;5;214mFor loops iterate over lists[39m
-[38;5;214mprints:[39m
-[38;5;214m dog is a mammal[39m
-[38;5;214m cat is a mammal[39m
-[38;5;214m mouse is a mammal[39m
-[38;5;214m"""[39m
-[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252manimal[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;214m"[39m[38;5;214mdog[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mcat[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mmouse[39m[38;5;214m"[39m[38;5;252m][39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;246m# You can use {0} to interpolate formatted strings. (See above.)[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214m{0} is a mammal[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;252manimal[39m[38;5;252m)[39m
-
-[38;5;214m"""[39m
-[38;5;214m"range(number)" returns a list of numbers[39m
-[38;5;214mfrom zero to the given number[39m
-[38;5;214mprints:[39m
-[38;5;214m 0[39m
-[38;5;214m 1[39m
-[38;5;214m 2[39m
-[38;5;214m 3[39m
-[38;5;214m"""[39m
-[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;31mrange[39m[38;5;252m([39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mi[39m
-
-[38;5;214m"""[39m
-[38;5;214m"range(lower, upper)" returns a list of numbers[39m
-[38;5;214mfrom the lower number to the upper number[39m
-[38;5;214mprints:[39m
-[38;5;214m 4[39m
-[38;5;214m 5[39m
-[38;5;214m 6[39m
-[38;5;214m 7[39m
-[38;5;214m"""[39m
-[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;31mrange[39m[38;5;252m([39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m8[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mi[39m
-
-[38;5;214m"""[39m
-[38;5;214mWhile loops go until a condition is no longer met.[39m
-[38;5;214mprints:[39m
-[38;5;214m 0[39m
-[38;5;214m 1[39m
-[38;5;214m 2[39m
-[38;5;214m 3[39m
-[38;5;214m"""[39m
-[38;5;252mx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m0[39m
-[38;5;70;01mwhile[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mx[39m
-[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# Shorthand for x = x + 1[39m
-
-[38;5;246m# Handle exceptions with a try/except block[39m
-
-[38;5;246m# Works on Python 2.6 and up:[39m
-[38;5;70;01mtry[39;00m[38;5;252m:[39m
-[38;5;252m [39m[38;5;246m# Use "raise" to raise an error[39m
-[38;5;252m [39m[38;5;70;01mraise[39;00m[38;5;252m [39m[38;5;250mIndexError[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mThis is an index error[39m[38;5;214m"[39m[38;5;252m)[39m
-[38;5;70;01mexcept[39;00m[38;5;252m [39m[38;5;250mIndexError[39m[38;5;252m [39m[38;5;70;01mas[39;00m[38;5;252m [39m[38;5;252me[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mpass[39;00m[38;5;252m [39m[38;5;246m# Pass is just a no-op. Usually you would do recovery here.[39m
-[38;5;70;01mexcept[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;250mTypeError[39m[38;5;252m,[39m[38;5;252m [39m[38;5;250mNameError[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mpass[39;00m[38;5;252m [39m[38;5;246m# Multiple exceptions can be handled together, if required.[39m
-[38;5;70;01melse[39;00m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# Optional clause to the try/except block. Must follow all except blocks[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214mAll good![39m[38;5;214m"[39m[38;5;252m [39m[38;5;246m# Runs only if the code in try raises no exceptions[39m
-[38;5;70;01mfinally[39;00m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# Execute under all circumstances[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214mWe can clean up resources here[39m[38;5;214m"[39m
-
-[38;5;246m# Instead of try/finally to cleanup resources you can use a with statement[39m
-[38;5;70;01mwith[39;00m[38;5;252m [39m[38;5;31mopen[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mmyfile.txt[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;70;01mas[39;00m[38;5;252m [39m[38;5;252mf[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mline[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mf[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mline[39m
-
-
-[38;5;246m####################################################[39m
-[38;5;246m# 4. Functions[39m
-[38;5;246m####################################################[39m
-
-[38;5;246m# Use "def" to create new functions[39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68madd[39m[38;5;252m([39m[38;5;252mx[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214mx is {0} and y is {1}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;252mx[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m)[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m [39m[38;5;246m# Return values with a return statement[39m
-
-
-[38;5;246m# Calling functions with parameters[39m
-[38;5;252madd[39m[38;5;252m([39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => prints out "x is 5 and y is 6" and returns 11[39m
-
-[38;5;246m# Another way to call functions is with keyword arguments[39m
-[38;5;252madd[39m[38;5;252m([39m[38;5;252my[39m[38;5;252m=[39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mx[39m[38;5;252m=[39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Keyword arguments can arrive in any order.[39m
-
-
-[38;5;246m# You can define functions that take a variable number of[39m
-[38;5;246m# positional args, which will be interpreted as a tuple by using *[39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mvarargs[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252margs[39m
-
-
-[38;5;252mvarargs[39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => (1, 2, 3)[39m
-
-
-[38;5;246m# You can define functions that take a variable number of[39m
-[38;5;246m# keyword args, as well, which will be interpreted as a dict by using **[39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mkeyword_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mkwargs[39m
-
-
-[38;5;246m# Let's call it to see what happens[39m
-[38;5;252mkeyword_args[39m[38;5;252m([39m[38;5;252mbig[39m[38;5;252m=[39m[38;5;214m"[39m[38;5;214mfoot[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mloch[39m[38;5;252m=[39m[38;5;214m"[39m[38;5;214mness[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => {"big": "foot", "loch": "ness"}[39m
-
-
-[38;5;246m# You can do both at once, if you like[39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mall_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252margs[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mkwargs[39m
-
-
-[38;5;214m"""[39m
-[38;5;214mall_the_args(1, 2, a=3, b=4) prints:[39m
-[38;5;214m (1, 2)[39m
-[38;5;214m {"a": 3, "b": 4}[39m
-[38;5;214m"""[39m
-
-[38;5;246m# When calling functions, you can do the opposite of args/kwargs![39m
-[38;5;246m# Use * to expand positional args and use ** to expand keyword args.[39m
-[38;5;252margs[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m)[39m
-[38;5;252mkwargs[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;214m"[39m[38;5;214ma[39m[38;5;214m"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mb[39m[38;5;214m"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m}[39m
-[38;5;252mall_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# equivalent to all_the_args(1, 2, 3, 4)[39m
-[38;5;252mall_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# equivalent to all_the_args(a=3, b=4)[39m
-[38;5;252mall_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# equivalent to all_the_args(1, 2, 3, 4, a=3, b=4)[39m
-
-
-[38;5;246m# you can pass args and kwargs along to other functions that take args/kwargs[39m
-[38;5;246m# by expanding them with * and ** respectively[39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mpass_all_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;252mall_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mvarargs[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m)[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mkeyword_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m
-
-
-[38;5;246m# Function Scope[39m
-[38;5;252mx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m
-
-
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mset_x[39m[38;5;252m([39m[38;5;252mnum[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;246m# Local var x not the same as global variable x[39m
-[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mnum[39m[38;5;252m [39m[38;5;246m# => 43[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;246m# => 43[39m
-
-
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mset_global_x[39m[38;5;252m([39m[38;5;252mnum[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mglobal[39;00m[38;5;252m [39m[38;5;252mx[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;246m# => 5[39m
-[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mnum[39m[38;5;252m [39m[38;5;246m# global var x is now set to 6[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;246m# => 6[39m
-
-
-[38;5;252mset_x[39m[38;5;252m([39m[38;5;67m43[39m[38;5;252m)[39m
-[38;5;252mset_global_x[39m[38;5;252m([39m[38;5;67m6[39m[38;5;252m)[39m
-
-
-[38;5;246m# Python has first class functions[39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mcreate_adder[39m[38;5;252m([39m[38;5;252mx[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68madder[39m[38;5;252m([39m[38;5;252my[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252my[39m
-
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252madder[39m
-
-
-[38;5;252madd_10[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mcreate_adder[39m[38;5;252m([39m[38;5;67m10[39m[38;5;252m)[39m
-[38;5;252madd_10[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 13[39m
-
-[38;5;246m# There are also anonymous functions[39m
-[38;5;252m([39m[38;5;70;01mlambda[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m:[39m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => True[39m
-[38;5;252m([39m[38;5;70;01mlambda[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m:[39m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 5[39m
-
-[38;5;246m# There are built-in higher order functions[39m
-[38;5;31mmap[39m[38;5;252m([39m[38;5;252madd_10[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [11, 12, 13][39m
-[38;5;31mmap[39m[38;5;252m([39m[38;5;31mmax[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [4, 2, 3][39m
-
-[38;5;31mfilter[39m[38;5;252m([39m[38;5;70;01mlambda[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m:[39m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m7[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [6, 7][39m
-
-[38;5;246m# We can use list comprehensions for nice maps and filters[39m
-[38;5;252m[[39m[38;5;252madd_10[39m[38;5;252m([39m[38;5;252mi[39m[38;5;252m)[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [11, 12, 13][39m
-[38;5;252m[[39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m7[39m[38;5;252m][39m[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [6, 7][39m
-
-[38;5;246m# You can construct set and dict comprehensions as well.[39m
-[38;5;252m{[39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;214m'[39m[38;5;214mabcddeef[39m[38;5;214m'[39m[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;214m'[39m[38;5;214mabc[39m[38;5;214m'[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => {'a', 'b', 'c'}[39m
-[38;5;252m{[39m[38;5;252mx[39m[38;5;252m:[39m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;31mrange[39m[38;5;252m([39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}[39m
-
-
-[38;5;246m####################################################[39m
-[38;5;246m# 5. Classes[39m
-[38;5;246m####################################################[39m
-
-[38;5;246m# We subclass from object to get a class.[39m
-[38;5;70;01mclass[39;00m[38;5;252m [39m[38;5;68;04mHuman[39;00m[38;5;252m([39m[38;5;31mobject[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;246m# A class attribute. It is shared by all instances of this class[39m
-[38;5;252m [39m[38;5;252mspecies[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mH. sapiens[39m[38;5;214m"[39m
-
-[38;5;252m [39m[38;5;246m# Basic initializer, this is called when this class is instantiated.[39m
-[38;5;252m [39m[38;5;246m# Note that the double leading and trailing underscores denote objects[39m
-[38;5;252m [39m[38;5;246m# or attributes that are used by python but that live in user-controlled[39m
-[38;5;252m [39m[38;5;246m# namespaces. You should not invent such names on your own.[39m
-[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68m__init__[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mname[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;246m# Assign the argument to the instance's name attribute[39m
-[38;5;252m [39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252mname[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mname[39m
-
-[38;5;252m [39m[38;5;246m# Initialize property[39m
-[38;5;252m [39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252mage[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m0[39m
-
-[38;5;252m [39m[38;5;246m# An instance method. All methods take "self" as the first argument[39m
-[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68msay[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mmsg[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214m{0}: {1}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252mname[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mmsg[39m[38;5;252m)[39m
-
-[38;5;252m [39m[38;5;246m# A class method is shared among all instances[39m
-[38;5;252m [39m[38;5;246m# They are called with the calling class as the first argument[39m
-[38;5;252m [39m[38;5;214m@classmethod[39m
-[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mget_species[39m[38;5;252m([39m[38;5;31mcls[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;31mcls[39m[38;5;252m.[39m[38;5;252mspecies[39m
-
-[38;5;252m [39m[38;5;246m# A static method is called without a class or instance reference[39m
-[38;5;252m [39m[38;5;214m@staticmethod[39m
-[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mgrunt[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214m*grunt*[39m[38;5;214m"[39m
-
-[38;5;252m [39m[38;5;246m# A property is just like a getter.[39m
-[38;5;252m [39m[38;5;246m# It turns the method age() into an read-only attribute[39m
-[38;5;252m [39m[38;5;246m# of the same name.[39m
-[38;5;252m [39m[38;5;214m@property[39m
-[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mage[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252m_age[39m
-
-[38;5;252m [39m[38;5;246m# This allows the property to be set[39m
-[38;5;252m [39m[38;5;214m@age.setter[39m
-[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mage[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mage[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252m_age[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mage[39m
-
-[38;5;252m [39m[38;5;246m# This allows the property to be deleted[39m
-[38;5;252m [39m[38;5;214m@age.deleter[39m
-[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mage[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mdel[39;00m[38;5;252m [39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252m_age[39m
-
-
-[38;5;246m# Instantiate a class[39m
-[38;5;252mi[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mHuman[39m[38;5;252m([39m[38;5;252mname[39m[38;5;252m=[39m[38;5;214m"[39m[38;5;214mIan[39m[38;5;214m"[39m[38;5;252m)[39m
-[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m.[39m[38;5;252msay[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mhi[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# prints out "Ian: hi"[39m
-
-[38;5;252mj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mHuman[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mJoel[39m[38;5;214m"[39m[38;5;252m)[39m
-[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mj[39m[38;5;252m.[39m[38;5;252msay[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mhello[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# prints out "Joel: hello"[39m
-
-[38;5;246m# Call our class method[39m
-[38;5;252mi[39m[38;5;252m.[39m[38;5;252mget_species[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => "H. sapiens"[39m
-
-[38;5;246m# Change the shared attribute[39m
-[38;5;252mHuman[39m[38;5;252m.[39m[38;5;252mspecies[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mH. neanderthalensis[39m[38;5;214m"[39m
-[38;5;252mi[39m[38;5;252m.[39m[38;5;252mget_species[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => "H. neanderthalensis"[39m
-[38;5;252mj[39m[38;5;252m.[39m[38;5;252mget_species[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => "H. neanderthalensis"[39m
-
-[38;5;246m# Call the static method[39m
-[38;5;252mHuman[39m[38;5;252m.[39m[38;5;252mgrunt[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => "*grunt*"[39m
-
-[38;5;246m# Update the property[39m
-[38;5;252mi[39m[38;5;252m.[39m[38;5;252mage[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m42[39m
-
-[38;5;246m# Get the property[39m
-[38;5;252mi[39m[38;5;252m.[39m[38;5;252mage[39m[38;5;252m [39m[38;5;246m# => 42[39m
-
-[38;5;246m# Delete the property[39m
-[38;5;70;01mdel[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m.[39m[38;5;252mage[39m
-[38;5;252mi[39m[38;5;252m.[39m[38;5;252mage[39m[38;5;252m [39m[38;5;246m# => raises an AttributeError[39m
-
-[38;5;246m####################################################[39m
-[38;5;246m# 6. Modules[39m
-[38;5;246m####################################################[39m
-
-[38;5;246m# You can import modules[39m
-[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m
-
-[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mmath[39m[38;5;252m.[39m[38;5;252msqrt[39m[38;5;252m([39m[38;5;67m16[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 4[39m
-
-[38;5;246m# You can get specific functions from a module[39m
-[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252mceil[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mfloor[39m
-
-[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mceil[39m[38;5;252m([39m[38;5;67m3.7[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 4.0[39m
-[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mfloor[39m[38;5;252m([39m[38;5;67m3.7[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 3.0[39m
-
-[38;5;246m# You can import all functions from a module.[39m
-[38;5;246m# Warning: this is not recommended[39m
-[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252m*[39m
-
-[38;5;246m# You can shorten module names[39m
-[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m[38;5;252m [39m[38;5;70;01mas[39;00m[38;5;252m [39m[38;5;68;04mm[39;00m
-
-[38;5;252mmath[39m[38;5;252m.[39m[38;5;252msqrt[39m[38;5;252m([39m[38;5;67m16[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;252mm[39m[38;5;252m.[39m[38;5;252msqrt[39m[38;5;252m([39m[38;5;67m16[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => True[39m
-[38;5;246m# you can also test that the functions are equivalent[39m
-[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252msqrt[39m
-
-[38;5;252mmath[39m[38;5;252m.[39m[38;5;252msqrt[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;252mm[39m[38;5;252m.[39m[38;5;252msqrt[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;252msqrt[39m[38;5;252m [39m[38;5;246m# => True[39m
-
-[38;5;246m# Python modules are just ordinary python files. You[39m
-[38;5;246m# can write your own, and import them. The name of the[39m
-[38;5;246m# module is the same as the name of the file.[39m
-
-[38;5;246m# You can find out which functions and attributes[39m
-[38;5;246m# defines a module.[39m
-[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m
-
-[38;5;31mdir[39m[38;5;252m([39m[38;5;252mmath[39m[38;5;252m)[39m
-
-
-[38;5;246m# If you have a Python script named math.py in the same[39m
-[38;5;246m# folder as your current script, the file math.py will[39m
-[38;5;246m# be loaded instead of the built-in Python module.[39m
-[38;5;246m# This happens because the local folder has priority[39m
-[38;5;246m# over Python's built-in libraries.[39m
-
-
-[38;5;246m####################################################[39m
-[38;5;246m# 7. Advanced[39m
-[38;5;246m####################################################[39m
-
-[38;5;246m# Generators[39m
-[38;5;246m# A generator "generates" values as they are requested instead of storing[39m
-[38;5;246m# everything up front[39m
-
-[38;5;246m# The following method (*NOT* a generator) will double all values and store it[39m
-[38;5;246m# in `double_arr`. For large size of iterables, that might get huge![39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mdouble_numbers[39m[38;5;252m([39m[38;5;252miterable[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;252mdouble_arr[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;252m][39m
-[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252miterable[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;252mdouble_arr[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252mi[39m[38;5;252m)[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mdouble_arr[39m
-
-
-[38;5;246m# Running the following would mean we'll double all values first and return all[39m
-[38;5;246m# of them back to be checked by our condition[39m
-[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mvalue[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mdouble_numbers[39m[38;5;252m([39m[38;5;31mrange[39m[38;5;252m([39m[38;5;67m1000000[39m[38;5;252m)[39m[38;5;252m)[39m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# `test_non_generator`[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mvalue[39m
-[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252mvalue[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mbreak[39;00m
-
-
-[38;5;246m# We could instead use a generator to "generate" the doubled value as the item[39m
-[38;5;246m# is being requested[39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mdouble_numbers_generator[39m[38;5;252m([39m[38;5;252miterable[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252miterable[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01myield[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252mi[39m
-
-
-[38;5;246m# Running the same code as before, but with a generator, now allows us to iterate[39m
-[38;5;246m# over the values and doubling them one by one as they are being consumed by[39m
-[38;5;246m# our logic. Hence as soon as we see a value > 5, we break out of the[39m
-[38;5;246m# loop and don't need to double most of the values sent in (MUCH FASTER!)[39m
-[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mvalue[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mdouble_numbers_generator[39m[38;5;252m([39m[38;5;31mxrange[39m[38;5;252m([39m[38;5;67m1000000[39m[38;5;252m)[39m[38;5;252m)[39m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# `test_generator`[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mvalue[39m
-[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252mvalue[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mbreak[39;00m
-
-[38;5;246m# BTW: did you notice the use of `range` in `test_non_generator` and `xrange` in `test_generator`?[39m
-[38;5;246m# Just as `double_numbers_generator` is the generator version of `double_numbers`[39m
-[38;5;246m# We have `xrange` as the generator version of `range`[39m
-[38;5;246m# `range` would return back and array with 1000000 values for us to use[39m
-[38;5;246m# `xrange` would generate 1000000 values for us as we request / iterate over those items[39m
-
-[38;5;246m# Just as you can create a list comprehension, you can create generator[39m
-[38;5;246m# comprehensions as well.[39m
-[38;5;252mvalues[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m([39m[38;5;252m-[39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m][39m[38;5;252m)[39m
-[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mvalues[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m([39m[38;5;252mx[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# prints -1 -2 -3 -4 -5 to console/terminal[39m
-
-[38;5;246m# You can also cast a generator comprehension directly to a list.[39m
-[38;5;252mvalues[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m([39m[38;5;252m-[39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m][39m[38;5;252m)[39m
-[38;5;252mgen_to_list[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mlist[39m[38;5;252m([39m[38;5;252mvalues[39m[38;5;252m)[39m
-[38;5;70;01mprint[39;00m[38;5;252m([39m[38;5;252mgen_to_list[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [-1, -2, -3, -4, -5][39m
-
-[38;5;246m# Decorators[39m
-[38;5;246m# A decorator is a higher order function, which accepts and returns a function.[39m
-[38;5;246m# Simple usage example – add_apples decorator will add 'Apple' element into[39m
-[38;5;246m# fruits list returned by get_fruits target function.[39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68madd_apples[39m[38;5;252m([39m[38;5;252mfunc[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mget_fruits[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;252mfruits[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mfunc[39m[38;5;252m([39m[38;5;252m)[39m
-[38;5;252m [39m[38;5;252mfruits[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;214m'[39m[38;5;214mApple[39m[38;5;214m'[39m[38;5;252m)[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mfruits[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mget_fruits[39m
-
-[38;5;214m@add_apples[39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mget_fruits[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;214m'[39m[38;5;214mBanana[39m[38;5;214m'[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mMango[39m[38;5;214m'[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mOrange[39m[38;5;214m'[39m[38;5;252m][39m
-
-[38;5;246m# Prints out the list of fruits with 'Apple' element in it:[39m
-[38;5;246m# Banana, Mango, Orange, Apple[39m
-[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m'[39m[38;5;214m, [39m[38;5;214m'[39m[38;5;252m.[39m[38;5;252mjoin[39m[38;5;252m([39m[38;5;252mget_fruits[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m)[39m
-
-[38;5;246m# in this example beg wraps say[39m
-[38;5;246m# Beg will call say. If say_please is True then it will change the returned[39m
-[38;5;246m# message[39m
-[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mfunctools[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252mwraps[39m
-
-
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mbeg[39m[38;5;252m([39m[38;5;252mtarget_function[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;214m@wraps[39m[38;5;252m([39m[38;5;252mtarget_function[39m[38;5;252m)[39m
-[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mwrapper[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;252mmsg[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252msay_please[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mtarget_function[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m
-[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252msay_please[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214m{} {}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;252mmsg[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mPlease! I am poor :([39m[38;5;214m"[39m[38;5;252m)[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mmsg[39m
-
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mwrapper[39m
-
-
-[38;5;214m@beg[39m
-[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68msay[39m[38;5;252m([39m[38;5;252msay_please[39m[38;5;252m=[39m[38;5;31mFalse[39m[38;5;252m)[39m[38;5;252m:[39m
-[38;5;252m [39m[38;5;252mmsg[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mCan you buy me a beer?[39m[38;5;214m"[39m
-[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mmsg[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252msay_please[39m
-
-
-[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252msay[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Can you buy me a beer?[39m
-[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252msay[39m[38;5;252m([39m[38;5;252msay_please[39m[38;5;252m=[39m[38;5;31mTrue[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Can you buy me a beer? Please! I am poor :([39m
+ _ _ _ [38;2;0;204;0m_[0m[38;2;0;204;0m_[0m[38;2;0;204;0m [0m[38;2;0;204;0m [0m[38;2;0;204;0m [0m
+ ___| |__ ___ __ _| |_ ___| |__ [38;2;0;204;0m\[0m[38;2;0;204;0m [0m[38;2;0;204;0m\[0m[38;2;0;204;0m [0m[38;2;0;204;0m [0m [48;2;85;85;85m [0m[48;2;85;85;85m [0m[48;2;85;85;85m [0m[48;2;85;85;85mT[0m[48;2;85;85;85mh[0m[48;2;85;85;85me[0m[48;2;85;85;85m [0m[48;2;85;85;85mo[0m[48;2;85;85;85mn[0m[48;2;85;85;85ml[0m[48;2;85;85;85my[0m[48;2;85;85;85m [0m[48;2;85;85;85mc[0m[48;2;85;85;85mh[0m[48;2;85;85;85me[0m[48;2;85;85;85ma[0m[48;2;85;85;85mt[0m[48;2;85;85;85m [0m[48;2;85;85;85ms[0m[48;2;85;85;85mh[0m[48;2;85;85;85me[0m[48;2;85;85;85me[0m[48;2;85;85;85mt[0m[48;2;85;85;85m [0m[48;2;85;85;85my[0m[48;2;85;85;85mo[0m[48;2;85;85;85mu[0m[48;2;85;85;85m [0m[48;2;85;85;85mn[0m[48;2;85;85;85me[0m[48;2;85;85;85me[0m[48;2;85;85;85md[0m[48;2;85;85;85m [0m[48;2;85;85;85m [0m[48;2;85;85;85m [0m
+ / __| '_ \ / _ \/ _` | __| / __| '_ \ [38;2;0;204;0m\[0m[38;2;0;204;0m [0m[38;2;0;204;0m\[0m[38;2;0;204;0m [0m [38;2;136;136;136mU[0m[38;2;136;136;136mn[0m[38;2;136;136;136mi[0m[38;2;136;136;136mf[0m[38;2;136;136;136mi[0m[38;2;136;136;136me[0m[38;2;136;136;136md[0m[38;2;136;136;136m [0m[38;2;136;136;136ma[0m[38;2;136;136;136mc[0m[38;2;136;136;136mc[0m[38;2;136;136;136me[0m[38;2;136;136;136ms[0m[38;2;136;136;136ms[0m[38;2;136;136;136m [0m[38;2;136;136;136mt[0m[38;2;136;136;136mo[0m[38;2;136;136;136m [0m[38;2;136;136;136mt[0m[38;2;136;136;136mh[0m[38;2;136;136;136me[0m[38;2;136;136;136m [0m[38;2;136;136;136mb[0m[38;2;136;136;136me[0m[38;2;136;136;136ms[0m[38;2;136;136;136mt[0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m
+| (__| | | | __/ (_| | |_ _\__ \ | | |[38;2;0;204;0m/[0m[38;2;0;204;0m [0m[38;2;0;204;0m/[0m[38;2;0;204;0m [0m [38;2;136;136;136mc[0m[38;2;136;136;136mo[0m[38;2;136;136;136mm[0m[38;2;136;136;136mm[0m[38;2;136;136;136mu[0m[38;2;136;136;136mn[0m[38;2;136;136;136mi[0m[38;2;136;136;136mt[0m[38;2;136;136;136my[0m[38;2;136;136;136m [0m[38;2;136;136;136md[0m[38;2;136;136;136mr[0m[38;2;136;136;136mi[0m[38;2;136;136;136mv[0m[38;2;136;136;136me[0m[38;2;136;136;136mn[0m[38;2;136;136;136m [0m[38;2;136;136;136md[0m[38;2;136;136;136mo[0m[38;2;136;136;136mc[0m[38;2;136;136;136mu[0m[38;2;136;136;136mm[0m[38;2;136;136;136me[0m[38;2;136;136;136mn[0m[38;2;136;136;136mt[0m[38;2;136;136;136ma[0m[38;2;136;136;136mt[0m[38;2;136;136;136mi[0m[38;2;136;136;136mo[0m[38;2;136;136;136mn[0m
+ \___|_| |_|\___|\__,_|\__(_)___/_| |_[38;2;0;204;0m/[0m[38;2;0;204;0m_[0m[38;2;0;204;0m/[0m[38;2;0;204;0m [0m[38;2;0;204;0m [0m [38;2;136;136;136mr[0m[38;2;136;136;136me[0m[38;2;136;136;136mp[0m[38;2;136;136;136mo[0m[38;2;136;136;136ms[0m[38;2;136;136;136mi[0m[38;2;136;136;136mt[0m[38;2;136;136;136mo[0m[38;2;136;136;136mr[0m[38;2;136;136;136mi[0m[38;2;136;136;136me[0m[38;2;136;136;136ms[0m[38;2;136;136;136m [0m[38;2;136;136;136mo[0m[38;2;136;136;136mf[0m[38;2;136;136;136m [0m[38;2;136;136;136mt[0m[38;2;136;136;136mh[0m[38;2;136;136;136me[0m[38;2;136;136;136m [0m[38;2;136;136;136mw[0m[38;2;136;136;136mo[0m[38;2;136;136;136mr[0m[38;2;136;136;136ml[0m[38;2;136;136;136md[0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m[38;2;136;136;136m [0m
+
+[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
+[38;2;136;136;136m|[0m $ [38;2;0;204;0mc[0m[38;2;0;204;0mu[0m[38;2;0;204;0mr[0m[38;2;0;204;0ml[0m [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204me[0m[38;2;0;170;204ma[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m/[0m[38;2;0;170;204ml[0m[38;2;0;170;204ms[0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;204;0mc[0m[38;2;0;204;0mh[0m[38;2;0;204;0mt[0m[38;2;0;204;0m.[0m[38;2;0;204;0ms[0m[38;2;0;204;0mh[0m [38;2;0;170;204mb[0m[38;2;0;170;204mt[0m[38;2;0;170;204mr[0m[38;2;0;170;204mf[0m[38;2;0;170;204ms[0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m [0m[38;2;0;170;204ml[0m[38;2;0;170;204mu[0m[38;2;0;170;204ma[0m[38;2;0;170;204m/[0m[38;2;0;204;0m:[0m[38;2;0;204;0ml[0m[38;2;0;204;0me[0m[38;2;0;204;0ma[0m[38;2;0;204;0mr[0m[38;2;0;204;0mn[0m [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m $ [38;2;0;204;0mc[0m[38;2;0;204;0mu[0m[38;2;0;204;0mr[0m[38;2;0;204;0ml[0m [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m/[0m[38;2;0;170;204mb[0m[38;2;0;170;204mt[0m[38;2;0;170;204mr[0m[38;2;0;170;204mf[0m[38;2;0;170;204ms[0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;204;0mc[0m[38;2;0;204;0mh[0m[38;2;0;204;0mt[0m[38;2;0;204;0m.[0m[38;2;0;204;0ms[0m[38;2;0;204;0mh[0m [38;2;0;170;204mt[0m[38;2;0;170;204ma[0m[38;2;0;170;204mr[0m[38;2;0;170;204m~[0m[38;2;0;170;204ml[0m[38;2;0;170;204mi[0m[38;2;0;170;204ms[0m[38;2;0;170;204mt[0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m[38;2;0;170;204m [0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m Learn any[38;2;255;0;0m*[0m programming [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m $ [38;2;0;204;0mc[0m[38;2;0;204;0mu[0m[38;2;0;204;0mr[0m[38;2;0;204;0ml[0m [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m/[0m[38;2;0;170;204mt[0m[38;2;0;170;204ma[0m[38;2;0;170;204mr[0m[38;2;0;170;204m~[0m[38;2;0;170;204ml[0m[38;2;0;170;204mi[0m[38;2;0;170;204ms[0m[38;2;0;170;204mt[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m language not leaving [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mu[0m[38;2;0;170;204mr[0m[38;2;0;170;204ml[0m [38;2;0;204;0mh[0m[38;2;0;204;0mt[0m[38;2;0;204;0mt[0m[38;2;0;204;0mp[0m[38;2;0;204;0ms[0m[38;2;0;204;0m:[0m[38;2;0;170;204m/[0m[38;2;0;170;204m/[0m[38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m [0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m your shell [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;255;0;0m*[0m[38;2;255;0;0m)[0m any of 60 [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m
+[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;204;204;0m [0m[38;2;204;204;0mq[0m[38;2;204;204;0mu[0m[38;2;204;204;0me[0m[38;2;204;204;0mr[0m[38;2;204;204;0mi[0m[38;2;204;204;0me[0m[38;2;204;204;0ms[0m[38;2;204;204;0m [0m[38;2;204;204;0mw[0m[38;2;204;204;0mi[0m[38;2;204;204;0mt[0m[38;2;204;204;0mh[0m[38;2;204;204;0m [0m[38;2;204;204;0mc[0m[38;2;204;204;0mu[0m[38;2;204;204;0mr[0m[38;2;204;204;0ml[0m[38;2;204;204;0m [0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m [38;2;204;204;0mo[0m[38;2;204;204;0mw[0m[38;2;204;204;0mn[0m[38;2;204;204;0m [0m[38;2;204;204;0mo[0m[38;2;204;204;0mp[0m[38;2;204;204;0mt[0m[38;2;204;204;0mi[0m[38;2;204;204;0mo[0m[38;2;204;204;0mn[0m[38;2;204;204;0ma[0m[38;2;204;204;0ml[0m[38;2;204;204;0m [0m[38;2;204;204;0mc[0m[38;2;204;204;0ml[0m[38;2;204;204;0mi[0m[38;2;204;204;0me[0m[38;2;204;204;0mn[0m[38;2;204;204;0mt[0m [38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m [38;2;204;204;0ml[0m[38;2;204;204;0me[0m[38;2;204;204;0ma[0m[38;2;204;204;0mr[0m[38;2;204;204;0mn[0m[38;2;204;204;0m,[0m[38;2;204;204;0m [0m[38;2;204;204;0ml[0m[38;2;204;204;0me[0m[38;2;204;204;0ma[0m[38;2;204;204;0mr[0m[38;2;204;204;0mn[0m[38;2;204;204;0m,[0m[38;2;204;204;0m [0m[38;2;204;204;0ml[0m[38;2;204;204;0me[0m[38;2;204;204;0ma[0m[38;2;204;204;0mr[0m[38;2;204;204;0mn[0m[38;2;204;204;0m![0m [38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
+[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
+[38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m [0m[38;2;0;170;204mg[0m[38;2;0;170;204mo[0m[38;2;0;170;204m/[0m[38;2;0;170;204mf[0m[38;2;0;204;0m<[0m[38;2;0;204;0mt[0m[38;2;0;204;0ma[0m[38;2;0;204;0mb[0m[38;2;0;204;0m>[0m[38;2;0;204;0m<[0m[38;2;0;204;0mt[0m[38;2;0;204;0ma[0m[38;2;0;204;0mb[0m[38;2;0;204;0m>[0m[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m [38;2;0;204;0m-[0m[38;2;0;204;0m-[0m[38;2;0;204;0ms[0m[38;2;0;204;0mh[0m[38;2;0;204;0me[0m[38;2;0;204;0ml[0m[38;2;0;204;0ml[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m [38;2;0;204;0mg[0m[38;2;0;204;0mo[0m[38;2;0;204;0m [0m[38;2;0;204;0mz[0m[38;2;0;204;0mi[0m[38;2;0;204;0mp[0m[38;2;0;204;0m [0m[38;2;0;204;0ml[0m[38;2;0;204;0mi[0m[38;2;0;204;0ms[0m[38;2;0;204;0mt[0m[38;2;0;204;0ms[0m [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m go/for go/func [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;34;170;34mc[0m[38;2;34;170;34mh[0m[38;2;34;170;34mt[0m[38;2;34;170;34m.[0m[38;2;34;170;34ms[0m[38;2;34;170;34mh[0m[38;2;34;170;34m>[0m help [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m Ask any question using [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m $ cht.sh go/for [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m ... [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m cht.sh or curl cht.sh: [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m ... [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m /go[38;2;34;170;34m/[0mzip[38;2;34;170;34m+[0mlists [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m (use [38;2;34;170;34m/[0m,[38;2;34;170;34m+[0m when curling) [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m
+[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;204;204;0m [0m[38;2;204;204;0mT[0m[38;2;204;204;0mA[0m[38;2;204;204;0mB[0m[38;2;204;204;0m-[0m[38;2;204;204;0mc[0m[38;2;204;204;0mo[0m[38;2;204;204;0mm[0m[38;2;204;204;0mp[0m[38;2;204;204;0ml[0m[38;2;204;204;0me[0m[38;2;204;204;0mt[0m[38;2;204;204;0mi[0m[38;2;204;204;0mo[0m[38;2;204;204;0mn[0m[38;2;204;204;0m [0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;204;204;0m [0m[38;2;204;204;0mi[0m[38;2;204;204;0mn[0m[38;2;204;204;0mt[0m[38;2;204;204;0me[0m[38;2;204;204;0mr[0m[38;2;204;204;0ma[0m[38;2;204;204;0mc[0m[38;2;204;204;0mt[0m[38;2;204;204;0mi[0m[38;2;204;204;0mv[0m[38;2;204;204;0me[0m[38;2;204;204;0m [0m[38;2;204;204;0ms[0m[38;2;204;204;0mh[0m[38;2;204;204;0me[0m[38;2;204;204;0ml[0m[38;2;204;204;0ml[0m[38;2;204;204;0m [0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;204;204;0m [0m[38;2;204;204;0mp[0m[38;2;204;204;0mr[0m[38;2;204;204;0mo[0m[38;2;204;204;0mg[0m[38;2;204;204;0mr[0m[38;2;204;204;0ma[0m[38;2;204;204;0mm[0m[38;2;204;204;0mm[0m[38;2;204;204;0mi[0m[38;2;204;204;0mn[0m[38;2;204;204;0mg[0m[38;2;204;204;0m [0m[38;2;204;204;0mq[0m[38;2;204;204;0mu[0m[38;2;204;204;0me[0m[38;2;204;204;0ms[0m[38;2;204;204;0mt[0m[38;2;204;204;0mi[0m[38;2;204;204;0mo[0m[38;2;204;204;0mn[0m[38;2;204;204;0ms[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
+[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
+[38;2;136;136;136m|[0m $ [38;2;0;170;204mc[0m[38;2;0;170;204mu[0m[38;2;0;170;204mr[0m[38;2;0;170;204ml[0m[38;2;0;170;204m [0m[38;2;0;170;204mc[0m[38;2;0;170;204mh[0m[38;2;0;170;204mt[0m[38;2;0;170;204m.[0m[38;2;0;170;204ms[0m[38;2;0;170;204mh[0m[38;2;0;170;204m/[0m[38;2;0;204;0m:[0m[38;2;0;204;0mh[0m[38;2;0;204;0me[0m[38;2;0;204;0ml[0m[38;2;0;204;0mp[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;204;0mv[0m[38;2;0;204;0mi[0m[38;2;0;204;0mm[0m [38;2;0;170;204mp[0m[38;2;0;170;204mr[0m[38;2;0;170;204mg[0m[38;2;0;170;204m.[0m[38;2;0;170;204mp[0m[38;2;0;170;204my[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m $ [38;2;0;204;0mt[0m[38;2;0;204;0mi[0m[38;2;0;204;0mm[0m[38;2;0;204;0me[0m curl cht.sh/ [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m see [38;2;34;170;34m/[0m[38;2;34;170;34m:[0m[38;2;34;170;34mh[0m[38;2;34;170;34me[0m[38;2;34;170;34ml[0m[38;2;34;170;34mp[0m and [38;2;34;170;34m/[0m[38;2;34;170;34m:[0m[38;2;34;170;34mi[0m[38;2;34;170;34mn[0m[38;2;34;170;34mt[0m[38;2;34;170;34mr[0m[38;2;34;170;34mo[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m ... [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m ... [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m for usage information [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m zip lists _ [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m real [38;2;34;170;34m0[0m[38;2;34;170;34mm[0m[38;2;34;170;34m0[0m[38;2;34;170;34m.[0m[38;2;34;170;34m0[0m[38;2;34;170;34m7[0m[38;2;34;170;34m5[0m[38;2;34;170;34ms[0m [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m and README.md on [38;2;0;170;204mG[0m[38;2;0;170;204mi[0m[38;2;0;170;204mt[0m[38;2;0;170;204mH[0m[38;2;0;170;204mu[0m[38;2;0;170;204mb[0m[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;34;170;34m<[0m[38;2;34;170;34ml[0m[38;2;34;170;34me[0m[38;2;34;170;34ma[0m[38;2;34;170;34md[0m[38;2;34;170;34me[0m[38;2;34;170;34mr[0m[38;2;34;170;34m>[0m[38;2;34;170;34mK[0m[38;2;34;170;34mK[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m for the details [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m *awesome* [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m
+[38;2;136;136;136m|[0m [38;2;255;0;0m*[0m[38;2;255;0;0ms[0m[38;2;255;0;0mt[0m[38;2;255;0;0ma[0m[38;2;255;0;0mr[0m[38;2;255;0;0mt[0m[38;2;255;0;0m [0m[38;2;255;0;0mh[0m[38;2;255;0;0me[0m[38;2;255;0;0mr[0m[38;2;255;0;0me[0m[38;2;255;0;0m*[0m[38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m [38;2;136;136;136m|[0m
+[38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m [38;2;204;204;0ms[0m[38;2;204;204;0me[0m[38;2;204;204;0ml[0m[38;2;204;204;0mf[0m[38;2;204;204;0m-[0m[38;2;204;204;0md[0m[38;2;204;204;0mo[0m[38;2;204;204;0mc[0m[38;2;204;204;0mu[0m[38;2;204;204;0mm[0m[38;2;204;204;0me[0m[38;2;204;204;0mn[0m[38;2;204;204;0mt[0m[38;2;204;204;0me[0m[38;2;204;204;0md[0m [38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m [38;2;204;204;0mq[0m[38;2;204;204;0mu[0m[38;2;204;204;0me[0m[38;2;204;204;0mr[0m[38;2;204;204;0mi[0m[38;2;204;204;0me[0m[38;2;204;204;0ms[0m[38;2;204;204;0m [0m[38;2;204;204;0mf[0m[38;2;204;204;0mr[0m[38;2;204;204;0mo[0m[38;2;204;204;0mm[0m[38;2;204;204;0m [0m[38;2;204;204;0me[0m[38;2;204;204;0md[0m[38;2;204;204;0mi[0m[38;2;204;204;0mt[0m[38;2;204;204;0mo[0m[38;2;204;204;0mr[0m[38;2;204;204;0m![0m [38;2;136;136;136m-[0m[38;2;136;136;136m+[0m [38;2;136;136;136m+[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m [38;2;204;204;0mi[0m[38;2;204;204;0mn[0m[38;2;204;204;0ms[0m[38;2;204;204;0mt[0m[38;2;204;204;0ma[0m[38;2;204;204;0mn[0m[38;2;204;204;0mt[0m[38;2;204;204;0m [0m[38;2;204;204;0ma[0m[38;2;204;204;0mn[0m[38;2;204;204;0ms[0m[38;2;204;204;0mw[0m[38;2;204;204;0me[0m[38;2;204;204;0mr[0m[38;2;204;204;0ms[0m [38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m-[0m[38;2;136;136;136m+[0m
+
+[38;2;136;136;136m[[0m[38;2;136;136;136mF[0m[38;2;136;136;136mo[0m[38;2;136;136;136ml[0m[38;2;136;136;136ml[0m[38;2;136;136;136mo[0m[38;2;136;136;136mw[0m[38;2;136;136;136m [0m[38;2;136;136;136m@[0m[38;2;136;136;136mi[0m[38;2;136;136;136mg[0m[38;2;136;136;136mo[0m[38;2;136;136;136mr[0m[38;2;136;136;136m_[0m[38;2;136;136;136mc[0m[38;2;136;136;136mh[0m[38;2;136;136;136mu[0m[38;2;136;136;136mb[0m[38;2;136;136;136mi[0m[38;2;136;136;136mn[0m[38;2;136;136;136m [0m[38;2;136;136;136mf[0m[38;2;136;136;136mo[0m[38;2;136;136;136mr[0m[38;2;136;136;136m [0m[38;2;136;136;136mu[0m[38;2;136;136;136mp[0m[38;2;136;136;136md[0m[38;2;136;136;136ma[0m[38;2;136;136;136mt[0m[38;2;136;136;136me[0m[38;2;136;136;136ms[0m[38;2;136;136;136m][0m[38;2;136;136;136m[[0m[38;2;136;136;136mg[0m[38;2;136;136;136mi[0m[38;2;136;136;136mt[0m[38;2;136;136;136mh[0m[38;2;136;136;136mu[0m[38;2;136;136;136mb[0m[38;2;136;136;136m.[0m[38;2;136;136;136mc[0m[38;2;136;136;136mo[0m[38;2;136;136;136mm[0m[38;2;136;136;136m/[0m[38;2;136;136;136mc[0m[38;2;136;136;136mh[0m[38;2;136;136;136mu[0m[38;2;136;136;136mb[0m[38;2;136;136;136mi[0m[38;2;136;136;136mn[0m[38;2;136;136;136m/[0m[38;2;136;136;136mc[0m[38;2;136;136;136mh[0m[38;2;136;136;136me[0m[38;2;136;136;136ma[0m[38;2;136;136;136mt[0m[38;2;136;136;136m.[0m[38;2;136;136;136ms[0m[38;2;136;136;136mh[0m[38;2;136;136;136m][0m
diff --git a/tests/results/15 b/tests/results/15
index b614d94..12fb820 100644
--- a/tests/results/15
+++ b/tests/results/15
@@ -1,35 +1,772 @@
-[38;5;242m# Latency numbers every programmer should know [m
+[38;5;246m# Single line comments start with a number symbol.[39m
-1ns Main memory reference: Send 2,000 bytes Read 1,000,000 bytes
-[37m▗▖ [m100ns over commodity network: sequentially from SSD: 61us
- [34m▗▖ [m62ns [31m▗ [m
-L1 cache reference: 1ns [32m▗ [m
-[37m▗▖ [m1us Disk seek: 2ms
- [34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [mSSD random read: 16us [31m▗▖▗▖▗ [m
-Branch mispredict: 3ns [34m [m[32m▗▖ [m
-[37m▗▖▗▖▗▖ [m Read 1,000,000 bytes
- Compress 1KB wth Snappy: Read 1,000,000 bytes sequentially from disk:
-L2 cache reference: 4ns 2us sequentially from memory: 947us
-[37m▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m3us [31m▗ [m
- [34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗ [m
-Mutex lock/unlock: 16ns [34m [m Packet roundtrip
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m Round trip CA to Netherlands: 150ms
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗ [m10us = [32m▗▖[m in same datacenter: 500us [31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
- [34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-100ns = [34m▗▖[m [34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m [31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m1ms = [31m▗▖[m [31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m [32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[37m [m [32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
- [32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[38;5;242m# [github.com/chubin/late.nz] [MIT License] [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m [m
-[38;5;242m# Console port of "Jeff Dean's latency numbers" [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[38;5;242m# from [github.com/colin-scott/interactive_latencies] [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
-[38;5;242m [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[38;5;214m""" Multiline strings can be written[39m
+[38;5;214m using three "s, and are often used[39m
+[38;5;214m as comments[39m
+[38;5;214m"""[39m
+
+[38;5;246m####################################################[39m
+[38;5;246m# 1. Primitive Datatypes and Operators[39m
+[38;5;246m####################################################[39m
+
+[38;5;246m# You have numbers[39m
+[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => 3[39m
+
+[38;5;246m# Math is what you would expect[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => 2[39m
+[38;5;67m8[39m[38;5;252m [39m[38;5;252m-[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => 7[39m
+[38;5;67m10[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => 20[39m
+[38;5;67m35[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m [39m[38;5;246m# => 7[39m
+
+[38;5;246m# Division is a bit tricky. It is integer division and floors the results[39m
+[38;5;246m# automatically.[39m
+[38;5;67m5[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => 2[39m
+
+[38;5;246m# To fix division we need to learn about floats.[39m
+[38;5;67m2.0[39m[38;5;252m [39m[38;5;246m# This is a float[39m
+[38;5;67m11.0[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m4.0[39m[38;5;252m [39m[38;5;246m# => 2.75 ahhh...much better[39m
+
+[38;5;246m# Result of integer division truncated down both for positive and negative.[39m
+[38;5;67m5[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => 1[39m
+[38;5;67m5.0[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m3.0[39m[38;5;252m [39m[38;5;246m# => 1.0 # works on floats too[39m
+[38;5;252m-[39m[38;5;67m5[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => -2[39m
+[38;5;252m-[39m[38;5;67m5.0[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m3.0[39m[38;5;252m [39m[38;5;246m# => -2.0[39m
+
+[38;5;246m# Note that we can also import division module(Section 6 Modules)[39m
+[38;5;246m# to carry out normal division with just one '/'.[39m
+[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04m__future__[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252mdivision[39m
+
+[38;5;67m11[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m [39m[38;5;246m# => 2.75 ...normal division[39m
+[38;5;67m11[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m [39m[38;5;246m# => 2 ...floored division[39m
+
+[38;5;246m# Modulo operation[39m
+[38;5;67m7[39m[38;5;252m [39m[38;5;252m%[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => 1[39m
+
+[38;5;246m# Exponentiation (x to the yth power)[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m [39m[38;5;246m# => 16[39m
+
+[38;5;246m# Enforce precedence with parentheses[39m
+[38;5;252m([39m[38;5;67m1[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => 8[39m
+
+[38;5;246m# Boolean Operators[39m
+[38;5;246m# Note "and" and "or" are case-sensitive[39m
+[38;5;31mTrue[39m[38;5;252m [39m[38;5;70;01mand[39;00m[38;5;252m [39m[38;5;31mFalse[39m[38;5;252m [39m[38;5;246m# => False[39m
+[38;5;31mFalse[39m[38;5;252m [39m[38;5;70;01mor[39;00m[38;5;252m [39m[38;5;31mTrue[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# Note using Bool operators with ints[39m
+[38;5;67m0[39m[38;5;252m [39m[38;5;70;01mand[39;00m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => 0[39m
+[38;5;252m-[39m[38;5;67m5[39m[38;5;252m [39m[38;5;70;01mor[39;00m[38;5;252m [39m[38;5;67m0[39m[38;5;252m [39m[38;5;246m# => -5[39m
+[38;5;67m0[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;31mFalse[39m[38;5;252m [39m[38;5;246m# => True[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;31mTrue[39m[38;5;252m [39m[38;5;246m# => False[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;31mTrue[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# negate with not[39m
+[38;5;70;01mnot[39;00m[38;5;252m [39m[38;5;31mTrue[39m[38;5;252m [39m[38;5;246m# => False[39m
+[38;5;70;01mnot[39;00m[38;5;252m [39m[38;5;31mFalse[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# Equality is ==[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => True[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => False[39m
+
+[38;5;246m# Inequality is !=[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m!=[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => False[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m!=[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# More comparisons[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m [39m[38;5;246m# => True[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m [39m[38;5;246m# => False[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => True[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# Comparisons can be chained![39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => True[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => False[39m
+
+[38;5;246m# Strings are created with " or '[39m
+[38;5;214m"[39m[38;5;214mThis is a string.[39m[38;5;214m"[39m
+[38;5;214m'[39m[38;5;214mThis is also a string.[39m[38;5;214m'[39m
+
+[38;5;246m# Strings can be added too![39m
+[38;5;214m"[39m[38;5;214mHello [39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mworld![39m[38;5;214m"[39m[38;5;252m [39m[38;5;246m# => "Hello world!"[39m
+[38;5;246m# Strings can be added without using '+'[39m
+[38;5;214m"[39m[38;5;214mHello [39m[38;5;214m"[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mworld![39m[38;5;214m"[39m[38;5;252m [39m[38;5;246m# => "Hello world!"[39m
+
+[38;5;246m# ... or multiplied[39m
+[38;5;214m"[39m[38;5;214mHello[39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# => "HelloHelloHello"[39m
+
+[38;5;246m# A string can be treated like a list of characters[39m
+[38;5;214m"[39m[38;5;214mThis is a string[39m[38;5;214m"[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 'T'[39m
+
+[38;5;246m# You can find the length of a string[39m
+[38;5;31mlen[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mThis is a string[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 16[39m
+
+[38;5;246m# String formatting with %[39m
+[38;5;246m# Even though the % string operator will be deprecated on Python 3.1 and removed[39m
+[38;5;246m# later at some time, it may still be good to know how it works.[39m
+[38;5;252mx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mapple[39m[38;5;214m'[39m
+[38;5;252my[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mlemon[39m[38;5;214m'[39m
+[38;5;252mz[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mThe items in the basket are [39m[38;5;214m%s[39m[38;5;214m and [39m[38;5;214m%s[39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m%[39m[38;5;252m [39m[38;5;252m([39m[38;5;252mx[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m)[39m
+
+[38;5;246m# A newer way to format strings is the format method.[39m
+[38;5;246m# This method is the preferred way[39m
+[38;5;214m"[39m[38;5;214m{} is a {}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mThis[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mplaceholder[39m[38;5;214m"[39m[38;5;252m)[39m
+[38;5;214m"[39m[38;5;214m{0} can be {1}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mstrings[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mformatted[39m[38;5;214m"[39m[38;5;252m)[39m
+[38;5;246m# You can use keywords if you don't want to count.[39m
+[38;5;214m"[39m[38;5;214m{name} wants to eat {food}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;252mname[39m[38;5;252m=[39m[38;5;214m"[39m[38;5;214mBob[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mfood[39m[38;5;252m=[39m[38;5;214m"[39m[38;5;214mlasagna[39m[38;5;214m"[39m[38;5;252m)[39m
+
+[38;5;246m# None is an object[39m
+[38;5;31mNone[39m[38;5;252m [39m[38;5;246m# => None[39m
+
+[38;5;246m# Don't use the equality "==" symbol to compare objects to None[39m
+[38;5;246m# Use "is" instead[39m
+[38;5;214m"[39m[38;5;214metc[39m[38;5;214m"[39m[38;5;252m [39m[38;5;70;01mis[39;00m[38;5;252m [39m[38;5;31mNone[39m[38;5;252m [39m[38;5;246m# => False[39m
+[38;5;31mNone[39m[38;5;252m [39m[38;5;70;01mis[39;00m[38;5;252m [39m[38;5;31mNone[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# The 'is' operator tests for object identity. This isn't[39m
+[38;5;246m# very useful when dealing with primitive values, but is[39m
+[38;5;246m# very useful when dealing with objects.[39m
+
+[38;5;246m# Any object can be used in a Boolean context.[39m
+[38;5;246m# The following values are considered falsey:[39m
+[38;5;246m# - None[39m
+[38;5;246m# - zero of any numeric type (e.g., 0, 0L, 0.0, 0j)[39m
+[38;5;246m# - empty sequences (e.g., '', (), [])[39m
+[38;5;246m# - empty containers (e.g., {}, set())[39m
+[38;5;246m# - instances of user-defined classes meeting certain conditions[39m
+[38;5;246m# see: https://docs.python.org/2/reference/datamodel.html#object.__nonzero__[39m
+[38;5;246m#[39m
+[38;5;246m# All other values are truthy (using the bool() function on them returns True).[39m
+[38;5;31mbool[39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => False[39m
+[38;5;31mbool[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => False[39m
+
+
+[38;5;246m####################################################[39m
+[38;5;246m# 2. Variables and Collections[39m
+[38;5;246m####################################################[39m
+
+[38;5;246m# Python has a print statement[39m
+[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214mI[39m[38;5;214m'[39m[38;5;214mm Python. Nice to meet you![39m[38;5;214m"[39m[38;5;252m [39m[38;5;246m# => I'm Python. Nice to meet you![39m
+
+[38;5;246m# Simple way to get input data from console[39m
+[38;5;252minput_string_var[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mraw_input[39m[38;5;252m([39m
+[38;5;252m [39m[38;5;214m"[39m[38;5;214mEnter some data: [39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Returns the data as a string[39m
+[38;5;252minput_var[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31minput[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mEnter some data: [39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Evaluates the data as python code[39m
+[38;5;246m# Warning: Caution is recommended for input() method usage[39m
+[38;5;246m# Note: In python 3, input() is deprecated and raw_input() is renamed to input()[39m
+
+[38;5;246m# No need to declare variables before assigning to them.[39m
+[38;5;252msome_var[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m [39m[38;5;246m# Convention is to use lower_case_with_underscores[39m
+[38;5;252msome_var[39m[38;5;252m [39m[38;5;246m# => 5[39m
+
+[38;5;246m# Accessing a previously unassigned variable is an exception.[39m
+[38;5;246m# See Control Flow to learn more about exception handling.[39m
+[38;5;252msome_other_var[39m[38;5;252m [39m[38;5;246m# Raises a name error[39m
+
+[38;5;246m# if can be used as an expression[39m
+[38;5;246m# Equivalent of C's '?:' ternary operator[39m
+[38;5;214m"[39m[38;5;214myahoo![39m[38;5;214m"[39m[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;70;01melse[39;00m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;246m# => "yahoo!"[39m
+
+[38;5;246m# Lists store sequences[39m
+[38;5;252mli[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;252m][39m
+[38;5;246m# You can start with a prefilled list[39m
+[38;5;252mother_li[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m][39m
+
+[38;5;246m# Add stuff to the end of a list with append[39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1][39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 2][39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 2, 4][39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 2, 4, 3][39m
+[38;5;246m# Remove from the end with pop[39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mpop[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 3 and li is now [1, 2, 4][39m
+[38;5;246m# Let's put it back[39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 2, 4, 3] again.[39m
+
+[38;5;246m# Access a list like you would any array[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 1[39m
+[38;5;246m# Assign new values to indexes that have already been initialized with =[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m42[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 42[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# Note: setting it back to the original value[39m
+[38;5;246m# Look at the last element[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 3[39m
+
+[38;5;246m# Looking out of bounds is an IndexError[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;67m4[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# Raises an IndexError[39m
+
+[38;5;246m# You can look at ranges with slice syntax.[39m
+[38;5;246m# (It's a closed/open range for you mathy types.)[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m:[39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [2, 4][39m
+[38;5;246m# Omit the beginning[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;67m2[39m[38;5;252m:[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [4, 3][39m
+[38;5;246m# Omit the end[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;252m:[39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [1, 2, 4][39m
+[38;5;246m# Select every second entry[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;252m:[39m[38;5;252m:[39m[38;5;67m2[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# =>[1, 4][39m
+[38;5;246m# Reverse a copy of the list[39m
+[38;5;252mli[39m[38;5;252m[[39m[38;5;252m:[39m[38;5;252m:[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [3, 4, 2, 1][39m
+[38;5;246m# Use any combination of these to make advanced slices[39m
+[38;5;246m# li[start:end:step][39m
+
+[38;5;246m# Remove arbitrary elements from a list with "del"[39m
+[38;5;70;01mdel[39;00m[38;5;252m [39m[38;5;252mli[39m[38;5;252m[[39m[38;5;67m2[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# li is now [1, 2, 3][39m
+
+[38;5;246m# You can add lists[39m
+[38;5;252mli[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252mother_li[39m[38;5;252m [39m[38;5;246m# => [1, 2, 3, 4, 5, 6][39m
+[38;5;246m# Note: values for li and for other_li are not modified.[39m
+
+[38;5;246m# Concatenate lists with "extend()"[39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mextend[39m[38;5;252m([39m[38;5;252mother_li[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Now li is [1, 2, 3, 4, 5, 6][39m
+
+[38;5;246m# Remove first occurrence of a value[39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mremove[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 3, 4, 5, 6][39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mremove[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Raises a ValueError as 2 is not in the list[39m
+
+[38;5;246m# Insert an element at a specific index[39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252minsert[39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# li is now [1, 2, 3, 4, 5, 6] again[39m
+
+[38;5;246m# Get the index of the first item found[39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mindex[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 1[39m
+[38;5;252mli[39m[38;5;252m.[39m[38;5;252mindex[39m[38;5;252m([39m[38;5;67m7[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Raises a ValueError as 7 is not in the list[39m
+
+[38;5;246m# Check for existence in a list with "in"[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mli[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# Examine the length with "len()"[39m
+[38;5;31mlen[39m[38;5;252m([39m[38;5;252mli[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 6[39m
+
+[38;5;246m# Tuples are like lists but are immutable.[39m
+[38;5;252mtup[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m
+[38;5;252mtup[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 1[39m
+[38;5;252mtup[39m[38;5;252m[[39m[38;5;67m0[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m [39m[38;5;246m# Raises a TypeError[39m
+
+[38;5;246m# You can do all those list thingies on tuples too[39m
+[38;5;31mlen[39m[38;5;252m([39m[38;5;252mtup[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 3[39m
+[38;5;252mtup[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252m([39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => (1, 2, 3, 4, 5, 6)[39m
+[38;5;252mtup[39m[38;5;252m[[39m[38;5;252m:[39m[38;5;67m2[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => (1, 2)[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mtup[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# You can unpack tuples (or lists) into variables[39m
+[38;5;252ma[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mb[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# a is now 1, b is now 2 and c is now 3[39m
+[38;5;252md[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252me[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mf[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m [39m[38;5;246m# you can leave out the parentheses[39m
+[38;5;246m# Tuples are created by default if you leave out the parentheses[39m
+[38;5;252mg[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m [39m[38;5;246m# => (4, 5, 6)[39m
+[38;5;246m# Now look how easy it is to swap two values[39m
+[38;5;252me[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252md[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252md[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252me[39m[38;5;252m [39m[38;5;246m# d is now 5 and e is now 4[39m
+
+[38;5;246m# Dictionaries store mappings[39m
+[38;5;252mempty_dict[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;252m}[39m
+[38;5;246m# Here is a prefilled dictionary[39m
+[38;5;252mfilled_dict[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;214m"[39m[38;5;214mone[39m[38;5;214m"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mtwo[39m[38;5;214m"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mthree[39m[38;5;214m"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m}[39m
+
+[38;5;246m# Look up values with [][39m
+[38;5;252mfilled_dict[39m[38;5;252m[[39m[38;5;214m"[39m[38;5;214mone[39m[38;5;214m"[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => 1[39m
+
+[38;5;246m# Get all keys as a list with "keys()"[39m
+[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mkeys[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => ["three", "two", "one"][39m
+[38;5;246m# Note - Dictionary key ordering is not guaranteed.[39m
+[38;5;246m# Your results might not match this exactly.[39m
+
+[38;5;246m# Get all values as a list with "values()"[39m
+[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mvalues[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [3, 2, 1][39m
+[38;5;246m# Note - Same as above regarding key ordering.[39m
+
+[38;5;246m# Get all key-value pairs as a list of tuples with "items()"[39m
+[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mitems[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [("one", 1), ("two", 2), ("three", 3)][39m
+
+[38;5;246m# Check for existence of keys in a dictionary with "in"[39m
+[38;5;214m"[39m[38;5;214mone[39m[38;5;214m"[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mfilled_dict[39m[38;5;252m [39m[38;5;246m# => True[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mfilled_dict[39m[38;5;252m [39m[38;5;246m# => False[39m
+
+[38;5;246m# Looking up a non-existing key is a KeyError[39m
+[38;5;252mfilled_dict[39m[38;5;252m[[39m[38;5;214m"[39m[38;5;214mfour[39m[38;5;214m"[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# KeyError[39m
+
+[38;5;246m# Use "get()" method to avoid the KeyError[39m
+[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mget[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mone[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 1[39m
+[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mget[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mfour[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => None[39m
+[38;5;246m# The get method supports a default argument when the value is missing[39m
+[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mget[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mone[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 1[39m
+[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252mget[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mfour[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 4[39m
+[38;5;246m# note that filled_dict.get("four") is still => None[39m
+[38;5;246m# (get doesn't set the value in the dictionary)[39m
+
+[38;5;246m# set the value of a key with a syntax similar to lists[39m
+[38;5;252mfilled_dict[39m[38;5;252m[[39m[38;5;214m"[39m[38;5;214mfour[39m[38;5;214m"[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m [39m[38;5;246m# now, filled_dict["four"] => 4[39m
+
+[38;5;246m# "setdefault()" inserts into a dictionary only if the given key isn't present[39m
+[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252msetdefault[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mfive[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# filled_dict["five"] is set to 5[39m
+[38;5;252mfilled_dict[39m[38;5;252m.[39m[38;5;252msetdefault[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mfive[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# filled_dict["five"] is still 5[39m
+
+[38;5;246m# Sets store ... well sets (which are like lists but can contain no duplicates)[39m
+[38;5;252mempty_set[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mset[39m[38;5;252m([39m[38;5;252m)[39m
+[38;5;246m# Initialize a "set()" with a bunch of values[39m
+[38;5;252msome_set[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mset[39m[38;5;252m([39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# some_set is now set([1, 2, 3, 4])[39m
+
+[38;5;246m# order is not guaranteed, even though it may sometimes look sorted[39m
+[38;5;252manother_set[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mset[39m[38;5;252m([39m[38;5;252m[[39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# another_set is now set([1, 2, 3, 4])[39m
+
+[38;5;246m# Since Python 2.7, {} can be used to declare a set[39m
+[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => {1, 2, 3, 4}[39m
+
+[38;5;246m# Add more items to a set[39m
+[38;5;252mfilled_set[39m[38;5;252m.[39m[38;5;252madd[39m[38;5;252m([39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# filled_set is now {1, 2, 3, 4, 5}[39m
+
+[38;5;246m# Do set intersection with &[39m
+[38;5;252mother_set[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m}[39m
+[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;252m&[39m[38;5;252m [39m[38;5;252mother_set[39m[38;5;252m [39m[38;5;246m# => {3, 4, 5}[39m
+
+[38;5;246m# Do set union with |[39m
+[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;252m|[39m[38;5;252m [39m[38;5;252mother_set[39m[38;5;252m [39m[38;5;246m# => {1, 2, 3, 4, 5, 6}[39m
+
+[38;5;246m# Do set difference with -[39m
+[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m}[39m[38;5;252m [39m[38;5;252m-[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => {1, 4}[39m
+
+[38;5;246m# Do set symmetric difference with ^[39m
+[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m}[39m[38;5;252m [39m[38;5;252m^[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => {1, 4, 5}[39m
+
+[38;5;246m# Check if set on the left is a superset of set on the right[39m
+[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m}[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => False[39m
+
+[38;5;246m# Check if set on the left is a subset of set on the right[39m
+[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m}[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# Check for existence in a set with in[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;246m# => True[39m
+[38;5;67m10[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;246m# => False[39m
+[38;5;67m10[39m[38;5;252m [39m[38;5;70;01mnot[39;00m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mfilled_set[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# Check data type of variable[39m
+[38;5;31mtype[39m[38;5;252m([39m[38;5;252mli[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => list[39m
+[38;5;31mtype[39m[38;5;252m([39m[38;5;252mfilled_dict[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => dict[39m
+[38;5;31mtype[39m[38;5;252m([39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => int[39m
+
+
+[38;5;246m####################################################[39m
+[38;5;246m# 3. Control Flow[39m
+[38;5;246m####################################################[39m
+
+[38;5;246m# Let's just make a variable[39m
+[38;5;252msome_var[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m
+
+[38;5;246m# Here is an if statement. Indentation is significant in python![39m
+[38;5;246m# prints "some_var is smaller than 10"[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252msome_var[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214msome_var is totally bigger than 10.[39m[38;5;214m"[39m
+[38;5;70;01melif[39;00m[38;5;252m [39m[38;5;252msome_var[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# This elif clause is optional.[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214msome_var is smaller than 10.[39m[38;5;214m"[39m
+[38;5;70;01melse[39;00m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# This is optional too.[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214msome_var is indeed 10.[39m[38;5;214m"[39m
+
+[38;5;214m"""[39m
+[38;5;214mFor loops iterate over lists[39m
+[38;5;214mprints:[39m
+[38;5;214m dog is a mammal[39m
+[38;5;214m cat is a mammal[39m
+[38;5;214m mouse is a mammal[39m
+[38;5;214m"""[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252manimal[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;214m"[39m[38;5;214mdog[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mcat[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mmouse[39m[38;5;214m"[39m[38;5;252m][39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;246m# You can use {0} to interpolate formatted strings. (See above.)[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214m{0} is a mammal[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;252manimal[39m[38;5;252m)[39m
+
+[38;5;214m"""[39m
+[38;5;214m"range(number)" returns a list of numbers[39m
+[38;5;214mfrom zero to the given number[39m
+[38;5;214mprints:[39m
+[38;5;214m 0[39m
+[38;5;214m 1[39m
+[38;5;214m 2[39m
+[38;5;214m 3[39m
+[38;5;214m"""[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;31mrange[39m[38;5;252m([39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mi[39m
+
+[38;5;214m"""[39m
+[38;5;214m"range(lower, upper)" returns a list of numbers[39m
+[38;5;214mfrom the lower number to the upper number[39m
+[38;5;214mprints:[39m
+[38;5;214m 4[39m
+[38;5;214m 5[39m
+[38;5;214m 6[39m
+[38;5;214m 7[39m
+[38;5;214m"""[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;31mrange[39m[38;5;252m([39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m8[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mi[39m
+
+[38;5;214m"""[39m
+[38;5;214mWhile loops go until a condition is no longer met.[39m
+[38;5;214mprints:[39m
+[38;5;214m 0[39m
+[38;5;214m 1[39m
+[38;5;214m 2[39m
+[38;5;214m 3[39m
+[38;5;214m"""[39m
+[38;5;252mx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m0[39m
+[38;5;70;01mwhile[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mx[39m
+[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m [39m[38;5;246m# Shorthand for x = x + 1[39m
+
+[38;5;246m# Handle exceptions with a try/except block[39m
+
+[38;5;246m# Works on Python 2.6 and up:[39m
+[38;5;70;01mtry[39;00m[38;5;252m:[39m
+[38;5;252m [39m[38;5;246m# Use "raise" to raise an error[39m
+[38;5;252m [39m[38;5;70;01mraise[39;00m[38;5;252m [39m[38;5;250mIndexError[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mThis is an index error[39m[38;5;214m"[39m[38;5;252m)[39m
+[38;5;70;01mexcept[39;00m[38;5;252m [39m[38;5;250mIndexError[39m[38;5;252m [39m[38;5;70;01mas[39;00m[38;5;252m [39m[38;5;252me[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mpass[39;00m[38;5;252m [39m[38;5;246m# Pass is just a no-op. Usually you would do recovery here.[39m
+[38;5;70;01mexcept[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;250mTypeError[39m[38;5;252m,[39m[38;5;252m [39m[38;5;250mNameError[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mpass[39;00m[38;5;252m [39m[38;5;246m# Multiple exceptions can be handled together, if required.[39m
+[38;5;70;01melse[39;00m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# Optional clause to the try/except block. Must follow all except blocks[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214mAll good![39m[38;5;214m"[39m[38;5;252m [39m[38;5;246m# Runs only if the code in try raises no exceptions[39m
+[38;5;70;01mfinally[39;00m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# Execute under all circumstances[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214mWe can clean up resources here[39m[38;5;214m"[39m
+
+[38;5;246m# Instead of try/finally to cleanup resources you can use a with statement[39m
+[38;5;70;01mwith[39;00m[38;5;252m [39m[38;5;31mopen[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mmyfile.txt[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;70;01mas[39;00m[38;5;252m [39m[38;5;252mf[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mline[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mf[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mline[39m
+
+
+[38;5;246m####################################################[39m
+[38;5;246m# 4. Functions[39m
+[38;5;246m####################################################[39m
+
+[38;5;246m# Use "def" to create new functions[39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68madd[39m[38;5;252m([39m[38;5;252mx[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214mx is {0} and y is {1}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;252mx[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m)[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m [39m[38;5;246m# Return values with a return statement[39m
+
+
+[38;5;246m# Calling functions with parameters[39m
+[38;5;252madd[39m[38;5;252m([39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => prints out "x is 5 and y is 6" and returns 11[39m
+
+[38;5;246m# Another way to call functions is with keyword arguments[39m
+[38;5;252madd[39m[38;5;252m([39m[38;5;252my[39m[38;5;252m=[39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mx[39m[38;5;252m=[39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Keyword arguments can arrive in any order.[39m
+
+
+[38;5;246m# You can define functions that take a variable number of[39m
+[38;5;246m# positional args, which will be interpreted as a tuple by using *[39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mvarargs[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252margs[39m
+
+
+[38;5;252mvarargs[39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => (1, 2, 3)[39m
+
+
+[38;5;246m# You can define functions that take a variable number of[39m
+[38;5;246m# keyword args, as well, which will be interpreted as a dict by using **[39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mkeyword_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mkwargs[39m
+
+
+[38;5;246m# Let's call it to see what happens[39m
+[38;5;252mkeyword_args[39m[38;5;252m([39m[38;5;252mbig[39m[38;5;252m=[39m[38;5;214m"[39m[38;5;214mfoot[39m[38;5;214m"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mloch[39m[38;5;252m=[39m[38;5;214m"[39m[38;5;214mness[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => {"big": "foot", "loch": "ness"}[39m
+
+
+[38;5;246m# You can do both at once, if you like[39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mall_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252margs[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mkwargs[39m
+
+
+[38;5;214m"""[39m
+[38;5;214mall_the_args(1, 2, a=3, b=4) prints:[39m
+[38;5;214m (1, 2)[39m
+[38;5;214m {"a": 3, "b": 4}[39m
+[38;5;214m"""[39m
+
+[38;5;246m# When calling functions, you can do the opposite of args/kwargs![39m
+[38;5;246m# Use * to expand positional args and use ** to expand keyword args.[39m
+[38;5;252margs[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m)[39m
+[38;5;252mkwargs[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;214m"[39m[38;5;214ma[39m[38;5;214m"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mb[39m[38;5;214m"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m}[39m
+[38;5;252mall_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# equivalent to all_the_args(1, 2, 3, 4)[39m
+[38;5;252mall_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# equivalent to all_the_args(a=3, b=4)[39m
+[38;5;252mall_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# equivalent to all_the_args(1, 2, 3, 4, a=3, b=4)[39m
+
+
+[38;5;246m# you can pass args and kwargs along to other functions that take args/kwargs[39m
+[38;5;246m# by expanding them with * and ** respectively[39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mpass_all_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mall_the_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mvarargs[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m)[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mkeyword_args[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m
+
+
+[38;5;246m# Function Scope[39m
+[38;5;252mx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m
+
+
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mset_x[39m[38;5;252m([39m[38;5;252mnum[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;246m# Local var x not the same as global variable x[39m
+[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mnum[39m[38;5;252m [39m[38;5;246m# => 43[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;246m# => 43[39m
+
+
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mset_global_x[39m[38;5;252m([39m[38;5;252mnum[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mglobal[39;00m[38;5;252m [39m[38;5;252mx[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;246m# => 5[39m
+[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mnum[39m[38;5;252m [39m[38;5;246m# global var x is now set to 6[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;246m# => 6[39m
+
+
+[38;5;252mset_x[39m[38;5;252m([39m[38;5;67m43[39m[38;5;252m)[39m
+[38;5;252mset_global_x[39m[38;5;252m([39m[38;5;67m6[39m[38;5;252m)[39m
+
+
+[38;5;246m# Python has first class functions[39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mcreate_adder[39m[38;5;252m([39m[38;5;252mx[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68madder[39m[38;5;252m([39m[38;5;252my[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252my[39m
+
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252madder[39m
+
+
+[38;5;252madd_10[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mcreate_adder[39m[38;5;252m([39m[38;5;67m10[39m[38;5;252m)[39m
+[38;5;252madd_10[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 13[39m
+
+[38;5;246m# There are also anonymous functions[39m
+[38;5;252m([39m[38;5;70;01mlambda[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m:[39m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => True[39m
+[38;5;252m([39m[38;5;70;01mlambda[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m:[39m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252my[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 5[39m
+
+[38;5;246m# There are built-in higher order functions[39m
+[38;5;31mmap[39m[38;5;252m([39m[38;5;252madd_10[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [11, 12, 13][39m
+[38;5;31mmap[39m[38;5;252m([39m[38;5;31mmax[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [4, 2, 3][39m
+
+[38;5;31mfilter[39m[38;5;252m([39m[38;5;70;01mlambda[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m:[39m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m7[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [6, 7][39m
+
+[38;5;246m# We can use list comprehensions for nice maps and filters[39m
+[38;5;252m[[39m[38;5;252madd_10[39m[38;5;252m([39m[38;5;252mi[39m[38;5;252m)[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [11, 12, 13][39m
+[38;5;252m[[39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m7[39m[38;5;252m][39m[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m][39m[38;5;252m [39m[38;5;246m# => [6, 7][39m
+
+[38;5;246m# You can construct set and dict comprehensions as well.[39m
+[38;5;252m{[39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;214m'[39m[38;5;214mabcddeef[39m[38;5;214m'[39m[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;214m'[39m[38;5;214mabc[39m[38;5;214m'[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => {'a', 'b', 'c'}[39m
+[38;5;252m{[39m[38;5;252mx[39m[38;5;252m:[39m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;31mrange[39m[38;5;252m([39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m}[39m[38;5;252m [39m[38;5;246m# => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}[39m
+
+
+[38;5;246m####################################################[39m
+[38;5;246m# 5. Classes[39m
+[38;5;246m####################################################[39m
+
+[38;5;246m# We subclass from object to get a class.[39m
+[38;5;70;01mclass[39;00m[38;5;252m [39m[38;5;68;04mHuman[39;00m[38;5;252m([39m[38;5;31mobject[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;246m# A class attribute. It is shared by all instances of this class[39m
+[38;5;252m [39m[38;5;252mspecies[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mH. sapiens[39m[38;5;214m"[39m
+
+[38;5;252m [39m[38;5;246m# Basic initializer, this is called when this class is instantiated.[39m
+[38;5;252m [39m[38;5;246m# Note that the double leading and trailing underscores denote objects[39m
+[38;5;252m [39m[38;5;246m# or attributes that are used by python but that live in user-controlled[39m
+[38;5;252m [39m[38;5;246m# namespaces. You should not invent such names on your own.[39m
+[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68m__init__[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mname[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;246m# Assign the argument to the instance's name attribute[39m
+[38;5;252m [39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252mname[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mname[39m
+
+[38;5;252m [39m[38;5;246m# Initialize property[39m
+[38;5;252m [39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252mage[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m0[39m
+
+[38;5;252m [39m[38;5;246m# An instance method. All methods take "self" as the first argument[39m
+[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68msay[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mmsg[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214m{0}: {1}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252mname[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mmsg[39m[38;5;252m)[39m
+
+[38;5;252m [39m[38;5;246m# A class method is shared among all instances[39m
+[38;5;252m [39m[38;5;246m# They are called with the calling class as the first argument[39m
+[38;5;252m [39m[38;5;214m@classmethod[39m
+[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mget_species[39m[38;5;252m([39m[38;5;31mcls[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;31mcls[39m[38;5;252m.[39m[38;5;252mspecies[39m
+
+[38;5;252m [39m[38;5;246m# A static method is called without a class or instance reference[39m
+[38;5;252m [39m[38;5;214m@staticmethod[39m
+[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mgrunt[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214m*grunt*[39m[38;5;214m"[39m
+
+[38;5;252m [39m[38;5;246m# A property is just like a getter.[39m
+[38;5;252m [39m[38;5;246m# It turns the method age() into an read-only attribute[39m
+[38;5;252m [39m[38;5;246m# of the same name.[39m
+[38;5;252m [39m[38;5;214m@property[39m
+[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mage[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252m_age[39m
+
+[38;5;252m [39m[38;5;246m# This allows the property to be set[39m
+[38;5;252m [39m[38;5;214m@age.setter[39m
+[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mage[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mage[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252m_age[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mage[39m
+
+[38;5;252m [39m[38;5;246m# This allows the property to be deleted[39m
+[38;5;252m [39m[38;5;214m@age.deleter[39m
+[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mage[39m[38;5;252m([39m[38;5;31mself[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mdel[39;00m[38;5;252m [39m[38;5;31mself[39m[38;5;252m.[39m[38;5;252m_age[39m
+
+
+[38;5;246m# Instantiate a class[39m
+[38;5;252mi[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mHuman[39m[38;5;252m([39m[38;5;252mname[39m[38;5;252m=[39m[38;5;214m"[39m[38;5;214mIan[39m[38;5;214m"[39m[38;5;252m)[39m
+[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m.[39m[38;5;252msay[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mhi[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# prints out "Ian: hi"[39m
+
+[38;5;252mj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mHuman[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mJoel[39m[38;5;214m"[39m[38;5;252m)[39m
+[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mj[39m[38;5;252m.[39m[38;5;252msay[39m[38;5;252m([39m[38;5;214m"[39m[38;5;214mhello[39m[38;5;214m"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# prints out "Joel: hello"[39m
+
+[38;5;246m# Call our class method[39m
+[38;5;252mi[39m[38;5;252m.[39m[38;5;252mget_species[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => "H. sapiens"[39m
+
+[38;5;246m# Change the shared attribute[39m
+[38;5;252mHuman[39m[38;5;252m.[39m[38;5;252mspecies[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mH. neanderthalensis[39m[38;5;214m"[39m
+[38;5;252mi[39m[38;5;252m.[39m[38;5;252mget_species[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => "H. neanderthalensis"[39m
+[38;5;252mj[39m[38;5;252m.[39m[38;5;252mget_species[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => "H. neanderthalensis"[39m
+
+[38;5;246m# Call the static method[39m
+[38;5;252mHuman[39m[38;5;252m.[39m[38;5;252mgrunt[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => "*grunt*"[39m
+
+[38;5;246m# Update the property[39m
+[38;5;252mi[39m[38;5;252m.[39m[38;5;252mage[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m42[39m
+
+[38;5;246m# Get the property[39m
+[38;5;252mi[39m[38;5;252m.[39m[38;5;252mage[39m[38;5;252m [39m[38;5;246m# => 42[39m
+
+[38;5;246m# Delete the property[39m
+[38;5;70;01mdel[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m.[39m[38;5;252mage[39m
+[38;5;252mi[39m[38;5;252m.[39m[38;5;252mage[39m[38;5;252m [39m[38;5;246m# => raises an AttributeError[39m
+
+[38;5;246m####################################################[39m
+[38;5;246m# 6. Modules[39m
+[38;5;246m####################################################[39m
+
+[38;5;246m# You can import modules[39m
+[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m
+
+[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mmath[39m[38;5;252m.[39m[38;5;252msqrt[39m[38;5;252m([39m[38;5;67m16[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 4[39m
+
+[38;5;246m# You can get specific functions from a module[39m
+[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252mceil[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mfloor[39m
+
+[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mceil[39m[38;5;252m([39m[38;5;67m3.7[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 4.0[39m
+[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mfloor[39m[38;5;252m([39m[38;5;67m3.7[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => 3.0[39m
+
+[38;5;246m# You can import all functions from a module.[39m
+[38;5;246m# Warning: this is not recommended[39m
+[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252m*[39m
+
+[38;5;246m# You can shorten module names[39m
+[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m[38;5;252m [39m[38;5;70;01mas[39;00m[38;5;252m [39m[38;5;68;04mm[39;00m
+
+[38;5;252mmath[39m[38;5;252m.[39m[38;5;252msqrt[39m[38;5;252m([39m[38;5;67m16[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;252mm[39m[38;5;252m.[39m[38;5;252msqrt[39m[38;5;252m([39m[38;5;67m16[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => True[39m
+[38;5;246m# you can also test that the functions are equivalent[39m
+[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252msqrt[39m
+
+[38;5;252mmath[39m[38;5;252m.[39m[38;5;252msqrt[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;252mm[39m[38;5;252m.[39m[38;5;252msqrt[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;252msqrt[39m[38;5;252m [39m[38;5;246m# => True[39m
+
+[38;5;246m# Python modules are just ordinary python files. You[39m
+[38;5;246m# can write your own, and import them. The name of the[39m
+[38;5;246m# module is the same as the name of the file.[39m
+
+[38;5;246m# You can find out which functions and attributes[39m
+[38;5;246m# defines a module.[39m
+[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;68;04mmath[39;00m
+
+[38;5;31mdir[39m[38;5;252m([39m[38;5;252mmath[39m[38;5;252m)[39m
+
+
+[38;5;246m# If you have a Python script named math.py in the same[39m
+[38;5;246m# folder as your current script, the file math.py will[39m
+[38;5;246m# be loaded instead of the built-in Python module.[39m
+[38;5;246m# This happens because the local folder has priority[39m
+[38;5;246m# over Python's built-in libraries.[39m
+
+
+[38;5;246m####################################################[39m
+[38;5;246m# 7. Advanced[39m
+[38;5;246m####################################################[39m
+
+[38;5;246m# Generators[39m
+[38;5;246m# A generator "generates" values as they are requested instead of storing[39m
+[38;5;246m# everything up front[39m
+
+[38;5;246m# The following method (*NOT* a generator) will double all values and store it[39m
+[38;5;246m# in `double_arr`. For large size of iterables, that might get huge![39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mdouble_numbers[39m[38;5;252m([39m[38;5;252miterable[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mdouble_arr[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;252m][39m
+[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252miterable[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mdouble_arr[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252mi[39m[38;5;252m)[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mdouble_arr[39m
+
+
+[38;5;246m# Running the following would mean we'll double all values first and return all[39m
+[38;5;246m# of them back to be checked by our condition[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mvalue[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mdouble_numbers[39m[38;5;252m([39m[38;5;31mrange[39m[38;5;252m([39m[38;5;67m1000000[39m[38;5;252m)[39m[38;5;252m)[39m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# `test_non_generator`[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mvalue[39m
+[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252mvalue[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m
+
+
+[38;5;246m# We could instead use a generator to "generate" the doubled value as the item[39m
+[38;5;246m# is being requested[39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mdouble_numbers_generator[39m[38;5;252m([39m[38;5;252miterable[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252miterable[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01myield[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252mi[39m
+
+
+[38;5;246m# Running the same code as before, but with a generator, now allows us to iterate[39m
+[38;5;246m# over the values and doubling them one by one as they are being consumed by[39m
+[38;5;246m# our logic. Hence as soon as we see a value > 5, we break out of the[39m
+[38;5;246m# loop and don't need to double most of the values sent in (MUCH FASTER!)[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mvalue[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mdouble_numbers_generator[39m[38;5;252m([39m[38;5;31mxrange[39m[38;5;252m([39m[38;5;67m1000000[39m[38;5;252m)[39m[38;5;252m)[39m[38;5;252m:[39m[38;5;252m [39m[38;5;246m# `test_generator`[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252mvalue[39m
+[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252mvalue[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m
+
+[38;5;246m# BTW: did you notice the use of `range` in `test_non_generator` and `xrange` in `test_generator`?[39m
+[38;5;246m# Just as `double_numbers_generator` is the generator version of `double_numbers`[39m
+[38;5;246m# We have `xrange` as the generator version of `range`[39m
+[38;5;246m# `range` would return back and array with 1000000 values for us to use[39m
+[38;5;246m# `xrange` would generate 1000000 values for us as we request / iterate over those items[39m
+
+[38;5;246m# Just as you can create a list comprehension, you can create generator[39m
+[38;5;246m# comprehensions as well.[39m
+[38;5;252mvalues[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m([39m[38;5;252m-[39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m][39m[38;5;252m)[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mvalues[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mprint[39;00m[38;5;252m([39m[38;5;252mx[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# prints -1 -2 -3 -4 -5 to console/terminal[39m
+
+[38;5;246m# You can also cast a generator comprehension directly to a list.[39m
+[38;5;252mvalues[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m([39m[38;5;252m-[39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m][39m[38;5;252m)[39m
+[38;5;252mgen_to_list[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mlist[39m[38;5;252m([39m[38;5;252mvalues[39m[38;5;252m)[39m
+[38;5;70;01mprint[39;00m[38;5;252m([39m[38;5;252mgen_to_list[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# => [-1, -2, -3, -4, -5][39m
+
+[38;5;246m# Decorators[39m
+[38;5;246m# A decorator is a higher order function, which accepts and returns a function.[39m
+[38;5;246m# Simple usage example – add_apples decorator will add 'Apple' element into[39m
+[38;5;246m# fruits list returned by get_fruits target function.[39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68madd_apples[39m[38;5;252m([39m[38;5;252mfunc[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mget_fruits[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mfruits[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mfunc[39m[38;5;252m([39m[38;5;252m)[39m
+[38;5;252m [39m[38;5;252mfruits[39m[38;5;252m.[39m[38;5;252mappend[39m[38;5;252m([39m[38;5;214m'[39m[38;5;214mApple[39m[38;5;214m'[39m[38;5;252m)[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mfruits[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mget_fruits[39m
+
+[38;5;214m@add_apples[39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mget_fruits[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252m[[39m[38;5;214m'[39m[38;5;214mBanana[39m[38;5;214m'[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mMango[39m[38;5;214m'[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mOrange[39m[38;5;214m'[39m[38;5;252m][39m
+
+[38;5;246m# Prints out the list of fruits with 'Apple' element in it:[39m
+[38;5;246m# Banana, Mango, Orange, Apple[39m
+[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;214m'[39m[38;5;214m, [39m[38;5;214m'[39m[38;5;252m.[39m[38;5;252mjoin[39m[38;5;252m([39m[38;5;252mget_fruits[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m)[39m
+
+[38;5;246m# in this example beg wraps say[39m
+[38;5;246m# Beg will call say. If say_please is True then it will change the returned[39m
+[38;5;246m# message[39m
+[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mfunctools[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252mwraps[39m
+
+
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mbeg[39m[38;5;252m([39m[38;5;252mtarget_function[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;214m@wraps[39m[38;5;252m([39m[38;5;252mtarget_function[39m[38;5;252m)[39m
+[38;5;252m [39m[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68mwrapper[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mmsg[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252msay_please[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mtarget_function[39m[38;5;252m([39m[38;5;252m*[39m[38;5;252margs[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m*[39m[38;5;252mkwargs[39m[38;5;252m)[39m
+[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252msay_please[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;214m"[39m[38;5;214m{} {}[39m[38;5;214m"[39m[38;5;252m.[39m[38;5;252mformat[39m[38;5;252m([39m[38;5;252mmsg[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mPlease! I am poor :([39m[38;5;214m"[39m[38;5;252m)[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mmsg[39m
+
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mwrapper[39m
+
+
+[38;5;214m@beg[39m
+[38;5;70;01mdef[39;00m[38;5;252m [39m[38;5;68msay[39m[38;5;252m([39m[38;5;252msay_please[39m[38;5;252m=[39m[38;5;31mFalse[39m[38;5;252m)[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mmsg[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214mCan you buy me a beer?[39m[38;5;214m"[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mmsg[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252msay_please[39m
+
+
+[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252msay[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Can you buy me a beer?[39m
+[38;5;70;01mprint[39;00m[38;5;252m [39m[38;5;252msay[39m[38;5;252m([39m[38;5;252msay_please[39m[38;5;252m=[39m[38;5;31mTrue[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m# Can you buy me a beer? Please! I am poor :([39m
diff --git a/tests/results/16 b/tests/results/16
index ba45f23..b614d94 100644
--- a/tests/results/16
+++ b/tests/results/16
@@ -1,90 +1,35 @@
-[38;5;246m# Microsoft Azure CLI 2.0[39m
-[38;5;246m# Command-line tools for Azure[39m
+[38;5;242m# Latency numbers every programmer should know [m
-[38;5;246m# Install Azure CLI 2.0 with one curl command.[39m
-[38;5;252mcurl[39m[38;5;252m [39m[38;5;252m-L[39m[38;5;252m [39m[38;5;252mhttps://aka.ms/InstallAzureCli[39m[38;5;252m [39m[38;5;252m|[39m[38;5;252m [39m[38;5;252mbash[39m
-
-[38;5;246m# create a resource group named "MyResourceGroup" in the westus2 region of Azure[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mgroup[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m-l[39m[38;5;252m [39m[38;5;252mwestus2[39m[38;5;252m [39m
-
-[38;5;246m# create a Linux VM using the UbuntuTLS image, with two attached storage disks of 10 GB and 20 GB[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m--ssh-key-value[39m[38;5;252m [39m[38;5;87m$HOME[39m[38;5;252m/.ssh/id_rsa.pub[39m[38;5;252m [39m[38;5;252m--image[39m[38;5;252m [39m[38;5;252mUbuntuLTS[39m[38;5;252m [39m[38;5;252m--data-disk-sizes-gb[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m [39m[38;5;67m20[39m
-
-[38;5;246m# list VMs[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
-
-[38;5;246m# list only VMs having distinct state[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m-d[39m[38;5;252m [39m[38;5;252m--query[39m[38;5;252m [39m[38;5;214m"[?powerState=='VM running']"[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
-
-[38;5;246m# delete VM (with the name MyLinuxVM in the group MyResourceGroup)[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mdelete[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m[38;5;252m [39m[38;5;252m--yes[39m
-
-[38;5;246m# Delete all VMs in a resource group[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mdelete[39m[38;5;252m [39m[38;5;252m--ids[39m[38;5;252m [39m[38;5;70;01m$([39;00m[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m--query[39m[38;5;252m [39m[38;5;214m"[].id"[39m[38;5;252m [39m[38;5;252m-o[39m[38;5;252m [39m[38;5;252mtsv[39m[38;5;70;01m)[39;00m
-
-[38;5;246m# Create an Image based on a running VM[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mdeallocate[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mgeneralize[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mimage[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;252mMyTestImage[39m[38;5;252m [39m[38;5;252m--source[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m
-
-[38;5;246m# Running VM based on a VHD[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252mblob[39m[38;5;252m [39m[38;5;252mupload[39m[38;5;252m [39m[38;5;252m--account-name[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214m${[39m[38;5;87maccount_name[39m[38;5;214m}[39m[38;5;214m"[39m[38;5;252m [39m[38;5;214m\[39m
-[38;5;252m [39m[38;5;252m--account-key[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214m${[39m[38;5;87maccount_key[39m[38;5;214m}[39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m--container-name[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214m${[39m[38;5;87mcontainer_name[39m[38;5;214m}[39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m--type[39m[38;5;252m [39m[38;5;252mpage[39m[38;5;252m [39m[38;5;214m\[39m
-[38;5;252m [39m[38;5;252m--file[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214m${[39m[38;5;87mfile[39m[38;5;214m}[39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214m${[39m[38;5;87mvhd_name[39m[38;5;214m}[39m[38;5;214m"[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mdisk[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;214m\[39m
-[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;214m${[39m[38;5;87mresource_group[39m[38;5;214m}[39m[38;5;252m [39m[38;5;214m\[39m
-[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;252mmyManagedDisk[39m[38;5;252m [39m[38;5;214m\[39m
-[38;5;252m [39m[38;5;252m--source[39m[38;5;252m [39m[38;5;252mhttps://[39m[38;5;214m${[39m[38;5;87maccount_name[39m[38;5;214m}[39m[38;5;252m.blob.core.windows.net/[39m[38;5;214m${[39m[38;5;87mcontainer_name[39m[38;5;214m}[39m[38;5;252m/[39m[38;5;214m${[39m[38;5;87mvhd_name[39m[38;5;214m}[39m
-
-[38;5;246m# open port[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mopen-port[39m[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m[38;5;252m [39m[38;5;252m--port[39m[38;5;252m [39m[38;5;67m443[39m[38;5;252m [39m[38;5;252m--priority[39m[38;5;252m [39m[38;5;67m899[39m
-
-[38;5;246m# Show storage accounts[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252maccount[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
-
-[38;5;246m# Show contaniers for an account[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252mcontainer[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--account-name[39m[38;5;252m [39m[38;5;252mmystorageaccount[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
-
-[38;5;246m# Show blobs in a container[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252mblob[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--account-name[39m[38;5;252m [39m[38;5;252mmystorageaccount[39m[38;5;252m [39m[38;5;252m--container-name[39m[38;5;252m [39m[38;5;252mmycontainer[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
-
-[38;5;246m# list account keys[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252maccount[39m[38;5;252m [39m[38;5;252mkeys[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--account-name[39m[38;5;252m [39m[38;5;252mSTORAGE_NAME[39m[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;252mRESOURCE_GROUP[39m
-
-[38;5;246m# Show own images[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mimage[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
-
-[38;5;246m# Configure default storage location[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mconfigure[39m[38;5;252m [39m[38;5;252m--defaults[39m[38;5;252m [39m[38;5;87mlocation[39m[38;5;252m=[39m[38;5;252meastus2[39m
-
-[38;5;246m# Show disks[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mdisk[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
-
-[38;5;246m# Copy blob[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252mblob[39m[38;5;252m [39m[38;5;252mcopy[39m[38;5;252m [39m[38;5;252mstart[39m[38;5;252m [39m[38;5;214m\[39m
-[38;5;252m [39m[38;5;252m--source-uri[39m[38;5;252m [39m[38;5;214m'https://md-ldh5nknx2rkz.blob.core.windows.net/jzwuuuzzapn0/abcd?sv=2017-04-17&sr=b&si=68041718-6828-4f5e-9e6e-a1b719975062&sig=XXX'[39m[38;5;252m [39m[38;5;214m\[39m
-[38;5;252m [39m[38;5;252m--account-key[39m[38;5;252m [39m[38;5;87mXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX[39m[38;5;252m=[39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m\[39m
-[38;5;252m [39m[38;5;252m--account-name[39m[38;5;252m [39m[38;5;252mdestaccount[39m[38;5;252m [39m[38;5;214m\[39m
-[38;5;252m [39m[38;5;252m--destination-container[39m[38;5;252m [39m[38;5;252mvms[39m[38;5;252m [39m[38;5;214m\[39m
-[38;5;252m [39m[38;5;252m--destination-blob[39m[38;5;252m [39m[38;5;252mDESTINATION-blob.vhd[39m
-
-[38;5;246m# List virtual networks[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mnetwork[39m[38;5;252m [39m[38;5;252mvnet[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
-
-[38;5;246m# List virtual networks adapters[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mnetwork[39m[38;5;252m [39m[38;5;252mnic[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
-
-[38;5;246m# List public IP addresses used by the VMs[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mlist-ip-addresses[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
-
-[38;5;246m# create snapshot[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252msnapshot[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;252mIC-EXASOL-001[39m[38;5;252m [39m[38;5;252m--source[39m[38;5;252m [39m[38;5;252mvm1-disk1[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mvm1-snap1[39m
-
-[38;5;246m# create SAS url for a snapshot[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252msnapshot[39m[38;5;252m [39m[38;5;252mgrant-access[39m[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;252mIC-EXASOL-001[39m[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;252mvm1-snap1[39m[38;5;252m [39m[38;5;252m--duration-in-seconds[39m[38;5;252m [39m[38;5;67m36000[39m[38;5;252m [39m[38;5;252m--query[39m[38;5;252m [39m[38;5;214m'[accessSas]'[39m[38;5;252m [39m[38;5;252m-o[39m[38;5;252m [39m[38;5;252mtsv[39m
-
-[38;5;246m# attach disk[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mdisk[39m[38;5;252m [39m[38;5;252mattach[39m[38;5;252m [39m[38;5;252m--vm-name[39m[38;5;252m [39m[38;5;252mvm1[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mRESOURCE_GROUP[39m[38;5;252m [39m[38;5;252m--disk[39m[38;5;252m [39m[38;5;252mDISK1_ID[39m
-
-[38;5;246m# detach disk[39m
-[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mdisk[39m[38;5;252m [39m[38;5;252mdetach[39m[38;5;252m [39m[38;5;252m--vm-name[39m[38;5;252m [39m[38;5;252mvm1[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mRESOURCE_GROUP[39m[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;252mDISK1_ID[39m
+1ns Main memory reference: Send 2,000 bytes Read 1,000,000 bytes
+[37m▗▖ [m100ns over commodity network: sequentially from SSD: 61us
+ [34m▗▖ [m62ns [31m▗ [m
+L1 cache reference: 1ns [32m▗ [m
+[37m▗▖ [m1us Disk seek: 2ms
+ [34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [mSSD random read: 16us [31m▗▖▗▖▗ [m
+Branch mispredict: 3ns [34m [m[32m▗▖ [m
+[37m▗▖▗▖▗▖ [m Read 1,000,000 bytes
+ Compress 1KB wth Snappy: Read 1,000,000 bytes sequentially from disk:
+L2 cache reference: 4ns 2us sequentially from memory: 947us
+[37m▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m3us [31m▗ [m
+ [34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗ [m
+Mutex lock/unlock: 16ns [34m [m Packet roundtrip
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m Round trip CA to Netherlands: 150ms
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗ [m10us = [32m▗▖[m in same datacenter: 500us [31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+ [34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+100ns = [34m▗▖[m [34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m [31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m1ms = [31m▗▖[m [31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[34m [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m [32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[37m [m [32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+ [32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[38;5;242m# [github.com/chubin/late.nz] [MIT License] [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m[31m [m
+[38;5;242m# Console port of "Jeff Dean's latency numbers" [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[38;5;242m# from [github.com/colin-scott/interactive_latencies] [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
+[38;5;242m [m[32m▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ [m
diff --git a/tests/results/17 b/tests/results/17
index 2822759..ba45f23 100644
--- a/tests/results/17
+++ b/tests/results/17
@@ -1,20 +1,90 @@
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mabcdefgh[39m[38;5;214m'[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252mn[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mm[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mchar[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mchars[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214md[39m[38;5;214m'[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mcd[39m[38;5;214m'[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;246m# starting from n=2 characters in and m=3 in length;[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m[[39m[38;5;252mn[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m:[39m[38;5;252mn[39m[38;5;252m+[39m[38;5;252mm[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m][39m
-[38;5;214m'[39m[38;5;214mbcd[39m[38;5;214m'[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;246m# starting from n characters in, up to the end of the string;[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m[[39m[38;5;252mn[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m:[39m[38;5;252m][39m
-[38;5;214m'[39m[38;5;214mbcdefgh[39m[38;5;214m'[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;246m# whole string minus last character;[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m[[39m[38;5;252m:[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m][39m
-[38;5;214m'[39m[38;5;214mabcdefg[39m[38;5;214m'[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;246m# starting from a known character char="d" within the string and of m length;[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252mindx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m.[39m[38;5;252mindex[39m[38;5;252m([39m[38;5;252mchar[39m[38;5;252m)[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m[[39m[38;5;252mindx[39m[38;5;252m:[39m[38;5;252mindx[39m[38;5;252m+[39m[38;5;252mm[39m[38;5;252m][39m
-[38;5;214m'[39m[38;5;214mdef[39m[38;5;214m'[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;246m# starting from a known substring chars="cd" within the string and of m length.[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252mindx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m.[39m[38;5;252mindex[39m[38;5;252m([39m[38;5;252mchars[39m[38;5;252m)[39m
-[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m[[39m[38;5;252mindx[39m[38;5;252m:[39m[38;5;252mindx[39m[38;5;252m+[39m[38;5;252mm[39m[38;5;252m][39m
-[38;5;214m'[39m[38;5;214mcde[39m[38;5;214m'[39m
-[38;5;252m>>[39m[38;5;252m>[39m
+[38;5;246m# Microsoft Azure CLI 2.0[39m
+[38;5;246m# Command-line tools for Azure[39m
+
+[38;5;246m# Install Azure CLI 2.0 with one curl command.[39m
+[38;5;252mcurl[39m[38;5;252m [39m[38;5;252m-L[39m[38;5;252m [39m[38;5;252mhttps://aka.ms/InstallAzureCli[39m[38;5;252m [39m[38;5;252m|[39m[38;5;252m [39m[38;5;252mbash[39m
+
+[38;5;246m# create a resource group named "MyResourceGroup" in the westus2 region of Azure[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mgroup[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m-l[39m[38;5;252m [39m[38;5;252mwestus2[39m[38;5;252m [39m
+
+[38;5;246m# create a Linux VM using the UbuntuTLS image, with two attached storage disks of 10 GB and 20 GB[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m--ssh-key-value[39m[38;5;252m [39m[38;5;87m$HOME[39m[38;5;252m/.ssh/id_rsa.pub[39m[38;5;252m [39m[38;5;252m--image[39m[38;5;252m [39m[38;5;252mUbuntuLTS[39m[38;5;252m [39m[38;5;252m--data-disk-sizes-gb[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m [39m[38;5;67m20[39m
+
+[38;5;246m# list VMs[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
+
+[38;5;246m# list only VMs having distinct state[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m-d[39m[38;5;252m [39m[38;5;252m--query[39m[38;5;252m [39m[38;5;214m"[?powerState=='VM running']"[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
+
+[38;5;246m# delete VM (with the name MyLinuxVM in the group MyResourceGroup)[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mdelete[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m[38;5;252m [39m[38;5;252m--yes[39m
+
+[38;5;246m# Delete all VMs in a resource group[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mdelete[39m[38;5;252m [39m[38;5;252m--ids[39m[38;5;252m [39m[38;5;70;01m$([39;00m[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m--query[39m[38;5;252m [39m[38;5;214m"[].id"[39m[38;5;252m [39m[38;5;252m-o[39m[38;5;252m [39m[38;5;252mtsv[39m[38;5;70;01m)[39;00m
+
+[38;5;246m# Create an Image based on a running VM[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mdeallocate[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mgeneralize[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mimage[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;252mMyTestImage[39m[38;5;252m [39m[38;5;252m--source[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m
+
+[38;5;246m# Running VM based on a VHD[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252mblob[39m[38;5;252m [39m[38;5;252mupload[39m[38;5;252m [39m[38;5;252m--account-name[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214m${[39m[38;5;87maccount_name[39m[38;5;214m}[39m[38;5;214m"[39m[38;5;252m [39m[38;5;214m\[39m
+[38;5;252m [39m[38;5;252m--account-key[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214m${[39m[38;5;87maccount_key[39m[38;5;214m}[39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m--container-name[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214m${[39m[38;5;87mcontainer_name[39m[38;5;214m}[39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m--type[39m[38;5;252m [39m[38;5;252mpage[39m[38;5;252m [39m[38;5;214m\[39m
+[38;5;252m [39m[38;5;252m--file[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214m${[39m[38;5;87mfile[39m[38;5;214m}[39m[38;5;214m"[39m[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;214m"[39m[38;5;214m${[39m[38;5;87mvhd_name[39m[38;5;214m}[39m[38;5;214m"[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mdisk[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;214m\[39m
+[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;214m${[39m[38;5;87mresource_group[39m[38;5;214m}[39m[38;5;252m [39m[38;5;214m\[39m
+[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;252mmyManagedDisk[39m[38;5;252m [39m[38;5;214m\[39m
+[38;5;252m [39m[38;5;252m--source[39m[38;5;252m [39m[38;5;252mhttps://[39m[38;5;214m${[39m[38;5;87maccount_name[39m[38;5;214m}[39m[38;5;252m.blob.core.windows.net/[39m[38;5;214m${[39m[38;5;87mcontainer_name[39m[38;5;214m}[39m[38;5;252m/[39m[38;5;214m${[39m[38;5;87mvhd_name[39m[38;5;214m}[39m
+
+[38;5;246m# open port[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mopen-port[39m[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;252mMyResourceGroup[39m[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;252mMyLinuxVM[39m[38;5;252m [39m[38;5;252m--port[39m[38;5;252m [39m[38;5;67m443[39m[38;5;252m [39m[38;5;252m--priority[39m[38;5;252m [39m[38;5;67m899[39m
+
+[38;5;246m# Show storage accounts[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252maccount[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
+
+[38;5;246m# Show contaniers for an account[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252mcontainer[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--account-name[39m[38;5;252m [39m[38;5;252mmystorageaccount[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
+
+[38;5;246m# Show blobs in a container[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252mblob[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--account-name[39m[38;5;252m [39m[38;5;252mmystorageaccount[39m[38;5;252m [39m[38;5;252m--container-name[39m[38;5;252m [39m[38;5;252mmycontainer[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
+
+[38;5;246m# list account keys[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252maccount[39m[38;5;252m [39m[38;5;252mkeys[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--account-name[39m[38;5;252m [39m[38;5;252mSTORAGE_NAME[39m[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;252mRESOURCE_GROUP[39m
+
+[38;5;246m# Show own images[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mimage[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
+
+[38;5;246m# Configure default storage location[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mconfigure[39m[38;5;252m [39m[38;5;252m--defaults[39m[38;5;252m [39m[38;5;87mlocation[39m[38;5;252m=[39m[38;5;252meastus2[39m
+
+[38;5;246m# Show disks[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mdisk[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
+
+[38;5;246m# Copy blob[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mstorage[39m[38;5;252m [39m[38;5;252mblob[39m[38;5;252m [39m[38;5;252mcopy[39m[38;5;252m [39m[38;5;252mstart[39m[38;5;252m [39m[38;5;214m\[39m
+[38;5;252m [39m[38;5;252m--source-uri[39m[38;5;252m [39m[38;5;214m'https://md-ldh5nknx2rkz.blob.core.windows.net/jzwuuuzzapn0/abcd?sv=2017-04-17&sr=b&si=68041718-6828-4f5e-9e6e-a1b719975062&sig=XXX'[39m[38;5;252m [39m[38;5;214m\[39m
+[38;5;252m [39m[38;5;252m--account-key[39m[38;5;252m [39m[38;5;87mXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX[39m[38;5;252m=[39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m\[39m
+[38;5;252m [39m[38;5;252m--account-name[39m[38;5;252m [39m[38;5;252mdestaccount[39m[38;5;252m [39m[38;5;214m\[39m
+[38;5;252m [39m[38;5;252m--destination-container[39m[38;5;252m [39m[38;5;252mvms[39m[38;5;252m [39m[38;5;214m\[39m
+[38;5;252m [39m[38;5;252m--destination-blob[39m[38;5;252m [39m[38;5;252mDESTINATION-blob.vhd[39m
+
+[38;5;246m# List virtual networks[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mnetwork[39m[38;5;252m [39m[38;5;252mvnet[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
+
+[38;5;246m# List virtual networks adapters[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mnetwork[39m[38;5;252m [39m[38;5;252mnic[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
+
+[38;5;246m# List public IP addresses used by the VMs[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mlist-ip-addresses[39m[38;5;252m [39m[38;5;252m--output[39m[38;5;252m [39m[38;5;252mtable[39m
+
+[38;5;246m# create snapshot[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252msnapshot[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;252mIC-EXASOL-001[39m[38;5;252m [39m[38;5;252m--source[39m[38;5;252m [39m[38;5;252mvm1-disk1[39m[38;5;252m [39m[38;5;252m-n[39m[38;5;252m [39m[38;5;252mvm1-snap1[39m
+
+[38;5;246m# create SAS url for a snapshot[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252msnapshot[39m[38;5;252m [39m[38;5;252mgrant-access[39m[38;5;252m [39m[38;5;252m--resource-group[39m[38;5;252m [39m[38;5;252mIC-EXASOL-001[39m[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;252mvm1-snap1[39m[38;5;252m [39m[38;5;252m--duration-in-seconds[39m[38;5;252m [39m[38;5;67m36000[39m[38;5;252m [39m[38;5;252m--query[39m[38;5;252m [39m[38;5;214m'[accessSas]'[39m[38;5;252m [39m[38;5;252m-o[39m[38;5;252m [39m[38;5;252mtsv[39m
+
+[38;5;246m# attach disk[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mdisk[39m[38;5;252m [39m[38;5;252mattach[39m[38;5;252m [39m[38;5;252m--vm-name[39m[38;5;252m [39m[38;5;252mvm1[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mRESOURCE_GROUP[39m[38;5;252m [39m[38;5;252m--disk[39m[38;5;252m [39m[38;5;252mDISK1_ID[39m
+
+[38;5;246m# detach disk[39m
+[38;5;252maz[39m[38;5;252m [39m[38;5;252mvm[39m[38;5;252m [39m[38;5;252mdisk[39m[38;5;252m [39m[38;5;252mdetach[39m[38;5;252m [39m[38;5;252m--vm-name[39m[38;5;252m [39m[38;5;252mvm1[39m[38;5;252m [39m[38;5;252m-g[39m[38;5;252m [39m[38;5;252mRESOURCE_GROUP[39m[38;5;252m [39m[38;5;252m--name[39m[38;5;252m [39m[38;5;252mDISK1_ID[39m
diff --git a/tests/results/18 b/tests/results/18
new file mode 100644
index 0000000..2822759
--- /dev/null
+++ b/tests/results/18
@@ -0,0 +1,20 @@
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mabcdefgh[39m[38;5;214m'[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252mn[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mm[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mchar[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mchars[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214md[39m[38;5;214m'[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m'[39m[38;5;214mcd[39m[38;5;214m'[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;246m# starting from n=2 characters in and m=3 in length;[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m[[39m[38;5;252mn[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m:[39m[38;5;252mn[39m[38;5;252m+[39m[38;5;252mm[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m][39m
+[38;5;214m'[39m[38;5;214mbcd[39m[38;5;214m'[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;246m# starting from n characters in, up to the end of the string;[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m[[39m[38;5;252mn[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m:[39m[38;5;252m][39m
+[38;5;214m'[39m[38;5;214mbcdefgh[39m[38;5;214m'[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;246m# whole string minus last character;[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m[[39m[38;5;252m:[39m[38;5;252m-[39m[38;5;67m1[39m[38;5;252m][39m
+[38;5;214m'[39m[38;5;214mabcdefg[39m[38;5;214m'[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;246m# starting from a known character char="d" within the string and of m length;[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252mindx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m.[39m[38;5;252mindex[39m[38;5;252m([39m[38;5;252mchar[39m[38;5;252m)[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m[[39m[38;5;252mindx[39m[38;5;252m:[39m[38;5;252mindx[39m[38;5;252m+[39m[38;5;252mm[39m[38;5;252m][39m
+[38;5;214m'[39m[38;5;214mdef[39m[38;5;214m'[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;246m# starting from a known substring chars="cd" within the string and of m length.[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252mindx[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m.[39m[38;5;252mindex[39m[38;5;252m([39m[38;5;252mchars[39m[38;5;252m)[39m
+[38;5;252m>>[39m[38;5;252m>[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m[[39m[38;5;252mindx[39m[38;5;252m:[39m[38;5;252mindx[39m[38;5;252m+[39m[38;5;252mm[39m[38;5;252m][39m
+[38;5;214m'[39m[38;5;214mcde[39m[38;5;214m'[39m
+[38;5;252m>>[39m[38;5;252m>[39m
diff --git a/tests/results/19 b/tests/results/19
new file mode 100644
index 0000000..cf255ee
--- /dev/null
+++ b/tests/results/19
@@ -0,0 +1,20 @@
+>>> s = 'abcdefgh'
+>>> n, m, char, chars = 2, 3, 'd', 'cd'
+>>> # starting from n=2 characters in and m=3 in length;
+>>> s[n-1:n+m-1]
+'bcd'
+>>> # starting from n characters in, up to the end of the string;
+>>> s[n-1:]
+'bcdefgh'
+>>> # whole string minus last character;
+>>> s[:-1]
+'abcdefg'
+>>> # starting from a known character char="d" within the string and of m length;
+>>> indx = s.index(char)
+>>> s[indx:indx+m]
+'def'
+>>> # starting from a known substring chars="cd" within the string and of m length.
+>>> indx = s.index(chars)
+>>> s[indx:indx+m]
+'cde'
+>>>
diff --git a/tests/results/2 b/tests/results/2
index 41a116a..c7b10dc 100644
--- a/tests/results/2
+++ b/tests/results/2
@@ -1,17 +1,24 @@
-# Displays everything in the target directory
-ls path/to/the/target/directory
-
-# Displays everything including hidden files
-ls -a
-
-# Displays all files, along with the size (with unit suffixes) and timestamp
-ls -lh
-
-# Display files, sorted by size
-ls -S
-
-# Display directories only
-ls -d */
-
-# Display directories only, include hidden
-ls -d .*/ */
+[38;5;246m# [39m
+[38;5;246m# ls[39m
+[38;5;246m# [39m
+[38;5;246m# List directory contents.[39m
+[38;5;246m# [39m
+[38;5;246m# List files one per line:[39m
+[38;5;252m [39m[38;5;252mls[39m[38;5;252m [39m[38;5;252m-1[39m
+[38;5;246m# [39m
+[38;5;246m# List all files, including hidden files:[39m
+[38;5;252m [39m[38;5;252mls[39m[38;5;252m [39m[38;5;252m-a[39m
+[38;5;246m# [39m
+[38;5;246m# Long format list (permissions, ownership, size and modification date) of all files:[39m
+[38;5;252m [39m[38;5;252mls[39m[38;5;252m [39m[38;5;252m-la[39m
+[38;5;246m# [39m
+[38;5;246m# Long format list with size displayed using human readable units (KB, MB, GB):[39m
+[38;5;252m [39m[38;5;252mls[39m[38;5;252m [39m[38;5;252m-lh[39m
+[38;5;246m# [39m
+[38;5;246m# Long format list sorted by size (descending):[39m
+[38;5;252m [39m[38;5;252mls[39m[38;5;252m [39m[38;5;252m-lS[39m
+[38;5;246m# [39m
+[38;5;246m# Long format list of all files, sorted by modification date (oldest first):[39m
+[38;5;252m [39m[38;5;252mls[39m[38;5;252m [39m[38;5;252m-ltr[39m
+[38;5;246m# [39m
+[38;5;246m# [39m
diff --git a/tests/results/20 b/tests/results/20
new file mode 100644
index 0000000..c48b471
--- /dev/null
+++ b/tests/results/20
@@ -0,0 +1,721 @@
+00DESCRIPTION
+100-doors
+24-game
+24-game-Solve
+9-billion-names-of-God-the-integer
+99-Bottles-of-Beer
+A+B
+ABC-Problem
+AKS-test-for-primes
+Abstract-type
+Abundant,-deficient-and-perfect-number-classifications
+Accumulator-factory
+Ackermann-function
+Active-Directory-Connect
+Active-Directory-Search-for-a-user
+Active-object
+Add-a-variable-to-a-class-instance-at-runtime
+Address-of-a-variable
+Align-columns
+Aliquot-sequence-classifications
+Almost-prime
+Amb
+Amicable-pairs
+Anagrams
+Anagrams-Deranged-anagrams
+Animate-a-pendulum
+Animation
+Anonymous-recursion
+Append-a-record-to-the-end-of-a-text-file
+Apply-a-callback-to-an-array
+Arbitrary-precision-integers--included-
+Arithmetic-Complex
+Arithmetic-Integer
+Arithmetic-Rational
+Arithmetic-evaluation
+Arithmetic-geometric-mean
+Arithmetic-geometric-mean-Calculate-Pi
+Array-concatenation
+Arrays
+Assertions
+Associative-array-Creation
+Associative-array-Iteration
+Atomic-updates
+Average-loop-length
+Averages-Arithmetic-mean
+Averages-Mean-angle
+Averages-Mean-time-of-day
+Averages-Median
+Averages-Mode
+Averages-Pythagorean-means
+Averages-Root-mean-square
+Averages-Simple-moving-average
+Balanced-brackets
+Balanced-ternary
+Benfords-law
+Bernoulli-numbers
+Best-shuffle
+Binary-digits
+Binary-search
+Binary-strings
+Bitcoin-address-validation
+Bitcoin-public-point-to-address
+Bitmap-B-zier-curves-Cubic
+Bitmap-Bresenhams-line-algorithm
+Bitmap-Flood-fill
+Bitmap-Histogram
+Bitmap-Midpoint-circle-algorithm
+Bitmap-Read-a-PPM-file
+Bitmap-Write-a-PPM-file
+Bitwise-IO
+Bitwise-operations
+Boolean-values
+Box-the-compass
+Break-OO-privacy
+Brownian-tree
+Bulls-and-cows
+Bulls-and-cows-Player
+CRC-32
+CSV-data-manipulation
+CSV-to-HTML-translation
+Caesar-cipher
+Calendar
+Calendar---for-REAL-programmers
+Call-a-foreign-language-function
+Call-a-function
+Call-a-function-in-a-shared-library
+Call-an-object-method
+Canny-edge-detector
+Carmichael-3-strong-pseudoprimes
+Case-sensitivity-of-identifiers
+Casting-out-nines
+Catalan-numbers
+Catalan-numbers-Pascals-triangle
+Catamorphism
+Character-codes
+Chat-server
+Check-Machin-like-formulas
+Check-that-file-exists
+Chinese-remainder-theorem
+Cholesky-decomposition
+Circles-of-given-radius-through-two-points
+Classes
+Closest-pair-problem
+Closures-Value-capture
+Collections
+Color-of-a-screen-pixel
+Color-quantization
+Colour-bars-Display
+Colour-pinstripe-Display
+Combinations
+Combinations-and-permutations
+Combinations-with-repetitions
+Comma-quibbling
+Command-line-arguments
+Comments
+Compare-sorting-algorithms-performance
+Compound-data-type
+Concurrent-computing
+Conditional-structures
+Conjugate-transpose
+Constrained-random-points-on-a-circle
+Continued-fraction
+Continued-fraction-Arithmetic-Construct-from-rational-number
+Continued-fraction-Arithmetic-G-matrix-NG,-Contined-Fraction-N-
+Convert-decimal-number-to-rational
+Conways-Game-of-Life
+Copy-a-string
+Count-in-factors
+Count-in-octal
+Count-occurrences-of-a-substring
+Count-the-coins
+Create-a-file
+Create-a-file-on-magnetic-tape
+Create-a-two-dimensional-array-at-runtime
+Create-an-HTML-table
+Currying
+Cut-a-rectangle
+Date-format
+Date-manipulation
+Day-of-the-week
+Deal-cards-for-FreeCell
+Death-Star
+Deconvolution-1D
+Deepcopy
+Define-a-primitive-data-type
+Delegates
+Delete-a-file
+Detect-division-by-zero
+Determine-if-a-string-is-numeric
+Determine-if-only-one-instance-is-running
+Digital-root
+Digital-root-Multiplicative-digital-root
+Dinesmans-multiple-dwelling-problem
+Dining-philosophers
+Discordian-date
+Distributed-programming
+Documentation
+Dot-product
+Doubly-linked-list-Element-definition
+Doubly-linked-list-Element-insertion
+Doubly-linked-list-Traversal
+Dragon-curve
+Draw-a-clock
+Draw-a-cuboid
+Draw-a-sphere
+Dutch-national-flag-problem
+Dynamic-variable-names
+Echo-server
+Element-wise-operations
+Empty-directory
+Empty-string
+Enforced-immutability
+Entropy
+Enumerations
+Environment-variables
+Equilibrium-index
+Ethiopian-multiplication
+Euler-method
+Evaluate-binomial-coefficients
+Even-or-odd
+Events
+Evolutionary-algorithm
+Exceptions
+Exceptions-Catch-an-exception-thrown-in-a-nested-call
+Executable-library
+Execute-SNUSP
+Execute-a-Markov-algorithm
+Execute-a-system-command
+Exponentiation-operator
+Extensible-prime-generator
+Extreme-floating-point-values
+Factorial
+Factors-of-a-Mersenne-number
+Factors-of-an-integer
+Fast-Fourier-transform
+Fibonacci-n-step-number-sequences
+Fibonacci-sequence
+Fibonacci-word
+Fibonacci-word-fractal
+File-input-output
+File-modification-time
+File-size
+Filter
+Find-common-directory-path
+Find-largest-left-truncatable-prime-in-a-given-base
+Find-limit-of-recursion
+Find-the-last-Sunday-of-each-month
+Find-the-missing-permutation
+First-class-environments
+First-class-functions
+First-class-functions-Use-numbers-analogously
+Five-weekends
+FizzBuzz
+Flatten-a-list
+Flipping-bits-game
+Flow-control-structures
+Floyds-triangle
+Forest-fire
+Fork
+Formal-power-series
+Formatted-numeric-output
+Forward-difference
+Four-bit-adder
+Fractal-tree
+Fractran
+Function-composition
+Function-definition
+Function-frequency
+GUI-Maximum-window-dimensions
+GUI-component-interaction
+GUI-enabling-disabling-of-controls
+Galton-box-animation
+Gamma-function
+Gaussian-elimination
+Generate-Chess960-starting-position
+Generate-lower-case-ASCII-alphabet
+Generator-Exponential
+Generic-swap
+Globally-replace-text-in-several-files
+Gray-code
+Grayscale-image
+Greatest-common-divisor
+Greatest-element-of-a-list
+Greatest-subsequential-sum
+Greyscale-bars-Display
+Guess-the-number
+Guess-the-number-With-feedback
+Guess-the-number-With-feedback--player-
+HTTP
+HTTPS
+HTTPS-Authenticated
+HTTPS-Client-authenticated
+Hailstone-sequence
+Hamming-numbers
+Handle-a-signal
+Happy-numbers
+Harshad-or-Niven-series
+Hash-from-two-arrays
+Hash-join
+Haversine-formula
+Hello-world-Graphical
+Hello-world-Line-printer
+Hello-world-Newbie
+Hello-world-Newline-omission
+Hello-world-Standard-error
+Hello-world-Text
+Hello-world-Web-server
+Here-document
+Heronian-triangles
+Hickerson-series-of-almost-integers
+Higher-order-functions
+History-variables
+Hofstadter-Conway-$10,000-sequence
+Hofstadter-Figure-Figure-sequences
+Hofstadter-Q-sequence
+Holidays-related-to-Easter
+Horizontal-sundial-calculations
+Horners-rule-for-polynomial-evaluation
+Host-introspection
+Hostname
+Hough-transform
+Huffman-coding
+I-before-E-except-after-C
+IBAN
+Identity-matrix
+Image-convolution
+Image-noise
+Include-a-file
+Increment-a-numerical-string
+Infinity
+Inheritance-Multiple
+Inheritance-Single
+Input-loop
+Integer-comparison
+Integer-overflow
+Integer-sequence
+Interactive-programming
+Introspection
+Inverted-index
+Inverted-syntax
+Iterated-digits-squaring
+JSON
+Jensens-Device
+Josephus-problem
+Joystick-position
+Jump-anywhere
+K-d-tree
+K-means++-clustering
+Kaprekar-numbers
+Keyboard-input-Flush-the-keyboard-buffer
+Keyboard-input-Keypress-check
+Keyboard-input-Obtain-a-Y-or-N-response
+Keyboard-macros
+Knapsack-problem-0-1
+Knapsack-problem-Bounded
+Knapsack-problem-Continuous
+Knights-tour
+Knuth-shuffle
+Knuths-algorithm-S
+LU-decomposition
+LZW-compression
+Langtons-ant
+Largest-int-from-concatenated-ints
+Last-Friday-of-each-month
+Last-letter-first-letter
+Leap-year
+Least-common-multiple
+Left-factorials
+Letter-frequency
+Levenshtein-distance
+Linear-congruential-generator
+List-comprehensions
+Literals-Floating-point
+Literals-Integer
+Literals-String
+Logical-operations
+Long-multiplication
+Longest-common-subsequence
+Longest-increasing-subsequence
+Longest-string-challenge
+Look-and-say-sequence
+Loop-over-multiple-arrays-simultaneously
+Loops-Break
+Loops-Continue
+Loops-Do-while
+Loops-Downward-for
+Loops-For
+Loops-For-with-a-specified-step
+Loops-Foreach
+Loops-Infinite
+Loops-N-plus-one-half
+Loops-Nested
+Loops-While
+Lucas-Lehmer-test
+Ludic-numbers
+Luhn-test-of-credit-card-numbers
+MD4
+MD5
+MD5-Implementation
+Machine-code
+Mad-Libs
+Magic-squares-of-odd-order
+Main-step-of-GOST-28147-89
+Make-directory-path
+Man-or-boy-test
+Mandelbrot-set
+Map-range
+Matrix-arithmetic
+Matrix-exponentiation-operator
+Matrix-multiplication
+Matrix-transposition
+Maximum-triangle-path-sum
+Maze-generation
+Maze-solving
+Median-filter
+Memory-allocation
+Memory-layout-of-a-data-structure
+Menu
+Metaprogramming
+Metered-concurrency
+Metronome
+Middle-three-digits
+Minesweeper-game
+Modular-exponentiation
+Modular-inverse
+Monte-Carlo-methods
+Monty-Hall-problem
+Morse-code
+Mouse-position
+Move-to-front-algorithm
+Multifactorial
+Multiple-distinct-objects
+Multiple-regression
+Multiplication-tables
+Multiplicative-order
+Multisplit
+Munching-squares
+Mutual-recursion
+N-queens-problem
+Named-parameters
+Narcissist
+Narcissistic-decimal-number
+Natural-sorting
+Nautical-bell
+Non-continuous-subsequences
+Non-decimal-radices-Convert
+Non-decimal-radices-Input
+Non-decimal-radices-Output
+Nth
+Nth-root
+Null-object
+Number-reversal-game
+Numeric-error-propagation
+Numerical-integration
+Numerical-integration-Gauss-Legendre-Quadrature
+OLE-Automation
+Object-serialization
+Odd-word-problem
+Old-lady-swallowed-a-fly
+One-dimensional-cellular-automata
+One-of-n-lines-in-a-file
+OpenGL
+Optional-parameters
+Order-disjoint-list-items
+Order-two-numerical-lists
+Ordered-Partitions
+Ordered-words
+Palindrome-detection
+Pangram-checker
+Paraffins
+Parallel-calculations
+Parametrized-SQL-statement
+Parse-an-IP-Address
+Parsing-RPN-calculator-algorithm
+Parsing-RPN-to-infix-conversion
+Parsing-Shunting-yard-algorithm
+Partial-function-application
+Pascals-triangle
+Pascals-triangle-Puzzle
+Penneys-game
+Percentage-difference-between-images
+Percolation-Bond-percolation
+Percolation-Mean-cluster-density
+Percolation-Mean-run-density
+Percolation-Site-percolation
+Perfect-numbers
+Permutation-test
+Permutations
+Permutations-Derangements
+Permutations-Rank-of-a-permutation
+Permutations-by-swapping
+Pernicious-numbers
+Phrase-reversals
+Pi
+Pick-random-element
+Pig-the-dice-game
+Pig-the-dice-game-Player
+Pinstripe-Display
+Play-recorded-sounds
+Playing-cards
+Plot-coordinate-pairs
+Pointers-and-references
+Polymorphic-copy
+Polymorphism
+Polynomial-long-division
+Polynomial-regression
+Power-set
+Pragmatic-directives
+Price-fraction
+Primality-by-trial-division
+Prime-decomposition
+Priority-queue
+Probabilistic-choice
+Problem-of-Apollonius
+Program-name
+Program-termination
+Pythagorean-triples
+QR-decomposition
+Quaternion-type
+Queue-Definition
+Queue-Usage
+Quickselect-algorithm
+Quine
+README
+RIPEMD-160
+RSA-code
+Random-number-generator--device-
+Random-numbers
+Range-expansion
+Range-extraction
+Ranking-methods
+Rate-counter
+Ray-casting-algorithm
+Read-a-configuration-file
+Read-a-file-line-by-line
+Read-a-specific-line-from-a-file
+Read-entire-file
+Real-constants-and-functions
+Record-sound
+Reduced-row-echelon-form
+Regular-expressions
+Remove-duplicate-elements
+Remove-lines-from-a-file
+Rename-a-file
+Rep-string
+Repeat-a-string
+Resistor-mesh
+Respond-to-an-unknown-method-call
+Return-multiple-values
+Reverse-a-string
+Reverse-words-in-a-string
+Rock-paper-scissors
+Roman-numerals-Decode
+Roman-numerals-Encode
+Roots-of-a-function
+Roots-of-a-quadratic-function
+Roots-of-unity
+Rot-13
+Run-length-encoding
+Runge-Kutta-method
+Runtime-evaluation
+Runtime-evaluation-In-an-environment
+S-Expressions
+SEDOLs
+SHA-1
+SHA-256
+SOAP
+SQL-based-authentication
+Safe-addition
+Same-Fringe
+Scope-modifiers
+Search-a-list
+Secure-temporary-file
+Self-describing-numbers
+Self-referential-sequence
+Semiprime
+Semordnilap
+Send-an-unknown-method-call
+Send-email
+Sequence-of-non-squares
+Sequence-of-primes-by-Trial-Division
+Set
+Set-consolidation
+Set-of-real-numbers
+Set-puzzle
+Seven-sided-dice-from-five-sided-dice
+Shell-one-liner
+Short-circuit-evaluation
+Show-the-epoch
+Sierpinski-carpet
+Sierpinski-triangle
+Sierpinski-triangle-Graphical
+Sieve-of-Eratosthenes
+Simple-database
+Simple-windowed-application
+Simulate-input-Keyboard
+Simulate-input-Mouse
+Singleton
+Singly-linked-list-Element-definition
+Singly-linked-list-Element-insertion
+Singly-linked-list-Traversal
+Sleep
+Sockets
+Sokoban
+Solve-a-Hidato-puzzle
+Solve-a-Holy-Knights-tour
+Solve-a-Hopido-puzzle
+Solve-a-Numbrix-puzzle
+Solve-the-no-connection-puzzle
+Sort-an-array-of-composite-structures
+Sort-an-integer-array
+Sort-disjoint-sublist
+Sort-using-a-custom-comparator
+Sorting-algorithms-Bead-sort
+Sorting-algorithms-Bogosort
+Sorting-algorithms-Bubble-sort
+Sorting-algorithms-Cocktail-sort
+Sorting-algorithms-Comb-sort
+Sorting-algorithms-Counting-sort
+Sorting-algorithms-Gnome-sort
+Sorting-algorithms-Heapsort
+Sorting-algorithms-Insertion-sort
+Sorting-algorithms-Merge-sort
+Sorting-algorithms-Pancake-sort
+Sorting-algorithms-Permutation-sort
+Sorting-algorithms-Quicksort
+Sorting-algorithms-Radix-sort
+Sorting-algorithms-Selection-sort
+Sorting-algorithms-Shell-sort
+Sorting-algorithms-Sleep-sort
+Sorting-algorithms-Stooge-sort
+Sorting-algorithms-Strand-sort
+Soundex
+Sparkline-in-unicode
+Special-variables
+Speech-synthesis
+Spiral-matrix
+Stable-marriage-problem
+Stack
+Stack-traces
+Stair-climbing-puzzle
+State-name-puzzle
+Statistics-Basic
+Stem-and-leaf-plot
+Stern-Brocot-sequence
+String-append
+String-case
+String-comparison
+String-concatenation
+String-interpolation--included-
+String-length
+String-matching
+String-prepend
+Strip-a-set-of-characters-from-a-string
+Strip-block-comments
+Strip-comments-from-a-string
+Strip-control-codes-and-extended-characters-from-a-string
+Strip-whitespace-from-a-string-Top-and-tail
+Substring
+Substring-Top-and-tail
+Subtractive-generator
+Sudoku
+Sum-and-product-of-an-array
+Sum-digits-of-an-integer
+Sum-multiples-of-3-and-5
+Sum-of-a-series
+Sum-of-squares
+Sutherland-Hodgman-polygon-clipping
+Symmetric-difference
+Synchronous-concurrency
+System-time
+Table-creation-Postal-addresses
+Take-notes-on-the-command-line
+Temperature-conversion
+Terminal-control-Clear-the-screen
+Terminal-control-Coloured-text
+Terminal-control-Cursor-movement
+Terminal-control-Cursor-positioning
+Terminal-control-Dimensions
+Terminal-control-Display-an-extended-character
+Terminal-control-Hiding-the-cursor
+Terminal-control-Inverse-video
+Terminal-control-Positional-read
+Terminal-control-Preserve-screen
+Terminal-control-Ringing-the-terminal-bell
+Terminal-control-Unicode-output
+Ternary-logic
+Test-a-function
+Text-processing-1
+Text-processing-2
+Text-processing-Max-licenses-in-use
+Textonyms
+The-ISAAC-Cipher
+The-Twelve-Days-of-Christmas
+Thieles-interpolation-formula
+Tic-tac-toe
+Time-a-function
+Tokenize-a-string
+Top-rank-per-group
+Topic-variable
+Topological-sort
+Topswops
+Total-circles-area
+Towers-of-Hanoi
+Trabb-Pardo-Knuth-algorithm
+Tree-traversal
+Trigonometric-functions
+Truncate-a-file
+Twelve-statements
+URL-decoding
+URL-encoding
+Ulam-spiral--for-primes-
+Unbias-a-random-generator
+Undefined-values
+Unicode-strings
+Unicode-variable-names
+Universal-Turing-machine
+Unix-ls
+Update-a-configuration-file
+Use-another-language-to-call-a-function
+User-input-Graphical
+User-input-Text
+Vampire-number
+Van-der-Corput-sequence
+Variable-length-quantity
+Variable-size-Get
+Variables
+Variadic-function
+Vector-products
+Verify-distribution-uniformity-Chi-squared-test
+Verify-distribution-uniformity-Naive
+Vigen-re-cipher
+Vigen-re-cipher-Cryptanalysis
+Visualize-a-tree
+Vogels-approximation-method
+Voronoi-diagram
+Walk-a-directory-Non-recursively
+Walk-a-directory-Recursively
+Web-scraping
+Window-creation
+Window-creation-X11
+Window-management
+Wireworld
+Word-wrap
+World-Cup-group-stage
+Write-float-arrays-to-a-text-file
+Write-language-name-in-3D-ASCII
+Write-to-Windows-event-log
+XML-DOM-serialization
+XML-Input
+XML-Output
+XML-XPath
+Xiaolin-Wus-line-algorithm
+Y-combinator
+Yahoo--search-interface
+Yin-and-yang
+Zebra-puzzle
+Zeckendorf-number-representation
+Zero-to-the-zero-power
+Zhang-Suen-thinning-algorithm
+Zig-zag-matrix
diff --git a/tests/results/21 b/tests/results/21
new file mode 100644
index 0000000..854cab3
--- /dev/null
+++ b/tests/results/21
@@ -0,0 +1,563 @@
+[38;5;246m// Single-line comments start with two slashes.[39m
+[38;5;246m/* Multiline comments start with slash-star,[39m
+[38;5;246m and end with star-slash */[39m
+
+[38;5;246m// Statements can be terminated by ;[39m
+[38;5;252mdoStuff[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+
+[38;5;246m// ... but they don't have to be, as semicolons are automatically inserted[39m
+[38;5;246m// wherever there's a newline, except in certain cases.[39m
+[38;5;252mdoStuff[39m[38;5;252m([39m[38;5;252m)[39m
+
+[38;5;246m// Because those cases can cause unexpected results, we'll keep on using[39m
+[38;5;246m// semicolons in this guide.[39m
+
+[38;5;246m///////////////////////////////////[39m
+[38;5;246m// 1. Numbers, Strings and Operators[39m
+
+[38;5;246m// JavaScript has one number type (which is a 64-bit IEEE 754 double).[39m
+[38;5;246m// Doubles have a 52-bit mantissa, which is enough to store integers[39m
+[38;5;246m// up to about 9✕10¹⁵ precisely.[39m
+[38;5;67m3[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 3[39m
+[38;5;67m1.5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 1.5[39m
+
+[38;5;246m// Some basic arithmetic works as you'd expect.[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 2[39m
+[38;5;67m0.1[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m0.2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 0.30000000000000004[39m
+[38;5;67m8[39m[38;5;252m [39m[38;5;252m-[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 7[39m
+[38;5;67m10[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 20[39m
+[38;5;67m35[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 7[39m
+
+[38;5;246m// Including uneven division.[39m
+[38;5;67m5[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 2.5[39m
+
+[38;5;246m// And modulo division.[39m
+[38;5;67m10[39m[38;5;252m [39m[38;5;252m%[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 0[39m
+[38;5;67m30[39m[38;5;252m [39m[38;5;252m%[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 2[39m
+[38;5;67m18.5[39m[38;5;252m [39m[38;5;252m%[39m[38;5;252m [39m[38;5;67m7[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 4.5[39m
+
+[38;5;246m// Bitwise operations also work; when you perform a bitwise operation your float[39m
+[38;5;246m// is converted to a signed int *up to* 32 bits.[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m<<[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 4[39m
+
+[38;5;246m// Precedence is enforced with parentheses.[39m
+[38;5;252m([39m[38;5;67m1[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 8[39m
+
+[38;5;246m// There are three special not-a-real-number values:[39m
+[38;5;70;01mInfinity[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// result of e.g. 1/0[39m
+[38;5;252m-[39m[38;5;70;01mInfinity[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// result of e.g. -1/0[39m
+[38;5;70;01mNaN[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// result of e.g. 0/0, stands for 'Not a Number'[39m
+
+[38;5;246m// There's also a boolean type.[39m
+[38;5;70;01mtrue[39;00m[38;5;252m;[39m
+[38;5;70;01mfalse[39;00m[38;5;252m;[39m
+
+[38;5;246m// Strings are created with ' or ".[39m
+[38;5;214m'abc'[39m[38;5;252m;[39m
+[38;5;214m"Hello, world"[39m[38;5;252m;[39m
+
+[38;5;246m// Negation uses the ! symbol[39m
+[38;5;252m![39m[38;5;70;01mtrue[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+[38;5;252m![39m[38;5;70;01mfalse[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// Equality is ===[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+
+[38;5;246m// Inequality is !==[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m!==[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m!==[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// More comparisons[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m<=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m>=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// Strings are concatenated with +[39m
+[38;5;214m"Hello "[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;214m"world!"[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello world!"[39m
+
+[38;5;246m// ... which works with more than just strings[39m
+[38;5;214m"1, 2, "[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "1, 2, 3"[39m
+[38;5;214m"Hello "[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252m[[39m[38;5;214m"world"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"!"[39m[38;5;252m][39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello world,!"[39m
+
+[38;5;246m// and are compared with < and >[39m
+[38;5;214m"a"[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;214m"b"[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// Type coercion is performed for comparisons with double equals...[39m
+[38;5;214m"5"[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+[38;5;70;01mnull[39;00m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;70;01mundefined[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// ...unless you use ===[39m
+[38;5;214m"5"[39m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+[38;5;70;01mnull[39;00m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;70;01mundefined[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+
+[38;5;246m// ...which can result in some weird behaviour...[39m
+[38;5;67m13[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252m![39m[38;5;67m0[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// 14[39m
+[38;5;214m"13"[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252m![39m[38;5;67m0[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// '13true'[39m
+
+[38;5;246m// You can access characters in a string with `charAt`[39m
+[38;5;214m"This is a string"[39m[38;5;252m.[39m[38;5;252mcharAt[39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 'T'[39m
+
+[38;5;246m// ...or use `substring` to get larger pieces.[39m
+[38;5;214m"Hello world"[39m[38;5;252m.[39m[38;5;252msubstring[39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello"[39m
+
+[38;5;246m// `length` is a property, so don't use ().[39m
+[38;5;214m"Hello"[39m[38;5;252m.[39m[38;5;252mlength[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 5[39m
+
+[38;5;246m// There's also `null` and `undefined`.[39m
+[38;5;70;01mnull[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// used to indicate a deliberate non-value[39m
+[38;5;70;01mundefined[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// used to indicate a value is not currently present (although[39m
+[38;5;252m [39m[38;5;246m// `undefined` is actually a value itself)[39m
+
+[38;5;246m// false, null, undefined, NaN, 0 and "" are falsy; everything else is truthy.[39m
+[38;5;246m// Note that 0 is falsy and "0" is truthy, even though 0 == "0".[39m
+
+[38;5;246m///////////////////////////////////[39m
+[38;5;246m// 2. Variables, Arrays and Objects[39m
+
+[38;5;246m// Variables are declared with the `var` keyword. JavaScript is dynamically[39m
+[38;5;246m// typed, so you don't need to specify type. Assignment uses a single `=`[39m
+[38;5;246m// character.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252msomeVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m
+
+[38;5;246m// If you leave the var keyword off, you won't get an error...[39m
+[38;5;252msomeOtherVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m
+
+[38;5;246m// ...but your variable will be created in the global scope, not in the scope[39m
+[38;5;246m// you defined it in.[39m
+
+[38;5;246m// Variables declared without being assigned to are set to undefined.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252msomeThirdVar[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = undefined[39m
+
+[38;5;246m// If you want to declare a couple of variables, then you could use a comma[39m
+[38;5;246m// separator[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252msomeFourthVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252msomeFifthVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m;[39m
+
+[38;5;246m// There's shorthand for performing math operations on variables:[39m
+[38;5;252msomeVar[39m[38;5;252m [39m[38;5;252m+=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// equivalent to someVar = someVar + 5; someVar is 10 now[39m
+[38;5;252msomeVar[39m[38;5;252m [39m[38;5;252m*=[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// now someVar is 100[39m
+
+[38;5;246m// and an even-shorter-hand for adding or subtracting 1[39m
+[38;5;252msomeVar[39m[38;5;252m++[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// now someVar is 101[39m
+[38;5;252msomeVar[39m[38;5;252m--[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// back to 100[39m
+
+[38;5;246m// Arrays are ordered lists of values, of any type.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyArray[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;214m"Hello"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m45[39m[38;5;252m,[39m[38;5;252m [39m[38;5;70;01mtrue[39;00m[38;5;252m][39m[38;5;252m;[39m
+
+[38;5;246m// Their members can be accessed using the square-brackets subscript syntax.[39m
+[38;5;246m// Array indices start at zero.[39m
+[38;5;252mmyArray[39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m][39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 45[39m
+
+[38;5;246m// Arrays are mutable and of variable length.[39m
+[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252mpush[39m[38;5;252m([39m[38;5;214m"World"[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252mlength[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 4[39m
+
+[38;5;246m// Add/Modify at specific index[39m
+[38;5;252mmyArray[39m[38;5;252m[[39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"Hello"[39m[38;5;252m;[39m
+
+[38;5;246m// Add and remove element from front or back end of an array[39m
+[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252munshift[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// Add as the first element[39m
+[38;5;252msomeVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252mshift[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// Remove first element and return it[39m
+[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252mpush[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// Add as the last element[39m
+[38;5;252msomeVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252mpop[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// Remove last element and return it[39m
+
+[38;5;246m// Join all elements of an array with semicolon[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyArray0[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m32[39m[38;5;252m,[39m[38;5;70;01mfalse[39;00m[38;5;252m,[39m[38;5;214m"js"[39m[38;5;252m,[39m[38;5;67m12[39m[38;5;252m,[39m[38;5;67m56[39m[38;5;252m,[39m[38;5;67m90[39m[38;5;252m][39m[38;5;252m;[39m
+[38;5;252mmyArray0[39m[38;5;252m.[39m[38;5;252mjoin[39m[38;5;252m([39m[38;5;214m";"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m// = "32;false;js;12;56;90"[39m
+
+[38;5;246m// Get subarray of elements from index 1 (include) to 4 (exclude)[39m
+[38;5;252mmyArray0[39m[38;5;252m.[39m[38;5;252mslice[39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = [false,"js",12][39m
+
+[38;5;246m// Remove 4 elements starting from index 2, and insert there strings[39m
+[38;5;246m// "hi","wr" and "ld"; return removed subarray[39m
+[38;5;252mmyArray0[39m[38;5;252m.[39m[38;5;252msplice[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m,[39m[38;5;67m4[39m[38;5;252m,[39m[38;5;214m"hi"[39m[38;5;252m,[39m[38;5;214m"wr"[39m[38;5;252m,[39m[38;5;214m"ld"[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = ["js",12,56,90][39m
+[38;5;246m// myArray0 === [32,false,"hi","wr","ld"][39m
+
+[38;5;246m// JavaScript's objects are equivalent to "dictionaries" or "maps" in other[39m
+[38;5;246m// languages: an unordered collection of key-value pairs.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;252mkey1[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m"Hello"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mkey2[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m"World"[39m[38;5;252m}[39m[38;5;252m;[39m
+
+[38;5;246m// Keys are strings, but quotes aren't required if they're a valid[39m
+[38;5;246m// JavaScript identifier. Values can be any type.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;252mmyKey[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m"myValue"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"my other key"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m}[39m[38;5;252m;[39m
+
+[38;5;246m// Object attributes can also be accessed using the subscript syntax,[39m
+[38;5;252mmyObj[39m[38;5;252m[[39m[38;5;214m"my other key"[39m[38;5;252m][39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 4[39m
+
+[38;5;246m// ... or using the dot syntax, provided the key is a valid identifier.[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyKey[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "myValue"[39m
+
+[38;5;246m// Objects are mutable; values can be changed and new keys added.[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyThirdKey[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mtrue[39;00m[38;5;252m;[39m
+
+[38;5;246m// If you try to access a value that's not yet set, you'll get undefined.[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyFourthKey[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = undefined[39m
+
+[38;5;246m///////////////////////////////////[39m
+[38;5;246m// 3. Logic and Control Structures[39m
+
+[38;5;246m// The `if` structure works as you'd expect.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mcount[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mcount[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// evaluated if count is 3[39m
+[38;5;252m}[39m[38;5;252m [39m[38;5;70;01melse[39;00m[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mcount[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// evaluated if count is 4[39m
+[38;5;252m}[39m[38;5;252m [39m[38;5;70;01melse[39;00m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// evaluated if it's not either 3 or 4[39m
+[38;5;252m}[39m
+
+[38;5;246m// As does `while`.[39m
+[38;5;70;01mwhile[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mtrue[39;00m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// An infinite loop![39m
+[38;5;252m}[39m
+
+[38;5;246m// Do-while loops are like while loops, except they always run at least once.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252minput[39m[38;5;252m;[39m
+[38;5;70;01mdo[39;00m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252minput[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mgetInput[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m [39m[38;5;70;01mwhile[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252m![39m[38;5;252misValid[39m[38;5;252m([39m[38;5;252minput[39m[38;5;252m)[39m[38;5;252m)[39m[38;5;252m;[39m
+
+[38;5;246m// The `for` loop is the same as C and Java:[39m
+[38;5;246m// initialization; continue condition; iteration.[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m0[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mi[39m[38;5;252m++[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// will run 5 times[39m
+[38;5;252m}[39m
+
+[38;5;246m// Breaking out of labeled loops is similar to Java[39m
+[38;5;252mouter[39m[38;5;252m:[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m0[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mi[39m[38;5;252m++[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m0[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mj[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mj[39m[38;5;252m++[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m [39m[38;5;252m&&[39m[38;5;252m [39m[38;5;252mj[39m[38;5;252m [39m[38;5;252m==[39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m[38;5;252m [39m[38;5;252mouter[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;246m// breaks out of outer loop instead of only the inner one[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m
+
+[38;5;246m// The for/in statement allows iteration over properties of an object.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mdescription[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m""[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mperson[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;252mfname[39m[38;5;252m:[39m[38;5;214m"Paul"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mlname[39m[38;5;252m:[39m[38;5;214m"Ken"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mage[39m[38;5;252m:[39m[38;5;67m18[39m[38;5;252m}[39m[38;5;252m;[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mperson[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mdescription[39m[38;5;252m [39m[38;5;252m+=[39m[38;5;252m [39m[38;5;252mperson[39m[38;5;252m[[39m[38;5;252mx[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;214m" "[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m [39m[38;5;246m// description = 'Paul Ken 18 '[39m
+
+[38;5;246m// The for/of statement allows iteration over iterable objects (including the built-in String, [39m
+[38;5;246m// Array, e.g. the Array-like arguments or NodeList objects, TypedArray, Map and Set, [39m
+[38;5;246m// and user-defined iterables).[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyPets[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m""[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mpets[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;214m"cat"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"dog"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"hamster"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"hedgehog"[39m[38;5;252m][39m[38;5;252m;[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mpet[39m[38;5;252m [39m[38;5;70;01mof[39;00m[38;5;252m [39m[38;5;252mpets[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyPets[39m[38;5;252m [39m[38;5;252m+=[39m[38;5;252m [39m[38;5;252mpet[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;214m" "[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m [39m[38;5;246m// myPets = 'cat dog hamster hedgehog '[39m
+
+[38;5;246m// && is logical and, || is logical or[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mhouse[39m[38;5;252m.[39m[38;5;252msize[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;214m"big"[39m[38;5;252m [39m[38;5;252m&&[39m[38;5;252m [39m[38;5;252mhouse[39m[38;5;252m.[39m[38;5;252mcolour[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;214m"blue"[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mhouse[39m[38;5;252m.[39m[38;5;252mcontains[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"bear"[39m[38;5;252m;[39m
+[38;5;252m}[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mcolour[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;214m"red"[39m[38;5;252m [39m[38;5;252m||[39m[38;5;252m [39m[38;5;252mcolour[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;214m"blue"[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// colour is either red or blue[39m
+[38;5;252m}[39m
+
+[38;5;246m// && and || "short circuit", which is useful for setting default values.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mname[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252motherName[39m[38;5;252m [39m[38;5;252m||[39m[38;5;252m [39m[38;5;214m"default"[39m[38;5;252m;[39m
+
+[38;5;246m// The `switch` statement checks for equality with `===`.[39m
+[38;5;246m// Use 'break' after each case[39m
+[38;5;246m// or the cases after the correct one will be executed too.[39m
+[38;5;252mgrade[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m'B'[39m[38;5;252m;[39m
+[38;5;70;01mswitch[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mgrade[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mcase[39;00m[38;5;252m [39m[38;5;214m'A'[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;214m"Great job"[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mcase[39;00m[38;5;252m [39m[38;5;214m'B'[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;214m"OK job"[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mcase[39;00m[38;5;252m [39m[38;5;214m'C'[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;214m"You can do better"[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mdefault[39;00m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;214m"Oy vey"[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m[38;5;252m;[39m
+[38;5;252m}[39m
+
+
+[38;5;246m///////////////////////////////////[39m
+[38;5;246m// 4. Functions, Scope and Closures[39m
+
+[38;5;246m// JavaScript functions are declared with the `function` keyword.[39m
+[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;252mthing[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mthing[39m[38;5;252m.[39m[38;5;252mtoUpperCase[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m}[39m
+[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;214m"foo"[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "FOO"[39m
+
+[38;5;246m// Note that the value to be returned must start on the same line as the[39m
+[38;5;246m// `return` keyword, otherwise you'll always return `undefined` due to[39m
+[38;5;246m// automatic semicolon insertion. Watch out for this when using Allman style.[39m
+[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;246m// <- semicolon automatically inserted here[39m
+[38;5;252m [39m[38;5;252m{[39m[38;5;252mthisIsAn[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m'object literal'[39m[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252m}[39m
+[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = undefined[39m
+
+[38;5;246m// JavaScript functions are first class objects, so they can be reassigned to[39m
+[38;5;246m// different variable names and passed to other functions as arguments - for[39m
+[38;5;246m// example, when supplying an event handler:[39m
+[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// this code will be called in 5 seconds' time[39m
+[38;5;252m}[39m
+[38;5;252msetTimeout[39m[38;5;252m([39m[38;5;252mmyFunction[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5000[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;246m// Note: setTimeout isn't part of the JS language, but is provided by browsers[39m
+[38;5;246m// and Node.js.[39m
+
+[38;5;246m// Another function provided by browsers is setInterval[39m
+[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// this code will be called every 5 seconds[39m
+[38;5;252m}[39m
+[38;5;252msetInterval[39m[38;5;252m([39m[38;5;252mmyFunction[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5000[39m[38;5;252m)[39m[38;5;252m;[39m
+
+[38;5;246m// Function objects don't even have to be declared with a name - you can write[39m
+[38;5;246m// an anonymous function definition directly into the arguments of another.[39m
+[38;5;252msetTimeout[39m[38;5;252m([39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// this code will be called in 5 seconds' time[39m
+[38;5;252m}[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5000[39m[38;5;252m)[39m[38;5;252m;[39m
+
+[38;5;246m// JavaScript has function scope; functions get their own scope but other blocks[39m
+[38;5;246m// do not.[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mtrue[39;00m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m
+[38;5;252m}[39m
+[38;5;252mi[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 5 - not undefined as you'd expect in a block-scoped language[39m
+
+[38;5;246m// This has led to a common pattern of "immediately-executing anonymous[39m
+[38;5;246m// functions", which prevent temporary variables from leaking into the global[39m
+[38;5;246m// scope.[39m
+[38;5;252m([39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mtemporary[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;246m// We can access the global scope by assigning to the "global object", which[39m
+[38;5;252m [39m[38;5;246m// in a web browser is always `window`. The global object may have a[39m
+[38;5;252m [39m[38;5;246m// different name in non-browser environments such as Node.js.[39m
+[38;5;252m [39m[38;5;31mwindow[39m[38;5;252m.[39m[38;5;252mpermanent[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m)[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mtemporary[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// raises ReferenceError[39m
+[38;5;252mpermanent[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 10[39m
+
+[38;5;246m// One of JavaScript's most powerful features is closures. If a function is[39m
+[38;5;246m// defined inside another function, the inner function has access to all the[39m
+[38;5;246m// outer function's variables, even after the outer function exits.[39m
+[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252msayHelloInFiveSeconds[39m[38;5;252m([39m[38;5;252mname[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mprompt[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"Hello, "[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252mname[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;214m"!"[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;246m// Inner functions are put in the local scope by default, as if they were[39m
+[38;5;252m [39m[38;5;246m// declared with `var`.[39m
+[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252minner[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252malert[39m[38;5;252m([39m[38;5;252mprompt[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m [39m[38;5;252msetTimeout[39m[38;5;252m([39m[38;5;252minner[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5000[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;246m// setTimeout is asynchronous, so the sayHelloInFiveSeconds function will[39m
+[38;5;252m [39m[38;5;246m// exit immediately, and setTimeout will call inner afterwards. However,[39m
+[38;5;252m [39m[38;5;246m// because inner is "closed over" sayHelloInFiveSeconds, inner still has[39m
+[38;5;252m [39m[38;5;246m// access to the `prompt` variable when it is finally called.[39m
+[38;5;252m}[39m
+[38;5;252msayHelloInFiveSeconds[39m[38;5;252m([39m[38;5;214m"Adam"[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// will open a popup with "Hello, Adam!" in 5s[39m
+
+[38;5;246m///////////////////////////////////[39m
+[38;5;246m// 5. More about Objects; Constructors and Prototypes[39m
+
+[38;5;246m// Objects can contain functions.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyFunc[39m[38;5;252m:[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;214m"Hello world!"[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyFunc[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello world!"[39m
+
+[38;5;246m// When functions attached to an object are called, they can access the object[39m
+[38;5;246m// they're attached to using the `this` keyword.[39m
+[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyString[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m"Hello world!"[39m[38;5;252m,[39m
+[38;5;252m [39m[38;5;252mmyFunc[39m[38;5;252m:[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyString[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyFunc[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello world!"[39m
+
+[38;5;246m// What this is set to has to do with how the function is called, not where[39m
+[38;5;246m// it's defined. So, our function doesn't work if it isn't called in the[39m
+[38;5;246m// context of the object.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyFunc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyFunc[39m[38;5;252m;[39m
+[38;5;252mmyFunc[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = undefined[39m
+
+[38;5;246m// Inversely, a function can be assigned to the object and gain access to it[39m
+[38;5;246m// through `this`, even if it wasn't attached when it was defined.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyOtherFunc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyString[39m[38;5;252m.[39m[38;5;252mtoUpperCase[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyOtherFunc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mmyOtherFunc[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyOtherFunc[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "HELLO WORLD!"[39m
+
+[38;5;246m// We can also specify a context for a function to execute in when we invoke it[39m
+[38;5;246m// using `call` or `apply`.[39m
+
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252manotherFunc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252ms[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyString[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252manotherFunc[39m[38;5;252m.[39m[38;5;252mcall[39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m" And Hello Moon!"[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello World! And Hello Moon!"[39m
+
+[38;5;246m// The `apply` function is nearly identical, but takes an array for an argument[39m
+[38;5;246m// list.[39m
+
+[38;5;252manotherFunc[39m[38;5;252m.[39m[38;5;252mapply[39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;214m" And Hello Sun!"[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello World! And Hello Sun!"[39m
+
+[38;5;246m// This is useful when working with a function that accepts a sequence of[39m
+[38;5;246m// arguments and you want to pass an array.[39m
+
+[38;5;31mMath[39m[38;5;252m.[39m[38;5;252mmin[39m[38;5;252m([39m[38;5;67m42[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m27[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 6[39m
+[38;5;31mMath[39m[38;5;252m.[39m[38;5;252mmin[39m[38;5;252m([39m[38;5;252m[[39m[38;5;67m42[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m27[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = NaN (uh-oh!)[39m
+[38;5;31mMath[39m[38;5;252m.[39m[38;5;252mmin[39m[38;5;252m.[39m[38;5;252mapply[39m[38;5;252m([39m[38;5;31mMath[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m42[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m27[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 6[39m
+
+[38;5;246m// But, `call` and `apply` are only temporary. When we want it to stick, we can[39m
+[38;5;246m// use `bind`.[39m
+
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mboundFunc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252manotherFunc[39m[38;5;252m.[39m[38;5;252mbind[39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mboundFunc[39m[38;5;252m([39m[38;5;214m" And Hello Saturn!"[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello World! And Hello Saturn!"[39m
+
+[38;5;246m// `bind` can also be used to partially apply (curry) a function.[39m
+
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mproduct[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252ma[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mb[39m[38;5;252m)[39m[38;5;252m{[39m[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252ma[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;252mb[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252m}[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mdoubler[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mproduct[39m[38;5;252m.[39m[38;5;252mbind[39m[38;5;252m([39m[38;5;70;01mthis[39;00m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mdoubler[39m[38;5;252m([39m[38;5;67m8[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 16[39m
+
+[38;5;246m// When you call a function with the `new` keyword, a new object is created, and[39m
+[38;5;246m// made available to the function via the `this` keyword. Functions designed to be[39m
+[38;5;246m// called like that are called constructors.[39m
+
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mMyConstructor[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyNumber[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252mmyNewObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mnew[39;00m[38;5;252m [39m[38;5;252mMyConstructor[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = {myNumber: 5}[39m
+[38;5;252mmyNewObj[39m[38;5;252m.[39m[38;5;252mmyNumber[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 5[39m
+
+[38;5;246m// Unlike most other popular object-oriented languages, JavaScript has no[39m
+[38;5;246m// concept of 'instances' created from 'class' blueprints; instead, JavaScript[39m
+[38;5;246m// combines instantiation and inheritance into a single concept: a 'prototype'.[39m
+
+[38;5;246m// Every JavaScript object has a 'prototype'. When you go to access a property[39m
+[38;5;246m// on an object that doesn't exist on the actual object, the interpreter will[39m
+[38;5;246m// look at its prototype.[39m
+
+[38;5;246m// Some JS implementations let you access an object's prototype on the magic[39m
+[38;5;246m// property `__proto__`. While this is useful for explaining prototypes it's not[39m
+[38;5;246m// part of the standard; we'll get to standard ways of using prototypes later.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyString[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m"Hello world!"[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyPrototype[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmeaningOfLife[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m42[39m[38;5;252m,[39m
+[38;5;252m [39m[38;5;252mmyFunc[39m[38;5;252m:[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyString[39m[38;5;252m.[39m[38;5;252mtoLowerCase[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m[38;5;252m;[39m
+
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252m__proto__[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mmyPrototype[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmeaningOfLife[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 42[39m
+
+[38;5;246m// This works for functions, too.[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyFunc[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "hello world!"[39m
+
+[38;5;246m// Of course, if your property isn't on your prototype, the prototype's[39m
+[38;5;246m// prototype is searched, and so on.[39m
+[38;5;252mmyPrototype[39m[38;5;252m.[39m[38;5;252m__proto__[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyBoolean[39m[38;5;252m:[39m[38;5;252m [39m[38;5;70;01mtrue[39;00m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyBoolean[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// There's no copying involved here; each object stores a reference to its[39m
+[38;5;246m// prototype. This means we can alter the prototype and our changes will be[39m
+[38;5;246m// reflected everywhere.[39m
+[38;5;252mmyPrototype[39m[38;5;252m.[39m[38;5;252mmeaningOfLife[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m43[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmeaningOfLife[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 43[39m
+
+[38;5;246m// The for/in statement allows iteration over properties of an object,[39m
+[38;5;246m// walking up the prototype chain until it sees a null prototype.[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m[[39m[38;5;252mx[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m}[39m
+[38;5;246m///prints:[39m
+[38;5;246m// Hello world![39m
+[38;5;246m// 43[39m
+[38;5;246m// [Function: myFunc][39m
+
+[38;5;246m// To only consider properties attached to the object itself[39m
+[38;5;246m// and not its prototypes, use the `hasOwnProperty()` check.[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mhasOwnProperty[39m[38;5;252m([39m[38;5;252mx[39m[38;5;252m)[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m[[39m[38;5;252mx[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m
+[38;5;246m///prints:[39m
+[38;5;246m// Hello world![39m
+
+[38;5;246m// We mentioned that `__proto__` was non-standard, and there's no standard way to[39m
+[38;5;246m// change the prototype of an existing object. However, there are two ways to[39m
+[38;5;246m// create a new object with a given prototype.[39m
+
+[38;5;246m// The first is Object.create, which is a recent addition to JS, and therefore[39m
+[38;5;246m// not available in all implementations yet.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mObject[39m[38;5;252m.[39m[38;5;252mcreate[39m[38;5;252m([39m[38;5;252mmyPrototype[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmeaningOfLife[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 43[39m
+
+[38;5;246m// The second way, which works anywhere, has to do with constructors.[39m
+[38;5;246m// Constructors have a property called prototype. This is *not* the prototype of[39m
+[38;5;246m// the constructor function itself; instead, it's the prototype that new objects[39m
+[38;5;246m// are given when they're created with that constructor and the new keyword.[39m
+[38;5;252mMyConstructor[39m[38;5;252m.[39m[38;5;252mprototype[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyNumber[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m
+[38;5;252m [39m[38;5;252mgetMyNumber[39m[38;5;252m:[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyNumber[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyNewObj2[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mnew[39;00m[38;5;252m [39m[38;5;252mMyConstructor[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mmyNewObj2[39m[38;5;252m.[39m[38;5;252mgetMyNumber[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 5[39m
+[38;5;252mmyNewObj2[39m[38;5;252m.[39m[38;5;252mmyNumber[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m;[39m
+[38;5;252mmyNewObj2[39m[38;5;252m.[39m[38;5;252mgetMyNumber[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 6[39m
+
+[38;5;246m// Built-in types like strings and numbers also have constructors that create[39m
+[38;5;246m// equivalent wrapper objects.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyNumber[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m12[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyNumberObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mnew[39;00m[38;5;252m [39m[38;5;31mNumber[39m[38;5;252m([39m[38;5;67m12[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mmyNumber[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;252mmyNumberObj[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// Except, they aren't exactly equivalent.[39m
+[38;5;70;01mtypeof[39;00m[38;5;252m [39m[38;5;252mmyNumber[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 'number'[39m
+[38;5;70;01mtypeof[39;00m[38;5;252m [39m[38;5;252mmyNumberObj[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 'object'[39m
+[38;5;252mmyNumber[39m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;252mmyNumberObj[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// This code won't execute, because 0 is falsy.[39m
+[38;5;252m}[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mnew[39;00m[38;5;252m [39m[38;5;31mNumber[39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m)[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// This code will execute, because wrapped numbers are objects, and objects[39m
+[38;5;252m [39m[38;5;246m// are always truthy.[39m
+[38;5;252m}[39m
+
+[38;5;246m// However, the wrapper objects and the regular builtins share a prototype, so[39m
+[38;5;246m// you can actually add functionality to a string, for instance.[39m
+[38;5;31mString[39m[38;5;252m.[39m[38;5;252mprototype[39m[38;5;252m.[39m[38;5;252mfirstCharacter[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mcharAt[39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;214m"abc"[39m[38;5;252m.[39m[38;5;252mfirstCharacter[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "a"[39m
+
+[38;5;246m// This fact is often used in "polyfilling", which is implementing newer[39m
+[38;5;246m// features of JavaScript in an older subset of JavaScript, so that they can be[39m
+[38;5;246m// used in older environments such as outdated browsers.[39m
+
+[38;5;246m// For instance, we mentioned that Object.create isn't yet available in all[39m
+[38;5;246m// implementations, but we can still use it with this polyfill:[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;31mObject[39m[38;5;252m.[39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;70;01mundefined[39;00m[38;5;252m)[39m[38;5;252m{[39m[38;5;252m [39m[38;5;246m// don't overwrite it if it exists[39m
+[38;5;252m [39m[38;5;31mObject[39m[38;5;252m.[39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252mproto[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// make a temporary constructor with the right prototype[39m
+[38;5;252m [39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mConstructor[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252mConstructor[39m[38;5;252m.[39m[38;5;252mprototype[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mproto[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;246m// then use it to create a new, appropriately-prototyped object[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mnew[39;00m[38;5;252m [39m[38;5;252mConstructor[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252m}[39m
diff --git a/tests/results/22 b/tests/results/22
new file mode 100644
index 0000000..854cab3
--- /dev/null
+++ b/tests/results/22
@@ -0,0 +1,563 @@
+[38;5;246m// Single-line comments start with two slashes.[39m
+[38;5;246m/* Multiline comments start with slash-star,[39m
+[38;5;246m and end with star-slash */[39m
+
+[38;5;246m// Statements can be terminated by ;[39m
+[38;5;252mdoStuff[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+
+[38;5;246m// ... but they don't have to be, as semicolons are automatically inserted[39m
+[38;5;246m// wherever there's a newline, except in certain cases.[39m
+[38;5;252mdoStuff[39m[38;5;252m([39m[38;5;252m)[39m
+
+[38;5;246m// Because those cases can cause unexpected results, we'll keep on using[39m
+[38;5;246m// semicolons in this guide.[39m
+
+[38;5;246m///////////////////////////////////[39m
+[38;5;246m// 1. Numbers, Strings and Operators[39m
+
+[38;5;246m// JavaScript has one number type (which is a 64-bit IEEE 754 double).[39m
+[38;5;246m// Doubles have a 52-bit mantissa, which is enough to store integers[39m
+[38;5;246m// up to about 9✕10¹⁵ precisely.[39m
+[38;5;67m3[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 3[39m
+[38;5;67m1.5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 1.5[39m
+
+[38;5;246m// Some basic arithmetic works as you'd expect.[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 2[39m
+[38;5;67m0.1[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m0.2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 0.30000000000000004[39m
+[38;5;67m8[39m[38;5;252m [39m[38;5;252m-[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 7[39m
+[38;5;67m10[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 20[39m
+[38;5;67m35[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 7[39m
+
+[38;5;246m// Including uneven division.[39m
+[38;5;67m5[39m[38;5;252m [39m[38;5;252m/[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 2.5[39m
+
+[38;5;246m// And modulo division.[39m
+[38;5;67m10[39m[38;5;252m [39m[38;5;252m%[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 0[39m
+[38;5;67m30[39m[38;5;252m [39m[38;5;252m%[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 2[39m
+[38;5;67m18.5[39m[38;5;252m [39m[38;5;252m%[39m[38;5;252m [39m[38;5;67m7[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 4.5[39m
+
+[38;5;246m// Bitwise operations also work; when you perform a bitwise operation your float[39m
+[38;5;246m// is converted to a signed int *up to* 32 bits.[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m<<[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 4[39m
+
+[38;5;246m// Precedence is enforced with parentheses.[39m
+[38;5;252m([39m[38;5;67m1[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 8[39m
+
+[38;5;246m// There are three special not-a-real-number values:[39m
+[38;5;70;01mInfinity[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// result of e.g. 1/0[39m
+[38;5;252m-[39m[38;5;70;01mInfinity[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// result of e.g. -1/0[39m
+[38;5;70;01mNaN[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// result of e.g. 0/0, stands for 'Not a Number'[39m
+
+[38;5;246m// There's also a boolean type.[39m
+[38;5;70;01mtrue[39;00m[38;5;252m;[39m
+[38;5;70;01mfalse[39;00m[38;5;252m;[39m
+
+[38;5;246m// Strings are created with ' or ".[39m
+[38;5;214m'abc'[39m[38;5;252m;[39m
+[38;5;214m"Hello, world"[39m[38;5;252m;[39m
+
+[38;5;246m// Negation uses the ! symbol[39m
+[38;5;252m![39m[38;5;70;01mtrue[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+[38;5;252m![39m[38;5;70;01mfalse[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// Equality is ===[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+
+[38;5;246m// Inequality is !==[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m!==[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m!==[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// More comparisons[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+[38;5;67m1[39m[38;5;252m [39m[38;5;252m>[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m<=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+[38;5;67m2[39m[38;5;252m [39m[38;5;252m>=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// Strings are concatenated with +[39m
+[38;5;214m"Hello "[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;214m"world!"[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello world!"[39m
+
+[38;5;246m// ... which works with more than just strings[39m
+[38;5;214m"1, 2, "[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "1, 2, 3"[39m
+[38;5;214m"Hello "[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252m[[39m[38;5;214m"world"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"!"[39m[38;5;252m][39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello world,!"[39m
+
+[38;5;246m// and are compared with < and >[39m
+[38;5;214m"a"[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;214m"b"[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// Type coercion is performed for comparisons with double equals...[39m
+[38;5;214m"5"[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+[38;5;70;01mnull[39;00m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;70;01mundefined[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// ...unless you use ===[39m
+[38;5;214m"5"[39m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+[38;5;70;01mnull[39;00m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;70;01mundefined[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+
+[38;5;246m// ...which can result in some weird behaviour...[39m
+[38;5;67m13[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252m![39m[38;5;67m0[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// 14[39m
+[38;5;214m"13"[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252m![39m[38;5;67m0[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// '13true'[39m
+
+[38;5;246m// You can access characters in a string with `charAt`[39m
+[38;5;214m"This is a string"[39m[38;5;252m.[39m[38;5;252mcharAt[39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 'T'[39m
+
+[38;5;246m// ...or use `substring` to get larger pieces.[39m
+[38;5;214m"Hello world"[39m[38;5;252m.[39m[38;5;252msubstring[39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello"[39m
+
+[38;5;246m// `length` is a property, so don't use ().[39m
+[38;5;214m"Hello"[39m[38;5;252m.[39m[38;5;252mlength[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 5[39m
+
+[38;5;246m// There's also `null` and `undefined`.[39m
+[38;5;70;01mnull[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// used to indicate a deliberate non-value[39m
+[38;5;70;01mundefined[39;00m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// used to indicate a value is not currently present (although[39m
+[38;5;252m [39m[38;5;246m// `undefined` is actually a value itself)[39m
+
+[38;5;246m// false, null, undefined, NaN, 0 and "" are falsy; everything else is truthy.[39m
+[38;5;246m// Note that 0 is falsy and "0" is truthy, even though 0 == "0".[39m
+
+[38;5;246m///////////////////////////////////[39m
+[38;5;246m// 2. Variables, Arrays and Objects[39m
+
+[38;5;246m// Variables are declared with the `var` keyword. JavaScript is dynamically[39m
+[38;5;246m// typed, so you don't need to specify type. Assignment uses a single `=`[39m
+[38;5;246m// character.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252msomeVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m
+
+[38;5;246m// If you leave the var keyword off, you won't get an error...[39m
+[38;5;252msomeOtherVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m
+
+[38;5;246m// ...but your variable will be created in the global scope, not in the scope[39m
+[38;5;246m// you defined it in.[39m
+
+[38;5;246m// Variables declared without being assigned to are set to undefined.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252msomeThirdVar[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = undefined[39m
+
+[38;5;246m// If you want to declare a couple of variables, then you could use a comma[39m
+[38;5;246m// separator[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252msomeFourthVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252msomeFifthVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m;[39m
+
+[38;5;246m// There's shorthand for performing math operations on variables:[39m
+[38;5;252msomeVar[39m[38;5;252m [39m[38;5;252m+=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// equivalent to someVar = someVar + 5; someVar is 10 now[39m
+[38;5;252msomeVar[39m[38;5;252m [39m[38;5;252m*=[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// now someVar is 100[39m
+
+[38;5;246m// and an even-shorter-hand for adding or subtracting 1[39m
+[38;5;252msomeVar[39m[38;5;252m++[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// now someVar is 101[39m
+[38;5;252msomeVar[39m[38;5;252m--[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// back to 100[39m
+
+[38;5;246m// Arrays are ordered lists of values, of any type.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyArray[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;214m"Hello"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m45[39m[38;5;252m,[39m[38;5;252m [39m[38;5;70;01mtrue[39;00m[38;5;252m][39m[38;5;252m;[39m
+
+[38;5;246m// Their members can be accessed using the square-brackets subscript syntax.[39m
+[38;5;246m// Array indices start at zero.[39m
+[38;5;252mmyArray[39m[38;5;252m[[39m[38;5;67m1[39m[38;5;252m][39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 45[39m
+
+[38;5;246m// Arrays are mutable and of variable length.[39m
+[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252mpush[39m[38;5;252m([39m[38;5;214m"World"[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252mlength[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 4[39m
+
+[38;5;246m// Add/Modify at specific index[39m
+[38;5;252mmyArray[39m[38;5;252m[[39m[38;5;67m3[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"Hello"[39m[38;5;252m;[39m
+
+[38;5;246m// Add and remove element from front or back end of an array[39m
+[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252munshift[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// Add as the first element[39m
+[38;5;252msomeVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252mshift[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// Remove first element and return it[39m
+[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252mpush[39m[38;5;252m([39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// Add as the last element[39m
+[38;5;252msomeVar[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mmyArray[39m[38;5;252m.[39m[38;5;252mpop[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// Remove last element and return it[39m
+
+[38;5;246m// Join all elements of an array with semicolon[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyArray0[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m32[39m[38;5;252m,[39m[38;5;70;01mfalse[39;00m[38;5;252m,[39m[38;5;214m"js"[39m[38;5;252m,[39m[38;5;67m12[39m[38;5;252m,[39m[38;5;67m56[39m[38;5;252m,[39m[38;5;67m90[39m[38;5;252m][39m[38;5;252m;[39m
+[38;5;252mmyArray0[39m[38;5;252m.[39m[38;5;252mjoin[39m[38;5;252m([39m[38;5;214m";"[39m[38;5;252m)[39m[38;5;252m [39m[38;5;246m// = "32;false;js;12;56;90"[39m
+
+[38;5;246m// Get subarray of elements from index 1 (include) to 4 (exclude)[39m
+[38;5;252mmyArray0[39m[38;5;252m.[39m[38;5;252mslice[39m[38;5;252m([39m[38;5;67m1[39m[38;5;252m,[39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = [false,"js",12][39m
+
+[38;5;246m// Remove 4 elements starting from index 2, and insert there strings[39m
+[38;5;246m// "hi","wr" and "ld"; return removed subarray[39m
+[38;5;252mmyArray0[39m[38;5;252m.[39m[38;5;252msplice[39m[38;5;252m([39m[38;5;67m2[39m[38;5;252m,[39m[38;5;67m4[39m[38;5;252m,[39m[38;5;214m"hi"[39m[38;5;252m,[39m[38;5;214m"wr"[39m[38;5;252m,[39m[38;5;214m"ld"[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = ["js",12,56,90][39m
+[38;5;246m// myArray0 === [32,false,"hi","wr","ld"][39m
+
+[38;5;246m// JavaScript's objects are equivalent to "dictionaries" or "maps" in other[39m
+[38;5;246m// languages: an unordered collection of key-value pairs.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;252mkey1[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m"Hello"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mkey2[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m"World"[39m[38;5;252m}[39m[38;5;252m;[39m
+
+[38;5;246m// Keys are strings, but quotes aren't required if they're a valid[39m
+[38;5;246m// JavaScript identifier. Values can be any type.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;252mmyKey[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m"myValue"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"my other key"[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m}[39m[38;5;252m;[39m
+
+[38;5;246m// Object attributes can also be accessed using the subscript syntax,[39m
+[38;5;252mmyObj[39m[38;5;252m[[39m[38;5;214m"my other key"[39m[38;5;252m][39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 4[39m
+
+[38;5;246m// ... or using the dot syntax, provided the key is a valid identifier.[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyKey[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "myValue"[39m
+
+[38;5;246m// Objects are mutable; values can be changed and new keys added.[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyThirdKey[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mtrue[39;00m[38;5;252m;[39m
+
+[38;5;246m// If you try to access a value that's not yet set, you'll get undefined.[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyFourthKey[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = undefined[39m
+
+[38;5;246m///////////////////////////////////[39m
+[38;5;246m// 3. Logic and Control Structures[39m
+
+[38;5;246m// The `if` structure works as you'd expect.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mcount[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m1[39m[38;5;252m;[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mcount[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m3[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// evaluated if count is 3[39m
+[38;5;252m}[39m[38;5;252m [39m[38;5;70;01melse[39;00m[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mcount[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m4[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// evaluated if count is 4[39m
+[38;5;252m}[39m[38;5;252m [39m[38;5;70;01melse[39;00m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// evaluated if it's not either 3 or 4[39m
+[38;5;252m}[39m
+
+[38;5;246m// As does `while`.[39m
+[38;5;70;01mwhile[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mtrue[39;00m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// An infinite loop![39m
+[38;5;252m}[39m
+
+[38;5;246m// Do-while loops are like while loops, except they always run at least once.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252minput[39m[38;5;252m;[39m
+[38;5;70;01mdo[39;00m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252minput[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mgetInput[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m [39m[38;5;70;01mwhile[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252m![39m[38;5;252misValid[39m[38;5;252m([39m[38;5;252minput[39m[38;5;252m)[39m[38;5;252m)[39m[38;5;252m;[39m
+
+[38;5;246m// The `for` loop is the same as C and Java:[39m
+[38;5;246m// initialization; continue condition; iteration.[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m0[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mi[39m[38;5;252m++[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// will run 5 times[39m
+[38;5;252m}[39m
+
+[38;5;246m// Breaking out of labeled loops is similar to Java[39m
+[38;5;252mouter[39m[38;5;252m:[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m0[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mi[39m[38;5;252m++[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m0[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mj[39m[38;5;252m [39m[38;5;252m<[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252mj[39m[38;5;252m++[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m [39m[38;5;252m&&[39m[38;5;252m [39m[38;5;252mj[39m[38;5;252m [39m[38;5;252m==[39m[38;5;67m5[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m[38;5;252m [39m[38;5;252mouter[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;246m// breaks out of outer loop instead of only the inner one[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m
+
+[38;5;246m// The for/in statement allows iteration over properties of an object.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mdescription[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m""[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mperson[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m[38;5;252mfname[39m[38;5;252m:[39m[38;5;214m"Paul"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mlname[39m[38;5;252m:[39m[38;5;214m"Ken"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mage[39m[38;5;252m:[39m[38;5;67m18[39m[38;5;252m}[39m[38;5;252m;[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mperson[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mdescription[39m[38;5;252m [39m[38;5;252m+=[39m[38;5;252m [39m[38;5;252mperson[39m[38;5;252m[[39m[38;5;252mx[39m[38;5;252m][39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;214m" "[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m [39m[38;5;246m// description = 'Paul Ken 18 '[39m
+
+[38;5;246m// The for/of statement allows iteration over iterable objects (including the built-in String, [39m
+[38;5;246m// Array, e.g. the Array-like arguments or NodeList objects, TypedArray, Map and Set, [39m
+[38;5;246m// and user-defined iterables).[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyPets[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m""[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mpets[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m[[39m[38;5;214m"cat"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"dog"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"hamster"[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m"hedgehog"[39m[38;5;252m][39m[38;5;252m;[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mpet[39m[38;5;252m [39m[38;5;70;01mof[39;00m[38;5;252m [39m[38;5;252mpets[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyPets[39m[38;5;252m [39m[38;5;252m+=[39m[38;5;252m [39m[38;5;252mpet[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;214m" "[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m [39m[38;5;246m// myPets = 'cat dog hamster hedgehog '[39m
+
+[38;5;246m// && is logical and, || is logical or[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mhouse[39m[38;5;252m.[39m[38;5;252msize[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;214m"big"[39m[38;5;252m [39m[38;5;252m&&[39m[38;5;252m [39m[38;5;252mhouse[39m[38;5;252m.[39m[38;5;252mcolour[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;214m"blue"[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mhouse[39m[38;5;252m.[39m[38;5;252mcontains[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"bear"[39m[38;5;252m;[39m
+[38;5;252m}[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mcolour[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;214m"red"[39m[38;5;252m [39m[38;5;252m||[39m[38;5;252m [39m[38;5;252mcolour[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;214m"blue"[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// colour is either red or blue[39m
+[38;5;252m}[39m
+
+[38;5;246m// && and || "short circuit", which is useful for setting default values.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mname[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252motherName[39m[38;5;252m [39m[38;5;252m||[39m[38;5;252m [39m[38;5;214m"default"[39m[38;5;252m;[39m
+
+[38;5;246m// The `switch` statement checks for equality with `===`.[39m
+[38;5;246m// Use 'break' after each case[39m
+[38;5;246m// or the cases after the correct one will be executed too.[39m
+[38;5;252mgrade[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m'B'[39m[38;5;252m;[39m
+[38;5;70;01mswitch[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mgrade[39m[38;5;252m)[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mcase[39;00m[38;5;252m [39m[38;5;214m'A'[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;214m"Great job"[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mcase[39;00m[38;5;252m [39m[38;5;214m'B'[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;214m"OK job"[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mcase[39;00m[38;5;252m [39m[38;5;214m'C'[39m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;214m"You can do better"[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mdefault[39;00m[38;5;252m:[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;214m"Oy vey"[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;70;01mbreak[39;00m[38;5;252m;[39m
+[38;5;252m}[39m
+
+
+[38;5;246m///////////////////////////////////[39m
+[38;5;246m// 4. Functions, Scope and Closures[39m
+
+[38;5;246m// JavaScript functions are declared with the `function` keyword.[39m
+[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;252mthing[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252mthing[39m[38;5;252m.[39m[38;5;252mtoUpperCase[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m}[39m
+[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;214m"foo"[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "FOO"[39m
+
+[38;5;246m// Note that the value to be returned must start on the same line as the[39m
+[38;5;246m// `return` keyword, otherwise you'll always return `undefined` due to[39m
+[38;5;246m// automatic semicolon insertion. Watch out for this when using Allman style.[39m
+[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;246m// <- semicolon automatically inserted here[39m
+[38;5;252m [39m[38;5;252m{[39m[38;5;252mthisIsAn[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m'object literal'[39m[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252m}[39m
+[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = undefined[39m
+
+[38;5;246m// JavaScript functions are first class objects, so they can be reassigned to[39m
+[38;5;246m// different variable names and passed to other functions as arguments - for[39m
+[38;5;246m// example, when supplying an event handler:[39m
+[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// this code will be called in 5 seconds' time[39m
+[38;5;252m}[39m
+[38;5;252msetTimeout[39m[38;5;252m([39m[38;5;252mmyFunction[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5000[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;246m// Note: setTimeout isn't part of the JS language, but is provided by browsers[39m
+[38;5;246m// and Node.js.[39m
+
+[38;5;246m// Another function provided by browsers is setInterval[39m
+[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252mmyFunction[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// this code will be called every 5 seconds[39m
+[38;5;252m}[39m
+[38;5;252msetInterval[39m[38;5;252m([39m[38;5;252mmyFunction[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5000[39m[38;5;252m)[39m[38;5;252m;[39m
+
+[38;5;246m// Function objects don't even have to be declared with a name - you can write[39m
+[38;5;246m// an anonymous function definition directly into the arguments of another.[39m
+[38;5;252msetTimeout[39m[38;5;252m([39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// this code will be called in 5 seconds' time[39m
+[38;5;252m}[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5000[39m[38;5;252m)[39m[38;5;252m;[39m
+
+[38;5;246m// JavaScript has function scope; functions get their own scope but other blocks[39m
+[38;5;246m// do not.[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mtrue[39;00m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mi[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m
+[38;5;252m}[39m
+[38;5;252mi[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 5 - not undefined as you'd expect in a block-scoped language[39m
+
+[38;5;246m// This has led to a common pattern of "immediately-executing anonymous[39m
+[38;5;246m// functions", which prevent temporary variables from leaking into the global[39m
+[38;5;246m// scope.[39m
+[38;5;252m([39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mtemporary[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;246m// We can access the global scope by assigning to the "global object", which[39m
+[38;5;252m [39m[38;5;246m// in a web browser is always `window`. The global object may have a[39m
+[38;5;252m [39m[38;5;246m// different name in non-browser environments such as Node.js.[39m
+[38;5;252m [39m[38;5;31mwindow[39m[38;5;252m.[39m[38;5;252mpermanent[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m10[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m)[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mtemporary[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// raises ReferenceError[39m
+[38;5;252mpermanent[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 10[39m
+
+[38;5;246m// One of JavaScript's most powerful features is closures. If a function is[39m
+[38;5;246m// defined inside another function, the inner function has access to all the[39m
+[38;5;246m// outer function's variables, even after the outer function exits.[39m
+[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252msayHelloInFiveSeconds[39m[38;5;252m([39m[38;5;252mname[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mprompt[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;214m"Hello, "[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252mname[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;214m"!"[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;246m// Inner functions are put in the local scope by default, as if they were[39m
+[38;5;252m [39m[38;5;246m// declared with `var`.[39m
+[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m [39m[38;5;252minner[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252malert[39m[38;5;252m([39m[38;5;252mprompt[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m [39m[38;5;252msetTimeout[39m[38;5;252m([39m[38;5;252minner[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m5000[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;246m// setTimeout is asynchronous, so the sayHelloInFiveSeconds function will[39m
+[38;5;252m [39m[38;5;246m// exit immediately, and setTimeout will call inner afterwards. However,[39m
+[38;5;252m [39m[38;5;246m// because inner is "closed over" sayHelloInFiveSeconds, inner still has[39m
+[38;5;252m [39m[38;5;246m// access to the `prompt` variable when it is finally called.[39m
+[38;5;252m}[39m
+[38;5;252msayHelloInFiveSeconds[39m[38;5;252m([39m[38;5;214m"Adam"[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// will open a popup with "Hello, Adam!" in 5s[39m
+
+[38;5;246m///////////////////////////////////[39m
+[38;5;246m// 5. More about Objects; Constructors and Prototypes[39m
+
+[38;5;246m// Objects can contain functions.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyFunc[39m[38;5;252m:[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;214m"Hello world!"[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyFunc[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello world!"[39m
+
+[38;5;246m// When functions attached to an object are called, they can access the object[39m
+[38;5;246m// they're attached to using the `this` keyword.[39m
+[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyString[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m"Hello world!"[39m[38;5;252m,[39m
+[38;5;252m [39m[38;5;252mmyFunc[39m[38;5;252m:[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyString[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyFunc[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello world!"[39m
+
+[38;5;246m// What this is set to has to do with how the function is called, not where[39m
+[38;5;246m// it's defined. So, our function doesn't work if it isn't called in the[39m
+[38;5;246m// context of the object.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyFunc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyFunc[39m[38;5;252m;[39m
+[38;5;252mmyFunc[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = undefined[39m
+
+[38;5;246m// Inversely, a function can be assigned to the object and gain access to it[39m
+[38;5;246m// through `this`, even if it wasn't attached when it was defined.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyOtherFunc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyString[39m[38;5;252m.[39m[38;5;252mtoUpperCase[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyOtherFunc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mmyOtherFunc[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyOtherFunc[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "HELLO WORLD!"[39m
+
+[38;5;246m// We can also specify a context for a function to execute in when we invoke it[39m
+[38;5;246m// using `call` or `apply`.[39m
+
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252manotherFunc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252ms[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyString[39m[38;5;252m [39m[38;5;252m+[39m[38;5;252m [39m[38;5;252ms[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252manotherFunc[39m[38;5;252m.[39m[38;5;252mcall[39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m,[39m[38;5;252m [39m[38;5;214m" And Hello Moon!"[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello World! And Hello Moon!"[39m
+
+[38;5;246m// The `apply` function is nearly identical, but takes an array for an argument[39m
+[38;5;246m// list.[39m
+
+[38;5;252manotherFunc[39m[38;5;252m.[39m[38;5;252mapply[39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;214m" And Hello Sun!"[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello World! And Hello Sun!"[39m
+
+[38;5;246m// This is useful when working with a function that accepts a sequence of[39m
+[38;5;246m// arguments and you want to pass an array.[39m
+
+[38;5;31mMath[39m[38;5;252m.[39m[38;5;252mmin[39m[38;5;252m([39m[38;5;67m42[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m27[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 6[39m
+[38;5;31mMath[39m[38;5;252m.[39m[38;5;252mmin[39m[38;5;252m([39m[38;5;252m[[39m[38;5;67m42[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m27[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = NaN (uh-oh!)[39m
+[38;5;31mMath[39m[38;5;252m.[39m[38;5;252mmin[39m[38;5;252m.[39m[38;5;252mapply[39m[38;5;252m([39m[38;5;31mMath[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252m[[39m[38;5;67m42[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m,[39m[38;5;252m [39m[38;5;67m27[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 6[39m
+
+[38;5;246m// But, `call` and `apply` are only temporary. When we want it to stick, we can[39m
+[38;5;246m// use `bind`.[39m
+
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mboundFunc[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252manotherFunc[39m[38;5;252m.[39m[38;5;252mbind[39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mboundFunc[39m[38;5;252m([39m[38;5;214m" And Hello Saturn!"[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "Hello World! And Hello Saturn!"[39m
+
+[38;5;246m// `bind` can also be used to partially apply (curry) a function.[39m
+
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mproduct[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252ma[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mb[39m[38;5;252m)[39m[38;5;252m{[39m[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;252ma[39m[38;5;252m [39m[38;5;252m*[39m[38;5;252m [39m[38;5;252mb[39m[38;5;252m;[39m[38;5;252m [39m[38;5;252m}[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mdoubler[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mproduct[39m[38;5;252m.[39m[38;5;252mbind[39m[38;5;252m([39m[38;5;70;01mthis[39;00m[38;5;252m,[39m[38;5;252m [39m[38;5;67m2[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mdoubler[39m[38;5;252m([39m[38;5;67m8[39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 16[39m
+
+[38;5;246m// When you call a function with the `new` keyword, a new object is created, and[39m
+[38;5;246m// made available to the function via the `this` keyword. Functions designed to be[39m
+[38;5;246m// called like that are called constructors.[39m
+
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mMyConstructor[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyNumber[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252mmyNewObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mnew[39;00m[38;5;252m [39m[38;5;252mMyConstructor[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = {myNumber: 5}[39m
+[38;5;252mmyNewObj[39m[38;5;252m.[39m[38;5;252mmyNumber[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 5[39m
+
+[38;5;246m// Unlike most other popular object-oriented languages, JavaScript has no[39m
+[38;5;246m// concept of 'instances' created from 'class' blueprints; instead, JavaScript[39m
+[38;5;246m// combines instantiation and inheritance into a single concept: a 'prototype'.[39m
+
+[38;5;246m// Every JavaScript object has a 'prototype'. When you go to access a property[39m
+[38;5;246m// on an object that doesn't exist on the actual object, the interpreter will[39m
+[38;5;246m// look at its prototype.[39m
+
+[38;5;246m// Some JS implementations let you access an object's prototype on the magic[39m
+[38;5;246m// property `__proto__`. While this is useful for explaining prototypes it's not[39m
+[38;5;246m// part of the standard; we'll get to standard ways of using prototypes later.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyString[39m[38;5;252m:[39m[38;5;252m [39m[38;5;214m"Hello world!"[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyPrototype[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmeaningOfLife[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m42[39m[38;5;252m,[39m
+[38;5;252m [39m[38;5;252mmyFunc[39m[38;5;252m:[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyString[39m[38;5;252m.[39m[38;5;252mtoLowerCase[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m[38;5;252m;[39m
+
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252m__proto__[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mmyPrototype[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmeaningOfLife[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 42[39m
+
+[38;5;246m// This works for functions, too.[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyFunc[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "hello world!"[39m
+
+[38;5;246m// Of course, if your property isn't on your prototype, the prototype's[39m
+[38;5;246m// prototype is searched, and so on.[39m
+[38;5;252mmyPrototype[39m[38;5;252m.[39m[38;5;252m__proto__[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyBoolean[39m[38;5;252m:[39m[38;5;252m [39m[38;5;70;01mtrue[39;00m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmyBoolean[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// There's no copying involved here; each object stores a reference to its[39m
+[38;5;246m// prototype. This means we can alter the prototype and our changes will be[39m
+[38;5;246m// reflected everywhere.[39m
+[38;5;252mmyPrototype[39m[38;5;252m.[39m[38;5;252mmeaningOfLife[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m43[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmeaningOfLife[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 43[39m
+
+[38;5;246m// The for/in statement allows iteration over properties of an object,[39m
+[38;5;246m// walking up the prototype chain until it sees a null prototype.[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m[[39m[38;5;252mx[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m}[39m
+[38;5;246m///prints:[39m
+[38;5;246m// Hello world![39m
+[38;5;246m// 43[39m
+[38;5;246m// [Function: myFunc][39m
+
+[38;5;246m// To only consider properties attached to the object itself[39m
+[38;5;246m// and not its prototypes, use the `hasOwnProperty()` check.[39m
+[38;5;70;01mfor[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mx[39m[38;5;252m [39m[38;5;70;01min[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mhasOwnProperty[39m[38;5;252m([39m[38;5;252mx[39m[38;5;252m)[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mconsole[39m[38;5;252m.[39m[38;5;252mlog[39m[38;5;252m([39m[38;5;252mmyObj[39m[38;5;252m[[39m[38;5;252mx[39m[38;5;252m][39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m
+[38;5;246m///prints:[39m
+[38;5;246m// Hello world![39m
+
+[38;5;246m// We mentioned that `__proto__` was non-standard, and there's no standard way to[39m
+[38;5;246m// change the prototype of an existing object. However, there are two ways to[39m
+[38;5;246m// create a new object with a given prototype.[39m
+
+[38;5;246m// The first is Object.create, which is a recent addition to JS, and therefore[39m
+[38;5;246m// not available in all implementations yet.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;31mObject[39m[38;5;252m.[39m[38;5;252mcreate[39m[38;5;252m([39m[38;5;252mmyPrototype[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mmyObj[39m[38;5;252m.[39m[38;5;252mmeaningOfLife[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 43[39m
+
+[38;5;246m// The second way, which works anywhere, has to do with constructors.[39m
+[38;5;246m// Constructors have a property called prototype. This is *not* the prototype of[39m
+[38;5;246m// the constructor function itself; instead, it's the prototype that new objects[39m
+[38;5;246m// are given when they're created with that constructor and the new keyword.[39m
+[38;5;252mMyConstructor[39m[38;5;252m.[39m[38;5;252mprototype[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;252mmyNumber[39m[38;5;252m:[39m[38;5;252m [39m[38;5;67m5[39m[38;5;252m,[39m
+[38;5;252m [39m[38;5;252mgetMyNumber[39m[38;5;252m:[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mmyNumber[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyNewObj2[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mnew[39;00m[38;5;252m [39m[38;5;252mMyConstructor[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mmyNewObj2[39m[38;5;252m.[39m[38;5;252mgetMyNumber[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 5[39m
+[38;5;252mmyNewObj2[39m[38;5;252m.[39m[38;5;252mmyNumber[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m6[39m[38;5;252m;[39m
+[38;5;252mmyNewObj2[39m[38;5;252m.[39m[38;5;252mgetMyNumber[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 6[39m
+
+[38;5;246m// Built-in types like strings and numbers also have constructors that create[39m
+[38;5;246m// equivalent wrapper objects.[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyNumber[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;67m12[39m[38;5;252m;[39m
+[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mmyNumberObj[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mnew[39;00m[38;5;252m [39m[38;5;31mNumber[39m[38;5;252m([39m[38;5;67m12[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252mmyNumber[39m[38;5;252m [39m[38;5;252m==[39m[38;5;252m [39m[38;5;252mmyNumberObj[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = true[39m
+
+[38;5;246m// Except, they aren't exactly equivalent.[39m
+[38;5;70;01mtypeof[39;00m[38;5;252m [39m[38;5;252mmyNumber[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 'number'[39m
+[38;5;70;01mtypeof[39;00m[38;5;252m [39m[38;5;252mmyNumberObj[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = 'object'[39m
+[38;5;252mmyNumber[39m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;252mmyNumberObj[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = false[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// This code won't execute, because 0 is falsy.[39m
+[38;5;252m}[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;70;01mnew[39;00m[38;5;252m [39m[38;5;31mNumber[39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m)[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// This code will execute, because wrapped numbers are objects, and objects[39m
+[38;5;252m [39m[38;5;246m// are always truthy.[39m
+[38;5;252m}[39m
+
+[38;5;246m// However, the wrapper objects and the regular builtins share a prototype, so[39m
+[38;5;246m// you can actually add functionality to a string, for instance.[39m
+[38;5;31mString[39m[38;5;252m.[39m[38;5;252mprototype[39m[38;5;252m.[39m[38;5;252mfirstCharacter[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mthis[39;00m[38;5;252m.[39m[38;5;252mcharAt[39m[38;5;252m([39m[38;5;67m0[39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m}[39m[38;5;252m;[39m
+[38;5;214m"abc"[39m[38;5;252m.[39m[38;5;252mfirstCharacter[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m[38;5;252m [39m[38;5;246m// = "a"[39m
+
+[38;5;246m// This fact is often used in "polyfilling", which is implementing newer[39m
+[38;5;246m// features of JavaScript in an older subset of JavaScript, so that they can be[39m
+[38;5;246m// used in older environments such as outdated browsers.[39m
+
+[38;5;246m// For instance, we mentioned that Object.create isn't yet available in all[39m
+[38;5;246m// implementations, but we can still use it with this polyfill:[39m
+[38;5;70;01mif[39;00m[38;5;252m [39m[38;5;252m([39m[38;5;31mObject[39m[38;5;252m.[39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m===[39m[38;5;252m [39m[38;5;70;01mundefined[39;00m[38;5;252m)[39m[38;5;252m{[39m[38;5;252m [39m[38;5;246m// don't overwrite it if it exists[39m
+[38;5;252m [39m[38;5;31mObject[39m[38;5;252m.[39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252mproto[39m[38;5;252m)[39m[38;5;252m{[39m
+[38;5;252m [39m[38;5;246m// make a temporary constructor with the right prototype[39m
+[38;5;252m [39m[38;5;70;01mvar[39;00m[38;5;252m [39m[38;5;252mConstructor[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;70;01mfunction[39;00m[38;5;252m([39m[38;5;252m)[39m[38;5;252m{[39m[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252mConstructor[39m[38;5;252m.[39m[38;5;252mprototype[39m[38;5;252m [39m[38;5;252m=[39m[38;5;252m [39m[38;5;252mproto[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;246m// then use it to create a new, appropriately-prototyped object[39m
+[38;5;252m [39m[38;5;70;01mreturn[39;00m[38;5;252m [39m[38;5;70;01mnew[39;00m[38;5;252m [39m[38;5;252mConstructor[39m[38;5;252m([39m[38;5;252m)[39m[38;5;252m;[39m
+[38;5;252m [39m[38;5;252m}[39m[38;5;252m;[39m
+[38;5;252m}[39m
diff --git a/tests/results/23 b/tests/results/23
new file mode 100644
index 0000000..e869a69
--- /dev/null
+++ b/tests/results/23
@@ -0,0 +1,26 @@
+:learn
+:list
+Arrays
+Axioms
+Channels
+Declarations
+Embedding
+Errors
+Interfaces
+Maps
+Operators
+Pointers
+Structs
+for
+func
+go
+hello
+http
+if
+packages
+print
+range
+rosetta/
+slices
+switch
+types
diff --git a/tests/results/24 b/tests/results/24
new file mode 100644
index 0000000..74fa5e9
--- /dev/null
+++ b/tests/results/24
@@ -0,0 +1,7 @@
+Unknown topic.
+Do you mean one of these topics maybe?
+
+ * mkfs.fat 84
+ * mkfs.vfat 80
+ * mkfs.exfat 76
+
diff --git a/tests/results/3 b/tests/results/3
index 10b9309..3d2f50a 100644
--- a/tests/results/3
+++ b/tests/results/3
@@ -1,54 +1,24 @@
-[38;5;246m# Create a btrfs file system on /dev/sdb, /dev/sdc, and /dev/sdd[39m
-[38;5;252mmkfs.btrfs[39m[38;5;252m [39m[38;5;252m/dev/sdb[39m[38;5;252m [39m[38;5;252m/dev/sdc[39m[38;5;252m [39m[38;5;252m/dev/sdd[39m
-
-[38;5;246m# btrfs with just one hard drive, metadata not redundant [39m
-[38;5;246m# (this is danegerous: if your metadata is lost, your data is lost as well)[39m
-[38;5;252mmkfs.btrfs[39m[38;5;252m [39m[38;5;252m-m[39m[38;5;252m [39m[38;5;252msingle[39m[38;5;252m [39m[38;5;252m/dev/sdb[39m
-
-[38;5;246m# data to be redundant and metadata to be non-redundant:[39m
-[38;5;252mmkfs.btrfs[39m[38;5;252m [39m[38;5;252m-m[39m[38;5;252m [39m[38;5;252mraid0[39m[38;5;252m [39m[38;5;252m-d[39m[38;5;252m [39m[38;5;252mraid1[39m[38;5;252m [39m[38;5;252m/dev/sdb[39m[38;5;252m [39m[38;5;252m/dev/sdc[39m[38;5;252m [39m[38;5;252m/dev/sdd[39m
-
-[38;5;246m# both data and metadata to be redundan[39m
-[38;5;252mmkfs.btrfs[39m[38;5;252m [39m[38;5;252m-d[39m[38;5;252m [39m[38;5;252mraid1[39m[38;5;252m [39m[38;5;252m/dev/sdb[39m[38;5;252m [39m[38;5;252m/dev/sdc[39m[38;5;252m [39m[38;5;252m/dev/sdd[39m
-
-[38;5;246m# To get a list of all btrfs file systems[39m
-[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mfilesystem[39m[38;5;252m [39m[38;5;252mshow[39m
-
-[38;5;246m# detailed df for a fileesystem (mounted in /mnt)[39m
-[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mfilesystem[39m[38;5;252m [39m[38;5;252mdf[39m[38;5;252m [39m[38;5;252m/mnt[39m
-
-[38;5;246m# resize btrfs online (-2g decreases, +2g increases)[39m
-[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mfilesystem[39m[38;5;252m [39m[38;5;252mresize[39m[38;5;252m [39m[38;5;252m-2g[39m[38;5;252m [39m[38;5;252m/mnt[39m
-
-[38;5;246m# use maximum space[39m
-[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mfilesystem[39m[38;5;252m [39m[38;5;252mresize[39m[38;5;252m [39m[38;5;252mmax[39m[38;5;252m [39m[38;5;252m/mnt[39m
-
-[38;5;246m# add new device to a filesystem[39m
-[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mdevice[39m[38;5;252m [39m[38;5;252madd[39m[38;5;252m [39m[38;5;252m/dev/sdf[39m[38;5;252m [39m[38;5;252m/mnt[39m
-
-[38;5;246m# remove devices from a filesystem[39m
-[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mdevice[39m[38;5;252m [39m[38;5;252mdelete[39m[38;5;252m [39m[38;5;252mmissing[39m[38;5;252m [39m[38;5;252m/mnt[39m
-
-[38;5;246m# create the subvolume /mnt/sv1 in the /mnt volume[39m
-[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252msubvolume[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m/mnt/sv1[39m
-
-[38;5;246m# list subvolumes[39m
-[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252msubvolume[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m/mnt[39m
-
-[38;5;246m# mount subvolume without mounting the main filesystem[39m
-[38;5;252mmount[39m[38;5;252m [39m[38;5;252m-o[39m[38;5;252m [39m[38;5;87msubvol[39m[38;5;252m=[39m[38;5;252msv1[39m[38;5;252m [39m[38;5;252m/dev/sdb[39m[38;5;252m [39m[38;5;252m/mnt[39m
-
-[38;5;246m# delete subvolume[39m
-[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252msubvolume[39m[38;5;252m [39m[38;5;252mdelete[39m[38;5;252m [39m[38;5;252m/mnt/sv1[39m
-
-[38;5;246m# taking snapshot of a subvolume[39m
-[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252msubvolume[39m[38;5;252m [39m[38;5;252msnapshot[39m[38;5;252m [39m[38;5;252m/mnt/sv1[39m[38;5;252m [39m[38;5;252m/mnt/sv1_snapshot[39m
-
-[38;5;246m# taking snapshot of a file (copy file by reference)[39m
-[38;5;252mcp[39m[38;5;252m [39m[38;5;252m--reflink[39m[38;5;252m [39m[38;5;252m/mnt/sv1/test1[39m[38;5;252m [39m[38;5;252m/mnt/sv1/test3[39m
-
-[38;5;246m# convert ext3/ext4 to btrfs[39m
-[38;5;252mbtrfs-convert[39m[38;5;252m [39m[38;5;252m/dev/sdb1[39m
-
-[38;5;246m# convert btrfs to ext3/ext4[39m
-[38;5;252mbtrfs-convert[39m[38;5;252m [39m[38;5;252m-r[39m[38;5;252m [39m[38;5;252m/dev/sdb1[39m
+#
+# ls
+#
+# List directory contents.
+#
+# List files one per line:
+ ls -1
+#
+# List all files, including hidden files:
+ ls -a
+#
+# Long format list (permissions, ownership, size and modification date) of all files:
+ ls -la
+#
+# Long format list with size displayed using human readable units (KB, MB, GB):
+ ls -lh
+#
+# Long format list sorted by size (descending):
+ ls -lS
+#
+# Long format list of all files, sorted by modification date (oldest first):
+ ls -ltr
+#
+#
diff --git a/tests/results/4 b/tests/results/4
index 317da29..10b9309 100644
--- a/tests/results/4
+++ b/tests/results/4
@@ -1,3 +1,54 @@
-[48;5;8m[24m btrfs [24m[0m
+[38;5;246m# Create a btrfs file system on /dev/sdb, /dev/sdc, and /dev/sdd[39m
+[38;5;252mmkfs.btrfs[39m[38;5;252m [39m[38;5;252m/dev/sdb[39m[38;5;252m [39m[38;5;252m/dev/sdc[39m[38;5;252m [39m[38;5;252m/dev/sdd[39m
+
+[38;5;246m# btrfs with just one hard drive, metadata not redundant [39m
+[38;5;246m# (this is danegerous: if your metadata is lost, your data is lost as well)[39m
+[38;5;252mmkfs.btrfs[39m[38;5;252m [39m[38;5;252m-m[39m[38;5;252m [39m[38;5;252msingle[39m[38;5;252m [39m[38;5;252m/dev/sdb[39m
+
+[38;5;246m# data to be redundant and metadata to be non-redundant:[39m
+[38;5;252mmkfs.btrfs[39m[38;5;252m [39m[38;5;252m-m[39m[38;5;252m [39m[38;5;252mraid0[39m[38;5;252m [39m[38;5;252m-d[39m[38;5;252m [39m[38;5;252mraid1[39m[38;5;252m [39m[38;5;252m/dev/sdb[39m[38;5;252m [39m[38;5;252m/dev/sdc[39m[38;5;252m [39m[38;5;252m/dev/sdd[39m
+
+[38;5;246m# both data and metadata to be redundan[39m
+[38;5;252mmkfs.btrfs[39m[38;5;252m [39m[38;5;252m-d[39m[38;5;252m [39m[38;5;252mraid1[39m[38;5;252m [39m[38;5;252m/dev/sdb[39m[38;5;252m [39m[38;5;252m/dev/sdc[39m[38;5;252m [39m[38;5;252m/dev/sdd[39m
+
+[38;5;246m# To get a list of all btrfs file systems[39m
+[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mfilesystem[39m[38;5;252m [39m[38;5;252mshow[39m
+
+[38;5;246m# detailed df for a fileesystem (mounted in /mnt)[39m
+[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mfilesystem[39m[38;5;252m [39m[38;5;252mdf[39m[38;5;252m [39m[38;5;252m/mnt[39m
+
+[38;5;246m# resize btrfs online (-2g decreases, +2g increases)[39m
+[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mfilesystem[39m[38;5;252m [39m[38;5;252mresize[39m[38;5;252m [39m[38;5;252m-2g[39m[38;5;252m [39m[38;5;252m/mnt[39m
+
+[38;5;246m# use maximum space[39m
+[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mfilesystem[39m[38;5;252m [39m[38;5;252mresize[39m[38;5;252m [39m[38;5;252mmax[39m[38;5;252m [39m[38;5;252m/mnt[39m
+
+[38;5;246m# add new device to a filesystem[39m
+[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mdevice[39m[38;5;252m [39m[38;5;252madd[39m[38;5;252m [39m[38;5;252m/dev/sdf[39m[38;5;252m [39m[38;5;252m/mnt[39m
+
+[38;5;246m# remove devices from a filesystem[39m
+[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252mdevice[39m[38;5;252m [39m[38;5;252mdelete[39m[38;5;252m [39m[38;5;252mmissing[39m[38;5;252m [39m[38;5;252m/mnt[39m
+
[38;5;246m# create the subvolume /mnt/sv1 in the /mnt volume[39m
[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252msubvolume[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m/mnt/sv1[39m
+
+[38;5;246m# list subvolumes[39m
+[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252msubvolume[39m[38;5;252m [39m[38;5;252mlist[39m[38;5;252m [39m[38;5;252m/mnt[39m
+
+[38;5;246m# mount subvolume without mounting the main filesystem[39m
+[38;5;252mmount[39m[38;5;252m [39m[38;5;252m-o[39m[38;5;252m [39m[38;5;87msubvol[39m[38;5;252m=[39m[38;5;252msv1[39m[38;5;252m [39m[38;5;252m/dev/sdb[39m[38;5;252m [39m[38;5;252m/mnt[39m
+
+[38;5;246m# delete subvolume[39m
+[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252msubvolume[39m[38;5;252m [39m[38;5;252mdelete[39m[38;5;252m [39m[38;5;252m/mnt/sv1[39m
+
+[38;5;246m# taking snapshot of a subvolume[39m
+[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252msubvolume[39m[38;5;252m [39m[38;5;252msnapshot[39m[38;5;252m [39m[38;5;252m/mnt/sv1[39m[38;5;252m [39m[38;5;252m/mnt/sv1_snapshot[39m
+
+[38;5;246m# taking snapshot of a file (copy file by reference)[39m
+[38;5;252mcp[39m[38;5;252m [39m[38;5;252m--reflink[39m[38;5;252m [39m[38;5;252m/mnt/sv1/test1[39m[38;5;252m [39m[38;5;252m/mnt/sv1/test3[39m
+
+[38;5;246m# convert ext3/ext4 to btrfs[39m
+[38;5;252mbtrfs-convert[39m[38;5;252m [39m[38;5;252m/dev/sdb1[39m
+
+[38;5;246m# convert btrfs to ext3/ext4[39m
+[38;5;252mbtrfs-convert[39m[38;5;252m [39m[38;5;252m-r[39m[38;5;252m [39m[38;5;252m/dev/sdb1[39m
diff --git a/tests/results/5 b/tests/results/5
index 0b9624b..317da29 100644
--- a/tests/results/5
+++ b/tests/results/5
@@ -1,216 +1,3 @@
-[38;5;172m## curl cht.sh[0m
-
-To access a cheat sheet you can simply issue a plain HTTP or HTTPS request
-specifying the topic name in the query URL:
-
- [36mcurl cheat.sh[0m[32m/tar[0m
- [36mcurl https://cheat.sh[0m[32m/tar[0m
-
-You can use the full service name, [32mcheat.sh[0m, or the shorter variant, [32mcht.sh[0m.
-They are equivalent:
-
- [36mcurl https://[0m[32mcht.sh[0m[36m/tar[0m
- [36mcurl https://[0m[32mcheat.sh[0m[36m/tar[0m
-
-The preferred access protocol is HTTPS, and you should use it always when possible.
-
-Cheat sheets in the root namespaces cover UNIX/Linux commands.
-
-Cheat sheets covering programming languages are located in subsections:
-
- [36mcurl cht.sh/[0m[32mgo/func[0m
-
-All cheat sheets in a subsection can be listed using a special query [32m:list[0m :
-
- [36mcurl cht.sh/go/[0m[32m:list[0m
-
-There are several other special queries. All of them are starting with a [32mcolon[0m.
-See [32m/:help[0m for the full list of the special queries.
-
-
-[38;5;172m## Search[0m
-
-If a cheat sheet is too large, you can cut the needed part out using an
-additional search parameter. In this case, only the paragraph that contains the
-search term will be displayed:
-
- [36mcurl cht.sh/tar[0m[32m~extract[0m
-
-If the name of the cheat sheet is omitted, and only the serch query is specified,
-all cheat sheets in the namespace are scanned, and the found occurrencies
-are displayed:
-
- [36mcurl cht.sh/[0m[32m~extract[0m
-
-
-[38;5;172m## Options[0m
-
-cheat.sh queries as well as search queries have many options.
-They can be specified as a part of the query string in the URL, after [32m?[0m.
-Short single letter options could be written all jointly together,
-and long options are separated with [32m&[0m. For example, to switch
-syntax highlighting off the [32mT[0m switch is used:
-
- [36mcurl cht.sh/tar[0m[32m?T[0m
-
-Full list of all available cheat.sh options as well as description of all modes
-of operation can be found in [32m/:help[0m,
-
- [36mcurl cht.sh[0m[32m/:help[0m
-
-
-[38;5;172m## cht.sh client[0m
-
-Though it's perfectly possible to access cheat.sh using [36mcurl[0m (or any other
-HTTP client) alone, there is a special client, that has several advantages
-comparing to plain curling: [32mcht.sh[0m.
-
-To install the client in [32m~/bin[0m:
-
- [36mcurl[0m [32mhttps://cht.sh/:cht.sh[0m [36m> ~/bin/cht.sh[0m
- [36mchmod +x ~/bin/cht.sh[0m
-
-Queries look the same, but you can separate words in the query with [36mspaces[0m,
-instead of [36m+[0m as when using curl, what looks more natural:
-
- [36mcht.sh[0m [32mpython zip lists[0m
-
-
-[38;5;172m## cht.sh shell[0m
-
-If you always issuing queries about the same programming language, it's can be
-more convenient to run the client in the shell mode and specify the queries
-context:
-
- [36m$[0m [32mcht.sh --shell python[0m
- [36mcht.sh/python> zip lists[0m
-
-Of course, you can start the shell without the context too:
-
- [36m$[0m [32mcht.sh --shell[0m
- [36mcht.sh> python zip lists[0m
- [36mcht.sh> go http query[0m
- [36mcht.sh> js iterate list[0m
-
-If you ue predominantly one language but sometime issuing queries about other,
-you may prepend the query with [32m/[0m:
-
- [36mcht.sh/python>[0m [32mzip lists[0m
- [36mcht.sh/python>[0m [32m/go http query[0m
- [36mcht.sh/python>[0m [32m/js iterate list[0m
-
-
-[38;5;172m## :learn[0m
-
-If you are just start learning a new programming language, and you have no
-distinct queries for the moment, cheat.sh can be a good starting point too. As
-you know, it exports cheat sheets from the best cheat sheet repositories, and
-one of them is [36mLearn X in Y[0m, a repository of concise documentation devoted
-to learning programming languages from scratch (and not only them).
-
-If you want start learning a new programming language, do (use less -R because
-the output could be quite big):
-
- [36mcurl cht.sh/elixir/[0m[32m:learn[0m [36m| less -R[0m
-
-Or simply [32m:learn[0m with cht.sh (you don't need [32mless -R[0m here, because
-[36mcht.sh[0m starts pager if needed automatically):
-
- [2mcht.sh/elixir>[0m [32m:learn[0m
-
-
-[38;5;172m## Programming languages questions[0m
-
-One of the most important features of cheat.sh is that you can ask it any
-questions about programming languages and instantly get answers on them. You
-can use both direct HTTP queries or the cht.sh client for that:
-
- [36mcurl cht.sh/[0m[32mpython/reverse+list[0m
-
- [2mcht.sh/python>[0m [32mreverse list[0m
-
-In the latter case you don't need + to separate the words in the query, you can
-do it in a more natural way, with spaces.
-
-If context in the cht.sh shell is not specified, you have to write the
-programming language name as the first word in the query:
-
- [2mcht.sh>[0m [32mpython reverse list[0m
-
-But if you are using only one programming language and all queries are about
-it, it's better to change the current context and
-
-
-[38;5;172m## Comments[0m
-
-Text in the answers is syntactically formatted as comment in the correspondent
-programming language
-
-When using cht.sh, you can copy the result of the last query into the selection
-buffer (you may also call it "clibpoard") using [32mC[0m (or [32mc[0m, with text):
-
- [36mcht.sh/python> reverse list[0m
- [2m...[0m
- [36mcht.sh/python>[0m [32mC[0m
- [2m1 lines copied[0m
-
-
-[38;5;172m## bash TAB-completion for cht.sh[0m
-
-One of the advantages of the [36mcht.sh[0m client comparing to plain curl is that you
-can use TAB completion when writing its queries in [36mbash[0m
-(other supported shells: [36mzsh[0m and [36mfish[0m).
-
-Install the TAB completion script for that. Assuming you use bash, you have to do:
-
- [36mmkdir -p ~/.bash.d/[0m
- [36mcurl[0m [32mhttps://cht.sh/:bash_completion[0m [36m> ~/.bash.d/cht.sh[0m
- [36mecho 'source ~/.bash.d/cht.sh' >> ~/.bashrc[0m
- [36msource ~/.bash.d/cht.sh[0m
-
-
-[38;5;172m## Editor[0m
-
-You can access cheat.sh directly from editors: [36mVim[0m and [36mEmacs[0m.
-It's a very important feature! You should absolutely like it.
-
-[36mImagine:[0m
-instead of switching to your browser, googling, browsing Stack Overflow
-and eventually copying the code snippets you need and later pasting them into
-the editor, you can achieve the same instantly and without leaving
-the editor at all!
-
-Here is how it looks like:
-
-1. In Vim, if you have a question while editing a program, you can just type
-your question [36mdirectly in the buffer[0m and press [32mKK[0m. You will get
-the answer to your question in [36mpager[0m. (with [32mKB[0m you'll get the answer
-in a separate [36mbuffer[0m).
-
-2. If you like the answer. You can manually paste it from the buffer or
-the pager, or if you are lazy you can use [32mKP[0m to paste it under
-your question ([32mKR[0m will replace your question). If you want the
-answer without the comments, [32mKC[0m replays the last query
-toggling them.
-
-You have to install cheat.sh [36mVim/Emacs plugins[0m for the editor support.
-See [32m/:vim[0m or [32m/:emacs[0m with the detailed installation instructions.
-
-
-[38;5;172m## Feature requests, feedback and contribution[0m
-
-If you want to submit a new community driver repository for cheat.sh please
-open a ticket on the project page on GitHub.
-
-If you want to modify an existing cheat sheet, please check the source of the
-cheat sheet (it is always displayed in the cheat sheet bottom line).
-
-If you want to add a new cheat sheet, add it here:
-[36mhttps://github.com/chubin/cheat.sheets[0m
-
-If you want to suggest a new feature for cheat.sh, or if you've found a bug,
-please open a new issue on github:
-[36mhttps://github.com/chubin/cheat.sh[0m
-
-If you want to get the major project updates, follow @igor_chubin in Twitter
-or this RSS feed: [36mhttps://twitrss.me/twitter_user_to_rss/?user=igor_chubin[0m
+[48;5;8m[24m btrfs [24m[0m
+[38;5;246m# create the subvolume /mnt/sv1 in the /mnt volume[39m
+[38;5;252mbtrfs[39m[38;5;252m [39m[38;5;252msubvolume[39m[38;5;252m [39m[38;5;252mcreate[39m[38;5;252m [39m[38;5;252m/mnt/sv1[39m
diff --git a/tests/results/6 b/tests/results/6
index 4072487..0b9624b 100644
--- a/tests/results/6
+++ b/tests/results/6
@@ -1,85 +1,216 @@
-Usage:
+[38;5;172m## curl cht.sh[0m
+
+To access a cheat sheet you can simply issue a plain HTTP or HTTPS request
+specifying the topic name in the query URL:
+
+ [36mcurl cheat.sh[0m[32m/tar[0m
+ [36mcurl https://cheat.sh[0m[32m/tar[0m
+
+You can use the full service name, [32mcheat.sh[0m, or the shorter variant, [32mcht.sh[0m.
+They are equivalent:
+
+ [36mcurl https://[0m[32mcht.sh[0m[36m/tar[0m
+ [36mcurl https://[0m[32mcheat.sh[0m[36m/tar[0m
+
+The preferred access protocol is HTTPS, and you should use it always when possible.
+
+Cheat sheets in the root namespaces cover UNIX/Linux commands.
+
+Cheat sheets covering programming languages are located in subsections:
+
+ [36mcurl cht.sh/[0m[32mgo/func[0m
+
+All cheat sheets in a subsection can be listed using a special query [32m:list[0m :
+
+ [36mcurl cht.sh/go/[0m[32m:list[0m
+
+There are several other special queries. All of them are starting with a [32mcolon[0m.
+See [32m/:help[0m for the full list of the special queries.
+
+
+[38;5;172m## Search[0m
+
+If a cheat sheet is too large, you can cut the needed part out using an
+additional search parameter. In this case, only the paragraph that contains the
+search term will be displayed:
+
+ [36mcurl cht.sh/tar[0m[32m~extract[0m
+
+If the name of the cheat sheet is omitted, and only the serch query is specified,
+all cheat sheets in the namespace are scanned, and the found occurrencies
+are displayed:
+
+ [36mcurl cht.sh/[0m[32m~extract[0m
+
+
+[38;5;172m## Options[0m
+
+cheat.sh queries as well as search queries have many options.
+They can be specified as a part of the query string in the URL, after [32m?[0m.
+Short single letter options could be written all jointly together,
+and long options are separated with [32m&[0m. For example, to switch
+syntax highlighting off the [32mT[0m switch is used:
+
+ [36mcurl cht.sh/tar[0m[32m?T[0m
+
+Full list of all available cheat.sh options as well as description of all modes
+of operation can be found in [32m/:help[0m,
+
+ [36mcurl cht.sh[0m[32m/:help[0m
+
+
+[38;5;172m## cht.sh client[0m
+
+Though it's perfectly possible to access cheat.sh using [36mcurl[0m (or any other
+HTTP client) alone, there is a special client, that has several advantages
+comparing to plain curling: [32mcht.sh[0m.
+
+To install the client in [32m~/bin[0m:
+
+ [36mcurl[0m [32mhttps://cht.sh/:cht.sh[0m [36m> ~/bin/cht.sh[0m
+ [36mchmod +x ~/bin/cht.sh[0m
+
+Queries look the same, but you can separate words in the query with [36mspaces[0m,
+instead of [36m+[0m as when using curl, what looks more natural:
+
+ [36mcht.sh[0m [32mpython zip lists[0m
+
+
+[38;5;172m## cht.sh shell[0m
+
+If you always issuing queries about the same programming language, it's can be
+more convenient to run the client in the shell mode and specify the queries
+context:
+
+ [36m$[0m [32mcht.sh --shell python[0m
+ [36mcht.sh/python> zip lists[0m
+
+Of course, you can start the shell without the context too:
+
+ [36m$[0m [32mcht.sh --shell[0m
+ [36mcht.sh> python zip lists[0m
+ [36mcht.sh> go http query[0m
+ [36mcht.sh> js iterate list[0m
+
+If you ue predominantly one language but sometime issuing queries about other,
+you may prepend the query with [32m/[0m:
+
+ [36mcht.sh/python>[0m [32mzip lists[0m
+ [36mcht.sh/python>[0m [32m/go http query[0m
+ [36mcht.sh/python>[0m [32m/js iterate list[0m
+
+
+[38;5;172m## :learn[0m
+
+If you are just start learning a new programming language, and you have no
+distinct queries for the moment, cheat.sh can be a good starting point too. As
+you know, it exports cheat sheets from the best cheat sheet repositories, and
+one of them is [36mLearn X in Y[0m, a repository of concise documentation devoted
+to learning programming languages from scratch (and not only them).
+
+If you want start learning a new programming language, do (use less -R because
+the output could be quite big):
+
+ [36mcurl cht.sh/elixir/[0m[32m:learn[0m [36m| less -R[0m
+
+Or simply [32m:learn[0m with cht.sh (you don't need [32mless -R[0m here, because
+[36mcht.sh[0m starts pager if needed automatically):
+
+ [2mcht.sh/elixir>[0m [32m:learn[0m
+
+
+[38;5;172m## Programming languages questions[0m
+
+One of the most important features of cheat.sh is that you can ask it any
+questions about programming languages and instantly get answers on them. You
+can use both direct HTTP queries or the cht.sh client for that:
+
+ [36mcurl cht.sh/[0m[32mpython/reverse+list[0m
- $ curl cheat.sh/TOPIC show cheat sheet on the TOPIC
- $ curl cheat.sh/TOPIC/SUB show cheat sheet on the SUB topic in TOPIC
- $ curl cheat.sh/~KEYWORD search cheat sheets for KEYWORD
+ [2mcht.sh/python>[0m [32mreverse list[0m
-Options:
+In the latter case you don't need + to separate the words in the query, you can
+do it in a more natural way, with spaces.
- ?OPTIONS
+If context in the cht.sh shell is not specified, you have to write the
+programming language name as the first word in the query:
- q quiet mode, don't show github/twitter buttons
- T text only, no ANSI sequences
- style=STYLE color style
+ [2mcht.sh>[0m [32mpython reverse list[0m
- c do not comment text, do not shift code (QUERY+ only)
- C do not comment text, shift code (QUERY+ only)
- Q code only, don't show text (QUERY+ only)
+But if you are using only one programming language and all queries are about
+it, it's better to change the current context and
-Options can be combined together in this way:
- curl 'cheat.sh/for?qT&style=bw'
-
-(when using & in shell, don't forget to specify the quotes or escape & with \)
+[38;5;172m## Comments[0m
-Special pages:
+Text in the answers is syntactically formatted as comment in the correspondent
+programming language
- :help this page
- :list list all cheat sheets
- :post how to post new cheat sheet
- :cht.sh shell client (cht.sh)
- :bash_completion bash function for tab completion
- :styles list of color styles
- :styles-demo show color styles usage examples
+When using cht.sh, you can copy the result of the last query into the selection
+buffer (you may also call it "clibpoard") using [32mC[0m (or [32mc[0m, with text):
-Shell client:
+ [36mcht.sh/python> reverse list[0m
+ [2m...[0m
+ [36mcht.sh/python>[0m [32mC[0m
+ [2m1 lines copied[0m
- $ curl https://cht.sh/:cht.sh > ~/bin/cht.sh
- $ chmod +x ~/bin/cht.sh
- $ cht.sh python :learn
- $ cht.sh --shell
-Tab completion:
+[38;5;172m## bash TAB-completion for cht.sh[0m
- $ mkdir -p ~/.bash.d/
- $ curl cheat.sh/:bash_completion > ~/.bash.d/cht.sh
- $ . ~/.bash.d/cht.sh
- $ echo '. ~/.bash.d/cht.sh' >> ~/.bashrc
+One of the advantages of the [36mcht.sh[0m client comparing to plain curl is that you
+can use TAB completion when writing its queries in [36mbash[0m
+(other supported shells: [36mzsh[0m and [36mfish[0m).
-Editor integration:
+Install the TAB completion script for that. Assuming you use bash, you have to do:
- :emacs see the page for the Emacs configuration
- :vim see the page for the Vim configuration
+ [36mmkdir -p ~/.bash.d/[0m
+ [36mcurl[0m [32mhttps://cht.sh/:bash_completion[0m [36m> ~/.bash.d/cht.sh[0m
+ [36mecho 'source ~/.bash.d/cht.sh' >> ~/.bashrc[0m
+ [36msource ~/.bash.d/cht.sh[0m
-Search:
- /~snapshot look for "snapshot" in the first level cheat sheets
- /~ssh~passphrase several keywords can be combined together using ~
- /scala/~closure look for "closure" in scala cheat sheets
- /~snapshot/r look for "snapshot" in all cheat sheets recursively
+[38;5;172m## Editor[0m
-You can use special search options after the closing slash:
+You can access cheat.sh directly from editors: [36mVim[0m and [36mEmacs[0m.
+It's a very important feature! You should absolutely like it.
- /~shot/bi case insensitive (i), word boundaries (b)
+[36mImagine:[0m
+instead of switching to your browser, googling, browsing Stack Overflow
+and eventually copying the code snippets you need and later pasting them into
+the editor, you can achieve the same instantly and without leaving
+the editor at all!
-List of search options:
+Here is how it looks like:
- b word boundaries
- i case insensitive search
- r recursive
+1. In Vim, if you have a question while editing a program, you can just type
+your question [36mdirectly in the buffer[0m and press [32mKK[0m. You will get
+the answer to your question in [36mpager[0m. (with [32mKB[0m you'll get the answer
+in a separate [36mbuffer[0m).
-Programming languages topics:
+2. If you like the answer. You can manually paste it from the buffer or
+the pager, or if you are lazy you can use [32mKP[0m to paste it under
+your question ([32mKR[0m will replace your question). If you want the
+answer without the comments, [32mKC[0m replays the last query
+toggling them.
-each programming language topic has the following subptopics:
+You have to install cheat.sh [36mVim/Emacs plugins[0m for the editor support.
+See [32m/:vim[0m or [32m/:emacs[0m with the detailed installation instructions.
- hello hello world + how to start the program
- :learn big cheat sheet for learning language from scratch
- :list list of topics
-Support programming languages:
+[38;5;172m## Feature requests, feedback and contribution[0m
- go
- scala
- rust
- python
- php
+If you want to submit a new community driver repository for cheat.sh please
+open a ticket on the project page on GitHub.
+
+If you want to modify an existing cheat sheet, please check the source of the
+cheat sheet (it is always displayed in the cheat sheet bottom line).
+
+If you want to add a new cheat sheet, add it here:
+[36mhttps://github.com/chubin/cheat.sheets[0m
+
+If you want to suggest a new feature for cheat.sh, or if you've found a bug,
+please open a new issue on github:
+[36mhttps://github.com/chubin/cheat.sh[0m
+
+If you want to get the major project updates, follow @igor_chubin in Twitter
+or this RSS feed: [36mhttps://twitrss.me/twitter_user_to_rss/?user=igor_chubin[0m
diff --git a/tests/results/7 b/tests/results/7
index 5c2a34f..4072487 100644
--- a/tests/results/7
+++ b/tests/results/7
@@ -1,464 +1,85 @@
-#!/bin/sh
-#
-# [X] open section
-# [X] one shot mode
-# [X] usage info
-# [X] dependencies check
-# [X] help
-# [X] yank/y/copy/c
-# [X] Y/C
-# [X] eof problem
-# [X] more
-# [X] stealth mode
-#
-# here are several examples for the stealth mode:
-#
-# zip lists
-# list permutation
-# random list element
-# reverse a list
-# read json from file
-# append string to a file
-# run process in background
-# count words in text counter
-# group elements list
-
-__CHTSH_VERSION=4
-__CHTSH_DATETIME="2018-07-08 22:26:46 +0200"
-
-export LESSSECURE=1
-STEALTH_MAX_SELECTION_LENGTH=5
-
-case `uname -s` in
- Darwin) is_macos=yes ;;
- *) is_macos=no ;;
-esac
-
-# for KSH93
-if echo $KSH_VERSION | grep -q ' 93' && ! local foo 2>/dev/null; then
- alias local=typeset
-fi
-
-get_query_options()
-{
- local query="$*"
- if [ -n "$CHTSH_QUERY_OPTIONS" ]; then
- case $query in
- *\?*) query="$query&${CHTSH_QUERY_OPTIONS}";;
- *) query="$query?${CHTSH_QUERY_OPTIONS}";;
- esac
- fi
- printf "%s" "$query"
-}
-
-do_query()
-{
- local query="$*"
- local b_opts=
- local uri="${CHTSH_URL}/\"\$(get_query_options $query)\""
-
- if [ -e "$HOME/.cht.sh/id" ]; then
- b_opts="-b \"\$HOME/.cht.sh/id\""
- fi
-
- eval curl $b_opts -s $uri > "$TMP1"
-
- if [ -z "$lines" ] || [ "$(wc -l "$TMP1" | awk '{print $1}')" -lt "$lines" ]; then
- cat "$TMP1"
- else
- ${PAGER:-$defpager} "$TMP1"
- fi
-}
-
-prepare_query()
-{
- local section="$1"; shift
- local input="$1"; shift
- local arguments="$1"
-
- local query
- if [ -z "$section" ] || [ x"${input}" != x"${input#/}" ]; then
- query=$(printf %s "$input" | sed 's@ @/@; s@ @+@g')
- else
- query=$(printf %s "$section/$input" | sed 's@ @+@g')
- fi
-
- [ -n "$arguments" ] && arguments="?$arguments"
- printf %s "$query$arguments"
-}
-
-get_list_of_sections()
-{
- curl -s "${CHTSH_URL}"/:list | grep -v '/.*/' | grep '/$' | xargs
-}
-
-gen_random_str()
-(
- len=$1
- if command -v openssl >/dev/null; then
- openssl rand -base64 $(($len*3/4)) | awk -v ORS='' //
- else
- rdev=/dev/urandom
- for d in /dev/{srandom,random,arandom}; do
- test -r $d && rdev=$d
- done
- if command -v hexdump >/dev/null; then
- hexdump -vn $(($len/2)) -e '1/1 "%02X" 1 ""' $rdev
- elif command -v xxd >/dev/null; then
- xxd -l $(($len/2)) -ps $dev | awk -v ORS='' //
- else
- cd /tmp
- s=
- while [ $(echo "$s" | wc -c) -lt $len ]; do
- s="$s$(mktemp -u XXXXXXXXXX)"
- done
- printf %.${len}s "$s"
- fi
- fi
-)
-
-if [ -z "$CHTSH_CONF" ]; then
- CHTSH_CONF="$HOME"/.cht.sh/cht.sh.conf
-fi
-
-if [ -e "$CHTSH_CONF" ]; then
- # shellcheck disable=SC1090,SC2002
- . "$CHTSH_CONF"
-fi
-
-[ -z "$CHTSH_URL" ] && CHTSH_URL=https://cht.sh
-
-# any better test not involving either OS matching or actual query?
-if [ `uname -s` = OpenBSD ] && [ -x /usr/bin/ftp ]; then
- curl() {
- local opt args="-o -"
- while getopts "b:s" opt; do
- case $opt in
- b) args="$args -c $OPTARG";;
- s) args="$args -M -V";;
- *) echo "internal error: unsupported cURL option '$opt'" >&2; exit 1;;
- esac
- done
- shift $(($OPTIND - 1))
- /usr/bin/ftp $args "$@"
- }
-else
- command -v curl >/dev/null || { echo 'DEPENDENCY: install "curl" to use cht.sh' >&2; exit 1; }
- _CURL=$(command -v curl)
- if [ x"$CHTSH_CURL_OPTIONS" != x ]; then
- curl() {
- $_CURL ${CHTSH_CURL_OPTIONS} "$@"
- }
- fi
-fi
-
-
-if [ "$1" = --read ]; then
- read -r a || a=exit
- printf "%s\n" "$a"
- exit 0
-elif [ x"$1" = x--help ] || [ -z "$1" ]; then
- cat </dev/null || echo 'DEPENDENCY: please install "xsel" for "copy"' >&2
-fi
-command -v rlwrap >/dev/null || { echo 'DEPENDENCY: install "rlwrap" to use cht.sh in the shell mode' >&2; exit 1; }
+Options can be combined together in this way:
-mkdir -p "$HOME/.cht.sh/"
-lines=$(tput lines)
+ curl 'cheat.sh/for?qT&style=bw'
+
+(when using & in shell, don't forget to specify the quotes or escape & with \)
-if command -v less >/dev/null; then
- defpager="less -R"
-elif command -v more >/dev/null; then
- defpager=more
-else
- defpager=cat
-fi
+Special pages:
-cmd_cd() {
- if [ $# -eq 0 ]; then
- section=""
- else
- new_section=$(echo "$input" | sed 's/cd *//; s@/*$@@; s@^/*@@')
- if [ -z "$new_section" ] || [ ".." = "$new_section" ]; then
- section=""
- else
- valid_sections=$(get_list_of_sections)
- valid=no; for q in $valid_sections; do [ "$q" = "$new_section/" ] && { valid=yes; break; }; done
- if [ "$valid" = no ]; then
- echo "Invalid section: $new_section"
- echo "Valid sections:"
- echo $valid_sections | xargs printf "%-10s\n" | tr ' ' . | xargs -n 10 | sed 's/\./ /g; s/^/ /'
- else
- section="$new_section"
- fi
- fi
- fi
-}
+ :help this page
+ :list list all cheat sheets
+ :post how to post new cheat sheet
+ :cht.sh shell client (cht.sh)
+ :bash_completion bash function for tab completion
+ :styles list of color styles
+ :styles-demo show color styles usage examples
-cmd_copy() {
- if [ -z "$DISPLAY" ]; then
- echo copy: supported only in the Desktop version
- elif [ -z "$input" ]; then
- echo copy: Make at least one query first.
- else
- curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?T)" > "$TMP1"
- if [ "$is_macos" != yes ]; then
- xsel -bi < "$TMP1"
- else
- cat "$TMP1" | pbcopy
- fi
- echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection"
- fi
-}
+Shell client:
-cmd_ccopy() {
- if [ -z "$DISPLAY" ]; then
- echo copy: supported only in the Desktop version
- elif [ -z "$input" ]; then
- echo copy: Make at least one query first.
- else
- curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?TQ)" > "$TMP1"
- if [ "$is_macos" != yes ]; then
- xsel -bi < "$TMP1"
- else
- cat "$TMP1" | pbcopy
- fi
- echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection"
- fi
-}
+ $ curl https://cht.sh/:cht.sh > ~/bin/cht.sh
+ $ chmod +x ~/bin/cht.sh
+ $ cht.sh python :learn
+ $ cht.sh --shell
-cmd_exit() {
- exit 0
-}
+Tab completion:
-cmd_help() {
- cat < python zip list
- cht.sh/python> zip list
- cht.sh/go> /python zip list
-EOF
-}
+ $ mkdir -p ~/.bash.d/
+ $ curl cheat.sh/:bash_completion > ~/.bash.d/cht.sh
+ $ . ~/.bash.d/cht.sh
+ $ echo '. ~/.bash.d/cht.sh' >> ~/.bashrc
-cmd_hush() {
- mkdir -p $HOME/.cht.sh/ && touch $HOME/.cht.sh/.hushlogin && echo "Initial 'use help' message was disabled"
-}
+Editor integration:
-cmd_id() {
- id_file="$HOME/.cht.sh/id"
+ :emacs see the page for the Emacs configuration
+ :vim see the page for the Vim configuration
- if [ id = "$input" ]; then
- new_id=""
- else
- new_id=$(echo "$input" | sed 's/id *//; s/ *$//; s/ /+/g')
- fi
- if [ "$new_id" = remove ]; then
- if [ -e "$id_file" ]; then
- rm -f -- "$id_file" && echo "id is removed"
- else
- echo "id was not set, so you can't remove it"
- fi
- return
- fi
- if [ -n "$new_id" ] && [ reset != "$new_id" ] && [ $(/bin/echo -n "$new_id" | wc -c) -lt 16 ]; then
- echo "ERROR: $new_id: Too short id. Minimal id length is 16. Use 'id reset' for a random id"
- return
- fi
- if [ -z "$new_id" ]; then
- # if new_id is not specified check if we have some id already
- # if yes, just show it
- # if not, generate a new id
- if [ -e "$id_file" ]; then
- echo $(awk '$6 == "id" {print $NF}' <"$id_file" | tail -n 1)
- return
- else
- new_id=reset
- fi
- fi
- if [ "$new_id" = reset ]; then
- new_id=$(gen_random_str 12)
- else
- echo WARNING: if someone gueses your id, he can read your cht.sh search history
- fi
- if [ -e "$id_file" ] && grep -q '\tid\t[^\t][^\t]*$' "$id_file" 2> /dev/null; then
- sed -i 's/\tid\t[^\t][^\t]*$/ id '"$new_id"'/' "$id_file"
- else
- if ! [ -e "$id_file" ]; then
- printf '#\n\n' > "$id_file"
- fi
- printf ".cht.sh\tTRUE\t/\tTRUE\t0\tid\t$new_id\n" >> "$id_file"
- fi
- echo "$new_id"
-}
+Search:
-cmd_query() {
- query=$(prepare_query "$section" "$input")
- do_query "$query"
-}
+ /~snapshot look for "snapshot" in the first level cheat sheets
+ /~ssh~passphrase several keywords can be combined together using ~
+ /scala/~closure look for "closure" in scala cheat sheets
+ /~snapshot/r look for "snapshot" in all cheat sheets recursively
-cmd_stealth() {
- if [ "$input" != stealth ]; then
- arguments=$(echo "$input" | sed 's/stealth //; s/ /\&/')
- fi
- trap break INT
- if [ "$is_macos" = yes ]; then
- past=$(pbpaste)
- else
- past=$(xsel -o)
- fi
- printf "\033[0;31mstealth:\033[0m you are in the stealth mode; select any text in any window for a query\n"
- printf "\033[0;31mstealth:\033[0m selections longer than $STEALTH_MAX_SELECTION_LENGTH words are ignored\n"
- if [ -n "$arguments" ]; then
- printf "\033[0;31mstealth:\033[0m query arguments: ?$arguments\n"
- fi
- printf "\033[0;31mstealth:\033[0m use ^C to leave this mode\n"
- while true; do
- if [ "$is_macos" = yes ]; then
- current=$(pbpaste)
- else
- current=$(xsel -o)
- fi
- if [ "$past" != "$current" ]; then
- past=$current
- current_text="$(echo $current | tr -c '[a-zA-Z0-9]' ' ')"
- if [ $(echo $current_text | wc -w) -gt "$STEALTH_MAX_SELECTION_LENGTH" ]; then
- echo "\033[0;31mstealth:\033[0m selection length is longer than $STEALTH_MAX_SELECTION_LENGTH words; ignoring"
- continue
- else
- printf "\n\033[0;31mstealth: \033[7m $current_text\033[0m\n"
- query=$(prepare_query "$section" "$current_text" "$arguments")
- do_query "$query"
- fi
- fi
- sleep 1;
- done
- trap - INT
-}
+You can use special search options after the closing slash:
-cmd_update() {
- [ -w "$0" ] || { echo "The script is readonly; please update manually: curl -s "${CHTSH_URL}"/:bash | sudo tee $0"; return; }
- TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
- curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2"
- if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then
- if grep -q ^__CHTSH_VERSION= "$TMP2"; then
- # section was vaildated by us already
- args="--shell $section"
- cp "$TMP2" "$0" && echo "Updated. Restarting..." && rm "$TMP2" && CHEATSH_RESTART=1 exec "$0" $args
- else
- echo "Something went wrong. Please update manually"
- fi
- else
- echo "cht.sh is up to date. No update needed"
- fi
- rm -f "$TMP2" > /dev/null 2>&1
-}
+ /~shot/bi case insensitive (i), word boundaries (b)
-cmd_version() {
- insttime=$(ls -l -- "$0" | sed 's/ */ /g' | cut -d ' ' -f 6-8)
- echo "cht.sh version $__CHTSH_VERSION of $__CHTSH_DATETIME; installed at: $insttime"
- TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
- if curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2"; then
- if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then
- echo "Update needed (type 'update' for that)".
- else
- echo "Up to date. No update needed"
- fi
- fi
- rm -f "$TMP2" > /dev/null 2>&1
-}
+List of search options:
-TMP1=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
-trap 'rm -f $TMP1 $TMP2' EXIT
-trap 'true' INT
+ b word boundaries
+ i case insensitive search
+ r recursive
-if ! [ -e "$HOME/.cht.sh/.hushlogin" ] && [ -z "$this_query" ]; then
- echo "type 'help' for the cht.sh shell help"
-fi
+Programming languages topics:
-while true; do
- if [ "$section" != "" ]; then
- full_prompt="$prompt/$section> "
- else
- full_prompt="$prompt> "
- fi
+each programming language topic has the following subptopics:
- input=$(
- rlwrap -H "$HOME/.cht.sh/history" -pgreen -C cht.sh -S "$full_prompt" sh "$0" --read | sed 's/ *#.*//'
- )
+ hello hello world + how to start the program
+ :learn big cheat sheet for learning language from scratch
+ :list list of topics
- cmd_name=${input%% *}
- cmd_args=${input#* }
- case $cmd_name in
- "") continue;; # skip empty input lines
- '?'|h|help) cmd_name=help;;
- hush) cmd_name=hush;;
- cd) cmd_name=cd;;
- exit|quit) cmd_name=exit;;
- copy|yank|c|y) cmd_name=copy;;
- ccopy|cc|C|Y) cmd_name=ccopy;;
- id) cmd_name=id;;
- stealth) cmd_name=stealth;;
- update) cmd_name=update;;
- version) cmd_name=version;;
- *) cmd_name="query $cmd_name";;
- esac
- cmd_$cmd_name $cmd_args
-done
+Support programming languages:
+
+ go
+ scala
+ rust
+ python
+ php
diff --git a/tests/results/8 b/tests/results/8
index cc8bdf8..5c2a34f 100644
--- a/tests/results/8
+++ b/tests/results/8
@@ -1,16 +1,464 @@
-[38;5;246m# How do I copy a file in Python?[39m
-[38;5;246m# [39m
-[38;5;246m# shutil (http://docs.python.org/2/library/shutil.html) has many methods[39m
-[38;5;246m# you can use. One of which is:[39m
+#!/bin/sh
+#
+# [X] open section
+# [X] one shot mode
+# [X] usage info
+# [X] dependencies check
+# [X] help
+# [X] yank/y/copy/c
+# [X] Y/C
+# [X] eof problem
+# [X] more
+# [X] stealth mode
+#
+# here are several examples for the stealth mode:
+#
+# zip lists
+# list permutation
+# random list element
+# reverse a list
+# read json from file
+# append string to a file
+# run process in background
+# count words in text counter
+# group elements list
-[38;5;70;01mfrom[39;00m[38;5;252m [39m[38;5;68;04mshutil[39;00m[38;5;252m [39m[38;5;70;01mimport[39;00m[38;5;252m [39m[38;5;252mcopyfile[39m
+__CHTSH_VERSION=4
+__CHTSH_DATETIME="2018-07-08 22:26:46 +0200"
-[38;5;252mcopyfile[39m[38;5;252m([39m[38;5;252msrc[39m[38;5;252m,[39m[38;5;252m [39m[38;5;252mdst[39m[38;5;252m)[39m
+export LESSSECURE=1
+STEALTH_MAX_SELECTION_LENGTH=5
-[38;5;246m# Copy the contents of the file named src to a file named dst. The[39m
-[38;5;246m# destination location must be writable; otherwise, an IOError exception[39m
-[38;5;246m# will be raised. If dst already exists, it will be replaced. Special[39m
-[38;5;246m# files such as character or block devices and pipes cannot be copied[39m
-[38;5;246m# with this function. src and dst are path names given as strings.[39m
-[38;5;246m# [39m
-[38;5;246m# [Swati] [so/q/123198] [cc by-sa 3.0][39m
+case `uname -s` in
+ Darwin) is_macos=yes ;;
+ *) is_macos=no ;;
+esac
+
+# for KSH93
+if echo $KSH_VERSION | grep -q ' 93' && ! local foo 2>/dev/null; then
+ alias local=typeset
+fi
+
+get_query_options()
+{
+ local query="$*"
+ if [ -n "$CHTSH_QUERY_OPTIONS" ]; then
+ case $query in
+ *\?*) query="$query&${CHTSH_QUERY_OPTIONS}";;
+ *) query="$query?${CHTSH_QUERY_OPTIONS}";;
+ esac
+ fi
+ printf "%s" "$query"
+}
+
+do_query()
+{
+ local query="$*"
+ local b_opts=
+ local uri="${CHTSH_URL}/\"\$(get_query_options $query)\""
+
+ if [ -e "$HOME/.cht.sh/id" ]; then
+ b_opts="-b \"\$HOME/.cht.sh/id\""
+ fi
+
+ eval curl $b_opts -s $uri > "$TMP1"
+
+ if [ -z "$lines" ] || [ "$(wc -l "$TMP1" | awk '{print $1}')" -lt "$lines" ]; then
+ cat "$TMP1"
+ else
+ ${PAGER:-$defpager} "$TMP1"
+ fi
+}
+
+prepare_query()
+{
+ local section="$1"; shift
+ local input="$1"; shift
+ local arguments="$1"
+
+ local query
+ if [ -z "$section" ] || [ x"${input}" != x"${input#/}" ]; then
+ query=$(printf %s "$input" | sed 's@ @/@; s@ @+@g')
+ else
+ query=$(printf %s "$section/$input" | sed 's@ @+@g')
+ fi
+
+ [ -n "$arguments" ] && arguments="?$arguments"
+ printf %s "$query$arguments"
+}
+
+get_list_of_sections()
+{
+ curl -s "${CHTSH_URL}"/:list | grep -v '/.*/' | grep '/$' | xargs
+}
+
+gen_random_str()
+(
+ len=$1
+ if command -v openssl >/dev/null; then
+ openssl rand -base64 $(($len*3/4)) | awk -v ORS='' //
+ else
+ rdev=/dev/urandom
+ for d in /dev/{srandom,random,arandom}; do
+ test -r $d && rdev=$d
+ done
+ if command -v hexdump >/dev/null; then
+ hexdump -vn $(($len/2)) -e '1/1 "%02X" 1 ""' $rdev
+ elif command -v xxd >/dev/null; then
+ xxd -l $(($len/2)) -ps $dev | awk -v ORS='' //
+ else
+ cd /tmp
+ s=
+ while [ $(echo "$s" | wc -c) -lt $len ]; do
+ s="$s$(mktemp -u XXXXXXXXXX)"
+ done
+ printf %.${len}s "$s"
+ fi
+ fi
+)
+
+if [ -z "$CHTSH_CONF" ]; then
+ CHTSH_CONF="$HOME"/.cht.sh/cht.sh.conf
+fi
+
+if [ -e "$CHTSH_CONF" ]; then
+ # shellcheck disable=SC1090,SC2002
+ . "$CHTSH_CONF"
+fi
+
+[ -z "$CHTSH_URL" ] && CHTSH_URL=https://cht.sh
+
+# any better test not involving either OS matching or actual query?
+if [ `uname -s` = OpenBSD ] && [ -x /usr/bin/ftp ]; then
+ curl() {
+ local opt args="-o -"
+ while getopts "b:s" opt; do
+ case $opt in
+ b) args="$args -c $OPTARG";;
+ s) args="$args -M -V";;
+ *) echo "internal error: unsupported cURL option '$opt'" >&2; exit 1;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+ /usr/bin/ftp $args "$@"
+ }
+else
+ command -v curl >/dev/null || { echo 'DEPENDENCY: install "curl" to use cht.sh' >&2; exit 1; }
+ _CURL=$(command -v curl)
+ if [ x"$CHTSH_CURL_OPTIONS" != x ]; then
+ curl() {
+ $_CURL ${CHTSH_CURL_OPTIONS} "$@"
+ }
+ fi
+fi
+
+
+if [ "$1" = --read ]; then
+ read -r a || a=exit
+ printf "%s\n" "$a"
+ exit 0
+elif [ x"$1" = x--help ] || [ -z "$1" ]; then
+ cat </dev/null || echo 'DEPENDENCY: please install "xsel" for "copy"' >&2
+fi
+command -v rlwrap >/dev/null || { echo 'DEPENDENCY: install "rlwrap" to use cht.sh in the shell mode' >&2; exit 1; }
+
+mkdir -p "$HOME/.cht.sh/"
+lines=$(tput lines)
+
+if command -v less >/dev/null; then
+ defpager="less -R"
+elif command -v more >/dev/null; then
+ defpager=more
+else
+ defpager=cat
+fi
+
+cmd_cd() {
+ if [ $# -eq 0 ]; then
+ section=""
+ else
+ new_section=$(echo "$input" | sed 's/cd *//; s@/*$@@; s@^/*@@')
+ if [ -z "$new_section" ] || [ ".." = "$new_section" ]; then
+ section=""
+ else
+ valid_sections=$(get_list_of_sections)
+ valid=no; for q in $valid_sections; do [ "$q" = "$new_section/" ] && { valid=yes; break; }; done
+ if [ "$valid" = no ]; then
+ echo "Invalid section: $new_section"
+ echo "Valid sections:"
+ echo $valid_sections | xargs printf "%-10s\n" | tr ' ' . | xargs -n 10 | sed 's/\./ /g; s/^/ /'
+ else
+ section="$new_section"
+ fi
+ fi
+ fi
+}
+
+cmd_copy() {
+ if [ -z "$DISPLAY" ]; then
+ echo copy: supported only in the Desktop version
+ elif [ -z "$input" ]; then
+ echo copy: Make at least one query first.
+ else
+ curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?T)" > "$TMP1"
+ if [ "$is_macos" != yes ]; then
+ xsel -bi < "$TMP1"
+ else
+ cat "$TMP1" | pbcopy
+ fi
+ echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection"
+ fi
+}
+
+cmd_ccopy() {
+ if [ -z "$DISPLAY" ]; then
+ echo copy: supported only in the Desktop version
+ elif [ -z "$input" ]; then
+ echo copy: Make at least one query first.
+ else
+ curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?TQ)" > "$TMP1"
+ if [ "$is_macos" != yes ]; then
+ xsel -bi < "$TMP1"
+ else
+ cat "$TMP1" | pbcopy
+ fi
+ echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection"
+ fi
+}
+
+cmd_exit() {
+ exit 0
+}
+
+cmd_help() {
+ cat < python zip list
+ cht.sh/python> zip list
+ cht.sh/go> /python zip list
+EOF
+}
+
+cmd_hush() {
+ mkdir -p $HOME/.cht.sh/ && touch $HOME/.cht.sh/.hushlogin && echo "Initial 'use help' message was disabled"
+}
+
+cmd_id() {
+ id_file="$HOME/.cht.sh/id"
+
+ if [ id = "$input" ]; then
+ new_id=""
+ else
+ new_id=$(echo "$input" | sed 's/id *//; s/ *$//; s/ /+/g')
+ fi
+ if [ "$new_id" = remove ]; then
+ if [ -e "$id_file" ]; then
+ rm -f -- "$id_file" && echo "id is removed"
+ else
+ echo "id was not set, so you can't remove it"
+ fi
+ return
+ fi
+ if [ -n "$new_id" ] && [ reset != "$new_id" ] && [ $(/bin/echo -n "$new_id" | wc -c) -lt 16 ]; then
+ echo "ERROR: $new_id: Too short id. Minimal id length is 16. Use 'id reset' for a random id"
+ return
+ fi
+ if [ -z "$new_id" ]; then
+ # if new_id is not specified check if we have some id already
+ # if yes, just show it
+ # if not, generate a new id
+ if [ -e "$id_file" ]; then
+ echo $(awk '$6 == "id" {print $NF}' <"$id_file" | tail -n 1)
+ return
+ else
+ new_id=reset
+ fi
+ fi
+ if [ "$new_id" = reset ]; then
+ new_id=$(gen_random_str 12)
+ else
+ echo WARNING: if someone gueses your id, he can read your cht.sh search history
+ fi
+ if [ -e "$id_file" ] && grep -q '\tid\t[^\t][^\t]*$' "$id_file" 2> /dev/null; then
+ sed -i 's/\tid\t[^\t][^\t]*$/ id '"$new_id"'/' "$id_file"
+ else
+ if ! [ -e "$id_file" ]; then
+ printf '#\n\n' > "$id_file"
+ fi
+ printf ".cht.sh\tTRUE\t/\tTRUE\t0\tid\t$new_id\n" >> "$id_file"
+ fi
+ echo "$new_id"
+}
+
+cmd_query() {
+ query=$(prepare_query "$section" "$input")
+ do_query "$query"
+}
+
+cmd_stealth() {
+ if [ "$input" != stealth ]; then
+ arguments=$(echo "$input" | sed 's/stealth //; s/ /\&/')
+ fi
+ trap break INT
+ if [ "$is_macos" = yes ]; then
+ past=$(pbpaste)
+ else
+ past=$(xsel -o)
+ fi
+ printf "\033[0;31mstealth:\033[0m you are in the stealth mode; select any text in any window for a query\n"
+ printf "\033[0;31mstealth:\033[0m selections longer than $STEALTH_MAX_SELECTION_LENGTH words are ignored\n"
+ if [ -n "$arguments" ]; then
+ printf "\033[0;31mstealth:\033[0m query arguments: ?$arguments\n"
+ fi
+ printf "\033[0;31mstealth:\033[0m use ^C to leave this mode\n"
+ while true; do
+ if [ "$is_macos" = yes ]; then
+ current=$(pbpaste)
+ else
+ current=$(xsel -o)
+ fi
+ if [ "$past" != "$current" ]; then
+ past=$current
+ current_text="$(echo $current | tr -c '[a-zA-Z0-9]' ' ')"
+ if [ $(echo $current_text | wc -w) -gt "$STEALTH_MAX_SELECTION_LENGTH" ]; then
+ echo "\033[0;31mstealth:\033[0m selection length is longer than $STEALTH_MAX_SELECTION_LENGTH words; ignoring"
+ continue
+ else
+ printf "\n\033[0;31mstealth: \033[7m $current_text\033[0m\n"
+ query=$(prepare_query "$section" "$current_text" "$arguments")
+ do_query "$query"
+ fi
+ fi
+ sleep 1;
+ done
+ trap - INT
+}
+
+cmd_update() {
+ [ -w "$0" ] || { echo "The script is readonly; please update manually: curl -s "${CHTSH_URL}"/:bash | sudo tee $0"; return; }
+ TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
+ curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2"
+ if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then
+ if grep -q ^__CHTSH_VERSION= "$TMP2"; then
+ # section was vaildated by us already
+ args="--shell $section"
+ cp "$TMP2" "$0" && echo "Updated. Restarting..." && rm "$TMP2" && CHEATSH_RESTART=1 exec "$0" $args
+ else
+ echo "Something went wrong. Please update manually"
+ fi
+ else
+ echo "cht.sh is up to date. No update needed"
+ fi
+ rm -f "$TMP2" > /dev/null 2>&1
+}
+
+cmd_version() {
+ insttime=$(ls -l -- "$0" | sed 's/ */ /g' | cut -d ' ' -f 6-8)
+ echo "cht.sh version $__CHTSH_VERSION of $__CHTSH_DATETIME; installed at: $insttime"
+ TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
+ if curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2"; then
+ if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then
+ echo "Update needed (type 'update' for that)".
+ else
+ echo "Up to date. No update needed"
+ fi
+ fi
+ rm -f "$TMP2" > /dev/null 2>&1
+}
+
+TMP1=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
+trap 'rm -f $TMP1 $TMP2' EXIT
+trap 'true' INT
+
+if ! [ -e "$HOME/.cht.sh/.hushlogin" ] && [ -z "$this_query" ]; then
+ echo "type 'help' for the cht.sh shell help"
+fi
+
+while true; do
+ if [ "$section" != "" ]; then
+ full_prompt="$prompt/$section> "
+ else
+ full_prompt="$prompt> "
+ fi
+
+ input=$(
+ rlwrap -H "$HOME/.cht.sh/history" -pgreen -C cht.sh -S "$full_prompt" sh "$0" --read | sed 's/ *#.*//'
+ )
+
+ cmd_name=${input%% *}
+ cmd_args=${input#* }
+ case $cmd_name in
+ "") continue;; # skip empty input lines
+ '?'|h|help) cmd_name=help;;
+ hush) cmd_name=hush;;
+ cd) cmd_name=cd;;
+ exit|quit) cmd_name=exit;;
+ copy|yank|c|y) cmd_name=copy;;
+ ccopy|cc|C|Y) cmd_name=ccopy;;
+ id) cmd_name=id;;
+ stealth) cmd_name=stealth;;
+ update) cmd_name=update;;
+ version) cmd_name=version;;
+ *) cmd_name="query $cmd_name";;
+ esac
+ cmd_$cmd_name $cmd_args
+done
diff --git a/tests/run-tests.sh b/tests/run-tests.sh
index 66957d9..a8ef970 100644
--- a/tests/run-tests.sh
+++ b/tests/run-tests.sh
@@ -1,23 +1,47 @@
#!/bin/bash
+# 1) start server:
+# without caching:
+# REDIS_HOST=None CHEATSH_PORT=50000 python bin/srv.py
+# (recommended)
+# with caching:
+# REDIS_PREFIX=TEST1 CHEATSH_PORT=50000 python bin/srv.py
+# (for complex search queries + to test caching)
+# 2) configure CHTSH_URL
+# 3) run the script
+
TMP=$(mktemp /tmp/cht.sh.tests-XXXXXXXXXXXXXX)
TMP2=$(mktemp /tmp/cht.sh.tests-XXXXXXXXXXXXXX)
-trap 'rm -rf $TMP $TMP2' EXIT
+TMP3=$(mktemp /tmp/cht.sh.tests-XXXXXXXXXXXXXX)
+trap 'rm -rf $TMP $TMP2 $TMP3' EXIT
+export CHTSH_URL=http://cht.sh:50000
+CHTSH_SCRIPT=$(dirname "$(dirname "$(readlink -f "$0")")")/share/cht.sh.txt
i=0
failed=0
-while read -r test_line; do
+{
+ if [ -z "$1" ]; then
+ cat -n tests.txt
+ else
+ cat -n tests.txt | sed -n "$(echo "$*" | sed 's/ /p; /g;s/$/p/')"
+ fi
+} > "$TMP3"
+while read -r number test_line; do
test_line="${test_line// #.*//}"
- eval "$test_line" > "$TMP"
- diff results/"$i" "$TMP" > "$TMP2"
- if [ "$?" != 0 ]; then
- echo FAILED: [$i] $test_line
+ if [[ $test_line = "cht.sh "* ]]; then
+ test_line="${test_line//cht.sh /}"
+ eval "bash $CHTSH_SCRIPT $test_line" > "$TMP"
+ else
+ eval "curl -s $CHTSH_URL/$test_line" > "$TMP"
+ fi
+ if ! diff results/"$number" "$TMP" > "$TMP2"; then
+ echo "FAILED: [$number] $test_line"
((failed++))
fi
((i++))
-done < tests2.txt
+done < "$TMP3"
-echo TESTS/OK/FAILED "$i/$[i-failed]/$failed"
+echo TESTS/OK/FAILED "$i/$((i-failed))/$failed"
diff --git a/tests/tests.txt b/tests/tests.txt
index 0075fe3..ff69e1e 100644
--- a/tests/tests.txt
+++ b/tests/tests.txt
@@ -1,17 +1,24 @@
-cht.sh python :list
-cht.sh ls
-cht.sh ls?T
-cht.sh btrfs
-cht.sh btrfs~volume
-cht.sh :intro
-cht.sh :help
-cht.sh :cht.sh
+python/:list
+ls
+ls?T
+btrfs
+btrfs~volume # search on page
+:intro
+:help
+:cht.sh
cht.sh python copy file
-cht.sh python/copy+file
-cht.sh python/copy+file?Q
-cht.sh python/copy+file?QT
-cht.sh /
-cht.sh //
-cht.sh python/:learn
-cht.sh latencies
-cht.sh az # chubin/cheat.sheets
+python/copy+file
+python/copy+file?Q
+python/copy+file?QT
+/
+//
+python/:learn
+latencies
+az # chubin/cheat.sheets
+python/rosetta/Substring # rosetta
+python/rosetta/Substring?T # rosetta
+python/rosetta/:list # rosetta
+js/:learn # short names check
+javascript/:learn # short names check
+emacs:go-mode/:list # special editor names
+mkffs.ffatt # unknown