diff --git a/manimlib/animation/composition.py b/manimlib/animation/composition.py index e82ca79e32..0acdd49c2b 100644 --- a/manimlib/animation/composition.py +++ b/manimlib/animation/composition.py @@ -29,6 +29,7 @@ def __init__(self, run_time: float = -1, # If negative, default to sum of inputed animation runtimes lag_ratio: float = 0.0, group: Mobject | None = None, + group_type: type = Group, **kwargs ): self.animations = [prepare_animation(anim) for anim in animations] @@ -38,7 +39,7 @@ def __init__(self, self.lag_ratio = lag_ratio self.group = group if self.group is None: - self.group = Group(*remove_list_redundancies( + self.group = group_type(*remove_list_redundancies( [anim.mobject for anim in self.animations] )) @@ -49,7 +50,7 @@ def __init__(self, **kwargs ) - def get_all_mobjects(self) -> Group: + def get_all_mobjects(self) -> Mobject: return self.group def begin(self) -> None: diff --git a/manimlib/animation/transform_matching_parts.py b/manimlib/animation/transform_matching_parts.py index de32bf89ca..6c608cae7c 100644 --- a/manimlib/animation/transform_matching_parts.py +++ b/manimlib/animation/transform_matching_parts.py @@ -4,26 +4,20 @@ import numpy as np -from manimlib.animation.animation import Animation from manimlib.animation.composition import AnimationGroup from manimlib.animation.fading import FadeInFromPoint from manimlib.animation.fading import FadeOutToPoint from manimlib.animation.fading import FadeTransformPieces -from manimlib.animation.fading import FadeTransform -from manimlib.animation.transform import ReplacementTransform from manimlib.animation.transform import Transform from manimlib.mobject.mobject import Mobject from manimlib.mobject.mobject import Group from manimlib.mobject.svg.string_mobject import StringMobject -from manimlib.mobject.svg.old_tex_mobject import OldTex -from manimlib.mobject.svg.tex_mobject import Tex from manimlib.mobject.types.vectorized_mobject import VGroup from manimlib.mobject.types.vectorized_mobject import VMobject from typing import TYPE_CHECKING if TYPE_CHECKING: - from manimlib.mobject.svg.old_tex_mobject import SingleStringTex from manimlib.scene.scene import Scene @@ -151,13 +145,14 @@ def __init__( match_animation: type = Transform, mismatch_animation: type = Transform, run_time=2, + lag_ratio=0, **kwargs, ): self.source = source self.target = target matched_keys = matched_keys or list() key_map = key_map or dict() - self.anim_config = dict(run_time=run_time, **kwargs) + self.anim_config = dict(**kwargs) # We will progressively build up a list of transforms # from characters in source to those in target. These @@ -208,7 +203,12 @@ def __init__( target_char, source.get_center(), **self.anim_config )) - super().__init__(*self.anims) + super().__init__( + *self.anims, + run_time=run_time, + lag_ratio=lag_ratio, + group_type=VGroup, + ) def add_transform( self, @@ -230,18 +230,10 @@ def add_transform( self.target_chars.remove(char) def find_pairs_with_matching_shapes(self, chars1, chars2) -> list[tuple[VMobject, VMobject]]: - for char in (*chars1, *chars2): - char.save_state() - char.set_height(1) - char.center() result = [] for char1, char2 in it.product(chars1, chars2): - p1 = char1.get_points() - p2 = char2.get_points() - if len(p1) == len(p2) and np.isclose(p1, p2 , atol=1e-1).all(): + if char1.has_same_shape_as(char2): result.append((char1, char2)) - for char in (*chars1, *chars2): - char.restore() return result def clean_up_from_scene(self, scene: Scene) -> None: diff --git a/manimlib/config.py b/manimlib/config.py index 1f8e54f24e..17e203abb8 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -491,4 +491,6 @@ def get_configuration(args: Namespace) -> dict: "presenter_mode": args.presenter_mode, "leave_progress_bars": args.leave_progress_bars, "show_animation_progress": args.show_animation_progress, + "embed_exception_mode": custom_config["embed_exception_mode"], + "embed_error_sound": custom_config["embed_error_sound"], } diff --git a/manimlib/default_config.yml b/manimlib/default_config.yml index 650019835e..bfee529384 100644 --- a/manimlib/default_config.yml +++ b/manimlib/default_config.yml @@ -42,3 +42,5 @@ camera_resolutions: 4k: "3840x2160" default_resolution: "high" fps: 30 +embed_exception_mode: "Verbose" +embed_error_sound: False diff --git a/manimlib/extract_scene.py b/manimlib/extract_scene.py index 64a19db47a..efda54a083 100644 --- a/manimlib/extract_scene.py +++ b/manimlib/extract_scene.py @@ -54,22 +54,11 @@ def prompt_user_for_choice(scene_classes): def get_scene_config(config): - return dict([ - (key, config[key]) - for key in [ - "window_config", - "camera_config", - "file_writer_config", - "skip_animations", - "start_at_animation_number", - "end_at_animation_number", - "leave_progress_bars", - "show_animation_progress", - "preview", - "presenter_mode", - ] - ]) - + scene_parameters = inspect.signature(Scene).parameters.keys() + return { + key: config[key] + for key in set(scene_parameters).intersection(config.keys()) + } def compute_total_frames(scene_class, scene_config): """ diff --git a/manimlib/mobject/mobject.py b/manimlib/mobject/mobject.py index 368a3d11c3..9fa6ebeece 100644 --- a/manimlib/mobject/mobject.py +++ b/manimlib/mobject/mobject.py @@ -657,7 +657,7 @@ def become(self, mobject: Mobject, match_updaters=False): self.match_updaters(mobject) return self - def looks_identical(self, mobject: Mobject): + def looks_identical(self, mobject: Mobject) -> bool: fam1 = self.family_members_with_points() fam2 = mobject.family_members_with_points() if len(fam1) != len(fam2): @@ -667,15 +667,20 @@ def looks_identical(self, mobject: Mobject): if set(d1).difference(d2): return False for key in d1: - eq = (d1[key] == d2[key]) - if isinstance(eq, bool): - if not eq: - return False - else: - if not eq.all(): - return False + if not np.isclose(d1[key], d2[key]).all(): + return False return True + def has_same_shape_as(self, mobject: Mobject) -> bool: + # Normalize both point sets by centering and making height 1 + points1, points2 = ( + (m.get_all_points() - m.get_center()) / m.get_height() + for m in (self, mobject) + ) + if len(points1) != len(points2): + return False + return bool(np.isclose(points1, points2).all()) + # Creating new Mobjects from this one def replicate(self, n: int) -> Group: @@ -1723,7 +1728,7 @@ def lock_matching_data(self, mobject1: Mobject, mobject2: Mobject): for sm, sm1, sm2 in zip(self.get_family(), mobject1.get_family(), mobject2.get_family()): keys = sm.data.keys() & sm1.data.keys() & sm2.data.keys() sm.lock_data(list(filter( - lambda key: np.all(sm1.data[key] == sm2.data[key]), + lambda key: (sm1.data[key] == sm2.data[key]).all(), keys, ))) return self diff --git a/manimlib/mobject/svg/string_mobject.py b/manimlib/mobject/svg/string_mobject.py index 08a3c73dd1..3c81ccc1cd 100644 --- a/manimlib/mobject/svg/string_mobject.py +++ b/manimlib/mobject/svg/string_mobject.py @@ -547,7 +547,7 @@ def build_groups(self) -> VGroup: def select_parts(self, selector: Selector) -> VGroup: specified_substrings = self.get_specified_substrings() - if isinstance(selector, str) and selector not in specified_substrings: + if isinstance(selector, (str, re.Pattern)) and selector not in specified_substrings: return self.select_unisolated_substring(selector) indices_list = self.get_submob_indices_lists_by_selector(selector) return self.build_parts_from_indices_lists(indices_list) @@ -563,11 +563,14 @@ def select_part(self, selector: Selector, index: int = 0) -> VMobject: def substr_to_path_count(self, substr: str) -> int: return len(re.sub(R"\s", "", substr)) - def select_unisolated_substring(self, substr: str) -> VGroup: + def select_unisolated_substring(self, pattern: str | re.Pattern) -> VGroup: + if isinstance(pattern, str): + pattern = re.compile(re.escape(pattern)) result = [] - for match in re.finditer(re.escape(substr), self.string): + for match in re.finditer(pattern, self.string): index = match.start() start = self.substr_to_path_count(self.string[:index]) + substr = match.group() end = start + self.substr_to_path_count(substr) result.append(self[start:end]) return VGroup(*result) diff --git a/manimlib/mobject/svg/svg_mobject.py b/manimlib/mobject/svg/svg_mobject.py index e8ac932bc1..f8d9bc30bd 100644 --- a/manimlib/mobject/svg/svg_mobject.py +++ b/manimlib/mobject/svg/svg_mobject.py @@ -292,15 +292,13 @@ class VMobjectFromSVGPath(VMobject): def __init__( self, path_obj: se.Path, - long_lines: bool = False, should_subdivide_sharp_curves: bool = False, - should_remove_null_curves: bool = False, + should_remove_null_curves: bool = True, **kwargs ): # Get rid of arcs path_obj.approximate_arcs_with_quads() self.path_obj = path_obj - self.long_lines = long_lines self.should_subdivide_sharp_curves = should_subdivide_sharp_curves self.should_remove_null_curves = should_remove_null_curves super().__init__(**kwargs) diff --git a/manimlib/mobject/types/vectorized_mobject.py b/manimlib/mobject/types/vectorized_mobject.py index f7c703b66e..87df2601ed 100644 --- a/manimlib/mobject/types/vectorized_mobject.py +++ b/manimlib/mobject/types/vectorized_mobject.py @@ -148,11 +148,6 @@ def add(self, *vmobjects: VMobject): raise Exception("All submobjects must be of type VMobject") super().add(*vmobjects) - def copy(self, deep: bool = False) -> VMobject: - result = super().copy(deep) - result.shader_wrapper_list = [sw.copy() for sw in self.shader_wrapper_list] - return result - # Colors def init_colors(self): self.set_fill( @@ -809,6 +804,11 @@ def get_unit_normal(self) -> Vect3: # Alignment def align_points(self, vmobject: VMobject): if self.get_num_points() == len(vmobject.get_points()): + # If both have fill, and they have the same shape, just + # give them the same triangulation so that it's not recalculated + # needlessly throughout an animation + if self.has_fill() and vmobject.has_fill() and self.has_same_shape_as(vmobject): + vmobject.triangulation = self.triangulation return for mob in self, vmobject: @@ -1077,14 +1077,6 @@ def init_shader_data(self): render_primitive=self.render_primitive, ) - self.shader_wrapper_list = [ - self.stroke_shader_wrapper.copy(), # Use for back stroke - self.fill_shader_wrapper.copy(), - self.stroke_shader_wrapper.copy(), - ] - for sw in self.shader_wrapper_list: - sw.uniforms = self.uniforms - def refresh_shader_wrapper_id(self): for wrapper in [self.fill_shader_wrapper, self.stroke_shader_wrapper]: wrapper.refresh_id() @@ -1107,30 +1099,29 @@ def get_shader_wrapper_list(self) -> list[ShaderWrapper]: # Build up data lists fill_shader_wrappers = [] stroke_shader_wrappers = [] - back_stroke_shader_wrappers = [] for submob in self.family_members_with_points(): if submob.has_fill(): fill_shader_wrappers.append(submob.get_fill_shader_wrapper()) if submob.has_stroke(): - ssw = submob.get_stroke_shader_wrapper() - if submob.draw_stroke_behind_fill: - back_stroke_shader_wrappers.append(ssw) - else: - stroke_shader_wrappers.append(ssw) + stroke_shader_wrappers.append(submob.get_stroke_shader_wrapper()) + if submob.draw_stroke_behind_fill: + self.draw_stroke_behind_fill = True - # Combine data lists - sw_lists = [ - back_stroke_shader_wrappers, - fill_shader_wrappers, - stroke_shader_wrappers, - ] - for sw, sw_list in zip(self.shader_wrapper_list, sw_lists): + self_sws = [self.fill_shader_wrapper, self.stroke_shader_wrapper] + sw_lists = [fill_shader_wrappers, stroke_shader_wrappers] + for sw, sw_list in zip(self_sws, sw_lists): if not sw_list: + sw.vert_data = resize_array(sw.vert_data, 0) continue - sw.read_in(*sw_list) + if sw is sw_list[0]: + sw.combine_with(*sw_list[1:]) + else: + sw.read_in(*sw_list) sw.depth_test = any(sw.depth_test for sw in sw_list) sw.uniforms.update(sw_list[0].uniforms) - return list(filter(lambda sw: len(sw.vert_data) > 0, self.shader_wrapper_list)) + if self.draw_stroke_behind_fill: + self_sws.reverse() + return [sw for sw in self_sws if len(sw.vert_data) > 0] def get_stroke_shader_data(self) -> np.ndarray: points = self.get_points() diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index 3be11916a0..55b8c37d2c 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -16,15 +16,18 @@ from tqdm import tqdm as ProgressDisplay from manimlib.animation.animation import prepare_animation +from manimlib.animation.fading import VFadeInThenOut from manimlib.camera.camera import Camera from manimlib.config import get_module from manimlib.constants import ARROW_SYMBOLS from manimlib.constants import DEFAULT_WAIT_TIME from manimlib.constants import COMMAND_MODIFIER from manimlib.constants import SHIFT_MODIFIER +from manimlib.constants import RED from manimlib.event_handler import EVENT_DISPATCHER from manimlib.event_handler.event_type import EventType from manimlib.logger import log +from manimlib.mobject.frame import FullScreenRectangle from manimlib.mobject.mobject import _AnimationBuilder from manimlib.mobject.mobject import Group from manimlib.mobject.mobject import Mobject @@ -34,7 +37,6 @@ from manimlib.scene.scene_file_writer import SceneFileWriter from manimlib.utils.family_ops import extract_mobject_family_members from manimlib.utils.family_ops import recursive_mobject_remove -from manimlib.utils.iterables import list_difference_update from typing import TYPE_CHECKING @@ -74,6 +76,8 @@ def __init__( preview: bool = True, presenter_mode: bool = False, show_animation_progress: bool = False, + embed_exception_mode: str = "", + embed_error_sound: bool = False, ): self.skip_animations = skip_animations self.always_update_mobjects = always_update_mobjects @@ -83,6 +87,8 @@ def __init__( self.preview = preview self.presenter_mode = presenter_mode self.show_animation_progress = show_animation_progress + self.embed_exception_mode = embed_exception_mode + self.embed_error_sound = embed_error_sound self.camera_config = {**self.default_camera_config, **camera_config} self.window_config = {**self.default_window_config, **window_config} @@ -249,6 +255,22 @@ def post_cell_func(): shell.events.register("post_run_cell", post_cell_func) + # Flash border, and potentially play sound, on exceptions + def custom_exc(shell, etype, evalue, tb, tb_offset=None): + # still show the error don't just swallow it + shell.showtraceback((etype, evalue, tb), tb_offset=tb_offset) + if self.embed_error_sound: + os.system("printf '\a'") + self.play(VFadeInThenOut( + FullScreenRectangle().set_stroke(RED, 30).set_fill(opacity=0), + run_time=0.5, + )) + + shell.set_custom_exc((Exception,), custom_exc) + + # Set desired exception mode + shell.magic(f"xmode {self.embed_exception_mode}") + # Launch shell shell( local_ns=local_ns, diff --git a/manimlib/shader_wrapper.py b/manimlib/shader_wrapper.py index a053d9ca65..13fb3a2894 100644 --- a/manimlib/shader_wrapper.py +++ b/manimlib/shader_wrapper.py @@ -131,7 +131,8 @@ def replace_code(self, old: str, new: str) -> None: self.refresh_id() def combine_with(self, *shader_wrappers: ShaderWrapper) -> ShaderWrapper: - self.read_in(self.copy(), *shader_wrappers) + if len(shader_wrappers) > 0: + self.read_in(self.copy(), *shader_wrappers) return self def read_in(self, *shader_wrappers: ShaderWrapper) -> ShaderWrapper: diff --git a/manimlib/utils/tex.py b/manimlib/utils/tex.py index e3ea53bbdc..1a70e42921 100644 --- a/manimlib/utils/tex.py +++ b/manimlib/utils/tex.py @@ -1,57 +1,41 @@ from __future__ import annotations import re -from functools import lru_cache - -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from typing import List, Tuple - - -@lru_cache(maxsize=1) -def get_pattern_symbol_count_pairs() -> List[Tuple[str, int]]: - from manimlib.utils.tex_to_symbol_count import TEX_TO_SYMBOL_COUNT - - # Gather all keys of previous map, grouped by common value - count_to_tex_list = dict() - for command, num in TEX_TO_SYMBOL_COUNT.items(): - if num not in count_to_tex_list: - count_to_tex_list[num] = [] - count_to_tex_list[num].append(command) - - # Create a list associating each count with a regular expression - # that will find any tex commands matching that list - pattern_symbol_count_pairs = list() - - # Account for patterns like \begin{align} and \phantom{thing} - # which, together with the bracketed content account for zero paths. - # Deliberately put this first in the list - tex_list = ["begin", "end", "phantom"] - pattern_symbol_count_pairs.append( - ("|".join(r"\\" + s + r"\{[^\\}]+\}" for s in tex_list), 0) - ) - - for count, tex_list in count_to_tex_list.items(): - pattern = "|".join(r"\\" + s for s in tex_list) - pattern_symbol_count_pairs.append((pattern, count)) - # Assume all other expressions of the form \thing are drawn with one path - # Deliberately put this last in the list - pattern_symbol_count_pairs.append((r"\\[a-zA-Z]+", 1)) - - return pattern_symbol_count_pairs +from manimlib.utils.tex_to_symbol_count import TEX_TO_SYMBOL_COUNT def num_tex_symbols(tex: str) -> int: """ This function attempts to estimate the number of symbols that a given string of tex would produce. + + Warning, it may not behave perfectly """ + # First, remove patterns like \begin{align}, \phantom{thing}, + # \begin{array}{cc}, etc. + pattern = "|".join( + r"(\\" + s + ")" + r"(\{\w+\})?(\{\w+\})?(\[\w+\])?" + for s in ["begin", "end", "phantom"] + ) + for tup in re.findall(pattern, tex): + tex = tex.replace("".join(tup), " ") + + # Progressively count the symbols associated with certain tex commands, + # and remove those commands from the string, adding the number of symbols + # that command creates total = 0 - for pattern, count in get_pattern_symbol_count_pairs(): - total += count * len(re.findall(pattern, tex)) - tex = re.sub(pattern, " ", tex) # Remove that pattern + + # Start with the special case \sqrt[number] + for substr in re.findall(r"\\sqrt\[[0-9]+\]", tex): + total += len(substr) - 5 # e.g. \sqrt[3] is 3 symbols + tex = tex.replace(substr, " ") + + general_command = r"\\[a-zA-Z!,-/:;<>]+" + for substr in re.findall(general_command, tex): + total += TEX_TO_SYMBOL_COUNT.get(substr, 1) + tex = tex.replace(substr, " ") # Count remaining characters - total += sum(map(lambda c: c not in "^{} \n\t_$", tex)) + total += sum(map(lambda c: c not in "^{} \n\t_$\\&", tex)) return total diff --git a/manimlib/utils/tex_to_symbol_count.py b/manimlib/utils/tex_to_symbol_count.py index e858b57265..c314b5dda4 100644 --- a/manimlib/utils/tex_to_symbol_count.py +++ b/manimlib/utils/tex_to_symbol_count.py @@ -1,182 +1,181 @@ TEX_TO_SYMBOL_COUNT = { - "!": 0, - ",": 0, - ",": 0, - "-": 0, - "-": 0, - "/": 0, - ":": 0, - ";": 0, - ";": 0, - ">": 0, - "aa": 0, - "AA": 0, - "ae": 0, - "AE": 0, - "arccos": 6, - "arcsin": 6, - "arctan": 6, - "arg": 3, - "author": 0, - "bf": 0, - "bibliography": 0, - "bibliographystyle": 0, - "big": 0, - "Big": 0, - "bigodot": 4, - "bigoplus": 5, - "bigskip": 0, - "bmod": 3, - "boldmath": 0, - "bottomfraction": 2, - "bowtie": 2, - "cal": 0, - "cdots": 3, - "centering": 0, - "cite": 2, - "cong": 2, - "contentsline": 0, - "cos": 3, - "cosh": 4, - "cot": 3, - "coth": 4, - "csc": 3, - "date": 0, - "dblfloatpagefraction": 2, - "dbltopfraction": 2, - "ddots": 3, - "deg": 3, - "det": 3, - "dim": 3, - "displaystyle": 0, - "div": 2, - "doteq": 2, - "dotfill": 0, - "emph": 0, - "exp": 3, - "fbox": 4, - "floatpagefraction": 2, - "flushbottom": 0, - "footnotesize": 0, - "footnotetext": 0, - "frame": 2, - "framebox": 4, - "fussy": 0, - "gcd": 3, - "ghost": 0, - "glossary": 0, - "hfill": 0, - "hom": 3, - "hookleftarrow": 2, - "hookrightarrow": 2, - "hrulefill": 0, - "huge": 0, - "Huge": 0, - "hyphenation": 0, - "iff": 2, - "Im": 2, - "index": 0, - "inf": 3, - "it": 0, - "ker": 3, - "l": 0, - "L": 0, - "label": 0, - "large": 0, - "Large": 0, - "LARGE": 0, - "ldots": 3, - "lefteqn": 0, - "lg": 2, - "lim": 3, - "liminf": 6, - "limsup": 6, - "linebreak": 0, - "ln": 2, - "log": 3, - "longleftarrow": 2, - "Longleftarrow": 2, - "longleftrightarrow": 2, - "Longleftrightarrow": 2, - "longmapsto": 3, - "longrightarrow": 2, - "Longrightarrow": 2, - "makebox": 0, - "mapsto": 2, - "markright": 0, - "max": 3, - "mbox": 0, - "medskip": 0, - "min": 3, - "mit": 0, - "models": 2, - "ne": 2, - "neq": 2, - "newline": 0, - "noindent": 0, - "nolinebreak": 0, - "nonumber": 0, - "nopagebreak": 0, - "normalmarginpar": 0, - "normalsize": 0, - "notin": 2, - "o": 0, - "O": 0, - "obeycr": 0, - "oe": 0, - "OE": 0, - "overbrace": 4, - "pagebreak": 0, - "pagenumbering": 0, - "pageref": 2, - "pmod": 5, - "Pr": 2, - "protect": 0, - "qquad": 0, - "quad": 0, - "raggedbottom": 0, - "raggedleft": 0, - "raggedright": 0, - "Re": 2, - "ref": 2, - "restorecr": 0, - "reversemarginpar": 0, - "rm": 0, - "sc": 0, - "scriptscriptstyle": 0, - "scriptsize": 0, - "scriptstyle": 0, - "sec": 3, - "sf": 0, - "shortstack": 0, - "sin": 3, - "sinh": 4, - "sl": 0, - "sloppy": 0, - "small": 0, - "Small": 0, - "smallskip": 0, - "sqrt": 2, - "ss": 0, - "sup": 3, - "tan": 3, - "tanh": 4, - "textbf": 0, - "textfraction": 2, - "textstyle": 0, - "thicklines": 0, - "thinlines": 0, - "thinspace": 0, - "tiny": 0, - "title": 0, - "today": 15, - "topfraction": 2, - "tt": 0, - "typeout": 0, - "unboldmath": 0, - "underbrace": 6, - "underline": 0, - "value": 0, - "vdots": 3, - "vline": 0 + R"\!": 0, + R"\,": 0, + R"\-": 0, + R"\/": 0, + R"\:": 0, + R"\;": 0, + R"\>": 0, + R"\aa": 0, + R"\AA": 0, + R"\ae": 0, + R"\AE": 0, + R"\arccos": 6, + R"\arcsin": 6, + R"\arctan": 6, + R"\arg": 3, + R"\author": 0, + R"\bf": 0, + R"\bibliography": 0, + R"\bibliographystyle": 0, + R"\big": 0, + R"\Big": 0, + R"\bigodot": 4, + R"\bigoplus": 5, + R"\bigskip": 0, + R"\bmod": 3, + R"\boldmath": 0, + R"\bottomfraction": 2, + R"\bowtie": 2, + R"\cal": 0, + R"\cdots": 3, + R"\centering": 0, + R"\cite": 2, + R"\cong": 2, + R"\contentsline": 0, + R"\cos": 3, + R"\cosh": 4, + R"\cot": 3, + R"\coth": 4, + R"\csc": 3, + R"\date": 0, + R"\dblfloatpagefraction": 2, + R"\dbltopfraction": 2, + R"\ddots": 3, + R"\deg": 3, + R"\det": 3, + R"\dim": 3, + R"\displaystyle": 0, + R"\div": 2, + R"\doteq": 2, + R"\dotfill": 0, + R"\emph": 0, + R"\exp": 3, + R"\fbox": 4, + R"\floatpagefraction": 2, + R"\flushbottom": 0, + R"\footnotesize": 0, + R"\footnotetext": 0, + R"\frame": 2, + R"\framebox": 4, + R"\fussy": 0, + R"\gcd": 3, + R"\ghost": 0, + R"\glossary": 0, + R"\hfill": 0, + R"\hom": 3, + R"\hookleftarrow": 2, + R"\hookrightarrow": 2, + R"\hrulefill": 0, + R"\huge": 0, + R"\Huge": 0, + R"\hyphenation": 0, + R"\iff": 2, + R"\Im": 2, + R"\index": 0, + R"\inf": 3, + R"\it": 0, + R"\ker": 3, + R"\l": 0, + R"\L": 0, + R"\label": 0, + R"\large": 0, + R"\Large": 0, + R"\LARGE": 0, + R"\ldots": 3, + R"\lefteqn": 0, + R"\left": 0, + R"\lg": 2, + R"\lim": 3, + R"\liminf": 6, + R"\limsup": 6, + R"\linebreak": 0, + R"\ln": 2, + R"\log": 3, + R"\longleftarrow": 2, + R"\Longleftarrow": 2, + R"\longleftrightarrow": 2, + R"\Longleftrightarrow": 2, + R"\longmapsto": 3, + R"\longrightarrow": 2, + R"\Longrightarrow": 2, + R"\makebox": 0, + R"\mapsto": 2, + R"\markright": 0, + R"\max": 3, + R"\mbox": 0, + R"\medskip": 0, + R"\min": 3, + R"\mit": 0, + R"\models": 2, + R"\ne": 2, + R"\neq": 2, + R"\newline": 0, + R"\noindent": 0, + R"\nolinebreak": 0, + R"\nonumber": 0, + R"\nopagebreak": 0, + R"\normalmarginpar": 0, + R"\normalsize": 0, + R"\notin": 2, + R"\o": 0, + R"\O": 0, + R"\obeycr": 0, + R"\oe": 0, + R"\OE": 0, + R"\overbrace": 4, + R"\pagebreak": 0, + R"\pagenumbering": 0, + R"\pageref": 2, + R"\pmod": 5, + R"\Pr": 2, + R"\protect": 0, + R"\qquad": 0, + R"\quad": 0, + R"\raggedbottom": 0, + R"\raggedleft": 0, + R"\raggedright": 0, + R"\Re": 2, + R"\ref": 2, + R"\restorecr": 0, + R"\reversemarginpar": 0, + R"\right": 0, + R"\rm": 0, + R"\sc": 0, + R"\scriptscriptstyle": 0, + R"\scriptsize": 0, + R"\scriptstyle": 0, + R"\sec": 3, + R"\sf": 0, + R"\shortstack": 0, + R"\sin": 3, + R"\sinh": 4, + R"\sl": 0, + R"\sloppy": 0, + R"\small": 0, + R"\Small": 0, + R"\smallskip": 0, + R"\sqrt": 2, + R"\ss": 0, + R"\sup": 3, + R"\tan": 3, + R"\tanh": 4, + R"\textbf": 0, + R"\textfraction": 2, + R"\textstyle": 0, + R"\thicklines": 0, + R"\thinlines": 0, + R"\thinspace": 0, + R"\tiny": 0, + R"\title": 0, + R"\today": 15, + R"\topfraction": 2, + R"\tt": 0, + R"\typeout": 0, + R"\unboldmath": 0, + R"\underbrace": 6, + R"\underline": 0, + R"\value": 0, + R"\vdots": 3, + R"\vline": 0 } \ No newline at end of file