From 66eb136346cc0bb3fe8ba3b3cf9431ce95ba0d5c Mon Sep 17 00:00:00 2001 From: Ben Huddart Date: Thu, 23 Jul 2026 12:00:14 +0100 Subject: [PATCH 1/4] style: apply black formatting to modules touched by contour/heatmap work Pure formatting (mostly quote normalization) with no functional changes, separated out so the feature diff stays reviewable. Co-Authored-By: Claude Fable 5 --- examples/advanced/__init__.py | 44 +- examples/basic/__init__.py | 16 +- examples/run_all.py | 19 +- src/gleplot/__init__.py | 164 +++--- src/gleplot/axes.py | 689 +++++++++++++----------- src/gleplot/config.py | 103 ++-- src/gleplot/figure.py | 711 +++++++++++++------------ src/gleplot/gui/data/panel.py | 11 +- src/gleplot/gui/panels/axes_panel.py | 7 +- src/gleplot/gui/panels/series_panel.py | 113 +++- src/gleplot/parser/recognizer.py | 280 ++++++---- src/gleplot/parser/tables.py | 250 +++++++-- src/gleplot/writer.py | 694 +++++++++++++----------- tests/gui/test_data_panel.py | 8 +- tests/gui/test_panels.py | 8 +- tests/parser/_golden_battery.py | 153 ++++-- tests/parser/test_fixed_point.py | 15 +- tests/parser/test_recognizer.py | 29 +- tests/unit/test_serialization.py | 175 +++--- 19 files changed, 2066 insertions(+), 1423 deletions(-) diff --git a/examples/advanced/__init__.py b/examples/advanced/__init__.py index 17ab678..1f10f48 100644 --- a/examples/advanced/__init__.py +++ b/examples/advanced/__init__.py @@ -29,26 +29,26 @@ from .data_prefix import example_data_prefix __all__ = [ - 'example_fill_between', - 'example_fill_between_conditional', - 'example_log_scale', - 'example_combined_plot', - 'example_multiple_styles', - 'example_sharex_stacked', - 'example_sharey_sidebyside', - 'example_both_shared', - 'example_residual_plot', - 'example_comparison_with_without', - 'example_side_by_side', - 'example_stacked', - 'example_2x2_grid', - 'example_1x3_comparison', - 'example_errorbar_from_file', - 'example_dual_axis_from_file', - 'example_line_overlay_from_file', - 'example_text_annotations', - 'example_per_element_styling', - 'example_batch_figures', - 'example_line_from_file', - 'example_data_prefix', + "example_fill_between", + "example_fill_between_conditional", + "example_log_scale", + "example_combined_plot", + "example_multiple_styles", + "example_sharex_stacked", + "example_sharey_sidebyside", + "example_both_shared", + "example_residual_plot", + "example_comparison_with_without", + "example_side_by_side", + "example_stacked", + "example_2x2_grid", + "example_1x3_comparison", + "example_errorbar_from_file", + "example_dual_axis_from_file", + "example_line_overlay_from_file", + "example_text_annotations", + "example_per_element_styling", + "example_batch_figures", + "example_line_from_file", + "example_data_prefix", ] diff --git a/examples/basic/__init__.py b/examples/basic/__init__.py index cae6bfe..55bb22e 100644 --- a/examples/basic/__init__.py +++ b/examples/basic/__init__.py @@ -12,12 +12,12 @@ ) __all__ = [ - 'example_basic_line_plot', - 'example_scatter_plot', - 'example_bar_chart', - 'example_symmetric_error_bars', - 'example_asymmetric_error_bars', - 'example_horizontal_error_bars', - 'example_errorbar_with_line', - 'example_combined_errorbars', + "example_basic_line_plot", + "example_scatter_plot", + "example_bar_chart", + "example_symmetric_error_bars", + "example_asymmetric_error_bars", + "example_horizontal_error_bars", + "example_errorbar_with_line", + "example_combined_errorbars", ] diff --git a/examples/run_all.py b/examples/run_all.py index 0c07e7b..b810ac1 100644 --- a/examples/run_all.py +++ b/examples/run_all.py @@ -4,7 +4,7 @@ from pathlib import Path # Add src to path -sys.path.insert(0, str(Path(__file__).parent.parent / 'src')) +sys.path.insert(0, str(Path(__file__).parent.parent / "src")) from basic import ( example_basic_line_plot, @@ -35,10 +35,10 @@ def main(): """Run all examples.""" - print("\n" + "="*60) + print("\n" + "=" * 60) print("gleplot Examples - Matplotlib-like API for GLE") - print("="*60 + "\n") - + print("=" * 60 + "\n") + examples = [ ("Basic Line Plot", example_basic_line_plot), ("Scatter Plot", example_scatter_plot), @@ -62,7 +62,7 @@ def main(): ("Data Prefix Naming", example_data_prefix), ("Line Overlay From File", example_line_overlay_from_file), ] - + for name, example_func in examples: try: print(f"\n[{name}]") @@ -70,14 +70,15 @@ def main(): except Exception as e: print(f" ✗ Error: {e}") import traceback + traceback.print_exc() - - print("\n" + "="*60) + + print("\n" + "=" * 60) print("All examples completed!") print("Generated GLE files: example_*.gle") print("To compile to PDF: gle example_*.gle -d PDF") - print("="*60 + "\n") + print("=" * 60 + "\n") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/src/gleplot/__init__.py b/src/gleplot/__init__.py index 0d760d0..a558268 100644 --- a/src/gleplot/__init__.py +++ b/src/gleplot/__init__.py @@ -22,18 +22,18 @@ Usage ----- import gleplot as glp - + fig = glp.figure(figsize=(8, 6)) ax = fig.add_subplot(111) - + ax.plot([1, 2, 3], [1, 4, 9], 'b-', label='quadratic') ax.scatter([1, 2, 3], [1, 2, 3], color='red', label='points') - + ax.set_xlabel('X axis') ax.set_ylabel('Y axis') ax.set_title('Example Plot') ax.legend() - + fig.savefig('output.pdf') # Saves as PDF fig.savefig('output.gle') # Saves as GLE script only fig.view() # Display inline in Jupyter notebook @@ -44,15 +44,15 @@ Matplotlib-like figure container Axes Matplotlib-like axes for plotting - + Functions --------- figure(figsize=(8, 6), dpi=100) Create a new figure """ -__version__ = '1.7.0' -__author__ = 'gleplot contributors' +__version__ = "1.7.0" +__author__ = "gleplot contributors" from .figure import Figure from .axes import Axes @@ -92,13 +92,16 @@ def open_gle(path, *, base_dir=None) -> Figure: Figure """ from .parser.recognizer import parse_gle_figure + return parse_gle_figure(path, base_dir=base_dir).figure -def figure(figsize=(8, 6), dpi=100, style=None, graph=None, marker=None, data_prefix=None) -> Figure: +def figure( + figsize=(8, 6), dpi=100, style=None, graph=None, marker=None, data_prefix=None +) -> Figure: """ Create a new figure. - + Parameters ---------- figsize : tuple, optional @@ -114,30 +117,37 @@ def figure(figsize=(8, 6), dpi=100, style=None, graph=None, marker=None, data_pr data_prefix : str, optional Custom prefix for data file names (e.g., 'test9' creates 'test9_0.dat', 'test9_1.dat'). If None, uses global counter with ``data_`` prefix. - + Returns ------- Figure New figure object - + Examples -------- Create a figure with default settings: - + >>> fig = glp.figure() - + Create a figure with custom style: - + >>> style = glp.GLEStyleConfig(font='helvetica', fontsize=12) >>> fig = glp.figure(style=style) - + Or modify global defaults: - + >>> glp.GlobalConfig.style.font = 'helvetica' >>> fig = glp.figure() # Will use helvetica font """ global _current_figure - _current_figure = Figure(figsize=figsize, dpi=dpi, style=style, graph=graph, marker=marker, data_prefix=data_prefix) + _current_figure = Figure( + figsize=figsize, + dpi=dpi, + style=style, + graph=graph, + marker=marker, + data_prefix=data_prefix, + ) return _current_figure @@ -185,15 +195,23 @@ def text(*args, **kwargs): return gca().text(*args, **kwargs) -def subplots(nrows: int = 1, ncols: int = 1, figsize=None, dpi=100, - style=None, graph=None, marker=None, - sharex: bool = False, sharey: bool = False, - data_prefix=None): +def subplots( + nrows: int = 1, + ncols: int = 1, + figsize=None, + dpi=100, + style=None, + graph=None, + marker=None, + sharex: bool = False, + sharey: bool = False, + data_prefix=None, +): """ Create a figure and a set of subplots. - + Convenience function matching ``matplotlib.pyplot.subplots()``. - + Parameters ---------- nrows : int, optional @@ -220,7 +238,7 @@ def subplots(nrows: int = 1, ncols: int = 1, figsize=None, dpi=100, data_prefix : str, optional Custom prefix for data file names (e.g., 'test9' creates 'test9_0.dat', 'test9_1.dat'). If None, uses global counter with ``data_`` prefix. - + Returns ------- fig : Figure @@ -228,41 +246,49 @@ def subplots(nrows: int = 1, ncols: int = 1, figsize=None, dpi=100, axes : Axes or list of Axes A single Axes if nrows*ncols == 1, otherwise a list of Axes arranged in row-major order. - + Examples -------- Single plot: - + >>> fig, ax = glp.subplots() >>> ax.plot(x, y) - + 2x2 grid: - + >>> fig, axes = glp.subplots(2, 2, figsize=(12, 10)) >>> axes[0].plot(x, y1) # top-left >>> axes[1].scatter(x, y2) # top-right >>> axes[2].bar(x, y3) # bottom-left >>> axes[3].plot(x, y4) # bottom-right - + Shared x-axis (stacked plots): - + >>> fig, axes = glp.subplots(3, 1, sharex=True, figsize=(8, 12)) >>> # Only bottom subplot shows x-axis label and ticks """ global _current_figure - + if figsize is None: figsize = (max(6, 6 * ncols), max(4, 4 * nrows)) - - fig = Figure(figsize=figsize, dpi=dpi, style=style, graph=graph, marker=marker, - sharex=sharex, sharey=sharey, data_prefix=data_prefix) + + fig = Figure( + figsize=figsize, + dpi=dpi, + style=style, + graph=graph, + marker=marker, + sharex=sharex, + sharey=sharey, + data_prefix=data_prefix, + ) _current_figure = fig - + axes_list = [] for idx in range(1, nrows * ncols + 1): ax = fig.add_subplot(nrows, ncols, idx) axes_list.append(ax) - + if len(axes_list) == 1: return fig, axes_list[0] return fig, axes_list @@ -298,21 +324,21 @@ def show(): print(f"Figure saved to {gcf().figsize}") -def view(dpi=None, format='png'): +def view(dpi=None, format="png"): """Display current figure inline (in Jupyter notebooks). - + Parameters ---------- dpi : int, optional Resolution in dots per inch. If None, uses figure's dpi setting. format : {'png', 'pdf'}, optional Output format. Default is 'png' for inline display. - + Returns ------- Path or None Path to the generated file, or None when displayed inline in Jupyter. - + Examples -------- >>> import gleplot as glp @@ -336,33 +362,33 @@ def close(fig=None): __all__ = [ - 'Figure', - 'Axes', - 'figure', - 'gca', - 'gcf', - 'plot', - 'scatter', - 'bar', - 'fill_between', - 'errorbar', - 'text', - 'subplots', - 'xlabel', - 'ylabel', - 'title', - 'legend', - 'savefig', - 'view', - 'show', - 'close', - 'rgb_to_gle', - 'get_color_palette', - 'get_gle_marker', - 'GLECompiler', - 'GLEStyleConfig', - 'GLEGraphConfig', - 'GLEMarkerConfig', - 'GlobalConfig', - 'open_gle', + "Figure", + "Axes", + "figure", + "gca", + "gcf", + "plot", + "scatter", + "bar", + "fill_between", + "errorbar", + "text", + "subplots", + "xlabel", + "ylabel", + "title", + "legend", + "savefig", + "view", + "show", + "close", + "rgb_to_gle", + "get_color_palette", + "get_gle_marker", + "GLECompiler", + "GLEStyleConfig", + "GLEGraphConfig", + "GLEMarkerConfig", + "GlobalConfig", + "open_gle", ] diff --git a/src/gleplot/axes.py b/src/gleplot/axes.py index 8823f57..ce25780 100644 --- a/src/gleplot/axes.py +++ b/src/gleplot/axes.py @@ -7,7 +7,6 @@ from .markers import get_gle_marker from .parser.units import markersize_to_msize, capsize_pt_to_cm - # Global counter for unique data file names across all figures in a session _global_data_file_counter = 0 @@ -24,7 +23,7 @@ def _to_jsonable(value): if value is None or isinstance(value, (bool, str)): return value if isinstance(value, np.ndarray): - if value.dtype.kind in 'biufc': # bool/int/uint/float/complex: numeric + if value.dtype.kind in "biufc": # bool/int/uint/float/complex: numeric # ndarray.tolist() already recursively converts numeric dtypes to # native Python scalars (int/float/bool), so no need to re-wrap # every element in a Python-level comprehension (avoids iterating @@ -168,24 +167,24 @@ def _reserve_data_filename(filename: str, figure=None) -> str: def _get_next_data_file(figure=None): """Get next unique data file name. - + Parameters ---------- figure : Figure, optional If provided and has a custom data_prefix, uses figure's local counter. Otherwise uses global counter. - + Returns ------- str Data filename (e.g., 'data_5.dat' or 'mytest_2.dat') """ if figure and figure.data_prefix: - filename = f'{figure.data_prefix}_{figure._local_data_counter}.dat' + filename = f"{figure.data_prefix}_{figure._local_data_counter}.dat" figure._local_data_counter += 1 else: global _global_data_file_counter - filename = f'data_{_global_data_file_counter}.dat' + filename = f"data_{_global_data_file_counter}.dat" _global_data_file_counter += 1 return _reserve_data_filename(filename, figure) @@ -199,7 +198,10 @@ def _resolve_data_file(figure=None, data_name: object = None) -> str: def _build_errorbar_column_names( label: Optional[str], - yerr_up, yerr_down, xerr_left, xerr_right, + yerr_up, + yerr_down, + xerr_left, + xerr_right, ) -> List[str]: """Build the sidecar header row for an errorbar series. @@ -220,37 +222,47 @@ def _build_errorbar_column_names( the label) since GLE never auto-keys off an error dataset's column name directly relevant here -- only the uniqueness pass can rename them. """ - y_names = ['y'] + y_names = ["y"] has_yerr = yerr_up is not None or yerr_down is not None has_xerr = xerr_left is not None or xerr_right is not None - yerr_symmetric = (has_yerr and yerr_up is not None and yerr_down is not None - and np.array_equal(yerr_up, yerr_down)) - xerr_symmetric = (has_xerr and xerr_left is not None and xerr_right is not None - and np.array_equal(xerr_left, xerr_right)) + yerr_symmetric = ( + has_yerr + and yerr_up is not None + and yerr_down is not None + and np.array_equal(yerr_up, yerr_down) + ) + xerr_symmetric = ( + has_xerr + and xerr_left is not None + and xerr_right is not None + and np.array_equal(xerr_left, xerr_right) + ) if has_yerr: if yerr_symmetric: - y_names.append('err') + y_names.append("err") else: if yerr_up is not None: - y_names.append('err_up') + y_names.append("err_up") if yerr_down is not None: - y_names.append('err_down') + y_names.append("err_down") if has_xerr: if xerr_symmetric: - y_names.append('xerr') + y_names.append("xerr") else: if xerr_left is not None: - y_names.append('xerr_left') + y_names.append("xerr_left") if xerr_right is not None: - y_names.append('xerr_right') + y_names.append("xerr_right") - return _build_column_names('x', y_names, label) + return _build_column_names("x", y_names, label) -def _build_column_names(x_name: str, y_names: List[str], label: Optional[str]) -> List[str]: +def _build_column_names( + x_name: str, y_names: List[str], label: Optional[str] +) -> List[str]: """Build a sidecar header row: one name for x, then one per y-like column. Parameters @@ -287,11 +299,11 @@ def _build_column_names(x_name: str, y_names: List[str], label: Optional[str]) - class Axes: """Matplotlib-like axes for plotting.""" - + def __init__(self, figure, position: Tuple[int, int, int] = None): """ Initialize axes. - + Parameters ---------- figure : Figure @@ -301,15 +313,15 @@ def __init__(self, figure, position: Tuple[int, int, int] = None): """ self.figure = figure self.position = position - + # Axis properties - self.xlabel_text = '' - self.ylabel_text = '' - self.y2label_text = '' # Secondary y-axis label - self.title_text = '' - self.xscale = 'linear' - self.yscale = 'linear' - self.y2scale = 'linear' # Secondary y-axis scale + self.xlabel_text = "" + self.ylabel_text = "" + self.y2label_text = "" # Secondary y-axis label + self.title_text = "" + self.xscale = "linear" + self.yscale = "linear" + self.y2scale = "linear" # Secondary y-axis scale self.xmin = None self.xmax = None self.ymin = None @@ -319,14 +331,14 @@ def __init__(self, figure, position: Tuple[int, int, int] = None): # Tri-state: None = auto (show a legend iff any series has a label), # True/False = explicit user choice (the GUI toggle writes these). self.legend_on = None - self.legend_pos = 'top right' - + self.legend_pos = "top right" + # Shared axes visibility control self._show_xlabel = True self._show_ylabel = True self._show_xticks = True self._show_yticks = True - + # Plot data storage self.lines = [] # List of line plot data self.scatters = [] # List of scatter plot data @@ -341,14 +353,24 @@ def __init__(self, figure, position: Tuple[int, int, int] = None): # axes' graph block, immediately before 'end graph'. One entry per # source line, no trailing newline. Default: empty (nothing to emit). self.passthrough: list = [] - - def plot(self, x, y, linestyle: str = '-', color: Optional[str] = None, - marker: Optional[str] = None, markersize: float = 6, - linewidth: float = 1, label: Optional[str] = None, - yaxis: str = 'y', offset: float = 0.0, **kwargs): + + def plot( + self, + x, + y, + linestyle: str = "-", + color: Optional[str] = None, + marker: Optional[str] = None, + markersize: float = 6, + linewidth: float = 1, + label: Optional[str] = None, + yaxis: str = "y", + offset: float = 0.0, + **kwargs, + ): """ Plot line or scatter plot (if marker without line). - + Parameters ---------- x, y : array-like @@ -369,66 +391,79 @@ def plot(self, x, y, linestyle: str = '-', color: Optional[str] = None, Which y-axis to use: 'y' (left, default) or 'y2' (right) **kwargs Additional matplotlib-compatible arguments - + Returns ------- Line2D Line object (for compatibility) """ - data_name = kwargs.pop('data_name', None) + data_name = kwargs.pop("data_name", None) x = np.asarray(x) y = np.asarray(y) - + # Handle color if color is None: - color = 'BLUE' + color = "BLUE" else: color = rgb_to_gle(color) - + # Handle marker. GLE supports markers on line datasets natively, so a # marker requested alongside a solid/dashed line must be preserved # (not silently dropped). Only when there is *no* line is the series a # true scatter. - is_scatter = marker is not None and linestyle in ('', 'none', ' ', 'None') + is_scatter = marker is not None and linestyle in ("", "none", " ", "None") gle_marker = get_gle_marker(marker) if marker is not None else None - plot_type = 'scatter' if is_scatter else 'line' - + plot_type = "scatter" if is_scatter else "line" + # Scale markersize from matplotlib (typical 1-20, default 6) to GLE msize (0.05-0.5) # Examples: markersize 6 → 0.15, markersize 10 → 0.25, markersize 20 → 0.5 - gle_markersize = markersize_to_msize(markersize, self.figure.marker_config.msize_scale) - + gle_markersize = markersize_to_msize( + markersize, self.figure.marker_config.msize_scale + ) + line_data = { - 'type': plot_type, - 'x': x, - 'y': y, - 'color': color, - 'marker': gle_marker, - 'markersize': gle_markersize, - 'linestyle': linestyle, - 'linewidth': linewidth, - 'label': label, - 'yaxis': yaxis, # 'y' or 'y2' - 'offset': float(offset), - 'data_file': _resolve_data_file(self.figure, data_name), - 'column_names': _build_column_names('x', ['y'], label), + "type": plot_type, + "x": x, + "y": y, + "color": color, + "marker": gle_marker, + "markersize": gle_markersize, + "linestyle": linestyle, + "linewidth": linewidth, + "label": label, + "yaxis": yaxis, # 'y' or 'y2' + "offset": float(offset), + "data_file": _resolve_data_file(self.figure, data_name), + "column_names": _build_column_names("x", ["y"], label), } if is_scatter: self.scatters.append(line_data) else: self.lines.append(line_data) - + return self # Return self for method chaining - - def errorbar(self, x, y, yerr=None, xerr=None, fmt: str = '-', - color: Optional[str] = None, marker: Optional[str] = None, - markersize: float = 6, linewidth: float = 1, - label: Optional[str] = None, capsize: Optional[float] = None, - capsize_cm: Optional[float] = None, - yaxis: str = 'y', offset: float = 0.0, - **kwargs): + + def errorbar( + self, + x, + y, + yerr=None, + xerr=None, + fmt: str = "-", + color: Optional[str] = None, + marker: Optional[str] = None, + markersize: float = 6, + linewidth: float = 1, + label: Optional[str] = None, + capsize: Optional[float] = None, + capsize_cm: Optional[float] = None, + yaxis: str = "y", + offset: float = 0.0, + **kwargs, + ): """ Plot data with error bars. @@ -490,7 +525,7 @@ def errorbar(self, x, y, yerr=None, xerr=None, fmt: str = '-', # Handle color if color is None: - color = 'BLUE' + color = "BLUE" else: color = rgb_to_gle(color) @@ -498,19 +533,19 @@ def errorbar(self, x, y, yerr=None, xerr=None, fmt: str = '-', # Simple parsing: check for marker chars and line styles parsed_marker = marker parsed_linestyle = fmt - if fmt in ('', 'none', ' ', 'None'): - parsed_linestyle = 'none' - elif fmt == '-o' or fmt == 'o-': - parsed_marker = parsed_marker or 'o' - parsed_linestyle = '-' - elif fmt == '-s' or fmt == 's-': - parsed_marker = parsed_marker or 's' - parsed_linestyle = '-' - elif fmt in ('-', '--', ':', '-.'): + if fmt in ("", "none", " ", "None"): + parsed_linestyle = "none" + elif fmt == "-o" or fmt == "o-": + parsed_marker = parsed_marker or "o" + parsed_linestyle = "-" + elif fmt == "-s" or fmt == "s-": + parsed_marker = parsed_marker or "s" + parsed_linestyle = "-" + elif fmt in ("-", "--", ":", "-."): parsed_linestyle = fmt - elif len(fmt) == 1 and fmt in 'os^vD+x': + elif len(fmt) == 1 and fmt in "os^vD+x": parsed_marker = parsed_marker or fmt - parsed_linestyle = 'none' + parsed_linestyle = "none" # Determine GLE marker gle_marker = None @@ -518,7 +553,9 @@ def errorbar(self, x, y, yerr=None, xerr=None, fmt: str = '-', gle_marker = get_gle_marker(parsed_marker) # Scale markersize from matplotlib to GLE msize (with config scaling) - gle_markersize = markersize_to_msize(markersize, self.figure.marker_config.msize_scale) + gle_markersize = markersize_to_msize( + markersize, self.figure.marker_config.msize_scale + ) # Convert capsize from matplotlib points to GLE cm. # Store the original capsize for the data structure, convert for GLE output @@ -583,28 +620,28 @@ def errorbar(self, x, y, yerr=None, xerr=None, fmt: str = '-', xerr_left = err_arr xerr_right = err_arr - data_name = kwargs.pop('data_name', None) + data_name = kwargs.pop("data_name", None) errbar_data = { - 'type': 'errorbar', - 'x': x, - 'y': y, - 'yerr_up': yerr_up, - 'yerr_down': yerr_down, - 'xerr_left': xerr_left, - 'xerr_right': xerr_right, - 'color': color, - 'marker': gle_marker, - 'markersize': gle_markersize, - 'linestyle': parsed_linestyle, - 'linewidth': linewidth, - 'label': label, - 'capsize': stored_capsize, - 'gle_capsize': gle_capsize, # Separate field for the GLE-converted value - 'yaxis': yaxis, # 'y' or 'y2' - 'offset': float(offset), - 'data_file': _resolve_data_file(self.figure, data_name), - 'column_names': _build_errorbar_column_names( + "type": "errorbar", + "x": x, + "y": y, + "yerr_up": yerr_up, + "yerr_down": yerr_down, + "xerr_left": xerr_left, + "xerr_right": xerr_right, + "color": color, + "marker": gle_marker, + "markersize": gle_markersize, + "linestyle": parsed_linestyle, + "linewidth": linewidth, + "label": label, + "capsize": stored_capsize, + "gle_capsize": gle_capsize, # Separate field for the GLE-converted value + "yaxis": yaxis, # 'y' or 'y2' + "offset": float(offset), + "data_file": _resolve_data_file(self.figure, data_name), + "column_names": _build_errorbar_column_names( label, yerr_up, yerr_down, xerr_left, xerr_right ), } @@ -619,11 +656,11 @@ def errorbar_from_file( y_col: int, yerr_col: Optional[int] = None, color: Optional[str] = None, - marker: Optional[str] = 'o', + marker: Optional[str] = "o", markersize: float = 6, label: Optional[str] = None, capsize: Optional[float] = None, - yaxis: str = 'y', + yaxis: str = "y", ): """Plot by referencing columns in an existing external data file. @@ -634,27 +671,29 @@ def errorbar_from_file( raise ValueError("Column indices must be >= 1") if color is None: - gle_color = 'BLUE' + gle_color = "BLUE" else: gle_color = rgb_to_gle(color) gle_marker = get_gle_marker(marker) if marker else None - gle_markersize = markersize_to_msize(markersize, self.figure.marker_config.msize_scale) + gle_markersize = markersize_to_msize( + markersize, self.figure.marker_config.msize_scale + ) gle_capsize = capsize_pt_to_cm(capsize) if capsize is not None else None self.file_series.append( { - 'series_type': 'errorbar', - 'data_file': data_file, - 'x_col': int(x_col), - 'y_col': int(y_col), - 'yerr_col': int(yerr_col) if yerr_col is not None else None, - 'color': gle_color, - 'marker': gle_marker, - 'markersize': gle_markersize, - 'label': label, - 'capsize': gle_capsize, - 'yaxis': yaxis, + "series_type": "errorbar", + "data_file": data_file, + "x_col": int(x_col), + "y_col": int(y_col), + "yerr_col": int(yerr_col) if yerr_col is not None else None, + "color": gle_color, + "marker": gle_marker, + "markersize": gle_markersize, + "label": label, + "capsize": gle_capsize, + "yaxis": yaxis, } ) @@ -666,10 +705,10 @@ def line_from_file( x_col: int, y_col: int, color: Optional[str] = None, - linestyle: str = '-', + linestyle: str = "-", linewidth: float = 1, label: Optional[str] = None, - yaxis: str = 'y', + yaxis: str = "y", ): """Plot a line by referencing columns in an external data file. @@ -680,32 +719,40 @@ def line_from_file( raise ValueError("Column indices must be >= 1") if color is None: - gle_color = 'BLUE' + gle_color = "BLUE" else: gle_color = rgb_to_gle(color) self.file_series.append( { - 'series_type': 'line', - 'data_file': data_file, - 'x_col': int(x_col), - 'y_col': int(y_col), - 'color': gle_color, - 'linestyle': linestyle, - 'linewidth': float(linewidth), - 'label': label, - 'yaxis': yaxis, + "series_type": "line", + "data_file": data_file, + "x_col": int(x_col), + "y_col": int(y_col), + "color": gle_color, + "linestyle": linestyle, + "linewidth": float(linewidth), + "label": label, + "yaxis": yaxis, } ) return self - def scatter(self, x, y, color: Optional[str] = None, s: float = 20, - marker: str = 'o', label: Optional[str] = None, - yaxis: str = 'y', **kwargs): + def scatter( + self, + x, + y, + color: Optional[str] = None, + s: float = 20, + marker: str = "o", + label: Optional[str] = None, + yaxis: str = "y", + **kwargs, + ): """ Create scatter plot. - + Parameters ---------- x, y : array-like @@ -722,7 +769,7 @@ def scatter(self, x, y, color: Optional[str] = None, s: float = 20, Which y-axis to use: 'y' (left, default) or 'y2' (right) **kwargs Additional arguments - + Returns ------- self @@ -732,17 +779,31 @@ def scatter(self, x, y, color: Optional[str] = None, s: float = 20, # Convert to markersize: since area ~ size^2, markersize ~ sqrt(s) # Use factor of 1.2 for better visibility markersize = np.sqrt(s) * 1.2 - return self.plot(x, y, linestyle='none', color=color, marker=marker, - markersize=markersize, label=label, yaxis=yaxis) - - def bar(self, x, height, color: Optional[Union[str, List[str]]] = None, - label: Optional[str] = None, **kwargs): + return self.plot( + x, + y, + linestyle="none", + color=color, + marker=marker, + markersize=markersize, + label=label, + yaxis=yaxis, + ) + + def bar( + self, + x, + height, + color: Optional[Union[str, List[str]]] = None, + label: Optional[str] = None, + **kwargs, + ): """ Create bar chart. - + Note: Due to GLE limitations, all bars in a chart use the same color. If a list of colors is provided, only the first color is used. - + Parameters ---------- x : array-like @@ -756,11 +817,11 @@ def bar(self, x, height, color: Optional[Union[str, List[str]]] = None, Legend label (currently not supported by GLE for bar charts) **kwargs Additional arguments - + Returns ------- self - + Examples -------- >>> fig = glp.figure() @@ -770,38 +831,46 @@ def bar(self, x, height, color: Optional[Union[str, List[str]]] = None, >>> ax.bar(categories, values, color='blue') >>> fig.savefig('bar_chart.pdf') """ - data_name = kwargs.pop('data_name', None) + data_name = kwargs.pop("data_name", None) x = np.asarray(x, dtype=float) height = np.asarray(height, dtype=float) # Handle color - only first color is used due to GLE limitation if color is None: - colors = ['RED'] * len(height) + colors = ["RED"] * len(height) elif isinstance(color, str): colors = [rgb_to_gle(color)] * len(height) else: # Take first color only colors = [rgb_to_gle(color[0])] * len(height) - + bar_data = { - 'x': x, - 'height': height, - 'colors': colors, - 'label': label, - 'data_file': _resolve_data_file(self.figure, data_name), - 'column_names': _build_column_names('x', ['height'], label), + "x": x, + "height": height, + "colors": colors, + "label": label, + "data_file": _resolve_data_file(self.figure, data_name), + "column_names": _build_column_names("x", ["height"], label), } self.bars.append(bar_data) - + return self - - def fill_between(self, x, y1, y2, color: Optional[str] = None, - alpha: float = 0.3, label: Optional[str] = None, - offset: float = 0.0, **kwargs): + + def fill_between( + self, + x, + y1, + y2, + color: Optional[str] = None, + alpha: float = 0.3, + label: Optional[str] = None, + offset: float = 0.0, + **kwargs, + ): """ Fill area between two curves. - + Parameters ---------- x : array-like @@ -816,35 +885,35 @@ def fill_between(self, x, y1, y2, color: Optional[str] = None, Legend label **kwargs Additional arguments - + Returns ------- self """ - data_name = kwargs.pop('data_name', None) + data_name = kwargs.pop("data_name", None) x = np.asarray(x) y1 = np.asarray(y1) y2 = np.asarray(y2) - + if color is None: - color = 'LIGHTBLUE' + color = "LIGHTBLUE" else: color = rgb_to_gle(color) - + fill_data = { - 'x': x, - 'y1': y1, - 'y2': y2, - 'color': color, - 'alpha': alpha, - 'label': label, - 'offset': float(offset), - 'data_file': _resolve_data_file(self.figure, data_name), - 'column_names': _unique_column_names(['x', 'upper', 'lower']), + "x": x, + "y1": y1, + "y2": y2, + "color": color, + "alpha": alpha, + "label": label, + "offset": float(offset), + "data_file": _resolve_data_file(self.figure, data_name), + "column_names": _unique_column_names(["x", "upper", "lower"]), } self.fills.append(fill_data) - + return self def text( @@ -854,8 +923,8 @@ def text( s: str, color: Optional[str] = None, fontsize: Optional[float] = None, - ha: str = 'left', - va: str = 'center', + ha: str = "left", + va: str = "center", bbox: Optional[dict] = None, **kwargs, ): @@ -879,38 +948,38 @@ def text( Optional text box settings. Supported key: ``facecolor``. """ if color is None: - gle_color = 'BLACK' + gle_color = "BLACK" else: gle_color = rgb_to_gle(color) box_color = None if isinstance(bbox, dict): - facecolor = bbox.get('facecolor') + facecolor = bbox.get("facecolor") if facecolor is not None: box_color = rgb_to_gle(facecolor) self.texts.append( { - 'x': float(x), - 'y': float(y), - 'text': str(s), - 'color': gle_color, - 'fontsize': float(fontsize) if fontsize is not None else None, - 'ha': str(ha), - 'va': str(va), - 'box_color': box_color, + "x": float(x), + "y": float(y), + "text": str(s), + "color": gle_color, + "fontsize": float(fontsize) if fontsize is not None else None, + "ha": str(ha), + "va": str(va), + "box_color": box_color, } ) return self - + def set_xlabel(self, label: str): """Set x-axis label.""" self.xlabel_text = label return self - - def set_ylabel(self, label: str, axis: str = 'y'): + + def set_ylabel(self, label: str, axis: str = "y"): """Set y-axis label. - + Parameters ---------- label : str @@ -918,25 +987,25 @@ def set_ylabel(self, label: str, axis: str = 'y'): axis : str, optional Which axis: 'y' (left, default) or 'y2' (right) """ - if axis == 'y2': + if axis == "y2": self.y2label_text = label else: self.ylabel_text = label return self - + def set_title(self, label: str): """Set subplot title.""" self.title_text = label return self - + def set_xscale(self, scale: str): """Set x-axis scale ('linear' or 'log').""" self.xscale = scale return self - - def set_yscale(self, scale: str, axis: str = 'y'): + + def set_yscale(self, scale: str, axis: str = "y"): """Set y-axis scale. - + Parameters ---------- scale : str @@ -944,21 +1013,21 @@ def set_yscale(self, scale: str, axis: str = 'y'): axis : str, optional Which axis: 'y' (left, default) or 'y2' (right) """ - if axis == 'y2': + if axis == "y2": self.y2scale = scale else: self.yscale = scale return self - + def set_xlim(self, xmin: float, xmax: float): """Set x-axis limits.""" self.xmin = xmin self.xmax = xmax return self - - def set_ylim(self, ymin: float, ymax: float, axis: str = 'y'): + + def set_ylim(self, ymin: float, ymax: float, axis: str = "y"): """Set y-axis limits. - + Parameters ---------- ymin, ymax : float @@ -966,60 +1035,67 @@ def set_ylim(self, ymin: float, ymax: float, axis: str = 'y'): axis : str, optional Which axis: 'y' (left, default) or 'y2' (right) """ - if axis == 'y2': + if axis == "y2": self.y2min = ymin self.y2max = ymax else: self.ymin = ymin self.ymax = ymax return self - - def legend(self, loc: str = 'best', **kwargs): + + def legend(self, loc: str = "best", **kwargs): """Add legend.""" self.legend_on = True # Map matplotlib loc to GLE positions loc_map = { - 'best': 'top right', - 'upper right': 'top right', - 'upper left': 'top left', - 'lower left': 'bottom left', - 'lower right': 'bottom right', - 'center': 'center', + "best": "top right", + "upper right": "top right", + "upper left": "top left", + "lower left": "bottom left", + "lower right": "bottom right", + "center": "center", } - self.legend_pos = loc_map.get(loc, 'top right') + self.legend_pos = loc_map.get(loc, "top right") return self - + def grid(self, visible: bool = True, **kwargs): """Toggle grid (placeholder for future implementation).""" # GLE grid support can be added later return self - + def get_xlim(self) -> Tuple[float, float]: """Get x-axis limits.""" return self.xmin, self.xmax - - def get_ylim(self, axis: str = 'y') -> Tuple[float, float]: + + def get_ylim(self, axis: str = "y") -> Tuple[float, float]: """Get y-axis limits. - + Parameters ---------- axis : str, optional Which axis: 'y' (left, default) or 'y2' (right) """ - if axis == 'y2': + if axis == "y2": return self.y2min, self.y2max else: return self.ymin, self.ymax - + def has_plots(self) -> bool: """Check if axes has any plots.""" - return bool(self.lines or self.scatters or self.bars or self.fills or self.errorbars or self.file_series) - + return bool( + self.lines + or self.scatters + or self.bars + or self.fills + or self.errorbars + or self.file_series + ) + def has_y2_plots(self) -> bool: """Check if axes has any plots using the y2 axis.""" for plot_list in [self.lines, self.scatters, self.errorbars]: for plot_data in plot_list: - if plot_data.get('yaxis') == 'y2': + if plot_data.get("yaxis") == "y2": return True return False @@ -1029,18 +1105,25 @@ def has_y2_plots(self) -> bool: # this to restore ndarrays where the object model expects them; every # other key is a JSON scalar/string/None and is restored verbatim. _ARRAY_KEYS = { - 'lines': ('x', 'y'), - 'scatters': ('x', 'y'), - 'bars': ('x', 'height'), - 'fills': ('x', 'y1', 'y2'), - 'errorbars': ('x', 'y', 'yerr_up', 'yerr_down', 'xerr_left', 'xerr_right'), - 'file_series': (), - 'texts': (), + "lines": ("x", "y"), + "scatters": ("x", "y"), + "bars": ("x", "height"), + "fills": ("x", "y1", "y2"), + "errorbars": ("x", "y", "yerr_up", "yerr_down", "xerr_left", "xerr_right"), + "file_series": (), + "texts": (), } # Series list attributes serialized on every axes, in a stable order. - _SERIES_ATTRS = ('lines', 'scatters', 'bars', 'fills', 'errorbars', - 'file_series', 'texts') + _SERIES_ATTRS = ( + "lines", + "scatters", + "bars", + "fills", + "errorbars", + "file_series", + "texts", + ) @staticmethod def _default_column_names(attr: str, item: dict) -> Optional[List[str]]: @@ -1055,18 +1138,20 @@ def _default_column_names(attr: str, item: dict) -> Optional[List[str]]: JSON-scalar/array-restored ``item``. Returns ``None`` for ``file_series``/``texts`` (no generated sidecar, nothing to name). """ - label = item.get('label') - if attr in ('lines', 'scatters'): - return _build_column_names('x', ['y'], label) - if attr == 'bars': - return _build_column_names('x', ['height'], label) - if attr == 'fills': - return _unique_column_names(['x', 'upper', 'lower']) - if attr == 'errorbars': + label = item.get("label") + if attr in ("lines", "scatters"): + return _build_column_names("x", ["y"], label) + if attr == "bars": + return _build_column_names("x", ["height"], label) + if attr == "fills": + return _unique_column_names(["x", "upper", "lower"]) + if attr == "errorbars": return _build_errorbar_column_names( label, - item.get('yerr_up'), item.get('yerr_down'), - item.get('xerr_left'), item.get('xerr_right'), + item.get("yerr_up"), + item.get("yerr_down"), + item.get("xerr_left"), + item.get("xerr_right"), ) return None @@ -1085,42 +1170,42 @@ def to_dict(self) -> dict: of the module-global data-file counter state. """ return { - 'position': list(self.position) if self.position is not None else None, - 'xlabel_text': self.xlabel_text, - 'ylabel_text': self.ylabel_text, - 'y2label_text': self.y2label_text, - 'title_text': self.title_text, - 'xscale': self.xscale, - 'yscale': self.yscale, - 'y2scale': self.y2scale, - 'xmin': _to_jsonable(self.xmin), - 'xmax': _to_jsonable(self.xmax), - 'ymin': _to_jsonable(self.ymin), - 'ymax': _to_jsonable(self.ymax), - 'y2min': _to_jsonable(self.y2min), - 'y2max': _to_jsonable(self.y2max), - 'legend_on': self.legend_on, - 'legend_pos': self.legend_pos, - 'show_xlabel': self._show_xlabel, - 'show_ylabel': self._show_ylabel, - 'show_xticks': self._show_xticks, - 'show_yticks': self._show_yticks, - 'remove_last_xtick': getattr(self, '_remove_last_xtick', False), - 'remove_last_ytick': getattr(self, '_remove_last_ytick', False), - 'remove_first_xtick': getattr(self, '_remove_first_xtick', False), - 'remove_first_ytick': getattr(self, '_remove_first_ytick', False), - 'lines': [_to_jsonable(d) for d in self.lines], - 'scatters': [_to_jsonable(d) for d in self.scatters], - 'bars': [_to_jsonable(d) for d in self.bars], - 'fills': [_to_jsonable(d) for d in self.fills], - 'errorbars': [_to_jsonable(d) for d in self.errorbars], - 'file_series': [_to_jsonable(d) for d in self.file_series], - 'texts': [_to_jsonable(d) for d in self.texts], - 'passthrough': list(self.passthrough), + "position": list(self.position) if self.position is not None else None, + "xlabel_text": self.xlabel_text, + "ylabel_text": self.ylabel_text, + "y2label_text": self.y2label_text, + "title_text": self.title_text, + "xscale": self.xscale, + "yscale": self.yscale, + "y2scale": self.y2scale, + "xmin": _to_jsonable(self.xmin), + "xmax": _to_jsonable(self.xmax), + "ymin": _to_jsonable(self.ymin), + "ymax": _to_jsonable(self.ymax), + "y2min": _to_jsonable(self.y2min), + "y2max": _to_jsonable(self.y2max), + "legend_on": self.legend_on, + "legend_pos": self.legend_pos, + "show_xlabel": self._show_xlabel, + "show_ylabel": self._show_ylabel, + "show_xticks": self._show_xticks, + "show_yticks": self._show_yticks, + "remove_last_xtick": getattr(self, "_remove_last_xtick", False), + "remove_last_ytick": getattr(self, "_remove_last_ytick", False), + "remove_first_xtick": getattr(self, "_remove_first_xtick", False), + "remove_first_ytick": getattr(self, "_remove_first_ytick", False), + "lines": [_to_jsonable(d) for d in self.lines], + "scatters": [_to_jsonable(d) for d in self.scatters], + "bars": [_to_jsonable(d) for d in self.bars], + "fills": [_to_jsonable(d) for d in self.fills], + "errorbars": [_to_jsonable(d) for d in self.errorbars], + "file_series": [_to_jsonable(d) for d in self.file_series], + "texts": [_to_jsonable(d) for d in self.texts], + "passthrough": list(self.passthrough), } @classmethod - def from_dict(cls, figure, d: dict) -> 'Axes': + def from_dict(cls, figure, d: dict) -> "Axes": """Reconstruct an :class:`Axes` from a :meth:`to_dict` payload. Parameters @@ -1136,35 +1221,35 @@ def from_dict(cls, figure, d: dict) -> 'Axes': arrays that were ``None`` stay ``None``. All style keys, labels and the ``data_file`` names are restored verbatim. """ - position = d.get('position') + position = d.get("position") if position is not None: position = tuple(position) ax = cls(figure, position) - ax.xlabel_text = d.get('xlabel_text', '') - ax.ylabel_text = d.get('ylabel_text', '') - ax.y2label_text = d.get('y2label_text', '') - ax.title_text = d.get('title_text', '') - ax.xscale = d.get('xscale', 'linear') - ax.yscale = d.get('yscale', 'linear') - ax.y2scale = d.get('y2scale', 'linear') - ax.xmin = d.get('xmin') - ax.xmax = d.get('xmax') - ax.ymin = d.get('ymin') - ax.ymax = d.get('ymax') - ax.y2min = d.get('y2min') - ax.y2max = d.get('y2max') - ax.legend_on = d.get('legend_on') # tri-state; missing key = auto - ax.legend_pos = d.get('legend_pos', 'top right') - - ax._show_xlabel = d.get('show_xlabel', True) - ax._show_ylabel = d.get('show_ylabel', True) - ax._show_xticks = d.get('show_xticks', True) - ax._show_yticks = d.get('show_yticks', True) - ax._remove_last_xtick = d.get('remove_last_xtick', False) - ax._remove_last_ytick = d.get('remove_last_ytick', False) - ax._remove_first_xtick = d.get('remove_first_xtick', False) - ax._remove_first_ytick = d.get('remove_first_ytick', False) + ax.xlabel_text = d.get("xlabel_text", "") + ax.ylabel_text = d.get("ylabel_text", "") + ax.y2label_text = d.get("y2label_text", "") + ax.title_text = d.get("title_text", "") + ax.xscale = d.get("xscale", "linear") + ax.yscale = d.get("yscale", "linear") + ax.y2scale = d.get("y2scale", "linear") + ax.xmin = d.get("xmin") + ax.xmax = d.get("xmax") + ax.ymin = d.get("ymin") + ax.ymax = d.get("ymax") + ax.y2min = d.get("y2min") + ax.y2max = d.get("y2max") + ax.legend_on = d.get("legend_on") # tri-state; missing key = auto + ax.legend_pos = d.get("legend_pos", "top right") + + ax._show_xlabel = d.get("show_xlabel", True) + ax._show_ylabel = d.get("show_ylabel", True) + ax._show_xticks = d.get("show_xticks", True) + ax._show_yticks = d.get("show_yticks", True) + ax._remove_last_xtick = d.get("remove_last_xtick", False) + ax._remove_last_ytick = d.get("remove_last_ytick", False) + ax._remove_first_xtick = d.get("remove_first_xtick", False) + ax._remove_first_ytick = d.get("remove_first_ytick", False) for attr in cls._SERIES_ATTRS: array_keys = cls._ARRAY_KEYS[attr] @@ -1177,13 +1262,13 @@ def from_dict(cls, figure, d: dict) -> 'Axes': # all on their series dicts; regenerate the same defaults the # plotting methods would produce so the next save still gets # a named header row instead of silently reverting to none. - if 'column_names' not in item: + if "column_names" not in item: defaults = cls._default_column_names(attr, item) if defaults is not None: - item['column_names'] = defaults + item["column_names"] = defaults restored.append(item) setattr(ax, attr, restored) - ax.passthrough = list(d.get('passthrough', [])) + ax.passthrough = list(d.get("passthrough", [])) return ax diff --git a/src/gleplot/config.py b/src/gleplot/config.py index ac9b16b..44b5321 100644 --- a/src/gleplot/config.py +++ b/src/gleplot/config.py @@ -7,48 +7,49 @@ @dataclass class GLEStyleConfig: """GLE rendering style configuration. - + Attributes ---------- font : str or None GLE font name (e.g., 'times8', 'psagb', 'plti'). If None or empty string, uses GLE's default font. Default: None - + fontsize : float Font size in points. Default: 12 (optimized for GLE/PDF readability) - + default_linewidth : float Default line width in points (unit: 1/72 inch). Default: 1.5 points ≈ 0.053 cm (increased for visibility in PDFs) - + default_color : str Default line/plot color (GLE color name). Default: 'BLUE' - + default_marker_color : str Default marker color. Default: 'BLUE' - + line_style_solid : int GLE line style for solid lines. Default: 1 - + line_style_dashed : int GLE line style for dashed lines (--). Default: 2 - + line_style_dotted : int GLE line style for dotted lines (:). Default: 3 - + line_style_dashdot : int GLE line style for dash-dot lines (-.). Default: 4 """ - font: str = '' # Empty string = GLE default font + + font: str = "" # Empty string = GLE default font fontsize: float = 12 # Increased from 10 for better readability in GLE/PDF output default_linewidth: float = 1.5 # Increased from 1.0 for visibility - default_color: str = 'BLUE' - default_marker_color: str = 'BLUE' + default_color: str = "BLUE" + default_marker_color: str = "BLUE" line_style_solid: int = 1 line_style_dashed: int = 2 line_style_dotted: int = 3 line_style_dashdot: int = 4 - + def to_dict(self) -> Dict[str, Any]: """Convert config to dictionary.""" return asdict(self) @@ -57,50 +58,51 @@ def to_dict(self) -> Dict[str, Any]: @dataclass class GLEGraphConfig: """GLE graph configuration. - + Attributes ---------- scale_mode : str Graph scaling mode: 'auto' (auto-sizes and centers), 'fixed' (uses specified size), or 'fullsize' (axes fill entire box, no margins). Default: 'auto' - + title_distance : float Distance (cm) from graph top to title. Default: 0.1 - + xlabel_distance : float Distance (cm) from graph bottom to x-axis label. Default: 0.1 - + ylabel_distance : float Distance (cm) from graph left to y-axis label. Default: 0.1 - + legend_position : str Default legend position: 'tl', 'tr', 'bl', 'br', 'tc', 'bc', 'lc', 'rc', 'cc'. Options: 'top right', 'top left', 'bottom right', 'bottom left', 'center'. Default: 'tr' (top right) - + legend_offset_x : float Legend x-offset from position (cm). Default: 0.0 - + legend_offset_y : float Legend y-offset from position (cm). Default: 0.0 - + smooth_curves : bool Enable smooth curve fitting on line plots (GLE smooth keyword). Default: True - + show_grid : bool Show background grid. Default: False """ - scale_mode: str = 'auto' # 'auto', 'fixed', 'fullsize' + + scale_mode: str = "auto" # 'auto', 'fixed', 'fullsize' title_distance: float = 0.1 xlabel_distance: float = 0.1 ylabel_distance: float = 0.1 - legend_position: str = 'tr' + legend_position: str = "tr" legend_offset_x: float = 0.0 legend_offset_y: float = 0.0 smooth_curves: bool = True show_grid: bool = False - + def to_dict(self) -> Dict[str, Any]: """Convert config to dictionary.""" return asdict(self) @@ -109,7 +111,7 @@ def to_dict(self) -> Dict[str, Any]: @dataclass class GLEMarkerConfig: """Marker style configuration. - + Attributes ---------- default_marker : str @@ -117,19 +119,20 @@ class GLEMarkerConfig: Options: 'circle', 'square', 'triangle', 'diamond', 'cross', 'fcircle', 'fsquare', 'ftriangle', 'fdiamond' (filled variants). Default: 'fcircle' (filled circle) - + msize_scale : float Scaling factor for marker sizes. Multiplies the msize value. Default: 1.0 - + mdist : Optional[float] Default marker distance (space between markers on continuous lines). If None, markers appear at every point. Default: None """ - default_marker: str = 'fcircle' + + default_marker: str = "fcircle" msize_scale: float = 1.0 mdist: Optional[float] = None - + def to_dict(self) -> Dict[str, Any]: """Convert config to dictionary.""" return asdict(self) @@ -137,23 +140,23 @@ def to_dict(self) -> Dict[str, Any]: class GlobalConfigMeta(type): """Metaclass for GlobalConfig to provide attribute access.""" - + _instance = None - + @property def style(cls) -> GLEStyleConfig: """Get global style configuration.""" if cls._instance is None: cls._instance = cls() return cls._instance.style - + @property def graph(cls) -> GLEGraphConfig: """Get global graph configuration.""" if cls._instance is None: cls._instance = cls() return cls._instance.graph - + @property def marker(cls) -> GLEMarkerConfig: """Get global marker configuration.""" @@ -164,40 +167,40 @@ def marker(cls) -> GLEMarkerConfig: class GlobalConfig(metaclass=GlobalConfigMeta): """Global gleplot configuration. - + Provides singleton-like access to default configuration settings that apply to all new figures created. - + Access style, graph, and marker configurations directly as class attributes: - + Examples -------- >>> from gleplot.config import GlobalConfig >>> # Change default font globally >>> GlobalConfig.style.font = 'helvetica' >>> # All new figures will use this font - + >>> # Or reset to defaults >>> GlobalConfig.reset() """ - + _instance = None - + def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance - + def __init__(self): if self._initialized: return - + self.style = GLEStyleConfig() self.graph = GLEGraphConfig() self.marker = GLEMarkerConfig() self._initialized = True - + @classmethod def reset(cls): """Reset all configurations to defaults.""" @@ -205,29 +208,29 @@ def reset(cls): instance.style = GLEStyleConfig() instance.graph = GLEGraphConfig() instance.marker = GLEMarkerConfig() - + @classmethod def get_style(cls) -> GLEStyleConfig: """Get global style configuration.""" return cls.style - + @classmethod def get_graph(cls) -> GLEGraphConfig: """Get global graph configuration.""" return cls.graph - + @classmethod def get_marker(cls) -> GLEMarkerConfig: """Get global marker configuration.""" return cls.marker - + @classmethod def to_dict(cls) -> Dict[str, Dict[str, Any]]: """Export all configurations as dictionary.""" return { - 'style': cls.style.to_dict(), - 'graph': cls.graph.to_dict(), - 'marker': cls.marker.to_dict(), + "style": cls.style.to_dict(), + "graph": cls.graph.to_dict(), + "marker": cls.marker.to_dict(), } diff --git a/src/gleplot/figure.py b/src/gleplot/figure.py index 589b6ea..ca2b5a6 100644 --- a/src/gleplot/figure.py +++ b/src/gleplot/figure.py @@ -10,9 +10,8 @@ from .config import GLEStyleConfig, GLEGraphConfig, GLEMarkerConfig, GlobalConfig from .parser import metadata as _gle_metadata - #: Envelope identifiers for the gleplot project-file format. -PROJECT_FORMAT = 'gleplot-project' +PROJECT_FORMAT = "gleplot-project" PROJECT_VERSION = 1 @@ -31,7 +30,7 @@ def _filtered_dataclass_kwargs(cls, data: dict) -> dict: class Figure: """Matplotlib-like figure for GLE plotting. - + Parameters ---------- figsize : tuple, optional @@ -45,16 +44,20 @@ class Figure: marker : GLEMarkerConfig, optional Marker configuration. If None, uses global default. """ - - def __init__(self, figsize: Tuple[float, float] = (8, 6), dpi: int = 100, - style: Optional[GLEStyleConfig] = None, - graph: Optional[GLEGraphConfig] = None, - marker: Optional[GLEMarkerConfig] = None, - sharex: bool = False, - sharey: bool = False, - data_prefix: Optional[str] = None): + + def __init__( + self, + figsize: Tuple[float, float] = (8, 6), + dpi: int = 100, + style: Optional[GLEStyleConfig] = None, + graph: Optional[GLEGraphConfig] = None, + marker: Optional[GLEMarkerConfig] = None, + sharex: bool = False, + sharey: bool = False, + data_prefix: Optional[str] = None, + ): """Initialize figure with optional configuration objects. - + Parameters ---------- data_prefix : str, optional @@ -63,22 +66,22 @@ def __init__(self, figsize: Tuple[float, float] = (8, 6), dpi: int = 100, """ self.figsize = figsize self.dpi = dpi - + # Store configuration for passing to writer self.style = style or GlobalConfig.get_style() self.graph = graph or GlobalConfig.get_graph() self.marker_config = marker or GlobalConfig.get_marker() - + # Shared axes configuration self.sharex = sharex self.sharey = sharey - + # Custom data file naming self.data_prefix = data_prefix self._local_data_counter = 0 # Local counter when using custom prefix self._used_data_files: set[str] = set() self._subplot_adjust: dict[str, float] = {} - + self.axes_list = [] # List of Axes objects self._current_axes = None # Current working axes @@ -122,18 +125,18 @@ def subplots_adjust( """ candidate = dict(self._subplot_adjust) updates = { - 'left': left, - 'right': right, - 'bottom': bottom, - 'top': top, - 'wspace': wspace, - 'hspace': hspace, + "left": left, + "right": right, + "bottom": bottom, + "top": top, + "wspace": wspace, + "hspace": hspace, } for key, value in updates.items(): if value is None: continue val = float(value) - if key in {'left', 'right', 'bottom', 'top'}: + if key in {"left", "right", "bottom", "top"}: if not (0.0 <= val <= 1.0): raise ValueError(f"{key} must be within [0, 1], got {val}") else: @@ -141,28 +144,28 @@ def subplots_adjust( raise ValueError(f"{key} must be >= 0, got {val}") candidate[key] = val - left_val = candidate.get('left') - right_val = candidate.get('right') + left_val = candidate.get("left") + right_val = candidate.get("right") if left_val is not None and right_val is not None and left_val >= right_val: - raise ValueError('left must be less than right') + raise ValueError("left must be less than right") - bottom_val = candidate.get('bottom') - top_val = candidate.get('top') + bottom_val = candidate.get("bottom") + top_val = candidate.get("top") if bottom_val is not None and top_val is not None and bottom_val >= top_val: - raise ValueError('bottom must be less than top') + raise ValueError("bottom must be less than top") self._subplot_adjust = candidate - + def add_subplot(self, *args) -> Axes: """ Add subplot to figure. - + Parameters ---------- *args : int Subplot specification (rows, cols, index) or single int E.g., add_subplot(2, 2, 1) or add_subplot(221) - + Returns ------- Axes @@ -177,7 +180,7 @@ def add_subplot(self, *args) -> Axes: raise ValueError(f"Invalid subplot spec: {args[0]}") else: rows, cols, idx = args - + ax = Axes(self, (rows, cols, idx)) # Derive shared-axes tick/label visibility flags from this figure's @@ -205,19 +208,19 @@ def _apply_shared_axes_flags(self, ax: Axes) -> None: """ rows, cols, idx = ax.position # Convert 1-based index to 0-based row/col. - row = (idx - 1) // cols # 0 = top row - col = (idx - 1) % cols # 0 = left col + row = (idx - 1) // cols # 0 = top row + col = (idx - 1) % cols # 0 = left col if self.sharex: # Only show x-axis labels/ticks on bottom row - ax._show_xlabel = (row == rows - 1) - ax._show_xticks = (row == rows - 1) + ax._show_xlabel = row == rows - 1 + ax._show_xticks = row == rows - 1 # Remove last x-tick label if not the bottom row (to prevent overlap when subplots touch) - ax._remove_last_xtick = (row < rows - 1) + ax._remove_last_xtick = row < rows - 1 ax._remove_first_xtick = False # When sharing x, y-axes touch vertically - remove last (top) y-label from all but top row # (highest y-label of lower plots could overlap upward into the plot above) - ax._remove_last_ytick = (row > 0) + ax._remove_last_ytick = row > 0 ax._remove_first_ytick = False else: ax._show_xlabel = True @@ -229,10 +232,10 @@ def _apply_shared_axes_flags(self, ax: Axes) -> None: if self.sharey: # Only show y-axis labels/ticks on leftmost column - ax._show_ylabel = (col == 0) - ax._show_yticks = (col == 0) + ax._show_ylabel = col == 0 + ax._show_yticks = col == 0 # When sharing y, x-axes touch horizontally - remove last x-label from all but rightmost - ax._remove_last_xtick = (col < cols - 1) + ax._remove_last_xtick = col < cols - 1 ax._remove_first_xtick = False # Y-axis labels don't overlap in horizontal arrangement (only shown on leftmost) # No need to remove first/last y-labels when plots are side-by-side @@ -249,31 +252,31 @@ def _apply_shared_axes_flags(self, ax: Axes) -> None: ax._remove_last_xtick = False ax._remove_first_xtick = False ax._remove_first_xtick = False - + def gca(self) -> Axes: """Get current axes (or create if needed).""" if self._current_axes is None: self.add_subplot(111) return self._current_axes - + # Convenience plotting methods (plot on current axes) - + def plot(self, x, y, **kwargs): """Plot on current axes.""" return self.gca().plot(x, y, **kwargs) - + def scatter(self, x, y, **kwargs): """Scatter on current axes.""" return self.gca().scatter(x, y, **kwargs) - + def bar(self, x, height, **kwargs): """Bar chart on current axes.""" return self.gca().bar(x, height, **kwargs) - + def fill_between(self, x, y1, y2, **kwargs): """Fill between on current axes.""" return self.gca().fill_between(x, y1, y2, **kwargs) - + def errorbar(self, x, y, **kwargs): """Error bar plot on current axes.""" return self.gca().errorbar(x, y, **kwargs) @@ -281,14 +284,14 @@ def errorbar(self, x, y, **kwargs): def text(self, x, y, s, **kwargs): """Add text on current axes.""" return self.gca().text(x, y, s, **kwargs) - + def xlabel(self, label: str): """Set x label on current axes.""" return self.gca().set_xlabel(label) - - def ylabel(self, label: str, axis: str = 'y'): + + def ylabel(self, label: str, axis: str = "y"): """Set y label on current axes. - + Parameters ---------- label : str @@ -297,15 +300,15 @@ def ylabel(self, label: str, axis: str = 'y'): Which axis: 'y' (left, default) or 'y2' (right) """ return self.gca().set_ylabel(label, axis=axis) - + def title(self, label: str): """Set title on current axes.""" return self.gca().set_title(label) - + def legend(self, **kwargs): """Add legend to current axes.""" return self.gca().legend(**kwargs) - + # File I/O methods def absolutize_file_references(self, base_dir) -> None: @@ -324,17 +327,17 @@ def absolutize_file_references(self, base_dir) -> None: base = Path(base_dir) for ax in self.axes_list: for fs in ax.file_series: - name = fs.get('data_file') + name = fs.get("data_file") if not name: continue ref = Path(name) if not ref.is_absolute(): - fs['data_file'] = (base / ref).resolve().as_posix() + fs["data_file"] = (base / ref).resolve().as_posix() def savefig_gle(self, filepath: str, **kwargs) -> Path: """ Save figure as GLE script. - + Parameters ---------- filepath : str @@ -344,7 +347,7 @@ def savefig_gle(self, filepath: str, **kwargs) -> Path: in a sibling ``.gleplot`` directory. **kwargs Additional options - + Returns ------- Path @@ -352,28 +355,33 @@ def savefig_gle(self, filepath: str, **kwargs) -> Path: """ output_path, export_dir = self._resolve_export_paths( filepath, - folder=kwargs.pop('folder', False), + folder=kwargs.pop("folder", False), ) export_dir.mkdir(parents=True, exist_ok=True) - + # Generate and save GLE content with data files gle_content, data_content = self._generate_gle_with_files() - + # Write script - output_path.write_text(gle_content, encoding='utf-8') - + output_path.write_text(gle_content, encoding="utf-8") + # Write data files in same directory for filename, content in data_content.items(): data_file = export_dir / filename - data_file.write_text(content, encoding='utf-8') - + data_file.write_text(content, encoding="utf-8") + return output_path - - def savefig(self, filepath: str, format: Optional[str] = None, - dpi: Optional[int] = None, **kwargs) -> Path: + + def savefig( + self, + filepath: str, + format: Optional[str] = None, + dpi: Optional[int] = None, + **kwargs, + ) -> Path: """ Save figure as GLE script and/or compiled output. - + Parameters ---------- filepath : str @@ -391,7 +399,7 @@ def savefig(self, filepath: str, format: Optional[str] = None, ``.gleplot`` directory. **kwargs Additional arguments - + Returns ------- Path @@ -399,39 +407,39 @@ def savefig(self, filepath: str, format: Optional[str] = None, """ output_path, export_dir = self._resolve_export_paths( filepath, - folder=kwargs.pop('folder', False), + folder=kwargs.pop("folder", False), ) export_dir.mkdir(parents=True, exist_ok=True) - + # Determine output format from the file suffix. Driven by # SUFFIX_TO_COMPILE_FORMAT (shared with GLECompiler) so this can't # silently drift out of sync with what the compiler supports. # Unknown/missing suffixes (including '.gle') default to 'gle'. if format is None: - format = SUFFIX_TO_COMPILE_FORMAT.get(output_path.suffix.lower(), 'gle') - + format = SUFFIX_TO_COMPILE_FORMAT.get(output_path.suffix.lower(), "gle") + # Write GLE script and data files - base_path = output_path.with_suffix('.gle') + base_path = output_path.with_suffix(".gle") gle_content, data_files = self._generate_gle_with_files() - base_path.write_text(gle_content, encoding='utf-8') - + base_path.write_text(gle_content, encoding="utf-8") + # Write data files in same directory for filename, content in data_files.items(): data_file = export_dir / filename - data_file.write_text(content, encoding='utf-8') - + data_file.write_text(content, encoding="utf-8") + # Compile if needed - if format != 'gle': + if format != "gle": if not self.compiler: raise RuntimeError( "GLE compiler not available. " "Install GLE or use savefig_gle() to save script only." ) - + output_dpi = dpi or self.dpi self.compiler.compile(str(base_path), format, dpi=output_dpi) - return output_path.with_suffix(f'.{format}') - + return output_path.with_suffix(f".{format}") + return base_path @staticmethod @@ -441,11 +449,11 @@ def _resolve_export_paths(filepath: str, folder: bool = False) -> Tuple[Path, Pa export_dir = output_path.parent if folder: - export_dir = output_path.parent / f'{output_path.stem}.gleplot' + export_dir = output_path.parent / f"{output_path.stem}.gleplot" output_path = export_dir / output_path.name return output_path, export_dir - + def _generate_gle(self) -> str: """Generate complete GLE script content.""" content, _ = self._generate_gle_with_files() @@ -474,11 +482,11 @@ def _build_metadata_dict(self, data_files: dict) -> dict: recovered from a parsed file are passed through verbatim. """ data = { - 'dpi': self.dpi, - 'sharex': self.sharex, - 'sharey': self.sharey, - 'msize_scale': self.marker_config.msize_scale, - 'import-data': sorted(data_files.keys()), + "dpi": self.dpi, + "sharex": self.sharex, + "sharey": self.sharey, + "msize_scale": self.marker_config.msize_scale, + "import-data": sorted(data_files.keys()), } data.update(self.metadata_extra) return data @@ -486,26 +494,29 @@ def _build_metadata_dict(self, data_files: dict) -> dict: def _generate_gle_with_files(self) -> tuple: """ Generate complete GLE script content with data files. - + Supports both single-plot and multi-subplot layouts. For a single axes (1,1,1), generates a simple graph block. For multiple axes, positions each graph using ``amove`` and explicit ``size`` commands based on the subplot grid. - + Uses figure's configured style, graph, and marker settings. - + Returns ------- tuple (gle_content, data_files_dict) """ # Pass configuration to writer - writer = GLEWriter(self.figsize, self.dpi, - style=self.style, - graph=self.graph, - marker=self.marker_config) - - is_single = (len(self.axes_list) <= 1) + writer = GLEWriter( + self.figsize, + self.dpi, + style=self.style, + graph=self.graph, + marker=self.marker_config, + ) + + is_single = len(self.axes_list) <= 1 # A figure with NO axes that carries passthrough (e.g. a graph the # recognizer swallowed into an opaque 'begin translate/scale' wrapper, @@ -513,20 +524,22 @@ def _generate_gle_with_files(self) -> tuple: # empty 'begin graph ... end graph'. Emit only the passthrough. A # genuinely empty figure with no passthrough keeps the historical # default empty graph block (existing tests rely on it). - no_fabricate = ( - not self.axes_list - and (self.passthrough_header or self.passthrough_trailer) + no_fabricate = not self.axes_list and ( + self.passthrough_header or self.passthrough_trailer ) if is_single and no_fabricate: - writer.add_preamble(include_graph_begin=False, - passthrough_header=self.passthrough_header) - writer.finalize(include_graph_end=False, - passthrough_trailer=self.passthrough_trailer) + writer.add_preamble( + include_graph_begin=False, passthrough_header=self.passthrough_header + ) + writer.finalize( + include_graph_end=False, passthrough_trailer=self.passthrough_trailer + ) elif is_single: # Single plot — backward-compatible simple layout - writer.add_preamble(include_graph_begin=True, - passthrough_header=self.passthrough_header) + writer.add_preamble( + include_graph_begin=True, passthrough_header=self.passthrough_header + ) writer.add_graph_size() if self.axes_list: @@ -545,30 +558,33 @@ def _generate_gle_with_files(self) -> tuple: ax.ymin = data_ymin if ax.ymax is None: ax.ymax = data_ymax - + self._write_axes_content(writer, ax) graph_passthrough = ax.passthrough else: graph_passthrough = None - writer.finalize(include_graph_end=True, - graph_passthrough=graph_passthrough, - passthrough_trailer=self.passthrough_trailer) + writer.finalize( + include_graph_end=True, + graph_passthrough=graph_passthrough, + passthrough_trailer=self.passthrough_trailer, + ) else: # Multi-subplot layout - writer.add_preamble(include_graph_begin=False, - passthrough_header=self.passthrough_header) - + writer.add_preamble( + include_graph_begin=False, passthrough_header=self.passthrough_header + ) + # Determine grid dimensions from axes positions max_rows = max(ax.position[0] for ax in self.axes_list) max_cols = max(ax.position[1] for ax in self.axes_list) - + # Synchronize axis limits for shared axes if self.sharex: self._synchronize_x_limits() if self.sharey: self._synchronize_y_limits() - + # Calculate axis limits from data for any axes without explicit limits # This is especially important for bar charts for ax in self.axes_list: @@ -584,14 +600,14 @@ def _generate_gle_with_files(self) -> tuple: ax.ymin = data_ymin if ax.ymax is None: ax.ymax = data_ymax - + # Calculate per-subplot dimensions in cm. # Default margins/spacing are heuristic, but can be overridden via # subplots_adjust(left=..., right=..., top=..., bottom=..., wspace=..., hspace=...). - + # Check if any subplot has a title has_titles = any(ax.title_text for ax in self.axes_list) - + if self.sharex or self.sharey: # Top margin: more room needed if subplots have titles margin_top = 1.2 if has_titles else 0.5 @@ -606,16 +622,16 @@ def _generate_gle_with_files(self) -> tuple: margin_bottom = 1.0 margin_left = 1.0 margin_right = 1.0 - + # Convert margin overrides from normalized figure fractions to cm. - if 'left' in self._subplot_adjust: - margin_left = self._subplot_adjust['left'] * writer.width_cm - if 'right' in self._subplot_adjust: - margin_right = (1.0 - self._subplot_adjust['right']) * writer.width_cm - if 'bottom' in self._subplot_adjust: - margin_bottom = self._subplot_adjust['bottom'] * writer.height_cm - if 'top' in self._subplot_adjust: - margin_top = (1.0 - self._subplot_adjust['top']) * writer.height_cm + if "left" in self._subplot_adjust: + margin_left = self._subplot_adjust["left"] * writer.width_cm + if "right" in self._subplot_adjust: + margin_right = (1.0 - self._subplot_adjust["right"]) * writer.width_cm + if "bottom" in self._subplot_adjust: + margin_bottom = self._subplot_adjust["bottom"] * writer.height_cm + if "top" in self._subplot_adjust: + margin_top = (1.0 - self._subplot_adjust["top"]) * writer.height_cm avail_w = writer.width_cm - margin_left - margin_right avail_h = writer.height_cm - margin_bottom - margin_top @@ -623,8 +639,8 @@ def _generate_gle_with_files(self) -> tuple: # Spacing between subplots; defaults preserve existing behavior. default_hspace_cm = 0.0 if self.sharey else 1.5 default_vspace_cm = 0.0 if self.sharex else 2.0 - wspace_frac = self._subplot_adjust.get('wspace') - hspace_frac = self._subplot_adjust.get('hspace') + wspace_frac = self._subplot_adjust.get("wspace") + hspace_frac = self._subplot_adjust.get("hspace") if max_cols > 1 and wspace_frac is not None: denom = max_cols + wspace_frac * (max_cols - 1) @@ -643,29 +659,33 @@ def _generate_gle_with_files(self) -> tuple: vspace = default_vspace_cm usable_h = avail_h - (max_rows - 1) * vspace cell_h = usable_h / max_rows - + for ax in self.axes_list: rows, cols, idx = ax.position # Convert 1-based index to row/col (row-major, top-to-bottom) - row = (idx - 1) // cols # 0-based, 0 = top row - col = (idx - 1) % cols # 0-based, 0 = left col - + row = (idx - 1) // cols # 0-based, 0 = top row + col = (idx - 1) % cols # 0-based, 0 = left col + # GLE coordinates: origin is bottom-left, y increases upward x_pos = margin_left + col * (cell_w + hspace) - y_pos = writer.height_cm - margin_top - (row + 1) * cell_h - row * vspace - + y_pos = ( + writer.height_cm - margin_top - (row + 1) * cell_h - row * vspace + ) + writer.add_amove(x_pos, y_pos) writer.begin_graph() - writer.add_graph_size(width_cm=cell_w, height_cm=cell_h, - force_size=True) - + writer.add_graph_size( + width_cm=cell_w, height_cm=cell_h, force_size=True + ) + self._write_axes_content(writer, ax) writer.end_graph(passthrough=ax.passthrough) - writer.lines_gle.append('') # Blank line between subplots + writer.lines_gle.append("") # Blank line between subplots - writer.finalize(include_graph_end=False, - passthrough_trailer=self.passthrough_trailer) + writer.finalize( + include_graph_end=False, passthrough_trailer=self.passthrough_trailer + ) # Splice the metadata block in after the two header comment lines # ('! GLE graphics file' / '! Generated by gleplot') and before the @@ -677,13 +697,13 @@ def _generate_gle_with_files(self) -> tuple: writer.lines_gle[2:2] = metadata_lines return writer.get_gle_content(), writer.data_files - + def _write_axes_content(self, writer: GLEWriter, ax: Axes): """ Write all plot content for a single Axes into the current graph block. - + This method is shared between single-plot and multi-subplot paths. - + Parameters ---------- writer : GLEWriter @@ -697,9 +717,9 @@ def _write_axes_content(self, writer: GLEWriter, ax: Axes): ylabel=ax.ylabel_text or None, y2label=ax.y2label_text or None, title=ax.title_text or None, - xlog=(ax.xscale == 'log'), - ylog=(ax.yscale == 'log'), - y2log=(ax.y2scale == 'log'), + xlog=(ax.xscale == "log"), + ylog=(ax.yscale == "log"), + y2log=(ax.y2scale == "log"), xmin=ax.xmin, xmax=ax.xmax, ymin=ax.ymin, @@ -710,135 +730,137 @@ def _write_axes_content(self, writer: GLEWriter, ax: Axes): show_ylabel=ax._show_ylabel, show_xticks=ax._show_xticks, show_yticks=ax._show_yticks, - remove_last_xtick=getattr(ax, '_remove_last_xtick', False), - remove_last_ytick=getattr(ax, '_remove_last_ytick', False), - remove_first_xtick=getattr(ax, '_remove_first_xtick', False), - remove_first_ytick=getattr(ax, '_remove_first_ytick', False), + remove_last_xtick=getattr(ax, "_remove_last_xtick", False), + remove_last_ytick=getattr(ax, "_remove_last_ytick", False), + remove_first_xtick=getattr(ax, "_remove_first_xtick", False), + remove_first_ytick=getattr(ax, "_remove_first_ytick", False), ) - + # Add fill regions (background) for fill_data in ax.fills: writer.add_fill_between( - fill_data['x'], - fill_data['y1'], - fill_data['y2'], - fill_data['data_file'], - fill_data['color'], - fill_data['alpha'], - offset=fill_data.get('offset', 0.0), - column_names=fill_data.get('column_names'), + fill_data["x"], + fill_data["y1"], + fill_data["y2"], + fill_data["data_file"], + fill_data["color"], + fill_data["alpha"], + offset=fill_data.get("offset", 0.0), + column_names=fill_data.get("column_names"), ) # Add bar charts for bar_data in ax.bars: writer.add_bar_chart( - bar_data['x'], - bar_data['height'], - bar_data['data_file'], - bar_data['colors'], - bar_data['label'], - column_names=bar_data.get('column_names'), + bar_data["x"], + bar_data["height"], + bar_data["data_file"], + bar_data["colors"], + bar_data["label"], + column_names=bar_data.get("column_names"), ) # Add line plots for line_data in ax.lines: writer.add_plot_line( - line_data['x'], - line_data['y'], - line_data['data_file'], - color=line_data['color'], - linestyle=line_data['linestyle'], - linewidth=line_data['linewidth'], - label=line_data['label'], - marker=line_data.get('marker'), - markersize=line_data.get('markersize', 0.1), - yaxis=line_data.get('yaxis', 'y'), - offset=line_data.get('offset', 0.0), - column_names=line_data.get('column_names'), + line_data["x"], + line_data["y"], + line_data["data_file"], + color=line_data["color"], + linestyle=line_data["linestyle"], + linewidth=line_data["linewidth"], + label=line_data["label"], + marker=line_data.get("marker"), + markersize=line_data.get("markersize", 0.1), + yaxis=line_data.get("yaxis", "y"), + offset=line_data.get("offset", 0.0), + column_names=line_data.get("column_names"), ) # Add scatter plots for scatter_data in ax.scatters: writer.add_plot_line( - scatter_data['x'], - scatter_data['y'], - scatter_data['data_file'], - color=scatter_data['color'], - linestyle=scatter_data.get('linestyle', 'none'), - marker=scatter_data['marker'], - markersize=scatter_data['markersize'], - label=scatter_data['label'], - yaxis=scatter_data.get('yaxis', 'y'), - offset=scatter_data.get('offset', 0.0), - column_names=scatter_data.get('column_names'), + scatter_data["x"], + scatter_data["y"], + scatter_data["data_file"], + color=scatter_data["color"], + linestyle=scatter_data.get("linestyle", "none"), + marker=scatter_data["marker"], + markersize=scatter_data["markersize"], + label=scatter_data["label"], + yaxis=scatter_data.get("yaxis", "y"), + offset=scatter_data.get("offset", 0.0), + column_names=scatter_data.get("column_names"), ) # Add errorbar plots for eb_data in ax.errorbars: writer.add_errorbar( - eb_data['x'], - eb_data['y'], - eb_data['data_file'], - color=eb_data['color'], - linestyle=eb_data['linestyle'], - linewidth=eb_data['linewidth'], - label=eb_data['label'], - marker=eb_data['marker'], - markersize=eb_data['markersize'], - yerr_up=eb_data['yerr_up'], - yerr_down=eb_data['yerr_down'], - xerr_left=eb_data['xerr_left'], - xerr_right=eb_data['xerr_right'], - capsize=eb_data.get('gle_capsize', eb_data.get('capsize')), - yaxis=eb_data.get('yaxis', 'y'), - offset=eb_data.get('offset', 0.0), - column_names=eb_data.get('column_names'), + eb_data["x"], + eb_data["y"], + eb_data["data_file"], + color=eb_data["color"], + linestyle=eb_data["linestyle"], + linewidth=eb_data["linewidth"], + label=eb_data["label"], + marker=eb_data["marker"], + markersize=eb_data["markersize"], + yerr_up=eb_data["yerr_up"], + yerr_down=eb_data["yerr_down"], + xerr_left=eb_data["xerr_left"], + xerr_right=eb_data["xerr_right"], + capsize=eb_data.get("gle_capsize", eb_data.get("capsize")), + yaxis=eb_data.get("yaxis", "y"), + offset=eb_data.get("offset", 0.0), + column_names=eb_data.get("column_names"), ) # Add external-file series (no generated data files). for fs_data in ax.file_series: - series_type = fs_data.get('series_type', 'errorbar') - if series_type == 'line': + series_type = fs_data.get("series_type", "errorbar") + if series_type == "line": writer.add_plot_line_from_file( - fs_data['data_file'], - fs_data['x_col'], - fs_data['y_col'], - color=fs_data.get('color', 'BLUE'), - linestyle=fs_data.get('linestyle', '-'), - linewidth=fs_data.get('linewidth', 1.0), - label=fs_data.get('label'), - yaxis=fs_data.get('yaxis', 'y'), + fs_data["data_file"], + fs_data["x_col"], + fs_data["y_col"], + color=fs_data.get("color", "BLUE"), + linestyle=fs_data.get("linestyle", "-"), + linewidth=fs_data.get("linewidth", 1.0), + label=fs_data.get("label"), + yaxis=fs_data.get("yaxis", "y"), ) else: writer.add_errorbar_from_file( - fs_data['data_file'], - fs_data['x_col'], - fs_data['y_col'], - yerr_col=fs_data.get('yerr_col'), - color=fs_data['color'], - marker=fs_data.get('marker'), - markersize=fs_data.get('markersize', 0.1), - label=fs_data.get('label'), - capsize=fs_data.get('capsize'), - yaxis=fs_data.get('yaxis', 'y'), + fs_data["data_file"], + fs_data["x_col"], + fs_data["y_col"], + yerr_col=fs_data.get("yerr_col"), + color=fs_data["color"], + marker=fs_data.get("marker"), + markersize=fs_data.get("markersize", 0.1), + label=fs_data.get("label"), + capsize=fs_data.get("capsize"), + yaxis=fs_data.get("yaxis", "y"), ) # Add text annotations. for text_data in ax.texts: writer.add_text( - x=text_data['x'], - y=text_data['y'], - text=text_data['text'], - color=text_data.get('color', 'BLACK'), - fontsize=text_data.get('fontsize'), - halign=text_data.get('ha', 'left'), - box_color=text_data.get('box_color'), + x=text_data["x"], + y=text_data["y"], + text=text_data["text"], + color=text_data.get("color", "BLACK"), + fontsize=text_data.get("fontsize"), + halign=text_data.get("ha", "left"), + box_color=text_data.get("box_color"), ) - + # Add legend if needed. legend_on is tri-state: None means auto # (show iff labels exist); True/False is an explicit user choice. - legend_sources = ax.lines + ax.scatters + ax.bars + ax.errorbars + ax.file_series - labels_present = any(series.get('label') for series in legend_sources) + legend_sources = ( + ax.lines + ax.scatters + ax.bars + ax.errorbars + ax.file_series + ) + labels_present = any(series.get("label") for series in legend_sources) show_legend = ax.legend_on if ax.legend_on is not None else labels_present if show_legend: writer.add_legend(ax.legend_pos) @@ -846,13 +868,13 @@ def _write_axes_content(self, writer: GLEWriter, ax: Axes): # GLE draws an implicit key from per-dataset key "label" tokens; # it must be switched off explicitly. writer.add_key_off() - + def _synchronize_x_limits(self): """Synchronize x-axis limits across all axes when sharex is enabled.""" # Find global x-axis limits xmin_global = None xmax_global = None - + for ax in self.axes_list: # Calculate data limits if not explicitly set if ax.xmin is None or ax.xmax is None: @@ -861,7 +883,7 @@ def _synchronize_x_limits(self): ax.xmin = data_xmin if ax.xmax is None: ax.xmax = data_xmax - + # Track global limits if ax.xmin is not None: if xmin_global is None or ax.xmin < xmin_global: @@ -869,18 +891,18 @@ def _synchronize_x_limits(self): if ax.xmax is not None: if xmax_global is None or ax.xmax > xmax_global: xmax_global = ax.xmax - + # Apply global limits to all axes for ax in self.axes_list: ax.xmin = xmin_global ax.xmax = xmax_global - + def _synchronize_y_limits(self): """Synchronize y-axis limits across all axes when sharey is enabled.""" # Find global y-axis limits ymin_global = None ymax_global = None - + for ax in self.axes_list: # Calculate data limits if not explicitly set if ax.ymin is None or ax.ymax is None: @@ -889,7 +911,7 @@ def _synchronize_y_limits(self): ax.ymin = data_ymin if ax.ymax is None: ax.ymax = data_ymax - + # Track global limits if ax.ymin is not None: if ymin_global is None or ax.ymin < ymin_global: @@ -897,74 +919,74 @@ def _synchronize_y_limits(self): if ax.ymax is not None: if ymax_global is None or ax.ymax > ymax_global: ymax_global = ax.ymax - + # Apply global limits to all axes for ax in self.axes_list: ax.ymin = ymin_global ax.ymax = ymax_global - + def _get_data_xlim(self, ax: Axes) -> Tuple[Optional[float], Optional[float]]: """Calculate x-axis limits from data.""" xmin, xmax = None, None - + for data_list in [ax.lines, ax.scatters, ax.bars, ax.errorbars]: for data in data_list: - x = np.asarray(data['x']) + x = np.asarray(data["x"]) if len(x) > 0: if xmin is None or x.min() < xmin: xmin = float(x.min()) if xmax is None or x.max() > xmax: xmax = float(x.max()) - + for fill_data in ax.fills: - x = np.asarray(fill_data['x']) + x = np.asarray(fill_data["x"]) if len(x) > 0: if xmin is None or x.min() < xmin: xmin = float(x.min()) if xmax is None or x.max() > xmax: xmax = float(x.max()) - + return xmin, xmax - + def _get_data_ylim(self, ax: Axes) -> Tuple[Optional[float], Optional[float]]: """Calculate y-axis limits from data.""" ymin, ymax = None, None - + # A series' ``offset`` shifts its trace vertically at plot time (the .dat # values stay raw), so autoscale must add it back when bounding the data # -- otherwise a waterfall stack falls off the auto-computed axis. for data_list in [ax.lines, ax.scatters]: for data in data_list: - y = np.asarray(data['y']) + data.get('offset', 0.0) + y = np.asarray(data["y"]) + data.get("offset", 0.0) if len(y) > 0: if ymin is None or y.min() < ymin: ymin = float(y.min()) if ymax is None or y.max() > ymax: ymax = float(y.max()) - + for bar_data in ax.bars: - height = np.asarray(bar_data['height']) + height = np.asarray(bar_data["height"]) if len(height) > 0: if ymin is None or height.min() < ymin: ymin = float(min(0, height.min())) if ymax is None or height.max() > ymax: ymax = float(height.max()) - + for fill_data in ax.fills: - off = fill_data.get('offset', 0.0) - y1 = np.asarray(fill_data['y1']) + off - y2 = np.asarray(fill_data['y2']) + off + off = fill_data.get("offset", 0.0) + y1 = np.asarray(fill_data["y1"]) + off + y2 = np.asarray(fill_data["y2"]) + off all_y = np.concatenate([y1, y2]) if len(all_y) > 0: if ymin is None or all_y.min() < ymin: ymin = float(all_y.min()) if ymax is None or all_y.max() > ymax: ymax = float(all_y.max()) - + for eb_data in ax.errorbars: - y = np.asarray(eb_data['y']) + eb_data.get('offset', 0.0) - yerr_up = eb_data.get('yerr_up') - yerr_down = eb_data.get('yerr_down') + y = np.asarray(eb_data["y"]) + eb_data.get("offset", 0.0) + yerr_up = eb_data.get("yerr_up") + yerr_down = eb_data.get("yerr_down") if len(y) > 0: y_with_err = y.copy() @@ -976,39 +998,39 @@ def _get_data_ylim(self, ax: Axes) -> Tuple[Optional[float], Optional[float]]: y_with_err_down = y - np.asarray(yerr_down) if ymin is None or y_with_err_down.min() < ymin: ymin = float(y_with_err_down.min()) - + if ymin is None or y.min() < ymin: ymin = float(y.min()) if ymax is None or y.max() > ymax: ymax = float(y.max()) - + return ymin, ymax - - def view(self, dpi: Optional[int] = None, format: str = 'png') -> Optional[object]: + + def view(self, dpi: Optional[int] = None, format: str = "png") -> Optional[object]: """ Display the figure inline (in Jupyter notebooks) or save to a temporary file. - + This method renders the figure to an image format and displays it if running in a Jupyter notebook or IPython environment. Otherwise, it saves to a temporary file and returns the path. - + Parameters ---------- dpi : int, optional Resolution in dots per inch. If None, uses figure's dpi setting. format : {'png', 'pdf'}, optional Output format. Default is 'png' for inline display. - + Returns ------- Path or None Path to the generated file, or None when displayed inline in Jupyter. - + Raises ------ RuntimeError If GLE compiler is not available. - + Examples -------- >>> import gleplot as glp @@ -1016,62 +1038,62 @@ def view(self, dpi: Optional[int] = None, format: str = 'png') -> Optional[objec >>> ax = fig.add_subplot(111) >>> ax.plot([1, 2, 3], [1, 4, 9]) >>> fig.view() # Display in notebook - + Notes ----- Requires GLE to be installed for compilation. In non-Jupyter environments, saves to a temporary file instead. """ import tempfile - + if not self.compiler: - raise RuntimeError( - "GLE compiler not available. Install GLE to use view()." - ) - + raise RuntimeError("GLE compiler not available. Install GLE to use view().") + output_dpi = dpi or self.dpi - + # Try to detect Jupyter/IPython environment try: from IPython import get_ipython from IPython.display import Image, display - + ipython = get_ipython() - in_notebook = ipython is not None and 'IPKernelApp' in get_ipython().config + in_notebook = ipython is not None and "IPKernelApp" in get_ipython().config except ImportError: in_notebook = False - + # Create temporary file - with tempfile.NamedTemporaryFile(suffix=f'.{format}', delete=False) as tmp: + with tempfile.NamedTemporaryFile(suffix=f".{format}", delete=False) as tmp: tmp_path = Path(tmp.name) - + try: # Save to temporary file self.savefig(str(tmp_path), format=format, dpi=output_dpi) - + if in_notebook: # Display inline in Jupyter - if format == 'png': + if format == "png": img = Image(filename=str(tmp_path)) display(img) return None - elif format == 'pdf': + elif format == "pdf": # For PDF, try to display or provide a link print(f"PDF saved to: {tmp_path}") - print("Note: PDF inline display limited in Jupyter. Consider using 'png' format.") + print( + "Note: PDF inline display limited in Jupyter. Consider using 'png' format." + ) return tmp_path else: # Not in notebook - inform user of temp file location print(f"Plot saved to temporary file: {tmp_path}") print(f"Open this file to view the plot.") return tmp_path - + except Exception as e: # Clean up on error if tmp_path.exists(): tmp_path.unlink() raise e - + # -- Serialization ------------------------------------------------------ def to_dict(self) -> dict: @@ -1122,35 +1144,35 @@ def to_dict(self) -> dict: from . import axes as _axes_module figure_block = { - 'figsize': list(self.figsize), - 'dpi': self.dpi, - 'sharex': self.sharex, - 'sharey': self.sharey, - 'data_prefix': self.data_prefix, - 'local_data_counter': self._local_data_counter, - 'global_data_counter': _axes_module._global_data_file_counter, - 'used_data_files': sorted(self._used_data_files), - 'subplot_adjust': {k: float(v) for k, v in self._subplot_adjust.items()}, - 'passthrough_header': list(self.passthrough_header), - 'passthrough_trailer': list(self.passthrough_trailer), - 'metadata_extra': dict(self.metadata_extra), - 'config': { - 'style': self.style.to_dict(), - 'graph': self.graph.to_dict(), - 'marker': self.marker_config.to_dict(), + "figsize": list(self.figsize), + "dpi": self.dpi, + "sharex": self.sharex, + "sharey": self.sharey, + "data_prefix": self.data_prefix, + "local_data_counter": self._local_data_counter, + "global_data_counter": _axes_module._global_data_file_counter, + "used_data_files": sorted(self._used_data_files), + "subplot_adjust": {k: float(v) for k, v in self._subplot_adjust.items()}, + "passthrough_header": list(self.passthrough_header), + "passthrough_trailer": list(self.passthrough_trailer), + "metadata_extra": dict(self.metadata_extra), + "config": { + "style": self.style.to_dict(), + "graph": self.graph.to_dict(), + "marker": self.marker_config.to_dict(), }, - 'axes': [ax.to_dict() for ax in self.axes_list], + "axes": [ax.to_dict() for ax in self.axes_list], } return { - 'format': PROJECT_FORMAT, - 'version': PROJECT_VERSION, - 'gleplot_version': __version__, - 'figure': figure_block, + "format": PROJECT_FORMAT, + "version": PROJECT_VERSION, + "gleplot_version": __version__, + "figure": figure_block, } @classmethod - def from_dict(cls, d: dict) -> 'Figure': + def from_dict(cls, d: dict) -> "Figure": """Reconstruct an equivalent :class:`Figure` from a project dict. Parameters @@ -1187,65 +1209,76 @@ def from_dict(cls, d: dict) -> 'Figure': """ from . import axes as _axes_module - fmt = d.get('format') + fmt = d.get("format") if fmt != PROJECT_FORMAT: raise ValueError( f"Unrecognized project format {fmt!r}; expected {PROJECT_FORMAT!r}" ) - version = d.get('version') + version = d.get("version") if version != PROJECT_VERSION: raise ValueError( f"Unsupported project version {version!r}; this build supports " f"version {PROJECT_VERSION}" ) - fig_block = d.get('figure') + fig_block = d.get("figure") if not isinstance(fig_block, dict): raise ValueError("Project envelope is missing a 'figure' object") - config = fig_block.get('config') or {} + config = fig_block.get("config") or {} style = ( - GLEStyleConfig(**_filtered_dataclass_kwargs(GLEStyleConfig, config['style'])) - if config.get('style') else None + GLEStyleConfig( + **_filtered_dataclass_kwargs(GLEStyleConfig, config["style"]) + ) + if config.get("style") + else None ) graph = ( - GLEGraphConfig(**_filtered_dataclass_kwargs(GLEGraphConfig, config['graph'])) - if config.get('graph') else None + GLEGraphConfig( + **_filtered_dataclass_kwargs(GLEGraphConfig, config["graph"]) + ) + if config.get("graph") + else None ) marker = ( - GLEMarkerConfig(**_filtered_dataclass_kwargs(GLEMarkerConfig, config['marker'])) - if config.get('marker') else None + GLEMarkerConfig( + **_filtered_dataclass_kwargs(GLEMarkerConfig, config["marker"]) + ) + if config.get("marker") + else None ) - figsize = fig_block.get('figsize', (8, 6)) + figsize = fig_block.get("figsize", (8, 6)) figsize = tuple(figsize) fig = cls( figsize=figsize, - dpi=fig_block.get('dpi', 100), + dpi=fig_block.get("dpi", 100), style=style, graph=graph, marker=marker, - sharex=fig_block.get('sharex', False), - sharey=fig_block.get('sharey', False), - data_prefix=fig_block.get('data_prefix'), + sharex=fig_block.get("sharex", False), + sharey=fig_block.get("sharey", False), + data_prefix=fig_block.get("data_prefix"), ) - fig._local_data_counter = fig_block.get('local_data_counter', 0) - fig._used_data_files = set(fig_block.get('used_data_files', [])) + fig._local_data_counter = fig_block.get("local_data_counter", 0) + fig._used_data_files = set(fig_block.get("used_data_files", [])) fig._subplot_adjust = { - k: float(v) for k, v in (fig_block.get('subplot_adjust') or {}).items() + k: float(v) for k, v in (fig_block.get("subplot_adjust") or {}).items() } - fig.passthrough_header = list(fig_block.get('passthrough_header', [])) - fig.passthrough_trailer = list(fig_block.get('passthrough_trailer', [])) - fig.metadata_extra = dict(fig_block.get('metadata_extra', {})) + fig.passthrough_header = list(fig_block.get("passthrough_header", [])) + fig.passthrough_trailer = list(fig_block.get("passthrough_trailer", [])) + fig.metadata_extra = dict(fig_block.get("metadata_extra", {})) - saved_counter = fig_block.get('global_data_counter', 0) + saved_counter = fig_block.get("global_data_counter", 0) _axes_module._global_data_file_counter = max( _axes_module._global_data_file_counter, saved_counter ) - fig.axes_list = [Axes.from_dict(fig, ax_d) for ax_d in fig_block.get('axes', [])] + fig.axes_list = [ + Axes.from_dict(fig, ax_d) for ax_d in fig_block.get("axes", []) + ] fig._current_axes = fig.axes_list[-1] if fig.axes_list else None return fig diff --git a/src/gleplot/gui/data/panel.py b/src/gleplot/gui/data/panel.py index 92a69ac..166eb8b 100644 --- a/src/gleplot/gui/data/panel.py +++ b/src/gleplot/gui/data/panel.py @@ -73,8 +73,7 @@ #: Tooltip shown on non-editable (external-file) column headers. _EXTERNAL_HEADER_TOOLTIP = ( - "Column names come from the referenced file and are not edited by " - "gleplot." + "Column names come from the referenced file and are not edited by " "gleplot." ) #: Maximum number of rows shown in the preview table. @@ -301,8 +300,12 @@ def populate_from_figure(self, *_signal_args) -> None: first_added: Optional[str] = None for ax in getattr(fig, "axes_list", []): entries = ( - ax.lines + ax.scatters + ax.bars + ax.fills - + ax.errorbars + ax.file_series + ax.lines + + ax.scatters + + ax.bars + + ax.fills + + ax.errorbars + + ax.file_series ) for entry in entries: name = entry.get("data_file") diff --git a/src/gleplot/gui/panels/axes_panel.py b/src/gleplot/gui/panels/axes_panel.py index 6740db4..c9912d8 100644 --- a/src/gleplot/gui/panels/axes_panel.py +++ b/src/gleplot/gui/panels/axes_panel.py @@ -225,9 +225,10 @@ def refresh(self) -> None: # legend_on is tri-state (None = auto: shown iff labels exist); # display the EFFECTIVE state so the checkbox matches the preview. if ax.legend_on is None: - sources = (ax.lines + ax.scatters + ax.bars + ax.errorbars - + ax.file_series) - effective = any(s.get('label') for s in sources) + sources = ( + ax.lines + ax.scatters + ax.bars + ax.errorbars + ax.file_series + ) + effective = any(s.get("label") for s in sources) else: effective = bool(ax.legend_on) self.legend_enabled_check.setChecked(effective) diff --git a/src/gleplot/gui/panels/series_panel.py b/src/gleplot/gui/panels/series_panel.py index 7da8c63..8fc00af 100644 --- a/src/gleplot/gui/panels/series_panel.py +++ b/src/gleplot/gui/panels/series_panel.py @@ -396,7 +396,9 @@ def _on_selection_changed(self, row: int) -> None: series = series_list[index] if index < len(series_list) else None self._populate_style_editor(kind, series) - def _populate_style_editor(self, kind: Optional[str], series: Optional[dict] = None) -> None: + def _populate_style_editor( + self, kind: Optional[str], series: Optional[dict] = None + ) -> None: was_updating = self._updating self._updating = True try: @@ -406,7 +408,9 @@ def _populate_style_editor(self, kind: Optional[str], series: Optional[dict] = N return data_error = series.get("data_error") - self._set_error_strip_visible(bool(data_error), str(data_error) if data_error else "") + self._set_error_strip_visible( + bool(data_error), str(data_error) if data_error else "" + ) applicable = _applicable_controls(kind, series) self._set_style_controls_enabled(True, applicable) @@ -419,11 +423,17 @@ def _populate_style_editor(self, kind: Optional[str], series: Optional[dict] = N self._update_color_swatch(rgb) if applicable.get("linestyle"): - self._set_combo_text(self.linestyle_combo, series.get("linestyle") or "-") + self._set_combo_text( + self.linestyle_combo, series.get("linestyle") or "-" + ) if applicable.get("marker"): marker_name = series.get("marker") - code = _GLE_MARKER_TO_CODE.get(marker_name, "none") if marker_name else "none" + code = ( + _GLE_MARKER_TO_CODE.get(marker_name, "none") + if marker_name + else "none" + ) self._set_combo_text(self.marker_combo, code) if applicable.get("linewidth"): @@ -461,7 +471,9 @@ def _set_error_strip_visible(self, visible: bool, message: str = "") -> None: self.error_label.setText(message) self.error_label.setToolTip(message) - def _set_style_controls_enabled(self, enabled: bool, applicable: Optional[dict] = None) -> None: + def _set_style_controls_enabled( + self, enabled: bool, applicable: Optional[dict] = None + ) -> None: applicable = applicable or {} self.label_edit.setEnabled(enabled) self.color_button.setEnabled(enabled) @@ -697,11 +709,7 @@ def _format_entry(kind: str, series: dict, index: int) -> tuple[str, Optional[st """ data_error = series.get("data_error") if data_error: - file_label = ( - series.get("label") - or series.get("data_file") - or f"series {index}" - ) + file_label = series.get("label") or series.get("data_file") or f"series {index}" text = f"{_BROKEN_MARKER} {kind}: {file_label} (missing data)" return text, str(data_error) label = series.get("label") or f"series {index}" @@ -718,20 +726,49 @@ def _applicable_controls(kind: str, series: dict) -> dict: # GLE renders markers on line datasets natively (Axes.plot preserves # a marker alongside a solid/dashed line), so a line series may carry # both a line style and a marker with a marker size. - return {"color": True, "marker": True, "linestyle": True, - "linewidth": True, "markersize": True, "offset": True} + return { + "color": True, + "marker": True, + "linestyle": True, + "linewidth": True, + "markersize": True, + "offset": True, + } if kind == "scatter": - return {"color": True, "marker": True, "linestyle": False, - "linewidth": False, "markersize": True, "offset": True} + return { + "color": True, + "marker": True, + "linestyle": False, + "linewidth": False, + "markersize": True, + "offset": True, + } if kind == "errorbar": - return {"color": True, "marker": True, "linestyle": True, - "linewidth": True, "markersize": True, "offset": True} + return { + "color": True, + "marker": True, + "linestyle": True, + "linewidth": True, + "markersize": True, + "offset": True, + } if kind == "bar": - return {"color": True, "marker": False, "linestyle": False, - "linewidth": False, "markersize": False} + return { + "color": True, + "marker": False, + "linestyle": False, + "linewidth": False, + "markersize": False, + } if kind == "fill": - return {"color": True, "marker": False, "linestyle": False, - "linewidth": False, "markersize": False, "offset": True} + return { + "color": True, + "marker": False, + "linestyle": False, + "linewidth": False, + "markersize": False, + "offset": True, + } if kind == "file_series": if series.get("data_error"): # Broken reference: the file couldn't be read, so there is no @@ -741,13 +778,33 @@ def _applicable_controls(kind: str, series: dict) -> dict: # data"). Color and label are metadata only and always safe to # edit -- they affect regeneration regardless of whether the # data loads. - return {"color": True, "marker": False, "linestyle": False, - "linewidth": False, "markersize": False} + return { + "color": True, + "marker": False, + "linestyle": False, + "linewidth": False, + "markersize": False, + } series_type = series.get("series_type") if series_type == "errorbar": - return {"color": True, "marker": True, "linestyle": False, - "linewidth": False, "markersize": True} - return {"color": True, "marker": False, "linestyle": True, - "linewidth": True, "markersize": False} - return {"color": False, "marker": False, "linestyle": False, - "linewidth": False, "markersize": False} + return { + "color": True, + "marker": True, + "linestyle": False, + "linewidth": False, + "markersize": True, + } + return { + "color": True, + "marker": False, + "linestyle": True, + "linewidth": True, + "markersize": False, + } + return { + "color": False, + "marker": False, + "linestyle": False, + "linewidth": False, + "markersize": False, + } diff --git a/src/gleplot/parser/recognizer.py b/src/gleplot/parser/recognizer.py index 2e04177..e66c4ce 100644 --- a/src/gleplot/parser/recognizer.py +++ b/src/gleplot/parser/recognizer.py @@ -172,7 +172,6 @@ resolve_data_reference, ) - __all__ = ["RecognizedFigure", "parse_gle_figure"] @@ -199,6 +198,7 @@ class RecognizedFigure: # Token-stream helpers # --------------------------------------------------------------------------- # + def _words_and_values(stmt: Statement) -> List[Token]: """Tokens of a statement, dropping comments.""" return [t for t in stmt.tokens if t.type is not TokenType.COMMENT] @@ -288,6 +288,7 @@ def _collect_value(toks: List[Token], start: int) -> Tuple[Optional[float], int] # The recognizer # --------------------------------------------------------------------------- # + class _Recognizer: """Stateful recognizer for one document. See :func:`parse_gle_figure`.""" @@ -365,7 +366,7 @@ def run(self) -> RecognizedFigure: # --- Preamble: everything before the first graph-related node. --- first_graph_start = self._first_graph_region_start(nodes, graph_indices) - (figsize, font, fontsize, passthrough_header) = self._parse_preamble( + figsize, font, fontsize, passthrough_header = self._parse_preamble( nodes[:first_graph_start] ) @@ -450,9 +451,18 @@ def run(self) -> RecognizedFigure: #: flow / subroutines). The syntax parser has no awareness of these, so a #: graph inside such a construct parses as a top-level graph and editing it #: would restructure the file. We only *warn*; parse behavior is unchanged. - _PROGRAMMATIC_KEYWORDS = frozenset({ - "sub", "if", "for", "while", "until", "next", "else", "return", - }) + _PROGRAMMATIC_KEYWORDS = frozenset( + { + "sub", + "if", + "for", + "while", + "until", + "next", + "else", + "return", + } + ) #: Opaque wrapper block types that establish a coordinate transform. A #: graph nested inside one of these is preserved wholesale as raw GLE. @@ -508,7 +518,9 @@ def _first_graph_region_start(self, nodes, graph_indices) -> int: return j return first_graph - def _parse_preamble(self, pre_nodes) -> Tuple[Tuple[float, float], str, float, List[str]]: + def _parse_preamble( + self, pre_nodes + ) -> Tuple[Tuple[float, float], str, float, List[str]]: figsize = (8.0, 6.0) font = "" fontsize = 12.0 @@ -582,24 +594,37 @@ def _metadata_line_numbers(self) -> set: def _parse_graph_block(self, block: GraphBlock, marker_cfg, smooth_flags) -> dict: """Parse one ``begin graph`` .. ``end graph`` into an axes-info dict.""" info = { - "size_cm": None, # (w, h) if explicit 'size' present - "scale_mode": None, # 'auto' | 'fixed' | None + "size_cm": None, # (w, h) if explicit 'size' present + "scale_mode": None, # 'auto' | 'fixed' | None "title": None, "xlabel": None, "ylabel": None, "y2label": None, - "xmin": None, "xmax": None, "xlog": False, - "ymin": None, "ymax": None, "ylog": False, - "y2min": None, "y2max": None, "y2log": False, - "xlabels_off": False, "ylabels_off": False, - "nofirst_x": False, "nolast_x": False, - "nofirst_y": False, "nolast_y": False, - "key_pos": None, # short-form position or None + "xmin": None, + "xmax": None, + "xlog": False, + "ymin": None, + "ymax": None, + "ylog": False, + "y2min": None, + "y2max": None, + "y2log": False, + "xlabels_off": False, + "ylabels_off": False, + "nofirst_x": False, + "nolast_x": False, + "nofirst_y": False, + "nolast_y": False, + "key_pos": None, # short-form position or None "key_off": False, - "lines": [], "scatters": [], "bars": [], "fills": [], "errorbars": [], + "lines": [], + "scatters": [], + "bars": [], + "fills": [], + "errorbars": [], "file_series": [], "passthrough": [], - "series_order": [], # to preserve ordering info if needed + "series_order": [], # to preserve ordering info if needed # Dataset names (e.g. 'd1') consumed by a 'bar'/'fill' command. # The writer emits a standalone 'dN key ""' statement right after # 'bar'/'fill' to neutralize GLE's auto-key-from-header behavior @@ -695,7 +720,9 @@ def _parse_graph_block(self, block: GraphBlock, marker_cfg, smooth_flags) -> dic if name in emitted: continue attr_toks = merged_attr_toks.get(name, []) - if name in info["_key_suppress_datasets"] and self._is_bare_key_suppression(attr_toks): + if name in info[ + "_key_suppress_datasets" + ] and self._is_bare_key_suppression(attr_toks): # 'dN key ""' with no other attributes, following a # 'bar'/'fill' command that already consumed this # dataset -- the writer's auto-key-from-header @@ -713,7 +740,9 @@ def _parse_graph_block(self, block: GraphBlock, marker_cfg, smooth_flags) -> dic ) continue - self._dispatch_graph_statement(child, info, datasets, marker_cfg, smooth_flags) + self._dispatch_graph_statement( + child, info, datasets, marker_cfg, smooth_flags + ) return info @@ -741,10 +770,13 @@ def _is_bare_key_suppression(attr_toks: List[Token]) -> bool: return False return _string_value(val_tok) == "" - def _build_series_from_attrs(self, name, merged_toks, datasets, info, - marker_cfg, smooth_flags): + def _build_series_from_attrs( + self, name, merged_toks, datasets, info, marker_cfg, smooth_flags + ): """Build one series from a dataset's merged attribute tokens.""" - self._parse_series_command(merged_toks, datasets, info, marker_cfg, smooth_flags) + self._parse_series_command( + merged_toks, datasets, info, marker_cfg, smooth_flags + ) def _dispatch_graph_statement(self, stmt, info, datasets, marker_cfg, smooth_flags): kw = stmt.keyword @@ -936,7 +968,9 @@ def _parse_data_command(self, toks, datasets): given: List[Tuple[str, Optional[Tuple[int, int]]]] = [] while i < m: name_tok = toks[i] - if name_tok.type is not TokenType.WORD or not _DATASET_RE.match(name_tok.value): + if name_tok.type is not TokenType.WORD or not _DATASET_RE.match( + name_tok.value + ): i += 1 continue name = name_tok.value.lower() @@ -945,7 +979,11 @@ def _parse_data_command(self, toks, datasets): j = i + 2 while j < m: t = toks[j] - if t.type is TokenType.WORD and t.value.lower().startswith("c") and t.value[1:].isdigit(): + if ( + t.type is TokenType.WORD + and t.value.lower().startswith("c") + and t.value[1:].isdigit() + ): cols.append(int(t.value[1:])) j += 1 if j < m and toks[j].value == ",": @@ -993,9 +1031,7 @@ def _parse_data_command(self, toks, datasets): self._register_dataset("d1", data_file, 0, 1, datasets) else: for k in range(ncols - 1): - self._register_dataset( - f"d{k + 1}", data_file, 1, k + 2, datasets - ) + self._register_dataset(f"d{k + 1}", data_file, 1, k + 2, datasets) return # Explicit and/or positional clauses. @@ -1137,13 +1173,16 @@ def _parse_bar_command(self, toks, datasets, info): } if loaded is None or loaded.get("error"): # Broken data -> represent as file_series-style reference w/ error. - info["file_series"].append({ - "series_type": "bar", - "data_file": data_file, - "x_col": xcol, "y_col": ycol, - "color": color, - "data_error": (loaded or {}).get("error", "unresolved"), - }) + info["file_series"].append( + { + "series_type": "bar", + "data_file": data_file, + "x_col": xcol, + "y_col": ycol, + "color": color, + "data_error": (loaded or {}).get("error", "unresolved"), + } + ) return x = loaded["x"] height = loaded["y"] @@ -1182,20 +1221,28 @@ def _parse_fill_command(self, toks, datasets, info): # fill data file has c1=x, c2=y1, c3=y2. d1=c1,c2 ; d2=c1,c3. loaded = self._load_series(f1, xc1, yc1, extra_cols=[yc2]) if loaded is None or loaded.get("error"): - info["file_series"].append({ - "series_type": "fill", - "data_file": f1, - "x_col": xc1, "y1_col": yc1, "y2_col": yc2, - "color": color, - "data_error": (loaded or {}).get("error", "unresolved"), - }) + info["file_series"].append( + { + "series_type": "fill", + "data_file": f1, + "x_col": xc1, + "y1_col": yc1, + "y2_col": yc2, + "color": color, + "data_error": (loaded or {}).get("error", "unresolved"), + } + ) return x = loaded["x"] y1 = loaded["y"] y2 = loaded.get(f"c{yc2}") fill_entry = { - "x": x, "y1": y1, "y2": y2, - "color": color, "alpha": 0.3, "label": None, + "x": x, + "y1": y1, + "y2": y2, + "color": color, + "alpha": 0.3, + "label": None, "offset": info["_dataset_offsets"].get(d_names[0], 0.0), "data_file": f1, } @@ -1220,13 +1267,14 @@ def _parse_key_command(self, toks, info, stmt=None): # we cannot model; a cumulative re-emit would produce a competing 'key' # line. Keep the WHOLE original line as raw GLE (legend untouched) and # warn. - line = self._stmt_text(stmt) if stmt is not None else ( - " " + " ".join(t.value for t in toks) + line = ( + self._stmt_text(stmt) + if stmt is not None + else (" " + " ".join(t.value for t in toks)) ) info["passthrough"].append(line) self.warnings.append( - "structure: key has unsupported options; kept as raw GLE, " - "not editable" + "structure: key has unsupported options; kept as raw GLE, " "not editable" ) # -- series command -------------------------------------------------- @@ -1253,7 +1301,13 @@ def _parse_series_command(self, toks, datasets, info, marker_cfg, smooth_flags): if is_errorbar: self._build_errorbar( - info, datasets, data_file, xcol, ycol, attrs, is_import, + info, + datasets, + data_file, + xcol, + ycol, + attrs, + is_import, orig_toks=toks, ) return @@ -1266,7 +1320,12 @@ def _parse_series_command(self, toks, datasets, info, marker_cfg, smooth_flags): loaded = self._load_series(data_file, xcol, ycol) if loaded is None or loaded.get("error"): self._build_file_series( - info, data_file, xcol, ycol, attrs, has_line, + info, + data_file, + xcol, + ycol, + attrs, + has_line, error=(loaded or {}).get("error", "unresolved"), ) return @@ -1285,7 +1344,8 @@ def _parse_series_command(self, toks, datasets, info, marker_cfg, smooth_flags): entry = { "type": "scatter" if (has_marker and not has_line) else "line", - "x": x, "y": y, + "x": x, + "y": y, "color": attrs["color"] or "BLUE", "marker": attrs["marker"], "markersize": markersize, @@ -1318,8 +1378,8 @@ def _scan_series_attrs(self, toks) -> dict: "msize": None, "y2axis": False, "label": None, - "err_refs": {}, # kind -> dataset name (err/errup/errdown/herr/herrleft/herrright) - "err_consts": {}, # kind -> (value: float, is_percent: bool) for literals + "err_refs": {}, # kind -> dataset name (err/errup/errdown/herr/herrleft/herrright) + "err_consts": {}, # kind -> (value: float, is_percent: bool) for literals "errwidth": None, "herrwidth": None, } @@ -1363,7 +1423,10 @@ def _scan_series_attrs(self, toks) -> dict: a["msize"] = v i = nxt if v is not None else i + 2 continue - if w in ("err", "errup", "errdown", "herr", "herrleft", "herrright") and i + 1 < m: + if ( + w in ("err", "errup", "errdown", "herr", "herrleft", "herrright") + and i + 1 < m + ): nxt_tok = toks[i + 1] nxt_val = nxt_tok.value.lower() if nxt_tok.type is TokenType.WORD and _DATASET_RE.match(nxt_val): @@ -1410,8 +1473,9 @@ def _scan_series_attrs(self, toks) -> dict: i += 1 return a - def _build_errorbar(self, info, datasets, data_file, xcol, ycol, attrs, - is_import, orig_toks=None): + def _build_errorbar( + self, info, datasets, data_file, xcol, ycol, attrs, is_import, orig_toks=None + ): """Reconstruct an errorbar entry, matching Axes.errorbar's dict schema.""" # Resolve error column indices from referenced datasets. err = attrs["err_refs"] @@ -1459,7 +1523,9 @@ def col_of(ref_name): # capsize: writer emits errwidth (from gle_capsize) for yerr, herrwidth # for xerr; both derive from the SAME stored capsize. Recover via # capsize_cm_to_pt. - cap_cm = attrs["errwidth"] if attrs["errwidth"] is not None else attrs["herrwidth"] + cap_cm = ( + attrs["errwidth"] if attrs["errwidth"] is not None else attrs["herrwidth"] + ) gle_capsize = cap_cm stored_capsize = capsize_cm_to_pt(cap_cm) if cap_cm is not None else None @@ -1476,22 +1542,28 @@ def col_of(ref_name): if not is_import: # File-series errorbar reference. yerr_col = yerr_up_col if yerr_up_col is not None else None - info["file_series"].append({ - "series_type": "errorbar", - "data_file": data_file, - "x_col": xcol, "y_col": ycol, - "yerr_col": yerr_col, - "color": attrs["color"] or "BLUE", - "marker": marker, - "markersize": markersize, - "label": attrs["label"], - "capsize": gle_capsize, - "yaxis": "y2" if attrs["y2axis"] else "y", - }) + info["file_series"].append( + { + "series_type": "errorbar", + "data_file": data_file, + "x_col": xcol, + "y_col": ycol, + "yerr_col": yerr_col, + "color": attrs["color"] or "BLUE", + "marker": marker, + "markersize": markersize, + "label": attrs["label"], + "capsize": gle_capsize, + "yaxis": "y2" if attrs["y2axis"] else "y", + } + ) return - extra = [c for c in (yerr_up_col, yerr_down_col, xerr_left_col, xerr_right_col) - if c is not None] + extra = [ + c + for c in (yerr_up_col, yerr_down_col, xerr_left_col, xerr_right_col) + if c is not None + ] loaded = self._load_series(data_file, xcol, ycol, extra_cols=extra) if loaded is None or loaded.get("error"): if err_consts: @@ -1503,19 +1575,22 @@ def col_of(ref_name): ) return yerr_col = yerr_up_col if yerr_up_col is not None else None - info["file_series"].append({ - "series_type": "errorbar", - "data_file": data_file, - "x_col": xcol, "y_col": ycol, - "yerr_col": yerr_col, - "color": attrs["color"] or "BLUE", - "marker": marker, - "markersize": markersize, - "label": attrs["label"], - "capsize": gle_capsize, - "yaxis": "y2" if attrs["y2axis"] else "y", - "data_error": (loaded or {}).get("error", "unresolved"), - }) + info["file_series"].append( + { + "series_type": "errorbar", + "data_file": data_file, + "x_col": xcol, + "y_col": ycol, + "yerr_col": yerr_col, + "color": attrs["color"] or "BLUE", + "marker": marker, + "markersize": markersize, + "label": attrs["label"], + "capsize": gle_capsize, + "yaxis": "y2" if attrs["y2axis"] else "y", + "data_error": (loaded or {}).get("error", "unresolved"), + } + ) return def col_arr(c): @@ -1532,8 +1607,7 @@ def col_arr(c): # -- for vertical err the value dimension is y, for horizontal it is x. if err_consts: self.warnings.append( - "data: constant error expression converted to a data column on " - "save" + "data: constant error expression converted to a data column on " "save" ) y_arr = loaded["y"] x_arr = loaded["x"] @@ -1569,9 +1643,12 @@ def const_arr(value, is_percent, horizontal): entry = { "type": "errorbar", - "x": loaded["x"], "y": loaded["y"], - "yerr_up": yerr_up, "yerr_down": yerr_down, - "xerr_left": xerr_left, "xerr_right": xerr_right, + "x": loaded["x"], + "y": loaded["y"], + "yerr_up": yerr_up, + "yerr_down": yerr_down, + "xerr_left": xerr_left, + "xerr_right": xerr_right, "color": attrs["color"] or "BLUE", "marker": marker, "markersize": markersize, @@ -1620,7 +1697,9 @@ def _passthrough_original_dn(self, info, orig_toks): rendered = " ".join(self._token_text(t) for t in orig_toks) info["passthrough"].append(" " + rendered) - def _build_file_series(self, info, data_file, xcol, ycol, attrs, has_line, error=None): + def _build_file_series( + self, info, data_file, xcol, ycol, attrs, has_line, error=None + ): markersize = attrs["msize"] if attrs["msize"] is not None else 0.15 # Branch on has_line ALONE: a 'd1 line marker circle lwidth X' reference # is a line (carrying line/lwidth/linestyle) that ALSO has a marker -- @@ -1631,11 +1710,14 @@ def _build_file_series(self, info, data_file, xcol, ycol, attrs, has_line, error entry = { "series_type": "line", "data_file": data_file, - "x_col": xcol, "y_col": ycol, + "x_col": xcol, + "y_col": ycol, "color": attrs["color"] or "BLUE", "linestyle": attrs["linestyle"], "linewidth": ( - linewidth_cm_to_pt(attrs["lwidth"]) if attrs["lwidth"] is not None else 1.0 + linewidth_cm_to_pt(attrs["lwidth"]) + if attrs["lwidth"] is not None + else 1.0 ), "label": attrs["label"], "yaxis": "y2" if attrs["y2axis"] else "y", @@ -1649,7 +1731,8 @@ def _build_file_series(self, info, data_file, xcol, ycol, attrs, has_line, error entry = { "series_type": "errorbar", "data_file": data_file, - "x_col": xcol, "y_col": ycol, + "x_col": xcol, + "y_col": ycol, "yerr_col": None, "color": attrs["color"] or "BLUE", "marker": attrs["marker"], @@ -1883,9 +1966,14 @@ def _try_one_text(self, nodes, i) -> Tuple[int, Optional[dict]]: i += 1 return i, { - "x": x, "y": y, "text": text_str, - "color": self._text_color, "fontsize": self._text_fontsize, - "ha": self._text_just, "va": "center", "box_color": None, + "x": x, + "y": y, + "text": text_str, + "color": self._text_color, + "fontsize": self._text_fontsize, + "ha": self._text_just, + "va": "center", + "box_color": None, } def _skip_blanks(self, nodes, i) -> int: @@ -2205,6 +2293,7 @@ def _raw_lines(self, node) -> List[str]: # Grid-clustering helpers # --------------------------------------------------------------------------- # + def _cluster(values, tol=0.5, reverse=False): """Cluster near-equal floats into ordered groups (representative values).""" uniq = sorted(set(values), reverse=reverse) @@ -2239,6 +2328,7 @@ def _which_cluster(value, reps, tol=None, reverse=False): # Public entry point # --------------------------------------------------------------------------- # + def parse_gle_figure( path_or_text: Union[str, Path], *, diff --git a/src/gleplot/parser/tables.py b/src/gleplot/parser/tables.py index b29037d..75a87f1 100644 --- a/src/gleplot/parser/tables.py +++ b/src/gleplot/parser/tables.py @@ -56,44 +56,157 @@ # of the RGB value). Keys are the canonical uppercase GLE color names as # ``defineColor`` registers them; GLE itself is case-insensitive. COLORS: Dict[str, Tuple[int, int, int]] = { - "ALICEBLUE": (240, 248, 255), "ANTIQUEWHITE": (250, 235, 215), "AQUA": (0, 255, 255), "AQUAMARINE": (127, 255, 212), - "AZURE": (240, 255, 255), "BEIGE": (245, 245, 220), "BISQUE": (255, 228, 196), "BLACK": (0, 0, 0), - "BLANCHEDALMOND": (255, 235, 205), "BLUE": (0, 0, 255), "BLUEVIOLET": (138, 43, 226), "BROWN": (165, 42, 42), - "BURLYWOOD": (222, 184, 135), "CADETBLUE": (95, 158, 160), "CHARTREUSE": (127, 255, 0), "CHOCOLATE": (210, 105, 30), - "CORAL": (255, 127, 80), "CORNFLOWERBLUE": (100, 149, 237), "CORNSILK": (255, 248, 220), "CRIMSON": (220, 20, 60), - "CYAN": (0, 255, 255), "DARKBLUE": (0, 0, 139), "DARKCYAN": (0, 139, 139), "DARKGOLDENROD": (184, 134, 11), - "DARKGRAY": (169, 169, 169), "DARKGREEN": (0, 100, 0), "DARKKHAKI": (189, 183, 107), "DARKMAGENTA": (139, 0, 139), - "DARKOLIVEGREEN": (85, 107, 47), "DARKORANGE": (255, 140, 0), "DARKORCHID": (153, 50, 204), "DARKRED": (139, 0, 0), - "DARKSALMON": (233, 150, 122), "DARKSEAGREEN": (143, 188, 143), "DARKSLATEBLUE": (72, 61, 139), "DARKSLATEGRAY": (47, 79, 79), - "DARKTURQUOISE": (0, 206, 209), "DARKVIOLET": (148, 0, 211), "DEEPPINK": (255, 20, 147), "DEEPSKYBLUE": (0, 191, 255), - "DIMGRAY": (105, 105, 105), "DODGERBLUE": (30, 144, 255), "FIREBRICK": (178, 34, 34), "FLORALWHITE": (255, 250, 240), - "FORESTGREEN": (34, 139, 34), "FUCHSIA": (255, 0, 255), "GAINSBORO": (220, 220, 220), "GHOSTWHITE": (248, 248, 255), - "GOLD": (255, 215, 0), "GOLDENROD": (218, 165, 32), "GRAY": (128, 128, 128), "GRAY1": (253, 253, 253), - "GRAY10": (200, 200, 200), "GRAY20": (175, 175, 175), "GRAY30": (150, 150, 150), "GRAY40": (125, 125, 125), - "GRAY5": (240, 240, 240), "GRAY50": (100, 100, 100), "GRAY60": (75, 75, 75), "GRAY70": (50, 50, 50), - "GRAY80": (25, 25, 25), "GRAY90": (6, 6, 6), "GREEN": (0, 128, 0), "GREENYELLOW": (173, 255, 47), - "HONEYDEW": (240, 255, 240), "HOTPINK": (255, 105, 180), "INDIANRED": (205, 92, 92), "INDIGO": (75, 0, 130), - "IVORY": (255, 255, 240), "KHAKI": (240, 230, 140), "LAVENDER": (230, 230, 250), "LAVENDERBLUSH": (255, 240, 245), - "LAWNGREEN": (124, 252, 0), "LEMONCHIFFON": (255, 250, 205), "LIGHTBLUE": (173, 216, 230), "LIGHTCORAL": (240, 128, 128), - "LIGHTCYAN": (224, 255, 255), "LIGHTGOLDENRODYELLOW": (250, 250, 210), "LIGHTGRAY": (211, 211, 211), "LIGHTGREEN": (144, 238, 144), - "LIGHTPINK": (255, 182, 193), "LIGHTSALMON": (255, 160, 122), "LIGHTSEAGREEN": (32, 178, 170), "LIGHTSKYBLUE": (135, 206, 250), - "LIGHTSLATEGRAY": (119, 136, 153), "LIGHTSTEELBLUE": (176, 196, 222), "LIGHTYELLOW": (255, 255, 224), "LIME": (0, 255, 0), - "LIMEGREEN": (50, 205, 50), "LINEN": (250, 240, 230), "MAGENTA": (255, 0, 255), "MAROON": (128, 0, 0), - "MEDIUMAQUAMARINE": (102, 205, 170), "MEDIUMBLUE": (0, 0, 205), "MEDIUMORCHID": (186, 85, 211), "MEDIUMPURPLE": (147, 112, 219), - "MEDIUMSEAGREEN": (60, 179, 113), "MEDIUMSLATEBLUE": (123, 104, 238), "MEDIUMSPRINGGREEN": (0, 250, 154), "MEDIUMTURQUOISE": (72, 209, 204), - "MEDIUMVIOLETRED": (199, 21, 133), "MIDNIGHTBLUE": (25, 25, 112), "MINTCREAM": (245, 255, 250), "MISTYROSE": (255, 228, 225), - "MOCCASIN": (255, 228, 181), "NAVAJOWHITE": (255, 222, 173), "NAVY": (0, 0, 128), "OLDLACE": (253, 245, 230), - "OLIVE": (128, 128, 0), "OLIVEDRAB": (107, 142, 35), "ORANGE": (255, 165, 0), "ORANGERED": (255, 69, 0), - "ORCHID": (218, 112, 214), "PALEGOLDENROD": (238, 232, 170), "PALEGREEN": (152, 251, 152), "PALETURQUOISE": (175, 238, 238), - "PALEVIOLETRED": (219, 112, 147), "PAPAYAWHIP": (255, 239, 213), "PEACHPUFF": (255, 218, 185), "PERU": (205, 133, 63), - "PINK": (255, 192, 203), "PLUM": (221, 160, 221), "POWDERBLUE": (176, 224, 230), "PURPLE": (128, 0, 128), - "RED": (255, 0, 0), "ROSYBROWN": (188, 143, 143), "ROYALBLUE": (65, 105, 225), "SADDLEBROWN": (139, 69, 19), - "SALMON": (250, 128, 114), "SANDYBROWN": (244, 164, 96), "SEAGREEN": (46, 139, 87), "SEASHELL": (255, 245, 238), - "SIENNA": (160, 82, 45), "SILVER": (192, 192, 192), "SKYBLUE": (135, 206, 235), "SLATEBLUE": (106, 90, 205), - "SLATEGRAY": (112, 128, 144), "SNOW": (255, 250, 250), "SPRINGGREEN": (0, 255, 127), "STEELBLUE": (70, 130, 180), - "TAN": (210, 180, 140), "TEAL": (0, 128, 128), "THISTLE": (216, 191, 216), "TOMATO": (255, 99, 71), - "TURQUOISE": (64, 224, 208), "VIOLET": (238, 130, 238), "WHEAT": (245, 222, 179), "WHITE": (255, 255, 255), - "WHITESMOKE": (245, 245, 245), "YELLOW": (255, 255, 0), "YELLOWGREEN": (154, 205, 50), + "ALICEBLUE": (240, 248, 255), + "ANTIQUEWHITE": (250, 235, 215), + "AQUA": (0, 255, 255), + "AQUAMARINE": (127, 255, 212), + "AZURE": (240, 255, 255), + "BEIGE": (245, 245, 220), + "BISQUE": (255, 228, 196), + "BLACK": (0, 0, 0), + "BLANCHEDALMOND": (255, 235, 205), + "BLUE": (0, 0, 255), + "BLUEVIOLET": (138, 43, 226), + "BROWN": (165, 42, 42), + "BURLYWOOD": (222, 184, 135), + "CADETBLUE": (95, 158, 160), + "CHARTREUSE": (127, 255, 0), + "CHOCOLATE": (210, 105, 30), + "CORAL": (255, 127, 80), + "CORNFLOWERBLUE": (100, 149, 237), + "CORNSILK": (255, 248, 220), + "CRIMSON": (220, 20, 60), + "CYAN": (0, 255, 255), + "DARKBLUE": (0, 0, 139), + "DARKCYAN": (0, 139, 139), + "DARKGOLDENROD": (184, 134, 11), + "DARKGRAY": (169, 169, 169), + "DARKGREEN": (0, 100, 0), + "DARKKHAKI": (189, 183, 107), + "DARKMAGENTA": (139, 0, 139), + "DARKOLIVEGREEN": (85, 107, 47), + "DARKORANGE": (255, 140, 0), + "DARKORCHID": (153, 50, 204), + "DARKRED": (139, 0, 0), + "DARKSALMON": (233, 150, 122), + "DARKSEAGREEN": (143, 188, 143), + "DARKSLATEBLUE": (72, 61, 139), + "DARKSLATEGRAY": (47, 79, 79), + "DARKTURQUOISE": (0, 206, 209), + "DARKVIOLET": (148, 0, 211), + "DEEPPINK": (255, 20, 147), + "DEEPSKYBLUE": (0, 191, 255), + "DIMGRAY": (105, 105, 105), + "DODGERBLUE": (30, 144, 255), + "FIREBRICK": (178, 34, 34), + "FLORALWHITE": (255, 250, 240), + "FORESTGREEN": (34, 139, 34), + "FUCHSIA": (255, 0, 255), + "GAINSBORO": (220, 220, 220), + "GHOSTWHITE": (248, 248, 255), + "GOLD": (255, 215, 0), + "GOLDENROD": (218, 165, 32), + "GRAY": (128, 128, 128), + "GRAY1": (253, 253, 253), + "GRAY10": (200, 200, 200), + "GRAY20": (175, 175, 175), + "GRAY30": (150, 150, 150), + "GRAY40": (125, 125, 125), + "GRAY5": (240, 240, 240), + "GRAY50": (100, 100, 100), + "GRAY60": (75, 75, 75), + "GRAY70": (50, 50, 50), + "GRAY80": (25, 25, 25), + "GRAY90": (6, 6, 6), + "GREEN": (0, 128, 0), + "GREENYELLOW": (173, 255, 47), + "HONEYDEW": (240, 255, 240), + "HOTPINK": (255, 105, 180), + "INDIANRED": (205, 92, 92), + "INDIGO": (75, 0, 130), + "IVORY": (255, 255, 240), + "KHAKI": (240, 230, 140), + "LAVENDER": (230, 230, 250), + "LAVENDERBLUSH": (255, 240, 245), + "LAWNGREEN": (124, 252, 0), + "LEMONCHIFFON": (255, 250, 205), + "LIGHTBLUE": (173, 216, 230), + "LIGHTCORAL": (240, 128, 128), + "LIGHTCYAN": (224, 255, 255), + "LIGHTGOLDENRODYELLOW": (250, 250, 210), + "LIGHTGRAY": (211, 211, 211), + "LIGHTGREEN": (144, 238, 144), + "LIGHTPINK": (255, 182, 193), + "LIGHTSALMON": (255, 160, 122), + "LIGHTSEAGREEN": (32, 178, 170), + "LIGHTSKYBLUE": (135, 206, 250), + "LIGHTSLATEGRAY": (119, 136, 153), + "LIGHTSTEELBLUE": (176, 196, 222), + "LIGHTYELLOW": (255, 255, 224), + "LIME": (0, 255, 0), + "LIMEGREEN": (50, 205, 50), + "LINEN": (250, 240, 230), + "MAGENTA": (255, 0, 255), + "MAROON": (128, 0, 0), + "MEDIUMAQUAMARINE": (102, 205, 170), + "MEDIUMBLUE": (0, 0, 205), + "MEDIUMORCHID": (186, 85, 211), + "MEDIUMPURPLE": (147, 112, 219), + "MEDIUMSEAGREEN": (60, 179, 113), + "MEDIUMSLATEBLUE": (123, 104, 238), + "MEDIUMSPRINGGREEN": (0, 250, 154), + "MEDIUMTURQUOISE": (72, 209, 204), + "MEDIUMVIOLETRED": (199, 21, 133), + "MIDNIGHTBLUE": (25, 25, 112), + "MINTCREAM": (245, 255, 250), + "MISTYROSE": (255, 228, 225), + "MOCCASIN": (255, 228, 181), + "NAVAJOWHITE": (255, 222, 173), + "NAVY": (0, 0, 128), + "OLDLACE": (253, 245, 230), + "OLIVE": (128, 128, 0), + "OLIVEDRAB": (107, 142, 35), + "ORANGE": (255, 165, 0), + "ORANGERED": (255, 69, 0), + "ORCHID": (218, 112, 214), + "PALEGOLDENROD": (238, 232, 170), + "PALEGREEN": (152, 251, 152), + "PALETURQUOISE": (175, 238, 238), + "PALEVIOLETRED": (219, 112, 147), + "PAPAYAWHIP": (255, 239, 213), + "PEACHPUFF": (255, 218, 185), + "PERU": (205, 133, 63), + "PINK": (255, 192, 203), + "PLUM": (221, 160, 221), + "POWDERBLUE": (176, 224, 230), + "PURPLE": (128, 0, 128), + "RED": (255, 0, 0), + "ROSYBROWN": (188, 143, 143), + "ROYALBLUE": (65, 105, 225), + "SADDLEBROWN": (139, 69, 19), + "SALMON": (250, 128, 114), + "SANDYBROWN": (244, 164, 96), + "SEAGREEN": (46, 139, 87), + "SEASHELL": (255, 245, 238), + "SIENNA": (160, 82, 45), + "SILVER": (192, 192, 192), + "SKYBLUE": (135, 206, 235), + "SLATEBLUE": (106, 90, 205), + "SLATEGRAY": (112, 128, 144), + "SNOW": (255, 250, 250), + "SPRINGGREEN": (0, 255, 127), + "STEELBLUE": (70, 130, 180), + "TAN": (210, 180, 140), + "TEAL": (0, 128, 128), + "THISTLE": (216, 191, 216), + "TOMATO": (255, 99, 71), + "TURQUOISE": (64, 224, 208), + "VIOLET": (238, 130, 238), + "WHEAT": (245, 222, 179), + "WHITE": (255, 255, 255), + "WHITESMOKE": (245, 245, 245), + "YELLOW": (255, 255, 0), + "YELLOWGREEN": (154, 205, 50), } @@ -144,15 +257,58 @@ def nearest_gle_color(r: int, g: int, b: int) -> str: # ``stdmark[]`` array -- the current marker table; NOT the legacy # ``stdmark_v35[]`` array that precedes it). This is the full set of marker # names GLE's ``marker`` keyword accepts. -MARKERS = frozenset({ - "ASTERISK", "ASTERIX", "CIRCLE", "CLUB", "CROSS", "DAG", "DDAG", "DIAMOND", - "DIAMONDZ", "DOT", "FCIRCLE", "FDIAMOND", "FLOWER", "FSQUARE", "FSTARR", - "FTRIANGLE", "FTRIANGLED", "HANDPEN", "HEART", "LETTER", "MINUS", "ODOT", - "OMINUS", "OPLUS", "OTIMES", "PCROSS", "PHONE", "PLANE", "PLUS", "SCIRCLE", - "SNAKE", "SPADE", "SQUARE", "SSQUARE", "STAR", "STAR2", "STAR3", "STAR4", - "STARR", "TRIANGLE", "TRIANGLED", "TRIANGLEZ", "WCIRCLE", "WDIAMOND", - "WTRIANGLE", "WTRIANGLED", "WSQUARE", "WSTARR", -}) +MARKERS = frozenset( + { + "ASTERISK", + "ASTERIX", + "CIRCLE", + "CLUB", + "CROSS", + "DAG", + "DDAG", + "DIAMOND", + "DIAMONDZ", + "DOT", + "FCIRCLE", + "FDIAMOND", + "FLOWER", + "FSQUARE", + "FSTARR", + "FTRIANGLE", + "FTRIANGLED", + "HANDPEN", + "HEART", + "LETTER", + "MINUS", + "ODOT", + "OMINUS", + "OPLUS", + "OTIMES", + "PCROSS", + "PHONE", + "PLANE", + "PLUS", + "SCIRCLE", + "SNAKE", + "SPADE", + "SQUARE", + "SSQUARE", + "STAR", + "STAR2", + "STAR3", + "STAR4", + "STARR", + "TRIANGLE", + "TRIANGLED", + "TRIANGLEZ", + "WCIRCLE", + "WDIAMOND", + "WTRIANGLE", + "WTRIANGLED", + "WSQUARE", + "WSTARR", + } +) def _build_gle_marker_to_matplotlib() -> Dict[str, str]: diff --git a/src/gleplot/writer.py b/src/gleplot/writer.py index cfe155f..f28fecd 100644 --- a/src/gleplot/writer.py +++ b/src/gleplot/writer.py @@ -42,26 +42,32 @@ class GLEWriter: marker : GLEMarkerConfig, optional Marker configuration. If None, uses global default. """ - - def __init__(self, figsize: Tuple[float, float] = (8, 6), dpi: int = 100, - style: Optional[GLEStyleConfig] = None, - graph: Optional[GLEGraphConfig] = None, - marker: Optional[GLEMarkerConfig] = None): + + def __init__( + self, + figsize: Tuple[float, float] = (8, 6), + dpi: int = 100, + style: Optional[GLEStyleConfig] = None, + graph: Optional[GLEGraphConfig] = None, + marker: Optional[GLEMarkerConfig] = None, + ): """Initialize GLE writer with optional configuration objects.""" self.figsize = figsize self.dpi = dpi # Convert inches to cm (GLE uses cm) self.width_cm = inches_to_cm(figsize[0]) self.height_cm = inches_to_cm(figsize[1]) - + # Get configuration (fall back to global defaults) self.style = style or GlobalConfig.get_style() self.graph = graph or GlobalConfig.get_graph() self.marker = marker or GlobalConfig.get_marker() - + self.lines_gle = [] # GLE script lines self.data_files = {} # {filename: data_content} - self.dataset_index = 1 # Counter for unique dataset names (d1, d2, d3, ...) - GLE is 1-indexed + self.dataset_index = ( + 1 # Counter for unique dataset names (d1, d2, d3, ...) - GLE is 1-indexed + ) self._pending_graph_text_lines: List[str] = [] # Sticky GLE interpreter state as far as add_text is concerned: 'set # hei'/'set color'/'set just' persist across `write` statements until @@ -76,12 +82,15 @@ def __init__(self, figsize: Tuple[float, float] = (8, 6), dpi: int = 100, self._text_state_hei_cm: Optional[str] = self._format_number( fontsize_pt_to_cm(self.style.fontsize) ) - self._text_state_color: str = 'BLACK' - self._text_state_just: str = 'left' - - def add_preamble(self, include_graph_begin: bool = True, - metadata_lines: Optional[List[str]] = None, - passthrough_header: Optional[List[str]] = None): + self._text_state_color: str = "BLACK" + self._text_state_just: str = "left" + + def add_preamble( + self, + include_graph_begin: bool = True, + metadata_lines: Optional[List[str]] = None, + passthrough_header: Optional[List[str]] = None, + ): """Add GLE preamble. Includes: @@ -106,36 +115,42 @@ def add_preamble(self, include_graph_begin: bool = True, 'set hei ...' + the blank line) and before the first graph block/amove. Omitted entirely when falsy (no blank-line churn). """ - self.lines_gle.extend([ - '! GLE graphics file', - '! Generated by gleplot', - ]) + self.lines_gle.extend( + [ + "! GLE graphics file", + "! Generated by gleplot", + ] + ) if metadata_lines: self.lines_gle.extend(metadata_lines) - self.lines_gle.extend([ - '', - f'size {self._format_number(self.width_cm)} {self._format_number(self.height_cm)}', - ]) + self.lines_gle.extend( + [ + "", + f"size {self._format_number(self.width_cm)} {self._format_number(self.height_cm)}", + ] + ) # Only set font if explicitly specified if self.style.font: - self.lines_gle.append(f'set font {self.style.font}') - self.lines_gle.extend([ - f'set hei {self._format_number(fontsize_pt_to_cm(self.style.fontsize))}', - '', - ]) + self.lines_gle.append(f"set font {self.style.font}") + self.lines_gle.extend( + [ + f"set hei {self._format_number(fontsize_pt_to_cm(self.style.fontsize))}", + "", + ] + ) if passthrough_header: self.lines_gle.extend(passthrough_header) if include_graph_begin: - self.lines_gle.append('begin graph') - + self.lines_gle.append("begin graph") + def begin_graph(self): """Open a new graph block. - + Used in multi-subplot layouts. Each graph block must be closed with end_graph(). """ - self.lines_gle.append('begin graph') - + self.lines_gle.append("begin graph") + def end_graph(self, passthrough: Optional[List[str]] = None): """Close the current graph block. @@ -148,17 +163,17 @@ def end_graph(self, passthrough: Optional[List[str]] = None): """ if passthrough: self.lines_gle.extend(passthrough) - self.lines_gle.append('end graph') + self.lines_gle.append("end graph") if self._pending_graph_text_lines: self.lines_gle.extend(self._pending_graph_text_lines) self._pending_graph_text_lines = [] - + def add_amove(self, x_cm: float, y_cm: float): """Add absolute move command to position the next graph. - + In GLE, 'amove x y' positions the drawing cursor at absolute coordinates (in cm) from the bottom-left of the page. - + Parameters ---------- x_cm : float @@ -167,15 +182,19 @@ def add_amove(self, x_cm: float, y_cm: float): Y position in cm from the bottom edge of the page. """ self.lines_gle.append( - f'amove {self._format_number(x_cm)} {self._format_number(y_cm)}' + f"amove {self._format_number(x_cm)} {self._format_number(y_cm)}" ) - - def add_graph_size(self, width_cm: Optional[float] = None, height_cm: Optional[float] = None, - force_size: bool = False): + + def add_graph_size( + self, + width_cm: Optional[float] = None, + height_cm: Optional[float] = None, + force_size: bool = False, + ): """Set graph dimensions and scaling. - + Uses the configured scale_mode (auto, fixed, or fullsize). - + Parameters ---------- width_cm : float, optional @@ -189,30 +208,53 @@ def add_graph_size(self, width_cm: Optional[float] = None, height_cm: Optional[f with no padding, so axes from adjacent subplots can touch. """ if force_size and width_cm is not None and height_cm is not None: - self.lines_gle.append(f' size {self._format_number(width_cm)} {self._format_number(height_cm)}') - self.lines_gle.append(' scale 1 1') # Fill entire graph box for tight subplot layout - elif self.graph.scale_mode == 'fixed' and width_cm is not None and height_cm is not None: - self.lines_gle.append(f' size {self._format_number(width_cm)} {self._format_number(height_cm)}') - self.lines_gle.append(' scale 1 1') - elif self.graph.scale_mode == 'fullsize': - self.lines_gle.append(' fullsize') + self.lines_gle.append( + f" size {self._format_number(width_cm)} {self._format_number(height_cm)}" + ) + self.lines_gle.append( + " scale 1 1" + ) # Fill entire graph box for tight subplot layout + elif ( + self.graph.scale_mode == "fixed" + and width_cm is not None + and height_cm is not None + ): + self.lines_gle.append( + f" size {self._format_number(width_cm)} {self._format_number(height_cm)}" + ) + self.lines_gle.append(" scale 1 1") + elif self.graph.scale_mode == "fullsize": + self.lines_gle.append(" fullsize") else: # 'auto' - default # Auto-size and center axes within graph box - self.lines_gle.append(' scale auto') - - def add_axes(self, xlabel: Optional[str] = None, ylabel: Optional[str] = None, - y2label: Optional[str] = None, - title: Optional[str] = None, xlog: bool = False, ylog: bool = False, - y2log: bool = False, - xmin: Optional[float] = None, xmax: Optional[float] = None, - ymin: Optional[float] = None, ymax: Optional[float] = None, - y2min: Optional[float] = None, y2max: Optional[float] = None, - show_xlabel: bool = True, show_ylabel: bool = True, - show_xticks: bool = True, show_yticks: bool = True, - remove_last_xtick: bool = False, remove_last_ytick: bool = False, - remove_first_xtick: bool = False, remove_first_ytick: bool = False): + self.lines_gle.append(" scale auto") + + def add_axes( + self, + xlabel: Optional[str] = None, + ylabel: Optional[str] = None, + y2label: Optional[str] = None, + title: Optional[str] = None, + xlog: bool = False, + ylog: bool = False, + y2log: bool = False, + xmin: Optional[float] = None, + xmax: Optional[float] = None, + ymin: Optional[float] = None, + ymax: Optional[float] = None, + y2min: Optional[float] = None, + y2max: Optional[float] = None, + show_xlabel: bool = True, + show_ylabel: bool = True, + show_xticks: bool = True, + show_yticks: bool = True, + remove_last_xtick: bool = False, + remove_last_ytick: bool = False, + remove_first_xtick: bool = False, + remove_first_ytick: bool = False, + ): """Add axis configuration. - + Parameters ---------- xlabel, ylabel : str, optional @@ -242,75 +284,79 @@ def add_axes(self, xlabel: Optional[str] = None, ylabel: Optional[str] = None, """ if title: self.lines_gle.append(f' title "{title}"') - + # Only show axis titles if requested # Note: show_xlabel controls the title (e.g. "Time (s)"), not the tick marks if xlabel and show_xlabel: self.lines_gle.append(f' xtitle "{xlabel}"') - + if ylabel and show_ylabel: self.lines_gle.append(f' ytitle "{ylabel}"') - + # Add y2axis title if provided if y2label: self.lines_gle.append(f' y2title "{y2label}"') - + # Handle axis ranges and tick labels # Note: We keep the axis and ticks visible but can hide the tick labels - x_cmd = ' xaxis' + x_cmd = " xaxis" if xmin is not None: - x_cmd += f' min {self._format_number(xmin)}' + x_cmd += f" min {self._format_number(xmin)}" if xmax is not None: - x_cmd += f' max {self._format_number(xmax)}' + x_cmd += f" max {self._format_number(xmax)}" if xlog: - x_cmd += ' log' + x_cmd += " log" if remove_first_xtick: - x_cmd += ' nofirst' # Remove first tick label to prevent overlap + x_cmd += " nofirst" # Remove first tick label to prevent overlap if remove_last_xtick: - x_cmd += ' nolast' # Remove last tick label to prevent overlap - + x_cmd += " nolast" # Remove last tick label to prevent overlap + # Add xaxis command if it has parameters - if x_cmd != ' xaxis': + if x_cmd != " xaxis": self.lines_gle.append(x_cmd) - + # Hide x-axis tick labels if requested (but keep the ticks themselves) if not show_xticks: - self.lines_gle.append(' xlabels off') - + self.lines_gle.append(" xlabels off") + # Same for y-axis - y_cmd = ' yaxis' + y_cmd = " yaxis" if ymin is not None: - y_cmd += f' min {self._format_number(ymin)}' + y_cmd += f" min {self._format_number(ymin)}" if ymax is not None: - y_cmd += f' max {self._format_number(ymax)}' + y_cmd += f" max {self._format_number(ymax)}" if ylog: - y_cmd += ' log' + y_cmd += " log" if remove_first_ytick: - y_cmd += ' nofirst' # Remove first tick label to prevent overlap + y_cmd += " nofirst" # Remove first tick label to prevent overlap if remove_last_ytick: - y_cmd += ' nolast' # Remove last tick label to prevent overlap - + y_cmd += " nolast" # Remove last tick label to prevent overlap + # Add yaxis command if it has parameters - if y_cmd != ' yaxis': + if y_cmd != " yaxis": self.lines_gle.append(y_cmd) - + # Hide y-axis tick labels if requested (but keep the ticks themselves) if not show_yticks: - self.lines_gle.append(' ylabels off') - + self.lines_gle.append(" ylabels off") + # Handle y2axis (secondary y-axis) if limits or log scale specified if y2min is not None or y2max is not None or y2log: - y2_cmd = ' y2axis' + y2_cmd = " y2axis" if y2min is not None: - y2_cmd += f' min {self._format_number(y2min)}' + y2_cmd += f" min {self._format_number(y2min)}" if y2max is not None: - y2_cmd += f' max {self._format_number(y2max)}' + y2_cmd += f" max {self._format_number(y2max)}" if y2log: - y2_cmd += ' log' + y2_cmd += " log" self.lines_gle.append(y2_cmd) - - def add_data_file(self, filename: str, columns: List[np.ndarray], - column_names: Optional[List[str]] = None): + + def add_data_file( + self, + filename: str, + columns: List[np.ndarray], + column_names: Optional[List[str]] = None, + ): """ Add external data file. @@ -344,18 +390,18 @@ def add_data_file(self, filename: str, columns: List[np.ndarray], # Write header if provided if column_names: - lines.append(' '.join(column_names)) + lines.append(" ".join(column_names)) # Convert columns to 2D array data = np.column_stack(columns) # Write data rows for row in data: - line = ' '.join(self._format_number(val) for val in row) + line = " ".join(self._format_number(val) for val in row) lines.append(line) # Add trailing newline for GLE compatibility - self.data_files[filename] = '\n'.join(lines) + '\n' + self.data_files[filename] = "\n".join(lines) + "\n" def _apply_offset(self, source_name: str, offset: float) -> str: """Emit a ``let`` command that shifts *source_name* vertically by *offset*. @@ -373,19 +419,28 @@ def _apply_offset(self, source_name: str, offset: float) -> str: ``let d2 = d1+5`` works, so the operator is glued to its operands. A negative offset is emitted as ``d1-5`` (not ``d1+-5``). """ - shifted = f'd{self.dataset_index}' + shifted = f"d{self.dataset_index}" self.dataset_index += 1 mag = self._format_number(abs(offset)) - sign = '-' if offset < 0 else '+' - self.lines_gle.append(f' let {shifted} = {source_name}{sign}{mag}') + sign = "-" if offset < 0 else "+" + self.lines_gle.append(f" let {shifted} = {source_name}{sign}{mag}") return shifted - def add_plot_line(self, x: np.ndarray, y: np.ndarray, data_file: str, - color: str = 'BLUE', linestyle: str = '-', - linewidth: float = 1.0, label: Optional[str] = None, - marker: Optional[str] = None, markersize: float = 0.1, - yaxis: str = 'y', offset: float = 0.0, - column_names: Optional[List[str]] = None): + def add_plot_line( + self, + x: np.ndarray, + y: np.ndarray, + data_file: str, + color: str = "BLUE", + linestyle: str = "-", + linewidth: float = 1.0, + label: Optional[str] = None, + marker: Optional[str] = None, + markersize: float = 0.1, + yaxis: str = "y", + offset: float = 0.0, + column_names: Optional[List[str]] = None, + ): """ Add line plot to graph. @@ -425,12 +480,12 @@ def add_plot_line(self, x: np.ndarray, y: np.ndarray, data_file: str, # Add data file with sorted data self.add_data_file(data_file, [x_sorted, y_sorted], column_names) - + # Generate plot command with unique dataset name - d_name = f'd{self.dataset_index}' + d_name = f"d{self.dataset_index}" self.dataset_index += 1 - - cmd = f' data {_format_data_filename(data_file)} {d_name}=c1,c2' + + cmd = f" data {_format_data_filename(data_file)} {d_name}=c1,c2" self.lines_gle.append(cmd) # A non-zero offset shifts the trace vertically at plot time via a @@ -438,56 +493,62 @@ def add_plot_line(self, x: np.ndarray, y: np.ndarray, data_file: str, display_name = self._apply_offset(d_name, offset) if offset else d_name # Generate line command - line_cmd = f' {display_name}' - + line_cmd = f" {display_name}" + # Convert matplotlib linewidth (points) to GLE lwidth (cm) # If linewidth is 0 or 1, use default from style config if linewidth == 0 or linewidth == 1: gle_lwidth = linewidth_pt_to_cm(self.style.default_linewidth) else: gle_lwidth = linewidth_pt_to_cm(linewidth) - + # A "no line" state is signalled by a linestyle of none/empty. GLE # supports markers on line datasets natively (``d1 line marker circle # msize 0.2``), so a series may carry a line, a marker, or both. - has_line = linestyle not in ('none', 'None', '', ' ', None) + has_line = linestyle not in ("none", "None", "", " ", None) if has_line: # Line plot with optional smooth curves (configured via graph.smooth_curves) if self.graph.smooth_curves: - line_cmd += ' line smooth' + line_cmd += " line smooth" else: - line_cmd += ' line' - line_cmd += f' color {color} lwidth {self._format_number(gle_lwidth)}' + line_cmd += " line" + line_cmd += f" color {color} lwidth {self._format_number(gle_lwidth)}" # Use configured line styles from style config - if linestyle == '--': - line_cmd += f' lstyle {self.style.line_style_dashed}' - elif linestyle == ':': - line_cmd += f' lstyle {self.style.line_style_dotted}' - elif linestyle == '-.': - line_cmd += f' lstyle {self.style.line_style_dashdot}' - line_cmd += ' lstyle 4' + if linestyle == "--": + line_cmd += f" lstyle {self.style.line_style_dashed}" + elif linestyle == ":": + line_cmd += f" lstyle {self.style.line_style_dotted}" + elif linestyle == "-.": + line_cmd += f" lstyle {self.style.line_style_dashdot}" + line_cmd += " lstyle 4" if marker: # Marker overlaid on the line (line+markers). - line_cmd += f' marker {marker} msize {self._format_number(markersize)}' + line_cmd += f" marker {marker} msize {self._format_number(markersize)}" else: # No line: marker-only (scatter). Preserve the historical token # order ``marker msize color ``. - line_cmd += f' marker {marker} msize {self._format_number(markersize)} color {color}' + line_cmd += f" marker {marker} msize {self._format_number(markersize)} color {color}" # Add y2axis directive if using secondary y-axis - if yaxis == 'y2': - line_cmd += ' y2axis' + if yaxis == "y2": + line_cmd += " y2axis" line_cmd += self._key_clause(label, bool(column_names)) self.lines_gle.append(line_cmd) - def add_bar_chart(self, x: np.ndarray, heights: np.ndarray, data_file: str, - colors: Optional[List[str]] = None, label: Optional[str] = None, - column_names: Optional[List[str]] = None): + def add_bar_chart( + self, + x: np.ndarray, + heights: np.ndarray, + data_file: str, + colors: Optional[List[str]] = None, + label: Optional[str] = None, + column_names: Optional[List[str]] = None, + ): """ Add bar chart to graph. @@ -523,7 +584,7 @@ def add_bar_chart(self, x: np.ndarray, heights: np.ndarray, data_file: str, # Default to RED if no colors provided if colors is None: - colors = ['RED'] * len(x) + colors = ["RED"] * len(x) # GLE reliably supports one fill color per bar dataset. bar_color = colors[0] @@ -531,33 +592,42 @@ def add_bar_chart(self, x: np.ndarray, heights: np.ndarray, data_file: str, # Create single data file with all bars self.add_data_file(data_file, [x, heights], column_names) - d_name = f'd{self.dataset_index}' + d_name = f"d{self.dataset_index}" self.dataset_index += 1 - cmd = f' data {_format_data_filename(data_file)} {d_name}=c1,c2' + cmd = f" data {_format_data_filename(data_file)} {d_name}=c1,c2" self.lines_gle.append(cmd) - bar_cmd = f' bar {d_name} fill {bar_color}' + bar_cmd = f" bar {d_name} fill {bar_color}" self.lines_gle.append(bar_cmd) if column_names and not label: self.lines_gle.append(f' {d_name} key ""') - - def add_errorbar(self, x: np.ndarray, y: np.ndarray, data_file: str, - color: str = 'BLUE', linestyle: str = '-', - linewidth: float = 1.0, label: Optional[str] = None, - marker: Optional[str] = None, markersize: float = 0.1, - yerr_up: Optional[np.ndarray] = None, - yerr_down: Optional[np.ndarray] = None, - xerr_left: Optional[np.ndarray] = None, - xerr_right: Optional[np.ndarray] = None, - capsize: Optional[float] = None, - yaxis: str = 'y', offset: float = 0.0, - column_names: Optional[List[str]] = None): + + def add_errorbar( + self, + x: np.ndarray, + y: np.ndarray, + data_file: str, + color: str = "BLUE", + linestyle: str = "-", + linewidth: float = 1.0, + label: Optional[str] = None, + marker: Optional[str] = None, + markersize: float = 0.1, + yerr_up: Optional[np.ndarray] = None, + yerr_down: Optional[np.ndarray] = None, + xerr_left: Optional[np.ndarray] = None, + xerr_right: Optional[np.ndarray] = None, + capsize: Optional[float] = None, + yaxis: str = "y", + offset: float = 0.0, + column_names: Optional[List[str]] = None, + ): """ Add plot with error bars to graph. - + Generates GLE error bar syntax using datasets for error values. - + GLE error bar syntax reference (from GLE manual): - ``dn err `` — symmetric vertical errors - ``dn errup `` — upper vertical error @@ -567,7 +637,7 @@ def add_errorbar(self, x: np.ndarray, y: np.ndarray, data_file: str, - ``dn herrleft `` — left horizontal error - ``dn herrright `` — right horizontal error - ``dn herrwidth `` — horizontal error bar cap width - + Parameters ---------- x, y : arrays @@ -613,27 +683,35 @@ def add_errorbar(self, x: np.ndarray, y: np.ndarray, data_file: str, sorted_indices = np.argsort(x_array) x_sorted = x_array[sorted_indices] y_sorted = y_array[sorted_indices] - + # Build columns list: x, y, then error columns columns = [x_sorted, y_sorted] col_idx = 3 # Next column index (c1=x, c2=y, c3=...) - + # Track which columns hold error data yerr_up_col = None yerr_down_col = None xerr_left_col = None xerr_right_col = None - + has_yerr = yerr_up is not None or yerr_down is not None has_xerr = xerr_left is not None or xerr_right is not None - + # Check if vertical errors are symmetric (same arrays) - yerr_symmetric = (has_yerr and yerr_up is not None and yerr_down is not None - and np.array_equal(yerr_up, yerr_down)) + yerr_symmetric = ( + has_yerr + and yerr_up is not None + and yerr_down is not None + and np.array_equal(yerr_up, yerr_down) + ) # Check if horizontal errors are symmetric - xerr_symmetric = (has_xerr and xerr_left is not None and xerr_right is not None - and np.array_equal(xerr_left, xerr_right)) - + xerr_symmetric = ( + has_xerr + and xerr_left is not None + and xerr_right is not None + and np.array_equal(xerr_left, xerr_right) + ) + if has_yerr: if yerr_symmetric: # Single column for symmetric error @@ -650,7 +728,7 @@ def add_errorbar(self, x: np.ndarray, y: np.ndarray, data_file: str, columns.append(np.asarray(yerr_down)[sorted_indices]) yerr_down_col = col_idx col_idx += 1 - + if has_xerr: if xerr_symmetric: columns.append(np.asarray(xerr_left)[sorted_indices]) @@ -666,56 +744,56 @@ def add_errorbar(self, x: np.ndarray, y: np.ndarray, data_file: str, columns.append(np.asarray(xerr_right)[sorted_indices]) xerr_right_col = col_idx col_idx += 1 - + # Write data file with all columns self.add_data_file(data_file, columns, column_names) # Generate dataset name for main data - d_main = f'd{self.dataset_index}' + d_main = f"d{self.dataset_index}" self.dataset_index += 1 - + # Build data command with all dataset references - data_cmd = f' data {_format_data_filename(data_file)} {d_main}=c1,c2' - + data_cmd = f" data {_format_data_filename(data_file)} {d_main}=c1,c2" + # Create error datasets referencing the same file columns err_datasets = {} - + if has_yerr: if yerr_symmetric: - d_yerr = f'd{self.dataset_index}' + d_yerr = f"d{self.dataset_index}" self.dataset_index += 1 - data_cmd += f' {d_yerr}=c1,c{yerr_up_col}' - err_datasets['yerr'] = d_yerr + data_cmd += f" {d_yerr}=c1,c{yerr_up_col}" + err_datasets["yerr"] = d_yerr else: if yerr_up_col is not None: - d_yerr_up = f'd{self.dataset_index}' + d_yerr_up = f"d{self.dataset_index}" self.dataset_index += 1 - data_cmd += f' {d_yerr_up}=c1,c{yerr_up_col}' - err_datasets['yerr_up'] = d_yerr_up + data_cmd += f" {d_yerr_up}=c1,c{yerr_up_col}" + err_datasets["yerr_up"] = d_yerr_up if yerr_down_col is not None: - d_yerr_down = f'd{self.dataset_index}' + d_yerr_down = f"d{self.dataset_index}" self.dataset_index += 1 - data_cmd += f' {d_yerr_down}=c1,c{yerr_down_col}' - err_datasets['yerr_down'] = d_yerr_down - + data_cmd += f" {d_yerr_down}=c1,c{yerr_down_col}" + err_datasets["yerr_down"] = d_yerr_down + if has_xerr: if xerr_symmetric: - d_xerr = f'd{self.dataset_index}' + d_xerr = f"d{self.dataset_index}" self.dataset_index += 1 - data_cmd += f' {d_xerr}=c1,c{xerr_left_col}' - err_datasets['xerr'] = d_xerr + data_cmd += f" {d_xerr}=c1,c{xerr_left_col}" + err_datasets["xerr"] = d_xerr else: if xerr_left_col is not None: - d_xerr_left = f'd{self.dataset_index}' + d_xerr_left = f"d{self.dataset_index}" self.dataset_index += 1 - data_cmd += f' {d_xerr_left}=c1,c{xerr_left_col}' - err_datasets['xerr_left'] = d_xerr_left + data_cmd += f" {d_xerr_left}=c1,c{xerr_left_col}" + err_datasets["xerr_left"] = d_xerr_left if xerr_right_col is not None: - d_xerr_right = f'd{self.dataset_index}' + d_xerr_right = f"d{self.dataset_index}" self.dataset_index += 1 - data_cmd += f' {d_xerr_right}=c1,c{xerr_right_col}' - err_datasets['xerr_right'] = d_xerr_right - + data_cmd += f" {d_xerr_right}=c1,c{xerr_right_col}" + err_datasets["xerr_right"] = d_xerr_right + self.lines_gle.append(data_cmd) # A non-zero offset shifts the plotted centre vertically at plot time @@ -725,67 +803,67 @@ def add_errorbar(self, x: np.ndarray, y: np.ndarray, data_file: str, display_name = self._apply_offset(d_main, offset) if offset else d_main # Build the main dataset display command - line_cmd = f' {display_name}' - + line_cmd = f" {display_name}" + # Convert linewidth if linewidth == 0 or linewidth == 1: gle_lwidth = linewidth_pt_to_cm(self.style.default_linewidth) else: gle_lwidth = linewidth_pt_to_cm(linewidth) - + # Add line/marker styling if marker: - line_cmd += f' marker {marker} msize {self._format_number(markersize)} color {color}' + line_cmd += f" marker {marker} msize {self._format_number(markersize)} color {color}" # Also add line if linestyle is not 'none' - if linestyle not in ('', 'none', ' ', 'None'): - line_cmd += f' line lwidth {self._format_number(gle_lwidth)}' - if linestyle == '--': - line_cmd += f' lstyle {self.style.line_style_dashed}' - elif linestyle == ':': - line_cmd += f' lstyle {self.style.line_style_dotted}' - elif linestyle == '-.': - line_cmd += f' lstyle {self.style.line_style_dashdot}' + if linestyle not in ("", "none", " ", "None"): + line_cmd += f" line lwidth {self._format_number(gle_lwidth)}" + if linestyle == "--": + line_cmd += f" lstyle {self.style.line_style_dashed}" + elif linestyle == ":": + line_cmd += f" lstyle {self.style.line_style_dotted}" + elif linestyle == "-.": + line_cmd += f" lstyle {self.style.line_style_dashdot}" else: - if linestyle not in ('', 'none', ' ', 'None'): + if linestyle not in ("", "none", " ", "None"): if self.graph.smooth_curves: - line_cmd += ' line smooth' + line_cmd += " line smooth" else: - line_cmd += ' line' - line_cmd += f' color {color} lwidth {self._format_number(gle_lwidth)}' - if linestyle == '--': - line_cmd += f' lstyle {self.style.line_style_dashed}' - elif linestyle == ':': - line_cmd += f' lstyle {self.style.line_style_dotted}' - elif linestyle == '-.': - line_cmd += f' lstyle {self.style.line_style_dashdot}' - + line_cmd += " line" + line_cmd += f" color {color} lwidth {self._format_number(gle_lwidth)}" + if linestyle == "--": + line_cmd += f" lstyle {self.style.line_style_dashed}" + elif linestyle == ":": + line_cmd += f" lstyle {self.style.line_style_dotted}" + elif linestyle == "-.": + line_cmd += f" lstyle {self.style.line_style_dashdot}" + # Add vertical error bar commands - if 'yerr' in err_datasets: + if "yerr" in err_datasets: line_cmd += f' err {err_datasets["yerr"]}' else: - if 'yerr_up' in err_datasets: + if "yerr_up" in err_datasets: line_cmd += f' errup {err_datasets["yerr_up"]}' - if 'yerr_down' in err_datasets: + if "yerr_down" in err_datasets: line_cmd += f' errdown {err_datasets["yerr_down"]}' - + if capsize is not None and has_yerr: - line_cmd += f' errwidth {self._format_number(capsize)}' - + line_cmd += f" errwidth {self._format_number(capsize)}" + # Add horizontal error bar commands - if 'xerr' in err_datasets: + if "xerr" in err_datasets: line_cmd += f' herr {err_datasets["xerr"]}' else: - if 'xerr_left' in err_datasets: + if "xerr_left" in err_datasets: line_cmd += f' herrleft {err_datasets["xerr_left"]}' - if 'xerr_right' in err_datasets: + if "xerr_right" in err_datasets: line_cmd += f' herrright {err_datasets["xerr_right"]}' - + if capsize is not None and has_xerr: - line_cmd += f' herrwidth {self._format_number(capsize)}' - + line_cmd += f" herrwidth {self._format_number(capsize)}" + # Add y2axis directive if using secondary y-axis - if yaxis == 'y2': - line_cmd += ' y2axis' + if yaxis == "y2": + line_cmd += " y2axis" line_cmd += self._key_clause(label, bool(column_names)) @@ -797,38 +875,40 @@ def add_errorbar_from_file( x_col: int, y_col: int, yerr_col: Optional[int] = None, - color: str = 'BLUE', + color: str = "BLUE", marker: Optional[str] = None, markersize: float = 0.1, label: Optional[str] = None, capsize: Optional[float] = None, - yaxis: str = 'y', + yaxis: str = "y", ): """Add a marker/errorbar series that references columns in an external data file.""" - d_main = f'd{self.dataset_index}' + d_main = f"d{self.dataset_index}" self.dataset_index += 1 - data_cmd = f' data {_format_data_filename(data_file)} {d_main}=c{x_col},c{y_col}' + data_cmd = ( + f" data {_format_data_filename(data_file)} {d_main}=c{x_col},c{y_col}" + ) d_yerr = None if yerr_col is not None: - d_yerr = f'd{self.dataset_index}' + d_yerr = f"d{self.dataset_index}" self.dataset_index += 1 - data_cmd += f' {d_yerr}=c{x_col},c{yerr_col}' + data_cmd += f" {d_yerr}=c{x_col},c{yerr_col}" self.lines_gle.append(data_cmd) - line_cmd = f' {d_main}' + line_cmd = f" {d_main}" if marker: - line_cmd += f' marker {marker} msize {self._format_number(markersize)} color {color}' + line_cmd += f" marker {marker} msize {self._format_number(markersize)} color {color}" else: - line_cmd += f' color {color}' + line_cmd += f" color {color}" if d_yerr is not None: - line_cmd += f' err {d_yerr}' + line_cmd += f" err {d_yerr}" if capsize is not None: - line_cmd += f' errwidth {self._format_number(capsize)}' + line_cmd += f" errwidth {self._format_number(capsize)}" - if yaxis == 'y2': - line_cmd += ' y2axis' + if yaxis == "y2": + line_cmd += " y2axis" if label: line_cmd += f' key "{label}"' @@ -840,43 +920,54 @@ def add_plot_line_from_file( data_file: str, x_col: int, y_col: int, - color: str = 'BLUE', - linestyle: str = '-', + color: str = "BLUE", + linestyle: str = "-", linewidth: float = 1.0, label: Optional[str] = None, - yaxis: str = 'y', + yaxis: str = "y", ): """Add a line series that references columns in an external data file.""" - d_main = f'd{self.dataset_index}' + d_main = f"d{self.dataset_index}" self.dataset_index += 1 - self.lines_gle.append(f' data {_format_data_filename(data_file)} {d_main}=c{x_col},c{y_col}') + self.lines_gle.append( + f" data {_format_data_filename(data_file)} {d_main}=c{x_col},c{y_col}" + ) if linewidth == 0 or linewidth == 1: gle_lwidth = linewidth_pt_to_cm(self.style.default_linewidth) else: gle_lwidth = linewidth_pt_to_cm(linewidth) - line_cmd = f' {d_main} line color {color} lwidth {self._format_number(gle_lwidth)}' - if linestyle == '--': - line_cmd += f' lstyle {self.style.line_style_dashed}' - elif linestyle == ':': - line_cmd += f' lstyle {self.style.line_style_dotted}' - elif linestyle == '-.': - line_cmd += f' lstyle {self.style.line_style_dashdot}' + line_cmd = ( + f" {d_main} line color {color} lwidth {self._format_number(gle_lwidth)}" + ) + if linestyle == "--": + line_cmd += f" lstyle {self.style.line_style_dashed}" + elif linestyle == ":": + line_cmd += f" lstyle {self.style.line_style_dotted}" + elif linestyle == "-.": + line_cmd += f" lstyle {self.style.line_style_dashdot}" - if yaxis == 'y2': - line_cmd += ' y2axis' + if yaxis == "y2": + line_cmd += " y2axis" if label: line_cmd += f' key "{label}"' self.lines_gle.append(line_cmd) - - def add_fill_between(self, x: np.ndarray, y1: np.ndarray, y2: np.ndarray, - data_file: str, color: str = 'LIGHTBLUE', alpha: float = 1.0, - offset: float = 0.0, - column_names: Optional[List[str]] = None): + + def add_fill_between( + self, + x: np.ndarray, + y1: np.ndarray, + y2: np.ndarray, + data_file: str, + color: str = "LIGHTBLUE", + alpha: float = 1.0, + offset: float = 0.0, + column_names: Optional[List[str]] = None, + ): """ Add fill between two curves. @@ -904,11 +995,11 @@ def add_fill_between(self, x: np.ndarray, y1: np.ndarray, y2: np.ndarray, self.add_data_file(data_file, [x, y1, y2], column_names) # Create two unique dataset names for the fill between operation - d1_name = f'd{self.dataset_index}' - d2_name = f'd{self.dataset_index + 1}' + d1_name = f"d{self.dataset_index}" + d2_name = f"d{self.dataset_index + 1}" self.dataset_index += 2 - cmd = f' data {_format_data_filename(data_file)} {d1_name}=c1,c2 {d2_name}=c1,c3' + cmd = f" data {_format_data_filename(data_file)} {d1_name}=c1,c2 {d2_name}=c1,c3" self.lines_gle.append(cmd) # A non-zero offset shifts BOTH band edges vertically by the same amount @@ -921,7 +1012,7 @@ def add_fill_between(self, x: np.ndarray, y1: np.ndarray, y2: np.ndarray, fill_a, fill_b = d1_name, d2_name # GLE fill between two datasets: fill d1,d2 color X - self.lines_gle.append(f' fill {fill_a},{fill_b} color {color}') + self.lines_gle.append(f" fill {fill_a},{fill_b} color {color}") if column_names: self.lines_gle.append(f' {fill_a} key ""') @@ -932,9 +1023,9 @@ def add_text( x: float, y: float, text: str, - color: str = 'BLACK', + color: str = "BLACK", fontsize: Optional[float] = None, - halign: str = 'left', + halign: str = "left", box_color: Optional[str] = None, ): """Add text annotation in graph data coordinates.""" @@ -943,21 +1034,21 @@ def add_text( if fontsize is not None: hei_cm = self._format_number(fontsize_pt_to_cm(float(fontsize))) if hei_cm != self._text_state_hei_cm: - self._pending_graph_text_lines.append(f'set hei {hei_cm}') + self._pending_graph_text_lines.append(f"set hei {hei_cm}") self._text_state_hei_cm = hei_cm if color and color != self._text_state_color: - self._pending_graph_text_lines.append(f'set color {color}') + self._pending_graph_text_lines.append(f"set color {color}") self._text_state_color = color just_map = { - 'left': 'left', - 'center': 'center', - 'right': 'right', + "left": "left", + "center": "center", + "right": "right", } - just = just_map.get(str(halign).lower(), 'left') + just = just_map.get(str(halign).lower(), "left") if just != self._text_state_just: - self._pending_graph_text_lines.append(f'set just {just}') + self._pending_graph_text_lines.append(f"set just {just}") self._text_state_just = just # Boxed text in graph-data coordinates can produce invalid bounds in @@ -965,15 +1056,13 @@ def add_text( # box_color is currently accepted for API compatibility but ignored. _ = box_color self._pending_graph_text_lines.append( - f'amove xg({self._format_number(x)}) yg({self._format_number(y)})' - ) - self._pending_graph_text_lines.append( - f'write "{escaped_text}"' + f"amove xg({self._format_number(x)}) yg({self._format_number(y)})" ) - + self._pending_graph_text_lines.append(f'write "{escaped_text}"') + def add_legend(self, position: Optional[str] = None): """Add legend configuration. - + Parameters ---------- position : str, optional @@ -986,7 +1075,7 @@ def add_legend(self, position: Optional[str] = None): # Try long form, else use as-is (short form) gle_pos = KEY_POSITIONS_LONG_TO_SHORT.get(pos, pos) - self.lines_gle.append(f' key pos {gle_pos}') + self.lines_gle.append(f" key pos {gle_pos}") def add_key_off(self): """Suppress the graph key entirely. @@ -995,11 +1084,14 @@ def add_key_off(self): attribute, even without a ``key pos`` command, so an explicit ``key off`` is required to hide the legend for labeled series. """ - self.lines_gle.append(' key off') - - def finalize(self, include_graph_end: bool = True, - graph_passthrough: Optional[List[str]] = None, - passthrough_trailer: Optional[List[str]] = None): + self.lines_gle.append(" key off") + + def finalize( + self, + include_graph_end: bool = True, + graph_passthrough: Optional[List[str]] = None, + passthrough_trailer: Optional[List[str]] = None, + ): """Add closing statements. Parameters @@ -1021,22 +1113,22 @@ def finalize(self, include_graph_end: bool = True, self.end_graph(passthrough=graph_passthrough) if passthrough_trailer: self.lines_gle.extend(passthrough_trailer) - + def get_gle_content(self) -> str: """Get complete GLE script content.""" - return '\n'.join(self.lines_gle) - - def write_files(self, output_dir: str, base_filename: str = 'plot'): + return "\n".join(self.lines_gle) + + def write_files(self, output_dir: str, base_filename: str = "plot"): """ Write GLE script and data files. - + Parameters ---------- output_dir : str Directory for output files base_filename : str Base name for files (without extension) - + Returns ------- dict @@ -1044,32 +1136,32 @@ def write_files(self, output_dir: str, base_filename: str = 'plot'): """ output_dir = Path(output_dir) output_dir.mkdir(parents=True, exist_ok=True) - + files = {} - + # Write GLE script - gle_file = output_dir / f'{base_filename}.gle' - gle_file.write_text(self.get_gle_content(), encoding='utf-8') - files['script'] = gle_file - + gle_file = output_dir / f"{base_filename}.gle" + gle_file.write_text(self.get_gle_content(), encoding="utf-8") + files["script"] = gle_file + # Write data files for data_file, content in self.data_files.items(): data_path = output_dir / data_file - data_path.write_text(content, encoding='utf-8') + data_path.write_text(content, encoding="utf-8") files[data_file] = data_path - + return files - + @staticmethod def _format_number(val: float, precision: int = 6) -> str: """Format number for GLE output.""" if isinstance(val, (int, float)): if abs(val) < 1e-10: - return '0' + return "0" if abs(val) > 1e10: - return f'{val:.3e}' + return f"{val:.3e}" # Use general format with reasonable precision - formatted = f'{val:.{precision}g}' + formatted = f"{val:.{precision}g}" return formatted return str(val) @@ -1109,4 +1201,4 @@ def _key_clause(label: Optional[str], has_header: bool) -> str: return f' key "{label}"' if has_header: return ' key ""' - return '' + return "" diff --git a/tests/gui/test_data_panel.py b/tests/gui/test_data_panel.py index fce12bc..023ebd5 100644 --- a/tests/gui/test_data_panel.py +++ b/tests/gui/test_data_panel.py @@ -91,9 +91,7 @@ def test_load_file_populates_table(qapp, document, tmp_path): assert panel.file_list.item(0).text() == "data.csv" assert panel.preview_table.rowCount() == 4 assert panel.preview_table.columnCount() == 3 - headers = [ - panel.preview_table.horizontalHeaderItem(i).text() for i in range(3) - ] + headers = [panel.preview_table.horizontalHeaderItem(i).text() for i in range(3)] assert headers == ["x", "y", "yerr"] @@ -104,9 +102,7 @@ def test_column_combos_populated_after_load(qapp, document, tmp_path): x_items = [panel.x_combo.itemText(i) for i in range(panel.x_combo.count())] y_items = [panel.y_combo.itemText(i) for i in range(panel.y_combo.count())] - yerr_items = [ - panel.yerr_combo.itemText(i) for i in range(panel.yerr_combo.count()) - ] + yerr_items = [panel.yerr_combo.itemText(i) for i in range(panel.yerr_combo.count())] assert x_items == ["x", "y", "yerr"] assert y_items == ["x", "y", "yerr"] diff --git a/tests/gui/test_panels.py b/tests/gui/test_panels.py index ccf1d8c..fbbc7d7 100644 --- a/tests/gui/test_panels.py +++ b/tests/gui/test_panels.py @@ -65,7 +65,9 @@ def qapp(): def _make_figure(): fig = gleplot.figure(figsize=(8, 6), dpi=100) ax = fig.gca() - ax.plot([1, 2, 3], [1, 4, 9], color="blue", linestyle="--", linewidth=2, label="line a") + ax.plot( + [1, 2, 3], [1, 4, 9], color="blue", linestyle="--", linewidth=2, label="line a" + ) ax.scatter([1, 2, 3], [3, 2, 1], color="red", marker="s", label="scatter a") ax.set_title("My title") ax.set_xlabel("X") @@ -222,7 +224,9 @@ def test_set_axes_hook(self, document): class TestSeriesPanel: def test_populate_series_list(self, document): panel = SeriesPanel(document) - texts = [panel.series_list.item(i).text() for i in range(panel.series_list.count())] + texts = [ + panel.series_list.item(i).text() for i in range(panel.series_list.count()) + ] assert texts == ["line: line a", "scatter: scatter a"] def test_select_line_shows_correct_controls(self, document): diff --git a/tests/parser/_golden_battery.py b/tests/parser/_golden_battery.py index 69e2152..5d88639 100644 --- a/tests/parser/_golden_battery.py +++ b/tests/parser/_golden_battery.py @@ -15,99 +15,122 @@ def single_line(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) - ax.plot([1, 2, 3, 4], [1, 4, 9, 16], color='blue', label='quad') - ax.set_xlabel('x') - ax.set_ylabel('y') - ax.set_title('single line') + ax.plot([1, 2, 3, 4], [1, 4, 9, 16], color="blue", label="quad") + ax.set_xlabel("x") + ax.set_ylabel("y") + ax.set_title("single line") return fig def multi_series_styles(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) x = np.linspace(0, 10, 20) - ax.plot(x, np.sin(x), color='red', linestyle='--', linewidth=2, label='sin') - ax.plot(x, np.cos(x), color='green', linestyle=':', label='cos') - ax.plot(x, np.sin(x) * 0.5, color='blue', marker='o', linestyle='none', - markersize=8, label='half') - ax.plot(x, np.cos(x) * 0.5, color='black', linestyle='-.', linewidth=3, label='dashdot') - ax.legend(loc='upper left') + ax.plot(x, np.sin(x), color="red", linestyle="--", linewidth=2, label="sin") + ax.plot(x, np.cos(x), color="green", linestyle=":", label="cos") + ax.plot( + x, + np.sin(x) * 0.5, + color="blue", + marker="o", + linestyle="none", + markersize=8, + label="half", + ) + ax.plot( + x, np.cos(x) * 0.5, color="black", linestyle="-.", linewidth=3, label="dashdot" + ) + ax.legend(loc="upper left") return fig def scatter(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) - ax.scatter([1, 2, 3, 4], [4, 3, 2, 1], color='purple', s=40, marker='s', - label='pts') + ax.scatter( + [1, 2, 3, 4], [4, 3, 2, 1], color="purple", s=40, marker="s", label="pts" + ) ax.legend() return fig def bar(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) - ax.bar([1, 2, 3, 4, 5], [10, 24, 36, 18, 7], color='orange') - ax.set_title('bar') + ax.bar([1, 2, 3, 4, 5], [10, 24, 36, 18, 7], color="orange") + ax.set_title("bar") return fig def errorbar_symmetric(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) - ax.errorbar([1, 2, 3], [2, 4, 6], yerr=0.5, color='red', marker='o', - capsize=4, label='sym') + ax.errorbar( + [1, 2, 3], [2, 4, 6], yerr=0.5, color="red", marker="o", capsize=4, label="sym" + ) ax.legend() return fig def errorbar_asymmetric_xy(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) - ax.errorbar([1, 2, 3], [2, 4, 6], - yerr=([0.1, 0.2, 0.3], [0.4, 0.5, 0.6]), - xerr=0.2, capsize=3, - color='blue', marker='s') + ax.errorbar( + [1, 2, 3], + [2, 4, 6], + yerr=([0.1, 0.2, 0.3], [0.4, 0.5, 0.6]), + xerr=0.2, + capsize=3, + color="blue", + marker="s", + ) return fig def fill_between(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) x = np.linspace(0, 5, 10) - ax.fill_between(x, np.zeros_like(x), x ** 0.5, color='lightblue', alpha=0.4) - ax.plot(x, x ** 0.5, color='blue') + ax.fill_between(x, np.zeros_like(x), x**0.5, color="lightblue", alpha=0.4) + ax.plot(x, x**0.5, color="blue") return fig def text_annotations(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) ax.plot([1, 2, 3], [1, 2, 3]) - ax.text(1.5, 2.0, 'peak', color='red', fontsize=14, ha='center') - ax.text(2.5, 1.0, 'boxed', bbox={'facecolor': 'yellow'}) + ax.text(1.5, 2.0, "peak", color="red", fontsize=14, ha="center") + ax.text(2.5, 1.0, "boxed", bbox={"facecolor": "yellow"}) return fig def secondary_yaxis(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) - ax.plot([1, 2, 3], [1, 2, 3], color='blue', label='left', yaxis='y') - ax.plot([1, 2, 3], [100, 200, 300], color='red', label='right', yaxis='y2') - ax.set_ylabel('left y') - ax.set_ylabel('right y', axis='y2') - ax.set_ylim(0, 400, axis='y2') - ax.set_yscale('log', axis='y2') + ax.plot([1, 2, 3], [1, 2, 3], color="blue", label="left", yaxis="y") + ax.plot([1, 2, 3], [100, 200, 300], color="red", label="right", yaxis="y2") + ax.set_ylabel("left y") + ax.set_ylabel("right y", axis="y2") + ax.set_ylim(0, 400, axis="y2") + ax.set_yscale("log", axis="y2") ax.legend() return fig def legend_positions_all(): figs = [] - for loc in ('upper right', 'upper left', 'lower left', 'lower right', 'center', 'best'): - fig = glp.figure(data_prefix='golden') + for loc in ( + "upper right", + "upper left", + "lower left", + "lower right", + "center", + "best", + ): + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) ax.plot([1, 2, 3], [1, 2, 3], label=loc) ax.legend(loc=loc) @@ -116,46 +139,62 @@ def legend_positions_all(): def subplots_sharex(): - fig, axes = glp.subplots(3, 1, sharex=True, data_prefix='golden') + fig, axes = glp.subplots(3, 1, sharex=True, data_prefix="golden") for i, ax in enumerate(axes): - ax.plot([1, 2, 3], [i, i + 1, i + 2], label=f's{i}') - ax.set_ylabel(f'y{i}') - axes[-1].set_xlabel('shared x') + ax.plot([1, 2, 3], [i, i + 1, i + 2], label=f"s{i}") + ax.set_ylabel(f"y{i}") + axes[-1].set_xlabel("shared x") return fig def subplots_grid_mixed(): - fig, axes = glp.subplots(2, 2, data_prefix='golden') + fig, axes = glp.subplots(2, 2, data_prefix="golden") axes[0].plot([1, 2, 3], [1, 2, 3]) - axes[1].scatter([1, 2, 3], [3, 2, 1], marker='o') - axes[2].bar([1, 2, 3], [2, 4, 6], color='red') + axes[1].scatter([1, 2, 3], [3, 2, 1], marker="o") + axes[2].bar([1, 2, 3], [2, 4, 6], color="red") axes[3].errorbar([1, 2, 3], [1, 2, 3], yerr=0.2, capsize=3) fig.subplots_adjust(hspace=0.4, wspace=0.4) return fig def file_series(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) - ax.line_from_file('external.dat', 1, 2, color='blue', linestyle='--', - label='line-file') - ax.errorbar_from_file('external.dat', 1, 2, yerr_col=3, color='red', - marker='o', capsize=4, label='eb-file') + ax.line_from_file( + "external.dat", 1, 2, color="blue", linestyle="--", label="line-file" + ) + ax.errorbar_from_file( + "external.dat", + 1, + 2, + yerr_col=3, + color="red", + marker="o", + capsize=4, + label="eb-file", + ) ax.legend() return fig def large_markersize_and_linewidth(): - fig = glp.figure(data_prefix='golden') + fig = glp.figure(data_prefix="golden") ax = fig.add_subplot(111) - ax.plot([1, 2, 3], [1, 2, 3], linewidth=0.25, marker='D', markersize=20, label='thin-big') - ax.plot([1, 2, 3], [3, 2, 1], linewidth=4.5, label='thick') + ax.plot( + [1, 2, 3], + [1, 2, 3], + linewidth=0.25, + marker="D", + markersize=20, + label="thin-big", + ) + ax.plot([1, 2, 3], [3, 2, 1], linewidth=4.5, label="thick") ax.legend() return fig def custom_figsize_and_dpi(): - fig = glp.figure(figsize=(10, 4), dpi=150, data_prefix='golden') + fig = glp.figure(figsize=(10, 4), dpi=150, data_prefix="golden") ax = fig.add_subplot(111) ax.plot([1, 2, 3], [1, 2, 3]) return fig diff --git a/tests/parser/test_fixed_point.py b/tests/parser/test_fixed_point.py index a9ad829..6469622 100644 --- a/tests/parser/test_fixed_point.py +++ b/tests/parser/test_fixed_point.py @@ -25,14 +25,13 @@ from tests.parser import _golden_battery as golden from tests.integration import test_project_io as project_battery - # Builders whose GLE text is NOT byte-identical after a round-trip because they # use subplots_adjust (documented layout loss). Their data files must still # round-trip byte-identically, and the layout that IS emitted must be valid. _SUBPLOT_ADJUST_EXEMPT = { - "subplots_grid_mixed", # golden battery - "_subplots_grid", # project battery (default hspace/wspace) - "_subplots_sharey_adjust", # project battery + "subplots_grid_mixed", # golden battery + "_subplots_grid", # project battery (default hspace/wspace) + "_subplots_sharey_adjust", # project battery } @@ -76,6 +75,7 @@ def _round_trip(builder, tmp_path: Path): # -- Golden battery --------------------------------------------------------- + @pytest.mark.parametrize("name", golden.BUILDER_IDS) def test_golden_battery_fixed_point(name, tmp_path): builder = getattr(golden, name) @@ -89,15 +89,16 @@ def test_golden_battery_fixed_point(name, tmp_path): # Documented subplots_adjust loss: the text differs only in the baked # amove/size geometry. Everything that is not layout geometry must be # unchanged, so strip the geometry lines and compare the rest. - assert _strip_layout(text1) == _strip_layout(text2), ( - f"{name}: non-layout content changed after round-trip" - ) + assert _strip_layout(text1) == _strip_layout( + text2 + ), f"{name}: non-layout content changed after round-trip" else: assert text2 == text1, f"{name}: GLE text differs after round-trip" # -- Project-I/O battery ---------------------------------------------------- + @pytest.mark.parametrize( "builder", project_battery.BUILDERS, ids=project_battery.BUILDER_IDS ) diff --git a/tests/parser/test_recognizer.py b/tests/parser/test_recognizer.py index 04140ca..94218fc 100644 --- a/tests/parser/test_recognizer.py +++ b/tests/parser/test_recognizer.py @@ -42,6 +42,7 @@ def _reset_counter(): # to_dict equivalence up to documented normalizations # --------------------------------------------------------------------------- # + def normalize(d: dict) -> dict: """Apply the documented recognizer normalizations to a figure ``to_dict``. @@ -77,8 +78,13 @@ def normalize(d: dict) -> dict: fig["figsize"] = [float(v) for v in fig["figsize"]] # Figure-level: drop data-naming/counter state that need not round-trip. - for k in ("data_prefix", "local_data_counter", "global_data_counter", - "used_data_files", "subplot_adjust"): + for k in ( + "data_prefix", + "local_data_counter", + "global_data_counter", + "used_data_files", + "subplot_adjust", + ): fig.pop(k, None) # fontsize -> emitted cm -> pt. @@ -207,6 +213,7 @@ def test_to_dict_equivalence(name, tmp_path): # Broken-data recovery # --------------------------------------------------------------------------- # + def test_broken_data_becomes_file_series_with_error(tmp_path): _gleplot_axes._global_data_file_counter = 0 fig = golden.single_line() @@ -241,6 +248,7 @@ def test_broken_data_becomes_file_series_with_error(tmp_path): # Hand-written tolerance # --------------------------------------------------------------------------- # + def _write(tmp_path: Path, name: str, content: str, dats: dict | None = None) -> Path: for dat_name, dat_content in (dats or {}).items(): (tmp_path / dat_name).write_text(dat_content, encoding="utf-8") @@ -325,16 +333,16 @@ def test_unknown_statements_in_all_bucket_positions(tmp_path): src = ( "! GLE graphics file\n" "! hand note\n" - "set weird_directive 7\n" # header passthrough + "set weird_directive 7\n" # header passthrough "size 20.32 15.24\n" "set hei 0.42328\n" "begin graph\n" " data u_1.dat d1=c1,c2\n" " d1 line color BLUE lwidth 0.05292\n" - " mystery_stmt inside graph\n" # axes passthrough + " mystery_stmt inside graph\n" # axes passthrough "end graph\n" - "! trailing note\n" # trailer passthrough - "draw somebox\n" # trailer passthrough + "! trailing note\n" # trailer passthrough + "draw somebox\n" # trailer passthrough ) p = _write(tmp_path, "u.gle", src, {"u_1.dat": "1 1\n2 2\n"}) rec = parse_gle_figure(p) @@ -402,7 +410,9 @@ def test_no_metadata_block_all_references(tmp_path): "end graph\n" ) p = _write( - tmp_path, "h.gle", src, + tmp_path, + "h.gle", + src, {"data_5.dat": "1 1\n2 2\n", "other.csv": "1 2\n2 1\n"}, ) rec = parse_gle_figure(p) @@ -427,7 +437,9 @@ def test_missing_grid_multigraph_falls_back(tmp_path): "end graph\n" ) p = _write( - tmp_path, "mg.gle", src, + tmp_path, + "mg.gle", + src, {"g_1.dat": "1 1\n2 2\n", "h_1.dat": "1 2\n2 1\n"}, ) rec = parse_gle_figure(p) @@ -440,6 +452,7 @@ def test_missing_grid_multigraph_falls_back(tmp_path): # open_gle smoke # --------------------------------------------------------------------------- # + def test_open_gle_smoke(tmp_path): _gleplot_axes._global_data_file_counter = 0 fig = golden.single_line() diff --git a/tests/unit/test_serialization.py b/tests/unit/test_serialization.py index 99c4d5c..5cc5c38 100644 --- a/tests/unit/test_serialization.py +++ b/tests/unit/test_serialization.py @@ -18,25 +18,26 @@ def _simple_figure(): - fig = glp.figure(figsize=(8, 6), data_prefix='u') + fig = glp.figure(figsize=(8, 6), data_prefix="u") ax = fig.add_subplot(111) - ax.plot([1, 2, 3], [1, 4, 9], color='red', label='q') - ax.set_xlabel('x') - ax.set_ylabel('y') + ax.plot([1, 2, 3], [1, 4, 9], color="red", label="q") + ax.set_xlabel("x") + ax.set_ylabel("y") ax.legend() return fig # -- Envelope shape --------------------------------------------------------- + def test_envelope_shape(): fig = _simple_figure() d = fig.to_dict() - assert d['format'] == PROJECT_FORMAT - assert d['version'] == PROJECT_VERSION - assert d['gleplot_version'] == glp.__version__ - assert 'figure' in d - assert isinstance(d['figure']['axes'], list) + assert d["format"] == PROJECT_FORMAT + assert d["version"] == PROJECT_VERSION + assert d["gleplot_version"] == glp.__version__ + assert "figure" in d + assert isinstance(d["figure"]["axes"], list) def test_to_dict_is_json_safe(): @@ -49,16 +50,16 @@ def test_to_dict_is_json_safe(): def test_numpy_conversion_to_native_types(): - fig = glp.figure(data_prefix='u') + fig = glp.figure(data_prefix="u") ax = fig.add_subplot(111) x = np.array([1.0, 2.0, 3.0]) y = np.array([4.0, 5.0, 6.0]) ax.plot(x, y) - line = fig.to_dict()['figure']['axes'][0]['lines'][0] - assert isinstance(line['x'], list) - assert all(isinstance(v, float) for v in line['x']) + line = fig.to_dict()["figure"]["axes"][0]["lines"][0] + assert isinstance(line["x"], list) + assert all(isinstance(v, float) for v in line["x"]) # No numpy types leak through. - assert not isinstance(line['x'][0], np.generic) + assert not isinstance(line["x"][0], np.generic) def test_numpy_scalar_limits_converted(): @@ -66,15 +67,16 @@ def test_numpy_scalar_limits_converted(): ax = fig.axes_list[0] ax.xmin = np.float64(0.5) ax.xmax = np.int64(10) - d = fig.to_dict()['figure']['axes'][0] - assert d['xmin'] == 0.5 - assert d['xmax'] == 10 - assert not isinstance(d['xmin'], np.generic) + d = fig.to_dict()["figure"]["axes"][0] + assert d["xmin"] == 0.5 + assert d["xmax"] == 10 + assert not isinstance(d["xmin"], np.generic) json.dumps(d) # still json-safe # -- Determinism ------------------------------------------------------------ + def test_to_dict_deterministic(): fig = _simple_figure() assert fig.to_dict() == fig.to_dict() @@ -91,35 +93,36 @@ def test_round_trip_restores_arrays(): fig = _simple_figure() fig2 = Figure.from_dict(fig.to_dict()) line = fig2.axes_list[0].lines[0] - assert isinstance(line['x'], np.ndarray) - assert isinstance(line['y'], np.ndarray) + assert isinstance(line["x"], np.ndarray) + assert isinstance(line["y"], np.ndarray) def test_errorbar_none_arrays_stay_none(): - fig = glp.figure(data_prefix='u') + fig = glp.figure(data_prefix="u") ax = fig.add_subplot(111) ax.errorbar([1, 2, 3], [1, 2, 3], yerr=0.5) # no xerr fig2 = Figure.from_dict(fig.to_dict()) eb = fig2.axes_list[0].errorbars[0] - assert eb['xerr_left'] is None - assert eb['xerr_right'] is None - assert isinstance(eb['yerr_up'], np.ndarray) + assert eb["xerr_left"] is None + assert eb["xerr_right"] is None + assert isinstance(eb["yerr_up"], np.ndarray) # -- Envelope validation ---------------------------------------------------- + def test_wrong_format_raises(): fig = _simple_figure() d = fig.to_dict() - d['format'] = 'not-gleplot' - with pytest.raises(ValueError, match='format'): + d["format"] = "not-gleplot" + with pytest.raises(ValueError, match="format"): Figure.from_dict(d) def test_missing_format_raises(): fig = _simple_figure() d = fig.to_dict() - del d['format'] + del d["format"] with pytest.raises(ValueError): Figure.from_dict(d) @@ -127,35 +130,36 @@ def test_missing_format_raises(): def test_unsupported_version_raises(): fig = _simple_figure() d = fig.to_dict() - d['version'] = 999 - with pytest.raises(ValueError, match='version'): + d["version"] = 999 + with pytest.raises(ValueError, match="version"): Figure.from_dict(d) def test_missing_figure_block_raises(): - d = {'format': PROJECT_FORMAT, 'version': PROJECT_VERSION} - with pytest.raises(ValueError, match='figure'): + d = {"format": PROJECT_FORMAT, "version": PROJECT_VERSION} + with pytest.raises(ValueError, match="figure"): Figure.from_dict(d) # -- Forward compatibility -------------------------------------------------- + def test_unknown_keys_ignored(): fig = _simple_figure() d = fig.to_dict() - d['extra_top_level'] = 'ignored' - d['figure']['future_field'] = {'anything': 1} - d['figure']['axes'][0]['future_axes_field'] = [1, 2, 3] + d["extra_top_level"] = "ignored" + d["figure"]["future_field"] = {"anything": 1} + d["figure"]["axes"][0]["future_axes_field"] = [1, 2, 3] # Should reconstruct without error and preserve known state. fig2 = Figure.from_dict(d) - assert fig2.axes_list[0].xlabel_text == 'x' + assert fig2.axes_list[0].xlabel_text == "x" def test_data_file_names_preserved(): fig = _simple_figure() - original = fig.axes_list[0].lines[0]['data_file'] + original = fig.axes_list[0].lines[0]["data_file"] fig2 = Figure.from_dict(fig.to_dict()) - assert fig2.axes_list[0].lines[0]['data_file'] == original + assert fig2.axes_list[0].lines[0]["data_file"] == original def test_used_data_files_round_tripped(): @@ -166,25 +170,27 @@ def test_used_data_files_round_tripped(): # -- Config overrides ------------------------------------------------------- + def test_config_overrides_round_trip(): - style = glp.GLEStyleConfig(font='helvetica', fontsize=14) - graph = glp.GLEGraphConfig(smooth_curves=False, legend_position='bl') + style = glp.GLEStyleConfig(font="helvetica", fontsize=14) + graph = glp.GLEGraphConfig(smooth_curves=False, legend_position="bl") marker = glp.GLEMarkerConfig(msize_scale=2.0, mdist=0.5) - fig = glp.figure(style=style, graph=graph, marker=marker, data_prefix='u') + fig = glp.figure(style=style, graph=graph, marker=marker, data_prefix="u") fig.add_subplot(111).plot([1, 2], [3, 4]) fig2 = Figure.from_dict(fig.to_dict()) - assert fig2.style.font == 'helvetica' + assert fig2.style.font == "helvetica" assert fig2.style.fontsize == 14 assert fig2.graph.smooth_curves is False - assert fig2.graph.legend_position == 'bl' + assert fig2.graph.legend_position == "bl" assert fig2.marker_config.msize_scale == 2.0 assert fig2.marker_config.mdist == 0.5 # -- Empty figure ----------------------------------------------------------- + def test_empty_figure_round_trip(): - fig = glp.figure(data_prefix='u') + fig = glp.figure(data_prefix="u") d = fig.to_dict() fig2 = Figure.from_dict(d) assert fig2.axes_list == [] @@ -201,12 +207,13 @@ def test_from_dict_does_not_mutate_input(): # -- Forward-compat: unknown keys inside config sub-dicts ------------------- + def test_from_dict_ignores_unknown_style_graph_marker_keys(): fig = _simple_figure() d = fig.to_dict() - d['figure']['config']['style']['not_a_real_style_field'] = 'nonsense' - d['figure']['config']['graph']['not_a_real_graph_field'] = 123 - d['figure']['config']['marker']['not_a_real_marker_field'] = [1, 2, 3] + d["figure"]["config"]["style"]["not_a_real_style_field"] = "nonsense" + d["figure"]["config"]["graph"]["not_a_real_graph_field"] = 123 + d["figure"]["config"]["marker"]["not_a_real_marker_field"] = [1, 2, 3] # Should not raise TypeError despite the unrecognized keys. fig2 = Figure.from_dict(d) @@ -219,6 +226,7 @@ def test_from_dict_ignores_unknown_style_graph_marker_keys(): # -- Data-file counter round-trip (FIX 4) ------------------------------------ + def test_global_data_counter_round_trips_across_fresh_process(monkeypatch): """Simulate loading a saved project in a fresh process (counter reset to 0).""" fig = glp.figure() # no data_prefix -> uses the global counter @@ -227,18 +235,18 @@ def test_global_data_counter_round_trips_across_fresh_process(monkeypatch): ax.plot([1, 2, 3], [4, 5, 6]) d = fig.to_dict() - saved_counter = d['figure']['global_data_counter'] + saved_counter = d["figure"]["global_data_counter"] assert saved_counter >= 2 # Simulate a fresh process/session: reset the module-global counter. - monkeypatch.setattr(glp_axes, '_global_data_file_counter', 0) + monkeypatch.setattr(glp_axes, "_global_data_file_counter", 0) fig2 = Figure.from_dict(d) ax2 = fig2.add_subplot(111) ax2.plot([7, 8, 9], [1, 2, 3]) - new_data_file = ax2.lines[-1]['data_file'] - assert new_data_file == f'data_{saved_counter}.dat' + new_data_file = ax2.lines[-1]["data_file"] + assert new_data_file == f"data_{saved_counter}.dat" def test_global_data_counter_takes_max_with_in_process_value(monkeypatch): @@ -246,11 +254,11 @@ def test_global_data_counter_takes_max_with_in_process_value(monkeypatch): fig = glp.figure() fig.add_subplot(111).plot([1, 2], [1, 2]) d = fig.to_dict() - saved_counter = d['figure']['global_data_counter'] + saved_counter = d["figure"]["global_data_counter"] # Simulate another figure in the same process having advanced the # counter further than the saved value. - monkeypatch.setattr(glp_axes, '_global_data_file_counter', saved_counter + 10) + monkeypatch.setattr(glp_axes, "_global_data_file_counter", saved_counter + 10) Figure.from_dict(d) assert glp_axes._global_data_file_counter == saved_counter + 10 @@ -271,20 +279,44 @@ def test_global_data_counter_takes_max_with_in_process_value(monkeypatch): # format: `figure` is a back-reference to the parent Figure (set by the # constructor from the caller, not user/plot state), reconstructed by # Figure.from_dict passing itself into Axes.from_dict. -_AXES_RUNTIME_ONLY_ATTRS = {'figure'} +_AXES_RUNTIME_ONLY_ATTRS = {"figure"} # Axes attributes that ARE serialized, mapped to the to_dict()/from_dict() # keys that cover them (some are stored under a different dict key name, # e.g. the leading-underscore visibility/tick-removal flags). _AXES_SERIALIZED_ATTRS = { - 'position', 'xlabel_text', 'ylabel_text', 'y2label_text', 'title_text', - 'xscale', 'yscale', 'y2scale', 'xmin', 'xmax', 'ymin', 'ymax', - 'y2min', 'y2max', 'legend_on', 'legend_pos', - '_show_xlabel', '_show_ylabel', '_show_xticks', '_show_yticks', - '_remove_last_xtick', '_remove_last_ytick', - '_remove_first_xtick', '_remove_first_ytick', - 'lines', 'scatters', 'bars', 'fills', 'errorbars', 'file_series', 'texts', - 'passthrough', + "position", + "xlabel_text", + "ylabel_text", + "y2label_text", + "title_text", + "xscale", + "yscale", + "y2scale", + "xmin", + "xmax", + "ymin", + "ymax", + "y2min", + "y2max", + "legend_on", + "legend_pos", + "_show_xlabel", + "_show_ylabel", + "_show_xticks", + "_show_yticks", + "_remove_last_xtick", + "_remove_last_ytick", + "_remove_first_xtick", + "_remove_first_ytick", + "lines", + "scatters", + "bars", + "fills", + "errorbars", + "file_series", + "texts", + "passthrough", } # Figure attributes that are intentionally NOT part of the serialized @@ -294,16 +326,27 @@ def test_global_data_counter_takes_max_with_in_process_value(monkeypatch): # machines/processes. # - `_current_axes`: a derived pointer into axes_list, recomputed by # from_dict as `axes_list[-1]` (or None), not independent state. -_FIGURE_RUNTIME_ONLY_ATTRS = {'compiler', '_current_axes'} +_FIGURE_RUNTIME_ONLY_ATTRS = {"compiler", "_current_axes"} # Figure attributes that ARE serialized (style/graph/marker_config go into # the 'config' sub-dict; axes_list is serialized element-by-element via # Axes.to_dict/from_dict, not stored verbatim). _FIGURE_SERIALIZED_ATTRS = { - 'figsize', 'dpi', 'sharex', 'sharey', 'data_prefix', - '_local_data_counter', '_used_data_files', '_subplot_adjust', - 'style', 'graph', 'marker_config', 'axes_list', - 'passthrough_header', 'passthrough_trailer', 'metadata_extra', + "figsize", + "dpi", + "sharex", + "sharey", + "data_prefix", + "_local_data_counter", + "_used_data_files", + "_subplot_adjust", + "style", + "graph", + "marker_config", + "axes_list", + "passthrough_header", + "passthrough_trailer", + "metadata_extra", } From de09b7f699deea178a3d67850da8be727f18a9fc Mon Sep 17 00:00:00 2001 From: Ben Huddart Date: Thu, 23 Jul 2026 12:01:12 +0100 Subject: [PATCH 2/4] feat(plotting): contour and heatmap plots with colormap, fitz gridding and colorbars Add matplotlib-style 2-D plotting backed by GLE's native surface tooling: - imshow(Z): gridded heatmaps via the GLE colormap command and generated .z sidecar files; extent/origin/vmin/vmax/interpolation supported; renders correctly on log axes - contour(x, y, Z): contour lines via begin-contour blocks with explicit or automatic levels and optional inline value labels (clabel) - tripcolor/tricontour(x, y, z): scattered-data variants (e.g. magnetic susceptibility measurements) gridded at GLE compile time by begin-fitz Akima interpolation - no scipy dependency - Figure.colorbar(): self-contained colorbar subroutine with automatic right-margin reservation so single-axes figures no longer clip it - eight palettes (viridis default, magma, inferno, plasma, cividis, coolwarm, gray, rainbow) emitted as self-contained GLE subroutines transcribed from gle-library sources; values clamped to [0,1] so fitz overshoot saturates instead of speckling - mathtext_to_gle(): matplotlib $...$ math in labels is translated to GLE's TeX-like markup at store time (Greek letters, sub/superscripts) - full parser/recognizer round-trip: emitted GLE re-parses into the object model with byte-identical writer-recognizer-writer fixed point; foreign hand-written colormap/fitz/contour GLE is preserved verbatim - full GUI authoring: heatmap/contour series panels (palette, z-range, levels editor, colorbar controls) and scattered x/y/z creation flow in the data manager - guide (docs/guides/CONTOUR_AND_HEATMAPS.md) with an antiferromagnet field-temperature phase diagram worked example, plus runnable examples Co-Authored-By: Claude Fable 5 --- .gitignore | 11 + docs/DOCUMENTATION_INDEX.md | 15 + docs/guides/CONTOUR_AND_HEATMAPS.md | 228 ++ docs/guides/GUI_EDITOR.md | 15 +- docs/guides/README.md | 13 + docs/source/api.rst | 12 + docs/source/examples.rst | 37 + examples/advanced/__init__.py | 46 +- examples/advanced/data_0.dat | 201 ++ examples/advanced/data_1.dat | 201 ++ examples/advanced/data_2.dat | 201 ++ examples/advanced/data_points1.dat | 3200 +++++++++++++++++ examples/advanced/data_points2.dat | 3200 +++++++++++++++++ examples/advanced/example_phase_diagram.gle | 180 + examples/advanced/phase_diagram.py | 147 + examples/basic/__init__.py | 18 +- examples/basic/data_contour1.z | 101 + examples/basic/data_heatmap1.z | 101 + examples/basic/example_heatmap_imshow.gle | 131 + examples/basic/heatmap_imshow.py | 62 + examples/run_all.py | 23 +- src/gleplot/__init__.py | 32 + src/gleplot/axes.py | 515 ++- src/gleplot/config.py | 11 + src/gleplot/figure.py | 391 +- src/gleplot/gui/data/panel.py | 106 +- src/gleplot/gui/panels/axes_panel.py | 10 +- src/gleplot/gui/panels/series_panel.py | 435 ++- src/gleplot/mathtext.py | 514 +++ src/gleplot/palettes.py | 437 +++ src/gleplot/parser/recognizer.py | 873 ++++- src/gleplot/parser/tables.py | 26 + src/gleplot/writer.py | 216 ++ tests/gui/test_data_panel.py | 119 + tests/gui/test_panels.py | 231 ++ tests/integration/test_contour_compilation.py | 104 + tests/parser/_golden_battery.py | 64 + tests/parser/test_fixed_point.py | 8 +- tests/parser/test_recognizer.py | 30 + tests/parser/test_recognizer_contour.py | 299 ++ tests/unit/test_contour_heatmap.py | 414 +++ tests/unit/test_mathtext.py | 225 ++ tests/unit/test_serialization.py | 73 + 43 files changed, 13204 insertions(+), 72 deletions(-) create mode 100644 docs/guides/CONTOUR_AND_HEATMAPS.md create mode 100644 examples/advanced/data_0.dat create mode 100644 examples/advanced/data_1.dat create mode 100644 examples/advanced/data_2.dat create mode 100644 examples/advanced/data_points1.dat create mode 100644 examples/advanced/data_points2.dat create mode 100644 examples/advanced/example_phase_diagram.gle create mode 100644 examples/advanced/phase_diagram.py create mode 100644 examples/basic/data_contour1.z create mode 100644 examples/basic/data_heatmap1.z create mode 100644 examples/basic/example_heatmap_imshow.gle create mode 100644 examples/basic/heatmap_imshow.py create mode 100644 src/gleplot/mathtext.py create mode 100644 src/gleplot/palettes.py create mode 100644 tests/integration/test_contour_compilation.py create mode 100644 tests/parser/test_recognizer_contour.py create mode 100644 tests/unit/test_contour_heatmap.py create mode 100644 tests/unit/test_mathtext.py diff --git a/.gitignore b/.gitignore index 68371ab..2c6ccfe 100644 --- a/.gitignore +++ b/.gitignore @@ -149,6 +149,17 @@ test_custom_prefix_output/ # reached the repository). !examples/**/*.gle !examples/**/data_*.dat +# ... the imshow/grid-contour examples ship gleplot-written .z grid sidecars. +!examples/**/*.z +# ... but GLE's own build products are NOT source and must stay untracked even +# though they match the negations above (a later rule wins): +# - contour derivatives -cdata.dat / -clabels.dat / -cvalues.dat +# - fitz-gridded grids _points.z (GLE generates these at +# compile time from the tracked _points.dat triples) +examples/**/*-cdata.dat +examples/**/*-clabels.dat +examples/**/*-cvalues.dat +examples/**/*_points*.z examples/outputs/ tests/output/ docs/source/_static/gallery/ diff --git a/docs/DOCUMENTATION_INDEX.md b/docs/DOCUMENTATION_INDEX.md index aadd187..a8e3335 100644 --- a/docs/DOCUMENTATION_INDEX.md +++ b/docs/DOCUMENTATION_INDEX.md @@ -90,6 +90,18 @@ Reference for accepted color and marker mappings. - Marker symbol mappings - Native GLE marker names +### [CONTOUR_AND_HEATMAPS.md](guides/CONTOUR_AND_HEATMAPS.md) +Guide for heatmaps and contour plots. + +**Contains:** +- `imshow()`/`contour()` (gridded) and `tripcolor()`/`tricontour()` (scattered) usage +- `origin` semantics and the matplotlib default difference +- `vmin`/`vmax`, palette gallery, and one-heatmap-per-axes limitation +- Sidecar and GLE-generated file layout +- Greek letters and math in labels (matplotlib `$...$` mathtext and direct GLE markup) +- Worked antiferromagnet phase-diagram example +- Known colorbar-clipping cosmetic issue and workaround + ### [MATPLOTLIB_MIGRATION.md](guides/MATPLOTLIB_MIGRATION.md) Quick migration guide for porting matplotlib code to gleplot. @@ -223,6 +235,9 @@ Script to run and compile all basic examples. - `batch_figures.py` - Generating many figures programmatically - `line_from_file.py` - Overlay model lines from existing data files - `data_prefix.py` - Deterministic sidecar data file naming +- `phase_diagram.py` - Susceptibility phase diagram (`tripcolor` + `tricontour` + colorbar) + +Basic examples (`examples/basic/`) also include `heatmap_imshow.py` - gridded `imshow` + `contour` + colorbar. ## File Structure diff --git a/docs/guides/CONTOUR_AND_HEATMAPS.md b/docs/guides/CONTOUR_AND_HEATMAPS.md new file mode 100644 index 0000000..34a94fb --- /dev/null +++ b/docs/guides/CONTOUR_AND_HEATMAPS.md @@ -0,0 +1,228 @@ +# Contour Plots and Heatmaps + +gleplot can render 2-D scalar fields as heatmaps (matplotlib's `imshow`/`tripcolor`) and contour lines (`contour`/`tricontour`), with an optional colorbar. Under the hood these compile to GLE `colormap` / `begin contour` / `begin fitz` constructs, with self-contained colour-palette subroutines written directly into the `.gle` script — no `gle-library` include is required to compile the output. + +## Supported Methods + +- `Axes.imshow(Z, extent=None, origin='lower', cmap=None, vmin=None, vmax=None, interpolation='bicubic', pixels=None, invert=False, label=None)` — heatmap of a **gridded** 2-D array. +- `Axes.contour(*args, levels=None, colors='black', linewidths=1.0, linestyles='-', clabel=False, clabel_fmt='fix 1', label=None)` — contour lines of **gridded** data. Call as `contour(Z)` or `contour(x, y, Z)`. +- `Axes.tripcolor(x, y, z, gridsize=(50, 50), extent=None, **imshow_kwargs)` — heatmap of **scattered** `(x, y, z)` samples, gridded by GLE at compile time. +- `Axes.tricontour(x, y, z, gridsize=(50, 50), extent=None, ncontour=3, **contour_kwargs)` — contour lines of **scattered** `(x, y, z)` samples. +- `Figure.colorbar(label=None, format='fix 1', nticks=None, width=0.5, sep=0.3)` — attaches a vertical colorbar to the figure's single heatmap-bearing axes. + +Each has a module-level convenience wrapper too: `glp.imshow(...)`, `glp.contour(...)`, `glp.tripcolor(...)`, `glp.tricontour(...)`, `glp.colorbar(...)` (they operate on `gca()`/`gcf()`, matching `glp.plot()` and friends). + +There is no `pcolormesh` alias and no 3-D surface support — gleplot only targets 2-D colormap/contour output. + +## Minimal Example: Gridded Heatmap + Contour + +```python +import numpy as np +import gleplot as glp + +x = np.linspace(-3, 3, 120) +y = np.linspace(-2.5, 2.5, 100) +X, Y = np.meshgrid(x, y) +Z = np.exp(-(X**2 + Y**2) / 2.0) + +fig = glp.figure(figsize=(8, 6)) +ax = fig.add_subplot(111) + +ax.imshow(Z, extent=(x[0], x[-1], y[0], y[-1]), cmap='viridis') +ax.contour(x, y, Z, levels=6, colors='white', linewidths=0.7) + +ax.set_xlabel('x') +ax.set_ylabel('y') +fig.colorbar(label='amplitude') + +fig.savefig('gaussian.pdf') +``` + +`contour`'s `levels` argument accepts: + +- `None` (default) — GLE's own default of 10 automatic levels. +- An `int` `n` — resolved **at call time** into `n` explicit level values evenly spaced strictly between the data's min and max, and emitted as an explicit `values v1 v2 ...` list (not GLE's `values from a to b step s` form). This keeps the stored model self-contained and round-trip-safe, but means the levels are fixed to the data range at the moment you call `contour`/`tricontour`, not re-derived later. +- An explicit sequence, e.g. `levels=[0.2, 0.5, 0.8]` — stored verbatim. + +Add `clabel=True` to draw inline value labels at each contour crossing (formatted with `clabel_fmt`, a GLE `format$` string such as `'fix 1'` or `'fix 2'`): + +```python +ax.contour(x, y, Z, levels=[0.2, 0.5, 0.8], colors='black', clabel=True, clabel_fmt='fix 2') +``` + +## `origin` — Read This Before You Get Flipped Data + +`imshow`'s default is **`origin='lower'`**, which puts row 0 of `Z` at `ymin` — this **deviates from matplotlib**, whose `imshow` default is `origin='upper'` (row 0 at the top). gleplot chose `'lower'` because it matches the scientific/phase-diagram convention this feature was built for, and because GLE's own `.z` grid file format is y-increasing (row 0 = `ymin`) — `'lower'` is the identity mapping with no row-flipping needed. + +If your `Z` array was built the way most image libraries build one (row 0 = top of the image), pass `origin='upper'` explicitly; gleplot flips the rows when writing the `.z` sidecar so the displayed orientation matches. + +```python +ax.imshow(image_array, origin='upper') # row 0 is the top row, like a typical image +ax.imshow(field_grid, origin='lower') # row 0 is ymin, like np.meshgrid's row-0-is-y[0] layout (the default) +``` + +`tripcolor`/`tricontour` don't take an `origin` argument — they always store `origin='lower'`, since GLE's `fitz` gridding is inherently y-increasing. + +## `vmin` / `vmax` (colour normalization) + +`vmin`/`vmax` map to GLE's `zmin`/`zmax` colormap clauses. Leave them `None` to let GLE auto-scale to the data's own range (the `.z` file's or the scattered points' min/max, whichever applies). + +```python +ax.imshow(Z, cmap='magma', vmin=-1, vmax=1) +``` + +**For scattered data (`tripcolor`), prefer setting `vmin`/`vmax` explicitly** rather than leaving them `None`. GLE's `fitz` step performs Akima network interpolation from your scattered samples, which can overshoot beyond your data's actual value range in sparsely-sampled regions — and if `vmax` is left `None`, GLE auto-scales the colormap from the **gridded** `.z` file's own range, so a single wild overshoot node can compress your entire real data range into a sliver at one end of the colour scale. Pinning `vmin`/`vmax` to the range you care about keeps the colour mapping stable and readable. + +gleplot's generated palette subroutines **clamp** the normalized value into `[0, 1]`, so a grid node that overshoots above `vmax` (or dips below `vmin`) saturates at the palette's brightest (or darkest) end colour — the same "over"/"under" → end-colour behaviour matplotlib's default colormaps use — rather than producing stray speckles. A tight `vmax` set just above your physical peak is therefore safe. See `examples/advanced/phase_diagram.py` for a worked example. + +## Palette Gallery + +| `cmap` name | Notes | +|---|---| +| `viridis` | Default (`GLEGraphConfig.default_cmap`). Perceptually uniform. | +| `magma` | Perceptually uniform, dark-to-light. | +| `inferno` | Perceptually uniform, dark-to-light. | +| `plasma` | Perceptually uniform, dark-to-light. | +| `cividis` | Perceptually uniform, colourblind-safe. | +| `coolwarm` | Diverging blue -> white -> red. | +| `gray` | GLE's built-in grayscale; emits no palette clause. | +| `rainbow` (alias `jet`) | GLE's built-in rainbow; emits `colormap ... color` instead of a `palette` clause. | + +Unknown names raise `ValueError` listing the supported set. `viridis`/`magma`/`inferno`/`plasma`/`cividis` are transcribed from `gle-library`'s `include/palettes.gle` RGB stop tables; `coolwarm` from `include/color.gle`'s `palette_blue_white_red`. All are emitted as self-contained `sub gleplot_ z ... end sub` subroutines directly in the `.gle` script (deduplicated — only palettes actually used are written), so the output compiles without the `gle-library` on the include path. + +## One Heatmap Per Axes + +GLE supports at most one `colormap` per graph. Calling `imshow`/`tripcolor` a second time on the same `Axes` raises `ValueError`: + +```python +ax.imshow(Z1) +ax.imshow(Z2) # ValueError: GLE supports at most one heatmap (colormap) per axes +``` + +Contour lines have no such limit — you can layer multiple `contour`/`tricontour` calls (e.g. one heatmap plus several contour overlays at different levels) on the same axes. + +## Data Requirements + +GLE's colormap/contour grid has no missing-value support and requires ascending axis ranges, so gleplot validates these at call time and raises a clear `ValueError` rather than emitting a `.gle` that breaks at compile: + +- **Finite values only.** A `NaN` or infinity anywhere in `Z` (or in scattered `x`/`y`/`z`) is rejected — GLE's `.z` reader has no transparent/masked pixel (unlike matplotlib's `imshow`, which renders `NaN` transparent). Mask or fill missing cells before plotting. +- **Ascending `extent`.** `extent` must have `xmin < xmax` and `ymin < ymax`; a reversed or degenerate extent is rejected (gleplot cannot express an axis flipped purely via `extent`). +- **In-range contour levels.** Explicit `contour` levels that all fall outside the grid's data range are rejected (no lines would be drawn, and GLE aborts on the resulting empty polyline file). A partially in-range level set is fine — the out-of-range ones are simply not drawn. (For `tricontour` the grid is interpolated at compile time, so its exact range isn't known in advance and this check isn't applied.) + +A colorbar over a **constant** field (`zmax == zmin`) is handled gracefully — the generated colorbar subroutine expands the degenerate range to a nominal unit span so the bar still renders instead of dividing by zero. + +## What Files gleplot Writes + +- `imshow`/gridded `contour` write a `.z` sidecar directly: a text header `! nx ny xmin xmax ymin ymax ` followed by `ny` rows of `nx` values. +- `tripcolor`/`tricontour` write a points sidecar (`_points.dat`): raw whitespace-separated `x y z` triples, one per line, no header. +- These sidecars are **not** columnar data in the usual gleplot sense — they're excluded from the `! gleplot: import-data = ...` metadata comment (which only lists file-backed series you could re-import as columns). + +At **GLE compile time** (not written by gleplot), GLE itself generates further files next to your `.gle` script: + +- `begin fitz` grids a points sidecar into `.z`. +- `begin contour` on a `.z` file produces `-cdata.dat` (the drawn polylines), and, when `clabel=True`, `-clabels.dat` (label positions/values) and `-cvalues.dat`. + +These generated files only exist after compilation; `savefig()` (or the GLE binary itself) tolerates their absence beforehand, and you never need to create or manage them yourself. + +## Worked Example: Antiferromagnet Phase Diagram + +The canonical use case this feature was built for: plotting a magnetic-field/temperature phase diagram from noisy, scattered susceptibility measurements — the kind you'd actually get from an experiment (sweeping temperature at each of several fixed applied fields), not from a clean grid. + +The synthetic data models a susceptibility `chi(T, H)` that peaks along a Néel transition boundary: + +``` +T_N(H) = T_N0 * sqrt(1 - (H / Hc)^2) +``` + +with the peak broadening as `H` approaches the critical field `Hc` (critical broadening). + +```python +import numpy as np +import gleplot as glp + +rng = np.random.default_rng(11) +T_N0, Hc = 30.0, 9.5 +chi0, amplitude = 0.05, 1.0 + +n_points = 3200 +H = rng.uniform(0.0, 0.92 * Hc, n_points) +T = rng.uniform(1.5, 1.15 * T_N0, n_points) + +T_N = T_N0 * np.sqrt(1.0 - (H / Hc) ** 2) +width = 2.0 + 1.4 * (H / Hc) +chi = chi0 + amplitude * np.exp(-((T - T_N) ** 2) / (2.0 * width**2)) +chi += rng.normal(0.0, 0.015 * amplitude, n_points) + +fig = glp.figure(figsize=(9, 6.5)) +ax = fig.add_subplot(111) + +ax.tripcolor(T, H, chi, gridsize=(90, 70), cmap='magma', vmin=0.0, vmax=chi0 + 2.0 * amplitude) +ax.tricontour( + T, H, chi, gridsize=(90, 70), + levels=[chi0 + 0.55 * amplitude], + colors='black', linewidths=1.3, + clabel=True, clabel_fmt='fix 2', +) + +ax.set_xlabel('Temperature (K)') +ax.set_ylabel('Magnetic field (T)') +fig.colorbar(label=r'\chi{} (emu mol^{-1})', format='fix 2') + +fig.savefig('phase_diagram.pdf') +fig.savefig('phase_diagram.png') +``` + +(See [Greek letters and math in labels](#greek-letters-and-math-in-labels) below for the `\chi{}` label syntax.) + +The full runnable version — `examples/advanced/phase_diagram.py` — adds a second panel of susceptibility line-cuts at representative fields, showing the peak shift and broadening as `H → Hc`. Run it (and the simpler `examples/basic/heatmap_imshow.py`) via: + +```bash +cd examples +python advanced/phase_diagram.py +python basic/heatmap_imshow.py +``` + +## Greek Letters and Math in Labels + +You can write labels in **either** matplotlib-style `$...$` mathtext **or** GLE's own TeX-like markup — both reach the same result. Every display string entering the model (axis titles, series `label=`, `title`/`text`, `colorbar(label=)`, …) is translated at store time by `gleplot.mathtext_to_gle`, so matplotlib users get Greek/math rendering without learning GLE markup. + +### matplotlib mathtext (`$...$`) + +Anything between unescaped `$` delimiters is treated as math and translated to GLE markup; text outside the delimiters passes through verbatim: + +```python +ax.set_ylabel(r'$\chi_{mol}$ (emu mol$^{-1}$)') # renders: χ_mol (emu mol⁻¹) +ax.set_title(r'Susceptibility $\chi$ vs $T$') # renders: Susceptibility χ vs T +ax.plot(x, y, label=r'$\alpha$ decay') # legend key: α decay +``` + +Translation rules: + +- Greek letters / symbols GLE supports natively pass through: `\chi`, `\alpha`, `\Omega`, `\times`, `\pm`, `\cdot`, `\infty`, `\degree`, … +- Single-token sub/superscripts are braced automatically: `$x^2$` → `x^{2}`, `$x_i$` → `x_{i}`; already-braced forms (`$^{-1}$`) pass through. +- `\mathrm{…}`/`\text{…}` → `{\rm …}`, `\mathit{…}` → `{\it …}`, `\mathbf{…}` → `{\bf …}`, `\mathtt{…}` → `{\tt …}`. Families GLE has no inline font for (`\mathsf`, `\mathcal`, `\mathbb`) degrade to their contents. +- `\frac{a}{b}` degrades to `a/b` (GLE has no inline `\frac`). +- Spacing macros `\,` `\:` `\;` `\!` pass through; unknown macros pass through unchanged (GLE understands more than gleplot enumerates). +- `\$` is a literal dollar sign anywhere (not a delimiter). A string with an **odd/unmatched** `$` count is left completely unchanged (matplotlib would error; gleplot degrades gracefully rather than guess). + +### GLE markup (direct) + +Direct GLE markup still works — a string with no `$` is passed through byte-for-byte: + +- Greek letters and symbols use backslash escapes: `\chi`, `\alpha`, `\Omega`, `\mu`, `\pi`, … +- Subscripts/superscripts use braces: `T_{N}`, `mol^{-1}`, `10^{-3}`. +- Use Python **raw strings** so the backslash reaches GLE intact: `r'\chi'`, not `'\chi'` (which Python would treat as an invalid escape). + +```python +fig.colorbar(label=r'\chi{} (emu mol^{-1})') # renders: χ (emu mol⁻¹) +ax.set_ylabel(r'T_{N} (K)') # renders: T_N (K) +``` + +**The swallowed-space gotcha:** GLE (like TeX) eats the single space immediately after a macro name, so `r'\chi (emu ...)'` renders as `χ(emu ...)` with **no gap**. When a space must follow a macro, terminate it with an empty group `{}` — `r'\chi{} (emu ...)'` — or let a following brace group do it, as in `r'\chi_{mol} (emu ...)'`. Both preserve the space. When translating `$...$` mathtext, gleplot inserts this `{}` for you at math→text boundaries, so `r'$\chi$ (emu/mol)'` becomes `\chi{} (emu/mol)` automatically. + +## See Also + +- `examples/basic/heatmap_imshow.py` — simple gridded `imshow` + `contour` + colorbar. +- `examples/advanced/phase_diagram.py` — the full susceptibility phase-diagram example. +- `docs/guides/COLORS_AND_MARKERS.md` — matplotlib-style color/marker name mapping (contour `colors=` uses the same mapping). +- `docs/guides/FILE_BASED_SERIES.md` — for plotting from pre-existing columnar data files (not applicable to the raw `.z`/points sidecars described here). diff --git a/docs/guides/GUI_EDITOR.md b/docs/guides/GUI_EDITOR.md index 3f035d1..a04bf09 100644 --- a/docs/guides/GUI_EDITOR.md +++ b/docs/guides/GUI_EDITOR.md @@ -86,7 +86,7 @@ With a file selected, the "Add series" form fills its X/Y/Y-error column combos - **X column** / **Y column** -- required. - **Y error** -- optional, only used for the "Error bars" plot type. - **Label** -- defaults to the Y column's name; edit it and the default stops auto-updating. -- **Plot type** -- Line, Scatter, Line+markers, or Error bars. +- **Plot type** -- Line, Scatter, Line+markers, Error bars, or one of the two scattered-field types **Heatmap (scattered x,y,z)** and **Contour (scattered x,y,z)** (see [Heatmaps and contours](#heatmaps-and-contours) below). - **Mode** -- **Import data** copies the selected columns into the figure (they become `.dat` sidecars on export); **Reference file** instead points the GLE script at the original file by column index (via `Axes.line_from_file` / `Axes.errorbar_from_file`), so no data is duplicated. Note: reference mode's "Scatter" plot type currently falls back to a plain line -- markers on a referenced series aren't supported yet; use Import mode for a true scatter. Click **Add series**. It's added to the figure's current axes and the live preview recompiles. @@ -97,6 +97,19 @@ Switch to the **Properties** dock's **Series** tab, which lists every series on The **Axes** tab covers axis labels (title, X/Y/Y2), limits (leave a limit blank for "auto"), scale (linear/log per axis, including a secondary Y2 axis), and legend (on/off plus one of five placements). The legend checkbox reflects the *effective* state: with it left on auto, a legend appears automatically once any series has a label. The **Figure** tab covers figure-level settings: width and height (inches) and DPI. The **Texts** tab lists and edits any free-form text annotations on the current axes -- see [Working with annotations](#working-with-annotations) for the full walkthrough, including adding and dragging annotations directly on the preview. +### Heatmaps and contours + +The editor can draw scalar-field data as a colour map (heatmap) and/or contour lines. There are two ways in: creating one from scattered `(x, y, z)` samples in the **Data** dock, and styling it in the **Series** tab. (Gridded `imshow`/`contour` figures built in code, or opened from a `.gle`, are edited from the Series tab the same way.) + +**Creating from scattered data.** Load a data file with at least three numeric columns, then pick **Heatmap (scattered x,y,z)** or **Contour (scattered x,y,z)** as the plot type. The form swaps its **Y error** row for a **Z column** picker; choose the X, Y, and Z columns and click **Add series**. gleplot writes the raw `x y z` triples to a sidecar and lets GLE grid them (via `fitz`) at compile time -- so these types are **Import mode only** (the Mode combo is disabled while one is selected; there is no reference-file equivalent, since `fitz` needs gleplot's own triples file). GLE allows **at most one heatmap per axes**: if the current axes already has one, adding another is refused with an explanatory dialog rather than replacing it -- remove the existing heatmap first (Series tab) if you want a different one. A contour on the same axes as a heatmap is fine (they are independent layers). + +**Styling in the Series tab.** Heatmap and contour series appear in the Series list like any other. Selecting one shows its dedicated controls: + +- **Heatmap** -- **Palette** (viridis, magma, inferno, plasma, cividis, coolwarm, rainbow/jet, or gray), **Colour min** / **Colour max** (leave blank for GLE's automatic data range), **Pixels** (the colormap's bitmap resolution), **Interpolation** (bicubic or nearest), **Invert colours**, and a **Show colorbar** toggle. Turning the colorbar on reveals its **Colorbar label** and **Colorbar format** fields (the format is a GLE `format$` string such as `fix 1`); the tick range is derived from the heatmap's colour range. A heatmap has no single line colour, so the shared **Color** control is disabled for it. +- **Contour** -- a **Levels** field, plus a **Label contours** toggle with a **Label format** field. Contours reuse the shared **Color** and **Line width** controls for their line styling. The Levels field accepts an explicit space- or comma-separated list (`0.1 0.2 0.3`), the shorthand `n=10` for ten evenly-spaced automatic levels, or blank for GLE's default set. Enabling **Label contours** draws the level value inline along each contour, formatted with the label format. + +All of these edits go through the normal document flow, so the live preview recompiles and **Undo/redo** restores them like any other change. + ### 4. Arrange subplots The **Layout** tab shows the current subplot grid (rows x cols) and a list of grid slots. To add more panels: increase Rows/Cols and click **Apply grid**, then select an empty slot and click **Add axes here**. Selecting an existing (populated) slot retargets the Axes/Series tabs onto that subplot without marking the document dirty (it's a view change only). diff --git a/docs/guides/README.md b/docs/guides/README.md index 7a997fa..5ffa981 100644 --- a/docs/guides/README.md +++ b/docs/guides/README.md @@ -44,6 +44,19 @@ Guide for plotting directly from existing data files. - 1-based column indexing - Data format and workflow notes +### [CONTOUR_AND_HEATMAPS.md](CONTOUR_AND_HEATMAPS.md) +Guide for heatmaps and contour plots (`imshow`, `contour`, `tripcolor`, `tricontour`, `colorbar`). + +**Contents:** +- Gridded vs. scattered-data APIs +- `origin` semantics (default differs from matplotlib) +- `vmin`/`vmax` and Akima-overshoot pitfalls with scattered data +- Palette gallery +- One-heatmap-per-axes limitation and sidecar/generated-file layout +- Greek letters and math in labels — matplotlib `$...$` mathtext **and** direct GLE markup +- Worked antiferromagnet phase-diagram example +- Known colorbar-clipping issue on a lone axes and its workaround + ### [COLORS_AND_MARKERS.md](COLORS_AND_MARKERS.md) Reference for accepted color and marker mappings. diff --git a/docs/source/api.rst b/docs/source/api.rst index 03185f9..65013ad 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -79,6 +79,16 @@ Functions .. autofunction:: gleplot.errorbar +.. autofunction:: gleplot.imshow + +.. autofunction:: gleplot.contour + +.. autofunction:: gleplot.tripcolor + +.. autofunction:: gleplot.tricontour + +.. autofunction:: gleplot.colorbar + .. autofunction:: gleplot.text .. autofunction:: gleplot.xlabel @@ -106,6 +116,8 @@ Utilities .. autofunction:: gleplot.get_gle_marker +.. autofunction:: gleplot.mathtext_to_gle + Compiler -------- diff --git a/docs/source/examples.rst b/docs/source/examples.rst index 7cd0943..6364cf1 100644 --- a/docs/source/examples.rst +++ b/docs/source/examples.rst @@ -271,6 +271,39 @@ Both vertical and horizontal error bars: ax.errorbar(x, y, yerr=yerr_array, xerr=xerr_array, marker='o', fmt='none', color='blue', capsize=0.1) +Heatmaps and Contour Plots +--------------------------- + +See the `Contour Plots and Heatmaps guide +`_ +for the full guide. Minimal gridded example: + +.. code-block:: python + + import numpy as np + import gleplot as glp + + x = np.linspace(-3, 3, 120) + y = np.linspace(-2.5, 2.5, 100) + X, Y = np.meshgrid(x, y) + Z = np.exp(-(X**2 + Y**2) / 2.0) + + fig = glp.figure(figsize=(8, 6)) + ax = fig.add_subplot(111) + + ax.imshow(Z, extent=(x[0], x[-1], y[0], y[-1]), cmap='viridis') + ax.contour(x, y, Z, levels=6, colors='white', linewidths=0.7) + + ax.set_xlabel('x') + ax.set_ylabel('y') + fig.colorbar(label='amplitude') + + fig.savefig('example_heatmap_imshow.pdf') + +``examples/advanced/phase_diagram.py`` builds a full antiferromagnet H-T +phase diagram from synthetic scattered susceptibility data using +``tripcolor`` + ``tricontour`` + ``colorbar``, as described in the guide. + Additional Advanced Example Scripts ----------------------------------- @@ -282,6 +315,10 @@ for focused workflows: - ``batch_figures.py`` - loop-based generation of many figures - ``line_from_file.py`` - model overlays from existing data files - ``data_prefix.py`` - deterministic sidecar file naming patterns +- ``phase_diagram.py`` - susceptibility phase diagram (``tripcolor`` + ``tricontour`` + colorbar) + +The ``examples/basic`` directory also includes ``heatmap_imshow.py`` - a +simple gridded ``imshow`` + ``contour`` + colorbar example. Run the full suite from the repository root: diff --git a/examples/advanced/__init__.py b/examples/advanced/__init__.py index 1f10f48..b315244 100644 --- a/examples/advanced/__init__.py +++ b/examples/advanced/__init__.py @@ -27,28 +27,30 @@ from .batch_figures import example_batch_figures from .line_from_file import example_line_from_file from .data_prefix import example_data_prefix +from .phase_diagram import example_phase_diagram __all__ = [ - "example_fill_between", - "example_fill_between_conditional", - "example_log_scale", - "example_combined_plot", - "example_multiple_styles", - "example_sharex_stacked", - "example_sharey_sidebyside", - "example_both_shared", - "example_residual_plot", - "example_comparison_with_without", - "example_side_by_side", - "example_stacked", - "example_2x2_grid", - "example_1x3_comparison", - "example_errorbar_from_file", - "example_dual_axis_from_file", - "example_line_overlay_from_file", - "example_text_annotations", - "example_per_element_styling", - "example_batch_figures", - "example_line_from_file", - "example_data_prefix", + 'example_fill_between', + 'example_fill_between_conditional', + 'example_log_scale', + 'example_combined_plot', + 'example_multiple_styles', + 'example_sharex_stacked', + 'example_sharey_sidebyside', + 'example_both_shared', + 'example_residual_plot', + 'example_comparison_with_without', + 'example_side_by_side', + 'example_stacked', + 'example_2x2_grid', + 'example_1x3_comparison', + 'example_errorbar_from_file', + 'example_dual_axis_from_file', + 'example_line_overlay_from_file', + 'example_text_annotations', + 'example_per_element_styling', + 'example_batch_figures', + 'example_line_from_file', + 'example_data_prefix', + 'example_phase_diagram', ] diff --git a/examples/advanced/data_0.dat b/examples/advanced/data_0.dat new file mode 100644 index 0000000..5269133 --- /dev/null +++ b/examples/advanced/data_0.dat @@ -0,0 +1,201 @@ +x h_0_t +1.5 0.05 +1.66583 0.05 +1.83166 0.05 +1.99749 0.05 +2.16332 0.05 +2.32915 0.05 +2.49497 0.05 +2.6608 0.05 +2.82663 0.05 +2.99246 0.05 +3.15829 0.05 +3.32412 0.05 +3.48995 0.05 +3.65578 0.05 +3.82161 0.05 +3.98744 0.05 +4.15327 0.05 +4.3191 0.05 +4.48492 0.05 +4.65075 0.05 +4.81658 0.05 +4.98241 0.05 +5.14824 0.05 +5.31407 0.05 +5.4799 0.05 +5.64573 0.05 +5.81156 0.05 +5.97739 0.05 +6.14322 0.05 +6.30905 0.05 +6.47487 0.05 +6.6407 0.05 +6.80653 0.05 +6.97236 0.05 +7.13819 0.05 +7.30402 0.05 +7.46985 0.05 +7.63568 0.05 +7.80151 0.05 +7.96734 0.05 +8.13317 0.05 +8.29899 0.05 +8.46482 0.05 +8.63065 0.05 +8.79648 0.05 +8.96231 0.05 +9.12814 0.05 +9.29397 0.05 +9.4598 0.05 +9.62563 0.05 +9.79146 0.05 +9.95729 0.05 +10.1231 0.05 +10.2889 0.05 +10.4548 0.05 +10.6206 0.05 +10.7864 0.05 +10.9523 0.05 +11.1181 0.05 +11.2839 0.05 +11.4497 0.05 +11.6156 0.05 +11.7814 0.05 +11.9472 0.05 +12.1131 0.05 +12.2789 0.05 +12.4447 0.05 +12.6106 0.05 +12.7764 0.05 +12.9422 0.05 +13.108 0.05 +13.2739 0.05 +13.4397 0.05 +13.6055 0.05 +13.7714 0.05 +13.9372 0.05 +14.103 0.05 +14.2688 0.05 +14.4347 0.05 +14.6005 0.05 +14.7663 0.05 +14.9322 0.05 +15.098 0.05 +15.2638 0.05 +15.4296 0.05 +15.5955 0.05 +15.7613 0.05 +15.9271 0.05 +16.093 0.05 +16.2588 0.05 +16.4246 0.05 +16.5905 0.05 +16.7563 0.05 +16.9221 0.05 +17.0879 0.05 +17.2538 0.05 +17.4196 0.05 +17.5854 0.05 +17.7513 0.05 +17.9171 0.05 +18.0829 0.05 +18.2487 0.05 +18.4146 0.0500001 +18.5804 0.0500001 +18.7462 0.0500001 +18.9121 0.0500002 +19.0779 0.0500003 +19.2437 0.0500005 +19.4095 0.0500008 +19.5754 0.0500013 +19.7412 0.0500019 +19.907 0.050003 +20.0729 0.0500045 +20.2387 0.0500067 +20.4045 0.05001 +20.5704 0.0500149 +20.7362 0.0500219 +20.902 0.0500321 +21.0678 0.0500466 +21.2337 0.0500673 +21.3995 0.0500965 +21.5653 0.0501374 +21.7312 0.0501942 +21.897 0.0502726 +22.0628 0.0503802 +22.2286 0.0505265 +22.3945 0.0507242 +22.5603 0.0509892 +22.7261 0.0513419 +22.892 0.051808 +23.0578 0.0524193 +23.2236 0.053215 +23.3894 0.0542432 +23.5553 0.0555619 +23.7211 0.0572405 +23.8869 0.0593611 +24.0528 0.0620198 +24.2186 0.0653279 +24.3844 0.0694125 +24.5503 0.0744171 +24.7161 0.0805016 +24.8819 0.0878412 +25.0477 0.0966253 +25.2136 0.107055 +25.3794 0.119339 +25.5452 0.133691 +25.7111 0.150321 +25.8769 0.169432 +26.0427 0.191209 +26.2085 0.215813 +26.3744 0.24337 +26.5402 0.273963 +26.706 0.307617 +26.8719 0.344299 +27.0377 0.383901 +27.2035 0.426236 +27.3693 0.471034 +27.5352 0.517938 +27.701 0.566504 +27.8668 0.616205 +28.0327 0.666435 +28.1985 0.716524 +28.3643 0.765746 +28.5302 0.813336 +28.696 0.858513 +28.8618 0.900497 +29.0276 0.938531 +29.1935 0.971906 +29.3593 0.999982 +29.5251 1.02221 +29.691 1.03813 +29.8568 1.04744 +30.0226 1.04994 +30.1884 1.04557 +30.3543 1.03443 +30.5201 1.01675 +30.6859 0.992884 +30.8518 0.963304 +31.0176 0.928591 +31.1834 0.889407 +31.3492 0.846476 +31.5151 0.800563 +31.6809 0.752451 +31.8467 0.702918 +32.0126 0.652721 +32.1784 0.602571 +32.3442 0.553123 +32.5101 0.504961 +32.6759 0.458591 +32.8417 0.414433 +33.0075 0.37282 +33.1734 0.334 +33.3392 0.298136 +33.505 0.265316 +33.6709 0.235556 +33.8367 0.208815 +34.0025 0.184996 +34.1683 0.163963 +34.3342 0.145548 +34.5 0.12956 diff --git a/examples/advanced/data_1.dat b/examples/advanced/data_1.dat new file mode 100644 index 0000000..f93d233 --- /dev/null +++ b/examples/advanced/data_1.dat @@ -0,0 +1,201 @@ +x h_5_t +1.5 0.05 +1.66583 0.05 +1.83166 0.05 +1.99749 0.05 +2.16332 0.05 +2.32915 0.05 +2.49497 0.05 +2.6608 0.05 +2.82663 0.05 +2.99246 0.05 +3.15829 0.05 +3.32412 0.05 +3.48995 0.05 +3.65578 0.05 +3.82161 0.05 +3.98744 0.05 +4.15327 0.05 +4.3191 0.05 +4.48492 0.05 +4.65075 0.05 +4.81658 0.05 +4.98241 0.05 +5.14824 0.05 +5.31407 0.05 +5.4799 0.05 +5.64573 0.05 +5.81156 0.05 +5.97739 0.05 +6.14322 0.05 +6.30905 0.05 +6.47487 0.05 +6.6407 0.05 +6.80653 0.05 +6.97236 0.05 +7.13819 0.05 +7.30402 0.05 +7.46985 0.05 +7.63568 0.05 +7.80151 0.05 +7.96734 0.05 +8.13317 0.05 +8.29899 0.05 +8.46482 0.05 +8.63065 0.05 +8.79648 0.05 +8.96231 0.05 +9.12814 0.05 +9.29397 0.05 +9.4598 0.05 +9.62563 0.05 +9.79146 0.0500001 +9.95729 0.0500001 +10.1231 0.0500001 +10.2889 0.0500002 +10.4548 0.0500003 +10.6206 0.0500004 +10.7864 0.0500005 +10.9523 0.0500007 +11.1181 0.050001 +11.2839 0.0500014 +11.4497 0.0500019 +11.6156 0.0500025 +11.7814 0.0500034 +11.9472 0.0500047 +12.1131 0.0500063 +12.2789 0.0500084 +12.4447 0.0500113 +12.6106 0.050015 +12.7764 0.05002 +12.9422 0.0500264 +13.108 0.0500348 +13.2739 0.0500458 +13.4397 0.0500599 +13.6055 0.0500781 +13.7714 0.0501014 +13.9372 0.0501313 +14.103 0.0501693 +14.2688 0.0502175 +14.4347 0.0502785 +14.6005 0.0503552 +14.7663 0.0504514 +14.9322 0.0505715 +15.098 0.050721 +15.2638 0.0509062 +15.4296 0.0511349 +15.5955 0.051416 +15.7613 0.0517603 +15.9271 0.0521803 +16.093 0.0526905 +16.2588 0.053308 +16.4246 0.0540524 +16.5905 0.054946 +16.7563 0.0560146 +16.9221 0.0572872 +17.0879 0.0587968 +17.2538 0.0605801 +17.4196 0.0626784 +17.5854 0.0651371 +17.7513 0.0680064 +17.9171 0.0713411 +18.0829 0.0752006 +18.2487 0.0796491 +18.4146 0.0847551 +18.5804 0.0905911 +18.7462 0.0972333 +18.9121 0.104761 +19.0779 0.113256 +19.2437 0.1228 +19.4095 0.133478 +19.5754 0.145372 +19.7412 0.15856 +19.907 0.17312 +20.0729 0.18912 +20.2387 0.206623 +20.4045 0.225683 +20.5704 0.24634 +20.7362 0.268622 +20.902 0.29254 +21.0678 0.318089 +21.2337 0.345243 +21.3995 0.373956 +21.5653 0.404159 +21.7312 0.435759 +21.897 0.468639 +22.0628 0.502656 +22.2286 0.537644 +22.3945 0.573411 +22.5603 0.609743 +22.7261 0.646403 +22.892 0.683135 +23.0578 0.719667 +23.2236 0.755711 +23.3894 0.790969 +23.5553 0.825138 +23.7211 0.857911 +23.8869 0.888984 +24.0528 0.91806 +24.2186 0.944851 +24.3844 0.969089 +24.5503 0.990524 +24.7161 1.00893 +24.8819 1.02412 +25.0477 1.03592 +25.2136 1.0442 +25.3794 1.04888 +25.5452 1.04991 +25.7111 1.04727 +25.8769 1.04099 +26.0427 1.03114 +26.2085 1.01783 +26.3744 1.0012 +26.5402 0.981436 +26.706 0.958736 +26.8719 0.93334 +27.0377 0.905507 +27.2035 0.875516 +27.3693 0.843656 +27.5352 0.81023 +27.701 0.775543 +27.8668 0.739901 +28.0327 0.703606 +28.1985 0.666951 +28.3643 0.630218 +28.5302 0.593672 +28.696 0.557562 +28.8618 0.522113 +29.0276 0.487531 +29.1935 0.453996 +29.3593 0.421664 +29.5251 0.390667 +29.691 0.361111 +29.8568 0.333077 +30.0226 0.306626 +30.1884 0.281794 +30.3543 0.258598 +30.5201 0.237035 +30.6859 0.217086 +30.8518 0.198719 +31.0176 0.181885 +31.1834 0.166528 +31.3492 0.152582 +31.5151 0.139974 +31.6809 0.128626 +31.8467 0.118458 +32.0126 0.109387 +32.1784 0.101328 +32.3442 0.0942009 +32.5101 0.0879237 +32.6759 0.0824187 +32.8417 0.0776113 +33.0075 0.0734305 +33.1734 0.06981 +33.3392 0.0666875 +33.505 0.0640057 +33.6709 0.0617118 +33.8367 0.0597577 +34.0025 0.0580998 +34.1683 0.056699 +34.3342 0.0555202 +34.5 0.0545321 diff --git a/examples/advanced/data_2.dat b/examples/advanced/data_2.dat new file mode 100644 index 0000000..066c0eb --- /dev/null +++ b/examples/advanced/data_2.dat @@ -0,0 +1,201 @@ +x h_8_t +1.5 0.0500234 +1.66583 0.0500298 +1.83166 0.0500377 +1.99749 0.0500477 +2.16332 0.0500601 +2.32915 0.0500755 +2.49497 0.0500947 +2.6608 0.0501183 +2.82663 0.0501475 +2.99246 0.0501834 +3.15829 0.0502274 +3.32412 0.0502812 +3.48995 0.0503468 +3.65578 0.0504264 +3.82161 0.050523 +3.98744 0.0506397 +4.15327 0.0507804 +4.3191 0.0509493 +4.48492 0.0511517 +4.65075 0.0513935 +4.81658 0.0516814 +4.98241 0.0520232 +5.14824 0.052428 +5.31407 0.0529059 +5.4799 0.0534683 +5.64573 0.0541284 +5.81156 0.0549007 +5.97739 0.0558016 +6.14322 0.0568496 +6.30905 0.0580649 +6.47487 0.0594699 +6.6407 0.0610896 +6.80653 0.062951 +6.97236 0.0650837 +7.13819 0.0675199 +7.30402 0.0702943 +7.46985 0.0734441 +7.63568 0.0770092 +7.80151 0.0810318 +7.96734 0.0855567 +8.13317 0.0906307 +8.29899 0.0963026 +8.46482 0.102623 +8.63065 0.109643 +8.79648 0.117417 +8.96231 0.125996 +9.12814 0.135434 +9.29397 0.145784 +9.4598 0.157096 +9.62563 0.169417 +9.79146 0.182795 +9.95729 0.19727 +10.1231 0.21288 +10.2889 0.229654 +10.4548 0.247617 +10.6206 0.266785 +10.7864 0.287167 +10.9523 0.308759 +11.1181 0.331551 +11.2839 0.355517 +11.4497 0.380622 +11.6156 0.406818 +11.7814 0.434044 +11.9472 0.462223 +12.1131 0.491267 +12.2789 0.521075 +12.4447 0.551529 +12.6106 0.582501 +12.7764 0.613849 +12.9422 0.64542 +13.108 0.677051 +13.2739 0.708567 +13.4397 0.739788 +13.6055 0.770525 +13.7714 0.800587 +13.9372 0.829778 +14.103 0.857903 +14.2688 0.884768 +14.4347 0.910182 +14.6005 0.933962 +14.7663 0.95593 +14.9322 0.975921 +15.098 0.993782 +15.2638 1.00937 +15.4296 1.02257 +15.5955 1.03327 +15.7613 1.04139 +15.9271 1.04686 +16.093 1.04963 +16.2588 1.04969 +16.4246 1.04703 +16.5905 1.04167 +16.7563 1.03367 +16.9221 1.02308 +17.0879 1.00998 +17.2538 0.99449 +17.4196 0.976723 +17.5854 0.956819 +17.7513 0.934932 +17.9171 0.911225 +18.0829 0.885877 +18.2487 0.85907 +18.4146 0.830994 +18.5804 0.801844 +18.7462 0.771815 +18.9121 0.741102 +19.0779 0.709898 +19.2437 0.678391 +19.4095 0.646762 +19.5754 0.615185 +19.7412 0.583824 +19.907 0.552833 +20.0729 0.522354 +20.2387 0.492517 +20.4045 0.463438 +20.5704 0.43522 +20.7362 0.407953 +20.902 0.381712 +21.0678 0.356559 +21.2337 0.332543 +21.3995 0.309702 +21.5653 0.288058 +21.7312 0.267625 +21.897 0.248405 +22.0628 0.230391 +22.2286 0.213567 +22.3945 0.197909 +22.5603 0.183387 +22.7261 0.169963 +22.892 0.157597 +23.0578 0.146244 +23.2236 0.135854 +23.3894 0.126379 +23.5553 0.117764 +23.7211 0.109957 +23.8869 0.102906 +24.0528 0.0965571 +24.2186 0.0908588 +24.3844 0.0857604 +24.5503 0.0812132 +24.7161 0.0771702 +24.8819 0.0735866 +25.0477 0.07042 +25.2136 0.0676305 +25.3794 0.0651806 +25.5452 0.0630357 +25.7111 0.0611634 +25.8769 0.0595341 +26.0427 0.0581204 +26.2085 0.0568976 +26.3744 0.055843 +26.5402 0.0549361 +26.706 0.0541587 +26.8719 0.0534942 +27.0377 0.0529279 +27.2035 0.0524467 +27.3693 0.0520391 +27.5352 0.0516947 +27.701 0.0514047 +27.8668 0.0511611 +28.0327 0.0509572 +28.1985 0.0507869 +28.3643 0.0506452 +28.5302 0.0505275 +28.696 0.0504302 +28.8618 0.0503498 +29.0276 0.0502837 +29.1935 0.0502295 +29.3593 0.0501851 +29.5251 0.0501489 +29.691 0.0501194 +29.8568 0.0500956 +30.0226 0.0500762 +30.1884 0.0500607 +30.3543 0.0500481 +30.5201 0.0500381 +30.6859 0.0500301 +30.8518 0.0500237 +31.0176 0.0500186 +31.1834 0.0500145 +31.3492 0.0500114 +31.5151 0.0500088 +31.6809 0.0500069 +31.8467 0.0500053 +32.0126 0.0500041 +32.1784 0.0500032 +32.3442 0.0500024 +32.5101 0.0500019 +32.6759 0.0500014 +32.8417 0.0500011 +33.0075 0.0500008 +33.1734 0.0500006 +33.3392 0.0500005 +33.505 0.0500004 +33.6709 0.0500003 +33.8367 0.0500002 +34.0025 0.0500001 +34.1683 0.0500001 +34.3342 0.0500001 +34.5 0.0500001 diff --git a/examples/advanced/data_points1.dat b/examples/advanced/data_points1.dat new file mode 100644 index 0000000..b7a3a62 --- /dev/null +++ b/examples/advanced/data_points1.dat @@ -0,0 +1,3200 @@ +12.1761 1.1237 0.022788 +27.2517 4.36369 1.04412 +29.3813 5.2571 0.357506 +17.5911 0.250742 0.0665337 +28.556 1.29287 0.926928 +28.0544 8.11256 0.0730703 +7.3913 0.615476 0.024861 +6.12951 1.13422 0.0693155 +5.85763 8.28839 0.0434927 +4.34099 5.43526 0.0525009 +27.1395 3.225 0.96124 +14.0989 4.46955 0.0578312 +6.17008 5.79325 0.0599249 +19.6188 2.4062 0.0611342 +11.2799 1.20584 0.0395032 +3.07074 6.88747 0.0398518 +15.184 5.85895 0.050596 +28.764 4.47822 0.73865 +24.7614 7.13828 0.310988 +34.0252 4.79892 0.0370046 +17.1581 8.57319 0.495517 +33.2855 1.78741 0.273282 +14.1208 4.8396 0.0426933 +17.658 4.22688 0.0409022 +26.1173 3.08762 0.694801 +16.631 5.17054 0.0671193 +7.01336 2.05653 0.039522 +24.9368 7.01125 0.330365 +31.732 7.5805 0.0400471 +13.2607 1.12536 0.0344124 +15.2244 4.08222 0.0268211 +11.6964 2.42225 0.063663 +24.2279 0.726443 0.0935607 +17.7472 7.83055 0.999503 +9.08567 3.75775 0.0734952 +31.1494 1.29082 0.88609 +1.97198 5.88519 0.0290436 +29.0508 1.76737 1.03865 +30.017 7.87851 0.0458615 +4.61571 1.89788 0.0623958 +2.05834 0.289073 0.00569179 +22.8448 1.75472 0.0725742 +20.7502 3.02184 0.0514718 +7.19373 4.09826 0.0570755 +15.7813 7.91961 1.03275 +4.67472 6.09494 0.0143581 +15.1512 2.96566 0.0491455 +18.8303 0.147507 0.0498539 +31.0229 1.39686 0.888413 +13.8752 8.70885 0.914428 +6.7322 4.01792 0.0612964 +32.8949 6.03969 0.0417271 +22.5257 0.477799 0.0621535 +5.94831 0.297599 0.0385282 +3.48079 7.39308 0.0615009 +14.0735 5.13809 0.047582 +9.49542 2.69812 0.047222 +13.6089 2.77387 0.0519399 +10.4678 0.779934 0.0259599 +31.6123 1.50913 0.713948 +28.2962 0.214883 0.7377 +23.6333 7.33395 0.397567 +4.46828 4.07549 0.0690318 +27.6185 1.11175 0.631472 +14.6181 6.46102 0.0603058 +30.1645 1.71001 1.01217 +3.19093 0.541183 0.0413137 +6.31942 5.22995 0.0358997 +5.78892 7.82892 0.0861076 +28.4244 0.235485 0.787199 +32.9736 7.03689 0.0345828 +26.1205 1.66209 0.359769 +11.6663 0.811958 0.0542242 +3.2067 0.156988 0.0735834 +33.31 2.5606 0.232066 +7.1357 6.35496 0.0310928 +28.2463 4.31038 0.890764 +17.0625 7.45452 0.962208 +12.6788 1.89843 0.0237614 +15.0354 2.7547 0.0492012 +4.04925 2.25615 0.0676279 +5.35094 8.55035 0.114392 +30.114 8.22439 0.0467363 +16.445 2.9776 0.0366423 +23.6783 3.81065 0.384652 +12.4747 2.74715 0.0578314 +17.4169 6.52449 0.386808 +27.7201 0.349715 0.610626 +22.8116 0.589416 0.0559664 +14.6561 3.53129 0.069434 +16.7168 2.14212 0.0521384 +34.0499 7.38727 0.0637165 +2.5668 6.48339 0.0451482 +14.7577 4.77024 0.0764421 +5.79916 5.7813 0.0689395 +4.95902 6.05052 0.060467 +3.2583 6.82642 0.0351987 +26.7607 8.10637 0.0552793 +16.5839 1.30869 0.00589368 +19.6181 5.47238 0.261802 +11.454 1.25525 0.0525629 +14.5231 3.87296 0.0370675 +16.4115 6.87213 0.413298 +28.8531 7.81966 0.0610122 +4.50658 6.63565 0.0773482 +7.42687 0.309468 0.0503947 +7.09825 3.14126 0.0265803 +16.8644 1.42512 0.0148385 +17.2498 8.72953 0.305023 +23.9505 1.25869 0.0938039 +34.116 2.13531 0.175049 +31.7631 3.1221 0.430117 +9.42616 0.53215 0.0402563 +14.9577 7.60716 0.667299 +19.0125 5.5618 0.218019 +27.6022 1.3962 0.694838 +18.3895 4.35487 0.0666353 +6.76569 0.687574 0.052036 +3.15269 5.33996 0.0804303 +12.72 2.02474 0.0526043 +21.5233 0.337704 0.0578834 +7.43823 1.00718 0.0659806 +16.5053 4.853 0.0674141 +14.1582 5.56721 0.0421003 +33.1909 2.83876 0.194721 +24.5922 5.62377 1.02008 +33.6948 3.07706 0.151817 +18.6222 1.14196 0.0592357 +17.7197 2.75494 0.0555232 +33.7539 3.45464 0.13087 +14.9523 7.97646 0.984353 +19.7588 1.01087 0.0388163 +2.29063 0.753072 0.0888386 +7.07736 4.90753 0.0904238 +10.9653 8.42005 0.726997 +9.58468 7.92941 0.138791 +15.8068 6.12013 0.102802 +30.8417 0.583402 0.940733 +8.41496 7.04795 0.0514366 +3.06901 5.9727 0.0381869 +20.0637 1.25795 0.0490703 +13.4987 4.06361 0.0456447 +3.03837 0.43108 0.0815178 +26.5803 7.0084 0.150997 +32.4468 6.28086 0.0582547 +26.0957 7.03245 0.193276 +27.888 6.64603 0.170223 +26.5254 2.33583 0.618969 +33.797 6.90233 0.0275968 +6.10678 2.17775 0.0356861 +30.4433 1.20521 1.0063 +24.6513 3.41201 0.450122 +32.8358 4.35112 0.12271 +29.4541 2.49809 1.02879 +19.7228 5.29454 0.209817 +23.2152 5.26588 0.864997 +9.61952 2.09601 0.0576686 +12.5401 5.44124 0.0495001 +6.43121 3.12239 0.0525897 +14.7995 6.42119 0.102174 +28.7693 2.53825 1.04694 +12.7643 6.98145 0.114395 +30.598 3.62807 0.557766 +7.95739 4.83532 0.0575937 +22.8467 5.88505 1.01303 +9.22658 4.53062 0.0565923 +22.5092 2.25158 0.08065 +27.4493 8.5598 0.0601703 +30.4852 0.839524 1.02123 +26.6506 2.83983 0.777788 +6.18934 4.79924 0.0585395 +24.1164 0.250842 0.0627143 +11.2086 1.35394 0.0615691 +18.2028 6.98557 0.846427 +6.16337 6.85054 0.0635836 +16.0915 5.56709 0.0690998 +23.2262 7.88173 0.177313 +5.27213 6.60327 0.0206144 +20.9342 2.61138 0.0422852 +30.7109 5.63613 0.115618 +18.8812 2.97232 0.0659692 +12.2791 6.55364 0.0748007 +8.31206 3.3631 0.0526043 +16.8069 1.33935 0.0335298 +14.5199 7.66023 0.64592 +24.3951 6.03414 0.979464 +25.9385 6.50849 0.447543 +24.5247 4.89089 0.948114 +26.5188 6.84291 0.199384 +27.572 3.91441 1.03263 +25.4408 4.94503 1.03216 +9.7583 0.546807 0.0654518 +20.5602 4.8513 0.2111 +14.4092 7.11964 0.284967 +4.04863 6.16647 0.0752081 +22.2671 7.01955 0.861089 +6.182 4.33591 0.0400945 +24.1256 7.70287 0.171834 +19.24 0.946562 0.0571628 +3.85416 7.65362 0.0292629 +15.402 3.24491 0.0312445 +19.5406 0.795013 0.0392573 +9.72289 5.41297 0.0693277 +32.2207 3.97576 0.201663 +32.8769 3.74398 0.144682 +4.92886 7.44673 0.0516671 +25.1795 1.2043 0.169013 +15.6121 5.39201 0.0370871 +29.0033 3.61618 0.96241 +32.4198 4.61698 0.107838 +18.0187 4.36484 0.0710768 +22.4284 1.17229 0.0638866 +13.6161 4.47501 0.0392009 +23.4684 7.5348 0.296066 +34.1973 1.49659 0.170574 +12.4696 0.100238 0.0448294 +2.47755 0.590521 0.0615768 +33.5449 4.01861 0.100304 +2.39731 8.51671 0.0277269 +24.1936 0.38663 0.0708305 +20.6373 8.66072 0.0729803 +14.8311 4.6851 0.0467962 +32.1771 1.05003 0.602355 +5.46638 3.65803 0.0484659 +9.3784 1.81233 0.0655321 +28.8605 6.24168 0.150584 +1.73122 4.73283 0.0422852 +10.1698 2.51677 0.075058 +7.08391 2.23239 0.0612415 +15.3846 7.57738 0.733282 +25.8031 6.69696 0.367661 +32.9363 3.81226 0.14744 +9.56018 3.54417 0.0513689 +1.93248 6.44551 0.0356004 +25.989 8.48422 0.0773637 +31.519 0.69564 0.817469 +1.98232 1.39039 0.016237 +21.7619 3.16674 0.0659304 +21.0922 4.43743 0.172313 +19.8191 0.624112 0.0403267 +22.1884 0.467499 0.0187794 +10.702 1.9181 0.0605677 +31.0068 3.37329 0.527012 +8.80084 6.46132 0.0604361 +32.6945 5.33065 0.0755702 +32.9042 0.255244 0.432452 +28.9365 0.393412 0.917204 +14.264 3.95074 0.0776097 +32.7582 7.64619 0.0315323 +8.58582 7.99707 0.129736 +17.7208 3.19102 0.0662747 +18.16 7.75564 1.02203 +6.11912 8.12055 0.0463269 +34.0472 3.55381 0.1222 +7.46893 6.52068 0.0623006 +17.3251 3.19309 0.0250255 +4.3368 1.34226 0.0108522 +10.771 5.02723 0.0400737 +28.8976 0.755331 0.929477 +23.9174 5.79771 1.05943 +13.5354 7.07241 0.154614 +22.9274 8.00065 0.159787 +7.12259 3.91574 0.042368 +23.2797 1.02594 0.074251 +30.8654 7.88778 0.0415093 +1.9636 7.60703 0.0272611 +11.3947 8.45691 0.796461 +9.9728 5.19138 0.0580082 +4.40901 5.88555 0.0408932 +31.7017 3.26246 0.388297 +24.2385 1.60061 0.0949735 +21.801 2.54959 0.0556937 +21.7066 6.29854 1.02816 +23.6968 2.84036 0.143034 +10.9078 6.04447 0.0440767 +8.13733 4.38887 0.0613562 +4.03883 3.92423 0.0573615 +6.6239 8.45707 0.157711 +8.05135 1.45163 0.0802055 +27.8007 4.25779 0.969789 +12.1315 1.39591 0.0412034 +14.2612 8.19337 1.02612 +25.9026 4.27053 1.01859 +33.4607 2.90138 0.186682 +4.92176 1.47112 0.0456819 +20.8673 5.19244 0.339108 +15.2829 3.4869 0.0600093 +7.48719 1.11155 0.0559278 +12.7108 0.282947 0.0344399 +27.3337 3.41998 1.00914 +10.361 5.09689 0.0358335 +19.9857 4.53453 0.117077 +16.5375 7.86624 1.04449 +2.09998 7.96755 0.0695787 +29.8728 8.18324 0.0712206 +29.9369 6.98931 0.0622441 +29.071 4.17436 0.788259 +15.8543 4.57797 0.0671842 +15.6655 3.47253 0.0735787 +23.7149 3.49201 0.294148 +34.0497 0.363406 0.200593 +17.9645 6.98171 0.813356 +11.3337 2.15022 0.0610859 +17.1687 0.256423 0.0448535 +20.3737 4.07703 0.0903233 +22.2759 6.27406 1.08223 +32.5172 3.81092 0.19008 +20.5837 3.18252 0.0496829 +28.6259 5.75553 0.312726 +17.0822 1.42335 0.0456796 +16.4506 0.0990406 0.0276029 +31.4137 5.17778 0.104147 +10.89 4.63614 0.0515819 +11.4793 7.56727 0.143763 +19.5476 3.63471 0.0791887 +5.17889 6.87065 0.0423022 +6.94251 0.156672 0.0303408 +18.5464 0.238408 0.039646 +22.6365 5.23146 0.718808 +33.8307 1.97733 0.216104 +5.22463 0.5879 0.0395411 +2.8946 1.11653 0.0742676 +24.2127 3.27525 0.34245 +31.0653 2.94658 0.661324 +4.53438 4.92755 0.0498911 +11.32 7.75758 0.226363 +23.979 3.19602 0.244025 +22.6542 1.60953 0.0528127 +30.1957 5.05198 0.258297 +23.3657 0.018859 0.0731661 +32.7784 3.36331 0.21657 +11.177 2.77265 0.0617409 +26.0996 3.3858 0.782651 +20.9568 0.444001 0.0319864 +14.4843 4.84386 0.0504319 +25.2834 5.45523 1.03874 +29.4047 6.55033 0.0858465 +20.6249 8.36815 0.214459 +25.1248 3.10045 0.46739 +15.0972 5.73598 0.0544224 +29.8872 3.86345 0.673812 +14.1899 1.87952 0.047656 +22.082 7.77244 0.348388 +21.1639 6.51922 1.02358 +18.4415 8.23997 0.589883 +31.8004 7.18028 0.0362496 +24.7192 8.01867 0.0999556 +20.949 1.11914 0.0651921 +25.0378 0.137345 0.096167 +24.8924 1.73565 0.167125 +12.4242 3.60197 0.0277642 +11.9416 6.01 0.0610183 +21.6176 3.83215 0.11662 +6.10567 4.48665 0.0809386 +28.2525 6.65107 0.0973934 +26.8778 0.617074 0.399804 +22.6611 5.92834 1.03387 +12.4886 0.425888 0.0434957 +33.7951 2.17319 0.223828 +25.9873 6.31267 0.543395 +29.9284 5.97002 0.139575 +25.2405 3.5422 0.627316 +19.9344 2.57443 0.0682235 +10.8558 7.23766 0.087123 +7.45728 2.40134 0.053814 +28.4467 0.272787 0.831012 +22.01 2.16112 0.0649787 +9.30731 0.18523 0.0173471 +8.39168 7.08544 0.0577589 +17.5647 1.06864 0.0785388 +22.4497 8.5125 0.0621748 +26.6447 1.86148 0.538595 +17.3457 2.67506 0.042171 +9.75657 5.11458 0.0518348 +23.8438 2.69374 0.131698 +5.77855 3.31503 0.0242582 +7.17027 0.910797 0.0472585 +13.7703 3.40048 0.0429768 +20.0493 0.220536 0.0233091 +2.12597 1.95747 0.0624804 +5.96859 7.94877 0.0614027 +24.8577 2.45399 0.255926 +15.0672 2.36765 0.020096 +16.2523 5.93675 0.105063 +17.0693 7.71657 1.03472 +7.27822 2.58568 0.0626475 +33.344 3.28911 0.180738 +31.963 6.24624 0.0519973 +22.433 6.92336 0.869935 +4.52864 5.36456 0.06288 +24.1772 1.56444 0.0998723 +28.1914 1.57963 0.844558 +12.5571 6.62451 0.0712035 +6.47139 6.17769 0.0557569 +25.4778 7.83348 0.0684817 +22.4655 6.81545 0.931694 +14.4543 3.68143 0.0448887 +11.2419 1.16482 0.0443936 +32.1499 7.78273 0.0627384 +4.37235 6.07905 0.0294633 +11.7366 5.03752 0.0257128 +17.3136 5.95954 0.157904 +20.9477 3.36617 0.0475665 +9.94383 3.00027 0.0635111 +21.1088 5.02933 0.324103 +5.21114 4.21921 0.0238017 +19.0423 6.1964 0.490899 +4.30667 8.18145 0.0191073 +14.3693 7.40536 0.430541 +25.0112 3.38649 0.532068 +8.09113 3.25545 0.0606317 +6.5234 8.10051 0.0746643 +33.3581 3.449 0.151397 +15.653 6.99409 0.34261 +3.39515 0.810564 0.0622287 +15.5369 3.94268 0.045184 +10.4846 4.51193 0.0385403 +28.4848 7.06925 0.0403098 +30.5128 7.67031 0.0700208 +26.6453 1.33424 0.426232 +23.7365 5.39394 0.986774 +20.3418 5.49868 0.362146 +3.40978 1.32446 0.0644907 +22.9768 7.28542 0.528647 +14.2766 4.83727 0.0657482 +10.8018 8.4054 0.667462 +20.1433 1.62836 0.0373929 +9.48777 4.58824 0.0589009 +34.0813 2.82697 0.105606 +21.2322 5.61523 0.603332 +26.2107 5.31457 0.939069 +5.08567 4.47861 0.0615718 +2.70896 2.83924 0.0672209 +7.18724 4.40446 0.0488169 +7.58279 3.25352 0.0484259 +19.6629 7.78542 0.788056 +23.144 4.03371 0.348603 +31.0559 4.15354 0.338685 +4.64322 2.25981 0.0578207 +17.9189 2.21852 0.0425307 +23.2632 8.33698 0.0696814 +15.2364 2.7444 0.0684424 +3.88099 7.58931 0.0536143 +4.68523 5.19835 0.0423148 +1.7978 8.56974 0.0637388 +12.0994 1.43401 0.0480588 +6.79807 8.22936 0.0993081 +32.8317 6.75139 0.0375706 +22.3144 4.96443 0.528297 +4.73226 6.19541 0.0284815 +23.46 6.31409 1.00565 +18.1482 3.91888 0.0732724 +28.9044 5.54534 0.340311 +7.71768 1.99761 0.0555719 +12.9451 0.186688 0.0531911 +17.6311 5.91266 0.172064 +18.3842 4.14675 0.0633967 +6.22595 7.91464 0.0520354 +28.9049 2.30769 1.03124 +6.70831 7.14898 0.0653708 +16.7732 8.6205 0.507893 +20.2756 7.84924 0.622584 +15.9666 8.4289 0.855316 +12.7381 1.39024 0.0443175 +25.332 1.4428 0.200192 +18.799 1.9954 0.0532857 +24.0547 3.15936 0.260727 +26.6585 1.0086 0.379518 +31.9902 1.31704 0.616886 +17.7611 0.161489 0.0668743 +15.2744 8.15695 1.02909 +31.8461 3.15585 0.386359 +33.3608 5.47151 0.043696 +30.3061 3.97264 0.549919 +8.21641 6.49427 0.0359934 +31.4792 7.0456 0.0773724 +22.8404 3.34733 0.171288 +18.7376 4.67874 0.0371366 +5.83407 0.852078 0.0533098 +6.85652 2.49506 0.0551683 +30.0812 1.94691 0.982742 +11.0295 2.69681 0.0485292 +17.1416 2.83571 0.0511706 +15.6173 5.02554 0.0417994 +14.2162 2.90841 0.0483237 +15.5421 2.85677 0.0549235 +10.746 7.26542 0.0622018 +31.1877 0.10613 0.889055 +18.477 0.0459696 0.0317035 +11.1924 0.499199 0.0592746 +8.51003 7.56487 0.0485538 +27.9485 5.53889 0.514849 +27.5426 6.3624 0.281632 +28.6028 8.3702 0.0751235 +21.1513 1.90532 0.044267 +12.4012 8.0408 0.58885 +8.49208 2.12033 0.0458609 +13.8101 4.53489 0.0567392 +4.46039 5.95255 0.0372681 +10.5941 6.96804 0.0397865 +12.9688 2.16849 0.048976 +5.31763 4.16919 0.0376556 +6.51424 0.398463 0.0682343 +5.45147 2.51606 0.0606667 +14.3358 8.57366 0.965145 +32.0773 7.9106 0.047526 +33.1468 5.08747 0.0648713 +4.9112 6.87395 0.0643765 +21.1443 7.88532 0.428523 +1.67852 2.90041 0.0499668 +23.4947 8.56957 0.0833681 +22.1584 3.92014 0.185929 +18.4304 1.38875 0.0458937 +10.6788 4.63285 0.0463891 +16.8456 5.18844 0.0802586 +2.78121 6.05537 0.0423518 +23.1216 5.86413 1.02864 +27.1756 0.362231 0.460334 +4.32425 5.58525 0.0673805 +34.4683 8.055 0.0710989 +7.35206 5.85673 0.0369484 +31.7153 8.52339 0.0564844 +33.8016 4.47058 0.0636641 +15.4935 7.08122 0.390496 +29.248 1.22598 1.00002 +20.0377 7.82703 0.676077 +10.4941 3.81447 0.0661701 +24.1706 7.66209 0.173662 +13.3447 2.80404 0.013566 +6.55665 5.45656 0.0649458 +26.9632 7.39923 0.0863687 +7.78371 6.21726 0.0458209 +32.3309 5.41818 0.0728353 +8.05487 8.48345 0.290589 +12.1572 0.769091 0.067312 +27.947 0.891439 0.715741 +29.0924 4.82081 0.514823 +14.7533 8.34399 1.02903 +21.6116 5.99981 0.923903 +32.648 2.70629 0.335274 +11.1001 0.941452 0.0620309 +10.3527 3.91802 0.0380783 +4.95716 4.19774 0.0581682 +24.2302 4.09272 0.616344 +24.302 5.41299 1.04367 +5.54978 2.3843 0.0610707 +25.2519 7.69464 0.117755 +11.0042 2.51999 0.0657348 +18.372 0.935825 0.0569811 +20.0372 3.0499 0.0600529 +20.75 8.17816 0.262818 +19.727 1.39336 0.0457643 +14.6777 2.427 0.0627336 +24.6178 8.09827 0.0958771 +8.44178 3.04223 0.0529303 +30.1973 7.36865 0.067848 +14.3017 0.397075 0.0503886 +27.0067 7.76536 0.0440052 +21.5762 2.07381 0.0605434 +16.4012 1.57815 0.0571461 +17.1983 6.48721 0.31656 +18.8267 8.64789 0.184521 +15.3117 8.38413 0.984006 +20.0287 6.57522 0.903522 +17.2243 7.99083 1.00954 +29.3359 8.62692 0.0297806 +10.1259 4.83126 0.0625643 +26.336 1.26391 0.339951 +15.0821 7.42364 0.556612 +29.601 3.46001 0.829979 +28.8232 2.73259 1.06365 +3.97813 4.37796 0.0533043 +32.1005 0.304757 0.667543 +28.3074 0.163442 0.731466 +23.1371 2.31011 0.0616649 +7.83274 5.20795 0.0611004 +28.9714 6.54989 0.114427 +21.0186 8.36586 0.132569 +9.14922 7.15055 0.0607058 +22.6124 8.22533 0.125148 +5.45804 5.56161 0.0715017 +24.5735 6.41429 0.760983 +7.86258 5.1336 0.027016 +11.5907 0.711723 0.0210725 +2.25748 0.541039 0.081885 +13.8209 6.64362 0.094315 +21.5139 1.76851 0.0671737 +23.1541 6.90302 0.746446 +15.2302 4.05585 0.0468435 +10.7412 0.0607171 0.0471632 +4.64233 4.90777 0.0361855 +13.2536 3.87215 0.0459178 +27.2986 0.645748 0.487781 +32.2969 2.28179 0.441153 +31.8833 2.68006 0.499358 +4.28294 3.02125 0.0506772 +5.71664 6.17769 0.0401029 +13.6441 1.46197 0.0763843 +19.9444 3.87437 0.0850788 +3.1575 5.30371 0.0509844 +4.45456 8.27099 0.0299869 +28.7248 1.00499 0.945556 +34.3858 0.693254 0.174824 +15.9593 1.67557 0.0414694 +27.6598 1.92786 0.826018 +24.6839 1.42259 0.116876 +26.8685 0.664207 0.39194 +15.3907 1.83143 0.0434026 +32.1612 1.95221 0.525574 +5.56981 4.19126 0.0482728 +10.2861 5.66121 0.071985 +22.4789 3.18021 0.108966 +9.73639 1.46988 0.0570503 +29.1796 0.827434 0.997126 +28.612 2.29929 1.03181 +27.5379 1.49293 0.680086 +30.481 4.20425 0.443115 +22.4744 7.61333 0.420153 +7.51404 6.26178 0.0628223 +12.3542 1.77627 0.032653 +1.64205 0.31313 0.0442398 +14.5511 0.590972 0.0351302 +22.4835 3.80881 0.188224 +24.4929 3.17782 0.350352 +25.0772 5.9645 0.867186 +10.067 0.733192 0.0573416 +31.9785 2.22478 0.545925 +19.4701 5.99372 0.471217 +8.34495 4.96491 0.0431541 +32.4171 4.51593 0.130576 +23.6888 7.88054 0.128997 +24.031 0.990511 0.0918189 +21.549 1.70848 0.0875635 +1.54527 4.94309 0.00996511 +19.706 4.94082 0.151303 +25.3187 8.56609 0.0518619 +23.4709 8.5427 0.0597769 +8.60489 5.13274 0.0461665 +17.1654 6.63249 0.377917 +25.3817 6.09541 0.764733 +31.2615 1.16184 0.835301 +9.08201 5.26403 0.0480927 +20.4022 2.67238 0.0517902 +3.77086 3.13733 0.0427777 +7.29324 1.36592 0.0848378 +14.0158 0.0728363 0.0315155 +2.05978 7.46087 0.0444431 +13.1949 6.26708 0.0370502 +30.2363 6.57423 0.0632216 +11.4749 2.51736 0.0736445 +6.47991 3.64801 0.0496114 +4.94094 1.53914 0.0377168 +19.7241 5.26302 0.197239 +14.7778 7.33462 0.437986 +22.1646 6.80693 0.958128 +14.7651 8.1614 1.04293 +18.8933 2.27648 0.0398284 +14.8743 6.12964 0.0637278 +11.0173 1.1075 0.0493419 +1.83698 4.68727 0.0255758 +10.8343 0.963582 0.0683159 +25.9863 4.72265 1.03286 +12.8836 4.73816 0.0524751 +26.9759 3.03234 0.894976 +2.88024 3.05458 0.0856989 +24.7106 6.21515 0.843719 +28.7372 4.20435 0.845271 +9.54125 3.56275 0.0425509 +14.0171 0.985122 0.0480813 +10.213 2.46721 0.0500687 +15.8875 7.40336 0.707893 +31.8321 8.49516 0.0352749 +24.498 3.26157 0.389372 +4.944 7.61086 0.0934303 +34.3915 7.63612 0.0357443 +23.935 1.90619 0.0797902 +28.3549 2.39311 1.00176 +29.2143 6.08339 0.151534 +31.8416 2.67453 0.518344 +27.1279 8.34929 0.0579744 +10.3972 3.19082 0.0675036 +26.1484 6.38197 0.449696 +23.561 6.18174 1.01436 +15.2682 1.86727 0.0547202 +30.2124 7.56867 0.0381444 +1.88906 2.87304 0.054622 +18.1572 0.96205 0.046119 +30.6881 0.438393 0.994789 +19.0561 3.949 0.0355492 +14.1074 5.53236 0.0209325 +5.06554 2.37188 0.0709923 +7.76839 7.35832 0.0524669 +10.2391 2.33809 0.0490539 +34.2743 7.40709 0.0893466 +19.5981 0.289104 0.0658176 +11.643 2.29567 0.0573344 +30.7689 1.39386 0.925032 +21.6573 3.9129 0.144346 +11.2221 3.06631 0.0542404 +23.0914 3.45018 0.199967 +2.34486 7.44248 0.0722614 +18.7239 5.06536 0.115224 +18.8673 5.29161 0.153198 +3.75857 3.40879 0.0385882 +23.2888 7.06215 0.610778 +2.26824 4.38558 0.0676335 +7.90376 6.52007 0.0544786 +25.9601 7.99006 0.0518414 +14.7546 5.34897 0.0573306 +25.2864 3.80329 0.741767 +19.2902 2.30406 0.0447246 +16.8136 7.9709 1.03649 +21.054 6.6852 1.05868 +2.16806 6.98786 0.0561359 +1.60276 1.93503 0.0381986 +32.4534 3.81673 0.191207 +24.2747 7.72261 0.150913 +7.56037 2.14996 0.0353952 +24.3759 7.11363 0.390644 +32.0534 4.70779 0.133182 +29.1184 1.91195 1.02468 +22.6729 3.28151 0.102642 +18.8269 0.666265 0.0383647 +22.0445 5.88268 0.9209 +7.5182 6.35332 0.050807 +16.221 0.253237 0.0696802 +30.4017 2.63436 0.854753 +13.4807 6.719 0.0851585 +10.3332 1.75117 0.0563342 +18.6835 4.69326 0.0670313 +28.3194 7.04338 0.103012 +30.6048 4.98153 0.222438 +34.4568 1.06822 0.136058 +33.3214 7.94613 0.043092 +5.06103 6.52628 0.0687571 +16.5416 4.93756 0.0459905 +19.7259 2.90904 0.0405128 +23.8689 5.08939 0.927429 +5.17452 3.42204 0.0682114 +13.5171 1.03736 0.0442528 +13.9176 1.06747 0.0609521 +21.5456 0.396018 0.0408498 +13.6818 1.48888 0.0714149 +11.9091 2.14639 0.0343494 +7.64447 5.55244 0.0463171 +23.3736 5.07067 0.812088 +11.6942 5.16131 0.0364792 +8.25709 7.18436 0.0501713 +31.5746 0.831505 0.788818 +14.1935 3.399 0.037907 +10.9349 0.693022 0.0788699 +4.48508 3.66003 0.0540033 +18.6472 0.688661 0.0553232 +12.7608 0.579852 0.0386763 +20.8023 0.911169 0.049381 +11.7621 3.40763 0.0635694 +16.9843 6.66952 0.375539 +5.73184 2.46392 0.039835 +3.54885 6.48339 0.0377473 +32.295 2.03892 0.459268 +25.7368 2.51026 0.462208 +15.3508 4.12028 0.0381371 +8.0745 0.655328 0.0748392 +23.392 3.92317 0.344175 +21.3957 5.10041 0.408609 +21.9852 7.20679 0.778981 +33.7635 0.490844 0.220769 +13.144 6.99127 0.0789585 +32.1327 4.97882 0.0953791 +15.8035 5.05464 0.0519341 +33.6773 0.446478 0.235091 +33.5881 7.69016 0.0540565 +15.2004 2.42389 0.0221792 +17.9605 4.7762 0.0674654 +31.0331 4.70759 0.263049 +30.958 7.95408 0.0645276 +4.64928 5.25638 0.0650021 +18.1605 8.66447 0.247024 +4.73929 2.02895 0.0436685 +29.4363 5.78737 0.208077 +10.4104 5.04149 0.0450621 +12.3676 4.4883 0.0597137 +12.0513 5.48541 0.0354316 +25.2174 4.81218 1.02753 +12.1829 2.94392 0.0526811 +16.0508 5.6074 0.0321078 +30.3316 0.728842 1.02862 +16.029 4.18663 0.058712 +31.7854 3.95766 0.27559 +31.7594 1.86363 0.644091 +16.4919 4.96509 0.0732737 +9.15155 5.85509 0.0490574 +32.4666 1.09262 0.505847 +19.9098 2.59563 0.0645429 +13.2222 0.5421 0.081908 +3.45702 5.2192 0.0449232 +16.8092 0.713919 0.0728007 +2.63859 2.00497 0.047385 +22.19 3.91434 0.163974 +14.525 3.94822 0.0366086 +11.1181 0.288305 0.0371063 +8.74768 8.27963 0.247689 +26.4773 2.0105 0.516242 +31.1738 5.33115 0.116627 +1.7859 1.94209 0.051969 +18.9788 5.20708 0.158257 +4.07674 3.08301 0.0488596 +2.66421 2.78445 0.0392044 +33.9415 4.14624 0.0843698 +11.8386 5.12443 0.037498 +19.1128 5.22814 0.158361 +31.298 6.01748 0.0847565 +22.6951 2.34744 0.0777581 +13.0294 0.800606 0.0435472 +24.6688 2.21429 0.205175 +27.3374 2.85536 0.899143 +22.806 5.30845 0.831192 +14.2584 3.62172 0.0855537 +26.4344 2.72043 0.67998 +13.8215 0.859518 0.0190044 +13.4503 5.21462 0.0400072 +10.7847 0.961179 0.0282976 +33.6394 4.51766 0.04662 +5.44337 6.01627 0.0478917 +3.24253 5.44588 0.0475425 +11.1443 1.39597 0.040532 +5.57654 3.1111 0.0666413 +13.5885 8.22376 0.956146 +32.4732 0.793359 0.511666 +32.7791 5.93236 0.0613178 +9.02693 3.8142 0.0310184 +11.254 5.36201 0.0447148 +14.7713 3.44111 0.0374828 +32.841 1.08188 0.426615 +10.2456 4.91533 0.0459399 +26.6656 0.0533629 0.2932 +22.5921 7.55176 0.423779 +30.5222 6.98101 0.060625 +10.637 3.26666 0.053666 +32.1211 4.69644 0.147284 +34.3855 4.61037 0.0439902 +14.5772 8.56234 0.945123 +32.3745 0.382131 0.581612 +25.6606 2.60175 0.457949 +8.94671 3.55738 0.0167524 +16.0746 6.88201 0.375757 +9.78039 6.38031 0.0470906 +31.2965 3.70018 0.414261 +9.08077 0.102194 0.0541612 +30.466 5.66303 0.138819 +10.64 3.555 0.0683917 +30.9313 1.02093 0.928278 +27.6706 5.51498 0.576262 +25.6794 3.88845 0.835915 +10.3669 2.67798 0.0365858 +25.1524 2.35236 0.297138 +22.1834 4.55197 0.373108 +12.9703 2.16462 0.0375454 +34.0484 3.43732 0.0974762 +33.0694 8.23277 0.060018 +23.0567 4.17556 0.372895 +31.4539 2.21547 0.648835 +12.5327 4.55576 0.0203944 +7.10811 8.01741 0.0803657 +12.5526 7.57102 0.235435 +20.2584 5.91916 0.59145 +31.2587 7.85775 0.057739 +29.1817 6.61304 0.0717816 +9.45077 5.94449 0.0566947 +28.5031 3.74123 0.953839 +30.6996 1.30575 0.952395 +20.7569 6.39798 0.924455 +17.8429 5.54346 0.122055 +17.0087 8.45926 0.639001 +8.01422 2.8055 0.0526714 +10.0042 7.6627 0.106637 +18.2215 1.9102 0.0403112 +18.4053 8.27029 0.555142 +19.0197 8.70329 0.134624 +16.2422 0.412258 0.0649158 +29.5363 2.8373 0.966533 +7.02442 8.72485 0.381867 +7.37362 8.46304 0.211615 +32.255 5.86127 0.0580174 +20.8234 1.17538 0.0352231 +21.884 0.0578376 0.0184245 +17.0668 2.17828 0.0640545 +15.63 8.25436 1.01228 +13.0673 7.03491 0.0907194 +19.1116 3.74508 0.0443305 +28.7878 6.65315 0.0975175 +2.95636 7.88592 0.0204034 +32.5943 2.62124 0.341167 +7.7637 7.64221 0.0556554 +17.6461 2.35576 0.0566841 +28.8007 2.87286 1.04648 +5.88106 0.390854 0.0348912 +22.9693 1.4 0.0352412 +21.4936 4.81455 0.319484 +19.7862 0.209233 0.0419749 +28.8957 4.77027 0.623684 +5.78696 1.48563 0.0454655 +8.76956 6.54411 0.0560174 +31.5864 1.11859 0.759741 +32.8261 1.8093 0.353421 +5.59818 2.35019 0.061141 +23.625 5.09297 0.869032 +4.72906 7.06617 0.0279903 +32.333 6.58306 0.057445 +32.6352 6.47842 0.0697358 +24.3024 5.95818 0.996406 +17.332 4.74891 0.0513603 +23.0036 2.99453 0.121534 +19.9809 1.57201 0.043348 +28.722 7.54911 0.0697387 +28.9977 3.57632 0.943521 +29.9158 5.68282 0.173238 +29.1614 0.596761 0.991037 +8.79087 3.15409 0.00569642 +8.29097 0.877633 0.0521435 +24.5049 3.09862 0.35025 +19.6918 6.40849 0.759522 +33.4547 2.98158 0.152798 +3.35637 5.96232 0.0378641 +14.8329 1.0765 0.0597875 +30.1308 3.36465 0.763564 +16.3153 3.5841 0.0563363 +5.92498 4.44907 0.0324683 +12.2957 1.99613 0.0537193 +33.2472 6.10644 0.0731936 +26.9556 7.71138 0.0610917 +29.1966 7.10239 0.0795643 +10.0897 0.269861 0.0625887 +27.9782 4.26611 0.967178 +1.6742 3.00661 0.0395333 +8.74017 5.00665 0.0398798 +22.1474 0.727956 0.0406569 +23.2271 6.37704 1.0116 +16.5341 3.64263 0.068367 +33.1429 8.24977 0.03406 +13.7024 0.166719 0.0303626 +10.4807 3.65108 0.051483 +8.23168 4.25376 0.0691862 +26.1461 7.26367 0.139081 +28.4389 6.61082 0.127221 +28.713 1.59087 0.984901 +22.0275 2.6896 0.0772778 +2.6505 6.43066 0.047589 +15.1729 3.66824 0.0567907 +7.0181 2.29903 0.0598424 +10.8433 8.13388 0.394619 +2.41043 7.36903 0.0746991 +24.4048 3.1061 0.309256 +9.75536 4.80297 0.0438411 +20.0687 8.12114 0.407843 +33.2437 1.89271 0.306428 +9.27919 7.94586 0.143738 +10.9342 7.73029 0.15536 +9.96029 6.34951 0.0508602 +3.68362 5.99616 0.0428462 +20.222 5.98713 0.596222 +19.718 4.75646 0.106231 +21.2684 5.0892 0.390114 +33.2716 1.72472 0.304921 +20.5282 4.91719 0.207345 +21.6297 5.88149 0.846845 +30.8854 4.24365 0.367038 +30.351 5.93695 0.124857 +18.7845 4.00461 0.0598527 +21.4924 7.02245 0.951356 +13.6143 6.30061 0.0366437 +17.9279 0.778248 0.0506117 +17.443 6.72377 0.500558 +19.0039 2.48919 0.0410708 +9.27515 0.711694 0.0393497 +21.3262 1.05948 0.0417024 +10.8653 2.83168 0.0519261 +11.7516 4.58537 0.059694 +11.6203 2.88598 0.0374861 +27.3968 1.7781 0.734416 +3.81543 6.72304 0.0708116 +34.3371 3.26681 0.0915919 +6.94605 7.63801 0.0565623 +21.9107 2.13693 0.0790058 +9.16259 6.02965 0.036121 +24.0959 3.12429 0.267831 +29.7632 7.42654 0.042324 +15.8195 5.64952 0.048376 +16.9706 5.93092 0.140021 +33.9615 0.811588 0.204596 +20.9465 6.56697 1.02128 +13.5831 3.09764 0.0509791 +9.09048 6.46929 0.0468661 +29.5154 7.38708 0.0748847 +24.5729 1.06978 0.0999215 +11.7734 0.134103 0.0370501 +18.2036 6.62453 0.570256 +6.03709 8.46495 0.105286 +27.1408 7.86933 0.0573175 +31.1967 3.80665 0.40243 +25.5149 7.28595 0.179467 +24.9888 0.20549 0.0915653 +5.79387 0.511331 0.0563017 +31.5634 5.082 0.134108 +21.1426 4.61071 0.196901 +31.0723 7.20947 0.0565826 +26.1268 2.04037 0.442337 +30.8158 4.04371 0.414084 +28.1711 8.64823 0.0661487 +25.6265 2.64624 0.461943 +21.2873 3.53493 0.0725876 +10.7071 5.30978 0.0638745 +9.78896 8.69718 0.861578 +31.6997 1.59718 0.668749 +27.8201 6.03908 0.321099 +9.82834 3.62007 0.0630063 +4.94156 3.50237 0.058982 +30.421 2.24456 0.926996 +1.94538 4.06777 0.0693976 +22.093 5.02384 0.493058 +24.8502 3.42823 0.518744 +15.5273 4.41987 0.0259724 +31.655 5.55864 0.0700271 +22.8227 7.4442 0.435156 +34.2531 4.90352 0.0641942 +24.3011 3.34364 0.342081 +19.9068 8.4035 0.239993 +4.15312 1.50787 0.0274151 +30.5812 0.397608 1.03692 +5.37326 7.93899 0.0441833 +10.5985 2.32353 0.0496209 +22.9012 1.32427 0.0405208 +24.0744 3.36738 0.322218 +32.0441 2.99688 0.411539 +32.7162 3.09975 0.260879 +31.9075 7.43874 0.0326895 +27.5691 2.53835 0.895087 +14.1042 1.15935 0.0474878 +25.3844 1.88508 0.286794 +6.63496 8.30033 0.0955549 +12.206 3.74808 0.0563549 +34.3793 3.15266 0.113275 +22.6025 6.79419 0.947585 +32.1484 8.17713 0.0594979 +6.97966 4.45921 0.0339293 +21.2613 7.11931 0.935353 +7.56753 2.31239 0.0752593 +24.8198 3.06288 0.400559 +32.0915 5.03807 0.0737143 +15.9043 2.06611 0.0422194 +2.07971 2.40538 0.0430128 +12.4788 1.63914 0.0447424 +4.36203 2.29263 0.0594506 +16.5957 7.33569 0.785712 +27.957 6.94648 0.116262 +33.891 0.995808 0.189415 +10.6775 3.0496 0.0542562 +6.42528 6.31243 0.0468105 +3.0587 7.514 0.0340016 +11.6539 4.89615 0.0624336 +10.0133 6.35056 0.0301846 +8.34194 2.07495 0.0291281 +30.6905 6.83305 0.0787383 +10.9362 7.17447 0.0729895 +6.73866 3.55716 0.0621808 +6.48093 2.06124 0.06954 +25.4357 0.289165 0.114391 +15.6999 5.50224 0.0782273 +14.3064 5.52837 0.0530929 +24.7541 0.0104554 0.0902129 +22.3461 4.5461 0.388343 +16.7437 8.20201 0.941715 +21.9511 0.252378 0.0446759 +11.6746 1.20088 0.049759 +15.7549 0.504839 0.0524324 +23.8037 3.81534 0.424203 +2.21911 3.36295 0.0169063 +14.9703 7.60525 0.699756 +14.1079 0.580606 0.0360687 +12.1634 1.75247 0.082671 +18.0352 8.19803 0.726262 +24.2414 0.166136 0.0939847 +25.8844 2.26502 0.425779 +26.9265 3.44578 0.995102 +21.966 0.00511966 0.0567182 +2.01175 2.51017 0.0563677 +9.64428 5.2696 0.0569572 +23.5404 5.37751 0.925831 +5.37286 5.32047 0.0278584 +20.6404 5.68826 0.516333 +2.0058 5.37048 0.0513696 +29.1859 0.302085 0.973245 +7.01407 5.72281 0.0624542 +15.8447 5.10239 0.0581056 +5.48492 1.90314 0.0687433 +7.32833 3.66667 0.0395098 +17.0433 2.07728 0.0553778 +18.7097 7.5863 1.02091 +29.9997 2.43669 0.946078 +8.34705 1.42375 0.0233981 +30.6079 6.96256 0.0519502 +9.32322 6.65233 0.0363237 +14.5018 7.44799 0.454617 +33.1492 5.92014 0.0388515 +18.4647 3.64318 0.0346962 +9.90031 4.156 0.0923235 +16.448 4.8524 0.0531768 +28.6425 5.20902 0.46849 +30.2276 8.26104 0.0468922 +5.22279 2.31971 0.0500958 +18.5124 6.71598 0.691611 +13.8865 0.14791 0.0656987 +4.24849 1.66266 0.0357817 +16.0011 8.25394 0.995851 +11.2316 2.19443 0.0426866 +25.87 4.46662 1.01163 +2.03663 8.01009 0.0983454 +16.4372 5.72096 0.0707077 +12.8677 4.26911 0.0264643 +15.3883 6.13393 0.0765477 +15.8026 8.55095 0.738224 +33.7979 8.2261 0.0658011 +24.123 1.58421 0.106501 +14.1819 1.2239 0.0314294 +12.6279 2.51668 0.0760058 +15.9192 1.26838 0.045855 +32.3201 6.67785 0.0306517 +21.1926 7.2872 0.884247 +32.9726 1.22981 0.394408 +8.28126 0.639491 0.0339371 +18.6936 2.82955 0.0323796 +25.301 6.00369 0.823518 +28.7306 6.73176 0.0800769 +4.92592 4.02646 0.0575636 +14.8962 5.58532 0.0575818 +30.2825 8.01933 0.0312584 +21.7499 7.69313 0.449092 +6.18241 1.02881 0.0454586 +15.3778 6.23366 0.114234 +18.0597 6.78738 0.660143 +1.56173 7.92972 0.0583313 +15.3913 3.65435 0.0394493 +17.1476 1.36386 0.0558149 +33.2673 4.14087 0.096549 +18.5928 3.23591 0.0561177 +18.8919 3.44111 0.0370849 +4.02291 7.01613 0.0378252 +2.73447 5.69736 0.0784914 +16.6779 5.94442 0.128217 +22.3062 5.70455 0.86914 +8.69694 1.23827 0.0669414 +20.4193 0.510068 0.0465352 +4.64632 3.45277 0.0695979 +10.5439 7.42658 0.0594911 +18.7128 5.6696 0.215171 +11.6411 6.91644 0.0551531 +16.1767 5.44038 0.0493177 +25.0181 6.85509 0.400714 +7.38937 8.70648 0.451062 +23.1359 6.23421 1.03442 +33.2259 7.53115 0.0442488 +28.725 8.50286 0.0239175 +13.882 6.47285 0.0899146 +22.579 7.10548 0.748168 +5.22425 0.686802 0.0465662 +25.3164 0.318033 0.102608 +26.1858 8.07053 0.0629716 +8.79608 2.07762 0.0491848 +14.0371 2.66857 0.0270358 +21.4571 1.18188 0.0816379 +23.2163 5.54589 0.981554 +21.9691 0.259466 0.0345948 +26.2059 6.98405 0.189978 +29.5709 2.60788 1.01623 +16.9956 6.50145 0.324088 +1.9978 7.95957 0.0584154 +28.1051 6.03328 0.290328 +5.10794 3.7106 0.043797 +18.9925 5.46155 0.221898 +25.0497 7.47515 0.154056 +28.1399 6.21645 0.227049 +19.0028 2.20472 0.0556794 +22.3893 0.0666054 0.0463318 +33.3292 7.68909 0.061698 +26.9155 5.44843 0.779299 +10.8797 0.554975 0.0160797 +32.3473 4.81535 0.103689 +23.8776 5.14801 0.958445 +11.471 2.56397 0.0714016 +10.6101 7.7439 0.141105 +16.1172 2.09546 0.0602632 +18.3139 1.25717 0.0529354 +29.3826 2.70351 1.01911 +16.5697 2.9575 0.0405026 +24.9688 7.7268 0.122256 +12.5574 5.74275 0.0687064 +16.9788 1.06574 0.074412 +12.2016 3.44066 0.0541945 +14.4422 0.898106 0.0522549 +24.5749 6.16804 0.900485 +9.14602 7.47225 0.111045 +17.6527 0.817159 0.0467552 +25.985 4.36904 1.01882 +1.83683 2.13795 0.0771355 +14.9916 2.49128 0.0331047 +26.9977 6.52701 0.226032 +23.1599 8.53924 0.0742579 +28.3928 2.43765 1.03954 +9.98368 4.742 0.0706127 +21.7772 2.71213 0.0804175 +11.6697 0.543097 0.0672015 +4.28539 5.26558 0.0399915 +27.4376 1.35779 0.658028 +11.3602 6.01021 0.0356008 +25.6862 6.53064 0.447301 +26.9463 1.24409 0.487714 +28.0961 5.21152 0.583825 +11.0702 1.8211 0.0337129 +27.9563 6.66981 0.127471 +9.56303 2.49908 0.0440713 +23.4332 6.02687 1.04905 +1.78481 5.66258 0.0348078 +27.4184 5.02712 0.815025 +3.57884 1.90223 0.0479386 +25.2032 7.2916 0.204186 +22.3752 3.10117 0.109117 +3.5689 6.73471 0.0619618 +20.7383 4.15126 0.111031 +24.807 3.91621 0.67584 +28.1509 6.22482 0.230856 +33.8845 2.97911 0.136415 +9.41253 6.67267 0.0533808 +15.5502 3.88861 0.0364626 +13.604 6.47232 0.0720731 +23.352 6.09656 1.05815 +7.76587 8.26215 0.131491 +19.9669 3.57154 0.0784415 +9.36681 3.05017 0.0424446 +8.36544 8.69481 0.592244 +26.3907 4.51288 1.07574 +19.5964 3.65741 0.0599682 +27.1911 6.12439 0.385398 +23.367 2.18614 0.099982 +29.5736 8.37074 0.0442434 +17.2157 6.58866 0.370999 +1.91751 5.67256 0.0661163 +34.4919 2.55076 0.0837117 +9.07825 7.07351 0.0655501 +28.6805 8.0332 0.0388089 +8.07666 6.91692 0.0646033 +18.3503 7.97341 0.855929 +11.0268 0.80575 0.0779926 +23.1807 6.4783 0.958578 +4.11762 7.57152 0.0161413 +3.87867 4.16172 0.0946765 +16.7328 0.700446 0.0506782 +29.5166 4.74212 0.475134 +16.8785 2.11979 0.0500269 +5.39322 0.284948 0.0452996 +34.2682 8.17886 0.0546622 +4.98896 7.2744 0.0386488 +11.7372 5.54521 0.0833601 +20.1706 6.54196 0.906658 +10.3909 5.86843 0.0600171 +24.5746 4.04517 0.683224 +15.3356 1.05991 0.0467602 +27.7255 2.52361 0.943906 +4.22197 4.42305 0.041907 +28.6402 5.08742 0.520716 +31.2919 2.63516 0.623941 +9.88274 1.1119 0.0666446 +9.33937 2.9921 0.0660982 +30.611 1.62245 0.931838 +15.1525 8.13325 1.05962 +14.6269 1.93121 0.0363141 +20.7871 3.4441 0.069586 +19.6146 4.73588 0.121134 +28.3229 2.20506 0.952938 +23.6018 4.32544 0.558869 +24.7865 4.83075 0.97384 +15.7041 0.429184 0.0274972 +18.4528 8.28043 0.568419 +29.7141 4.70188 0.454211 +30.0961 1.27648 1.04133 +21.6797 8.58633 0.0712833 +21.0055 1.47044 0.0401714 +11.5044 6.35792 0.0309956 +17.3506 4.21656 0.047678 +7.97557 2.25099 0.0338118 +17.5297 3.31788 0.0431475 +3.47916 4.20565 0.0559301 +10.0048 3.24184 0.0618883 +10.9153 2.15897 0.0562157 +17.1317 3.63656 0.0542495 +24.0105 3.80892 0.444462 +4.7271 6.01094 0.0641403 +22.6428 1.72661 0.0774054 +27.0826 0.397489 0.415228 +6.97146 7.28714 0.043447 +6.08603 0.605629 0.0348343 +4.84511 5.51873 0.0452061 +22.3689 4.79789 0.471651 +14.5822 8.52177 1.00022 +2.76955 1.65997 0.07037 +5.34218 7.75829 0.0298126 +13.4943 1.13878 0.0615004 +3.2185 1.49896 0.0460735 +27.6672 5.71423 0.451586 +7.88927 1.44793 0.0535671 +28.2421 5.1261 0.595619 +2.66632 5.89963 0.0543801 +12.6535 1.09629 0.0576154 +32.1789 6.90598 0.0404195 +6.50717 3.69781 0.0724236 +2.70622 8.45209 0.0550005 +29.1439 0.0999511 0.951771 +27.7952 0.686942 0.624145 +17.881 5.41998 0.137715 +5.66317 2.248 0.0374984 +11.176 3.51949 0.0506474 +16.1653 7.94362 1.04985 +33.2792 3.00517 0.205983 +8.55207 0.447902 0.0290951 +23.3046 1.25006 0.0256114 +24.8316 4.85565 0.991402 +30.227 6.77154 0.0359974 +23.7446 6.62292 0.799189 +13.8642 4.12134 0.0808954 +16.0934 2.09438 0.040463 +17.1836 1.12316 0.0597606 +20.9237 1.95126 0.0336511 +19.0582 1.75127 0.0949224 +4.88545 7.93085 0.0449015 +27.0127 3.56376 1.00699 +13.5262 1.01983 0.0473161 +22.1186 5.57742 0.784825 +11.7639 3.99624 0.0656936 +16.2641 0.254152 0.0458755 +34.3623 8.56311 0.035367 +1.96361 8.27061 0.0415007 +22.6034 2.29503 0.0654033 +7.72303 7.28033 0.0515066 +33.007 3.97762 0.134747 +28.5804 5.88788 0.250507 +28.9584 5.71031 0.256199 +29.1405 6.39673 0.141534 +12.8469 3.84993 0.045024 +18.2059 1.28111 0.0696108 +31.9341 7.02958 0.0426825 +17.0482 3.84928 0.054509 +32.5133 3.86357 0.177985 +28.356 6.63591 0.0874326 +17.6118 5.99025 0.184493 +18.8919 8.64368 0.206022 +18.6579 3.39043 0.0687569 +10.3696 5.5487 0.0648986 +13.0469 0.732131 0.040044 +15.6098 0.983699 0.0434411 +19.0932 2.22608 0.0309922 +30.4034 2.44673 0.887988 +17.4917 3.89979 0.0614115 +19.9327 1.99983 0.0519622 +24.8309 8.18185 0.048337 +27.8801 3.74067 1.04732 +33.5996 3.34176 0.136061 +25.8803 7.88378 0.0709084 +21.2589 6.95456 1.00929 +25.1629 6.73136 0.461744 +5.53369 7.98922 0.0407695 +25.8932 6.43761 0.465498 +8.73193 2.67109 0.00971444 +30.5536 8.40203 0.0516505 +30.2828 0.237264 1.04062 +22.2327 7.84066 0.278641 +32.4571 3.6657 0.242187 +3.18733 2.95119 0.0671202 +4.00535 4.1478 0.074766 +17.8631 3.49428 0.0472552 +8.04436 7.52026 0.0490254 +2.23956 7.04536 0.0537994 +33.167 6.52581 0.0636112 +15.3241 2.03386 0.0718816 +29.2207 3.84466 0.81898 +10.9212 0.655895 0.0463473 +22.3558 6.48949 1.00667 +3.78129 0.368396 0.0449505 +21.933 3.46057 0.120021 +24.8038 1.32073 0.141799 +21.8675 7.44703 0.663538 +17.3723 8.02799 0.975693 +25.8689 2.53032 0.481012 +8.93456 7.62477 0.053885 +23.7674 1.55603 0.085976 +10.9686 6.21536 0.0747031 +3.65187 2.19081 0.029943 +13.8565 3.89388 0.0345096 +26.3326 5.34022 0.911998 +6.96513 3.06904 0.0439561 +27.0753 1.72917 0.624036 +13.2972 5.13786 0.042148 +17.7348 6.93918 0.710052 +3.79355 8.70344 0.092111 +7.02134 2.81703 0.0524825 +23.4822 2.25157 0.122478 +31.663 0.668012 0.73922 +10.3095 1.7892 0.0226673 +24.2148 0.226658 0.0440284 +30.5337 2.18231 0.892121 +29.4226 6.34511 0.104759 +32.9013 1.00247 0.41764 +5.06584 5.64905 0.0539452 +23.5591 2.57736 0.133672 +11.121 2.00024 0.0219244 +2.90802 4.22656 0.0390699 +17.9717 3.81273 0.0567476 +33.096 3.90491 0.113108 +12.8886 1.02774 0.0417262 +10.1376 4.93464 0.042496 +34.2 7.88441 0.0566318 +23.6621 0.0811883 0.0426918 +6.68608 0.707897 0.023514 +2.18266 5.12122 0.043489 +4.21508 2.78423 0.0446997 +4.21983 2.20174 0.0618495 +30.7585 8.63475 0.0398946 +24.692 2.69356 0.280946 +7.49582 8.04829 0.0710272 +15.8044 5.39101 0.0480132 +16.3237 0.264739 0.0807973 +30.0019 2.32392 0.93706 +33.6242 0.661933 0.269646 +1.78388 4.70163 0.0369115 +2.38924 7.93881 0.050295 +21.7326 0.459915 0.0258981 +3.30419 0.244958 0.0521109 +15.1766 0.256309 0.0366836 +22.5416 2.38588 0.0796401 +18.534 1.45272 0.0698987 +7.62928 8.46278 0.211179 +10.9705 6.77391 0.0280831 +19.3113 8.20933 0.48904 +6.53371 5.0679 0.0789173 +32.1792 7.17609 0.0494356 +9.86815 6.19552 0.0174791 +29.3749 2.55307 1.0051 +7.96789 5.70439 0.0636936 +17.9808 2.5281 0.075963 +28.5484 4.05749 0.90798 +23.4222 7.19052 0.487844 +5.95592 1.92895 0.0667089 +10.0287 1.52866 0.0282862 +14.1399 7.4428 0.385968 +18.1825 4.04838 0.0420427 +33.372 1.51925 0.279103 +13.0196 2.7779 0.0573705 +13.4122 2.45217 0.034255 +19.2334 7.83295 0.825265 +9.02844 4.13204 0.065738 +28.9086 8.04244 0.0660213 +1.50628 4.11025 0.0697057 +26.2861 8.21044 0.0456744 +31.6505 8.01059 0.0229835 +12.4269 4.96063 0.0520129 +26.728 6.8728 0.188719 +25.4718 2.50965 0.39747 +27.1916 1.00923 0.513696 +23.4635 5.28093 0.924777 +19.542 0.482368 0.0334461 +10.8656 4.08594 0.0616469 +15.5226 5.41899 0.0811831 +5.31257 6.70861 0.0513042 +22.4753 7.83679 0.266702 +18.4828 1.78366 0.0590052 +3.68398 7.56412 0.0577018 +5.16306 4.33756 0.0580173 +12.3228 5.44313 0.0524833 +7.981 4.03785 0.0450295 +7.98784 7.40965 0.0756275 +27.0424 2.622 0.785287 +12.3271 0.0710184 0.0664854 +3.63768 7.24727 0.0362405 +31.8588 5.78276 0.0750921 +32.9688 6.26226 0.0365238 +28.7763 1.08545 0.918762 +16.4234 1.80188 0.0334945 +33.3231 5.46629 0.0418562 +31.4622 6.53022 0.0744333 +22.8696 5.77845 0.986845 +24.5023 5.209 1.01632 +25.315 5.72286 0.924763 +19.5625 1.29194 0.0486924 +30.5611 3.93926 0.498618 +30.0088 2.87988 0.899951 +26.317 6.43434 0.393039 +24.8466 4.52216 0.877754 +16.8592 6.29328 0.207099 +12.6308 6.0728 0.063827 +33.3899 7.91152 0.0604351 +20.6115 7.48338 0.823955 +32.5115 0.451212 0.509815 +8.83435 5.83604 0.0541316 +6.96565 3.26413 0.0402374 +25.8315 0.881486 0.193311 +23.5176 4.18051 0.477177 +15.2167 7.3698 0.528403 +18.5863 6.9606 0.886327 +32.1653 0.924712 0.619919 +23.9602 1.76837 0.0925664 +19.656 6.07377 0.531189 +10.7565 8.46203 0.712506 +7.09052 0.674568 0.0608592 +14.0229 0.775638 0.0505871 +11.5563 6.07975 0.0612895 +24.8207 5.38359 1.04316 +34.2532 7.08888 0.0603704 +29.901 1.73287 1.02587 +10.8058 4.3198 0.0394174 +11.4274 6.99992 0.0630875 +21.241 3.34201 0.0704105 +29.5211 3.49249 0.857725 +11.5631 0.28696 0.0499472 +10.7653 6.97298 0.0701747 +17.2941 3.90332 0.0394511 +26.9544 3.30369 0.917272 +16.5168 5.72829 0.0888733 +1.92964 7.45472 0.0777964 +24.1603 7.57111 0.217985 +30.8066 2.993 0.664092 +24.3347 5.31639 1.03827 +2.60389 3.38645 0.0661351 +10.4518 7.33154 0.0826695 +18.7809 6.93584 0.92744 +17.8763 5.78359 0.177896 +4.11533 7.19764 0.00693119 +10.8944 4.45573 0.0579807 +14.8673 4.99083 0.0380588 +18.119 1.42145 0.0352124 +33.9549 6.18722 0.0238333 +25.1694 2.5186 0.314777 +18.9197 4.91031 0.0894823 +1.80722 0.0181035 0.0658652 +2.56413 3.47337 0.0665909 +27.0492 5.38222 0.765831 +20.1852 5.51249 0.387682 +23.7832 4.92715 0.834257 +5.73982 2.20264 0.0364586 +33.7083 5.71928 0.0810688 +18.4649 5.77912 0.228872 +20.4478 5.1561 0.271399 +23.9995 3.29553 0.310889 +22.381 1.32925 0.0417557 +27.4661 4.86472 0.895931 +8.42867 0.453608 0.0293583 +3.54604 8.00778 0.0414569 +15.4472 3.08165 0.0464641 +27.2364 2.47268 0.811531 +26.3985 3.93898 0.971054 +14.3374 0.907541 0.0624026 +26.1448 7.72769 0.0803514 +27.0886 8.56145 0.0502959 +27.7398 7.19519 0.0869707 +33.2176 5.01475 0.0607863 +16.7505 8.22197 0.93028 +25.5925 2.88779 0.508933 +31.7105 4.06511 0.242544 +11.0989 1.49924 0.0466915 +33.9939 3.71203 0.0898619 +12.6537 8.1072 0.70118 +23.4464 5.7979 1.05354 +6.05641 4.76929 0.0752869 +7.77884 4.68447 0.0292123 +4.51407 5.55031 0.0252152 +22.559 5.33055 0.770629 +2.36443 3.95501 0.0695158 +17.4449 4.46474 0.0684411 +26.0472 0.885722 0.248168 +10.4548 3.8757 0.046137 +7.87042 5.8973 0.0453788 +33.27 1.95203 0.308875 +8.20048 2.42029 0.0467349 +20.8244 7.03416 1.03144 +16.7995 1.18764 0.0313392 +31.4115 1.70834 0.764295 +22.3194 7.1536 0.748102 +17.4178 1.05709 0.0460351 +9.8808 2.62652 0.0455296 +29.141 7.95654 0.0484039 +11.8478 3.60903 0.0912311 +28.2646 7.14485 0.0802955 +24.5528 6.68234 0.617176 +19.7648 0.174562 0.0386611 +34.3757 1.91936 0.120101 +22.7766 7.96975 0.144603 +21.2434 2.80821 0.0502115 +24.8107 3.53006 0.535591 +7.02046 8.17012 0.0889551 +15.9923 0.958692 0.0281684 +1.50806 8.49241 0.0612738 +5.99428 5.65233 0.0417416 +2.64906 5.49331 0.0673692 +20.4325 0.00739173 0.0463876 +12.2505 5.51252 0.0510375 +7.21266 7.1855 0.0394706 +14.6866 3.44417 0.039918 +32.8518 0.638941 0.429225 +3.34893 1.15406 0.0590268 +5.18497 0.678113 0.0612353 +30.4304 3.62693 0.60594 +8.68513 7.14116 0.0329061 +23.4835 0.512979 0.0644849 +4.40104 3.54629 0.0697585 +2.72647 3.50328 0.042939 +21.8699 3.47914 0.09925 +25.987 4.60364 1.03915 +8.16168 4.6782 0.0554605 +16.787 1.78349 0.064841 +6.76784 6.94349 0.0362969 +28.8374 4.51888 0.712806 +5.94929 3.0772 0.0720862 +20.0709 8.42641 0.21841 +32.7015 6.169 0.0419925 +8.47345 1.42485 0.028617 +25.5573 8.4704 0.0521992 +17.7913 5.05397 0.0923191 +29.9065 2.97402 0.90773 +30.0186 2.0752 1.02169 +17.9781 0.59295 0.0451217 +17.0233 2.13197 0.0515887 +26.881 2.52604 0.729668 +25.034 3.00771 0.433829 +12.3926 8.63488 1.04715 +32.655 5.17797 0.063927 +18.1197 1.67957 0.0449341 +9.63914 3.23894 0.059257 +14.5507 3.77899 0.0640064 +14.7417 0.726715 0.062605 +17.9822 4.09378 0.0881374 +12.8859 4.44245 0.0622497 +16.6288 7.95001 1.03502 +33.6237 3.78641 0.124895 +34.2656 6.01898 0.0388664 +9.95312 5.94026 0.0345402 +11.6898 1.52915 0.0618621 +13.9712 4.95715 0.0423723 +9.12855 3.92449 0.0511952 +8.82624 1.42168 0.0435198 +17.5125 0.20916 0.048294 +15.2538 8.70362 0.641359 +4.06255 0.791065 0.0646428 +18.6868 7.74897 0.983634 +17.1332 3.20353 0.0476312 +32.9122 0.714081 0.432952 +29.6109 8.43077 0.0641826 +22.687 5.77073 0.991904 +18.7795 3.53565 0.0750716 +5.22287 7.32375 0.0329716 +22.7647 6.18541 1.06356 +32.2913 3.63196 0.246126 +33.3796 6.27406 0.0637914 +32.1655 6.44868 0.0501649 +25.8625 1.50492 0.321472 +12.8668 6.26098 0.0804419 +7.48516 8.01078 0.0762531 +8.84333 6.01001 0.0581682 +14.5123 6.98633 0.213297 +5.33438 8.6524 0.153779 +11.5807 6.9602 0.0795393 +33.5862 4.10578 0.0800755 +26.1244 4.91326 1.01678 +24.7116 6.70201 0.581859 +6.717 5.52665 0.0567453 +18.2202 0.61981 0.044781 +17.7181 0.525776 0.0414233 +32.0489 2.38294 0.470482 +23.609 7.15165 0.496904 +11.4558 1.93679 0.025635 +10.0539 2.78814 0.053604 +5.0333 7.19408 0.0557248 +17.9617 1.12687 0.0413369 +22.2094 2.44574 0.0827441 +14.6689 2.24112 0.0626607 +32.4762 0.413979 0.532079 +26.3236 6.49054 0.367554 +19.6401 2.80219 0.0422485 +14.5665 3.56254 0.0369293 +23.177 2.9107 0.107637 +24.309 0.575272 0.0769366 +25.7731 7.38392 0.144367 +11.8606 1.1755 0.0279922 +14.8008 2.49548 0.0561149 +9.26796 6.29999 0.0470207 +6.04908 3.04472 0.0523516 +11.7033 4.4295 0.061405 +10.0773 2.88586 0.0644295 +10.8614 4.8959 0.0403003 +17.316 2.77334 0.0617393 +14.1637 3.98913 0.0441814 +25.6709 0.0777011 0.110664 +10.0017 0.132848 0.081015 +25.1013 4.43025 0.914495 +30.8663 7.14078 0.0468926 +17.9931 8.72791 0.24448 +28.1087 7.01961 0.0910705 +4.05886 4.58343 0.0418161 +10.1348 5.63152 0.053458 +4.36314 5.8681 0.0551647 +17.7818 6.37686 0.364378 +14.0123 8.66863 0.883061 +17.4292 3.00373 0.0525239 +9.11632 3.9011 0.0360195 +2.64801 6.80826 0.0344058 +26.5643 1.88803 0.51134 +32.4522 0.838605 0.563599 +7.80525 7.85668 0.0484182 +25.869 0.13942 0.178901 +13.0908 7.81299 0.479043 +25.9704 7.99551 0.0347371 +21.3513 1.44743 0.0454654 +26.4351 6.60021 0.32707 +34.1767 2.52436 0.140268 +10.8075 2.36928 0.0543539 +30.5231 8.59689 0.0685385 +8.28196 7.75214 0.0731495 +27.7373 6.68051 0.14102 +6.70925 3.81132 0.0357048 +8.11907 8.6165 0.430949 +15.6907 4.98605 0.0569111 +8.34019 8.56559 0.409701 +28.1391 6.30492 0.197185 +2.73769 0.409142 0.0415974 +12.9174 6.97633 0.0865696 +20.3561 7.32393 0.953009 +27.9224 7.70071 0.042335 +33.6934 5.43674 0.0820863 +19.1978 0.328758 0.0663178 +7.85416 7.58179 0.0755776 +9.05546 3.68816 0.0518523 +21.6584 1.71812 0.0505629 +6.36045 1.82212 0.0623679 +29.441 1.76166 1.04426 +29.2949 7.12879 0.0754473 +25.4056 0.635274 0.148625 +33.5494 8.0254 0.0620903 +16.3558 0.147455 0.0384032 +33.1712 6.31808 0.078736 +8.09237 2.01524 0.0532628 +9.96937 4.49932 0.060198 +19.0372 1.54958 0.0389072 +30.8027 7.86333 0.0397946 +1.81122 1.36252 0.0607139 +29.7504 4.08015 0.618617 +13.8093 5.31893 0.0692794 +29.9838 3.96104 0.616705 +29.7909 1.88953 1.05013 +30.7732 2.53445 0.775746 +20.1054 8.16759 0.377408 +5.47107 1.09448 0.0514164 +11.4492 0.153615 0.0639612 +13.1915 7.94219 0.624826 +22.95 7.74557 0.26498 +12.6999 5.98835 0.0557542 +17.6913 4.12496 0.0634709 +13.4199 8.31996 0.984866 +21.9931 3.61078 0.125723 +6.54332 0.11341 0.0390463 +14.3349 3.80377 0.0443979 +30.4995 4.62531 0.343545 +10.9312 4.79216 0.0680316 +17.5705 7.76261 1.03225 +27.1653 0.591979 0.460531 +24.3969 7.46523 0.218317 +16.8917 4.56489 0.0378434 +3.93691 6.39929 0.0442145 +30.1195 3.61606 0.677588 +31.8591 2.58837 0.512195 +27.5994 2.93402 0.964681 +26.7634 2.39917 0.700961 +30.6935 3.96409 0.434907 +30.3325 5.22525 0.221461 +17.1652 7.26245 0.837337 +14.4365 1.5245 0.0402206 +16.5323 3.93361 0.0245799 +5.90894 4.65047 0.0526544 +7.83637 7.15085 0.0320345 +24.2138 6.20725 0.941824 +25.8431 5.44659 0.955003 +24.3582 5.8938 1.00874 +20.6035 3.91693 0.084716 +6.60856 7.04893 0.0612823 +13.9644 7.31856 0.29457 +32.881 4.94882 0.0868524 +32.9369 1.02269 0.406607 +11.2334 3.72523 0.0420596 +13.8569 2.31357 0.0664598 +7.59307 1.67629 0.0738896 +13.3509 2.59313 0.0665179 +16.2739 7.16104 0.582315 +10.8153 3.0397 0.0600571 +31.4299 3.75523 0.35033 +4.0401 3.34549 0.0374745 +15.9744 1.61339 0.0365978 +3.81124 4.99376 0.0410515 +13.3539 3.64225 0.0622815 +26.6928 8.18579 0.0390911 +4.82574 5.40533 0.0402023 +20.6234 8.5476 0.0964352 +20.3893 7.50667 0.851666 +20.2645 4.28931 0.0719907 +18.0781 0.143314 0.0630372 +21.9129 1.86104 0.0516304 +10.217 1.043 0.0635557 +10.2216 6.44634 0.0704167 +27.1068 1.20399 0.533169 +7.25295 5.63843 0.0546691 +28.685 2.81897 1.05154 +31.122 4.19974 0.317498 +20.2874 6.61345 0.93276 +10.7455 2.60748 0.0523992 +15.9127 1.50086 0.0392329 +18.3872 2.51746 0.0348692 +24.4968 8.24728 0.0525257 +32.5054 4.83177 0.0976518 +15.5845 2.50946 0.0600094 +8.2762 1.27356 0.034846 +11.6981 0.596255 0.0308548 +32.8175 2.91922 0.279501 +8.50569 5.36646 0.0579173 +9.83935 3.99261 0.061561 +2.55631 7.09881 0.0455899 +21.7083 8.69373 0.0425921 +17.0635 4.98382 0.0776034 +28.7125 3.01472 1.06244 +22.6735 6.94582 0.806918 +23.0503 0.775367 0.0611093 +24.091 6.63859 0.742819 +16.6109 7.70576 1.04331 +11.2892 6.78296 0.0730041 +2.11522 0.862364 0.0580699 +14.5927 5.07846 0.0470845 +1.91849 7.01122 0.0430679 +8.54439 3.59724 0.0489913 +13.9324 7.37698 0.313649 +18.7302 1.45089 0.0594196 +14.0538 4.62117 0.0521279 +8.61167 4.87862 0.0465187 +26.3549 6.88934 0.20263 +11.9707 3.60107 0.051738 +13.1751 2.25713 0.0450953 +8.86768 6.22396 0.0559336 +3.8326 0.998465 0.0360287 +26.4266 3.21506 0.826947 +16.5109 4.09068 0.0487076 +19.748 6.88534 1.0331 +30.2757 3.71245 0.618201 +30.6217 4.91356 0.271982 +20.6049 5.9285 0.707997 +32.7269 5.90435 0.0557082 +19.4056 8.42112 0.315487 +30.5763 4.04821 0.484164 +17.0221 4.99725 0.0407097 +10.2737 0.169611 0.0424472 +17.9561 1.67681 0.0660278 +6.812 2.17433 0.0555548 +14.0643 1.84584 0.0640937 +9.07172 4.53702 0.0574919 +16.4415 1.74211 0.0729419 +4.54844 4.98217 0.0475954 +15.8429 7.21785 0.524783 +20.318 0.634781 0.041249 +34.1563 2.12294 0.155317 +34.4002 4.3412 0.0516277 +23.2445 0.403387 0.0555106 +16.5051 1.25207 0.0479918 +26.2301 5.02529 1.00764 +31.934 3.4917 0.326652 +24.3038 7.89845 0.0997208 +7.81593 4.98564 0.0658345 +21.5996 6.61692 1.05227 +17.9383 3.02291 0.0626518 +31.7138 3.29565 0.384741 +23.4814 7.45915 0.337585 +33.5107 5.37767 0.0511026 +3.02941 7.99247 0.0221351 +24.542 2.20603 0.218249 +8.67606 1.00169 0.0314025 +16.9377 5.91908 0.138901 +29.3512 1.09337 1.04087 +34.3403 0.664091 0.177657 +8.8299 3.41224 0.0444938 +29.9062 6 0.0831891 +5.83834 3.14955 0.0604836 +11.5763 6.5818 0.052787 +8.68234 3.97927 0.0609319 +33.7854 2.49014 0.17301 +5.01485 4.6696 0.0704201 +27.9558 5.28347 0.612141 +18.7772 1.31047 0.0459717 +25.2972 5.17023 1.08602 +4.36517 1.4728 0.0423603 +16.727 8.46183 0.654372 +19.7904 4.64561 0.112524 +11.1086 1.79552 0.0584536 +22.9609 0.371741 0.0627566 +4.24642 1.87506 0.0432874 +4.1557 2.78196 0.0510994 +23.0184 5.01222 0.705405 +10.2123 5.79932 0.0451348 +29.8283 6.2193 0.110767 +20.1629 3.8553 0.0620428 +30.582 5.50793 0.154288 +29.55 4.8381 0.435912 +24.7983 2.59124 0.285866 +33.8767 1.91392 0.190611 +9.63636 3.48956 0.0683985 +20.0532 6.02731 0.607736 +5.76015 1.17545 0.0395836 +24.9603 5.55846 1.02836 +28.5648 0.181217 0.848614 +31.0513 1.60542 0.857892 +18.979 5.85402 0.303049 +32.0648 7.06269 0.0403428 +14.0775 6.70531 0.0917868 +13.195 5.97374 0.073478 +25.7365 2.22073 0.390859 +31.8402 8.63376 0.0641158 +4.14615 5.25247 0.0633392 +18.3047 7.95449 0.881465 +16.2334 8.18459 1.0197 +2.59353 2.086 0.0492566 +5.14627 8.47612 0.091065 +28.5741 0.709764 0.836768 +32.737 5.57053 0.0607353 +20.3148 2.24345 0.0452808 +9.4375 0.822319 0.0391724 +33.8297 4.97687 0.0638177 +15.4674 1.85773 0.0315546 +5.89637 6.87314 0.0464229 +27.4968 4.03434 1.00388 +24.5947 2.35779 0.210505 +24.2819 0.103014 0.057703 +14.1626 6.04814 0.0841221 +10.8138 5.56296 0.0529234 +9.04924 7.03742 0.0602484 +21.3868 3.34919 0.0338084 +11.8538 7.99629 0.436531 +25.4662 0.206203 0.115856 +31.6658 8.03047 0.0695915 +29.4148 6.68358 0.089669 +2.6339 0.894737 0.0517461 +22.8419 8.62764 0.0595276 +3.20956 0.784606 0.0514453 +11.3046 6.56438 0.0512353 +11.3466 2.1359 0.059538 +19.8339 6.0392 0.574761 +2.93212 7.31559 0.0475713 +9.07922 1.03078 0.0389701 +32.3327 2.83639 0.383912 +2.48019 7.82397 0.0434796 +12.862 4.1732 0.0390096 +17.6769 1.33795 0.0845952 +5.08805 0.759131 0.0515546 +11.1304 1.07503 0.0477562 +22.1059 0.924672 0.031189 +12.4054 3.11908 0.0345682 +19.9177 6.78739 1.00877 +32.6548 3.95852 0.159442 +4.68242 0.85957 0.0675057 +23.6439 3.08166 0.189855 +2.31575 4.22757 0.0627225 +14.3567 3.91723 0.0528116 +25.1976 5.60462 0.986794 +33.0637 5.94839 0.0278009 +16.7811 6.53556 0.312917 +22.2633 4.68349 0.414537 +17.5965 0.261735 0.0507078 +5.41297 6.72665 0.0643859 +4.4726 6.84454 0.0361574 +19.8456 6.37504 0.789708 +30.3551 3.82426 0.592399 +9.95905 7.75144 0.122978 +26.1647 0.841925 0.27674 +22.2248 6.46897 1.03788 +12.6146 5.21177 0.0491041 +23.1858 6.11926 1.05486 +3.60754 0.58784 0.0439963 +9.90897 8.46157 0.588993 +14.0865 0.264236 0.0639677 +22.6898 1.54592 0.080662 +2.69504 4.51523 0.0291826 +2.89738 5.00714 0.074771 +32.0922 1.55514 0.578359 +18.9738 2.51213 0.0403606 +31.0904 7.99836 0.0473225 +34.3945 2.80835 0.132302 +7.0008 3.89118 0.0585422 +13.9593 0.974619 0.0576772 +34.0584 4.97253 0.0751298 +1.75281 6.12609 0.0678329 +33.9142 6.47525 0.0691751 +34.4903 2.03303 0.14446 +26.4088 3.2272 0.822361 +14.3513 5.71949 0.06273 +23.8385 8.50359 0.0369804 +2.18734 6.7281 0.049334 +13.1194 1.91624 0.0187098 +2.68802 0.572604 0.0501441 +15.615 2.86783 0.0530386 +24.0562 4.62647 0.766238 +2.15593 2.37749 0.0436453 +31.0542 6.90363 0.0696654 +4.43901 2.24214 0.0461476 +9.30884 1.08277 0.0516209 +14.346 5.26533 0.0804144 +5.19487 6.64361 0.0561815 +26.1881 1.79139 0.416573 +23.926 2.91731 0.221479 +20.9552 2.27055 0.0618736 +15.6712 0.251561 0.0681208 +21.1517 5.96748 0.796544 +13.9062 0.444202 0.0623095 +23.4714 5.34829 0.937437 +31.0322 5.48118 0.111858 +27.0141 4.63369 1.01406 +13.5987 0.371155 0.0417928 +30.7262 0.326456 0.948058 +11.8034 4.50138 0.04735 +23.9215 4.28781 0.626577 +25.9007 0.135297 0.163661 +15.0723 0.0940753 0.0594025 +29.9073 6.75913 0.0581314 +33.6279 2.83597 0.146307 +5.46478 6.26595 0.0457303 +3.0031 1.73565 0.0163597 +11.1938 2.32167 0.068086 +2.36681 7.85035 0.0445308 +34.0737 1.72806 0.183568 +10.3801 3.56835 0.0501036 +6.66557 4.35808 0.0280605 +20.8279 6.22188 0.863293 +33.0278 2.80336 0.262553 +30.2945 5.67496 0.15129 +30.3909 1.94234 0.948893 +8.02612 5.95492 0.0392179 +10.3485 0.185481 0.0488158 +4.41027 1.63276 0.0306341 +24.5488 0.533266 0.0877171 +13.2456 0.891647 0.0255882 +23.381 6.53941 0.899987 +2.59477 4.0559 0.062365 +25.9848 6.84595 0.276346 +5.74159 8.52139 0.109523 +34.0925 1.6264 0.180654 +10.3657 6.72062 0.047277 +17.6202 8.49446 0.489959 +28.6094 2.70374 1.06392 +22.3377 0.674811 0.0497895 +4.16785 4.03585 0.0246296 +32.0464 4.74232 0.137056 +25.7555 6.01378 0.718614 +26.367 4.98892 1.02113 +18.3734 2.63735 0.0644319 +6.92527 1.41965 0.0496167 +23.1248 2.97161 0.145072 +11.759 4.0977 0.0282085 +28.9957 1.62198 0.992891 +12.3354 2.61049 0.044771 +23.6221 7.72664 0.199951 +19.3679 3.70144 0.07514 +6.57409 1.09517 0.0399644 +24.0227 5.81683 1.03056 +33.4933 3.10228 0.14963 +3.47315 4.50208 0.0388711 +20.8944 3.4324 0.0710038 +22.8763 1.003 0.0340793 +29.1782 6.44193 0.115009 +11.9985 4.97141 0.0304667 +28.885 8.64368 0.0833712 +25.946 0.0413775 0.177803 +7.79572 5.04554 0.0513135 +26.1276 3.72104 0.871135 +16.2979 3.79994 0.054275 +8.23786 6.31521 0.0216878 +9.51769 2.11865 0.0623738 +12.5425 3.49557 0.0404308 +25.8865 1.16524 0.255158 +6.54723 4.95345 0.0574504 +22.714 3.8227 0.243336 +28.4363 0.37267 0.82487 +18.5963 2.01333 0.0666181 +31.468 3.45532 0.434198 +25.2387 4.64867 0.983374 +18.5151 0.811777 0.0331429 +33.3967 5.41311 0.0685231 +19.6713 7.14656 1.05433 +6.39949 2.90913 0.042257 +8.57655 0.583318 0.0491374 +28.6805 1.28144 0.956053 +29.7201 6.66844 0.0831529 +14.5024 4.65166 0.0411427 +13.3821 1.18961 0.0305311 +8.2978 1.46754 0.0216157 +29.4438 7.28184 0.0472926 +34.0613 5.93878 0.0463617 +32.5198 6.07978 0.0351421 +16.1166 1.22718 0.0656634 +30.9549 8.65669 0.0779746 +8.18663 2.21941 0.0386335 +3.83792 3.09395 0.0608985 +20.005 2.04471 0.0485704 +12.8649 8.23869 0.870783 +17.3559 5.52009 0.0955069 +3.89405 4.83567 0.0488134 +7.25083 3.62023 0.0639935 +17.6223 6.26778 0.302849 +18.2104 4.09529 0.0470113 +25.1016 4.03491 0.787335 +25.8668 1.32711 0.25801 +14.1922 7.19807 0.26209 +16.3996 0.578328 0.0590376 +10.8023 7.93146 0.254179 +6.64223 8.00972 0.0388395 +14.7509 4.57419 0.0588567 +20.6868 1.42766 0.0326972 +34.4284 1.90433 0.140589 +25.9028 1.22096 0.271001 +28.2798 6.1254 0.211092 +28.7362 3.17046 1.01734 +32.2964 8.58557 0.0621572 +31.8051 2.98313 0.450353 +7.34045 2.88186 0.019443 +11.9951 5.91439 0.064598 +10.2979 7.67614 0.103366 +27.9996 4.6031 0.839283 +17.5707 1.06802 0.0539099 +32.6955 1.49199 0.425623 +20.4129 6.24965 0.807372 +2.43189 6.69285 0.0539472 +9.50175 8.65161 0.763339 +14.8024 5.01 0.0642915 +5.91806 6.83507 0.0539284 +20.0293 5.89802 0.529064 +3.05833 4.80516 0.0552443 +9.53267 7.06408 0.059823 +1.88787 1.78114 0.0408839 +5.4413 1.33837 0.0576513 +24.8175 2.70697 0.299715 +18.1066 3.36471 0.042084 +33.139 8.29859 0.0487867 +5.85703 7.20998 0.0651806 +7.77671 6.50562 0.0623027 +3.50388 7.79196 0.00645254 +8.069 3.35117 0.0679656 +11.2346 8.60667 0.955272 +15.6755 7.93283 1.0175 +9.72159 1.61857 0.0716045 +7.1086 3.30138 0.0394303 +14.0441 2.69156 0.0346327 +3.16549 1.68688 0.0207641 +28.6797 8.19137 0.0327451 +20.3163 3.41428 0.071171 +18.3032 7.59534 1.03389 +16.8931 5.21345 0.0472856 +19.8218 0.522631 0.0557252 +34.2839 3.78911 0.0650016 +14.1335 6.81757 0.118345 +20.2434 2.7876 0.0401418 +8.40811 2.5586 0.055724 +14.5255 0.817513 0.0395757 +4.62851 4.14317 0.0423389 +30.0531 8.23658 0.0470373 +23.9503 0.163575 0.0341468 +26.8655 7.75022 0.0561625 +26.6473 5.24147 0.864823 +5.91101 2.45008 0.0319113 +22.4675 7.85157 0.252443 +6.34172 8.10644 0.0656768 +21.8986 8.50529 0.0777588 +5.91885 4.04384 0.0650767 +6.70438 3.62229 0.0511859 +5.58492 5.80865 0.0578632 +19.2273 7.76812 0.866214 +5.3946 3.05881 0.0399054 +13.5163 2.90139 0.0741446 +15.524 3.57183 0.025191 +34.0842 0.183853 0.197284 +27.2579 7.34222 0.0667963 +20.8068 3.99962 0.106163 +25.6822 4.07956 0.893557 +1.86695 5.53079 0.0566663 +13.6535 7.07 0.164522 +10.968 6.16538 0.0184775 +23.3178 2.9155 0.138074 +5.77494 2.57562 0.0588602 +32.6774 2.91147 0.301492 +12.1926 4.65118 0.0537541 +30.319 7.08615 0.0441518 +29.5345 0.0696464 1.02225 +16.898 5.2901 0.0457893 +1.58393 7.37532 0.0347127 +5.43423 4.90713 0.0526795 +14.2501 3.33055 0.0493183 +25.8555 4.14325 0.968679 +17.2275 4.4974 0.0546041 +32.4247 5.4803 0.0767067 +31.402 0.65643 0.829053 +12.2438 6.926 0.0815783 +34.141 4.82148 0.0949203 +18.9242 2.42199 0.0350967 +3.9397 8.00186 0.0521508 +2.70988 3.48322 0.0427028 +13.6464 6.77219 0.104724 +15.1964 2.60048 0.0564218 +7.45845 4.79122 0.0377114 +31.2094 1.65948 0.810031 +11.7074 2.3914 0.052807 +27.0036 2.87725 0.855135 +8.67776 1.25517 0.0544139 +15.8321 6.64688 0.241019 +14.132 3.93866 0.0588316 +26.7963 3.03351 0.846421 +13.5672 5.11352 0.0535743 +8.84448 2.0093 0.0793991 +29.1341 3.82682 0.848536 +22.954 4.96178 0.695741 +7.11142 5.38363 0.0401605 +9.35127 3.69222 0.0264277 +4.76359 6.49414 0.0398193 +28.9963 7.57615 0.0572089 +15.3282 8.41968 0.953264 +28.9102 4.89653 0.540146 +20.7599 8.43603 0.138089 +27.6939 8.20126 0.0362575 +26.4542 5.72701 0.709942 +16.8742 1.1759 0.0409864 +9.95271 5.60358 0.0132034 +14.3813 4.6427 0.0562999 +19.0329 6.14005 0.467915 +34.3976 2.36693 0.126525 +10.9552 8.679 0.99378 +25.7865 3.0271 0.597249 +9.96071 8.73657 0.907322 +22.3676 2.41577 0.0302011 +25.7598 4.17486 0.954545 +1.5279 5.95601 0.0439137 +25.5409 2.27016 0.365271 +30.1662 3.55329 0.71198 +31.3601 5.78365 0.0769764 +30.345 6.57861 0.0448019 +14.0994 8.013 0.872812 +6.34623 4.82316 0.0622038 +12.7646 8.41466 1.00109 +30.5694 2.46438 0.837999 +23.8064 5.32356 0.968662 +18.1643 6.62045 0.576098 +1.70281 0.727048 0.0343525 +3.87571 6.87843 0.0179197 +19.8094 7.15922 1.05456 +30.2536 4.91737 0.298742 +21.6334 8.0729 0.248592 +13.4041 6.11313 0.0413457 +1.69739 3.20732 0.0489743 +8.44901 7.05058 0.032296 +21.8121 4.5353 0.261625 +11.1503 5.97097 0.0565422 +19.2177 8.63681 0.143886 +2.82816 6.08604 0.0444127 +19.3134 6.79427 0.94315 +16.6912 7.13317 0.659719 +2.22775 1.75174 0.0594139 +28.5837 0.328702 0.850583 +27.4729 0.662791 0.550998 +5.29921 4.06184 0.0407919 +22.1582 8.44036 0.114528 +29.8414 3.89858 0.674527 +18.4223 8.07374 0.755924 +26.341 2.56243 0.622777 +5.55803 1.24136 0.0487401 +3.46682 3.55273 0.0688019 +9.27193 7.37878 0.0480818 +26.8078 8.45512 0.0377627 +11.0374 4.70593 0.0437482 +29.3594 3.32248 0.954181 +3.70898 6.88263 0.0634794 +17.4151 3.71643 0.0539939 +15.8999 2.63992 0.0511598 +28.2757 6.60886 0.132481 +23.6133 0.72567 0.0469978 +26.0218 6.78884 0.30186 +21.5377 8.3386 0.127942 +6.71503 0.899303 0.0533576 +26.6044 6.83383 0.209449 +25.2512 0.205374 0.119842 +25.632 1.09201 0.198684 +16.6728 2.55844 0.0640318 +15.483 8.73434 0.589014 +6.36382 0.463313 0.0386711 +28.4065 3.98846 0.95799 +15.859 3.86666 0.04505 +32.9979 1.54689 0.368162 +10.4649 8.54667 0.784071 +17.4574 2.06658 0.0428918 +13.1024 7.87882 0.561261 +21.5463 4.74607 0.309359 +33.4124 6.23165 0.05176 +8.83775 6.498 0.0800944 +14.2092 5.89682 0.0604249 +11.2456 7.07167 0.0487459 +4.61872 7.70203 0.0519546 +2.69193 1.23747 0.0420723 +34.2575 0.779195 0.205637 +12.4021 3.96435 0.0408475 +24.8303 0.940771 0.0973314 +27.3396 5.16442 0.797409 +22.6492 8.22974 0.100664 +10.2487 4.50032 0.0445576 +23.6342 1.46915 0.0722274 +31.887 1.02045 0.67212 +16.6031 4.64897 0.045884 +30.9254 6.49611 0.0272548 +29.9195 7.0469 0.0542521 +26.963 6.60622 0.251517 +24.4757 4.06166 0.673651 +33.1503 7.65033 0.0512618 +18.0405 4.72724 0.0750297 +31.8456 5.97026 0.05239 +4.40462 0.0216768 0.0718592 +24.898 4.2875 0.859686 +5.42203 0.758052 0.0553461 +13.7162 1.85775 0.0454307 +20.8037 2.63264 0.0547717 +24.3551 5.01222 0.991756 +24.0207 0.669036 0.0512966 +7.41352 6.22438 0.0693525 +4.43158 3.44968 0.0359057 +6.20264 1.90471 0.0383828 +21.9129 8.35216 0.116569 +7.40401 5.77635 0.0600578 +31.7359 2.93559 0.484822 +23.935 1.07397 0.0962594 +11.4541 4.28155 0.0339358 +22.315 6.74397 0.976259 +3.91502 4.47561 0.0619735 +21.4554 2.70694 0.0643833 +6.61486 0.986483 0.0672279 +22.3691 1.95603 0.0606382 +10.7075 6.90662 0.0509286 +19.7593 1.39432 0.0176889 +24.6757 8.57356 0.0245029 +8.75053 6.37468 0.0622547 +3.42142 3.52381 0.0337126 +6.1905 1.65136 0.0551389 +29.1417 4.06848 0.796933 +32.9069 8.32471 0.048867 +20.5241 1.03927 0.0655796 +4.27893 2.12941 0.0410809 +21.2491 0.67167 0.0594774 +12.6625 6.32625 0.0528647 +7.21059 8.31082 0.104597 +26.1182 4.86605 1.01266 +30.0486 2.60152 0.929523 +19.1028 8.42363 0.346267 +20.4444 5.1309 0.286634 +32.732 8.3911 0.0575435 +1.59239 3.26082 0.062282 +29.7651 1.27246 1.06369 +32.1646 0.611066 0.613844 +29.5013 2.33404 1.05388 +19.683 3.78681 0.0636374 +30.9054 6.67267 0.0489666 +17.888 4.09789 0.109508 +13.6339 2.35336 0.0603846 +17.94 7.87019 0.98602 +2.92439 5.04231 0.0618783 +2.49189 5.26799 0.0326208 +18.6167 7.16758 0.97271 +20.9592 5.86333 0.73169 +3.78881 2.11146 0.0597622 +26.6952 8.32728 0.0352895 +6.45426 1.41025 0.0264809 +8.53422 1.40067 0.0488748 +17.2096 5.67137 0.0886333 +27.0562 5.0921 0.890481 +17.6264 2.00331 0.0217928 +31.2127 6.13838 0.0850091 +26.5065 0.868644 0.320947 +20.0395 4.15259 0.0608662 +30.1824 8.27699 0.0507762 +12.9437 7.83024 0.470897 +20.6228 5.27728 0.33934 +22.7511 6.93258 0.801599 +4.50716 5.79609 0.0139802 +10.1836 2.03829 0.0323416 +33.9027 7.503 0.0550518 +11.585 5.34091 0.0190901 +31.5104 5.20699 0.0975329 +3.17074 8.33074 0.0385191 +9.74543 4.88699 0.0635467 +9.14882 2.84754 0.0635545 +1.56869 3.69115 0.0720558 +25.4061 4.85659 1.02588 +9.21216 7.62148 0.0826999 +20.8179 7.36229 0.887444 +4.66384 3.47498 0.0304505 +20.4262 4.26928 0.110526 +17.8374 0.436572 0.0496789 +17.0555 5.5847 0.0781014 +27.538 7.01476 0.0886235 +13.0851 1.72777 0.025657 +25.1064 5.55594 1.02045 +28.1246 1.22475 0.813911 +9.54036 7.33289 0.0537933 +11.3051 6.38427 0.0450617 +21.7061 8.16724 0.17824 +7.53178 2.48189 0.0611297 +1.66823 1.48851 0.0526718 +21.5622 4.9884 0.37202 +24.7266 0.506628 0.0926908 +5.02698 7.17497 0.0568079 +20.317 1.78648 0.0410462 +17.158 1.16506 0.0426533 +18.3479 1.29392 0.040926 +32.4011 6.14831 0.0638227 +2.92805 0.431756 0.043738 +7.58969 3.68486 0.0493206 +4.2206 1.58687 0.0510424 +21.9409 6.82229 1.01216 +29.9344 0.472955 1.02715 +19.2341 1.71626 0.0846962 +2.55086 6.74524 0.0317183 +18.1061 4.50803 0.0542968 +5.13256 1.00642 0.0376599 +4.35766 8.65279 0.101398 +11.5165 3.24331 0.0580516 +29.594 3.66639 0.836678 +24.4624 6.48132 0.761133 +33.9965 4.30259 0.0623188 +17.4082 7.25155 0.866506 +7.6433 4.68291 0.0516489 +13.4842 2.14747 0.0398879 +4.28642 4.68285 0.0587463 +14.708 7.33041 0.418683 +21.3919 2.45007 0.0488986 +23.1615 5.55596 0.956606 +33.5122 5.06605 0.0729823 +13.9838 6.05623 0.0550162 +4.38802 3.14668 0.0677851 +29.9904 1.98086 1.02346 +7.26715 5.8074 0.0317655 +12.2196 7.40967 0.124072 +6.22735 4.12045 0.0505482 +24.9367 1.19474 0.125332 +30.6345 7.02606 0.0370296 +8.45876 4.61617 0.039051 +20.617 5.96349 0.708247 +2.30649 0.885881 0.0465872 +24.1115 6.85901 0.615941 +6.28038 3.25542 0.0616243 +11.8194 7.77775 0.252251 +19.7441 2.16504 0.0568528 +16.71 0.0532939 0.0722993 +27.0541 2.79042 0.820601 +2.60002 6.32652 0.0308948 +8.44648 6.29136 0.048316 +8.06184 7.40984 0.0441793 +14.2941 1.8402 0.0753993 +29.5401 5.19069 0.31664 +33.3147 0.636838 0.346855 +33.2705 1.96251 0.26017 +7.64242 0.3997 0.0371109 +8.25907 5.49615 0.0549687 +10.9794 7.82595 0.218691 +20.9701 5.25371 0.401392 +29.0517 5.26802 0.412675 +18.2846 7.89859 0.955136 +2.73102 2.88128 0.0797191 +23.9962 4.86628 0.857665 +21.9408 7.07548 0.882473 +13.9662 1.69778 0.0460357 +19.1853 6.14589 0.517988 +9.41533 0.673676 0.0153092 +11.3971 3.23072 0.0403835 +20.863 1.61622 0.0474771 +13.7399 3.77964 0.0428455 +12.7952 1.786 0.0741553 +6.24137 0.536126 0.0292671 +30.4346 3.88507 0.531253 +18.5626 3.41195 0.0329348 +30.0264 6.4151 0.0770022 +21.0535 0.122831 0.0365821 +3.93697 5.55561 0.0644112 +2.68384 0.387697 0.063655 +7.2954 4.24489 0.0183942 +13.6938 0.176844 0.0181415 +7.15805 7.96149 0.0773443 +13.0502 4.2621 0.0560941 +31.7485 4.17054 0.212402 +2.05338 0.620474 0.0496595 +3.98199 6.87589 0.0315192 +25.0081 2.3273 0.282184 +25.6536 4.11008 0.89707 +9.9862 6.31021 0.0409695 +3.97689 6.91707 0.0573142 +12.6597 3.37466 0.0509061 +24.4325 0.530622 0.0953617 +10.6201 8.61719 0.884437 +18.2324 2.23004 0.0276288 +15.8966 0.367464 0.0537291 +30.9857 5.41808 0.109793 +21.8402 7.63412 0.515496 +12.9096 0.767941 0.0739429 +14.5013 0.400157 0.0672727 +5.63356 5.91799 0.0832665 +27.7569 5.00445 0.774085 +7.79082 2.26352 0.072394 +5.24393 2.31615 0.0644019 +12.2348 6.70637 0.0740986 +23.3971 0.183474 0.068268 +18.896 2.10292 0.0546427 +1.81054 3.16858 0.0773479 +20.7001 6.76703 1.02675 +25.9396 2.74616 0.539226 +25.6646 0.474912 0.154404 +8.26906 6.49698 0.0740924 +3.9937 6.01472 0.0461158 +13.8159 1.11325 0.0378704 +8.47349 6.36284 0.0374022 +33.0713 5.44718 0.0672447 +24.0072 3.2153 0.282322 +11.6205 5.05375 0.0681879 +28.6836 4.39412 0.778476 +21.3121 6.93536 1.02857 +2.86011 8.31611 0.0385877 +8.66925 2.97967 0.0582505 +10.0091 0.789162 0.0959209 +31.7255 3.18653 0.421192 +22.6149 5.23902 0.733375 +7.72242 2.24104 0.0648166 +29.4569 1.3525 1.02947 +14.316 1.85283 0.0432692 +34.2144 7.41666 0.0419849 +19.7658 6.23999 0.67395 +1.90803 1.09762 0.0295451 +14.8174 2.61757 0.0717314 +23.642 2.36748 0.122631 +15.8216 0.982736 0.0529823 +23.2803 7.33293 0.441246 +4.59226 2.52691 0.049704 +2.0016 4.93708 0.0154075 +4.58457 3.34179 0.0694171 +22.5572 0.612826 0.0389378 +8.82926 0.31817 0.0626428 +28.4636 6.07122 0.224663 +27.5945 5.48853 0.589287 +5.74857 4.30249 0.0577477 +3.41647 1.5497 0.0755688 +23.5426 2.97586 0.176507 +24.5842 4.43081 0.804189 +30.7787 1.88657 0.890508 +13.0439 5.03888 0.0130854 +2.38118 7.33815 0.0372929 +26.4271 1.55341 0.399258 +21.229 8.13765 0.243691 +32.9858 4.43783 0.0812957 +11.5268 8.54462 0.951395 +1.8232 8.45828 0.0468502 +3.35751 0.558275 0.0522841 +30.6941 5.95881 0.0894822 +13.1732 2.12639 0.0454612 +3.95154 5.42672 0.0403413 +2.44763 7.77494 0.0696322 +22.5841 4.00453 0.266393 +13.3927 5.70642 0.0736455 +25.9902 5.83678 0.767397 +21.5589 1.64109 0.0561219 +9.12569 3.72064 0.055595 +21.5707 2.54439 0.0537611 +14.5309 4.22037 0.0496524 +23.6489 6.66475 0.774237 +9.61862 1.26737 0.0323018 +11.4967 4.83425 0.0363387 +7.07005 4.40609 0.0330026 +5.91571 3.36464 0.0566978 +14.8064 7.35638 0.455483 +21.7467 3.06139 0.08145 +20.8389 6.99109 1.0313 +7.78094 0.617802 0.065391 +14.3089 3.83987 0.0639869 +15.9336 0.952126 0.0587764 +8.68973 7.40063 0.0498528 +21.9865 5.80788 0.901493 +12.7434 1.85665 0.0323026 +4.74736 3.2546 0.0256286 +24.0632 1.11164 0.0605653 +7.20523 7.73532 0.0733656 +13.1381 7.80267 0.502557 +26.9626 7.79963 0.0489435 +5.70348 5.29291 0.0152228 +19.6643 2.31131 0.0470656 +20.9927 3.98494 0.0897794 +10.4123 5.81071 0.0187509 +5.24106 3.71798 0.066554 +14.852 0.506334 0.0506554 +9.17817 4.58886 0.0710594 +14.173 0.765629 0.049918 +6.12715 2.05466 0.0586236 +14.6969 7.14086 0.288862 +31.8804 5.36442 0.0981188 +1.79447 6.96579 0.0441369 +20.3976 5.93598 0.634219 +15.2616 5.74825 0.0635867 +29.3967 2.16089 1.03063 +26.8566 6.18967 0.422037 +16.417 1.10352 0.0527451 +9.94299 0.159009 0.0397084 +32.6247 2.96371 0.287667 +28.5756 1.36938 0.944661 +30.6344 6.0742 0.101637 +4.33265 2.49679 0.0512213 +14.4785 0.302431 0.0160965 +16.938 6.77834 0.457651 +11.2253 8.17145 0.523224 +8.85981 6.21376 0.0467918 +12.2407 8.34856 0.850925 +7.13903 6.84405 0.0548805 +33.0598 8.12299 0.053473 +31.2452 5.88202 0.0910233 +22.0018 6.85886 0.958982 +20.4008 6.95155 1.07645 +34.1805 1.01987 0.192826 +17.96 2.79622 0.0351452 +26.6413 5.39026 0.852164 +2.57449 2.80956 0.0484108 +15.5058 7.62876 0.810289 +7.82439 0.47438 0.0558287 +4.2942 1.11872 0.0511569 +29.3015 2.86474 1.01443 +25.635 2.99627 0.553901 +13.2124 7.27748 0.18127 +5.52279 0.130872 0.0468193 +27.8337 7.22955 0.0701045 +13.1229 8.4995 1.05259 +27.2954 0.433296 0.485347 +21.4917 8.31942 0.1205 +25.2911 5.77103 0.939818 +21.2224 1.15307 0.0501103 +33.2087 7.26556 0.0632556 +9.43715 1.0417 0.0605772 +30.2805 3.35685 0.728506 +22.1064 2.08535 0.0669527 +24.4571 3.35925 0.380997 +33.9736 1.27348 0.22593 +2.62122 3.31161 0.079449 +10.3461 4.9716 0.059946 +25.4165 4.05736 0.847958 +27.5353 4.55853 0.962667 +9.73561 7.80722 0.110804 +17.0551 7.44307 0.951773 +29.8505 0.986207 1.04385 +33.6393 6.42339 0.0335126 +33.567 6.95828 0.0381561 +8.40377 7.94695 0.0797201 +14.0276 3.43331 0.0612909 +15.6976 8.06834 1.04885 +17.5057 4.8664 0.0645044 +29.8748 4.09586 0.604809 +17.4832 4.44205 0.0733119 +11.1832 3.60352 0.0507476 +22.3501 6.03098 1.01348 +27.5213 4.64747 0.954359 +21.5047 7.67482 0.529361 +32.1156 4.1001 0.194899 +33.8681 0.457736 0.215213 +26.1543 1.36947 0.343761 +30.8054 3.60736 0.539702 +4.37139 4.1218 0.0389883 +20.6763 7.10963 1.03608 +2.2861 5.16155 0.0287379 +10.4222 1.48097 0.0183812 +22.7002 2.64247 0.0736626 +24.1658 4.6864 0.795399 +28.8486 6.93439 0.0697322 +29.1736 6.19533 0.13851 +10.4614 5.59665 0.0465458 +16.1049 2.23338 0.0419442 +26.9496 4.98575 0.909752 +8.39818 2.14463 0.0644323 +30.5442 0.570396 0.980715 +2.53966 6.63015 0.0614084 +13.9525 5.68233 0.0561685 +2.88556 8.63798 0.0629898 +10.8007 6.53059 0.0582316 +29.2884 2.20774 1.02606 +33.9625 0.245046 0.214243 +15.5685 3.76082 0.0581373 +9.31163 2.97161 0.0671703 +12.1743 4.50695 0.0195679 +29.197 3.27333 0.971125 +7.88433 0.419445 0.0484539 +26.8658 0.598352 0.354188 +11.7137 3.13419 0.0454058 +9.72548 5.14888 0.0512222 +31.0844 1.53403 0.84075 +8.75247 1.56866 0.0561481 +32.5718 2.02723 0.424054 +16.1237 6.48267 0.188847 +34.3796 6.67913 0.0452482 +25.6218 4.25861 0.963659 +29.0591 0.476363 0.944886 +19.4474 8.21894 0.448663 +33.5165 7.59314 0.0703018 +25.0883 2.6344 0.344241 +15.9308 0.0764592 0.05598 +3.38041 4.299 0.0507638 +7.77761 1.47007 0.0626954 +30.0734 3.32153 0.759736 +32.5911 3.49593 0.199755 +2.52693 2.7228 0.0375137 +15.0532 7.98261 0.970951 +16.2689 3.40446 0.0649291 +6.81342 0.979864 0.0708928 +2.6036 5.02483 0.0590619 +29.9302 7.76352 0.0693774 +28.9049 2.02242 1.02864 +30.6946 4.43592 0.333926 +11.3192 3.30242 0.0272418 +30.7744 5.90436 0.10808 +33.6616 2.73022 0.180398 +27.7806 2.22998 0.888387 +12.9226 8.39708 0.973226 +13.8711 7.19612 0.209942 +18.0261 6.1456 0.305744 +31.6784 5.73547 0.0743163 +17.9958 8.1613 0.778491 +15.5774 0.499394 0.0530231 +3.52891 3.85032 0.0396812 +12.2321 5.44452 0.0325632 +8.47644 1.12588 0.0501288 +17.1154 8.7008 0.377801 +17.2139 5.99848 0.190734 +7.92654 6.63482 0.0391718 +4.32423 8.44364 0.0725861 +22.4135 5.05296 0.591595 +9.48294 7.97973 0.155425 +23.3966 3.25968 0.163637 +20.2598 8.05464 0.434108 +30.2027 4.43378 0.40716 +10.1794 6.77627 0.0733815 +33.5694 2.37398 0.201353 +29.4097 4.16291 0.702991 +17.8277 4.8623 0.0644773 +12.9902 4.50564 0.0300613 +8.41272 8.07063 0.126541 +20.2575 3.99371 0.0931881 +7.58107 7.65629 0.0585055 +13.2128 0.971108 0.058405 +3.59326 1.83955 0.0667977 +23.5444 1.53561 0.0833075 +21.3617 1.59802 0.0495402 +23.9311 5.11829 0.913695 +29.3446 0.890114 1.00773 +19.6377 5.8857 0.433176 +13.7608 7.6179 0.472645 +9.80879 8.06357 0.235272 +18.7027 0.484927 0.0505868 +15.932 2.15333 0.0651519 +15.9766 3.85368 0.0144447 +10.3387 0.126632 0.0544678 +7.9908 5.88878 0.032611 +27.7241 3.97397 1.0477 +33.6803 3.73123 0.126938 +23.5575 1.35979 0.0878324 +21.4343 0.602751 0.0626632 +7.13184 7.72621 0.0721089 +16.2131 5.22999 0.0837398 +32.5493 3.02709 0.286143 +33.2287 7.69271 0.0742892 +3.93542 1.02049 0.045954 +29.2786 3.47692 0.89401 +8.65294 0.640558 0.0598593 +32.6908 6.93579 0.0626824 +8.95297 7.7162 0.0802645 +26.9759 2.48858 0.747012 +33.0896 7.15195 0.0603054 +3.04454 8.31733 0.0495388 +1.92118 2.01324 0.0879271 +19.1197 8.44086 0.312616 +15.7318 2.93453 0.0573323 +32.5608 6.19035 0.0469683 +11.1555 4.69081 0.0602498 +2.17074 3.05379 0.0762503 +30.1953 5.2561 0.212618 +21.3468 0.0026167 0.0459263 +18.4832 4.3851 0.0315686 +22.3087 4.36433 0.310096 +31.3376 5.54134 0.122054 +10.1477 6.68914 0.0341284 +9.78311 7.69206 0.0894612 +34.1384 7.0085 0.0397304 +18.6024 7.61862 1.0232 +2.61966 3.28113 0.039194 +26.6045 0.924522 0.359062 +20.1229 5.68519 0.420654 +3.03388 0.394432 0.0538337 +13.8507 8.343 1.05339 +21.0896 4.21414 0.148747 +30.0351 2.24225 0.953508 +23.3121 0.271259 0.0751403 +22.5293 7.36799 0.569331 +26.1121 7.28797 0.150084 +15.3933 1.17093 0.0375027 +28.2897 6.74782 0.0985096 +25.4087 0.762939 0.164781 +4.64214 6.08163 0.0768225 +25.4205 4.65754 1.00054 +30.9767 5.60281 0.112512 +32.6339 1.19911 0.475519 +16.0704 6.92877 0.382709 +5.51127 6.87792 0.0558869 +20.2474 7.35488 0.963094 +4.76559 3.51975 0.069845 +20.4233 2.26363 0.0274399 +26.1723 4.80483 1.04945 +21.0871 6.105 0.850136 +24.3916 6.64631 0.661001 +26.2223 3.9235 0.950126 +17.3028 5.32147 0.0311103 +10.0105 5.64791 0.0733185 +27.5005 5.16942 0.761885 +18.2887 6.27292 0.406329 +24.5704 2.7045 0.267978 +14.3101 2.05308 0.0554028 +31.3484 2.52403 0.63867 +14.5318 6.92027 0.189569 +21.3063 8.46492 0.132312 +22.4268 2.77866 0.0757202 +18.9635 5.7187 0.251979 +27.7181 5.08041 0.768743 +10.3592 7.69475 0.113277 +3.55429 0.131145 0.0542705 +6.6251 5.4355 0.0483946 +15.5899 7.6019 0.803383 +24.6519 8.43382 0.0643158 +6.5694 3.63918 0.0527761 +15.3631 4.20901 0.0365534 +22.2861 7.33867 0.613142 +8.32672 8.16903 0.144795 +22.7618 2.06286 0.0963609 +22.9668 7.76075 0.221602 +9.07941 1.04865 0.0493699 +30.9449 0.449339 0.951787 +32.8615 0.286472 0.413583 +32.1941 0.656761 0.612072 +8.99914 0.83989 0.049316 +12.2185 2.05159 0.0627161 +19.4413 2.51678 0.0759027 +28.3793 8.4729 0.0461733 +2.26684 7.01156 0.0552792 +9.8224 0.904879 0.0574812 +23.0033 1.68577 0.0681041 +8.98112 6.06027 0.0509274 +8.76844 5.24855 0.0188753 +34.3934 3.89711 0.0760846 +21.5677 3.7197 0.095238 +24.2617 6.51894 0.756376 +4.43731 4.29424 0.05708 +11.3234 6.29005 0.0597619 +5.52618 0.0998814 0.0438692 +9.69398 0.877113 0.0436203 +5.92177 1.62301 0.0463058 +5.22674 5.17749 0.0348348 +26.8429 5.08471 0.934959 +10.627 7.59361 0.136331 +31.0411 7.75114 0.0477166 +1.84271 7.06178 0.0743984 +12.0763 2.89475 0.0619694 +11.4605 0.515316 0.0344086 +14.8012 7.73554 0.771312 +20.6212 8.13377 0.337927 +18.1352 0.2139 0.0527043 +12.9672 0.0333888 0.0286795 +12.716 1.55387 0.0414588 +24.9195 3.15321 0.406611 +11.9379 6.16919 0.0507976 +24.4552 3.98706 0.608708 +18.0904 5.77803 0.167161 +9.37917 7.11269 0.0342975 +26.3922 1.54217 0.424123 +19.2967 2.70858 0.0281155 +22.4965 8.48495 0.0827316 +4.02267 4.72783 0.0518338 +16.6559 4.30792 0.0693405 +18.4355 4.81703 0.0495909 +15.0987 4.78395 0.052182 +13.7909 6.33357 0.0828174 +5.88796 7.60691 0.0483551 +17.8941 5.45504 0.08841 +27.1401 5.51818 0.647803 +20.7442 5.05457 0.2769 +11.7015 2.08902 0.0653481 +7.86416 6.67836 0.0730615 +13.8113 1.73207 0.0511506 +11.8862 2.25054 0.0634918 +7.77414 2.96739 0.065211 +15.0429 4.58722 0.0594601 +13.2865 7.15061 0.144195 +19.0714 3.1152 0.0619458 +28.9056 5.26787 0.400571 +13.2962 3.80329 0.0519979 +23.7413 3.90124 0.435985 +7.12387 8.447 0.192493 +9.35073 5.66177 0.04861 +29.8882 0.154568 1.06587 +26.0958 7.55329 0.11309 +25.0629 4.37995 0.90563 +28.5752 8.42161 0.0498677 +30.9692 1.08859 0.919478 +32.4395 8.38714 0.050913 +25.4801 5.72752 0.914131 +32.164 5.43141 0.0878446 +10.3647 4.34749 0.0669419 +17.2536 8.45297 0.598412 +7.75877 7.31353 0.0628457 +4.98403 4.57878 0.0544687 +14.574 3.27893 0.0530507 +12.6998 3.39589 0.036188 +22.3317 4.17673 0.267476 +14.9288 1.5706 0.0490339 +6.63922 2.04113 0.0550369 +17.8574 5.78258 0.179655 +12.3849 6.74023 0.0757787 +26.8245 7.92318 0.066567 +24.2246 1.38309 0.0898849 +33.0361 2.70038 0.231149 +33.3641 1.61553 0.281274 +20.4783 5.47232 0.404392 +10.1125 2.63797 0.0535013 +20.0957 2.79708 0.0725241 +11.3436 0.589052 0.0550754 +18.669 6.75797 0.794323 +26.5722 8.14744 0.0469527 +25.3869 1.19205 0.166823 +16.6355 4.28382 0.0640453 +11.5678 6.86163 0.0435003 +2.47497 7.59613 0.027321 +29.8377 3.07143 0.882191 +28.9628 0.522434 0.947824 +21.4911 6.69658 1.06436 +28.1809 6.20163 0.224088 +7.89387 3.46114 0.064001 +23.2607 8.59032 0.0562428 +11.1407 6.261 0.0269149 +28.6093 4.98415 0.566783 +14.7115 5.15679 0.0265339 +17.3458 0.698433 0.0475308 +13.538 8.38711 1.00516 +15.6028 8.65121 0.648277 +30.2614 1.43971 1.00105 +33.6091 6.71836 0.0638657 +10.3411 5.06356 0.0582952 +27.9721 3.44547 1.0447 +33.0931 5.16194 0.0698031 +18.6109 1.05886 0.0466367 +21.2619 3.06171 0.105158 +28.6543 2.91381 1.0248 +23.3483 5.95448 1.02011 +19.2093 4.6136 0.0541812 +31.0665 0.229955 0.898577 +19.9814 2.52 0.0456867 +27.4622 3.35423 1.03478 +31.7269 3.17376 0.422634 +11.9919 6.87639 0.0613136 +29.007 7.24378 0.0550893 +7.71248 8.31761 0.162295 +19.3654 8.22192 0.438087 +23.1172 4.27076 0.43442 +29.8091 4.88181 0.374612 +11.9246 5.27086 0.0531852 +33.4936 4.43333 0.0897031 +26.2845 0.333006 0.254343 +31.955 0.966082 0.67672 +9.92701 6.95926 0.0391626 +19.551 6.58027 0.837658 +16.4303 2.71527 0.0266009 +33.2246 1.72775 0.273981 +28.3217 4.96976 0.645563 +6.92949 7.41151 0.056046 +31.5557 0.48158 0.799977 +13.4468 5.69753 0.0555898 +17.1173 1.96834 0.06181 +3.30479 2.23801 0.0232759 +18.3493 3.85652 0.0431489 +31.3855 1.43122 0.78273 +16.7941 6.51104 0.270301 +2.57295 0.839519 0.0540315 +29.3384 4.32868 0.665284 +17.0144 5.841 0.121135 +4.8516 7.25423 0.0509408 +24.5083 4.83622 0.972653 +7.60021 1.13461 0.0503763 +21.6351 7.06647 0.922778 +15.3288 8.20479 1.06511 +9.68688 0.263512 0.0760641 +15.8253 7.998 1.02949 +11.3569 5.43178 0.034829 +17.0175 5.22219 0.0839707 +20.7989 3.39389 0.0813391 +18.1544 1.42221 0.0618942 +3.33144 5.24836 0.0457278 +20.3307 7.736 0.708507 +32.6968 0.211369 0.441031 +29.6518 4.8503 0.397279 +15.4426 5.57218 0.079574 +26.9743 8.03367 0.0337562 +7.10651 0.131435 0.0471931 +24.9346 3.23103 0.475303 +7.35109 1.10916 0.0541056 +4.14634 2.71699 0.0334982 +23.3698 7.93 0.137462 +13.2435 1.57433 0.0580013 +18.5329 3.13869 0.0434477 +3.14791 7.18255 0.0735878 +8.99599 6.55066 0.0475127 +4.00657 8.01463 0.0767648 +32.116 1.0825 0.613096 +5.31442 0.627525 0.0674108 +31.8792 1.93927 0.602762 +3.7884 4.32277 0.0503637 +8.16919 5.94129 0.0295523 +24.9391 1.02628 0.133684 +22.3623 5.48714 0.801043 +3.16983 1.9415 0.0545162 +12.4815 8.41325 0.945826 +5.74795 4.85744 0.035459 +12.6308 1.22092 0.0350711 +13.3076 7.27854 0.198873 +19.2612 4.31667 0.0806499 +26.2532 6.73016 0.280616 +2.55273 4.73946 0.0327862 +23.8342 7.73404 0.16096 +32.1907 2.45944 0.428617 +12.5584 8.4754 0.979281 +23.3451 0.157503 0.0477401 +16.2652 3.29534 0.0391841 +11.2402 1.37499 0.0434714 +19.0602 8.6305 0.213564 +3.4335 0.17075 0.0626225 +23.9084 2.52026 0.176827 +19.2651 3.39377 0.0514947 +10.2514 0.65877 0.052878 +2.54066 8.02499 0.0373907 +32.2066 6.73689 0.0242925 +23.102 4.7033 0.571726 +2.99452 3.55104 0.043635 +27.5403 1.69751 0.735767 +5.11335 1.4404 0.0817923 +16.5842 3.55081 0.0479477 +5.46396 5.36078 0.0265793 +31.8571 7.4608 0.0435841 +22.6609 2.12574 0.0739718 +24.1447 7.54065 0.197907 +12.89 5.78259 0.0426183 +12.2734 1.39268 0.0564094 +3.58585 0.276824 0.0507982 +30.5535 0.214997 1.01807 +5.85227 8.42623 0.0975904 +21.6158 2.14553 0.0534539 +8.35401 0.576819 0.0692725 +4.58077 5.76025 0.0625179 +3.04526 2.40892 0.0530612 +12.2991 8.24851 0.781207 +5.34285 5.80146 0.0486334 +29.2437 7.75131 0.049432 +21.6399 5.68801 0.757317 +7.52548 6.40083 0.0526282 +2.3745 4.03026 0.086967 +17.0012 3.7118 0.0535416 +29.3267 6.82917 0.0747731 +9.6765 1.59523 0.0810948 +20.6297 0.575209 0.0364561 +9.50145 7.96325 0.148455 +33.5034 3.35091 0.161806 +9.67888 4.88605 0.0532046 +8.35055 3.45037 0.0494925 +21.7658 6.50462 1.03737 +10.9927 6.78961 0.0543267 +29.9816 1.60339 1.02295 +26.7349 6.75063 0.236627 +24.3554 2.77543 0.256408 +32.9847 4.97612 0.0708231 +15.6857 4.22332 0.0671756 +2.86688 7.39541 0.0353251 +11.8965 3.65098 0.0375982 +9.78733 7.21421 0.0675007 +7.34659 8.2246 0.112145 +14.6689 5.02573 0.0339205 +3.51574 7.13028 0.0392552 +1.57183 7.75359 0.0467661 +18.8587 1.99748 0.0407946 +6.4336 3.94479 0.0531754 +28.7267 7.87789 0.0493181 +5.18237 6.64894 0.0572545 +4.61343 7.40106 0.0491886 +20.8505 5.2642 0.387227 +28.0602 3.31551 1.04357 +27.2771 6.87654 0.148012 +29.9371 3.01766 0.885487 +16.769 5.08709 0.0681296 +9.70629 1.84154 0.0551444 +2.30703 3.50257 0.0443815 +32.6191 3.92134 0.170523 +20.9247 0.994934 0.0691695 +32.5764 0.888547 0.492246 +29.1937 7.74 0.0519014 +23.3299 8.61671 0.0778678 +26.7488 1.41412 0.477374 +7.13943 3.19944 0.0600638 +31.253 5.89168 0.0640078 +29.6661 1.25065 1.03648 +17.23 0.583268 0.0541836 +15.9492 7.91965 1.06197 +17.8022 2.4655 0.0239258 +20.2991 8.35491 0.201859 +6.9735 4.54953 0.0581836 +32.8313 5.13108 0.0764308 +13.1198 4.72044 0.0357602 +2.71434 4.43333 0.0630765 +21.2551 2.34599 0.0565941 +6.23641 5.47511 0.0429765 +34.4863 3.84428 0.0716245 +32.1683 7.41722 0.0421167 +26.4069 2.52905 0.625628 +3.70264 1.68375 0.0396645 +29.3312 7.5242 0.0353037 +7.53064 4.68369 0.039397 +22.3721 2.00265 0.0652864 +7.18 6.79775 0.0661284 +25.1481 2.50561 0.318853 +24.8033 6.28579 0.767474 +12.9613 5.73215 0.0546584 +9.40041 2.554 0.0659257 +29.324 5.61266 0.238333 +32.6333 4.27396 0.156585 +7.07331 0.25872 0.0385545 +34.3527 8.54829 0.0705269 +20.2458 2.27977 0.0605702 +12.2064 3.35669 0.0253256 +17.5489 7.69518 1.05744 +3.64038 6.03167 0.0466398 +18.3515 1.48543 0.0473197 +21.3402 5.60663 0.634663 +18.5521 0.189655 0.0501248 +2.55673 6.54406 0.04039 +25.9424 5.28496 0.98751 +17.7073 5.79031 0.168811 +7.07565 4.96172 0.0697294 +29.0409 3.09431 1.02862 +21.986 5.73089 0.854937 +2.17366 4.01932 0.0561929 +15.8305 6.54525 0.183774 +26.6907 0.306085 0.307077 +22.9207 2.31879 0.0955177 +14.0839 1.36476 0.0435215 +27.8187 4.47015 0.93206 +17.0622 5.4285 0.0562944 +6.78727 1.92182 0.0562443 +6.04329 8.26931 0.0952369 +31.1102 3.23891 0.55072 +10.1169 0.121902 0.0629306 +20.9331 6.34817 0.942297 +9.66858 0.977974 0.0522665 +12.0786 0.127777 0.0429893 +12.2189 2.5274 0.011295 +1.97753 4.09947 0.05514 +1.83649 2.10428 0.0568941 +4.41786 2.88942 0.0582484 +25.4627 1.14975 0.212622 +10.6318 3.11916 0.0631765 +16.0844 6.95744 0.413425 +21.9359 2.71861 0.0582919 +18.3492 5.08428 0.131442 +30.9986 1.8369 0.819071 +27.2536 1.08924 0.543124 +30.5547 2.77916 0.78347 +11.54 0.290314 0.0654902 +34.046 4.13899 0.0810615 +32.909 6.16349 0.0602654 +23.2674 3.68501 0.250984 +27.9898 1.9718 0.88149 +7.51248 2.83648 0.0650276 +33.6039 7.16216 0.0427234 +7.8571 2.10784 0.0319761 +27.7276 2.89714 0.994411 +2.61893 6.0038 0.0415165 +26.88 2.17841 0.656677 +20.4013 8.12659 0.369693 +14.8196 1.51079 0.0358007 +10.3931 0.214666 0.0494378 +17.1299 3.73066 0.0699296 +11.0892 4.72221 0.0463687 +13.4376 4.97687 0.047055 diff --git a/examples/advanced/data_points2.dat b/examples/advanced/data_points2.dat new file mode 100644 index 0000000..b7a3a62 --- /dev/null +++ b/examples/advanced/data_points2.dat @@ -0,0 +1,3200 @@ +12.1761 1.1237 0.022788 +27.2517 4.36369 1.04412 +29.3813 5.2571 0.357506 +17.5911 0.250742 0.0665337 +28.556 1.29287 0.926928 +28.0544 8.11256 0.0730703 +7.3913 0.615476 0.024861 +6.12951 1.13422 0.0693155 +5.85763 8.28839 0.0434927 +4.34099 5.43526 0.0525009 +27.1395 3.225 0.96124 +14.0989 4.46955 0.0578312 +6.17008 5.79325 0.0599249 +19.6188 2.4062 0.0611342 +11.2799 1.20584 0.0395032 +3.07074 6.88747 0.0398518 +15.184 5.85895 0.050596 +28.764 4.47822 0.73865 +24.7614 7.13828 0.310988 +34.0252 4.79892 0.0370046 +17.1581 8.57319 0.495517 +33.2855 1.78741 0.273282 +14.1208 4.8396 0.0426933 +17.658 4.22688 0.0409022 +26.1173 3.08762 0.694801 +16.631 5.17054 0.0671193 +7.01336 2.05653 0.039522 +24.9368 7.01125 0.330365 +31.732 7.5805 0.0400471 +13.2607 1.12536 0.0344124 +15.2244 4.08222 0.0268211 +11.6964 2.42225 0.063663 +24.2279 0.726443 0.0935607 +17.7472 7.83055 0.999503 +9.08567 3.75775 0.0734952 +31.1494 1.29082 0.88609 +1.97198 5.88519 0.0290436 +29.0508 1.76737 1.03865 +30.017 7.87851 0.0458615 +4.61571 1.89788 0.0623958 +2.05834 0.289073 0.00569179 +22.8448 1.75472 0.0725742 +20.7502 3.02184 0.0514718 +7.19373 4.09826 0.0570755 +15.7813 7.91961 1.03275 +4.67472 6.09494 0.0143581 +15.1512 2.96566 0.0491455 +18.8303 0.147507 0.0498539 +31.0229 1.39686 0.888413 +13.8752 8.70885 0.914428 +6.7322 4.01792 0.0612964 +32.8949 6.03969 0.0417271 +22.5257 0.477799 0.0621535 +5.94831 0.297599 0.0385282 +3.48079 7.39308 0.0615009 +14.0735 5.13809 0.047582 +9.49542 2.69812 0.047222 +13.6089 2.77387 0.0519399 +10.4678 0.779934 0.0259599 +31.6123 1.50913 0.713948 +28.2962 0.214883 0.7377 +23.6333 7.33395 0.397567 +4.46828 4.07549 0.0690318 +27.6185 1.11175 0.631472 +14.6181 6.46102 0.0603058 +30.1645 1.71001 1.01217 +3.19093 0.541183 0.0413137 +6.31942 5.22995 0.0358997 +5.78892 7.82892 0.0861076 +28.4244 0.235485 0.787199 +32.9736 7.03689 0.0345828 +26.1205 1.66209 0.359769 +11.6663 0.811958 0.0542242 +3.2067 0.156988 0.0735834 +33.31 2.5606 0.232066 +7.1357 6.35496 0.0310928 +28.2463 4.31038 0.890764 +17.0625 7.45452 0.962208 +12.6788 1.89843 0.0237614 +15.0354 2.7547 0.0492012 +4.04925 2.25615 0.0676279 +5.35094 8.55035 0.114392 +30.114 8.22439 0.0467363 +16.445 2.9776 0.0366423 +23.6783 3.81065 0.384652 +12.4747 2.74715 0.0578314 +17.4169 6.52449 0.386808 +27.7201 0.349715 0.610626 +22.8116 0.589416 0.0559664 +14.6561 3.53129 0.069434 +16.7168 2.14212 0.0521384 +34.0499 7.38727 0.0637165 +2.5668 6.48339 0.0451482 +14.7577 4.77024 0.0764421 +5.79916 5.7813 0.0689395 +4.95902 6.05052 0.060467 +3.2583 6.82642 0.0351987 +26.7607 8.10637 0.0552793 +16.5839 1.30869 0.00589368 +19.6181 5.47238 0.261802 +11.454 1.25525 0.0525629 +14.5231 3.87296 0.0370675 +16.4115 6.87213 0.413298 +28.8531 7.81966 0.0610122 +4.50658 6.63565 0.0773482 +7.42687 0.309468 0.0503947 +7.09825 3.14126 0.0265803 +16.8644 1.42512 0.0148385 +17.2498 8.72953 0.305023 +23.9505 1.25869 0.0938039 +34.116 2.13531 0.175049 +31.7631 3.1221 0.430117 +9.42616 0.53215 0.0402563 +14.9577 7.60716 0.667299 +19.0125 5.5618 0.218019 +27.6022 1.3962 0.694838 +18.3895 4.35487 0.0666353 +6.76569 0.687574 0.052036 +3.15269 5.33996 0.0804303 +12.72 2.02474 0.0526043 +21.5233 0.337704 0.0578834 +7.43823 1.00718 0.0659806 +16.5053 4.853 0.0674141 +14.1582 5.56721 0.0421003 +33.1909 2.83876 0.194721 +24.5922 5.62377 1.02008 +33.6948 3.07706 0.151817 +18.6222 1.14196 0.0592357 +17.7197 2.75494 0.0555232 +33.7539 3.45464 0.13087 +14.9523 7.97646 0.984353 +19.7588 1.01087 0.0388163 +2.29063 0.753072 0.0888386 +7.07736 4.90753 0.0904238 +10.9653 8.42005 0.726997 +9.58468 7.92941 0.138791 +15.8068 6.12013 0.102802 +30.8417 0.583402 0.940733 +8.41496 7.04795 0.0514366 +3.06901 5.9727 0.0381869 +20.0637 1.25795 0.0490703 +13.4987 4.06361 0.0456447 +3.03837 0.43108 0.0815178 +26.5803 7.0084 0.150997 +32.4468 6.28086 0.0582547 +26.0957 7.03245 0.193276 +27.888 6.64603 0.170223 +26.5254 2.33583 0.618969 +33.797 6.90233 0.0275968 +6.10678 2.17775 0.0356861 +30.4433 1.20521 1.0063 +24.6513 3.41201 0.450122 +32.8358 4.35112 0.12271 +29.4541 2.49809 1.02879 +19.7228 5.29454 0.209817 +23.2152 5.26588 0.864997 +9.61952 2.09601 0.0576686 +12.5401 5.44124 0.0495001 +6.43121 3.12239 0.0525897 +14.7995 6.42119 0.102174 +28.7693 2.53825 1.04694 +12.7643 6.98145 0.114395 +30.598 3.62807 0.557766 +7.95739 4.83532 0.0575937 +22.8467 5.88505 1.01303 +9.22658 4.53062 0.0565923 +22.5092 2.25158 0.08065 +27.4493 8.5598 0.0601703 +30.4852 0.839524 1.02123 +26.6506 2.83983 0.777788 +6.18934 4.79924 0.0585395 +24.1164 0.250842 0.0627143 +11.2086 1.35394 0.0615691 +18.2028 6.98557 0.846427 +6.16337 6.85054 0.0635836 +16.0915 5.56709 0.0690998 +23.2262 7.88173 0.177313 +5.27213 6.60327 0.0206144 +20.9342 2.61138 0.0422852 +30.7109 5.63613 0.115618 +18.8812 2.97232 0.0659692 +12.2791 6.55364 0.0748007 +8.31206 3.3631 0.0526043 +16.8069 1.33935 0.0335298 +14.5199 7.66023 0.64592 +24.3951 6.03414 0.979464 +25.9385 6.50849 0.447543 +24.5247 4.89089 0.948114 +26.5188 6.84291 0.199384 +27.572 3.91441 1.03263 +25.4408 4.94503 1.03216 +9.7583 0.546807 0.0654518 +20.5602 4.8513 0.2111 +14.4092 7.11964 0.284967 +4.04863 6.16647 0.0752081 +22.2671 7.01955 0.861089 +6.182 4.33591 0.0400945 +24.1256 7.70287 0.171834 +19.24 0.946562 0.0571628 +3.85416 7.65362 0.0292629 +15.402 3.24491 0.0312445 +19.5406 0.795013 0.0392573 +9.72289 5.41297 0.0693277 +32.2207 3.97576 0.201663 +32.8769 3.74398 0.144682 +4.92886 7.44673 0.0516671 +25.1795 1.2043 0.169013 +15.6121 5.39201 0.0370871 +29.0033 3.61618 0.96241 +32.4198 4.61698 0.107838 +18.0187 4.36484 0.0710768 +22.4284 1.17229 0.0638866 +13.6161 4.47501 0.0392009 +23.4684 7.5348 0.296066 +34.1973 1.49659 0.170574 +12.4696 0.100238 0.0448294 +2.47755 0.590521 0.0615768 +33.5449 4.01861 0.100304 +2.39731 8.51671 0.0277269 +24.1936 0.38663 0.0708305 +20.6373 8.66072 0.0729803 +14.8311 4.6851 0.0467962 +32.1771 1.05003 0.602355 +5.46638 3.65803 0.0484659 +9.3784 1.81233 0.0655321 +28.8605 6.24168 0.150584 +1.73122 4.73283 0.0422852 +10.1698 2.51677 0.075058 +7.08391 2.23239 0.0612415 +15.3846 7.57738 0.733282 +25.8031 6.69696 0.367661 +32.9363 3.81226 0.14744 +9.56018 3.54417 0.0513689 +1.93248 6.44551 0.0356004 +25.989 8.48422 0.0773637 +31.519 0.69564 0.817469 +1.98232 1.39039 0.016237 +21.7619 3.16674 0.0659304 +21.0922 4.43743 0.172313 +19.8191 0.624112 0.0403267 +22.1884 0.467499 0.0187794 +10.702 1.9181 0.0605677 +31.0068 3.37329 0.527012 +8.80084 6.46132 0.0604361 +32.6945 5.33065 0.0755702 +32.9042 0.255244 0.432452 +28.9365 0.393412 0.917204 +14.264 3.95074 0.0776097 +32.7582 7.64619 0.0315323 +8.58582 7.99707 0.129736 +17.7208 3.19102 0.0662747 +18.16 7.75564 1.02203 +6.11912 8.12055 0.0463269 +34.0472 3.55381 0.1222 +7.46893 6.52068 0.0623006 +17.3251 3.19309 0.0250255 +4.3368 1.34226 0.0108522 +10.771 5.02723 0.0400737 +28.8976 0.755331 0.929477 +23.9174 5.79771 1.05943 +13.5354 7.07241 0.154614 +22.9274 8.00065 0.159787 +7.12259 3.91574 0.042368 +23.2797 1.02594 0.074251 +30.8654 7.88778 0.0415093 +1.9636 7.60703 0.0272611 +11.3947 8.45691 0.796461 +9.9728 5.19138 0.0580082 +4.40901 5.88555 0.0408932 +31.7017 3.26246 0.388297 +24.2385 1.60061 0.0949735 +21.801 2.54959 0.0556937 +21.7066 6.29854 1.02816 +23.6968 2.84036 0.143034 +10.9078 6.04447 0.0440767 +8.13733 4.38887 0.0613562 +4.03883 3.92423 0.0573615 +6.6239 8.45707 0.157711 +8.05135 1.45163 0.0802055 +27.8007 4.25779 0.969789 +12.1315 1.39591 0.0412034 +14.2612 8.19337 1.02612 +25.9026 4.27053 1.01859 +33.4607 2.90138 0.186682 +4.92176 1.47112 0.0456819 +20.8673 5.19244 0.339108 +15.2829 3.4869 0.0600093 +7.48719 1.11155 0.0559278 +12.7108 0.282947 0.0344399 +27.3337 3.41998 1.00914 +10.361 5.09689 0.0358335 +19.9857 4.53453 0.117077 +16.5375 7.86624 1.04449 +2.09998 7.96755 0.0695787 +29.8728 8.18324 0.0712206 +29.9369 6.98931 0.0622441 +29.071 4.17436 0.788259 +15.8543 4.57797 0.0671842 +15.6655 3.47253 0.0735787 +23.7149 3.49201 0.294148 +34.0497 0.363406 0.200593 +17.9645 6.98171 0.813356 +11.3337 2.15022 0.0610859 +17.1687 0.256423 0.0448535 +20.3737 4.07703 0.0903233 +22.2759 6.27406 1.08223 +32.5172 3.81092 0.19008 +20.5837 3.18252 0.0496829 +28.6259 5.75553 0.312726 +17.0822 1.42335 0.0456796 +16.4506 0.0990406 0.0276029 +31.4137 5.17778 0.104147 +10.89 4.63614 0.0515819 +11.4793 7.56727 0.143763 +19.5476 3.63471 0.0791887 +5.17889 6.87065 0.0423022 +6.94251 0.156672 0.0303408 +18.5464 0.238408 0.039646 +22.6365 5.23146 0.718808 +33.8307 1.97733 0.216104 +5.22463 0.5879 0.0395411 +2.8946 1.11653 0.0742676 +24.2127 3.27525 0.34245 +31.0653 2.94658 0.661324 +4.53438 4.92755 0.0498911 +11.32 7.75758 0.226363 +23.979 3.19602 0.244025 +22.6542 1.60953 0.0528127 +30.1957 5.05198 0.258297 +23.3657 0.018859 0.0731661 +32.7784 3.36331 0.21657 +11.177 2.77265 0.0617409 +26.0996 3.3858 0.782651 +20.9568 0.444001 0.0319864 +14.4843 4.84386 0.0504319 +25.2834 5.45523 1.03874 +29.4047 6.55033 0.0858465 +20.6249 8.36815 0.214459 +25.1248 3.10045 0.46739 +15.0972 5.73598 0.0544224 +29.8872 3.86345 0.673812 +14.1899 1.87952 0.047656 +22.082 7.77244 0.348388 +21.1639 6.51922 1.02358 +18.4415 8.23997 0.589883 +31.8004 7.18028 0.0362496 +24.7192 8.01867 0.0999556 +20.949 1.11914 0.0651921 +25.0378 0.137345 0.096167 +24.8924 1.73565 0.167125 +12.4242 3.60197 0.0277642 +11.9416 6.01 0.0610183 +21.6176 3.83215 0.11662 +6.10567 4.48665 0.0809386 +28.2525 6.65107 0.0973934 +26.8778 0.617074 0.399804 +22.6611 5.92834 1.03387 +12.4886 0.425888 0.0434957 +33.7951 2.17319 0.223828 +25.9873 6.31267 0.543395 +29.9284 5.97002 0.139575 +25.2405 3.5422 0.627316 +19.9344 2.57443 0.0682235 +10.8558 7.23766 0.087123 +7.45728 2.40134 0.053814 +28.4467 0.272787 0.831012 +22.01 2.16112 0.0649787 +9.30731 0.18523 0.0173471 +8.39168 7.08544 0.0577589 +17.5647 1.06864 0.0785388 +22.4497 8.5125 0.0621748 +26.6447 1.86148 0.538595 +17.3457 2.67506 0.042171 +9.75657 5.11458 0.0518348 +23.8438 2.69374 0.131698 +5.77855 3.31503 0.0242582 +7.17027 0.910797 0.0472585 +13.7703 3.40048 0.0429768 +20.0493 0.220536 0.0233091 +2.12597 1.95747 0.0624804 +5.96859 7.94877 0.0614027 +24.8577 2.45399 0.255926 +15.0672 2.36765 0.020096 +16.2523 5.93675 0.105063 +17.0693 7.71657 1.03472 +7.27822 2.58568 0.0626475 +33.344 3.28911 0.180738 +31.963 6.24624 0.0519973 +22.433 6.92336 0.869935 +4.52864 5.36456 0.06288 +24.1772 1.56444 0.0998723 +28.1914 1.57963 0.844558 +12.5571 6.62451 0.0712035 +6.47139 6.17769 0.0557569 +25.4778 7.83348 0.0684817 +22.4655 6.81545 0.931694 +14.4543 3.68143 0.0448887 +11.2419 1.16482 0.0443936 +32.1499 7.78273 0.0627384 +4.37235 6.07905 0.0294633 +11.7366 5.03752 0.0257128 +17.3136 5.95954 0.157904 +20.9477 3.36617 0.0475665 +9.94383 3.00027 0.0635111 +21.1088 5.02933 0.324103 +5.21114 4.21921 0.0238017 +19.0423 6.1964 0.490899 +4.30667 8.18145 0.0191073 +14.3693 7.40536 0.430541 +25.0112 3.38649 0.532068 +8.09113 3.25545 0.0606317 +6.5234 8.10051 0.0746643 +33.3581 3.449 0.151397 +15.653 6.99409 0.34261 +3.39515 0.810564 0.0622287 +15.5369 3.94268 0.045184 +10.4846 4.51193 0.0385403 +28.4848 7.06925 0.0403098 +30.5128 7.67031 0.0700208 +26.6453 1.33424 0.426232 +23.7365 5.39394 0.986774 +20.3418 5.49868 0.362146 +3.40978 1.32446 0.0644907 +22.9768 7.28542 0.528647 +14.2766 4.83727 0.0657482 +10.8018 8.4054 0.667462 +20.1433 1.62836 0.0373929 +9.48777 4.58824 0.0589009 +34.0813 2.82697 0.105606 +21.2322 5.61523 0.603332 +26.2107 5.31457 0.939069 +5.08567 4.47861 0.0615718 +2.70896 2.83924 0.0672209 +7.18724 4.40446 0.0488169 +7.58279 3.25352 0.0484259 +19.6629 7.78542 0.788056 +23.144 4.03371 0.348603 +31.0559 4.15354 0.338685 +4.64322 2.25981 0.0578207 +17.9189 2.21852 0.0425307 +23.2632 8.33698 0.0696814 +15.2364 2.7444 0.0684424 +3.88099 7.58931 0.0536143 +4.68523 5.19835 0.0423148 +1.7978 8.56974 0.0637388 +12.0994 1.43401 0.0480588 +6.79807 8.22936 0.0993081 +32.8317 6.75139 0.0375706 +22.3144 4.96443 0.528297 +4.73226 6.19541 0.0284815 +23.46 6.31409 1.00565 +18.1482 3.91888 0.0732724 +28.9044 5.54534 0.340311 +7.71768 1.99761 0.0555719 +12.9451 0.186688 0.0531911 +17.6311 5.91266 0.172064 +18.3842 4.14675 0.0633967 +6.22595 7.91464 0.0520354 +28.9049 2.30769 1.03124 +6.70831 7.14898 0.0653708 +16.7732 8.6205 0.507893 +20.2756 7.84924 0.622584 +15.9666 8.4289 0.855316 +12.7381 1.39024 0.0443175 +25.332 1.4428 0.200192 +18.799 1.9954 0.0532857 +24.0547 3.15936 0.260727 +26.6585 1.0086 0.379518 +31.9902 1.31704 0.616886 +17.7611 0.161489 0.0668743 +15.2744 8.15695 1.02909 +31.8461 3.15585 0.386359 +33.3608 5.47151 0.043696 +30.3061 3.97264 0.549919 +8.21641 6.49427 0.0359934 +31.4792 7.0456 0.0773724 +22.8404 3.34733 0.171288 +18.7376 4.67874 0.0371366 +5.83407 0.852078 0.0533098 +6.85652 2.49506 0.0551683 +30.0812 1.94691 0.982742 +11.0295 2.69681 0.0485292 +17.1416 2.83571 0.0511706 +15.6173 5.02554 0.0417994 +14.2162 2.90841 0.0483237 +15.5421 2.85677 0.0549235 +10.746 7.26542 0.0622018 +31.1877 0.10613 0.889055 +18.477 0.0459696 0.0317035 +11.1924 0.499199 0.0592746 +8.51003 7.56487 0.0485538 +27.9485 5.53889 0.514849 +27.5426 6.3624 0.281632 +28.6028 8.3702 0.0751235 +21.1513 1.90532 0.044267 +12.4012 8.0408 0.58885 +8.49208 2.12033 0.0458609 +13.8101 4.53489 0.0567392 +4.46039 5.95255 0.0372681 +10.5941 6.96804 0.0397865 +12.9688 2.16849 0.048976 +5.31763 4.16919 0.0376556 +6.51424 0.398463 0.0682343 +5.45147 2.51606 0.0606667 +14.3358 8.57366 0.965145 +32.0773 7.9106 0.047526 +33.1468 5.08747 0.0648713 +4.9112 6.87395 0.0643765 +21.1443 7.88532 0.428523 +1.67852 2.90041 0.0499668 +23.4947 8.56957 0.0833681 +22.1584 3.92014 0.185929 +18.4304 1.38875 0.0458937 +10.6788 4.63285 0.0463891 +16.8456 5.18844 0.0802586 +2.78121 6.05537 0.0423518 +23.1216 5.86413 1.02864 +27.1756 0.362231 0.460334 +4.32425 5.58525 0.0673805 +34.4683 8.055 0.0710989 +7.35206 5.85673 0.0369484 +31.7153 8.52339 0.0564844 +33.8016 4.47058 0.0636641 +15.4935 7.08122 0.390496 +29.248 1.22598 1.00002 +20.0377 7.82703 0.676077 +10.4941 3.81447 0.0661701 +24.1706 7.66209 0.173662 +13.3447 2.80404 0.013566 +6.55665 5.45656 0.0649458 +26.9632 7.39923 0.0863687 +7.78371 6.21726 0.0458209 +32.3309 5.41818 0.0728353 +8.05487 8.48345 0.290589 +12.1572 0.769091 0.067312 +27.947 0.891439 0.715741 +29.0924 4.82081 0.514823 +14.7533 8.34399 1.02903 +21.6116 5.99981 0.923903 +32.648 2.70629 0.335274 +11.1001 0.941452 0.0620309 +10.3527 3.91802 0.0380783 +4.95716 4.19774 0.0581682 +24.2302 4.09272 0.616344 +24.302 5.41299 1.04367 +5.54978 2.3843 0.0610707 +25.2519 7.69464 0.117755 +11.0042 2.51999 0.0657348 +18.372 0.935825 0.0569811 +20.0372 3.0499 0.0600529 +20.75 8.17816 0.262818 +19.727 1.39336 0.0457643 +14.6777 2.427 0.0627336 +24.6178 8.09827 0.0958771 +8.44178 3.04223 0.0529303 +30.1973 7.36865 0.067848 +14.3017 0.397075 0.0503886 +27.0067 7.76536 0.0440052 +21.5762 2.07381 0.0605434 +16.4012 1.57815 0.0571461 +17.1983 6.48721 0.31656 +18.8267 8.64789 0.184521 +15.3117 8.38413 0.984006 +20.0287 6.57522 0.903522 +17.2243 7.99083 1.00954 +29.3359 8.62692 0.0297806 +10.1259 4.83126 0.0625643 +26.336 1.26391 0.339951 +15.0821 7.42364 0.556612 +29.601 3.46001 0.829979 +28.8232 2.73259 1.06365 +3.97813 4.37796 0.0533043 +32.1005 0.304757 0.667543 +28.3074 0.163442 0.731466 +23.1371 2.31011 0.0616649 +7.83274 5.20795 0.0611004 +28.9714 6.54989 0.114427 +21.0186 8.36586 0.132569 +9.14922 7.15055 0.0607058 +22.6124 8.22533 0.125148 +5.45804 5.56161 0.0715017 +24.5735 6.41429 0.760983 +7.86258 5.1336 0.027016 +11.5907 0.711723 0.0210725 +2.25748 0.541039 0.081885 +13.8209 6.64362 0.094315 +21.5139 1.76851 0.0671737 +23.1541 6.90302 0.746446 +15.2302 4.05585 0.0468435 +10.7412 0.0607171 0.0471632 +4.64233 4.90777 0.0361855 +13.2536 3.87215 0.0459178 +27.2986 0.645748 0.487781 +32.2969 2.28179 0.441153 +31.8833 2.68006 0.499358 +4.28294 3.02125 0.0506772 +5.71664 6.17769 0.0401029 +13.6441 1.46197 0.0763843 +19.9444 3.87437 0.0850788 +3.1575 5.30371 0.0509844 +4.45456 8.27099 0.0299869 +28.7248 1.00499 0.945556 +34.3858 0.693254 0.174824 +15.9593 1.67557 0.0414694 +27.6598 1.92786 0.826018 +24.6839 1.42259 0.116876 +26.8685 0.664207 0.39194 +15.3907 1.83143 0.0434026 +32.1612 1.95221 0.525574 +5.56981 4.19126 0.0482728 +10.2861 5.66121 0.071985 +22.4789 3.18021 0.108966 +9.73639 1.46988 0.0570503 +29.1796 0.827434 0.997126 +28.612 2.29929 1.03181 +27.5379 1.49293 0.680086 +30.481 4.20425 0.443115 +22.4744 7.61333 0.420153 +7.51404 6.26178 0.0628223 +12.3542 1.77627 0.032653 +1.64205 0.31313 0.0442398 +14.5511 0.590972 0.0351302 +22.4835 3.80881 0.188224 +24.4929 3.17782 0.350352 +25.0772 5.9645 0.867186 +10.067 0.733192 0.0573416 +31.9785 2.22478 0.545925 +19.4701 5.99372 0.471217 +8.34495 4.96491 0.0431541 +32.4171 4.51593 0.130576 +23.6888 7.88054 0.128997 +24.031 0.990511 0.0918189 +21.549 1.70848 0.0875635 +1.54527 4.94309 0.00996511 +19.706 4.94082 0.151303 +25.3187 8.56609 0.0518619 +23.4709 8.5427 0.0597769 +8.60489 5.13274 0.0461665 +17.1654 6.63249 0.377917 +25.3817 6.09541 0.764733 +31.2615 1.16184 0.835301 +9.08201 5.26403 0.0480927 +20.4022 2.67238 0.0517902 +3.77086 3.13733 0.0427777 +7.29324 1.36592 0.0848378 +14.0158 0.0728363 0.0315155 +2.05978 7.46087 0.0444431 +13.1949 6.26708 0.0370502 +30.2363 6.57423 0.0632216 +11.4749 2.51736 0.0736445 +6.47991 3.64801 0.0496114 +4.94094 1.53914 0.0377168 +19.7241 5.26302 0.197239 +14.7778 7.33462 0.437986 +22.1646 6.80693 0.958128 +14.7651 8.1614 1.04293 +18.8933 2.27648 0.0398284 +14.8743 6.12964 0.0637278 +11.0173 1.1075 0.0493419 +1.83698 4.68727 0.0255758 +10.8343 0.963582 0.0683159 +25.9863 4.72265 1.03286 +12.8836 4.73816 0.0524751 +26.9759 3.03234 0.894976 +2.88024 3.05458 0.0856989 +24.7106 6.21515 0.843719 +28.7372 4.20435 0.845271 +9.54125 3.56275 0.0425509 +14.0171 0.985122 0.0480813 +10.213 2.46721 0.0500687 +15.8875 7.40336 0.707893 +31.8321 8.49516 0.0352749 +24.498 3.26157 0.389372 +4.944 7.61086 0.0934303 +34.3915 7.63612 0.0357443 +23.935 1.90619 0.0797902 +28.3549 2.39311 1.00176 +29.2143 6.08339 0.151534 +31.8416 2.67453 0.518344 +27.1279 8.34929 0.0579744 +10.3972 3.19082 0.0675036 +26.1484 6.38197 0.449696 +23.561 6.18174 1.01436 +15.2682 1.86727 0.0547202 +30.2124 7.56867 0.0381444 +1.88906 2.87304 0.054622 +18.1572 0.96205 0.046119 +30.6881 0.438393 0.994789 +19.0561 3.949 0.0355492 +14.1074 5.53236 0.0209325 +5.06554 2.37188 0.0709923 +7.76839 7.35832 0.0524669 +10.2391 2.33809 0.0490539 +34.2743 7.40709 0.0893466 +19.5981 0.289104 0.0658176 +11.643 2.29567 0.0573344 +30.7689 1.39386 0.925032 +21.6573 3.9129 0.144346 +11.2221 3.06631 0.0542404 +23.0914 3.45018 0.199967 +2.34486 7.44248 0.0722614 +18.7239 5.06536 0.115224 +18.8673 5.29161 0.153198 +3.75857 3.40879 0.0385882 +23.2888 7.06215 0.610778 +2.26824 4.38558 0.0676335 +7.90376 6.52007 0.0544786 +25.9601 7.99006 0.0518414 +14.7546 5.34897 0.0573306 +25.2864 3.80329 0.741767 +19.2902 2.30406 0.0447246 +16.8136 7.9709 1.03649 +21.054 6.6852 1.05868 +2.16806 6.98786 0.0561359 +1.60276 1.93503 0.0381986 +32.4534 3.81673 0.191207 +24.2747 7.72261 0.150913 +7.56037 2.14996 0.0353952 +24.3759 7.11363 0.390644 +32.0534 4.70779 0.133182 +29.1184 1.91195 1.02468 +22.6729 3.28151 0.102642 +18.8269 0.666265 0.0383647 +22.0445 5.88268 0.9209 +7.5182 6.35332 0.050807 +16.221 0.253237 0.0696802 +30.4017 2.63436 0.854753 +13.4807 6.719 0.0851585 +10.3332 1.75117 0.0563342 +18.6835 4.69326 0.0670313 +28.3194 7.04338 0.103012 +30.6048 4.98153 0.222438 +34.4568 1.06822 0.136058 +33.3214 7.94613 0.043092 +5.06103 6.52628 0.0687571 +16.5416 4.93756 0.0459905 +19.7259 2.90904 0.0405128 +23.8689 5.08939 0.927429 +5.17452 3.42204 0.0682114 +13.5171 1.03736 0.0442528 +13.9176 1.06747 0.0609521 +21.5456 0.396018 0.0408498 +13.6818 1.48888 0.0714149 +11.9091 2.14639 0.0343494 +7.64447 5.55244 0.0463171 +23.3736 5.07067 0.812088 +11.6942 5.16131 0.0364792 +8.25709 7.18436 0.0501713 +31.5746 0.831505 0.788818 +14.1935 3.399 0.037907 +10.9349 0.693022 0.0788699 +4.48508 3.66003 0.0540033 +18.6472 0.688661 0.0553232 +12.7608 0.579852 0.0386763 +20.8023 0.911169 0.049381 +11.7621 3.40763 0.0635694 +16.9843 6.66952 0.375539 +5.73184 2.46392 0.039835 +3.54885 6.48339 0.0377473 +32.295 2.03892 0.459268 +25.7368 2.51026 0.462208 +15.3508 4.12028 0.0381371 +8.0745 0.655328 0.0748392 +23.392 3.92317 0.344175 +21.3957 5.10041 0.408609 +21.9852 7.20679 0.778981 +33.7635 0.490844 0.220769 +13.144 6.99127 0.0789585 +32.1327 4.97882 0.0953791 +15.8035 5.05464 0.0519341 +33.6773 0.446478 0.235091 +33.5881 7.69016 0.0540565 +15.2004 2.42389 0.0221792 +17.9605 4.7762 0.0674654 +31.0331 4.70759 0.263049 +30.958 7.95408 0.0645276 +4.64928 5.25638 0.0650021 +18.1605 8.66447 0.247024 +4.73929 2.02895 0.0436685 +29.4363 5.78737 0.208077 +10.4104 5.04149 0.0450621 +12.3676 4.4883 0.0597137 +12.0513 5.48541 0.0354316 +25.2174 4.81218 1.02753 +12.1829 2.94392 0.0526811 +16.0508 5.6074 0.0321078 +30.3316 0.728842 1.02862 +16.029 4.18663 0.058712 +31.7854 3.95766 0.27559 +31.7594 1.86363 0.644091 +16.4919 4.96509 0.0732737 +9.15155 5.85509 0.0490574 +32.4666 1.09262 0.505847 +19.9098 2.59563 0.0645429 +13.2222 0.5421 0.081908 +3.45702 5.2192 0.0449232 +16.8092 0.713919 0.0728007 +2.63859 2.00497 0.047385 +22.19 3.91434 0.163974 +14.525 3.94822 0.0366086 +11.1181 0.288305 0.0371063 +8.74768 8.27963 0.247689 +26.4773 2.0105 0.516242 +31.1738 5.33115 0.116627 +1.7859 1.94209 0.051969 +18.9788 5.20708 0.158257 +4.07674 3.08301 0.0488596 +2.66421 2.78445 0.0392044 +33.9415 4.14624 0.0843698 +11.8386 5.12443 0.037498 +19.1128 5.22814 0.158361 +31.298 6.01748 0.0847565 +22.6951 2.34744 0.0777581 +13.0294 0.800606 0.0435472 +24.6688 2.21429 0.205175 +27.3374 2.85536 0.899143 +22.806 5.30845 0.831192 +14.2584 3.62172 0.0855537 +26.4344 2.72043 0.67998 +13.8215 0.859518 0.0190044 +13.4503 5.21462 0.0400072 +10.7847 0.961179 0.0282976 +33.6394 4.51766 0.04662 +5.44337 6.01627 0.0478917 +3.24253 5.44588 0.0475425 +11.1443 1.39597 0.040532 +5.57654 3.1111 0.0666413 +13.5885 8.22376 0.956146 +32.4732 0.793359 0.511666 +32.7791 5.93236 0.0613178 +9.02693 3.8142 0.0310184 +11.254 5.36201 0.0447148 +14.7713 3.44111 0.0374828 +32.841 1.08188 0.426615 +10.2456 4.91533 0.0459399 +26.6656 0.0533629 0.2932 +22.5921 7.55176 0.423779 +30.5222 6.98101 0.060625 +10.637 3.26666 0.053666 +32.1211 4.69644 0.147284 +34.3855 4.61037 0.0439902 +14.5772 8.56234 0.945123 +32.3745 0.382131 0.581612 +25.6606 2.60175 0.457949 +8.94671 3.55738 0.0167524 +16.0746 6.88201 0.375757 +9.78039 6.38031 0.0470906 +31.2965 3.70018 0.414261 +9.08077 0.102194 0.0541612 +30.466 5.66303 0.138819 +10.64 3.555 0.0683917 +30.9313 1.02093 0.928278 +27.6706 5.51498 0.576262 +25.6794 3.88845 0.835915 +10.3669 2.67798 0.0365858 +25.1524 2.35236 0.297138 +22.1834 4.55197 0.373108 +12.9703 2.16462 0.0375454 +34.0484 3.43732 0.0974762 +33.0694 8.23277 0.060018 +23.0567 4.17556 0.372895 +31.4539 2.21547 0.648835 +12.5327 4.55576 0.0203944 +7.10811 8.01741 0.0803657 +12.5526 7.57102 0.235435 +20.2584 5.91916 0.59145 +31.2587 7.85775 0.057739 +29.1817 6.61304 0.0717816 +9.45077 5.94449 0.0566947 +28.5031 3.74123 0.953839 +30.6996 1.30575 0.952395 +20.7569 6.39798 0.924455 +17.8429 5.54346 0.122055 +17.0087 8.45926 0.639001 +8.01422 2.8055 0.0526714 +10.0042 7.6627 0.106637 +18.2215 1.9102 0.0403112 +18.4053 8.27029 0.555142 +19.0197 8.70329 0.134624 +16.2422 0.412258 0.0649158 +29.5363 2.8373 0.966533 +7.02442 8.72485 0.381867 +7.37362 8.46304 0.211615 +32.255 5.86127 0.0580174 +20.8234 1.17538 0.0352231 +21.884 0.0578376 0.0184245 +17.0668 2.17828 0.0640545 +15.63 8.25436 1.01228 +13.0673 7.03491 0.0907194 +19.1116 3.74508 0.0443305 +28.7878 6.65315 0.0975175 +2.95636 7.88592 0.0204034 +32.5943 2.62124 0.341167 +7.7637 7.64221 0.0556554 +17.6461 2.35576 0.0566841 +28.8007 2.87286 1.04648 +5.88106 0.390854 0.0348912 +22.9693 1.4 0.0352412 +21.4936 4.81455 0.319484 +19.7862 0.209233 0.0419749 +28.8957 4.77027 0.623684 +5.78696 1.48563 0.0454655 +8.76956 6.54411 0.0560174 +31.5864 1.11859 0.759741 +32.8261 1.8093 0.353421 +5.59818 2.35019 0.061141 +23.625 5.09297 0.869032 +4.72906 7.06617 0.0279903 +32.333 6.58306 0.057445 +32.6352 6.47842 0.0697358 +24.3024 5.95818 0.996406 +17.332 4.74891 0.0513603 +23.0036 2.99453 0.121534 +19.9809 1.57201 0.043348 +28.722 7.54911 0.0697387 +28.9977 3.57632 0.943521 +29.9158 5.68282 0.173238 +29.1614 0.596761 0.991037 +8.79087 3.15409 0.00569642 +8.29097 0.877633 0.0521435 +24.5049 3.09862 0.35025 +19.6918 6.40849 0.759522 +33.4547 2.98158 0.152798 +3.35637 5.96232 0.0378641 +14.8329 1.0765 0.0597875 +30.1308 3.36465 0.763564 +16.3153 3.5841 0.0563363 +5.92498 4.44907 0.0324683 +12.2957 1.99613 0.0537193 +33.2472 6.10644 0.0731936 +26.9556 7.71138 0.0610917 +29.1966 7.10239 0.0795643 +10.0897 0.269861 0.0625887 +27.9782 4.26611 0.967178 +1.6742 3.00661 0.0395333 +8.74017 5.00665 0.0398798 +22.1474 0.727956 0.0406569 +23.2271 6.37704 1.0116 +16.5341 3.64263 0.068367 +33.1429 8.24977 0.03406 +13.7024 0.166719 0.0303626 +10.4807 3.65108 0.051483 +8.23168 4.25376 0.0691862 +26.1461 7.26367 0.139081 +28.4389 6.61082 0.127221 +28.713 1.59087 0.984901 +22.0275 2.6896 0.0772778 +2.6505 6.43066 0.047589 +15.1729 3.66824 0.0567907 +7.0181 2.29903 0.0598424 +10.8433 8.13388 0.394619 +2.41043 7.36903 0.0746991 +24.4048 3.1061 0.309256 +9.75536 4.80297 0.0438411 +20.0687 8.12114 0.407843 +33.2437 1.89271 0.306428 +9.27919 7.94586 0.143738 +10.9342 7.73029 0.15536 +9.96029 6.34951 0.0508602 +3.68362 5.99616 0.0428462 +20.222 5.98713 0.596222 +19.718 4.75646 0.106231 +21.2684 5.0892 0.390114 +33.2716 1.72472 0.304921 +20.5282 4.91719 0.207345 +21.6297 5.88149 0.846845 +30.8854 4.24365 0.367038 +30.351 5.93695 0.124857 +18.7845 4.00461 0.0598527 +21.4924 7.02245 0.951356 +13.6143 6.30061 0.0366437 +17.9279 0.778248 0.0506117 +17.443 6.72377 0.500558 +19.0039 2.48919 0.0410708 +9.27515 0.711694 0.0393497 +21.3262 1.05948 0.0417024 +10.8653 2.83168 0.0519261 +11.7516 4.58537 0.059694 +11.6203 2.88598 0.0374861 +27.3968 1.7781 0.734416 +3.81543 6.72304 0.0708116 +34.3371 3.26681 0.0915919 +6.94605 7.63801 0.0565623 +21.9107 2.13693 0.0790058 +9.16259 6.02965 0.036121 +24.0959 3.12429 0.267831 +29.7632 7.42654 0.042324 +15.8195 5.64952 0.048376 +16.9706 5.93092 0.140021 +33.9615 0.811588 0.204596 +20.9465 6.56697 1.02128 +13.5831 3.09764 0.0509791 +9.09048 6.46929 0.0468661 +29.5154 7.38708 0.0748847 +24.5729 1.06978 0.0999215 +11.7734 0.134103 0.0370501 +18.2036 6.62453 0.570256 +6.03709 8.46495 0.105286 +27.1408 7.86933 0.0573175 +31.1967 3.80665 0.40243 +25.5149 7.28595 0.179467 +24.9888 0.20549 0.0915653 +5.79387 0.511331 0.0563017 +31.5634 5.082 0.134108 +21.1426 4.61071 0.196901 +31.0723 7.20947 0.0565826 +26.1268 2.04037 0.442337 +30.8158 4.04371 0.414084 +28.1711 8.64823 0.0661487 +25.6265 2.64624 0.461943 +21.2873 3.53493 0.0725876 +10.7071 5.30978 0.0638745 +9.78896 8.69718 0.861578 +31.6997 1.59718 0.668749 +27.8201 6.03908 0.321099 +9.82834 3.62007 0.0630063 +4.94156 3.50237 0.058982 +30.421 2.24456 0.926996 +1.94538 4.06777 0.0693976 +22.093 5.02384 0.493058 +24.8502 3.42823 0.518744 +15.5273 4.41987 0.0259724 +31.655 5.55864 0.0700271 +22.8227 7.4442 0.435156 +34.2531 4.90352 0.0641942 +24.3011 3.34364 0.342081 +19.9068 8.4035 0.239993 +4.15312 1.50787 0.0274151 +30.5812 0.397608 1.03692 +5.37326 7.93899 0.0441833 +10.5985 2.32353 0.0496209 +22.9012 1.32427 0.0405208 +24.0744 3.36738 0.322218 +32.0441 2.99688 0.411539 +32.7162 3.09975 0.260879 +31.9075 7.43874 0.0326895 +27.5691 2.53835 0.895087 +14.1042 1.15935 0.0474878 +25.3844 1.88508 0.286794 +6.63496 8.30033 0.0955549 +12.206 3.74808 0.0563549 +34.3793 3.15266 0.113275 +22.6025 6.79419 0.947585 +32.1484 8.17713 0.0594979 +6.97966 4.45921 0.0339293 +21.2613 7.11931 0.935353 +7.56753 2.31239 0.0752593 +24.8198 3.06288 0.400559 +32.0915 5.03807 0.0737143 +15.9043 2.06611 0.0422194 +2.07971 2.40538 0.0430128 +12.4788 1.63914 0.0447424 +4.36203 2.29263 0.0594506 +16.5957 7.33569 0.785712 +27.957 6.94648 0.116262 +33.891 0.995808 0.189415 +10.6775 3.0496 0.0542562 +6.42528 6.31243 0.0468105 +3.0587 7.514 0.0340016 +11.6539 4.89615 0.0624336 +10.0133 6.35056 0.0301846 +8.34194 2.07495 0.0291281 +30.6905 6.83305 0.0787383 +10.9362 7.17447 0.0729895 +6.73866 3.55716 0.0621808 +6.48093 2.06124 0.06954 +25.4357 0.289165 0.114391 +15.6999 5.50224 0.0782273 +14.3064 5.52837 0.0530929 +24.7541 0.0104554 0.0902129 +22.3461 4.5461 0.388343 +16.7437 8.20201 0.941715 +21.9511 0.252378 0.0446759 +11.6746 1.20088 0.049759 +15.7549 0.504839 0.0524324 +23.8037 3.81534 0.424203 +2.21911 3.36295 0.0169063 +14.9703 7.60525 0.699756 +14.1079 0.580606 0.0360687 +12.1634 1.75247 0.082671 +18.0352 8.19803 0.726262 +24.2414 0.166136 0.0939847 +25.8844 2.26502 0.425779 +26.9265 3.44578 0.995102 +21.966 0.00511966 0.0567182 +2.01175 2.51017 0.0563677 +9.64428 5.2696 0.0569572 +23.5404 5.37751 0.925831 +5.37286 5.32047 0.0278584 +20.6404 5.68826 0.516333 +2.0058 5.37048 0.0513696 +29.1859 0.302085 0.973245 +7.01407 5.72281 0.0624542 +15.8447 5.10239 0.0581056 +5.48492 1.90314 0.0687433 +7.32833 3.66667 0.0395098 +17.0433 2.07728 0.0553778 +18.7097 7.5863 1.02091 +29.9997 2.43669 0.946078 +8.34705 1.42375 0.0233981 +30.6079 6.96256 0.0519502 +9.32322 6.65233 0.0363237 +14.5018 7.44799 0.454617 +33.1492 5.92014 0.0388515 +18.4647 3.64318 0.0346962 +9.90031 4.156 0.0923235 +16.448 4.8524 0.0531768 +28.6425 5.20902 0.46849 +30.2276 8.26104 0.0468922 +5.22279 2.31971 0.0500958 +18.5124 6.71598 0.691611 +13.8865 0.14791 0.0656987 +4.24849 1.66266 0.0357817 +16.0011 8.25394 0.995851 +11.2316 2.19443 0.0426866 +25.87 4.46662 1.01163 +2.03663 8.01009 0.0983454 +16.4372 5.72096 0.0707077 +12.8677 4.26911 0.0264643 +15.3883 6.13393 0.0765477 +15.8026 8.55095 0.738224 +33.7979 8.2261 0.0658011 +24.123 1.58421 0.106501 +14.1819 1.2239 0.0314294 +12.6279 2.51668 0.0760058 +15.9192 1.26838 0.045855 +32.3201 6.67785 0.0306517 +21.1926 7.2872 0.884247 +32.9726 1.22981 0.394408 +8.28126 0.639491 0.0339371 +18.6936 2.82955 0.0323796 +25.301 6.00369 0.823518 +28.7306 6.73176 0.0800769 +4.92592 4.02646 0.0575636 +14.8962 5.58532 0.0575818 +30.2825 8.01933 0.0312584 +21.7499 7.69313 0.449092 +6.18241 1.02881 0.0454586 +15.3778 6.23366 0.114234 +18.0597 6.78738 0.660143 +1.56173 7.92972 0.0583313 +15.3913 3.65435 0.0394493 +17.1476 1.36386 0.0558149 +33.2673 4.14087 0.096549 +18.5928 3.23591 0.0561177 +18.8919 3.44111 0.0370849 +4.02291 7.01613 0.0378252 +2.73447 5.69736 0.0784914 +16.6779 5.94442 0.128217 +22.3062 5.70455 0.86914 +8.69694 1.23827 0.0669414 +20.4193 0.510068 0.0465352 +4.64632 3.45277 0.0695979 +10.5439 7.42658 0.0594911 +18.7128 5.6696 0.215171 +11.6411 6.91644 0.0551531 +16.1767 5.44038 0.0493177 +25.0181 6.85509 0.400714 +7.38937 8.70648 0.451062 +23.1359 6.23421 1.03442 +33.2259 7.53115 0.0442488 +28.725 8.50286 0.0239175 +13.882 6.47285 0.0899146 +22.579 7.10548 0.748168 +5.22425 0.686802 0.0465662 +25.3164 0.318033 0.102608 +26.1858 8.07053 0.0629716 +8.79608 2.07762 0.0491848 +14.0371 2.66857 0.0270358 +21.4571 1.18188 0.0816379 +23.2163 5.54589 0.981554 +21.9691 0.259466 0.0345948 +26.2059 6.98405 0.189978 +29.5709 2.60788 1.01623 +16.9956 6.50145 0.324088 +1.9978 7.95957 0.0584154 +28.1051 6.03328 0.290328 +5.10794 3.7106 0.043797 +18.9925 5.46155 0.221898 +25.0497 7.47515 0.154056 +28.1399 6.21645 0.227049 +19.0028 2.20472 0.0556794 +22.3893 0.0666054 0.0463318 +33.3292 7.68909 0.061698 +26.9155 5.44843 0.779299 +10.8797 0.554975 0.0160797 +32.3473 4.81535 0.103689 +23.8776 5.14801 0.958445 +11.471 2.56397 0.0714016 +10.6101 7.7439 0.141105 +16.1172 2.09546 0.0602632 +18.3139 1.25717 0.0529354 +29.3826 2.70351 1.01911 +16.5697 2.9575 0.0405026 +24.9688 7.7268 0.122256 +12.5574 5.74275 0.0687064 +16.9788 1.06574 0.074412 +12.2016 3.44066 0.0541945 +14.4422 0.898106 0.0522549 +24.5749 6.16804 0.900485 +9.14602 7.47225 0.111045 +17.6527 0.817159 0.0467552 +25.985 4.36904 1.01882 +1.83683 2.13795 0.0771355 +14.9916 2.49128 0.0331047 +26.9977 6.52701 0.226032 +23.1599 8.53924 0.0742579 +28.3928 2.43765 1.03954 +9.98368 4.742 0.0706127 +21.7772 2.71213 0.0804175 +11.6697 0.543097 0.0672015 +4.28539 5.26558 0.0399915 +27.4376 1.35779 0.658028 +11.3602 6.01021 0.0356008 +25.6862 6.53064 0.447301 +26.9463 1.24409 0.487714 +28.0961 5.21152 0.583825 +11.0702 1.8211 0.0337129 +27.9563 6.66981 0.127471 +9.56303 2.49908 0.0440713 +23.4332 6.02687 1.04905 +1.78481 5.66258 0.0348078 +27.4184 5.02712 0.815025 +3.57884 1.90223 0.0479386 +25.2032 7.2916 0.204186 +22.3752 3.10117 0.109117 +3.5689 6.73471 0.0619618 +20.7383 4.15126 0.111031 +24.807 3.91621 0.67584 +28.1509 6.22482 0.230856 +33.8845 2.97911 0.136415 +9.41253 6.67267 0.0533808 +15.5502 3.88861 0.0364626 +13.604 6.47232 0.0720731 +23.352 6.09656 1.05815 +7.76587 8.26215 0.131491 +19.9669 3.57154 0.0784415 +9.36681 3.05017 0.0424446 +8.36544 8.69481 0.592244 +26.3907 4.51288 1.07574 +19.5964 3.65741 0.0599682 +27.1911 6.12439 0.385398 +23.367 2.18614 0.099982 +29.5736 8.37074 0.0442434 +17.2157 6.58866 0.370999 +1.91751 5.67256 0.0661163 +34.4919 2.55076 0.0837117 +9.07825 7.07351 0.0655501 +28.6805 8.0332 0.0388089 +8.07666 6.91692 0.0646033 +18.3503 7.97341 0.855929 +11.0268 0.80575 0.0779926 +23.1807 6.4783 0.958578 +4.11762 7.57152 0.0161413 +3.87867 4.16172 0.0946765 +16.7328 0.700446 0.0506782 +29.5166 4.74212 0.475134 +16.8785 2.11979 0.0500269 +5.39322 0.284948 0.0452996 +34.2682 8.17886 0.0546622 +4.98896 7.2744 0.0386488 +11.7372 5.54521 0.0833601 +20.1706 6.54196 0.906658 +10.3909 5.86843 0.0600171 +24.5746 4.04517 0.683224 +15.3356 1.05991 0.0467602 +27.7255 2.52361 0.943906 +4.22197 4.42305 0.041907 +28.6402 5.08742 0.520716 +31.2919 2.63516 0.623941 +9.88274 1.1119 0.0666446 +9.33937 2.9921 0.0660982 +30.611 1.62245 0.931838 +15.1525 8.13325 1.05962 +14.6269 1.93121 0.0363141 +20.7871 3.4441 0.069586 +19.6146 4.73588 0.121134 +28.3229 2.20506 0.952938 +23.6018 4.32544 0.558869 +24.7865 4.83075 0.97384 +15.7041 0.429184 0.0274972 +18.4528 8.28043 0.568419 +29.7141 4.70188 0.454211 +30.0961 1.27648 1.04133 +21.6797 8.58633 0.0712833 +21.0055 1.47044 0.0401714 +11.5044 6.35792 0.0309956 +17.3506 4.21656 0.047678 +7.97557 2.25099 0.0338118 +17.5297 3.31788 0.0431475 +3.47916 4.20565 0.0559301 +10.0048 3.24184 0.0618883 +10.9153 2.15897 0.0562157 +17.1317 3.63656 0.0542495 +24.0105 3.80892 0.444462 +4.7271 6.01094 0.0641403 +22.6428 1.72661 0.0774054 +27.0826 0.397489 0.415228 +6.97146 7.28714 0.043447 +6.08603 0.605629 0.0348343 +4.84511 5.51873 0.0452061 +22.3689 4.79789 0.471651 +14.5822 8.52177 1.00022 +2.76955 1.65997 0.07037 +5.34218 7.75829 0.0298126 +13.4943 1.13878 0.0615004 +3.2185 1.49896 0.0460735 +27.6672 5.71423 0.451586 +7.88927 1.44793 0.0535671 +28.2421 5.1261 0.595619 +2.66632 5.89963 0.0543801 +12.6535 1.09629 0.0576154 +32.1789 6.90598 0.0404195 +6.50717 3.69781 0.0724236 +2.70622 8.45209 0.0550005 +29.1439 0.0999511 0.951771 +27.7952 0.686942 0.624145 +17.881 5.41998 0.137715 +5.66317 2.248 0.0374984 +11.176 3.51949 0.0506474 +16.1653 7.94362 1.04985 +33.2792 3.00517 0.205983 +8.55207 0.447902 0.0290951 +23.3046 1.25006 0.0256114 +24.8316 4.85565 0.991402 +30.227 6.77154 0.0359974 +23.7446 6.62292 0.799189 +13.8642 4.12134 0.0808954 +16.0934 2.09438 0.040463 +17.1836 1.12316 0.0597606 +20.9237 1.95126 0.0336511 +19.0582 1.75127 0.0949224 +4.88545 7.93085 0.0449015 +27.0127 3.56376 1.00699 +13.5262 1.01983 0.0473161 +22.1186 5.57742 0.784825 +11.7639 3.99624 0.0656936 +16.2641 0.254152 0.0458755 +34.3623 8.56311 0.035367 +1.96361 8.27061 0.0415007 +22.6034 2.29503 0.0654033 +7.72303 7.28033 0.0515066 +33.007 3.97762 0.134747 +28.5804 5.88788 0.250507 +28.9584 5.71031 0.256199 +29.1405 6.39673 0.141534 +12.8469 3.84993 0.045024 +18.2059 1.28111 0.0696108 +31.9341 7.02958 0.0426825 +17.0482 3.84928 0.054509 +32.5133 3.86357 0.177985 +28.356 6.63591 0.0874326 +17.6118 5.99025 0.184493 +18.8919 8.64368 0.206022 +18.6579 3.39043 0.0687569 +10.3696 5.5487 0.0648986 +13.0469 0.732131 0.040044 +15.6098 0.983699 0.0434411 +19.0932 2.22608 0.0309922 +30.4034 2.44673 0.887988 +17.4917 3.89979 0.0614115 +19.9327 1.99983 0.0519622 +24.8309 8.18185 0.048337 +27.8801 3.74067 1.04732 +33.5996 3.34176 0.136061 +25.8803 7.88378 0.0709084 +21.2589 6.95456 1.00929 +25.1629 6.73136 0.461744 +5.53369 7.98922 0.0407695 +25.8932 6.43761 0.465498 +8.73193 2.67109 0.00971444 +30.5536 8.40203 0.0516505 +30.2828 0.237264 1.04062 +22.2327 7.84066 0.278641 +32.4571 3.6657 0.242187 +3.18733 2.95119 0.0671202 +4.00535 4.1478 0.074766 +17.8631 3.49428 0.0472552 +8.04436 7.52026 0.0490254 +2.23956 7.04536 0.0537994 +33.167 6.52581 0.0636112 +15.3241 2.03386 0.0718816 +29.2207 3.84466 0.81898 +10.9212 0.655895 0.0463473 +22.3558 6.48949 1.00667 +3.78129 0.368396 0.0449505 +21.933 3.46057 0.120021 +24.8038 1.32073 0.141799 +21.8675 7.44703 0.663538 +17.3723 8.02799 0.975693 +25.8689 2.53032 0.481012 +8.93456 7.62477 0.053885 +23.7674 1.55603 0.085976 +10.9686 6.21536 0.0747031 +3.65187 2.19081 0.029943 +13.8565 3.89388 0.0345096 +26.3326 5.34022 0.911998 +6.96513 3.06904 0.0439561 +27.0753 1.72917 0.624036 +13.2972 5.13786 0.042148 +17.7348 6.93918 0.710052 +3.79355 8.70344 0.092111 +7.02134 2.81703 0.0524825 +23.4822 2.25157 0.122478 +31.663 0.668012 0.73922 +10.3095 1.7892 0.0226673 +24.2148 0.226658 0.0440284 +30.5337 2.18231 0.892121 +29.4226 6.34511 0.104759 +32.9013 1.00247 0.41764 +5.06584 5.64905 0.0539452 +23.5591 2.57736 0.133672 +11.121 2.00024 0.0219244 +2.90802 4.22656 0.0390699 +17.9717 3.81273 0.0567476 +33.096 3.90491 0.113108 +12.8886 1.02774 0.0417262 +10.1376 4.93464 0.042496 +34.2 7.88441 0.0566318 +23.6621 0.0811883 0.0426918 +6.68608 0.707897 0.023514 +2.18266 5.12122 0.043489 +4.21508 2.78423 0.0446997 +4.21983 2.20174 0.0618495 +30.7585 8.63475 0.0398946 +24.692 2.69356 0.280946 +7.49582 8.04829 0.0710272 +15.8044 5.39101 0.0480132 +16.3237 0.264739 0.0807973 +30.0019 2.32392 0.93706 +33.6242 0.661933 0.269646 +1.78388 4.70163 0.0369115 +2.38924 7.93881 0.050295 +21.7326 0.459915 0.0258981 +3.30419 0.244958 0.0521109 +15.1766 0.256309 0.0366836 +22.5416 2.38588 0.0796401 +18.534 1.45272 0.0698987 +7.62928 8.46278 0.211179 +10.9705 6.77391 0.0280831 +19.3113 8.20933 0.48904 +6.53371 5.0679 0.0789173 +32.1792 7.17609 0.0494356 +9.86815 6.19552 0.0174791 +29.3749 2.55307 1.0051 +7.96789 5.70439 0.0636936 +17.9808 2.5281 0.075963 +28.5484 4.05749 0.90798 +23.4222 7.19052 0.487844 +5.95592 1.92895 0.0667089 +10.0287 1.52866 0.0282862 +14.1399 7.4428 0.385968 +18.1825 4.04838 0.0420427 +33.372 1.51925 0.279103 +13.0196 2.7779 0.0573705 +13.4122 2.45217 0.034255 +19.2334 7.83295 0.825265 +9.02844 4.13204 0.065738 +28.9086 8.04244 0.0660213 +1.50628 4.11025 0.0697057 +26.2861 8.21044 0.0456744 +31.6505 8.01059 0.0229835 +12.4269 4.96063 0.0520129 +26.728 6.8728 0.188719 +25.4718 2.50965 0.39747 +27.1916 1.00923 0.513696 +23.4635 5.28093 0.924777 +19.542 0.482368 0.0334461 +10.8656 4.08594 0.0616469 +15.5226 5.41899 0.0811831 +5.31257 6.70861 0.0513042 +22.4753 7.83679 0.266702 +18.4828 1.78366 0.0590052 +3.68398 7.56412 0.0577018 +5.16306 4.33756 0.0580173 +12.3228 5.44313 0.0524833 +7.981 4.03785 0.0450295 +7.98784 7.40965 0.0756275 +27.0424 2.622 0.785287 +12.3271 0.0710184 0.0664854 +3.63768 7.24727 0.0362405 +31.8588 5.78276 0.0750921 +32.9688 6.26226 0.0365238 +28.7763 1.08545 0.918762 +16.4234 1.80188 0.0334945 +33.3231 5.46629 0.0418562 +31.4622 6.53022 0.0744333 +22.8696 5.77845 0.986845 +24.5023 5.209 1.01632 +25.315 5.72286 0.924763 +19.5625 1.29194 0.0486924 +30.5611 3.93926 0.498618 +30.0088 2.87988 0.899951 +26.317 6.43434 0.393039 +24.8466 4.52216 0.877754 +16.8592 6.29328 0.207099 +12.6308 6.0728 0.063827 +33.3899 7.91152 0.0604351 +20.6115 7.48338 0.823955 +32.5115 0.451212 0.509815 +8.83435 5.83604 0.0541316 +6.96565 3.26413 0.0402374 +25.8315 0.881486 0.193311 +23.5176 4.18051 0.477177 +15.2167 7.3698 0.528403 +18.5863 6.9606 0.886327 +32.1653 0.924712 0.619919 +23.9602 1.76837 0.0925664 +19.656 6.07377 0.531189 +10.7565 8.46203 0.712506 +7.09052 0.674568 0.0608592 +14.0229 0.775638 0.0505871 +11.5563 6.07975 0.0612895 +24.8207 5.38359 1.04316 +34.2532 7.08888 0.0603704 +29.901 1.73287 1.02587 +10.8058 4.3198 0.0394174 +11.4274 6.99992 0.0630875 +21.241 3.34201 0.0704105 +29.5211 3.49249 0.857725 +11.5631 0.28696 0.0499472 +10.7653 6.97298 0.0701747 +17.2941 3.90332 0.0394511 +26.9544 3.30369 0.917272 +16.5168 5.72829 0.0888733 +1.92964 7.45472 0.0777964 +24.1603 7.57111 0.217985 +30.8066 2.993 0.664092 +24.3347 5.31639 1.03827 +2.60389 3.38645 0.0661351 +10.4518 7.33154 0.0826695 +18.7809 6.93584 0.92744 +17.8763 5.78359 0.177896 +4.11533 7.19764 0.00693119 +10.8944 4.45573 0.0579807 +14.8673 4.99083 0.0380588 +18.119 1.42145 0.0352124 +33.9549 6.18722 0.0238333 +25.1694 2.5186 0.314777 +18.9197 4.91031 0.0894823 +1.80722 0.0181035 0.0658652 +2.56413 3.47337 0.0665909 +27.0492 5.38222 0.765831 +20.1852 5.51249 0.387682 +23.7832 4.92715 0.834257 +5.73982 2.20264 0.0364586 +33.7083 5.71928 0.0810688 +18.4649 5.77912 0.228872 +20.4478 5.1561 0.271399 +23.9995 3.29553 0.310889 +22.381 1.32925 0.0417557 +27.4661 4.86472 0.895931 +8.42867 0.453608 0.0293583 +3.54604 8.00778 0.0414569 +15.4472 3.08165 0.0464641 +27.2364 2.47268 0.811531 +26.3985 3.93898 0.971054 +14.3374 0.907541 0.0624026 +26.1448 7.72769 0.0803514 +27.0886 8.56145 0.0502959 +27.7398 7.19519 0.0869707 +33.2176 5.01475 0.0607863 +16.7505 8.22197 0.93028 +25.5925 2.88779 0.508933 +31.7105 4.06511 0.242544 +11.0989 1.49924 0.0466915 +33.9939 3.71203 0.0898619 +12.6537 8.1072 0.70118 +23.4464 5.7979 1.05354 +6.05641 4.76929 0.0752869 +7.77884 4.68447 0.0292123 +4.51407 5.55031 0.0252152 +22.559 5.33055 0.770629 +2.36443 3.95501 0.0695158 +17.4449 4.46474 0.0684411 +26.0472 0.885722 0.248168 +10.4548 3.8757 0.046137 +7.87042 5.8973 0.0453788 +33.27 1.95203 0.308875 +8.20048 2.42029 0.0467349 +20.8244 7.03416 1.03144 +16.7995 1.18764 0.0313392 +31.4115 1.70834 0.764295 +22.3194 7.1536 0.748102 +17.4178 1.05709 0.0460351 +9.8808 2.62652 0.0455296 +29.141 7.95654 0.0484039 +11.8478 3.60903 0.0912311 +28.2646 7.14485 0.0802955 +24.5528 6.68234 0.617176 +19.7648 0.174562 0.0386611 +34.3757 1.91936 0.120101 +22.7766 7.96975 0.144603 +21.2434 2.80821 0.0502115 +24.8107 3.53006 0.535591 +7.02046 8.17012 0.0889551 +15.9923 0.958692 0.0281684 +1.50806 8.49241 0.0612738 +5.99428 5.65233 0.0417416 +2.64906 5.49331 0.0673692 +20.4325 0.00739173 0.0463876 +12.2505 5.51252 0.0510375 +7.21266 7.1855 0.0394706 +14.6866 3.44417 0.039918 +32.8518 0.638941 0.429225 +3.34893 1.15406 0.0590268 +5.18497 0.678113 0.0612353 +30.4304 3.62693 0.60594 +8.68513 7.14116 0.0329061 +23.4835 0.512979 0.0644849 +4.40104 3.54629 0.0697585 +2.72647 3.50328 0.042939 +21.8699 3.47914 0.09925 +25.987 4.60364 1.03915 +8.16168 4.6782 0.0554605 +16.787 1.78349 0.064841 +6.76784 6.94349 0.0362969 +28.8374 4.51888 0.712806 +5.94929 3.0772 0.0720862 +20.0709 8.42641 0.21841 +32.7015 6.169 0.0419925 +8.47345 1.42485 0.028617 +25.5573 8.4704 0.0521992 +17.7913 5.05397 0.0923191 +29.9065 2.97402 0.90773 +30.0186 2.0752 1.02169 +17.9781 0.59295 0.0451217 +17.0233 2.13197 0.0515887 +26.881 2.52604 0.729668 +25.034 3.00771 0.433829 +12.3926 8.63488 1.04715 +32.655 5.17797 0.063927 +18.1197 1.67957 0.0449341 +9.63914 3.23894 0.059257 +14.5507 3.77899 0.0640064 +14.7417 0.726715 0.062605 +17.9822 4.09378 0.0881374 +12.8859 4.44245 0.0622497 +16.6288 7.95001 1.03502 +33.6237 3.78641 0.124895 +34.2656 6.01898 0.0388664 +9.95312 5.94026 0.0345402 +11.6898 1.52915 0.0618621 +13.9712 4.95715 0.0423723 +9.12855 3.92449 0.0511952 +8.82624 1.42168 0.0435198 +17.5125 0.20916 0.048294 +15.2538 8.70362 0.641359 +4.06255 0.791065 0.0646428 +18.6868 7.74897 0.983634 +17.1332 3.20353 0.0476312 +32.9122 0.714081 0.432952 +29.6109 8.43077 0.0641826 +22.687 5.77073 0.991904 +18.7795 3.53565 0.0750716 +5.22287 7.32375 0.0329716 +22.7647 6.18541 1.06356 +32.2913 3.63196 0.246126 +33.3796 6.27406 0.0637914 +32.1655 6.44868 0.0501649 +25.8625 1.50492 0.321472 +12.8668 6.26098 0.0804419 +7.48516 8.01078 0.0762531 +8.84333 6.01001 0.0581682 +14.5123 6.98633 0.213297 +5.33438 8.6524 0.153779 +11.5807 6.9602 0.0795393 +33.5862 4.10578 0.0800755 +26.1244 4.91326 1.01678 +24.7116 6.70201 0.581859 +6.717 5.52665 0.0567453 +18.2202 0.61981 0.044781 +17.7181 0.525776 0.0414233 +32.0489 2.38294 0.470482 +23.609 7.15165 0.496904 +11.4558 1.93679 0.025635 +10.0539 2.78814 0.053604 +5.0333 7.19408 0.0557248 +17.9617 1.12687 0.0413369 +22.2094 2.44574 0.0827441 +14.6689 2.24112 0.0626607 +32.4762 0.413979 0.532079 +26.3236 6.49054 0.367554 +19.6401 2.80219 0.0422485 +14.5665 3.56254 0.0369293 +23.177 2.9107 0.107637 +24.309 0.575272 0.0769366 +25.7731 7.38392 0.144367 +11.8606 1.1755 0.0279922 +14.8008 2.49548 0.0561149 +9.26796 6.29999 0.0470207 +6.04908 3.04472 0.0523516 +11.7033 4.4295 0.061405 +10.0773 2.88586 0.0644295 +10.8614 4.8959 0.0403003 +17.316 2.77334 0.0617393 +14.1637 3.98913 0.0441814 +25.6709 0.0777011 0.110664 +10.0017 0.132848 0.081015 +25.1013 4.43025 0.914495 +30.8663 7.14078 0.0468926 +17.9931 8.72791 0.24448 +28.1087 7.01961 0.0910705 +4.05886 4.58343 0.0418161 +10.1348 5.63152 0.053458 +4.36314 5.8681 0.0551647 +17.7818 6.37686 0.364378 +14.0123 8.66863 0.883061 +17.4292 3.00373 0.0525239 +9.11632 3.9011 0.0360195 +2.64801 6.80826 0.0344058 +26.5643 1.88803 0.51134 +32.4522 0.838605 0.563599 +7.80525 7.85668 0.0484182 +25.869 0.13942 0.178901 +13.0908 7.81299 0.479043 +25.9704 7.99551 0.0347371 +21.3513 1.44743 0.0454654 +26.4351 6.60021 0.32707 +34.1767 2.52436 0.140268 +10.8075 2.36928 0.0543539 +30.5231 8.59689 0.0685385 +8.28196 7.75214 0.0731495 +27.7373 6.68051 0.14102 +6.70925 3.81132 0.0357048 +8.11907 8.6165 0.430949 +15.6907 4.98605 0.0569111 +8.34019 8.56559 0.409701 +28.1391 6.30492 0.197185 +2.73769 0.409142 0.0415974 +12.9174 6.97633 0.0865696 +20.3561 7.32393 0.953009 +27.9224 7.70071 0.042335 +33.6934 5.43674 0.0820863 +19.1978 0.328758 0.0663178 +7.85416 7.58179 0.0755776 +9.05546 3.68816 0.0518523 +21.6584 1.71812 0.0505629 +6.36045 1.82212 0.0623679 +29.441 1.76166 1.04426 +29.2949 7.12879 0.0754473 +25.4056 0.635274 0.148625 +33.5494 8.0254 0.0620903 +16.3558 0.147455 0.0384032 +33.1712 6.31808 0.078736 +8.09237 2.01524 0.0532628 +9.96937 4.49932 0.060198 +19.0372 1.54958 0.0389072 +30.8027 7.86333 0.0397946 +1.81122 1.36252 0.0607139 +29.7504 4.08015 0.618617 +13.8093 5.31893 0.0692794 +29.9838 3.96104 0.616705 +29.7909 1.88953 1.05013 +30.7732 2.53445 0.775746 +20.1054 8.16759 0.377408 +5.47107 1.09448 0.0514164 +11.4492 0.153615 0.0639612 +13.1915 7.94219 0.624826 +22.95 7.74557 0.26498 +12.6999 5.98835 0.0557542 +17.6913 4.12496 0.0634709 +13.4199 8.31996 0.984866 +21.9931 3.61078 0.125723 +6.54332 0.11341 0.0390463 +14.3349 3.80377 0.0443979 +30.4995 4.62531 0.343545 +10.9312 4.79216 0.0680316 +17.5705 7.76261 1.03225 +27.1653 0.591979 0.460531 +24.3969 7.46523 0.218317 +16.8917 4.56489 0.0378434 +3.93691 6.39929 0.0442145 +30.1195 3.61606 0.677588 +31.8591 2.58837 0.512195 +27.5994 2.93402 0.964681 +26.7634 2.39917 0.700961 +30.6935 3.96409 0.434907 +30.3325 5.22525 0.221461 +17.1652 7.26245 0.837337 +14.4365 1.5245 0.0402206 +16.5323 3.93361 0.0245799 +5.90894 4.65047 0.0526544 +7.83637 7.15085 0.0320345 +24.2138 6.20725 0.941824 +25.8431 5.44659 0.955003 +24.3582 5.8938 1.00874 +20.6035 3.91693 0.084716 +6.60856 7.04893 0.0612823 +13.9644 7.31856 0.29457 +32.881 4.94882 0.0868524 +32.9369 1.02269 0.406607 +11.2334 3.72523 0.0420596 +13.8569 2.31357 0.0664598 +7.59307 1.67629 0.0738896 +13.3509 2.59313 0.0665179 +16.2739 7.16104 0.582315 +10.8153 3.0397 0.0600571 +31.4299 3.75523 0.35033 +4.0401 3.34549 0.0374745 +15.9744 1.61339 0.0365978 +3.81124 4.99376 0.0410515 +13.3539 3.64225 0.0622815 +26.6928 8.18579 0.0390911 +4.82574 5.40533 0.0402023 +20.6234 8.5476 0.0964352 +20.3893 7.50667 0.851666 +20.2645 4.28931 0.0719907 +18.0781 0.143314 0.0630372 +21.9129 1.86104 0.0516304 +10.217 1.043 0.0635557 +10.2216 6.44634 0.0704167 +27.1068 1.20399 0.533169 +7.25295 5.63843 0.0546691 +28.685 2.81897 1.05154 +31.122 4.19974 0.317498 +20.2874 6.61345 0.93276 +10.7455 2.60748 0.0523992 +15.9127 1.50086 0.0392329 +18.3872 2.51746 0.0348692 +24.4968 8.24728 0.0525257 +32.5054 4.83177 0.0976518 +15.5845 2.50946 0.0600094 +8.2762 1.27356 0.034846 +11.6981 0.596255 0.0308548 +32.8175 2.91922 0.279501 +8.50569 5.36646 0.0579173 +9.83935 3.99261 0.061561 +2.55631 7.09881 0.0455899 +21.7083 8.69373 0.0425921 +17.0635 4.98382 0.0776034 +28.7125 3.01472 1.06244 +22.6735 6.94582 0.806918 +23.0503 0.775367 0.0611093 +24.091 6.63859 0.742819 +16.6109 7.70576 1.04331 +11.2892 6.78296 0.0730041 +2.11522 0.862364 0.0580699 +14.5927 5.07846 0.0470845 +1.91849 7.01122 0.0430679 +8.54439 3.59724 0.0489913 +13.9324 7.37698 0.313649 +18.7302 1.45089 0.0594196 +14.0538 4.62117 0.0521279 +8.61167 4.87862 0.0465187 +26.3549 6.88934 0.20263 +11.9707 3.60107 0.051738 +13.1751 2.25713 0.0450953 +8.86768 6.22396 0.0559336 +3.8326 0.998465 0.0360287 +26.4266 3.21506 0.826947 +16.5109 4.09068 0.0487076 +19.748 6.88534 1.0331 +30.2757 3.71245 0.618201 +30.6217 4.91356 0.271982 +20.6049 5.9285 0.707997 +32.7269 5.90435 0.0557082 +19.4056 8.42112 0.315487 +30.5763 4.04821 0.484164 +17.0221 4.99725 0.0407097 +10.2737 0.169611 0.0424472 +17.9561 1.67681 0.0660278 +6.812 2.17433 0.0555548 +14.0643 1.84584 0.0640937 +9.07172 4.53702 0.0574919 +16.4415 1.74211 0.0729419 +4.54844 4.98217 0.0475954 +15.8429 7.21785 0.524783 +20.318 0.634781 0.041249 +34.1563 2.12294 0.155317 +34.4002 4.3412 0.0516277 +23.2445 0.403387 0.0555106 +16.5051 1.25207 0.0479918 +26.2301 5.02529 1.00764 +31.934 3.4917 0.326652 +24.3038 7.89845 0.0997208 +7.81593 4.98564 0.0658345 +21.5996 6.61692 1.05227 +17.9383 3.02291 0.0626518 +31.7138 3.29565 0.384741 +23.4814 7.45915 0.337585 +33.5107 5.37767 0.0511026 +3.02941 7.99247 0.0221351 +24.542 2.20603 0.218249 +8.67606 1.00169 0.0314025 +16.9377 5.91908 0.138901 +29.3512 1.09337 1.04087 +34.3403 0.664091 0.177657 +8.8299 3.41224 0.0444938 +29.9062 6 0.0831891 +5.83834 3.14955 0.0604836 +11.5763 6.5818 0.052787 +8.68234 3.97927 0.0609319 +33.7854 2.49014 0.17301 +5.01485 4.6696 0.0704201 +27.9558 5.28347 0.612141 +18.7772 1.31047 0.0459717 +25.2972 5.17023 1.08602 +4.36517 1.4728 0.0423603 +16.727 8.46183 0.654372 +19.7904 4.64561 0.112524 +11.1086 1.79552 0.0584536 +22.9609 0.371741 0.0627566 +4.24642 1.87506 0.0432874 +4.1557 2.78196 0.0510994 +23.0184 5.01222 0.705405 +10.2123 5.79932 0.0451348 +29.8283 6.2193 0.110767 +20.1629 3.8553 0.0620428 +30.582 5.50793 0.154288 +29.55 4.8381 0.435912 +24.7983 2.59124 0.285866 +33.8767 1.91392 0.190611 +9.63636 3.48956 0.0683985 +20.0532 6.02731 0.607736 +5.76015 1.17545 0.0395836 +24.9603 5.55846 1.02836 +28.5648 0.181217 0.848614 +31.0513 1.60542 0.857892 +18.979 5.85402 0.303049 +32.0648 7.06269 0.0403428 +14.0775 6.70531 0.0917868 +13.195 5.97374 0.073478 +25.7365 2.22073 0.390859 +31.8402 8.63376 0.0641158 +4.14615 5.25247 0.0633392 +18.3047 7.95449 0.881465 +16.2334 8.18459 1.0197 +2.59353 2.086 0.0492566 +5.14627 8.47612 0.091065 +28.5741 0.709764 0.836768 +32.737 5.57053 0.0607353 +20.3148 2.24345 0.0452808 +9.4375 0.822319 0.0391724 +33.8297 4.97687 0.0638177 +15.4674 1.85773 0.0315546 +5.89637 6.87314 0.0464229 +27.4968 4.03434 1.00388 +24.5947 2.35779 0.210505 +24.2819 0.103014 0.057703 +14.1626 6.04814 0.0841221 +10.8138 5.56296 0.0529234 +9.04924 7.03742 0.0602484 +21.3868 3.34919 0.0338084 +11.8538 7.99629 0.436531 +25.4662 0.206203 0.115856 +31.6658 8.03047 0.0695915 +29.4148 6.68358 0.089669 +2.6339 0.894737 0.0517461 +22.8419 8.62764 0.0595276 +3.20956 0.784606 0.0514453 +11.3046 6.56438 0.0512353 +11.3466 2.1359 0.059538 +19.8339 6.0392 0.574761 +2.93212 7.31559 0.0475713 +9.07922 1.03078 0.0389701 +32.3327 2.83639 0.383912 +2.48019 7.82397 0.0434796 +12.862 4.1732 0.0390096 +17.6769 1.33795 0.0845952 +5.08805 0.759131 0.0515546 +11.1304 1.07503 0.0477562 +22.1059 0.924672 0.031189 +12.4054 3.11908 0.0345682 +19.9177 6.78739 1.00877 +32.6548 3.95852 0.159442 +4.68242 0.85957 0.0675057 +23.6439 3.08166 0.189855 +2.31575 4.22757 0.0627225 +14.3567 3.91723 0.0528116 +25.1976 5.60462 0.986794 +33.0637 5.94839 0.0278009 +16.7811 6.53556 0.312917 +22.2633 4.68349 0.414537 +17.5965 0.261735 0.0507078 +5.41297 6.72665 0.0643859 +4.4726 6.84454 0.0361574 +19.8456 6.37504 0.789708 +30.3551 3.82426 0.592399 +9.95905 7.75144 0.122978 +26.1647 0.841925 0.27674 +22.2248 6.46897 1.03788 +12.6146 5.21177 0.0491041 +23.1858 6.11926 1.05486 +3.60754 0.58784 0.0439963 +9.90897 8.46157 0.588993 +14.0865 0.264236 0.0639677 +22.6898 1.54592 0.080662 +2.69504 4.51523 0.0291826 +2.89738 5.00714 0.074771 +32.0922 1.55514 0.578359 +18.9738 2.51213 0.0403606 +31.0904 7.99836 0.0473225 +34.3945 2.80835 0.132302 +7.0008 3.89118 0.0585422 +13.9593 0.974619 0.0576772 +34.0584 4.97253 0.0751298 +1.75281 6.12609 0.0678329 +33.9142 6.47525 0.0691751 +34.4903 2.03303 0.14446 +26.4088 3.2272 0.822361 +14.3513 5.71949 0.06273 +23.8385 8.50359 0.0369804 +2.18734 6.7281 0.049334 +13.1194 1.91624 0.0187098 +2.68802 0.572604 0.0501441 +15.615 2.86783 0.0530386 +24.0562 4.62647 0.766238 +2.15593 2.37749 0.0436453 +31.0542 6.90363 0.0696654 +4.43901 2.24214 0.0461476 +9.30884 1.08277 0.0516209 +14.346 5.26533 0.0804144 +5.19487 6.64361 0.0561815 +26.1881 1.79139 0.416573 +23.926 2.91731 0.221479 +20.9552 2.27055 0.0618736 +15.6712 0.251561 0.0681208 +21.1517 5.96748 0.796544 +13.9062 0.444202 0.0623095 +23.4714 5.34829 0.937437 +31.0322 5.48118 0.111858 +27.0141 4.63369 1.01406 +13.5987 0.371155 0.0417928 +30.7262 0.326456 0.948058 +11.8034 4.50138 0.04735 +23.9215 4.28781 0.626577 +25.9007 0.135297 0.163661 +15.0723 0.0940753 0.0594025 +29.9073 6.75913 0.0581314 +33.6279 2.83597 0.146307 +5.46478 6.26595 0.0457303 +3.0031 1.73565 0.0163597 +11.1938 2.32167 0.068086 +2.36681 7.85035 0.0445308 +34.0737 1.72806 0.183568 +10.3801 3.56835 0.0501036 +6.66557 4.35808 0.0280605 +20.8279 6.22188 0.863293 +33.0278 2.80336 0.262553 +30.2945 5.67496 0.15129 +30.3909 1.94234 0.948893 +8.02612 5.95492 0.0392179 +10.3485 0.185481 0.0488158 +4.41027 1.63276 0.0306341 +24.5488 0.533266 0.0877171 +13.2456 0.891647 0.0255882 +23.381 6.53941 0.899987 +2.59477 4.0559 0.062365 +25.9848 6.84595 0.276346 +5.74159 8.52139 0.109523 +34.0925 1.6264 0.180654 +10.3657 6.72062 0.047277 +17.6202 8.49446 0.489959 +28.6094 2.70374 1.06392 +22.3377 0.674811 0.0497895 +4.16785 4.03585 0.0246296 +32.0464 4.74232 0.137056 +25.7555 6.01378 0.718614 +26.367 4.98892 1.02113 +18.3734 2.63735 0.0644319 +6.92527 1.41965 0.0496167 +23.1248 2.97161 0.145072 +11.759 4.0977 0.0282085 +28.9957 1.62198 0.992891 +12.3354 2.61049 0.044771 +23.6221 7.72664 0.199951 +19.3679 3.70144 0.07514 +6.57409 1.09517 0.0399644 +24.0227 5.81683 1.03056 +33.4933 3.10228 0.14963 +3.47315 4.50208 0.0388711 +20.8944 3.4324 0.0710038 +22.8763 1.003 0.0340793 +29.1782 6.44193 0.115009 +11.9985 4.97141 0.0304667 +28.885 8.64368 0.0833712 +25.946 0.0413775 0.177803 +7.79572 5.04554 0.0513135 +26.1276 3.72104 0.871135 +16.2979 3.79994 0.054275 +8.23786 6.31521 0.0216878 +9.51769 2.11865 0.0623738 +12.5425 3.49557 0.0404308 +25.8865 1.16524 0.255158 +6.54723 4.95345 0.0574504 +22.714 3.8227 0.243336 +28.4363 0.37267 0.82487 +18.5963 2.01333 0.0666181 +31.468 3.45532 0.434198 +25.2387 4.64867 0.983374 +18.5151 0.811777 0.0331429 +33.3967 5.41311 0.0685231 +19.6713 7.14656 1.05433 +6.39949 2.90913 0.042257 +8.57655 0.583318 0.0491374 +28.6805 1.28144 0.956053 +29.7201 6.66844 0.0831529 +14.5024 4.65166 0.0411427 +13.3821 1.18961 0.0305311 +8.2978 1.46754 0.0216157 +29.4438 7.28184 0.0472926 +34.0613 5.93878 0.0463617 +32.5198 6.07978 0.0351421 +16.1166 1.22718 0.0656634 +30.9549 8.65669 0.0779746 +8.18663 2.21941 0.0386335 +3.83792 3.09395 0.0608985 +20.005 2.04471 0.0485704 +12.8649 8.23869 0.870783 +17.3559 5.52009 0.0955069 +3.89405 4.83567 0.0488134 +7.25083 3.62023 0.0639935 +17.6223 6.26778 0.302849 +18.2104 4.09529 0.0470113 +25.1016 4.03491 0.787335 +25.8668 1.32711 0.25801 +14.1922 7.19807 0.26209 +16.3996 0.578328 0.0590376 +10.8023 7.93146 0.254179 +6.64223 8.00972 0.0388395 +14.7509 4.57419 0.0588567 +20.6868 1.42766 0.0326972 +34.4284 1.90433 0.140589 +25.9028 1.22096 0.271001 +28.2798 6.1254 0.211092 +28.7362 3.17046 1.01734 +32.2964 8.58557 0.0621572 +31.8051 2.98313 0.450353 +7.34045 2.88186 0.019443 +11.9951 5.91439 0.064598 +10.2979 7.67614 0.103366 +27.9996 4.6031 0.839283 +17.5707 1.06802 0.0539099 +32.6955 1.49199 0.425623 +20.4129 6.24965 0.807372 +2.43189 6.69285 0.0539472 +9.50175 8.65161 0.763339 +14.8024 5.01 0.0642915 +5.91806 6.83507 0.0539284 +20.0293 5.89802 0.529064 +3.05833 4.80516 0.0552443 +9.53267 7.06408 0.059823 +1.88787 1.78114 0.0408839 +5.4413 1.33837 0.0576513 +24.8175 2.70697 0.299715 +18.1066 3.36471 0.042084 +33.139 8.29859 0.0487867 +5.85703 7.20998 0.0651806 +7.77671 6.50562 0.0623027 +3.50388 7.79196 0.00645254 +8.069 3.35117 0.0679656 +11.2346 8.60667 0.955272 +15.6755 7.93283 1.0175 +9.72159 1.61857 0.0716045 +7.1086 3.30138 0.0394303 +14.0441 2.69156 0.0346327 +3.16549 1.68688 0.0207641 +28.6797 8.19137 0.0327451 +20.3163 3.41428 0.071171 +18.3032 7.59534 1.03389 +16.8931 5.21345 0.0472856 +19.8218 0.522631 0.0557252 +34.2839 3.78911 0.0650016 +14.1335 6.81757 0.118345 +20.2434 2.7876 0.0401418 +8.40811 2.5586 0.055724 +14.5255 0.817513 0.0395757 +4.62851 4.14317 0.0423389 +30.0531 8.23658 0.0470373 +23.9503 0.163575 0.0341468 +26.8655 7.75022 0.0561625 +26.6473 5.24147 0.864823 +5.91101 2.45008 0.0319113 +22.4675 7.85157 0.252443 +6.34172 8.10644 0.0656768 +21.8986 8.50529 0.0777588 +5.91885 4.04384 0.0650767 +6.70438 3.62229 0.0511859 +5.58492 5.80865 0.0578632 +19.2273 7.76812 0.866214 +5.3946 3.05881 0.0399054 +13.5163 2.90139 0.0741446 +15.524 3.57183 0.025191 +34.0842 0.183853 0.197284 +27.2579 7.34222 0.0667963 +20.8068 3.99962 0.106163 +25.6822 4.07956 0.893557 +1.86695 5.53079 0.0566663 +13.6535 7.07 0.164522 +10.968 6.16538 0.0184775 +23.3178 2.9155 0.138074 +5.77494 2.57562 0.0588602 +32.6774 2.91147 0.301492 +12.1926 4.65118 0.0537541 +30.319 7.08615 0.0441518 +29.5345 0.0696464 1.02225 +16.898 5.2901 0.0457893 +1.58393 7.37532 0.0347127 +5.43423 4.90713 0.0526795 +14.2501 3.33055 0.0493183 +25.8555 4.14325 0.968679 +17.2275 4.4974 0.0546041 +32.4247 5.4803 0.0767067 +31.402 0.65643 0.829053 +12.2438 6.926 0.0815783 +34.141 4.82148 0.0949203 +18.9242 2.42199 0.0350967 +3.9397 8.00186 0.0521508 +2.70988 3.48322 0.0427028 +13.6464 6.77219 0.104724 +15.1964 2.60048 0.0564218 +7.45845 4.79122 0.0377114 +31.2094 1.65948 0.810031 +11.7074 2.3914 0.052807 +27.0036 2.87725 0.855135 +8.67776 1.25517 0.0544139 +15.8321 6.64688 0.241019 +14.132 3.93866 0.0588316 +26.7963 3.03351 0.846421 +13.5672 5.11352 0.0535743 +8.84448 2.0093 0.0793991 +29.1341 3.82682 0.848536 +22.954 4.96178 0.695741 +7.11142 5.38363 0.0401605 +9.35127 3.69222 0.0264277 +4.76359 6.49414 0.0398193 +28.9963 7.57615 0.0572089 +15.3282 8.41968 0.953264 +28.9102 4.89653 0.540146 +20.7599 8.43603 0.138089 +27.6939 8.20126 0.0362575 +26.4542 5.72701 0.709942 +16.8742 1.1759 0.0409864 +9.95271 5.60358 0.0132034 +14.3813 4.6427 0.0562999 +19.0329 6.14005 0.467915 +34.3976 2.36693 0.126525 +10.9552 8.679 0.99378 +25.7865 3.0271 0.597249 +9.96071 8.73657 0.907322 +22.3676 2.41577 0.0302011 +25.7598 4.17486 0.954545 +1.5279 5.95601 0.0439137 +25.5409 2.27016 0.365271 +30.1662 3.55329 0.71198 +31.3601 5.78365 0.0769764 +30.345 6.57861 0.0448019 +14.0994 8.013 0.872812 +6.34623 4.82316 0.0622038 +12.7646 8.41466 1.00109 +30.5694 2.46438 0.837999 +23.8064 5.32356 0.968662 +18.1643 6.62045 0.576098 +1.70281 0.727048 0.0343525 +3.87571 6.87843 0.0179197 +19.8094 7.15922 1.05456 +30.2536 4.91737 0.298742 +21.6334 8.0729 0.248592 +13.4041 6.11313 0.0413457 +1.69739 3.20732 0.0489743 +8.44901 7.05058 0.032296 +21.8121 4.5353 0.261625 +11.1503 5.97097 0.0565422 +19.2177 8.63681 0.143886 +2.82816 6.08604 0.0444127 +19.3134 6.79427 0.94315 +16.6912 7.13317 0.659719 +2.22775 1.75174 0.0594139 +28.5837 0.328702 0.850583 +27.4729 0.662791 0.550998 +5.29921 4.06184 0.0407919 +22.1582 8.44036 0.114528 +29.8414 3.89858 0.674527 +18.4223 8.07374 0.755924 +26.341 2.56243 0.622777 +5.55803 1.24136 0.0487401 +3.46682 3.55273 0.0688019 +9.27193 7.37878 0.0480818 +26.8078 8.45512 0.0377627 +11.0374 4.70593 0.0437482 +29.3594 3.32248 0.954181 +3.70898 6.88263 0.0634794 +17.4151 3.71643 0.0539939 +15.8999 2.63992 0.0511598 +28.2757 6.60886 0.132481 +23.6133 0.72567 0.0469978 +26.0218 6.78884 0.30186 +21.5377 8.3386 0.127942 +6.71503 0.899303 0.0533576 +26.6044 6.83383 0.209449 +25.2512 0.205374 0.119842 +25.632 1.09201 0.198684 +16.6728 2.55844 0.0640318 +15.483 8.73434 0.589014 +6.36382 0.463313 0.0386711 +28.4065 3.98846 0.95799 +15.859 3.86666 0.04505 +32.9979 1.54689 0.368162 +10.4649 8.54667 0.784071 +17.4574 2.06658 0.0428918 +13.1024 7.87882 0.561261 +21.5463 4.74607 0.309359 +33.4124 6.23165 0.05176 +8.83775 6.498 0.0800944 +14.2092 5.89682 0.0604249 +11.2456 7.07167 0.0487459 +4.61872 7.70203 0.0519546 +2.69193 1.23747 0.0420723 +34.2575 0.779195 0.205637 +12.4021 3.96435 0.0408475 +24.8303 0.940771 0.0973314 +27.3396 5.16442 0.797409 +22.6492 8.22974 0.100664 +10.2487 4.50032 0.0445576 +23.6342 1.46915 0.0722274 +31.887 1.02045 0.67212 +16.6031 4.64897 0.045884 +30.9254 6.49611 0.0272548 +29.9195 7.0469 0.0542521 +26.963 6.60622 0.251517 +24.4757 4.06166 0.673651 +33.1503 7.65033 0.0512618 +18.0405 4.72724 0.0750297 +31.8456 5.97026 0.05239 +4.40462 0.0216768 0.0718592 +24.898 4.2875 0.859686 +5.42203 0.758052 0.0553461 +13.7162 1.85775 0.0454307 +20.8037 2.63264 0.0547717 +24.3551 5.01222 0.991756 +24.0207 0.669036 0.0512966 +7.41352 6.22438 0.0693525 +4.43158 3.44968 0.0359057 +6.20264 1.90471 0.0383828 +21.9129 8.35216 0.116569 +7.40401 5.77635 0.0600578 +31.7359 2.93559 0.484822 +23.935 1.07397 0.0962594 +11.4541 4.28155 0.0339358 +22.315 6.74397 0.976259 +3.91502 4.47561 0.0619735 +21.4554 2.70694 0.0643833 +6.61486 0.986483 0.0672279 +22.3691 1.95603 0.0606382 +10.7075 6.90662 0.0509286 +19.7593 1.39432 0.0176889 +24.6757 8.57356 0.0245029 +8.75053 6.37468 0.0622547 +3.42142 3.52381 0.0337126 +6.1905 1.65136 0.0551389 +29.1417 4.06848 0.796933 +32.9069 8.32471 0.048867 +20.5241 1.03927 0.0655796 +4.27893 2.12941 0.0410809 +21.2491 0.67167 0.0594774 +12.6625 6.32625 0.0528647 +7.21059 8.31082 0.104597 +26.1182 4.86605 1.01266 +30.0486 2.60152 0.929523 +19.1028 8.42363 0.346267 +20.4444 5.1309 0.286634 +32.732 8.3911 0.0575435 +1.59239 3.26082 0.062282 +29.7651 1.27246 1.06369 +32.1646 0.611066 0.613844 +29.5013 2.33404 1.05388 +19.683 3.78681 0.0636374 +30.9054 6.67267 0.0489666 +17.888 4.09789 0.109508 +13.6339 2.35336 0.0603846 +17.94 7.87019 0.98602 +2.92439 5.04231 0.0618783 +2.49189 5.26799 0.0326208 +18.6167 7.16758 0.97271 +20.9592 5.86333 0.73169 +3.78881 2.11146 0.0597622 +26.6952 8.32728 0.0352895 +6.45426 1.41025 0.0264809 +8.53422 1.40067 0.0488748 +17.2096 5.67137 0.0886333 +27.0562 5.0921 0.890481 +17.6264 2.00331 0.0217928 +31.2127 6.13838 0.0850091 +26.5065 0.868644 0.320947 +20.0395 4.15259 0.0608662 +30.1824 8.27699 0.0507762 +12.9437 7.83024 0.470897 +20.6228 5.27728 0.33934 +22.7511 6.93258 0.801599 +4.50716 5.79609 0.0139802 +10.1836 2.03829 0.0323416 +33.9027 7.503 0.0550518 +11.585 5.34091 0.0190901 +31.5104 5.20699 0.0975329 +3.17074 8.33074 0.0385191 +9.74543 4.88699 0.0635467 +9.14882 2.84754 0.0635545 +1.56869 3.69115 0.0720558 +25.4061 4.85659 1.02588 +9.21216 7.62148 0.0826999 +20.8179 7.36229 0.887444 +4.66384 3.47498 0.0304505 +20.4262 4.26928 0.110526 +17.8374 0.436572 0.0496789 +17.0555 5.5847 0.0781014 +27.538 7.01476 0.0886235 +13.0851 1.72777 0.025657 +25.1064 5.55594 1.02045 +28.1246 1.22475 0.813911 +9.54036 7.33289 0.0537933 +11.3051 6.38427 0.0450617 +21.7061 8.16724 0.17824 +7.53178 2.48189 0.0611297 +1.66823 1.48851 0.0526718 +21.5622 4.9884 0.37202 +24.7266 0.506628 0.0926908 +5.02698 7.17497 0.0568079 +20.317 1.78648 0.0410462 +17.158 1.16506 0.0426533 +18.3479 1.29392 0.040926 +32.4011 6.14831 0.0638227 +2.92805 0.431756 0.043738 +7.58969 3.68486 0.0493206 +4.2206 1.58687 0.0510424 +21.9409 6.82229 1.01216 +29.9344 0.472955 1.02715 +19.2341 1.71626 0.0846962 +2.55086 6.74524 0.0317183 +18.1061 4.50803 0.0542968 +5.13256 1.00642 0.0376599 +4.35766 8.65279 0.101398 +11.5165 3.24331 0.0580516 +29.594 3.66639 0.836678 +24.4624 6.48132 0.761133 +33.9965 4.30259 0.0623188 +17.4082 7.25155 0.866506 +7.6433 4.68291 0.0516489 +13.4842 2.14747 0.0398879 +4.28642 4.68285 0.0587463 +14.708 7.33041 0.418683 +21.3919 2.45007 0.0488986 +23.1615 5.55596 0.956606 +33.5122 5.06605 0.0729823 +13.9838 6.05623 0.0550162 +4.38802 3.14668 0.0677851 +29.9904 1.98086 1.02346 +7.26715 5.8074 0.0317655 +12.2196 7.40967 0.124072 +6.22735 4.12045 0.0505482 +24.9367 1.19474 0.125332 +30.6345 7.02606 0.0370296 +8.45876 4.61617 0.039051 +20.617 5.96349 0.708247 +2.30649 0.885881 0.0465872 +24.1115 6.85901 0.615941 +6.28038 3.25542 0.0616243 +11.8194 7.77775 0.252251 +19.7441 2.16504 0.0568528 +16.71 0.0532939 0.0722993 +27.0541 2.79042 0.820601 +2.60002 6.32652 0.0308948 +8.44648 6.29136 0.048316 +8.06184 7.40984 0.0441793 +14.2941 1.8402 0.0753993 +29.5401 5.19069 0.31664 +33.3147 0.636838 0.346855 +33.2705 1.96251 0.26017 +7.64242 0.3997 0.0371109 +8.25907 5.49615 0.0549687 +10.9794 7.82595 0.218691 +20.9701 5.25371 0.401392 +29.0517 5.26802 0.412675 +18.2846 7.89859 0.955136 +2.73102 2.88128 0.0797191 +23.9962 4.86628 0.857665 +21.9408 7.07548 0.882473 +13.9662 1.69778 0.0460357 +19.1853 6.14589 0.517988 +9.41533 0.673676 0.0153092 +11.3971 3.23072 0.0403835 +20.863 1.61622 0.0474771 +13.7399 3.77964 0.0428455 +12.7952 1.786 0.0741553 +6.24137 0.536126 0.0292671 +30.4346 3.88507 0.531253 +18.5626 3.41195 0.0329348 +30.0264 6.4151 0.0770022 +21.0535 0.122831 0.0365821 +3.93697 5.55561 0.0644112 +2.68384 0.387697 0.063655 +7.2954 4.24489 0.0183942 +13.6938 0.176844 0.0181415 +7.15805 7.96149 0.0773443 +13.0502 4.2621 0.0560941 +31.7485 4.17054 0.212402 +2.05338 0.620474 0.0496595 +3.98199 6.87589 0.0315192 +25.0081 2.3273 0.282184 +25.6536 4.11008 0.89707 +9.9862 6.31021 0.0409695 +3.97689 6.91707 0.0573142 +12.6597 3.37466 0.0509061 +24.4325 0.530622 0.0953617 +10.6201 8.61719 0.884437 +18.2324 2.23004 0.0276288 +15.8966 0.367464 0.0537291 +30.9857 5.41808 0.109793 +21.8402 7.63412 0.515496 +12.9096 0.767941 0.0739429 +14.5013 0.400157 0.0672727 +5.63356 5.91799 0.0832665 +27.7569 5.00445 0.774085 +7.79082 2.26352 0.072394 +5.24393 2.31615 0.0644019 +12.2348 6.70637 0.0740986 +23.3971 0.183474 0.068268 +18.896 2.10292 0.0546427 +1.81054 3.16858 0.0773479 +20.7001 6.76703 1.02675 +25.9396 2.74616 0.539226 +25.6646 0.474912 0.154404 +8.26906 6.49698 0.0740924 +3.9937 6.01472 0.0461158 +13.8159 1.11325 0.0378704 +8.47349 6.36284 0.0374022 +33.0713 5.44718 0.0672447 +24.0072 3.2153 0.282322 +11.6205 5.05375 0.0681879 +28.6836 4.39412 0.778476 +21.3121 6.93536 1.02857 +2.86011 8.31611 0.0385877 +8.66925 2.97967 0.0582505 +10.0091 0.789162 0.0959209 +31.7255 3.18653 0.421192 +22.6149 5.23902 0.733375 +7.72242 2.24104 0.0648166 +29.4569 1.3525 1.02947 +14.316 1.85283 0.0432692 +34.2144 7.41666 0.0419849 +19.7658 6.23999 0.67395 +1.90803 1.09762 0.0295451 +14.8174 2.61757 0.0717314 +23.642 2.36748 0.122631 +15.8216 0.982736 0.0529823 +23.2803 7.33293 0.441246 +4.59226 2.52691 0.049704 +2.0016 4.93708 0.0154075 +4.58457 3.34179 0.0694171 +22.5572 0.612826 0.0389378 +8.82926 0.31817 0.0626428 +28.4636 6.07122 0.224663 +27.5945 5.48853 0.589287 +5.74857 4.30249 0.0577477 +3.41647 1.5497 0.0755688 +23.5426 2.97586 0.176507 +24.5842 4.43081 0.804189 +30.7787 1.88657 0.890508 +13.0439 5.03888 0.0130854 +2.38118 7.33815 0.0372929 +26.4271 1.55341 0.399258 +21.229 8.13765 0.243691 +32.9858 4.43783 0.0812957 +11.5268 8.54462 0.951395 +1.8232 8.45828 0.0468502 +3.35751 0.558275 0.0522841 +30.6941 5.95881 0.0894822 +13.1732 2.12639 0.0454612 +3.95154 5.42672 0.0403413 +2.44763 7.77494 0.0696322 +22.5841 4.00453 0.266393 +13.3927 5.70642 0.0736455 +25.9902 5.83678 0.767397 +21.5589 1.64109 0.0561219 +9.12569 3.72064 0.055595 +21.5707 2.54439 0.0537611 +14.5309 4.22037 0.0496524 +23.6489 6.66475 0.774237 +9.61862 1.26737 0.0323018 +11.4967 4.83425 0.0363387 +7.07005 4.40609 0.0330026 +5.91571 3.36464 0.0566978 +14.8064 7.35638 0.455483 +21.7467 3.06139 0.08145 +20.8389 6.99109 1.0313 +7.78094 0.617802 0.065391 +14.3089 3.83987 0.0639869 +15.9336 0.952126 0.0587764 +8.68973 7.40063 0.0498528 +21.9865 5.80788 0.901493 +12.7434 1.85665 0.0323026 +4.74736 3.2546 0.0256286 +24.0632 1.11164 0.0605653 +7.20523 7.73532 0.0733656 +13.1381 7.80267 0.502557 +26.9626 7.79963 0.0489435 +5.70348 5.29291 0.0152228 +19.6643 2.31131 0.0470656 +20.9927 3.98494 0.0897794 +10.4123 5.81071 0.0187509 +5.24106 3.71798 0.066554 +14.852 0.506334 0.0506554 +9.17817 4.58886 0.0710594 +14.173 0.765629 0.049918 +6.12715 2.05466 0.0586236 +14.6969 7.14086 0.288862 +31.8804 5.36442 0.0981188 +1.79447 6.96579 0.0441369 +20.3976 5.93598 0.634219 +15.2616 5.74825 0.0635867 +29.3967 2.16089 1.03063 +26.8566 6.18967 0.422037 +16.417 1.10352 0.0527451 +9.94299 0.159009 0.0397084 +32.6247 2.96371 0.287667 +28.5756 1.36938 0.944661 +30.6344 6.0742 0.101637 +4.33265 2.49679 0.0512213 +14.4785 0.302431 0.0160965 +16.938 6.77834 0.457651 +11.2253 8.17145 0.523224 +8.85981 6.21376 0.0467918 +12.2407 8.34856 0.850925 +7.13903 6.84405 0.0548805 +33.0598 8.12299 0.053473 +31.2452 5.88202 0.0910233 +22.0018 6.85886 0.958982 +20.4008 6.95155 1.07645 +34.1805 1.01987 0.192826 +17.96 2.79622 0.0351452 +26.6413 5.39026 0.852164 +2.57449 2.80956 0.0484108 +15.5058 7.62876 0.810289 +7.82439 0.47438 0.0558287 +4.2942 1.11872 0.0511569 +29.3015 2.86474 1.01443 +25.635 2.99627 0.553901 +13.2124 7.27748 0.18127 +5.52279 0.130872 0.0468193 +27.8337 7.22955 0.0701045 +13.1229 8.4995 1.05259 +27.2954 0.433296 0.485347 +21.4917 8.31942 0.1205 +25.2911 5.77103 0.939818 +21.2224 1.15307 0.0501103 +33.2087 7.26556 0.0632556 +9.43715 1.0417 0.0605772 +30.2805 3.35685 0.728506 +22.1064 2.08535 0.0669527 +24.4571 3.35925 0.380997 +33.9736 1.27348 0.22593 +2.62122 3.31161 0.079449 +10.3461 4.9716 0.059946 +25.4165 4.05736 0.847958 +27.5353 4.55853 0.962667 +9.73561 7.80722 0.110804 +17.0551 7.44307 0.951773 +29.8505 0.986207 1.04385 +33.6393 6.42339 0.0335126 +33.567 6.95828 0.0381561 +8.40377 7.94695 0.0797201 +14.0276 3.43331 0.0612909 +15.6976 8.06834 1.04885 +17.5057 4.8664 0.0645044 +29.8748 4.09586 0.604809 +17.4832 4.44205 0.0733119 +11.1832 3.60352 0.0507476 +22.3501 6.03098 1.01348 +27.5213 4.64747 0.954359 +21.5047 7.67482 0.529361 +32.1156 4.1001 0.194899 +33.8681 0.457736 0.215213 +26.1543 1.36947 0.343761 +30.8054 3.60736 0.539702 +4.37139 4.1218 0.0389883 +20.6763 7.10963 1.03608 +2.2861 5.16155 0.0287379 +10.4222 1.48097 0.0183812 +22.7002 2.64247 0.0736626 +24.1658 4.6864 0.795399 +28.8486 6.93439 0.0697322 +29.1736 6.19533 0.13851 +10.4614 5.59665 0.0465458 +16.1049 2.23338 0.0419442 +26.9496 4.98575 0.909752 +8.39818 2.14463 0.0644323 +30.5442 0.570396 0.980715 +2.53966 6.63015 0.0614084 +13.9525 5.68233 0.0561685 +2.88556 8.63798 0.0629898 +10.8007 6.53059 0.0582316 +29.2884 2.20774 1.02606 +33.9625 0.245046 0.214243 +15.5685 3.76082 0.0581373 +9.31163 2.97161 0.0671703 +12.1743 4.50695 0.0195679 +29.197 3.27333 0.971125 +7.88433 0.419445 0.0484539 +26.8658 0.598352 0.354188 +11.7137 3.13419 0.0454058 +9.72548 5.14888 0.0512222 +31.0844 1.53403 0.84075 +8.75247 1.56866 0.0561481 +32.5718 2.02723 0.424054 +16.1237 6.48267 0.188847 +34.3796 6.67913 0.0452482 +25.6218 4.25861 0.963659 +29.0591 0.476363 0.944886 +19.4474 8.21894 0.448663 +33.5165 7.59314 0.0703018 +25.0883 2.6344 0.344241 +15.9308 0.0764592 0.05598 +3.38041 4.299 0.0507638 +7.77761 1.47007 0.0626954 +30.0734 3.32153 0.759736 +32.5911 3.49593 0.199755 +2.52693 2.7228 0.0375137 +15.0532 7.98261 0.970951 +16.2689 3.40446 0.0649291 +6.81342 0.979864 0.0708928 +2.6036 5.02483 0.0590619 +29.9302 7.76352 0.0693774 +28.9049 2.02242 1.02864 +30.6946 4.43592 0.333926 +11.3192 3.30242 0.0272418 +30.7744 5.90436 0.10808 +33.6616 2.73022 0.180398 +27.7806 2.22998 0.888387 +12.9226 8.39708 0.973226 +13.8711 7.19612 0.209942 +18.0261 6.1456 0.305744 +31.6784 5.73547 0.0743163 +17.9958 8.1613 0.778491 +15.5774 0.499394 0.0530231 +3.52891 3.85032 0.0396812 +12.2321 5.44452 0.0325632 +8.47644 1.12588 0.0501288 +17.1154 8.7008 0.377801 +17.2139 5.99848 0.190734 +7.92654 6.63482 0.0391718 +4.32423 8.44364 0.0725861 +22.4135 5.05296 0.591595 +9.48294 7.97973 0.155425 +23.3966 3.25968 0.163637 +20.2598 8.05464 0.434108 +30.2027 4.43378 0.40716 +10.1794 6.77627 0.0733815 +33.5694 2.37398 0.201353 +29.4097 4.16291 0.702991 +17.8277 4.8623 0.0644773 +12.9902 4.50564 0.0300613 +8.41272 8.07063 0.126541 +20.2575 3.99371 0.0931881 +7.58107 7.65629 0.0585055 +13.2128 0.971108 0.058405 +3.59326 1.83955 0.0667977 +23.5444 1.53561 0.0833075 +21.3617 1.59802 0.0495402 +23.9311 5.11829 0.913695 +29.3446 0.890114 1.00773 +19.6377 5.8857 0.433176 +13.7608 7.6179 0.472645 +9.80879 8.06357 0.235272 +18.7027 0.484927 0.0505868 +15.932 2.15333 0.0651519 +15.9766 3.85368 0.0144447 +10.3387 0.126632 0.0544678 +7.9908 5.88878 0.032611 +27.7241 3.97397 1.0477 +33.6803 3.73123 0.126938 +23.5575 1.35979 0.0878324 +21.4343 0.602751 0.0626632 +7.13184 7.72621 0.0721089 +16.2131 5.22999 0.0837398 +32.5493 3.02709 0.286143 +33.2287 7.69271 0.0742892 +3.93542 1.02049 0.045954 +29.2786 3.47692 0.89401 +8.65294 0.640558 0.0598593 +32.6908 6.93579 0.0626824 +8.95297 7.7162 0.0802645 +26.9759 2.48858 0.747012 +33.0896 7.15195 0.0603054 +3.04454 8.31733 0.0495388 +1.92118 2.01324 0.0879271 +19.1197 8.44086 0.312616 +15.7318 2.93453 0.0573323 +32.5608 6.19035 0.0469683 +11.1555 4.69081 0.0602498 +2.17074 3.05379 0.0762503 +30.1953 5.2561 0.212618 +21.3468 0.0026167 0.0459263 +18.4832 4.3851 0.0315686 +22.3087 4.36433 0.310096 +31.3376 5.54134 0.122054 +10.1477 6.68914 0.0341284 +9.78311 7.69206 0.0894612 +34.1384 7.0085 0.0397304 +18.6024 7.61862 1.0232 +2.61966 3.28113 0.039194 +26.6045 0.924522 0.359062 +20.1229 5.68519 0.420654 +3.03388 0.394432 0.0538337 +13.8507 8.343 1.05339 +21.0896 4.21414 0.148747 +30.0351 2.24225 0.953508 +23.3121 0.271259 0.0751403 +22.5293 7.36799 0.569331 +26.1121 7.28797 0.150084 +15.3933 1.17093 0.0375027 +28.2897 6.74782 0.0985096 +25.4087 0.762939 0.164781 +4.64214 6.08163 0.0768225 +25.4205 4.65754 1.00054 +30.9767 5.60281 0.112512 +32.6339 1.19911 0.475519 +16.0704 6.92877 0.382709 +5.51127 6.87792 0.0558869 +20.2474 7.35488 0.963094 +4.76559 3.51975 0.069845 +20.4233 2.26363 0.0274399 +26.1723 4.80483 1.04945 +21.0871 6.105 0.850136 +24.3916 6.64631 0.661001 +26.2223 3.9235 0.950126 +17.3028 5.32147 0.0311103 +10.0105 5.64791 0.0733185 +27.5005 5.16942 0.761885 +18.2887 6.27292 0.406329 +24.5704 2.7045 0.267978 +14.3101 2.05308 0.0554028 +31.3484 2.52403 0.63867 +14.5318 6.92027 0.189569 +21.3063 8.46492 0.132312 +22.4268 2.77866 0.0757202 +18.9635 5.7187 0.251979 +27.7181 5.08041 0.768743 +10.3592 7.69475 0.113277 +3.55429 0.131145 0.0542705 +6.6251 5.4355 0.0483946 +15.5899 7.6019 0.803383 +24.6519 8.43382 0.0643158 +6.5694 3.63918 0.0527761 +15.3631 4.20901 0.0365534 +22.2861 7.33867 0.613142 +8.32672 8.16903 0.144795 +22.7618 2.06286 0.0963609 +22.9668 7.76075 0.221602 +9.07941 1.04865 0.0493699 +30.9449 0.449339 0.951787 +32.8615 0.286472 0.413583 +32.1941 0.656761 0.612072 +8.99914 0.83989 0.049316 +12.2185 2.05159 0.0627161 +19.4413 2.51678 0.0759027 +28.3793 8.4729 0.0461733 +2.26684 7.01156 0.0552792 +9.8224 0.904879 0.0574812 +23.0033 1.68577 0.0681041 +8.98112 6.06027 0.0509274 +8.76844 5.24855 0.0188753 +34.3934 3.89711 0.0760846 +21.5677 3.7197 0.095238 +24.2617 6.51894 0.756376 +4.43731 4.29424 0.05708 +11.3234 6.29005 0.0597619 +5.52618 0.0998814 0.0438692 +9.69398 0.877113 0.0436203 +5.92177 1.62301 0.0463058 +5.22674 5.17749 0.0348348 +26.8429 5.08471 0.934959 +10.627 7.59361 0.136331 +31.0411 7.75114 0.0477166 +1.84271 7.06178 0.0743984 +12.0763 2.89475 0.0619694 +11.4605 0.515316 0.0344086 +14.8012 7.73554 0.771312 +20.6212 8.13377 0.337927 +18.1352 0.2139 0.0527043 +12.9672 0.0333888 0.0286795 +12.716 1.55387 0.0414588 +24.9195 3.15321 0.406611 +11.9379 6.16919 0.0507976 +24.4552 3.98706 0.608708 +18.0904 5.77803 0.167161 +9.37917 7.11269 0.0342975 +26.3922 1.54217 0.424123 +19.2967 2.70858 0.0281155 +22.4965 8.48495 0.0827316 +4.02267 4.72783 0.0518338 +16.6559 4.30792 0.0693405 +18.4355 4.81703 0.0495909 +15.0987 4.78395 0.052182 +13.7909 6.33357 0.0828174 +5.88796 7.60691 0.0483551 +17.8941 5.45504 0.08841 +27.1401 5.51818 0.647803 +20.7442 5.05457 0.2769 +11.7015 2.08902 0.0653481 +7.86416 6.67836 0.0730615 +13.8113 1.73207 0.0511506 +11.8862 2.25054 0.0634918 +7.77414 2.96739 0.065211 +15.0429 4.58722 0.0594601 +13.2865 7.15061 0.144195 +19.0714 3.1152 0.0619458 +28.9056 5.26787 0.400571 +13.2962 3.80329 0.0519979 +23.7413 3.90124 0.435985 +7.12387 8.447 0.192493 +9.35073 5.66177 0.04861 +29.8882 0.154568 1.06587 +26.0958 7.55329 0.11309 +25.0629 4.37995 0.90563 +28.5752 8.42161 0.0498677 +30.9692 1.08859 0.919478 +32.4395 8.38714 0.050913 +25.4801 5.72752 0.914131 +32.164 5.43141 0.0878446 +10.3647 4.34749 0.0669419 +17.2536 8.45297 0.598412 +7.75877 7.31353 0.0628457 +4.98403 4.57878 0.0544687 +14.574 3.27893 0.0530507 +12.6998 3.39589 0.036188 +22.3317 4.17673 0.267476 +14.9288 1.5706 0.0490339 +6.63922 2.04113 0.0550369 +17.8574 5.78258 0.179655 +12.3849 6.74023 0.0757787 +26.8245 7.92318 0.066567 +24.2246 1.38309 0.0898849 +33.0361 2.70038 0.231149 +33.3641 1.61553 0.281274 +20.4783 5.47232 0.404392 +10.1125 2.63797 0.0535013 +20.0957 2.79708 0.0725241 +11.3436 0.589052 0.0550754 +18.669 6.75797 0.794323 +26.5722 8.14744 0.0469527 +25.3869 1.19205 0.166823 +16.6355 4.28382 0.0640453 +11.5678 6.86163 0.0435003 +2.47497 7.59613 0.027321 +29.8377 3.07143 0.882191 +28.9628 0.522434 0.947824 +21.4911 6.69658 1.06436 +28.1809 6.20163 0.224088 +7.89387 3.46114 0.064001 +23.2607 8.59032 0.0562428 +11.1407 6.261 0.0269149 +28.6093 4.98415 0.566783 +14.7115 5.15679 0.0265339 +17.3458 0.698433 0.0475308 +13.538 8.38711 1.00516 +15.6028 8.65121 0.648277 +30.2614 1.43971 1.00105 +33.6091 6.71836 0.0638657 +10.3411 5.06356 0.0582952 +27.9721 3.44547 1.0447 +33.0931 5.16194 0.0698031 +18.6109 1.05886 0.0466367 +21.2619 3.06171 0.105158 +28.6543 2.91381 1.0248 +23.3483 5.95448 1.02011 +19.2093 4.6136 0.0541812 +31.0665 0.229955 0.898577 +19.9814 2.52 0.0456867 +27.4622 3.35423 1.03478 +31.7269 3.17376 0.422634 +11.9919 6.87639 0.0613136 +29.007 7.24378 0.0550893 +7.71248 8.31761 0.162295 +19.3654 8.22192 0.438087 +23.1172 4.27076 0.43442 +29.8091 4.88181 0.374612 +11.9246 5.27086 0.0531852 +33.4936 4.43333 0.0897031 +26.2845 0.333006 0.254343 +31.955 0.966082 0.67672 +9.92701 6.95926 0.0391626 +19.551 6.58027 0.837658 +16.4303 2.71527 0.0266009 +33.2246 1.72775 0.273981 +28.3217 4.96976 0.645563 +6.92949 7.41151 0.056046 +31.5557 0.48158 0.799977 +13.4468 5.69753 0.0555898 +17.1173 1.96834 0.06181 +3.30479 2.23801 0.0232759 +18.3493 3.85652 0.0431489 +31.3855 1.43122 0.78273 +16.7941 6.51104 0.270301 +2.57295 0.839519 0.0540315 +29.3384 4.32868 0.665284 +17.0144 5.841 0.121135 +4.8516 7.25423 0.0509408 +24.5083 4.83622 0.972653 +7.60021 1.13461 0.0503763 +21.6351 7.06647 0.922778 +15.3288 8.20479 1.06511 +9.68688 0.263512 0.0760641 +15.8253 7.998 1.02949 +11.3569 5.43178 0.034829 +17.0175 5.22219 0.0839707 +20.7989 3.39389 0.0813391 +18.1544 1.42221 0.0618942 +3.33144 5.24836 0.0457278 +20.3307 7.736 0.708507 +32.6968 0.211369 0.441031 +29.6518 4.8503 0.397279 +15.4426 5.57218 0.079574 +26.9743 8.03367 0.0337562 +7.10651 0.131435 0.0471931 +24.9346 3.23103 0.475303 +7.35109 1.10916 0.0541056 +4.14634 2.71699 0.0334982 +23.3698 7.93 0.137462 +13.2435 1.57433 0.0580013 +18.5329 3.13869 0.0434477 +3.14791 7.18255 0.0735878 +8.99599 6.55066 0.0475127 +4.00657 8.01463 0.0767648 +32.116 1.0825 0.613096 +5.31442 0.627525 0.0674108 +31.8792 1.93927 0.602762 +3.7884 4.32277 0.0503637 +8.16919 5.94129 0.0295523 +24.9391 1.02628 0.133684 +22.3623 5.48714 0.801043 +3.16983 1.9415 0.0545162 +12.4815 8.41325 0.945826 +5.74795 4.85744 0.035459 +12.6308 1.22092 0.0350711 +13.3076 7.27854 0.198873 +19.2612 4.31667 0.0806499 +26.2532 6.73016 0.280616 +2.55273 4.73946 0.0327862 +23.8342 7.73404 0.16096 +32.1907 2.45944 0.428617 +12.5584 8.4754 0.979281 +23.3451 0.157503 0.0477401 +16.2652 3.29534 0.0391841 +11.2402 1.37499 0.0434714 +19.0602 8.6305 0.213564 +3.4335 0.17075 0.0626225 +23.9084 2.52026 0.176827 +19.2651 3.39377 0.0514947 +10.2514 0.65877 0.052878 +2.54066 8.02499 0.0373907 +32.2066 6.73689 0.0242925 +23.102 4.7033 0.571726 +2.99452 3.55104 0.043635 +27.5403 1.69751 0.735767 +5.11335 1.4404 0.0817923 +16.5842 3.55081 0.0479477 +5.46396 5.36078 0.0265793 +31.8571 7.4608 0.0435841 +22.6609 2.12574 0.0739718 +24.1447 7.54065 0.197907 +12.89 5.78259 0.0426183 +12.2734 1.39268 0.0564094 +3.58585 0.276824 0.0507982 +30.5535 0.214997 1.01807 +5.85227 8.42623 0.0975904 +21.6158 2.14553 0.0534539 +8.35401 0.576819 0.0692725 +4.58077 5.76025 0.0625179 +3.04526 2.40892 0.0530612 +12.2991 8.24851 0.781207 +5.34285 5.80146 0.0486334 +29.2437 7.75131 0.049432 +21.6399 5.68801 0.757317 +7.52548 6.40083 0.0526282 +2.3745 4.03026 0.086967 +17.0012 3.7118 0.0535416 +29.3267 6.82917 0.0747731 +9.6765 1.59523 0.0810948 +20.6297 0.575209 0.0364561 +9.50145 7.96325 0.148455 +33.5034 3.35091 0.161806 +9.67888 4.88605 0.0532046 +8.35055 3.45037 0.0494925 +21.7658 6.50462 1.03737 +10.9927 6.78961 0.0543267 +29.9816 1.60339 1.02295 +26.7349 6.75063 0.236627 +24.3554 2.77543 0.256408 +32.9847 4.97612 0.0708231 +15.6857 4.22332 0.0671756 +2.86688 7.39541 0.0353251 +11.8965 3.65098 0.0375982 +9.78733 7.21421 0.0675007 +7.34659 8.2246 0.112145 +14.6689 5.02573 0.0339205 +3.51574 7.13028 0.0392552 +1.57183 7.75359 0.0467661 +18.8587 1.99748 0.0407946 +6.4336 3.94479 0.0531754 +28.7267 7.87789 0.0493181 +5.18237 6.64894 0.0572545 +4.61343 7.40106 0.0491886 +20.8505 5.2642 0.387227 +28.0602 3.31551 1.04357 +27.2771 6.87654 0.148012 +29.9371 3.01766 0.885487 +16.769 5.08709 0.0681296 +9.70629 1.84154 0.0551444 +2.30703 3.50257 0.0443815 +32.6191 3.92134 0.170523 +20.9247 0.994934 0.0691695 +32.5764 0.888547 0.492246 +29.1937 7.74 0.0519014 +23.3299 8.61671 0.0778678 +26.7488 1.41412 0.477374 +7.13943 3.19944 0.0600638 +31.253 5.89168 0.0640078 +29.6661 1.25065 1.03648 +17.23 0.583268 0.0541836 +15.9492 7.91965 1.06197 +17.8022 2.4655 0.0239258 +20.2991 8.35491 0.201859 +6.9735 4.54953 0.0581836 +32.8313 5.13108 0.0764308 +13.1198 4.72044 0.0357602 +2.71434 4.43333 0.0630765 +21.2551 2.34599 0.0565941 +6.23641 5.47511 0.0429765 +34.4863 3.84428 0.0716245 +32.1683 7.41722 0.0421167 +26.4069 2.52905 0.625628 +3.70264 1.68375 0.0396645 +29.3312 7.5242 0.0353037 +7.53064 4.68369 0.039397 +22.3721 2.00265 0.0652864 +7.18 6.79775 0.0661284 +25.1481 2.50561 0.318853 +24.8033 6.28579 0.767474 +12.9613 5.73215 0.0546584 +9.40041 2.554 0.0659257 +29.324 5.61266 0.238333 +32.6333 4.27396 0.156585 +7.07331 0.25872 0.0385545 +34.3527 8.54829 0.0705269 +20.2458 2.27977 0.0605702 +12.2064 3.35669 0.0253256 +17.5489 7.69518 1.05744 +3.64038 6.03167 0.0466398 +18.3515 1.48543 0.0473197 +21.3402 5.60663 0.634663 +18.5521 0.189655 0.0501248 +2.55673 6.54406 0.04039 +25.9424 5.28496 0.98751 +17.7073 5.79031 0.168811 +7.07565 4.96172 0.0697294 +29.0409 3.09431 1.02862 +21.986 5.73089 0.854937 +2.17366 4.01932 0.0561929 +15.8305 6.54525 0.183774 +26.6907 0.306085 0.307077 +22.9207 2.31879 0.0955177 +14.0839 1.36476 0.0435215 +27.8187 4.47015 0.93206 +17.0622 5.4285 0.0562944 +6.78727 1.92182 0.0562443 +6.04329 8.26931 0.0952369 +31.1102 3.23891 0.55072 +10.1169 0.121902 0.0629306 +20.9331 6.34817 0.942297 +9.66858 0.977974 0.0522665 +12.0786 0.127777 0.0429893 +12.2189 2.5274 0.011295 +1.97753 4.09947 0.05514 +1.83649 2.10428 0.0568941 +4.41786 2.88942 0.0582484 +25.4627 1.14975 0.212622 +10.6318 3.11916 0.0631765 +16.0844 6.95744 0.413425 +21.9359 2.71861 0.0582919 +18.3492 5.08428 0.131442 +30.9986 1.8369 0.819071 +27.2536 1.08924 0.543124 +30.5547 2.77916 0.78347 +11.54 0.290314 0.0654902 +34.046 4.13899 0.0810615 +32.909 6.16349 0.0602654 +23.2674 3.68501 0.250984 +27.9898 1.9718 0.88149 +7.51248 2.83648 0.0650276 +33.6039 7.16216 0.0427234 +7.8571 2.10784 0.0319761 +27.7276 2.89714 0.994411 +2.61893 6.0038 0.0415165 +26.88 2.17841 0.656677 +20.4013 8.12659 0.369693 +14.8196 1.51079 0.0358007 +10.3931 0.214666 0.0494378 +17.1299 3.73066 0.0699296 +11.0892 4.72221 0.0463687 +13.4376 4.97687 0.047055 diff --git a/examples/advanced/example_phase_diagram.gle b/examples/advanced/example_phase_diagram.gle new file mode 100644 index 0000000..d62eda7 --- /dev/null +++ b/examples/advanced/example_phase_diagram.gle @@ -0,0 +1,180 @@ +! GLE graphics file +! Generated by gleplot +! gleplot-meta-begin v1 +! gleplot: dpi = 100 +! gleplot: import-data = data_0.dat, data_1.dat, data_2.dat +! gleplot-meta-end + +size 33.02 15.24 +set hei 0.42328 + +sub gleplot_magma z + if z < 0 then z = 0 + if z > 1 then z = 1 + local r = 0.001462 + local g = 0.000466 + local b = 0.013866 + if (z >= 0/17) and (z <= 1/17) then r = 0.001462+(0.035520-0.001462)*(z-0/17)*17 + if (z >= 0/17) and (z <= 1/17) then g = 0.000466+(0.028397-0.000466)*(z-0/17)*17 + if (z >= 0/17) and (z <= 1/17) then b = 0.013866+(0.125209-0.013866)*(z-0/17)*17 + if (z >= 1/17) and (z <= 2/17) then r = 0.035520+(0.102815-0.035520)*(z-1/17)*17 + if (z >= 1/17) and (z <= 2/17) then g = 0.028397+(0.063010-0.028397)*(z-1/17)*17 + if (z >= 1/17) and (z <= 2/17) then b = 0.125209+(0.257854-0.125209)*(z-1/17)*17 + if (z >= 2/17) and (z <= 3/17) then r = 0.102815+(0.191460-0.102815)*(z-2/17)*17 + if (z >= 2/17) and (z <= 3/17) then g = 0.063010+(0.064818-0.063010)*(z-2/17)*17 + if (z >= 2/17) and (z <= 3/17) then b = 0.257854+(0.396152-0.257854)*(z-2/17)*17 + if (z >= 3/17) and (z <= 4/17) then r = 0.191460+(0.291366-0.191460)*(z-3/17)*17 + if (z >= 3/17) and (z <= 4/17) then g = 0.064818+(0.064553-0.064818)*(z-3/17)*17 + if (z >= 3/17) and (z <= 4/17) then b = 0.396152+(0.475462-0.396152)*(z-3/17)*17 + if (z >= 4/17) and (z <= 5/17) then r = 0.291366+(0.384299-0.291366)*(z-4/17)*17 + if (z >= 4/17) and (z <= 5/17) then g = 0.064553+(0.097855-0.064553)*(z-4/17)*17 + if (z >= 4/17) and (z <= 5/17) then b = 0.475462+(0.501002-0.475462)*(z-4/17)*17 + if (z >= 5/17) and (z <= 6/17) then r = 0.384299+(0.475780-0.384299)*(z-5/17)*17 + if (z >= 5/17) and (z <= 6/17) then g = 0.097855+(0.134577-0.097855)*(z-5/17)*17 + if (z >= 5/17) and (z <= 6/17) then b = 0.501002+(0.507921-0.501002)*(z-5/17)*17 + if (z >= 6/17) and (z <= 7/17) then r = 0.475780+(0.569172-0.475780)*(z-6/17)*17 + if (z >= 6/17) and (z <= 7/17) then g = 0.134577+(0.167454-0.134577)*(z-6/17)*17 + if (z >= 6/17) and (z <= 7/17) then b = 0.507921+(0.504105-0.507921)*(z-6/17)*17 + if (z >= 7/17) and (z <= 8/17) then r = 0.569172+(0.664915-0.569172)*(z-7/17)*17 + if (z >= 7/17) and (z <= 8/17) then g = 0.167454+(0.198075-0.167454)*(z-7/17)*17 + if (z >= 7/17) and (z <= 8/17) then b = 0.504105+(0.488836-0.504105)*(z-7/17)*17 + if (z >= 8/17) and (z <= 9/17) then r = 0.664915+(0.761077-0.664915)*(z-8/17)*17 + if (z >= 8/17) and (z <= 9/17) then g = 0.198075+(0.231214-0.198075)*(z-8/17)*17 + if (z >= 8/17) and (z <= 9/17) then b = 0.488836+(0.460162-0.488836)*(z-8/17)*17 + if (z >= 9/17) and (z <= 10/17) then r = 0.761077+(0.852126-0.761077)*(z-9/17)*17 + if (z >= 9/17) and (z <= 10/17) then g = 0.231214+(0.276106-0.231214)*(z-9/17)*17 + if (z >= 9/17) and (z <= 10/17) then b = 0.460162+(0.418573-0.460162)*(z-9/17)*17 + if (z >= 10/17) and (z <= 11/17) then r = 0.852126+(0.925937-0.852126)*(z-10/17)*17 + if (z >= 10/17) and (z <= 11/17) then g = 0.276106+(0.346844-0.276106)*(z-10/17)*17 + if (z >= 10/17) and (z <= 11/17) then b = 0.418573+(0.374959-0.418573)*(z-10/17)*17 + if (z >= 11/17) and (z <= 12/17) then r = 0.925937+(0.969680-0.925937)*(z-11/17)*17 + if (z >= 11/17) and (z <= 12/17) then g = 0.346844+(0.446936-0.346844)*(z-11/17)*17 + if (z >= 11/17) and (z <= 12/17) then b = 0.374959+(0.360311-0.374959)*(z-11/17)*17 + if (z >= 12/17) and (z <= 13/17) then r = 0.969680+(0.989363-0.969680)*(z-12/17)*17 + if (z >= 12/17) and (z <= 13/17) then g = 0.446936+(0.557873-0.446936)*(z-12/17)*17 + if (z >= 12/17) and (z <= 13/17) then b = 0.360311+(0.391671-0.360311)*(z-12/17)*17 + if (z >= 13/17) and (z <= 14/17) then r = 0.989363+(0.996580-0.989363)*(z-13/17)*17 + if (z >= 13/17) and (z <= 14/17) then g = 0.557873+(0.668256-0.557873)*(z-13/17)*17 + if (z >= 13/17) and (z <= 14/17) then b = 0.391671+(0.456192-0.391671)*(z-13/17)*17 + if (z >= 14/17) and (z <= 15/17) then r = 0.996580+(0.996727-0.996580)*(z-14/17)*17 + if (z >= 14/17) and (z <= 15/17) then g = 0.668256+(0.776795-0.668256)*(z-14/17)*17 + if (z >= 14/17) and (z <= 15/17) then b = 0.456192+(0.541039-0.456192)*(z-14/17)*17 + if (z >= 15/17) and (z <= 16/17) then r = 0.996727+(0.992440-0.996727)*(z-15/17)*17 + if (z >= 15/17) and (z <= 16/17) then g = 0.776795+(0.884330-0.776795)*(z-15/17)*17 + if (z >= 15/17) and (z <= 16/17) then b = 0.541039+(0.640099-0.541039)*(z-15/17)*17 + if (z >= 16/17) and (z <= 17/17) then r = 0.992440+(0.987053-0.992440)*(z-16/17)*17 + if (z >= 16/17) and (z <= 17/17) then g = 0.884330+(0.991438-0.884330)*(z-16/17)*17 + if (z >= 16/17) and (z <= 17/17) then b = 0.640099+(0.749504-0.640099)*(z-16/17)*17 + return rgb(r,g,b) +end sub + +sub gleplot_colorbar_v zmin zmax zstep palette$ wd hi format$ label$ + default zstep 0 + default wd 0.5 + default format "fix 1" + default label "" + ! Guard a degenerate (constant-field) range: zmax = zmin would + ! divide by zero in the tick-position math below and abort the + ! render. Expand to a nominal unit span so the bar still draws. + if zmax = zmin then + zmax = zmin+1 + end if + if zstep = 0 then + zstep = (zmax-zmin)/5 + end if + begin box name gleplot_cbar + if palette$ = "gray" then + colormap "y" 0 1 0 1 1 200 wd hi + else if palette$ = "color" then + colormap "y" 0 1 0 1 1 200 wd hi color + else + colormap "y" 0 1 0 1 1 200 wd hi palette palette$ + end if + end box + amove pointx(gleplot_cbar.bl) pointy(gleplot_cbar.bl) + box wd hi + set just lc + local zp = zmin + while zp <= zmax+(zmax-zmin)/1e6 + local yy = pointy(gleplot_cbar.bc)+(zp-zmin)/(zmax-zmin)*hi + amove pointx(gleplot_cbar.rc) yy + rline wd/3 0 + rmove 0.1 0 + write format$(zp,format$) + zp = zp+zstep + next + if label$ <> "" then + amove pointx(gleplot_cbar.rc)+1.3 pointy(gleplot_cbar.cc) + begin rotate 90 + set just cc + write label$ + end rotate + end if +end sub + +sub gleplot_contour_labels file$ format$ + default format "fix 1" + set just cc hei 0.25 color black + fopen file$ f1 read + until feof(f1) + fread f1 x y i v + amove xg(x) yg(y) + s$ = format$(v,format$) + begin box add 0.05 fill white + write s$ + end box + write s$ + next + fclose f1 +end sub + +begin fitz + data "data_points1.dat" + x from 1.50628 to 34.4919 step 0.370625 + y from 0.0026167 to 8.73657 step 0.126579 +end fitz +begin fitz + data "data_points2.dat" + x from 1.50628 to 34.4919 step 0.370625 + y from 0.0026167 to 8.73657 step 0.126579 + ncontour 3 +end fitz +begin contour + data "data_points2.z" + values 0.6 +end contour +amove 1 1 +begin graph + size 12.4255 12.74 + scale 1 1 + title "Antiferromagnet Phase Diagram (synthetic data)" + xtitle "Temperature (K)" + ytitle "Magnetic field (T)" + xaxis min 1.50628 max 34.4919 + yaxis min 0.0026167 max 8.73657 + colormap "data_points1.z" 200 200 zmin 0 zmax 1.2 palette gleplot_magma + data "data_points2-cdata.dat" d1=c1,c2 + d1 line color BLACK lwidth 0.045864 +end graph +amove xg(xgmax)+0.3 yg(ygmin) +gleplot_colorbar_v zmin 0 zmax 1.2 zstep 0.24 palette "gleplot_magma" wd 0.5 hi yg(ygmax)-yg(ygmin) format "fix 2" label "\chi{} (emu mol^{-1})" +amove 0 0 +gleplot_contour_labels file "data_points2-clabels.dat" format "fix 2" + +amove 17.7745 1 +begin graph + size 12.4255 12.74 + scale 1 1 + title "Susceptibility line cuts" + xtitle "Temperature (K)" + ytitle "\chi{} (emu mol^{-1})" + xaxis min 1.5 max 34.5 + yaxis min 0 max 1.2 + data data_0.dat d2=c1,c2 + d2 line smooth color BLUE lwidth 0.07056 key "H = 0 T" + data data_1.dat d3=c1,c2 + d3 line smooth color GREEN lwidth 0.07056 key "H = 5 T" + data data_2.dat d4=c1,c2 + d4 line smooth color RED lwidth 0.07056 key "H = 8 T" + key pos tr +end graph diff --git a/examples/advanced/phase_diagram.py b/examples/advanced/phase_diagram.py new file mode 100644 index 0000000..6c0c71f --- /dev/null +++ b/examples/advanced/phase_diagram.py @@ -0,0 +1,147 @@ +"""Antiferromagnet H-T phase diagram from synthetic susceptibility data. + +Demonstrates the scattered-data heatmap/contour path (``tripcolor`` + +``tricontour``): magnetic susceptibility measurements are simulated at +scattered (temperature, field) points -- as a real experiment would collect +them, sweeping temperature at a handful of fixed applied fields -- rather +than on a perfect grid. gleplot writes the raw ``(x, y, z)`` triples to a +sidecar file and lets GLE's own ``fitz`` (Akima interpolation) grid them at +compile time. + +The susceptibility chi(T, H) is modelled as a broad ridge that peaks along +the Neel transition line: + + T_N(H) = T_N0 * sqrt(1 - (H / Hc)^2) + +(critical-field suppression of the ordering temperature), broadening as H +approaches the critical field Hc. A ``tricontour`` near the ridge's +half-height traces the transition boundary directly on top of the +``tripcolor`` heatmap. + +The phase diagram itself is a single axes with a colorbar; the second panel +(susceptibility line cuts at representative fields) is a genuine companion +that shows the peak shifting to lower T and broadening as H -> Hc. + +``vmax`` is set just above the physical peak (chi0 + amplitude ~ 1.05). GLE's +Akima gridding can overshoot the data range near a sharp ridge from +scattered/noisy samples, but gleplot's generated palette subroutines clamp +the normalized value into [0, 1], so any overshoot saturates at the palette's +brightest colour rather than producing stray speckles -- a tight ``vmax`` is +safe. (Setting ``vmax`` explicitly is still worthwhile: with ``vmax=None`` a +single overshoot node would auto-scale the whole colour range off the real +data.) Greek and math in labels use GLE's TeX-like markup directly, e.g. +``\\chi`` for the susceptibility symbol. +""" + +import sys +import numpy as np +from pathlib import Path + +# Add src to path +sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src")) + +import gleplot as glp + + +def example_phase_diagram(): + """Example: susceptibility phase diagram (tripcolor + tricontour + colorbar).""" + print("Creating example: Antiferromagnet phase diagram...") + + rng = np.random.default_rng(11) + + T_N0 = 30.0 # zero-field Neel temperature (K) + Hc = 9.5 # critical field (T) + chi0 = 0.05 # paramagnetic background susceptibility (emu/mol) + amplitude = 1.0 # peak height above background at the transition + + n_points = 3200 + H = rng.uniform(0.0, 0.92 * Hc, n_points) + T = rng.uniform(1.5, 1.15 * T_N0, n_points) + + T_N = T_N0 * np.sqrt(1.0 - (H / Hc) ** 2) + width = 2.0 + 1.4 * (H / Hc) # critical broadening near Hc + chi = chi0 + amplitude * np.exp(-((T - T_N) ** 2) / (2.0 * width**2)) + chi += rng.normal(0.0, 0.015 * amplitude, n_points) # measurement noise + + fig, axes = glp.subplots(1, 2, figsize=(13, 6)) + fig.subplots_adjust(wspace=0.35) + + ax = axes[0] + # Scattered-data heatmap: gridded at GLE compile time via `begin fitz`. + # vmax sits just above the physical peak (chi0 + amplitude); the palette + # subs clamp overshoot, so this stays speckle-free (see module docstring). + ax.tripcolor( + T, + H, + chi, + gridsize=(90, 70), + cmap="magma", + vmin=0.0, + vmax=chi0 + 1.15 * amplitude, + ) + + # Trace the transition boundary near the susceptibility ridge's + # half-height, with inline value labels. + boundary_level = chi0 + 0.55 * amplitude + ax.tricontour( + T, + H, + chi, + gridsize=(90, 70), + levels=[boundary_level], + colors="black", + linewidths=1.3, + clabel=True, + clabel_fmt="fix 2", + ) + + ax.set_xlabel("Temperature (K)") + ax.set_ylabel("Magnetic field (T)") + ax.set_title("Antiferromagnet Phase Diagram (synthetic data)") + + # Greek chi via GLE's TeX-like markup. The '{}' after \chi is required: + # GLE (like TeX) swallows the space right after a macro name, so + # r'\chi (emu ...)' would render as "chi(emu ...)" with no gap. + fig.colorbar(label=r"\chi{} (emu mol^{-1})", format="fix 2") + + # Companion panel: susceptibility line cuts at representative fields, + # showing the peak shifting to lower T and broadening as H -> Hc. + ax2 = axes[1] + T_line = np.linspace(1.5, 1.15 * T_N0, 200) + for H_val, color in ((0.0, "blue"), (5.0, "green"), (8.0, "red")): + T_N_line = T_N0 * np.sqrt(1.0 - (H_val / Hc) ** 2) + width_line = 2.0 + 1.4 * (H_val / Hc) + chi_line = chi0 + amplitude * np.exp( + -((T_line - T_N_line) ** 2) / (2.0 * width_line**2) + ) + ax2.plot(T_line, chi_line, color=color, linewidth=2, label=f"H = {H_val:.0f} T") + + ax2.set_xlabel("Temperature (K)") + # Same label as the colorbar above, but written as matplotlib-style + # mathtext instead of GLE markup -- gleplot translates $...$ segments to + # GLE markup at store time (here to r'\chi{} (emu mol^{-1})'), so both + # styles render identically. Text outside the $...$ passes through. + ax2.set_ylabel(r"$\chi$ (emu mol$^{-1}$)") + ax2.set_title("Susceptibility line cuts") + ax2.set_ylim(0, 1.2) + ax2.legend() + + # Save the GLE script + its points sidecars next to this script (the + # tracked-example convention: the .gle and its input sidecars live beside + # the .py). PDF/PNG and GLE's own -cdata/-clabels/-cvalues derivatives are + # build products (gitignored), so compilation is optional. + output_dir = Path(__file__).parent + gle_file = output_dir / "example_phase_diagram.gle" + fig.savefig_gle(str(gle_file)) + print(f" Saved to {gle_file}") + + try: + png_file = output_dir / "example_phase_diagram.png" + fig.savefig(str(png_file), dpi=150) + print(f" Compiled to {png_file}") + except RuntimeError: + print(" GLE not available for compilation; GLE script saved only.") + + +if __name__ == "__main__": + example_phase_diagram() diff --git a/examples/basic/__init__.py b/examples/basic/__init__.py index 55bb22e..bdea384 100644 --- a/examples/basic/__init__.py +++ b/examples/basic/__init__.py @@ -10,14 +10,16 @@ example_errorbar_with_line, example_combined_errorbars, ) +from .heatmap_imshow import example_heatmap_imshow __all__ = [ - "example_basic_line_plot", - "example_scatter_plot", - "example_bar_chart", - "example_symmetric_error_bars", - "example_asymmetric_error_bars", - "example_horizontal_error_bars", - "example_errorbar_with_line", - "example_combined_errorbars", + 'example_basic_line_plot', + 'example_scatter_plot', + 'example_bar_chart', + 'example_symmetric_error_bars', + 'example_asymmetric_error_bars', + 'example_horizontal_error_bars', + 'example_errorbar_with_line', + 'example_combined_errorbars', + 'example_heatmap_imshow', ] diff --git a/examples/basic/data_contour1.z b/examples/basic/data_contour1.z new file mode 100644 index 0000000..c5a0427 --- /dev/null +++ b/examples/basic/data_contour1.z @@ -0,0 +1,101 @@ +! nx 120 ny 100 xmin -3 xmax 3 ymin -2.5 ymax 2.5 +0.000488095 0.00056708 0.000657173 0.000759646 0.000875868 0.00100731 0.00115553 0.0013222 0.00150906 0.00171797 0.00195082 0.00220961 0.00249638 0.00281321 0.0031622 0.00354545 0.00396507 0.00442308 0.00492148 0.00546213 0.00604678 0.00667702 0.00735423 0.00807955 0.00885388 0.00967778 0.0105515 0.0114749 0.0124474 0.013468 0.0145353 0.0156474 0.0168018 0.0179956 0.0192253 0.0204868 0.0217756 0.0230868 0.0244148 0.0257537 0.0270969 0.0284379 0.0297694 0.0310842 0.0323746 0.033633 0.0348516 0.0360226 0.0371384 0.0381916 0.039175 0.0400816 0.0409052 0.0416396 0.0422796 0.0428204 0.0432581 0.0435893 0.0438115 0.043923 0.043923 0.0438115 0.0435893 0.0432581 0.0428204 0.0422796 0.0416396 0.0409052 0.0400816 0.039175 0.0381916 0.0371384 0.0360226 0.0348516 0.033633 0.0323746 0.0310842 0.0297694 0.0284379 0.0270969 0.0257537 0.0244148 0.0230868 0.0217756 0.0204868 0.0192253 0.0179956 0.0168018 0.0156474 0.0145353 0.013468 0.0124474 0.0114749 0.0105515 0.00967778 0.00885388 0.00807955 0.00735423 0.00667702 0.00604678 0.00546213 0.00492148 0.00442308 0.00396507 0.00354545 0.0031622 0.00281321 0.00249638 0.00220961 0.00195082 0.00171797 0.00150906 0.0013222 0.00115553 0.00100731 0.000875868 0.000759646 0.000657173 0.00056708 0.000488095 +0.000553077 0.000642577 0.000744665 0.00086078 0.000992475 0.00114141 0.00130937 0.00149823 0.00170997 0.00194669 0.00221054 0.00250379 0.00282874 0.00318774 0.00358319 0.00401747 0.00449295 0.00501195 0.00557669 0.00618933 0.00685182 0.00756596 0.00833333 0.00915522 0.0100326 0.0109662 0.0119563 0.0130026 0.0141045 0.0152611 0.0164705 0.0177306 0.0190387 0.0203914 0.0217848 0.0232143 0.0246747 0.0261605 0.0276653 0.0291824 0.0307045 0.0322239 0.0337327 0.0352225 0.0366848 0.0381107 0.0394915 0.0408184 0.0420828 0.0432763 0.0443905 0.0454179 0.046351 0.0471832 0.0479084 0.0485213 0.0490172 0.0493925 0.0496442 0.0497706 0.0497706 0.0496442 0.0493925 0.0490172 0.0485213 0.0479084 0.0471832 0.046351 0.0454179 0.0443905 0.0432763 0.0420828 0.0408184 0.0394915 0.0381107 0.0366848 0.0352225 0.0337327 0.0322239 0.0307045 0.0291824 0.0276653 0.0261605 0.0246747 0.0232143 0.0217848 0.0203914 0.0190387 0.0177306 0.0164705 0.0152611 0.0141045 0.0130026 0.0119563 0.0109662 0.0100326 0.00915522 0.00833333 0.00756596 0.00685182 0.00618933 0.00557669 0.00501195 0.00449295 0.00401747 0.00358319 0.00318774 0.00282874 0.00250379 0.00221054 0.00194669 0.00170997 0.00149823 0.00130937 0.00114141 0.000992475 0.00086078 0.000744665 0.000642577 0.000553077 +0.000625114 0.000726271 0.000841656 0.000972895 0.00112174 0.00129008 0.00147991 0.00169337 0.00193269 0.00220024 0.00249846 0.0028299 0.00319717 0.00360294 0.0040499 0.00454074 0.00507815 0.00566474 0.00630304 0.00699547 0.00774425 0.00855141 0.00941872 0.0103477 0.0113394 0.0123945 0.0135135 0.0146961 0.0159416 0.0172488 0.0186157 0.02004 0.0215185 0.0230474 0.0246222 0.0262379 0.0278885 0.0295678 0.0312686 0.0329833 0.0347036 0.036421 0.0381264 0.0398102 0.0414629 0.0430745 0.0446351 0.0461349 0.047564 0.0489129 0.0501723 0.0513334 0.0523881 0.0533287 0.0541484 0.0548411 0.0554016 0.0558257 0.0561103 0.0562531 0.0562531 0.0561103 0.0558257 0.0554016 0.0548411 0.0541484 0.0533287 0.0523881 0.0513334 0.0501723 0.0489129 0.047564 0.0461349 0.0446351 0.0430745 0.0414629 0.0398102 0.0381264 0.036421 0.0347036 0.0329833 0.0312686 0.0295678 0.0278885 0.0262379 0.0246222 0.0230474 0.0215185 0.02004 0.0186157 0.0172488 0.0159416 0.0146961 0.0135135 0.0123945 0.0113394 0.0103477 0.00941872 0.00855141 0.00774425 0.00699547 0.00630304 0.00566474 0.00507815 0.00454074 0.0040499 0.00360294 0.00319717 0.0028299 0.00249846 0.00220024 0.00193269 0.00169337 0.00147991 0.00129008 0.00112174 0.000972895 0.000841656 0.000726271 0.000625114 +0.000704734 0.000818775 0.000948856 0.00109681 0.00126462 0.00145439 0.00166841 0.00190905 0.00217885 0.00248048 0.00281668 0.00319034 0.00360439 0.00406184 0.00456572 0.00511908 0.00572494 0.00638624 0.00710585 0.00788647 0.00873062 0.00964058 0.0106184 0.0116656 0.0127836 0.0139732 0.0152347 0.0165679 0.0179721 0.0194457 0.0209868 0.0225925 0.0242592 0.0259829 0.0277583 0.0295797 0.0314406 0.0333338 0.0352512 0.0371843 0.0391238 0.0410599 0.0429824 0.0448807 0.0467439 0.0485608 0.0503202 0.052011 0.0536221 0.0551428 0.0565626 0.0578717 0.0590607 0.0601211 0.0610452 0.0618261 0.062458 0.0629361 0.0632569 0.063418 0.063418 0.0632569 0.0629361 0.062458 0.0618261 0.0610452 0.0601211 0.0590607 0.0578717 0.0565626 0.0551428 0.0536221 0.052011 0.0503202 0.0485608 0.0467439 0.0448807 0.0429824 0.0410599 0.0391238 0.0371843 0.0352512 0.0333338 0.0314406 0.0295797 0.0277583 0.0259829 0.0242592 0.0225925 0.0209868 0.0194457 0.0179721 0.0165679 0.0152347 0.0139732 0.0127836 0.0116656 0.0106184 0.00964058 0.00873062 0.00788647 0.00710585 0.00638624 0.00572494 0.00511908 0.00456572 0.00406184 0.00360439 0.00319034 0.00281668 0.00248048 0.00217885 0.00190905 0.00166841 0.00145439 0.00126462 0.00109681 0.000948856 0.000818775 0.000704734 +0.00079247 0.000920709 0.00106698 0.00123336 0.00142206 0.00163546 0.00187612 0.00214672 0.00245011 0.00278929 0.00316735 0.00358753 0.00405312 0.00456752 0.00513414 0.00575639 0.00643767 0.00718131 0.0079905 0.0088683 0.00981755 0.0108408 0.0119403 0.0131179 0.0143751 0.0157128 0.0171314 0.0186306 0.0202095 0.0218666 0.0235996 0.0254051 0.0272794 0.0292176 0.0312141 0.0332623 0.0353549 0.0374837 0.0396399 0.0418136 0.0439945 0.0461717 0.0483336 0.0504682 0.0525633 0.0546064 0.0565849 0.0584862 0.0602979 0.0620079 0.0636044 0.0650765 0.0664135 0.0676059 0.0686451 0.0695232 0.0702337 0.0707714 0.0711322 0.0713132 0.0713132 0.0711322 0.0707714 0.0702337 0.0695232 0.0686451 0.0676059 0.0664135 0.0650765 0.0636044 0.0620079 0.0602979 0.0584862 0.0565849 0.0546064 0.0525633 0.0504682 0.0483336 0.0461717 0.0439945 0.0418136 0.0396399 0.0374837 0.0353549 0.0332623 0.0312141 0.0292176 0.0272794 0.0254051 0.0235996 0.0218666 0.0202095 0.0186306 0.0171314 0.0157128 0.0143751 0.0131179 0.0119403 0.0108408 0.00981755 0.0088683 0.0079905 0.00718131 0.00643767 0.00575639 0.00513414 0.00456752 0.00405312 0.00358753 0.00316735 0.00278929 0.00245011 0.00214672 0.00187612 0.00163546 0.00142206 0.00123336 0.00106698 0.000920709 0.00079247 +0.00088886 0.0010327 0.00119676 0.00138337 0.00159502 0.00183438 0.00210431 0.00240782 0.00274812 0.00312855 0.0035526 0.00402388 0.00454611 0.00512308 0.00575861 0.00645655 0.0072207 0.00805478 0.0089624 0.00994697 0.0110117 0.0121594 0.0133926 0.0147135 0.0161236 0.017624 0.0192151 0.0208966 0.0226676 0.0245263 0.02647 0.0284952 0.0305975 0.0327714 0.0350107 0.037308 0.0396551 0.0420429 0.0444613 0.0468994 0.0493456 0.0517876 0.0542124 0.0566067 0.0589567 0.0612483 0.0634674 0.0656 0.067632 0.06955 0.0713407 0.0729918 0.0744915 0.0758289 0.0769944 0.0779794 0.0787764 0.0793795 0.0797841 0.0799872 0.0799872 0.0797841 0.0793795 0.0787764 0.0779794 0.0769944 0.0758289 0.0744915 0.0729918 0.0713407 0.06955 0.067632 0.0656 0.0634674 0.0612483 0.0589567 0.0566067 0.0542124 0.0517876 0.0493456 0.0468994 0.0444613 0.0420429 0.0396551 0.037308 0.0350107 0.0327714 0.0305975 0.0284952 0.02647 0.0245263 0.0226676 0.0208966 0.0192151 0.017624 0.0161236 0.0147135 0.0133926 0.0121594 0.0110117 0.00994697 0.0089624 0.00805478 0.0072207 0.00645655 0.00575861 0.00512308 0.00454611 0.00402388 0.0035526 0.00312855 0.00274812 0.00240782 0.00210431 0.00183438 0.00159502 0.00138337 0.00119676 0.0010327 0.00088886 +0.000994433 0.00115535 0.00133891 0.00154768 0.00178447 0.00205226 0.00235425 0.00269381 0.00307453 0.00350014 0.00397455 0.00450181 0.00508607 0.00573156 0.00644258 0.00722342 0.00807833 0.00901148 0.0100269 0.0111284 0.0123196 0.0136036 0.0149833 0.0164611 0.0180387 0.0197173 0.0214973 0.0233786 0.02536 0.0274394 0.0296139 0.0318797 0.0342316 0.0366638 0.039169 0.0417392 0.0443651 0.0470365 0.0497422 0.0524699 0.0552066 0.0579386 0.0606515 0.0633301 0.0659592 0.068523 0.0710057 0.0733915 0.0756649 0.0778107 0.0798142 0.0816613 0.0833391 0.0848355 0.0861394 0.0872413 0.0881329 0.0888077 0.0892604 0.0894876 0.0894876 0.0892604 0.0888077 0.0881329 0.0872413 0.0861394 0.0848355 0.0833391 0.0816613 0.0798142 0.0778107 0.0756649 0.0733915 0.0710057 0.068523 0.0659592 0.0633301 0.0606515 0.0579386 0.0552066 0.0524699 0.0497422 0.0470365 0.0443651 0.0417392 0.039169 0.0366638 0.0342316 0.0318797 0.0296139 0.0274394 0.02536 0.0233786 0.0214973 0.0197173 0.0180387 0.0164611 0.0149833 0.0136036 0.0123196 0.0111284 0.0100269 0.00901148 0.00807833 0.00722342 0.00644258 0.00573156 0.00508607 0.00450181 0.00397455 0.00350014 0.00307453 0.00269381 0.00235425 0.00205226 0.00178447 0.00154768 0.00133891 0.00115535 0.000994433 +0.00110971 0.00128929 0.00149412 0.0017271 0.00199133 0.00229017 0.00262716 0.00300609 0.00343094 0.00390589 0.0044353 0.00502368 0.00567567 0.00639599 0.00718943 0.00806078 0.0090148 0.0100561 0.0111893 0.0124185 0.0137477 0.0151806 0.0167202 0.0183693 0.0201298 0.022003 0.0239894 0.0260887 0.0282998 0.0306203 0.0330469 0.0355753 0.0381999 0.040914 0.0437097 0.0465778 0.0495081 0.0524892 0.0555085 0.0585524 0.0616064 0.0646551 0.0676824 0.0706716 0.0736054 0.0764664 0.0792369 0.0818994 0.0844363 0.0868308 0.0890665 0.0911278 0.0930001 0.0946699 0.096125 0.0973546 0.0983497 0.0991026 0.0996078 0.0998613 0.0998613 0.0996078 0.0991026 0.0983497 0.0973546 0.096125 0.0946699 0.0930001 0.0911278 0.0890665 0.0868308 0.0844363 0.0818994 0.0792369 0.0764664 0.0736054 0.0706716 0.0676824 0.0646551 0.0616064 0.0585524 0.0555085 0.0524892 0.0495081 0.0465778 0.0437097 0.040914 0.0381999 0.0355753 0.0330469 0.0306203 0.0282998 0.0260887 0.0239894 0.022003 0.0201298 0.0183693 0.0167202 0.0151806 0.0137477 0.0124185 0.0111893 0.0100561 0.0090148 0.00806078 0.00718943 0.00639599 0.00567567 0.00502368 0.0044353 0.00390589 0.00343094 0.00300609 0.00262716 0.00229017 0.00199133 0.0017271 0.00149412 0.00128929 0.00110971 +0.0012352 0.00143508 0.00166308 0.0019224 0.00221652 0.00254914 0.00292424 0.00334602 0.00381891 0.00434757 0.00493685 0.00559176 0.00631748 0.00711926 0.00800242 0.00897231 0.0100342 0.0111933 0.0124545 0.0138227 0.0153023 0.0168972 0.018611 0.0204465 0.0224061 0.0244911 0.0267021 0.0290389 0.0315 0.0340828 0.0367839 0.0395982 0.0425196 0.0455406 0.0486524 0.0518449 0.0551066 0.0584247 0.0617854 0.0651735 0.0685729 0.0719664 0.075336 0.0786632 0.0819288 0.0851133 0.0881971 0.0911606 0.0939845 0.0966497 0.0991383 0.101433 0.103517 0.105375 0.106995 0.108364 0.109471 0.110309 0.110872 0.111154 0.111154 0.110872 0.110309 0.109471 0.108364 0.106995 0.105375 0.103517 0.101433 0.0991383 0.0966497 0.0939845 0.0911606 0.0881971 0.0851133 0.0819288 0.0786632 0.075336 0.0719664 0.0685729 0.0651735 0.0617854 0.0584247 0.0551066 0.0518449 0.0486524 0.0455406 0.0425196 0.0395982 0.0367839 0.0340828 0.0315 0.0290389 0.0267021 0.0244911 0.0224061 0.0204465 0.018611 0.0168972 0.0153023 0.0138227 0.0124545 0.0111933 0.0100342 0.00897231 0.00800242 0.00711926 0.00631748 0.00559176 0.00493685 0.00434757 0.00381891 0.00334602 0.00292424 0.00254914 0.00221652 0.0019224 0.00166308 0.00143508 0.0012352 +0.00137137 0.00159329 0.00184642 0.00213433 0.00246088 0.00283017 0.00324663 0.0037149 0.00423993 0.00482687 0.00548111 0.00620823 0.00701395 0.00790412 0.00888465 0.00996146 0.0111404 0.0124273 0.0138276 0.0153466 0.0169893 0.0187601 0.0206628 0.0227007 0.0248763 0.0271911 0.0296459 0.0322403 0.0349727 0.0378403 0.0408392 0.0439637 0.0472072 0.0505613 0.0540161 0.0575605 0.0611818 0.0648658 0.068597 0.0723586 0.0761328 0.0799004 0.0836415 0.0873355 0.0909611 0.0944967 0.0979205 0.101211 0.104346 0.107305 0.110068 0.112615 0.114929 0.116992 0.118791 0.12031 0.12154 0.12247 0.123095 0.123408 0.123408 0.123095 0.12247 0.12154 0.12031 0.118791 0.116992 0.114929 0.112615 0.110068 0.107305 0.104346 0.101211 0.0979205 0.0944967 0.0909611 0.0873355 0.0836415 0.0799004 0.0761328 0.0723586 0.068597 0.0648658 0.0611818 0.0575605 0.0540161 0.0505613 0.0472072 0.0439637 0.0408392 0.0378403 0.0349727 0.0322403 0.0296459 0.0271911 0.0248763 0.0227007 0.0206628 0.0187601 0.0169893 0.0153466 0.0138276 0.0124273 0.0111404 0.00996146 0.00888465 0.00790412 0.00701395 0.00620823 0.00548111 0.00482687 0.00423993 0.0037149 0.00324663 0.00283017 0.00246088 0.00213433 0.00184642 0.00159329 0.00137137 +0.00151868 0.00176444 0.00204476 0.0023636 0.00272522 0.00313418 0.00359537 0.00411395 0.00469537 0.00534536 0.00606988 0.0068751 0.00776737 0.00875316 0.00983902 0.0110315 0.0123371 0.0137622 0.0153129 0.0169951 0.0188143 0.0207752 0.0228823 0.0251391 0.0275484 0.0301119 0.0328304 0.0357035 0.0387294 0.041905 0.045226 0.0486862 0.0522781 0.0559924 0.0598184 0.0637435 0.0677538 0.0718335 0.0759655 0.0801312 0.0843107 0.088483 0.092626 0.0967168 0.100732 0.104647 0.108439 0.112082 0.115554 0.118831 0.121891 0.124712 0.127274 0.129559 0.131551 0.133234 0.134595 0.135626 0.136317 0.136664 0.136664 0.136317 0.135626 0.134595 0.133234 0.131551 0.129559 0.127274 0.124712 0.121891 0.118831 0.115554 0.112082 0.108439 0.104647 0.100732 0.0967168 0.092626 0.088483 0.0843107 0.0801312 0.0759655 0.0718335 0.0677538 0.0637435 0.0598184 0.0559924 0.0522781 0.0486862 0.045226 0.041905 0.0387294 0.0357035 0.0328304 0.0301119 0.0275484 0.0251391 0.0228823 0.0207752 0.0188143 0.0169951 0.0153129 0.0137622 0.0123371 0.0110315 0.00983902 0.00875316 0.00776737 0.0068751 0.00606988 0.00534536 0.00469537 0.00411395 0.00359537 0.00313418 0.00272522 0.0023636 0.00204476 0.00176444 0.00151868 +0.00167753 0.00194899 0.00225863 0.00261082 0.00301026 0.00346201 0.00397143 0.00454425 0.00518649 0.00590447 0.00670477 0.00759421 0.00857981 0.00966871 0.0108681 0.0121853 0.0136275 0.0152017 0.0169146 0.0187728 0.0207822 0.0229482 0.0252757 0.0277686 0.0304298 0.0332615 0.0362643 0.0394379 0.0427803 0.0462881 0.0499564 0.0537786 0.0577461 0.061849 0.0660751 0.0704109 0.0748406 0.079347 0.0839112 0.0885126 0.0931293 0.097738 0.102314 0.106833 0.111268 0.115593 0.119781 0.123806 0.127641 0.131261 0.13464 0.137756 0.140587 0.143111 0.14531 0.147169 0.148673 0.149812 0.150575 0.150959 0.150959 0.150575 0.149812 0.148673 0.147169 0.14531 0.143111 0.140587 0.137756 0.13464 0.131261 0.127641 0.123806 0.119781 0.115593 0.111268 0.106833 0.102314 0.097738 0.0931293 0.0885126 0.0839112 0.079347 0.0748406 0.0704109 0.0660751 0.061849 0.0577461 0.0537786 0.0499564 0.0462881 0.0427803 0.0394379 0.0362643 0.0332615 0.0304298 0.0277686 0.0252757 0.0229482 0.0207822 0.0187728 0.0169146 0.0152017 0.0136275 0.0121853 0.0108681 0.00966871 0.00857981 0.00759421 0.00670477 0.00590447 0.00518649 0.00454425 0.00397143 0.00346201 0.00301026 0.00261082 0.00225863 0.00194899 0.00167753 +0.00184827 0.00214736 0.00248852 0.00287656 0.00331665 0.00381438 0.00437565 0.00500677 0.00571438 0.00650543 0.00738719 0.00836716 0.00945308 0.0106528 0.0119743 0.0134256 0.0150145 0.0167489 0.0186362 0.0206835 0.0228974 0.0252839 0.0278483 0.0305949 0.033527 0.0366469 0.0399554 0.043452 0.0471346 0.0509994 0.0550411 0.0592523 0.0636236 0.0681441 0.0728004 0.0775774 0.082458 0.0874231 0.0924518 0.0975216 0.102608 0.107686 0.112728 0.117707 0.122593 0.127358 0.131973 0.136407 0.140632 0.144621 0.148344 0.151777 0.154896 0.157677 0.1601 0.162148 0.163806 0.16506 0.165901 0.166323 0.166323 0.165901 0.16506 0.163806 0.162148 0.1601 0.157677 0.154896 0.151777 0.148344 0.144621 0.140632 0.136407 0.131973 0.127358 0.122593 0.117707 0.112728 0.107686 0.102608 0.0975216 0.0924518 0.0874231 0.082458 0.0775774 0.0728004 0.0681441 0.0636236 0.0592523 0.0550411 0.0509994 0.0471346 0.043452 0.0399554 0.0366469 0.033527 0.0305949 0.0278483 0.0252839 0.0228974 0.0206835 0.0186362 0.0167489 0.0150145 0.0134256 0.0119743 0.0106528 0.00945308 0.00836716 0.00738719 0.00650543 0.00571438 0.00500677 0.00437565 0.00381438 0.00331665 0.00287656 0.00248852 0.00214736 0.00184827 +0.00203121 0.0023599 0.00273482 0.00316126 0.00364492 0.00419191 0.00480873 0.00550232 0.00627996 0.00714931 0.00811834 0.00919531 0.0103887 0.0117072 0.0131595 0.0147544 0.0165006 0.0184066 0.0204807 0.0227306 0.0251637 0.0277864 0.0306046 0.033623 0.0368454 0.0402741 0.04391 0.0477526 0.0517997 0.0560471 0.0604888 0.0651168 0.0699208 0.0748887 0.0800058 0.0852557 0.0906193 0.0960758 0.101602 0.107174 0.112764 0.118344 0.123885 0.129357 0.134727 0.139964 0.145035 0.149908 0.154552 0.158934 0.163027 0.1668 0.170227 0.173283 0.175946 0.178197 0.180018 0.181397 0.182321 0.182785 0.182785 0.182321 0.181397 0.180018 0.178197 0.175946 0.173283 0.170227 0.1668 0.163027 0.158934 0.154552 0.149908 0.145035 0.139964 0.134727 0.129357 0.123885 0.118344 0.112764 0.107174 0.101602 0.0960758 0.0906193 0.0852557 0.0800058 0.0748887 0.0699208 0.0651168 0.0604888 0.0560471 0.0517997 0.0477526 0.04391 0.0402741 0.0368454 0.033623 0.0306046 0.0277864 0.0251637 0.0227306 0.0204807 0.0184066 0.0165006 0.0147544 0.0131595 0.0117072 0.0103887 0.00919531 0.00811834 0.00714931 0.00627996 0.00550232 0.00480873 0.00419191 0.00364492 0.00316126 0.00273482 0.0023599 0.00203121 +0.00222656 0.00258687 0.00299785 0.0034653 0.00399547 0.00459506 0.00527122 0.00603151 0.00688394 0.0078369 0.00889912 0.0100797 0.0113878 0.0128331 0.0144251 0.0161734 0.0180876 0.0201769 0.0224505 0.0249168 0.0275838 0.0304588 0.033548 0.0368568 0.040389 0.0441474 0.0481331 0.0523453 0.0567816 0.0614375 0.0663064 0.0713794 0.0766455 0.0820911 0.0877004 0.0934552 0.0993346 0.105316 0.111374 0.117481 0.123609 0.129726 0.1358 0.141798 0.147684 0.153425 0.158983 0.164325 0.169416 0.17422 0.178706 0.182842 0.186598 0.189949 0.192868 0.195335 0.197332 0.198843 0.199856 0.200365 0.200365 0.199856 0.198843 0.197332 0.195335 0.192868 0.189949 0.186598 0.182842 0.178706 0.17422 0.169416 0.164325 0.158983 0.153425 0.147684 0.141798 0.1358 0.129726 0.123609 0.117481 0.111374 0.105316 0.0993346 0.0934552 0.0877004 0.0820911 0.0766455 0.0713794 0.0663064 0.0614375 0.0567816 0.0523453 0.0481331 0.0441474 0.040389 0.0368568 0.033548 0.0304588 0.0275838 0.0249168 0.0224505 0.0201769 0.0180876 0.0161734 0.0144251 0.0128331 0.0113878 0.0100797 0.00889912 0.0078369 0.00688394 0.00603151 0.00527122 0.00459506 0.00399547 0.0034653 0.00299785 0.00258687 0.00222656 +0.00243448 0.00282844 0.00327779 0.0037889 0.00436858 0.00502416 0.00576346 0.00659475 0.00752679 0.00856873 0.00973015 0.0110209 0.0124513 0.0140315 0.0157722 0.0176837 0.0197766 0.0220611 0.024547 0.0272436 0.0301597 0.0333031 0.0366808 0.0402986 0.0441607 0.0482701 0.0526279 0.0572334 0.062084 0.0671747 0.0724982 0.078045 0.0838029 0.089757 0.0958902 0.102182 0.108611 0.115151 0.121774 0.128452 0.135152 0.14184 0.148482 0.155039 0.161475 0.167752 0.17383 0.179671 0.185236 0.190489 0.195394 0.199916 0.204023 0.207687 0.210879 0.213576 0.215759 0.217411 0.218519 0.219075 0.219075 0.218519 0.217411 0.215759 0.213576 0.210879 0.207687 0.204023 0.199916 0.195394 0.190489 0.185236 0.179671 0.17383 0.167752 0.161475 0.155039 0.148482 0.14184 0.135152 0.128452 0.121774 0.115151 0.108611 0.102182 0.0958902 0.089757 0.0838029 0.078045 0.0724982 0.0671747 0.062084 0.0572334 0.0526279 0.0482701 0.0441607 0.0402986 0.0366808 0.0333031 0.0301597 0.0272436 0.024547 0.0220611 0.0197766 0.0176837 0.0157722 0.0140315 0.0124513 0.0110209 0.00973015 0.00856873 0.00752679 0.00659475 0.00576346 0.00502416 0.00436858 0.0037889 0.00327779 0.00282844 0.00243448 +0.00265504 0.00308468 0.00357475 0.00413217 0.00476437 0.00547934 0.00628561 0.00719222 0.00820869 0.00934504 0.0106117 0.0120194 0.0135793 0.0153027 0.0172011 0.0192858 0.0215684 0.0240598 0.0267709 0.0297118 0.0328921 0.0363203 0.040004 0.0439495 0.0481615 0.0526432 0.0573958 0.0624187 0.0677087 0.0732606 0.0790664 0.0851157 0.0913952 0.0978888 0.104578 0.11144 0.118451 0.125583 0.132807 0.14009 0.147396 0.154691 0.161934 0.169085 0.176105 0.18295 0.189578 0.195948 0.202018 0.207747 0.213096 0.218028 0.222508 0.226503 0.229984 0.232926 0.235307 0.237108 0.238317 0.238923 0.238923 0.238317 0.237108 0.235307 0.232926 0.229984 0.226503 0.222508 0.218028 0.213096 0.207747 0.202018 0.195948 0.189578 0.18295 0.176105 0.169085 0.161934 0.154691 0.147396 0.14009 0.132807 0.125583 0.118451 0.11144 0.104578 0.0978888 0.0913952 0.0851157 0.0790664 0.0732606 0.0677087 0.0624187 0.0573958 0.0526432 0.0481615 0.0439495 0.040004 0.0363203 0.0328921 0.0297118 0.0267709 0.0240598 0.0215684 0.0192858 0.0172011 0.0153027 0.0135793 0.0120194 0.0106117 0.00934504 0.00820869 0.00719222 0.00628561 0.00547934 0.00476437 0.00413217 0.00357475 0.00308468 0.00265504 +0.0028882 0.00335558 0.00388869 0.00449505 0.00518277 0.00596053 0.00683761 0.00782384 0.00892958 0.0101657 0.0115436 0.0130749 0.0147718 0.0166466 0.0187117 0.0209795 0.0234625 0.0261727 0.0291219 0.0323211 0.0357806 0.0395099 0.0435172 0.0478091 0.052391 0.0572663 0.0624363 0.0679002 0.0736548 0.0796942 0.08601 0.0925905 0.0994215 0.106485 0.113762 0.121226 0.128853 0.136612 0.14447 0.152392 0.160341 0.168275 0.176155 0.183934 0.19157 0.199016 0.206227 0.213156 0.219759 0.225991 0.23181 0.237175 0.242048 0.246394 0.250181 0.253381 0.255971 0.257931 0.259245 0.259905 0.259905 0.259245 0.257931 0.255971 0.253381 0.250181 0.246394 0.242048 0.237175 0.23181 0.225991 0.219759 0.213156 0.206227 0.199016 0.19157 0.183934 0.176155 0.168275 0.160341 0.152392 0.14447 0.136612 0.128853 0.121226 0.113762 0.106485 0.0994215 0.0925905 0.08601 0.0796942 0.0736548 0.0679002 0.0624363 0.0572663 0.052391 0.0478091 0.0435172 0.0395099 0.0357806 0.0323211 0.0291219 0.0261727 0.0234625 0.0209795 0.0187117 0.0166466 0.0147718 0.0130749 0.0115436 0.0101657 0.00892958 0.00782384 0.00683761 0.00596053 0.00518277 0.00449505 0.00388869 0.00335558 0.0028882 +0.00313384 0.00364097 0.00421941 0.00487735 0.00562355 0.00646747 0.00741914 0.00848924 0.00968902 0.0110303 0.0125254 0.0141869 0.0160282 0.0180624 0.0203031 0.0227638 0.0254579 0.0283986 0.0315986 0.0350699 0.0388237 0.0428702 0.0472182 0.0518752 0.0568468 0.0621367 0.0677464 0.073675 0.079919 0.0864721 0.093325 0.100465 0.107877 0.115542 0.123437 0.131536 0.139812 0.14823 0.156757 0.165353 0.173977 0.182587 0.191136 0.199578 0.207863 0.215942 0.223766 0.231285 0.238449 0.245211 0.251525 0.257346 0.262634 0.267349 0.271458 0.274931 0.277741 0.279867 0.281294 0.28201 0.28201 0.281294 0.279867 0.277741 0.274931 0.271458 0.267349 0.262634 0.257346 0.251525 0.245211 0.238449 0.231285 0.223766 0.215942 0.207863 0.199578 0.191136 0.182587 0.173977 0.165353 0.156757 0.14823 0.139812 0.131536 0.123437 0.115542 0.107877 0.100465 0.093325 0.0864721 0.079919 0.073675 0.0677464 0.0621367 0.0568468 0.0518752 0.0472182 0.0428702 0.0388237 0.0350699 0.0315986 0.0283986 0.0254579 0.0227638 0.0203031 0.0180624 0.0160282 0.0141869 0.0125254 0.0110303 0.00968902 0.00848924 0.00741914 0.00646747 0.00562355 0.00487735 0.00421941 0.00364097 0.00313384 +0.00339171 0.00394056 0.0045666 0.00527867 0.00608628 0.00699964 0.00802961 0.00918777 0.0104863 0.0119379 0.013556 0.0153543 0.017347 0.0195486 0.0219737 0.0246369 0.0275527 0.0307354 0.0341987 0.0379556 0.0420183 0.0463977 0.0511035 0.0561437 0.0615244 0.0672495 0.0733208 0.0797373 0.0864951 0.0935874 0.101004 0.108732 0.116754 0.125049 0.133594 0.14236 0.151316 0.160427 0.169655 0.178959 0.188293 0.197611 0.206864 0.216 0.224967 0.233711 0.242179 0.250316 0.25807 0.265388 0.272222 0.278522 0.284244 0.289348 0.293795 0.297553 0.300594 0.302896 0.30444 0.305215 0.305215 0.30444 0.302896 0.300594 0.297553 0.293795 0.289348 0.284244 0.278522 0.272222 0.265388 0.25807 0.250316 0.242179 0.233711 0.224967 0.216 0.206864 0.197611 0.188293 0.178959 0.169655 0.160427 0.151316 0.14236 0.133594 0.125049 0.116754 0.108732 0.101004 0.0935874 0.0864951 0.0797373 0.0733208 0.0672495 0.0615244 0.0561437 0.0511035 0.0463977 0.0420183 0.0379556 0.0341987 0.0307354 0.0275527 0.0246369 0.0219737 0.0195486 0.017347 0.0153543 0.013556 0.0119379 0.0104863 0.00918777 0.00802961 0.00699964 0.00608628 0.00527867 0.0045666 0.00394056 0.00339171 +0.00366144 0.00425394 0.00492977 0.00569847 0.0065703 0.00755629 0.00866818 0.00991844 0.0113202 0.0128873 0.0146341 0.0165754 0.0187266 0.0211032 0.0237212 0.0265962 0.0297439 0.0331797 0.0369184 0.0409741 0.0453598 0.0500876 0.0551676 0.0606086 0.0664172 0.0725977 0.0791518 0.0860785 0.0933737 0.10103 0.109037 0.117379 0.126039 0.134994 0.144218 0.153681 0.16335 0.173185 0.183147 0.193191 0.203267 0.213326 0.223315 0.233177 0.242857 0.252297 0.261438 0.270223 0.278593 0.286494 0.293871 0.300672 0.306849 0.312359 0.31716 0.321217 0.3245 0.326984 0.328651 0.329487 0.329487 0.328651 0.326984 0.3245 0.321217 0.31716 0.312359 0.306849 0.300672 0.293871 0.286494 0.278593 0.270223 0.261438 0.252297 0.242857 0.233177 0.223315 0.213326 0.203267 0.193191 0.183147 0.173185 0.16335 0.153681 0.144218 0.134994 0.126039 0.117379 0.109037 0.10103 0.0933737 0.0860785 0.0791518 0.0725977 0.0664172 0.0606086 0.0551676 0.0500876 0.0453598 0.0409741 0.0369184 0.0331797 0.0297439 0.0265962 0.0237212 0.0211032 0.0187266 0.0165754 0.0146341 0.0128873 0.0113202 0.00991844 0.00866818 0.00755629 0.0065703 0.00569847 0.00492977 0.00425394 0.00366144 +0.00394255 0.00458054 0.00530826 0.00613598 0.00707475 0.00813644 0.0093337 0.0106799 0.0121893 0.0138767 0.0157576 0.017848 0.0201643 0.0227235 0.0255424 0.0286381 0.0320275 0.0357271 0.0397529 0.0441199 0.0488424 0.0539331 0.0594032 0.065262 0.0715165 0.0781715 0.0852288 0.0926873 0.100543 0.108787 0.117408 0.126391 0.135716 0.145358 0.15529 0.16548 0.175891 0.186482 0.197209 0.208023 0.218873 0.229705 0.24046 0.25108 0.261503 0.271668 0.281511 0.29097 0.299983 0.30849 0.316433 0.323756 0.330408 0.33634 0.34151 0.345879 0.349414 0.352089 0.353884 0.354784 0.354784 0.353884 0.352089 0.349414 0.345879 0.34151 0.33634 0.330408 0.323756 0.316433 0.30849 0.299983 0.29097 0.281511 0.271668 0.261503 0.25108 0.24046 0.229705 0.218873 0.208023 0.197209 0.186482 0.175891 0.16548 0.15529 0.145358 0.135716 0.126391 0.117408 0.108787 0.100543 0.0926873 0.0852288 0.0781715 0.0715165 0.065262 0.0594032 0.0539331 0.0488424 0.0441199 0.0397529 0.0357271 0.0320275 0.0286381 0.0255424 0.0227235 0.0201643 0.017848 0.0157576 0.0138767 0.0121893 0.0106799 0.0093337 0.00813644 0.00707475 0.00613598 0.00530826 0.00458054 0.00394255 +0.00423443 0.00491966 0.00570125 0.00659025 0.00759852 0.00873881 0.0100247 0.0114706 0.0130918 0.0149041 0.0169242 0.0191693 0.0216572 0.0244058 0.0274334 0.0307583 0.0343986 0.0383721 0.0426959 0.0473863 0.0524584 0.057926 0.063801 0.0700935 0.0768111 0.0839588 0.0915386 0.0995493 0.107986 0.116841 0.1261 0.135748 0.145763 0.156119 0.166787 0.177731 0.188913 0.200288 0.211809 0.223424 0.235077 0.246711 0.258262 0.269668 0.280863 0.29178 0.302352 0.312511 0.322192 0.331329 0.33986 0.347725 0.354869 0.361241 0.366793 0.371485 0.375282 0.378155 0.380083 0.38105 0.38105 0.380083 0.378155 0.375282 0.371485 0.366793 0.361241 0.354869 0.347725 0.33986 0.331329 0.322192 0.312511 0.302352 0.29178 0.280863 0.269668 0.258262 0.246711 0.235077 0.223424 0.211809 0.200288 0.188913 0.177731 0.166787 0.156119 0.145763 0.135748 0.1261 0.116841 0.107986 0.0995493 0.0915386 0.0839588 0.0768111 0.0700935 0.063801 0.057926 0.0524584 0.0473863 0.0426959 0.0383721 0.0343986 0.0307583 0.0274334 0.0244058 0.0216572 0.0191693 0.0169242 0.0149041 0.0130918 0.0114706 0.0100247 0.00873881 0.00759852 0.00659025 0.00570125 0.00491966 0.00423443 +0.00453634 0.00527041 0.00610774 0.00706011 0.00814028 0.00936187 0.0107394 0.0122884 0.0140252 0.0159667 0.0181309 0.0205361 0.0232013 0.0261459 0.0293893 0.0329513 0.0368512 0.0411079 0.04574 0.0507648 0.0561986 0.062056 0.0683499 0.075091 0.0822876 0.0899449 0.0980651 0.106647 0.115685 0.125171 0.135091 0.145427 0.156156 0.16725 0.178679 0.190403 0.202382 0.214568 0.22691 0.239353 0.251838 0.264301 0.276676 0.288895 0.300888 0.312583 0.323909 0.334792 0.345163 0.354952 0.364091 0.372517 0.380171 0.386997 0.392945 0.397971 0.402039 0.405117 0.407182 0.408218 0.408218 0.407182 0.405117 0.402039 0.397971 0.392945 0.386997 0.380171 0.372517 0.364091 0.354952 0.345163 0.334792 0.323909 0.312583 0.300888 0.288895 0.276676 0.264301 0.251838 0.239353 0.22691 0.214568 0.202382 0.190403 0.178679 0.16725 0.156156 0.145427 0.135091 0.125171 0.115685 0.106647 0.0980651 0.0899449 0.0822876 0.075091 0.0683499 0.062056 0.0561986 0.0507648 0.04574 0.0411079 0.0368512 0.0329513 0.0293893 0.0261459 0.0232013 0.0205361 0.0181309 0.0159667 0.0140252 0.0122884 0.0107394 0.00936187 0.00814028 0.00706011 0.00610774 0.00527041 0.00453634 +0.00484739 0.0056318 0.00652653 0.00754421 0.00869844 0.0100038 0.0114758 0.013131 0.0149869 0.0170615 0.0193741 0.0219442 0.0247922 0.0279386 0.0314045 0.0352107 0.039378 0.0439266 0.0488763 0.0542457 0.060052 0.066311 0.0730365 0.0802399 0.0879299 0.0961122 0.104789 0.11396 0.123618 0.133754 0.144354 0.155398 0.166863 0.178718 0.19093 0.203459 0.216259 0.229281 0.242469 0.255766 0.269106 0.282423 0.295647 0.308704 0.32152 0.334017 0.346119 0.357749 0.36883 0.37929 0.389056 0.39806 0.406238 0.413532 0.419888 0.42526 0.429606 0.432895 0.435102 0.436209 0.436209 0.435102 0.432895 0.429606 0.42526 0.419888 0.413532 0.406238 0.39806 0.389056 0.37929 0.36883 0.357749 0.346119 0.334017 0.32152 0.308704 0.295647 0.282423 0.269106 0.255766 0.242469 0.229281 0.216259 0.203459 0.19093 0.178718 0.166863 0.155398 0.144354 0.133754 0.123618 0.11396 0.104789 0.0961122 0.0879299 0.0802399 0.0730365 0.066311 0.060052 0.0542457 0.0488763 0.0439266 0.039378 0.0352107 0.0314045 0.0279386 0.0247922 0.0219442 0.0193741 0.0170615 0.0149869 0.013131 0.0114758 0.0100038 0.00869844 0.00754421 0.00652653 0.0056318 0.00484739 +0.00516657 0.00600263 0.00695628 0.00804097 0.0092712 0.0106625 0.0122315 0.0139957 0.0159737 0.0181849 0.0206498 0.0233891 0.0264246 0.0297783 0.0334724 0.0375292 0.0419709 0.046819 0.0520946 0.0578175 0.0640062 0.0706774 0.0778457 0.0855234 0.0937197 0.102441 0.111689 0.121463 0.131757 0.142561 0.153859 0.165631 0.17785 0.190486 0.203502 0.216856 0.230499 0.244378 0.258435 0.272607 0.286825 0.30102 0.315114 0.329031 0.34269 0.356011 0.368909 0.381305 0.393116 0.404265 0.414674 0.424271 0.432988 0.440762 0.447536 0.453261 0.457894 0.461399 0.463751 0.464932 0.464932 0.463751 0.461399 0.457894 0.453261 0.447536 0.440762 0.432988 0.424271 0.414674 0.404265 0.393116 0.381305 0.368909 0.356011 0.34269 0.329031 0.315114 0.30102 0.286825 0.272607 0.258435 0.244378 0.230499 0.216856 0.203502 0.190486 0.17785 0.165631 0.153859 0.142561 0.131757 0.121463 0.111689 0.102441 0.0937197 0.0855234 0.0778457 0.0706774 0.0640062 0.0578175 0.0520946 0.046819 0.0419709 0.0375292 0.0334724 0.0297783 0.0264246 0.0233891 0.0206498 0.0181849 0.0159737 0.0139957 0.0122315 0.0106625 0.0092712 0.00804097 0.00695628 0.00600263 0.00516657 +0.00549274 0.00638158 0.00739544 0.00854861 0.0098565 0.0113356 0.0130037 0.0148792 0.0169821 0.019333 0.0219534 0.0248657 0.0280928 0.0316582 0.0355855 0.0398984 0.0446205 0.0497748 0.0553834 0.0614676 0.068047 0.0751393 0.0827602 0.0909226 0.0996363 0.108908 0.11874 0.129131 0.140075 0.151561 0.163572 0.176087 0.189078 0.202512 0.21635 0.230546 0.24505 0.259806 0.27475 0.289817 0.304933 0.320023 0.335008 0.349803 0.364325 0.378486 0.392199 0.405377 0.417934 0.429786 0.440852 0.451055 0.460323 0.468587 0.47579 0.481876 0.486801 0.490528 0.493028 0.494283 0.494283 0.493028 0.490528 0.486801 0.481876 0.47579 0.468587 0.460323 0.451055 0.440852 0.429786 0.417934 0.405377 0.392199 0.378486 0.364325 0.349803 0.335008 0.320023 0.304933 0.289817 0.27475 0.259806 0.24505 0.230546 0.21635 0.202512 0.189078 0.176087 0.163572 0.151561 0.140075 0.129131 0.11874 0.108908 0.0996363 0.0909226 0.0827602 0.0751393 0.068047 0.0614676 0.0553834 0.0497748 0.0446205 0.0398984 0.0355855 0.0316582 0.0280928 0.0248657 0.0219534 0.019333 0.0169821 0.0148792 0.0130037 0.0113356 0.0098565 0.00854861 0.00739544 0.00638158 0.00549274 +0.00582462 0.00676717 0.00784229 0.00906514 0.0104521 0.0120206 0.0137894 0.0157783 0.0180082 0.0205011 0.0232799 0.0263682 0.0297903 0.0335711 0.0377357 0.0423092 0.0473166 0.0527823 0.0587298 0.0651816 0.0721585 0.0796794 0.0877608 0.0964163 0.105657 0.115489 0.125915 0.136934 0.148539 0.160719 0.173456 0.186727 0.200503 0.214748 0.229422 0.244476 0.259857 0.275504 0.291351 0.307328 0.323358 0.33936 0.35525 0.370939 0.386338 0.401355 0.415897 0.429871 0.443187 0.455755 0.46749 0.478309 0.488136 0.496901 0.504538 0.510992 0.516215 0.520167 0.522818 0.524149 0.524149 0.522818 0.520167 0.516215 0.510992 0.504538 0.496901 0.488136 0.478309 0.46749 0.455755 0.443187 0.429871 0.415897 0.401355 0.386338 0.370939 0.35525 0.33936 0.323358 0.307328 0.291351 0.275504 0.259857 0.244476 0.229422 0.214748 0.200503 0.186727 0.173456 0.160719 0.148539 0.136934 0.125915 0.115489 0.105657 0.0964163 0.0877608 0.0796794 0.0721585 0.0651816 0.0587298 0.0527823 0.0473166 0.0423092 0.0377357 0.0335711 0.0297903 0.0263682 0.0232799 0.0205011 0.0180082 0.0157783 0.0137894 0.0120206 0.0104521 0.00906514 0.00784229 0.00676717 0.00582462 +0.00616083 0.00715778 0.00829495 0.00958839 0.0110554 0.0127144 0.0145853 0.016689 0.0190477 0.0216845 0.0246236 0.0278902 0.0315098 0.0355089 0.0399138 0.0447514 0.0500478 0.0558289 0.0621198 0.068944 0.0763236 0.0842786 0.0928264 0.101982 0.111755 0.122155 0.133183 0.144838 0.157113 0.169996 0.183468 0.197505 0.212076 0.227144 0.242665 0.258588 0.274856 0.291406 0.308168 0.325067 0.342022 0.358948 0.375755 0.39235 0.408638 0.424522 0.439903 0.454684 0.468768 0.482062 0.494474 0.505918 0.516312 0.525582 0.533661 0.540487 0.546011 0.550192 0.552996 0.554404 0.554404 0.552996 0.550192 0.546011 0.540487 0.533661 0.525582 0.516312 0.505918 0.494474 0.482062 0.468768 0.454684 0.439903 0.424522 0.408638 0.39235 0.375755 0.358948 0.342022 0.325067 0.308168 0.291406 0.274856 0.258588 0.242665 0.227144 0.212076 0.197505 0.183468 0.169996 0.157113 0.144838 0.133183 0.122155 0.111755 0.101982 0.0928264 0.0842786 0.0763236 0.068944 0.0621198 0.0558289 0.0500478 0.0447514 0.0399138 0.0355089 0.0315098 0.0278902 0.0246236 0.0216845 0.0190477 0.016689 0.0145853 0.0127144 0.0110554 0.00958839 0.00829495 0.00715778 0.00616083 +0.00649984 0.00755165 0.0087514 0.010116 0.0116637 0.013414 0.0153879 0.0176074 0.0200958 0.0228777 0.0259786 0.0294249 0.0332437 0.0374628 0.0421102 0.0472139 0.0528017 0.058901 0.065538 0.0727377 0.0805234 0.0889161 0.0979343 0.107593 0.117905 0.128876 0.140511 0.152808 0.165758 0.17935 0.193563 0.208373 0.223746 0.239643 0.256018 0.272817 0.28998 0.307441 0.325126 0.342955 0.360843 0.3787 0.396432 0.41394 0.431124 0.447882 0.464109 0.479703 0.494563 0.508588 0.521683 0.533757 0.544723 0.554503 0.563026 0.570228 0.576057 0.580467 0.583426 0.584911 0.584911 0.583426 0.580467 0.576057 0.570228 0.563026 0.554503 0.544723 0.533757 0.521683 0.508588 0.494563 0.479703 0.464109 0.447882 0.431124 0.41394 0.396432 0.3787 0.360843 0.342955 0.325126 0.307441 0.28998 0.272817 0.256018 0.239643 0.223746 0.208373 0.193563 0.17935 0.165758 0.152808 0.140511 0.128876 0.117905 0.107593 0.0979343 0.0889161 0.0805234 0.0727377 0.065538 0.058901 0.0528017 0.0472139 0.0421102 0.0374628 0.0332437 0.0294249 0.0259786 0.0228777 0.0200958 0.0176074 0.0153879 0.013414 0.0116637 0.010116 0.0087514 0.00755165 0.00649984 +0.00684003 0.0079469 0.00920944 0.0106455 0.0122742 0.0141161 0.0161933 0.0185289 0.0211476 0.0240751 0.0273383 0.0309649 0.0349836 0.0394235 0.0443142 0.049685 0.0555653 0.0619838 0.0689682 0.0765448 0.0847379 0.0935699 0.10306 0.113225 0.124076 0.135622 0.147866 0.160806 0.174434 0.188737 0.203694 0.219279 0.235456 0.252185 0.269417 0.287096 0.305158 0.323532 0.342143 0.360905 0.379729 0.398521 0.41718 0.435605 0.453689 0.471323 0.4884 0.504811 0.520448 0.535207 0.548987 0.561693 0.573233 0.583526 0.592494 0.600074 0.606207 0.610848 0.613961 0.615524 0.615524 0.613961 0.610848 0.606207 0.600074 0.592494 0.583526 0.573233 0.561693 0.548987 0.535207 0.520448 0.504811 0.4884 0.471323 0.453689 0.435605 0.41718 0.398521 0.379729 0.360905 0.342143 0.323532 0.305158 0.287096 0.269417 0.252185 0.235456 0.219279 0.203694 0.188737 0.174434 0.160806 0.147866 0.135622 0.124076 0.113225 0.10306 0.0935699 0.0847379 0.0765448 0.0689682 0.0619838 0.0555653 0.049685 0.0443142 0.0394235 0.0349836 0.0309649 0.0273383 0.0240751 0.0211476 0.0185289 0.0161933 0.0141161 0.0122742 0.0106455 0.00920944 0.0079469 0.00684003 +0.00717969 0.00834152 0.00966676 0.0111741 0.0128837 0.0148171 0.0169974 0.019449 0.0221977 0.0252706 0.0286958 0.0325026 0.0367208 0.0413812 0.0465147 0.0521522 0.0583246 0.0650618 0.072393 0.0803458 0.0889459 0.0982164 0.108178 0.118847 0.130237 0.142356 0.155208 0.168791 0.183096 0.198109 0.213809 0.230168 0.247149 0.264708 0.282796 0.301352 0.320311 0.339598 0.359133 0.378826 0.398585 0.41831 0.437897 0.457236 0.476218 0.494728 0.512653 0.529878 0.546292 0.561784 0.576249 0.589585 0.601699 0.612502 0.621916 0.629872 0.63631 0.641181 0.64445 0.64609 0.64609 0.64445 0.641181 0.63631 0.629872 0.621916 0.612502 0.601699 0.589585 0.576249 0.561784 0.546292 0.529878 0.512653 0.494728 0.476218 0.457236 0.437897 0.41831 0.398585 0.378826 0.359133 0.339598 0.320311 0.301352 0.282796 0.264708 0.247149 0.230168 0.213809 0.198109 0.183096 0.168791 0.155208 0.142356 0.130237 0.118847 0.108178 0.0982164 0.0889459 0.0803458 0.072393 0.0650618 0.0583246 0.0521522 0.0465147 0.0413812 0.0367208 0.0325026 0.0286958 0.0252706 0.0221977 0.019449 0.0169974 0.0148171 0.0128837 0.0111741 0.00966676 0.00834152 0.00717969 +0.00751702 0.00873344 0.0101209 0.0116991 0.013489 0.0155133 0.017796 0.0203628 0.0232407 0.0264579 0.0300441 0.0340297 0.0384461 0.0433255 0.0487002 0.0546026 0.0610649 0.0681187 0.0757944 0.0841208 0.0931249 0.102831 0.11326 0.124431 0.136356 0.149045 0.162501 0.176721 0.191699 0.207417 0.223855 0.240982 0.258761 0.277145 0.296083 0.315511 0.335361 0.355554 0.376006 0.396625 0.417313 0.437964 0.458471 0.478719 0.498593 0.517972 0.536739 0.554774 0.571959 0.588179 0.603324 0.617287 0.629969 0.64128 0.651137 0.659466 0.666206 0.671306 0.674728 0.676446 0.676446 0.674728 0.671306 0.666206 0.659466 0.651137 0.64128 0.629969 0.617287 0.603324 0.588179 0.571959 0.554774 0.536739 0.517972 0.498593 0.478719 0.458471 0.437964 0.417313 0.396625 0.376006 0.355554 0.335361 0.315511 0.296083 0.277145 0.258761 0.240982 0.223855 0.207417 0.191699 0.176721 0.162501 0.149045 0.136356 0.124431 0.11326 0.102831 0.0931249 0.0841208 0.0757944 0.0681187 0.0610649 0.0546026 0.0487002 0.0433255 0.0384461 0.0340297 0.0300441 0.0264579 0.0232407 0.0203628 0.017796 0.0155133 0.013489 0.0116991 0.0101209 0.00873344 0.00751702 +0.00785015 0.00912048 0.0105695 0.0122176 0.0140868 0.0162008 0.0185847 0.0212652 0.0242706 0.0276305 0.0313755 0.0355378 0.0401499 0.0452455 0.0508584 0.0570224 0.0637711 0.0711375 0.0791533 0.0878488 0.0972519 0.107388 0.11828 0.129945 0.142399 0.15565 0.169702 0.184553 0.200194 0.216609 0.233775 0.251661 0.270228 0.289428 0.309204 0.329494 0.350223 0.371311 0.39267 0.414202 0.435807 0.457373 0.478789 0.499934 0.520689 0.540927 0.560526 0.57936 0.597307 0.614245 0.630061 0.644643 0.657887 0.6697 0.679993 0.688691 0.69573 0.701057 0.70463 0.706424 0.706424 0.70463 0.701057 0.69573 0.688691 0.679993 0.6697 0.657887 0.644643 0.630061 0.614245 0.597307 0.57936 0.560526 0.540927 0.520689 0.499934 0.478789 0.457373 0.435807 0.414202 0.39267 0.371311 0.350223 0.329494 0.309204 0.289428 0.270228 0.251661 0.233775 0.216609 0.200194 0.184553 0.169702 0.15565 0.142399 0.129945 0.11828 0.107388 0.0972519 0.0878488 0.0791533 0.0711375 0.0637711 0.0570224 0.0508584 0.0452455 0.0401499 0.0355378 0.0313755 0.0276305 0.0242706 0.0212652 0.0185847 0.0162008 0.0140868 0.0122176 0.0105695 0.00912048 0.00785015 +0.00817716 0.00950041 0.0110098 0.0127265 0.0146736 0.0168756 0.0193588 0.0221511 0.0252817 0.0287814 0.0326825 0.0370181 0.0418225 0.0471303 0.052977 0.0593977 0.0664276 0.0741008 0.0824506 0.0915082 0.101303 0.111862 0.123207 0.135358 0.148331 0.162134 0.176771 0.192241 0.208533 0.225632 0.243514 0.262145 0.281485 0.301484 0.322085 0.343219 0.364812 0.386779 0.409027 0.431457 0.453961 0.476426 0.498733 0.52076 0.542379 0.56346 0.583876 0.603494 0.622188 0.639833 0.656307 0.671496 0.685293 0.697597 0.708319 0.71738 0.724712 0.73026 0.733983 0.735851 0.735851 0.733983 0.73026 0.724712 0.71738 0.708319 0.697597 0.685293 0.671496 0.656307 0.639833 0.622188 0.603494 0.583876 0.56346 0.542379 0.52076 0.498733 0.476426 0.453961 0.431457 0.409027 0.386779 0.364812 0.343219 0.322085 0.301484 0.281485 0.262145 0.243514 0.225632 0.208533 0.192241 0.176771 0.162134 0.148331 0.135358 0.123207 0.111862 0.101303 0.0915082 0.0824506 0.0741008 0.0664276 0.0593977 0.052977 0.0471303 0.0418225 0.0370181 0.0326825 0.0287814 0.0252817 0.0221511 0.0193588 0.0168756 0.0146736 0.0127265 0.0110098 0.00950041 0.00817716 +0.00849609 0.00987095 0.0114392 0.0132229 0.0152459 0.0175338 0.0201139 0.023015 0.0262677 0.029904 0.0339572 0.038462 0.0434536 0.0489685 0.0550432 0.0617144 0.0690185 0.076991 0.0856664 0.0950773 0.105254 0.116224 0.128012 0.140638 0.154116 0.168458 0.183666 0.199739 0.216667 0.234433 0.253011 0.272369 0.292463 0.313243 0.334647 0.356606 0.379041 0.401864 0.42498 0.448285 0.471666 0.495008 0.518185 0.541071 0.563533 0.585437 0.606648 0.627032 0.646455 0.664788 0.681905 0.697687 0.712021 0.724805 0.735945 0.74536 0.752978 0.758742 0.76261 0.764551 0.764551 0.76261 0.758742 0.752978 0.74536 0.735945 0.724805 0.712021 0.697687 0.681905 0.664788 0.646455 0.627032 0.606648 0.585437 0.563533 0.541071 0.518185 0.495008 0.471666 0.448285 0.42498 0.401864 0.379041 0.356606 0.334647 0.313243 0.292463 0.272369 0.253011 0.234433 0.216667 0.199739 0.183666 0.168458 0.154116 0.140638 0.128012 0.116224 0.105254 0.0950773 0.0856664 0.076991 0.0690185 0.0617144 0.0550432 0.0489685 0.0434536 0.038462 0.0339572 0.029904 0.0262677 0.023015 0.0201139 0.0175338 0.0152459 0.0132229 0.0114392 0.00987095 0.00849609 +0.00880498 0.0102298 0.011855 0.0137036 0.0158002 0.0181713 0.0208451 0.0238517 0.0272227 0.0309912 0.0351918 0.0398603 0.0450334 0.0507488 0.0570444 0.0639581 0.0715277 0.0797901 0.0887809 0.0985339 0.109081 0.12045 0.132666 0.145751 0.159719 0.174582 0.190343 0.207001 0.224544 0.242956 0.26221 0.282271 0.303096 0.324631 0.346813 0.36957 0.392821 0.416474 0.440431 0.464582 0.488814 0.513004 0.537025 0.560742 0.584021 0.606721 0.628704 0.649828 0.669958 0.688957 0.706696 0.723052 0.737907 0.751156 0.762701 0.772458 0.780353 0.786327 0.790335 0.792347 0.792347 0.790335 0.786327 0.780353 0.772458 0.762701 0.751156 0.737907 0.723052 0.706696 0.688957 0.669958 0.649828 0.628704 0.606721 0.584021 0.560742 0.537025 0.513004 0.488814 0.464582 0.440431 0.416474 0.392821 0.36957 0.346813 0.324631 0.303096 0.282271 0.26221 0.242956 0.224544 0.207001 0.190343 0.174582 0.159719 0.145751 0.132666 0.12045 0.109081 0.0985339 0.0887809 0.0797901 0.0715277 0.0639581 0.0570444 0.0507488 0.0450334 0.0398603 0.0351918 0.0309912 0.0272227 0.0238517 0.0208451 0.0181713 0.0158002 0.0137036 0.011855 0.0102298 0.00880498 +0.00910185 0.0105747 0.0122548 0.0141656 0.0163329 0.0187839 0.021548 0.0246559 0.0281405 0.0320361 0.0363783 0.0412042 0.0465518 0.0524599 0.0589677 0.0661145 0.0739393 0.0824802 0.0917742 0.101856 0.112759 0.124511 0.137139 0.150665 0.165104 0.180468 0.196761 0.21398 0.232115 0.251147 0.271051 0.291788 0.313315 0.335576 0.358506 0.382031 0.406065 0.430516 0.45528 0.480246 0.505295 0.530301 0.555131 0.579648 0.603711 0.627177 0.649901 0.671738 0.692546 0.712186 0.730523 0.74743 0.762786 0.776482 0.788416 0.798502 0.806663 0.812839 0.816982 0.819062 0.819062 0.816982 0.812839 0.806663 0.798502 0.788416 0.776482 0.762786 0.74743 0.730523 0.712186 0.692546 0.671738 0.649901 0.627177 0.603711 0.579648 0.555131 0.530301 0.505295 0.480246 0.45528 0.430516 0.406065 0.382031 0.358506 0.335576 0.313315 0.291788 0.271051 0.251147 0.232115 0.21398 0.196761 0.180468 0.165104 0.150665 0.137139 0.124511 0.112759 0.101856 0.0917742 0.0824802 0.0739393 0.0661145 0.0589677 0.0524599 0.0465518 0.0412042 0.0363783 0.0320361 0.0281405 0.0246559 0.021548 0.0187839 0.0163329 0.0141656 0.0122548 0.0105747 0.00910185 +0.00938475 0.0109034 0.0126357 0.0146059 0.0168406 0.0193678 0.0222177 0.0254223 0.0290152 0.0330318 0.037509 0.0424849 0.0479987 0.0540904 0.0608005 0.0681695 0.0762375 0.0850439 0.0946267 0.105022 0.116263 0.128381 0.141402 0.155348 0.170236 0.186078 0.202877 0.220631 0.239329 0.258954 0.279475 0.300858 0.323054 0.346007 0.36965 0.393905 0.418687 0.443897 0.469431 0.495173 0.521001 0.546784 0.572386 0.597665 0.622476 0.646671 0.670101 0.692617 0.714072 0.734322 0.753229 0.770662 0.786496 0.800617 0.812922 0.823321 0.831736 0.838104 0.842376 0.84452 0.84452 0.842376 0.838104 0.831736 0.823321 0.812922 0.800617 0.786496 0.770662 0.753229 0.734322 0.714072 0.692617 0.670101 0.646671 0.622476 0.597665 0.572386 0.546784 0.521001 0.495173 0.469431 0.443897 0.418687 0.393905 0.36965 0.346007 0.323054 0.300858 0.279475 0.258954 0.239329 0.220631 0.202877 0.186078 0.170236 0.155348 0.141402 0.128381 0.116263 0.105022 0.0946267 0.0850439 0.0762375 0.0681695 0.0608005 0.0540904 0.0479987 0.0424849 0.037509 0.0330318 0.0290152 0.0254223 0.0222177 0.0193678 0.0168406 0.0146059 0.0126357 0.0109034 0.00938475 +0.0096518 0.0112137 0.0129952 0.0150216 0.0173198 0.0199189 0.0228499 0.0261457 0.0298409 0.0339718 0.0385764 0.0436939 0.0493646 0.0556296 0.0625306 0.0701093 0.0784069 0.0874639 0.0973194 0.10801 0.119572 0.132034 0.145426 0.159769 0.17508 0.191373 0.20865 0.226909 0.24614 0.266322 0.287428 0.309419 0.332247 0.355853 0.380168 0.405114 0.430601 0.456529 0.482789 0.509264 0.535826 0.562343 0.588673 0.614672 0.640189 0.665073 0.68917 0.712326 0.734391 0.755218 0.774663 0.792591 0.808876 0.823399 0.836055 0.84675 0.855404 0.861953 0.866346 0.868551 0.868551 0.866346 0.861953 0.855404 0.84675 0.836055 0.823399 0.808876 0.792591 0.774663 0.755218 0.734391 0.712326 0.68917 0.665073 0.640189 0.614672 0.588673 0.562343 0.535826 0.509264 0.482789 0.456529 0.430601 0.405114 0.380168 0.355853 0.332247 0.309419 0.287428 0.266322 0.24614 0.226909 0.20865 0.191373 0.17508 0.159769 0.145426 0.132034 0.119572 0.10801 0.0973194 0.0874639 0.0784069 0.0701093 0.0625306 0.0556296 0.0493646 0.0436939 0.0385764 0.0339718 0.0298409 0.0261457 0.0228499 0.0199189 0.0173198 0.0150216 0.0129952 0.0112137 0.0096518 +0.00990116 0.0115034 0.013331 0.0154096 0.0177672 0.0204335 0.0234403 0.0268212 0.0306118 0.0348495 0.039573 0.0448227 0.0506399 0.0570669 0.0641462 0.0719206 0.0804326 0.0897236 0.0998337 0.110801 0.122661 0.135445 0.149183 0.163896 0.179604 0.196317 0.21404 0.232771 0.252499 0.273203 0.294854 0.317413 0.340831 0.365046 0.38999 0.415581 0.441726 0.468324 0.495262 0.522421 0.54967 0.576871 0.603882 0.630552 0.656729 0.682255 0.706975 0.73073 0.753365 0.774729 0.794677 0.813069 0.829774 0.844672 0.857655 0.868626 0.877504 0.884222 0.888729 0.890991 0.890991 0.888729 0.884222 0.877504 0.868626 0.857655 0.844672 0.829774 0.813069 0.794677 0.774729 0.753365 0.73073 0.706975 0.682255 0.656729 0.630552 0.603882 0.576871 0.54967 0.522421 0.495262 0.468324 0.441726 0.415581 0.38999 0.365046 0.340831 0.317413 0.294854 0.273203 0.252499 0.232771 0.21404 0.196317 0.179604 0.163896 0.149183 0.135445 0.122661 0.110801 0.0998337 0.0897236 0.0804326 0.0719206 0.0641462 0.0570669 0.0506399 0.0448227 0.039573 0.0348495 0.0306118 0.0268212 0.0234403 0.0204335 0.0177672 0.0154096 0.013331 0.0115034 0.00990116 +0.0101311 0.0117705 0.0136405 0.0157675 0.0181798 0.020908 0.0239846 0.027444 0.0313227 0.0356588 0.040492 0.0458636 0.0518159 0.0583921 0.0656358 0.0735908 0.0823004 0.0918072 0.102152 0.113374 0.125509 0.138591 0.152647 0.167702 0.183775 0.200876 0.219011 0.238177 0.258362 0.279547 0.301701 0.324784 0.348745 0.373524 0.399047 0.425231 0.451984 0.479199 0.506764 0.534553 0.562434 0.590268 0.617906 0.645195 0.67198 0.698099 0.723392 0.747699 0.77086 0.79272 0.813131 0.83195 0.849043 0.864287 0.877571 0.888797 0.897882 0.904755 0.909367 0.911682 0.911682 0.909367 0.904755 0.897882 0.888797 0.877571 0.864287 0.849043 0.83195 0.813131 0.79272 0.77086 0.747699 0.723392 0.698099 0.67198 0.645195 0.617906 0.590268 0.562434 0.534553 0.506764 0.479199 0.451984 0.425231 0.399047 0.373524 0.348745 0.324784 0.301701 0.279547 0.258362 0.238177 0.219011 0.200876 0.183775 0.167702 0.152647 0.138591 0.125509 0.113374 0.102152 0.0918072 0.0823004 0.0735908 0.0656358 0.0583921 0.0518159 0.0458636 0.040492 0.0356588 0.0313227 0.027444 0.0239846 0.020908 0.0181798 0.0157675 0.0136405 0.0117705 0.0101311 +0.01034 0.0120132 0.0139217 0.0160926 0.0185546 0.0213391 0.0244791 0.0280098 0.0319684 0.0363939 0.0413268 0.0468091 0.0528841 0.0595959 0.0669889 0.0751079 0.0839971 0.0936999 0.104258 0.115711 0.128097 0.141448 0.155794 0.17116 0.187563 0.205017 0.223526 0.243087 0.263689 0.28531 0.307921 0.33148 0.355935 0.381224 0.407273 0.433998 0.461302 0.489078 0.517211 0.545573 0.574029 0.602437 0.630644 0.658496 0.685833 0.712491 0.738306 0.763113 0.786752 0.809063 0.829895 0.849101 0.866547 0.882105 0.895663 0.907121 0.916392 0.923408 0.928115 0.930477 0.930477 0.928115 0.923408 0.916392 0.907121 0.895663 0.882105 0.866547 0.849101 0.829895 0.809063 0.786752 0.763113 0.738306 0.712491 0.685833 0.658496 0.630644 0.602437 0.574029 0.545573 0.517211 0.489078 0.461302 0.433998 0.407273 0.381224 0.355935 0.33148 0.307921 0.28531 0.263689 0.243087 0.223526 0.205017 0.187563 0.17116 0.155794 0.141448 0.128097 0.115711 0.104258 0.0936999 0.0839971 0.0751079 0.0669889 0.0595959 0.0528841 0.0468091 0.0413268 0.0363939 0.0319684 0.0280098 0.0244791 0.0213391 0.0185546 0.0160926 0.0139217 0.0120132 0.01034 +0.0105262 0.0122296 0.0141726 0.0163825 0.0188889 0.0217235 0.0249201 0.0285144 0.0325444 0.0370495 0.0420713 0.0476524 0.0538369 0.0606695 0.0681958 0.076461 0.0855104 0.0953879 0.106136 0.117796 0.130405 0.143996 0.158601 0.174243 0.190942 0.20871 0.227553 0.247466 0.268439 0.29045 0.313468 0.337452 0.362347 0.388092 0.414611 0.441817 0.469612 0.497889 0.526529 0.555402 0.584371 0.61329 0.642006 0.67036 0.698189 0.725327 0.751607 0.776861 0.800926 0.823639 0.844846 0.864398 0.882158 0.897997 0.911799 0.923463 0.932901 0.940043 0.944835 0.94724 0.94724 0.944835 0.940043 0.932901 0.923463 0.911799 0.897997 0.882158 0.864398 0.844846 0.823639 0.800926 0.776861 0.751607 0.725327 0.698189 0.67036 0.642006 0.61329 0.584371 0.555402 0.526529 0.497889 0.469612 0.441817 0.414611 0.388092 0.362347 0.337452 0.313468 0.29045 0.268439 0.247466 0.227553 0.20871 0.190942 0.174243 0.158601 0.143996 0.130405 0.117796 0.106136 0.0953879 0.0855104 0.076461 0.0681958 0.0606695 0.0538369 0.0476524 0.0420713 0.0370495 0.0325444 0.0285144 0.0249201 0.0217235 0.0188889 0.0163825 0.0141726 0.0122296 0.0105262 +0.0106886 0.0124182 0.0143911 0.0166351 0.0191802 0.0220585 0.0253044 0.0289542 0.0330463 0.0376209 0.0427201 0.0483873 0.0546672 0.0616052 0.0692475 0.0776402 0.0868291 0.096859 0.107773 0.119613 0.132416 0.146217 0.161047 0.17693 0.193887 0.211929 0.231062 0.251283 0.272579 0.29493 0.318303 0.342656 0.367936 0.394077 0.421005 0.44863 0.476855 0.505568 0.534649 0.563968 0.593383 0.622748 0.651907 0.680698 0.708956 0.736513 0.763198 0.788842 0.813278 0.836341 0.857875 0.877729 0.895763 0.911846 0.925861 0.937705 0.947289 0.954541 0.959407 0.961849 0.961849 0.959407 0.954541 0.947289 0.937705 0.925861 0.911846 0.895763 0.877729 0.857875 0.836341 0.813278 0.788842 0.763198 0.736513 0.708956 0.680698 0.651907 0.622748 0.593383 0.563968 0.534649 0.505568 0.476855 0.44863 0.421005 0.394077 0.367936 0.342656 0.318303 0.29493 0.272579 0.251283 0.231062 0.211929 0.193887 0.17693 0.161047 0.146217 0.132416 0.119613 0.107773 0.096859 0.0868291 0.0776402 0.0692475 0.0616052 0.0546672 0.0483873 0.0427201 0.0376209 0.0330463 0.0289542 0.0253044 0.0220585 0.0191802 0.0166351 0.0143911 0.0124182 0.0106886 +0.0108258 0.0125776 0.0145758 0.0168486 0.0194264 0.0223417 0.0256292 0.0293258 0.0334704 0.0381038 0.0432685 0.0490084 0.0553688 0.0623959 0.0701363 0.0786368 0.0879436 0.0981023 0.109156 0.121148 0.134115 0.148094 0.163114 0.179201 0.196376 0.214649 0.234028 0.254508 0.276078 0.298715 0.322388 0.347054 0.372658 0.399136 0.426409 0.454389 0.482975 0.512057 0.541512 0.571206 0.601 0.630741 0.660274 0.689435 0.718056 0.745967 0.772994 0.798967 0.823716 0.847076 0.868886 0.888995 0.907261 0.92355 0.937745 0.949741 0.959448 0.966793 0.971721 0.974195 0.974195 0.971721 0.966793 0.959448 0.949741 0.937745 0.92355 0.907261 0.888995 0.868886 0.847076 0.823716 0.798967 0.772994 0.745967 0.718056 0.689435 0.660274 0.630741 0.601 0.571206 0.541512 0.512057 0.482975 0.454389 0.426409 0.399136 0.372658 0.347054 0.322388 0.298715 0.276078 0.254508 0.234028 0.214649 0.196376 0.179201 0.163114 0.148094 0.134115 0.121148 0.109156 0.0981023 0.0879436 0.0786368 0.0701363 0.0623959 0.0553688 0.0490084 0.0432685 0.0381038 0.0334704 0.0293258 0.0256292 0.0223417 0.0194264 0.0168486 0.0145758 0.0125776 0.0108258 +0.0109368 0.0127066 0.0147253 0.0170214 0.0196256 0.0225708 0.025892 0.0296266 0.0338137 0.0384946 0.0437122 0.049511 0.0559367 0.0630358 0.0708556 0.0794432 0.0888455 0.0991083 0.110276 0.12239 0.135491 0.149613 0.164787 0.181039 0.19839 0.216851 0.236428 0.257118 0.278909 0.301779 0.325695 0.350613 0.37648 0.403229 0.430782 0.459049 0.487928 0.517308 0.547065 0.577064 0.607163 0.63721 0.667046 0.696506 0.72542 0.753617 0.780921 0.807161 0.832164 0.855763 0.877797 0.898112 0.916565 0.933021 0.947362 0.959481 0.969287 0.976708 0.981686 0.984185 0.984185 0.981686 0.976708 0.969287 0.959481 0.947362 0.933021 0.916565 0.898112 0.877797 0.855763 0.832164 0.807161 0.780921 0.753617 0.72542 0.696506 0.667046 0.63721 0.607163 0.577064 0.547065 0.517308 0.487928 0.459049 0.430782 0.403229 0.37648 0.350613 0.325695 0.301779 0.278909 0.257118 0.236428 0.216851 0.19839 0.181039 0.164787 0.149613 0.135491 0.12239 0.110276 0.0991083 0.0888455 0.0794432 0.0708556 0.0630358 0.0559367 0.049511 0.0437122 0.0384946 0.0338137 0.0296266 0.025892 0.0225708 0.0196256 0.0170214 0.0147253 0.0127066 0.0109368 +0.0110208 0.0128042 0.0148384 0.0171522 0.0197764 0.0227442 0.0260909 0.0298542 0.0340734 0.0387903 0.044048 0.0498913 0.0563663 0.06352 0.0713999 0.0800535 0.089528 0.0998696 0.111123 0.123331 0.136532 0.150762 0.166053 0.18243 0.199913 0.218516 0.238244 0.259093 0.281052 0.304097 0.328196 0.353306 0.379372 0.406326 0.434091 0.462575 0.491677 0.521282 0.551267 0.581497 0.611827 0.642105 0.67217 0.701856 0.730992 0.759406 0.78692 0.813361 0.838556 0.862337 0.88454 0.905011 0.923605 0.940188 0.954639 0.966851 0.976733 0.984211 0.989227 0.991745 0.991745 0.989227 0.984211 0.976733 0.966851 0.954639 0.940188 0.923605 0.905011 0.88454 0.862337 0.838556 0.813361 0.78692 0.759406 0.730992 0.701856 0.67217 0.642105 0.611827 0.581497 0.551267 0.521282 0.491677 0.462575 0.434091 0.406326 0.379372 0.353306 0.328196 0.304097 0.281052 0.259093 0.238244 0.218516 0.199913 0.18243 0.166053 0.150762 0.136532 0.123331 0.111123 0.0998696 0.089528 0.0800535 0.0713999 0.06352 0.0563663 0.0498913 0.044048 0.0387903 0.0340734 0.0298542 0.0260909 0.0227442 0.0197764 0.0171522 0.0148384 0.0128042 0.0110208 +0.0110772 0.0128697 0.0149143 0.0172399 0.0198775 0.0228605 0.0262244 0.0300068 0.0342477 0.0389887 0.0442733 0.0501465 0.0566546 0.0638449 0.0717651 0.0804629 0.0899859 0.10038 0.111691 0.123961 0.13723 0.151533 0.166902 0.183363 0.200936 0.219634 0.239463 0.260418 0.282489 0.305652 0.329875 0.355113 0.381312 0.408404 0.436311 0.464941 0.494191 0.523948 0.554087 0.584471 0.614956 0.645389 0.675608 0.705446 0.734731 0.76329 0.790945 0.817521 0.842845 0.866747 0.889064 0.90964 0.928329 0.944997 0.959522 0.971796 0.981728 0.989244 0.994287 0.996818 0.996818 0.994287 0.989244 0.981728 0.971796 0.959522 0.944997 0.928329 0.90964 0.889064 0.866747 0.842845 0.817521 0.790945 0.76329 0.734731 0.705446 0.675608 0.645389 0.614956 0.584471 0.554087 0.523948 0.494191 0.464941 0.436311 0.408404 0.381312 0.355113 0.329875 0.305652 0.282489 0.260418 0.239463 0.219634 0.200936 0.183363 0.166902 0.151533 0.13723 0.123961 0.111691 0.10038 0.0899859 0.0804629 0.0717651 0.0638449 0.0566546 0.0501465 0.0442733 0.0389887 0.0342477 0.0300068 0.0262244 0.0228605 0.0198775 0.0172399 0.0149143 0.0128697 0.0110772 +0.0111055 0.0129026 0.0149524 0.0172839 0.0199283 0.0229189 0.0262913 0.0300835 0.0343352 0.0390883 0.0443863 0.0502746 0.0567993 0.064008 0.0719484 0.0806684 0.0902157 0.100637 0.111977 0.124278 0.13758 0.15192 0.167328 0.183831 0.201449 0.220195 0.240074 0.261084 0.283211 0.306433 0.330718 0.35602 0.382286 0.409448 0.437425 0.466128 0.495453 0.525286 0.555502 0.585964 0.616527 0.647037 0.677333 0.707247 0.736608 0.765239 0.792965 0.819609 0.844998 0.868961 0.891335 0.911963 0.9307 0.947411 0.961972 0.974278 0.984236 0.991771 0.996826 0.999364 0.999364 0.996826 0.991771 0.984236 0.974278 0.961972 0.947411 0.9307 0.911963 0.891335 0.868961 0.844998 0.819609 0.792965 0.765239 0.736608 0.707247 0.677333 0.647037 0.616527 0.585964 0.555502 0.525286 0.495453 0.466128 0.437425 0.409448 0.382286 0.35602 0.330718 0.306433 0.283211 0.261084 0.240074 0.220195 0.201449 0.183831 0.167328 0.15192 0.13758 0.124278 0.111977 0.100637 0.0902157 0.0806684 0.0719484 0.064008 0.0567993 0.0502746 0.0443863 0.0390883 0.0343352 0.0300835 0.0262913 0.0229189 0.0199283 0.0172839 0.0149524 0.0129026 0.0111055 +0.0111055 0.0129026 0.0149524 0.0172839 0.0199283 0.0229189 0.0262913 0.0300835 0.0343352 0.0390883 0.0443863 0.0502746 0.0567993 0.064008 0.0719484 0.0806684 0.0902157 0.100637 0.111977 0.124278 0.13758 0.15192 0.167328 0.183831 0.201449 0.220195 0.240074 0.261084 0.283211 0.306433 0.330718 0.35602 0.382286 0.409448 0.437425 0.466128 0.495453 0.525286 0.555502 0.585964 0.616527 0.647037 0.677333 0.707247 0.736608 0.765239 0.792965 0.819609 0.844998 0.868961 0.891335 0.911963 0.9307 0.947411 0.961972 0.974278 0.984236 0.991771 0.996826 0.999364 0.999364 0.996826 0.991771 0.984236 0.974278 0.961972 0.947411 0.9307 0.911963 0.891335 0.868961 0.844998 0.819609 0.792965 0.765239 0.736608 0.707247 0.677333 0.647037 0.616527 0.585964 0.555502 0.525286 0.495453 0.466128 0.437425 0.409448 0.382286 0.35602 0.330718 0.306433 0.283211 0.261084 0.240074 0.220195 0.201449 0.183831 0.167328 0.15192 0.13758 0.124278 0.111977 0.100637 0.0902157 0.0806684 0.0719484 0.064008 0.0567993 0.0502746 0.0443863 0.0390883 0.0343352 0.0300835 0.0262913 0.0229189 0.0199283 0.0172839 0.0149524 0.0129026 0.0111055 +0.0110772 0.0128697 0.0149143 0.0172399 0.0198775 0.0228605 0.0262244 0.0300068 0.0342477 0.0389887 0.0442733 0.0501465 0.0566546 0.0638449 0.0717651 0.0804629 0.0899859 0.10038 0.111691 0.123961 0.13723 0.151533 0.166902 0.183363 0.200936 0.219634 0.239463 0.260418 0.282489 0.305652 0.329875 0.355113 0.381312 0.408404 0.436311 0.464941 0.494191 0.523948 0.554087 0.584471 0.614956 0.645389 0.675608 0.705446 0.734731 0.76329 0.790945 0.817521 0.842845 0.866747 0.889064 0.90964 0.928329 0.944997 0.959522 0.971796 0.981728 0.989244 0.994287 0.996818 0.996818 0.994287 0.989244 0.981728 0.971796 0.959522 0.944997 0.928329 0.90964 0.889064 0.866747 0.842845 0.817521 0.790945 0.76329 0.734731 0.705446 0.675608 0.645389 0.614956 0.584471 0.554087 0.523948 0.494191 0.464941 0.436311 0.408404 0.381312 0.355113 0.329875 0.305652 0.282489 0.260418 0.239463 0.219634 0.200936 0.183363 0.166902 0.151533 0.13723 0.123961 0.111691 0.10038 0.0899859 0.0804629 0.0717651 0.0638449 0.0566546 0.0501465 0.0442733 0.0389887 0.0342477 0.0300068 0.0262244 0.0228605 0.0198775 0.0172399 0.0149143 0.0128697 0.0110772 +0.0110208 0.0128042 0.0148384 0.0171522 0.0197764 0.0227442 0.0260909 0.0298542 0.0340734 0.0387903 0.044048 0.0498913 0.0563663 0.06352 0.0713999 0.0800535 0.089528 0.0998696 0.111123 0.123331 0.136532 0.150762 0.166053 0.18243 0.199913 0.218516 0.238244 0.259093 0.281052 0.304097 0.328196 0.353306 0.379372 0.406326 0.434091 0.462575 0.491677 0.521282 0.551267 0.581497 0.611827 0.642105 0.67217 0.701856 0.730992 0.759406 0.78692 0.813361 0.838556 0.862337 0.88454 0.905011 0.923605 0.940188 0.954639 0.966851 0.976733 0.984211 0.989227 0.991745 0.991745 0.989227 0.984211 0.976733 0.966851 0.954639 0.940188 0.923605 0.905011 0.88454 0.862337 0.838556 0.813361 0.78692 0.759406 0.730992 0.701856 0.67217 0.642105 0.611827 0.581497 0.551267 0.521282 0.491677 0.462575 0.434091 0.406326 0.379372 0.353306 0.328196 0.304097 0.281052 0.259093 0.238244 0.218516 0.199913 0.18243 0.166053 0.150762 0.136532 0.123331 0.111123 0.0998696 0.089528 0.0800535 0.0713999 0.06352 0.0563663 0.0498913 0.044048 0.0387903 0.0340734 0.0298542 0.0260909 0.0227442 0.0197764 0.0171522 0.0148384 0.0128042 0.0110208 +0.0109368 0.0127066 0.0147253 0.0170214 0.0196256 0.0225708 0.025892 0.0296266 0.0338137 0.0384946 0.0437122 0.049511 0.0559367 0.0630358 0.0708556 0.0794432 0.0888455 0.0991083 0.110276 0.12239 0.135491 0.149613 0.164787 0.181039 0.19839 0.216851 0.236428 0.257118 0.278909 0.301779 0.325695 0.350613 0.37648 0.403229 0.430782 0.459049 0.487928 0.517308 0.547065 0.577064 0.607163 0.63721 0.667046 0.696506 0.72542 0.753617 0.780921 0.807161 0.832164 0.855763 0.877797 0.898112 0.916565 0.933021 0.947362 0.959481 0.969287 0.976708 0.981686 0.984185 0.984185 0.981686 0.976708 0.969287 0.959481 0.947362 0.933021 0.916565 0.898112 0.877797 0.855763 0.832164 0.807161 0.780921 0.753617 0.72542 0.696506 0.667046 0.63721 0.607163 0.577064 0.547065 0.517308 0.487928 0.459049 0.430782 0.403229 0.37648 0.350613 0.325695 0.301779 0.278909 0.257118 0.236428 0.216851 0.19839 0.181039 0.164787 0.149613 0.135491 0.12239 0.110276 0.0991083 0.0888455 0.0794432 0.0708556 0.0630358 0.0559367 0.049511 0.0437122 0.0384946 0.0338137 0.0296266 0.025892 0.0225708 0.0196256 0.0170214 0.0147253 0.0127066 0.0109368 +0.0108258 0.0125776 0.0145758 0.0168486 0.0194264 0.0223417 0.0256292 0.0293258 0.0334704 0.0381038 0.0432685 0.0490084 0.0553688 0.0623959 0.0701363 0.0786368 0.0879436 0.0981023 0.109156 0.121148 0.134115 0.148094 0.163114 0.179201 0.196376 0.214649 0.234028 0.254508 0.276078 0.298715 0.322388 0.347054 0.372658 0.399136 0.426409 0.454389 0.482975 0.512057 0.541512 0.571206 0.601 0.630741 0.660274 0.689435 0.718056 0.745967 0.772994 0.798967 0.823716 0.847076 0.868886 0.888995 0.907261 0.92355 0.937745 0.949741 0.959448 0.966793 0.971721 0.974195 0.974195 0.971721 0.966793 0.959448 0.949741 0.937745 0.92355 0.907261 0.888995 0.868886 0.847076 0.823716 0.798967 0.772994 0.745967 0.718056 0.689435 0.660274 0.630741 0.601 0.571206 0.541512 0.512057 0.482975 0.454389 0.426409 0.399136 0.372658 0.347054 0.322388 0.298715 0.276078 0.254508 0.234028 0.214649 0.196376 0.179201 0.163114 0.148094 0.134115 0.121148 0.109156 0.0981023 0.0879436 0.0786368 0.0701363 0.0623959 0.0553688 0.0490084 0.0432685 0.0381038 0.0334704 0.0293258 0.0256292 0.0223417 0.0194264 0.0168486 0.0145758 0.0125776 0.0108258 +0.0106886 0.0124182 0.0143911 0.0166351 0.0191802 0.0220585 0.0253044 0.0289542 0.0330463 0.0376209 0.0427201 0.0483873 0.0546672 0.0616052 0.0692475 0.0776402 0.0868291 0.096859 0.107773 0.119613 0.132416 0.146217 0.161047 0.17693 0.193887 0.211929 0.231062 0.251283 0.272579 0.29493 0.318303 0.342656 0.367936 0.394077 0.421005 0.44863 0.476855 0.505568 0.534649 0.563968 0.593383 0.622748 0.651907 0.680698 0.708956 0.736513 0.763198 0.788842 0.813278 0.836341 0.857875 0.877729 0.895763 0.911846 0.925861 0.937705 0.947289 0.954541 0.959407 0.961849 0.961849 0.959407 0.954541 0.947289 0.937705 0.925861 0.911846 0.895763 0.877729 0.857875 0.836341 0.813278 0.788842 0.763198 0.736513 0.708956 0.680698 0.651907 0.622748 0.593383 0.563968 0.534649 0.505568 0.476855 0.44863 0.421005 0.394077 0.367936 0.342656 0.318303 0.29493 0.272579 0.251283 0.231062 0.211929 0.193887 0.17693 0.161047 0.146217 0.132416 0.119613 0.107773 0.096859 0.0868291 0.0776402 0.0692475 0.0616052 0.0546672 0.0483873 0.0427201 0.0376209 0.0330463 0.0289542 0.0253044 0.0220585 0.0191802 0.0166351 0.0143911 0.0124182 0.0106886 +0.0105262 0.0122296 0.0141726 0.0163825 0.0188889 0.0217235 0.0249201 0.0285144 0.0325444 0.0370495 0.0420713 0.0476524 0.0538369 0.0606695 0.0681958 0.076461 0.0855104 0.0953879 0.106136 0.117796 0.130405 0.143996 0.158601 0.174243 0.190942 0.20871 0.227553 0.247466 0.268439 0.29045 0.313468 0.337452 0.362347 0.388092 0.414611 0.441817 0.469612 0.497889 0.526529 0.555402 0.584371 0.61329 0.642006 0.67036 0.698189 0.725327 0.751607 0.776861 0.800926 0.823639 0.844846 0.864398 0.882158 0.897997 0.911799 0.923463 0.932901 0.940043 0.944835 0.94724 0.94724 0.944835 0.940043 0.932901 0.923463 0.911799 0.897997 0.882158 0.864398 0.844846 0.823639 0.800926 0.776861 0.751607 0.725327 0.698189 0.67036 0.642006 0.61329 0.584371 0.555402 0.526529 0.497889 0.469612 0.441817 0.414611 0.388092 0.362347 0.337452 0.313468 0.29045 0.268439 0.247466 0.227553 0.20871 0.190942 0.174243 0.158601 0.143996 0.130405 0.117796 0.106136 0.0953879 0.0855104 0.076461 0.0681958 0.0606695 0.0538369 0.0476524 0.0420713 0.0370495 0.0325444 0.0285144 0.0249201 0.0217235 0.0188889 0.0163825 0.0141726 0.0122296 0.0105262 +0.01034 0.0120132 0.0139217 0.0160926 0.0185546 0.0213391 0.0244791 0.0280098 0.0319684 0.0363939 0.0413268 0.0468091 0.0528841 0.0595959 0.0669889 0.0751079 0.0839971 0.0936999 0.104258 0.115711 0.128097 0.141448 0.155794 0.17116 0.187563 0.205017 0.223526 0.243087 0.263689 0.28531 0.307921 0.33148 0.355935 0.381224 0.407273 0.433998 0.461302 0.489078 0.517211 0.545573 0.574029 0.602437 0.630644 0.658496 0.685833 0.712491 0.738306 0.763113 0.786752 0.809063 0.829895 0.849101 0.866547 0.882105 0.895663 0.907121 0.916392 0.923408 0.928115 0.930477 0.930477 0.928115 0.923408 0.916392 0.907121 0.895663 0.882105 0.866547 0.849101 0.829895 0.809063 0.786752 0.763113 0.738306 0.712491 0.685833 0.658496 0.630644 0.602437 0.574029 0.545573 0.517211 0.489078 0.461302 0.433998 0.407273 0.381224 0.355935 0.33148 0.307921 0.28531 0.263689 0.243087 0.223526 0.205017 0.187563 0.17116 0.155794 0.141448 0.128097 0.115711 0.104258 0.0936999 0.0839971 0.0751079 0.0669889 0.0595959 0.0528841 0.0468091 0.0413268 0.0363939 0.0319684 0.0280098 0.0244791 0.0213391 0.0185546 0.0160926 0.0139217 0.0120132 0.01034 +0.0101311 0.0117705 0.0136405 0.0157675 0.0181798 0.020908 0.0239846 0.027444 0.0313227 0.0356588 0.040492 0.0458636 0.0518159 0.0583921 0.0656358 0.0735908 0.0823004 0.0918072 0.102152 0.113374 0.125509 0.138591 0.152647 0.167702 0.183775 0.200876 0.219011 0.238177 0.258362 0.279547 0.301701 0.324784 0.348745 0.373524 0.399047 0.425231 0.451984 0.479199 0.506764 0.534553 0.562434 0.590268 0.617906 0.645195 0.67198 0.698099 0.723392 0.747699 0.77086 0.79272 0.813131 0.83195 0.849043 0.864287 0.877571 0.888797 0.897882 0.904755 0.909367 0.911682 0.911682 0.909367 0.904755 0.897882 0.888797 0.877571 0.864287 0.849043 0.83195 0.813131 0.79272 0.77086 0.747699 0.723392 0.698099 0.67198 0.645195 0.617906 0.590268 0.562434 0.534553 0.506764 0.479199 0.451984 0.425231 0.399047 0.373524 0.348745 0.324784 0.301701 0.279547 0.258362 0.238177 0.219011 0.200876 0.183775 0.167702 0.152647 0.138591 0.125509 0.113374 0.102152 0.0918072 0.0823004 0.0735908 0.0656358 0.0583921 0.0518159 0.0458636 0.040492 0.0356588 0.0313227 0.027444 0.0239846 0.020908 0.0181798 0.0157675 0.0136405 0.0117705 0.0101311 +0.00990116 0.0115034 0.013331 0.0154096 0.0177672 0.0204335 0.0234403 0.0268212 0.0306118 0.0348495 0.039573 0.0448227 0.0506399 0.0570669 0.0641462 0.0719206 0.0804326 0.0897236 0.0998337 0.110801 0.122661 0.135445 0.149183 0.163896 0.179604 0.196317 0.21404 0.232771 0.252499 0.273203 0.294854 0.317413 0.340831 0.365046 0.38999 0.415581 0.441726 0.468324 0.495262 0.522421 0.54967 0.576871 0.603882 0.630552 0.656729 0.682255 0.706975 0.73073 0.753365 0.774729 0.794677 0.813069 0.829774 0.844672 0.857655 0.868626 0.877504 0.884222 0.888729 0.890991 0.890991 0.888729 0.884222 0.877504 0.868626 0.857655 0.844672 0.829774 0.813069 0.794677 0.774729 0.753365 0.73073 0.706975 0.682255 0.656729 0.630552 0.603882 0.576871 0.54967 0.522421 0.495262 0.468324 0.441726 0.415581 0.38999 0.365046 0.340831 0.317413 0.294854 0.273203 0.252499 0.232771 0.21404 0.196317 0.179604 0.163896 0.149183 0.135445 0.122661 0.110801 0.0998337 0.0897236 0.0804326 0.0719206 0.0641462 0.0570669 0.0506399 0.0448227 0.039573 0.0348495 0.0306118 0.0268212 0.0234403 0.0204335 0.0177672 0.0154096 0.013331 0.0115034 0.00990116 +0.0096518 0.0112137 0.0129952 0.0150216 0.0173198 0.0199189 0.0228499 0.0261457 0.0298409 0.0339718 0.0385764 0.0436939 0.0493646 0.0556296 0.0625306 0.0701093 0.0784069 0.0874639 0.0973194 0.10801 0.119572 0.132034 0.145426 0.159769 0.17508 0.191373 0.20865 0.226909 0.24614 0.266322 0.287428 0.309419 0.332247 0.355853 0.380168 0.405114 0.430601 0.456529 0.482789 0.509264 0.535826 0.562343 0.588673 0.614672 0.640189 0.665073 0.68917 0.712326 0.734391 0.755218 0.774663 0.792591 0.808876 0.823399 0.836055 0.84675 0.855404 0.861953 0.866346 0.868551 0.868551 0.866346 0.861953 0.855404 0.84675 0.836055 0.823399 0.808876 0.792591 0.774663 0.755218 0.734391 0.712326 0.68917 0.665073 0.640189 0.614672 0.588673 0.562343 0.535826 0.509264 0.482789 0.456529 0.430601 0.405114 0.380168 0.355853 0.332247 0.309419 0.287428 0.266322 0.24614 0.226909 0.20865 0.191373 0.17508 0.159769 0.145426 0.132034 0.119572 0.10801 0.0973194 0.0874639 0.0784069 0.0701093 0.0625306 0.0556296 0.0493646 0.0436939 0.0385764 0.0339718 0.0298409 0.0261457 0.0228499 0.0199189 0.0173198 0.0150216 0.0129952 0.0112137 0.0096518 +0.00938475 0.0109034 0.0126357 0.0146059 0.0168406 0.0193678 0.0222177 0.0254223 0.0290152 0.0330318 0.037509 0.0424849 0.0479987 0.0540904 0.0608005 0.0681695 0.0762375 0.0850439 0.0946267 0.105022 0.116263 0.128381 0.141402 0.155348 0.170236 0.186078 0.202877 0.220631 0.239329 0.258954 0.279475 0.300858 0.323054 0.346007 0.36965 0.393905 0.418687 0.443897 0.469431 0.495173 0.521001 0.546784 0.572386 0.597665 0.622476 0.646671 0.670101 0.692617 0.714072 0.734322 0.753229 0.770662 0.786496 0.800617 0.812922 0.823321 0.831736 0.838104 0.842376 0.84452 0.84452 0.842376 0.838104 0.831736 0.823321 0.812922 0.800617 0.786496 0.770662 0.753229 0.734322 0.714072 0.692617 0.670101 0.646671 0.622476 0.597665 0.572386 0.546784 0.521001 0.495173 0.469431 0.443897 0.418687 0.393905 0.36965 0.346007 0.323054 0.300858 0.279475 0.258954 0.239329 0.220631 0.202877 0.186078 0.170236 0.155348 0.141402 0.128381 0.116263 0.105022 0.0946267 0.0850439 0.0762375 0.0681695 0.0608005 0.0540904 0.0479987 0.0424849 0.037509 0.0330318 0.0290152 0.0254223 0.0222177 0.0193678 0.0168406 0.0146059 0.0126357 0.0109034 0.00938475 +0.00910185 0.0105747 0.0122548 0.0141656 0.0163329 0.0187839 0.021548 0.0246559 0.0281405 0.0320361 0.0363783 0.0412042 0.0465518 0.0524599 0.0589677 0.0661145 0.0739393 0.0824802 0.0917742 0.101856 0.112759 0.124511 0.137139 0.150665 0.165104 0.180468 0.196761 0.21398 0.232115 0.251147 0.271051 0.291788 0.313315 0.335576 0.358506 0.382031 0.406065 0.430516 0.45528 0.480246 0.505295 0.530301 0.555131 0.579648 0.603711 0.627177 0.649901 0.671738 0.692546 0.712186 0.730523 0.74743 0.762786 0.776482 0.788416 0.798502 0.806663 0.812839 0.816982 0.819062 0.819062 0.816982 0.812839 0.806663 0.798502 0.788416 0.776482 0.762786 0.74743 0.730523 0.712186 0.692546 0.671738 0.649901 0.627177 0.603711 0.579648 0.555131 0.530301 0.505295 0.480246 0.45528 0.430516 0.406065 0.382031 0.358506 0.335576 0.313315 0.291788 0.271051 0.251147 0.232115 0.21398 0.196761 0.180468 0.165104 0.150665 0.137139 0.124511 0.112759 0.101856 0.0917742 0.0824802 0.0739393 0.0661145 0.0589677 0.0524599 0.0465518 0.0412042 0.0363783 0.0320361 0.0281405 0.0246559 0.021548 0.0187839 0.0163329 0.0141656 0.0122548 0.0105747 0.00910185 +0.00880498 0.0102298 0.011855 0.0137036 0.0158002 0.0181713 0.0208451 0.0238517 0.0272227 0.0309912 0.0351918 0.0398603 0.0450334 0.0507488 0.0570444 0.0639581 0.0715277 0.0797901 0.0887809 0.0985339 0.109081 0.12045 0.132666 0.145751 0.159719 0.174582 0.190343 0.207001 0.224544 0.242956 0.26221 0.282271 0.303096 0.324631 0.346813 0.36957 0.392821 0.416474 0.440431 0.464582 0.488814 0.513004 0.537025 0.560742 0.584021 0.606721 0.628704 0.649828 0.669958 0.688957 0.706696 0.723052 0.737907 0.751156 0.762701 0.772458 0.780353 0.786327 0.790335 0.792347 0.792347 0.790335 0.786327 0.780353 0.772458 0.762701 0.751156 0.737907 0.723052 0.706696 0.688957 0.669958 0.649828 0.628704 0.606721 0.584021 0.560742 0.537025 0.513004 0.488814 0.464582 0.440431 0.416474 0.392821 0.36957 0.346813 0.324631 0.303096 0.282271 0.26221 0.242956 0.224544 0.207001 0.190343 0.174582 0.159719 0.145751 0.132666 0.12045 0.109081 0.0985339 0.0887809 0.0797901 0.0715277 0.0639581 0.0570444 0.0507488 0.0450334 0.0398603 0.0351918 0.0309912 0.0272227 0.0238517 0.0208451 0.0181713 0.0158002 0.0137036 0.011855 0.0102298 0.00880498 +0.00849609 0.00987095 0.0114392 0.0132229 0.0152459 0.0175338 0.0201139 0.023015 0.0262677 0.029904 0.0339572 0.038462 0.0434536 0.0489685 0.0550432 0.0617144 0.0690185 0.076991 0.0856664 0.0950773 0.105254 0.116224 0.128012 0.140638 0.154116 0.168458 0.183666 0.199739 0.216667 0.234433 0.253011 0.272369 0.292463 0.313243 0.334647 0.356606 0.379041 0.401864 0.42498 0.448285 0.471666 0.495008 0.518185 0.541071 0.563533 0.585437 0.606648 0.627032 0.646455 0.664788 0.681905 0.697687 0.712021 0.724805 0.735945 0.74536 0.752978 0.758742 0.76261 0.764551 0.764551 0.76261 0.758742 0.752978 0.74536 0.735945 0.724805 0.712021 0.697687 0.681905 0.664788 0.646455 0.627032 0.606648 0.585437 0.563533 0.541071 0.518185 0.495008 0.471666 0.448285 0.42498 0.401864 0.379041 0.356606 0.334647 0.313243 0.292463 0.272369 0.253011 0.234433 0.216667 0.199739 0.183666 0.168458 0.154116 0.140638 0.128012 0.116224 0.105254 0.0950773 0.0856664 0.076991 0.0690185 0.0617144 0.0550432 0.0489685 0.0434536 0.038462 0.0339572 0.029904 0.0262677 0.023015 0.0201139 0.0175338 0.0152459 0.0132229 0.0114392 0.00987095 0.00849609 +0.00817716 0.00950041 0.0110098 0.0127265 0.0146736 0.0168756 0.0193588 0.0221511 0.0252817 0.0287814 0.0326825 0.0370181 0.0418225 0.0471303 0.052977 0.0593977 0.0664276 0.0741008 0.0824506 0.0915082 0.101303 0.111862 0.123207 0.135358 0.148331 0.162134 0.176771 0.192241 0.208533 0.225632 0.243514 0.262145 0.281485 0.301484 0.322085 0.343219 0.364812 0.386779 0.409027 0.431457 0.453961 0.476426 0.498733 0.52076 0.542379 0.56346 0.583876 0.603494 0.622188 0.639833 0.656307 0.671496 0.685293 0.697597 0.708319 0.71738 0.724712 0.73026 0.733983 0.735851 0.735851 0.733983 0.73026 0.724712 0.71738 0.708319 0.697597 0.685293 0.671496 0.656307 0.639833 0.622188 0.603494 0.583876 0.56346 0.542379 0.52076 0.498733 0.476426 0.453961 0.431457 0.409027 0.386779 0.364812 0.343219 0.322085 0.301484 0.281485 0.262145 0.243514 0.225632 0.208533 0.192241 0.176771 0.162134 0.148331 0.135358 0.123207 0.111862 0.101303 0.0915082 0.0824506 0.0741008 0.0664276 0.0593977 0.052977 0.0471303 0.0418225 0.0370181 0.0326825 0.0287814 0.0252817 0.0221511 0.0193588 0.0168756 0.0146736 0.0127265 0.0110098 0.00950041 0.00817716 +0.00785015 0.00912048 0.0105695 0.0122176 0.0140868 0.0162008 0.0185847 0.0212652 0.0242706 0.0276305 0.0313755 0.0355378 0.0401499 0.0452455 0.0508584 0.0570224 0.0637711 0.0711375 0.0791533 0.0878488 0.0972519 0.107388 0.11828 0.129945 0.142399 0.15565 0.169702 0.184553 0.200194 0.216609 0.233775 0.251661 0.270228 0.289428 0.309204 0.329494 0.350223 0.371311 0.39267 0.414202 0.435807 0.457373 0.478789 0.499934 0.520689 0.540927 0.560526 0.57936 0.597307 0.614245 0.630061 0.644643 0.657887 0.6697 0.679993 0.688691 0.69573 0.701057 0.70463 0.706424 0.706424 0.70463 0.701057 0.69573 0.688691 0.679993 0.6697 0.657887 0.644643 0.630061 0.614245 0.597307 0.57936 0.560526 0.540927 0.520689 0.499934 0.478789 0.457373 0.435807 0.414202 0.39267 0.371311 0.350223 0.329494 0.309204 0.289428 0.270228 0.251661 0.233775 0.216609 0.200194 0.184553 0.169702 0.15565 0.142399 0.129945 0.11828 0.107388 0.0972519 0.0878488 0.0791533 0.0711375 0.0637711 0.0570224 0.0508584 0.0452455 0.0401499 0.0355378 0.0313755 0.0276305 0.0242706 0.0212652 0.0185847 0.0162008 0.0140868 0.0122176 0.0105695 0.00912048 0.00785015 +0.00751702 0.00873344 0.0101209 0.0116991 0.013489 0.0155133 0.017796 0.0203628 0.0232407 0.0264579 0.0300441 0.0340297 0.0384461 0.0433255 0.0487002 0.0546026 0.0610649 0.0681187 0.0757944 0.0841208 0.0931249 0.102831 0.11326 0.124431 0.136356 0.149045 0.162501 0.176721 0.191699 0.207417 0.223855 0.240982 0.258761 0.277145 0.296083 0.315511 0.335361 0.355554 0.376006 0.396625 0.417313 0.437964 0.458471 0.478719 0.498593 0.517972 0.536739 0.554774 0.571959 0.588179 0.603324 0.617287 0.629969 0.64128 0.651137 0.659466 0.666206 0.671306 0.674728 0.676446 0.676446 0.674728 0.671306 0.666206 0.659466 0.651137 0.64128 0.629969 0.617287 0.603324 0.588179 0.571959 0.554774 0.536739 0.517972 0.498593 0.478719 0.458471 0.437964 0.417313 0.396625 0.376006 0.355554 0.335361 0.315511 0.296083 0.277145 0.258761 0.240982 0.223855 0.207417 0.191699 0.176721 0.162501 0.149045 0.136356 0.124431 0.11326 0.102831 0.0931249 0.0841208 0.0757944 0.0681187 0.0610649 0.0546026 0.0487002 0.0433255 0.0384461 0.0340297 0.0300441 0.0264579 0.0232407 0.0203628 0.017796 0.0155133 0.013489 0.0116991 0.0101209 0.00873344 0.00751702 +0.00717969 0.00834152 0.00966676 0.0111741 0.0128837 0.0148171 0.0169974 0.019449 0.0221977 0.0252706 0.0286958 0.0325026 0.0367208 0.0413812 0.0465147 0.0521522 0.0583246 0.0650618 0.072393 0.0803458 0.0889459 0.0982164 0.108178 0.118847 0.130237 0.142356 0.155208 0.168791 0.183096 0.198109 0.213809 0.230168 0.247149 0.264708 0.282796 0.301352 0.320311 0.339598 0.359133 0.378826 0.398585 0.41831 0.437897 0.457236 0.476218 0.494728 0.512653 0.529878 0.546292 0.561784 0.576249 0.589585 0.601699 0.612502 0.621916 0.629872 0.63631 0.641181 0.64445 0.64609 0.64609 0.64445 0.641181 0.63631 0.629872 0.621916 0.612502 0.601699 0.589585 0.576249 0.561784 0.546292 0.529878 0.512653 0.494728 0.476218 0.457236 0.437897 0.41831 0.398585 0.378826 0.359133 0.339598 0.320311 0.301352 0.282796 0.264708 0.247149 0.230168 0.213809 0.198109 0.183096 0.168791 0.155208 0.142356 0.130237 0.118847 0.108178 0.0982164 0.0889459 0.0803458 0.072393 0.0650618 0.0583246 0.0521522 0.0465147 0.0413812 0.0367208 0.0325026 0.0286958 0.0252706 0.0221977 0.019449 0.0169974 0.0148171 0.0128837 0.0111741 0.00966676 0.00834152 0.00717969 +0.00684003 0.0079469 0.00920944 0.0106455 0.0122742 0.0141161 0.0161933 0.0185289 0.0211476 0.0240751 0.0273383 0.0309649 0.0349836 0.0394235 0.0443142 0.049685 0.0555653 0.0619838 0.0689682 0.0765448 0.0847379 0.0935699 0.10306 0.113225 0.124076 0.135622 0.147866 0.160806 0.174434 0.188737 0.203694 0.219279 0.235456 0.252185 0.269417 0.287096 0.305158 0.323532 0.342143 0.360905 0.379729 0.398521 0.41718 0.435605 0.453689 0.471323 0.4884 0.504811 0.520448 0.535207 0.548987 0.561693 0.573233 0.583526 0.592494 0.600074 0.606207 0.610848 0.613961 0.615524 0.615524 0.613961 0.610848 0.606207 0.600074 0.592494 0.583526 0.573233 0.561693 0.548987 0.535207 0.520448 0.504811 0.4884 0.471323 0.453689 0.435605 0.41718 0.398521 0.379729 0.360905 0.342143 0.323532 0.305158 0.287096 0.269417 0.252185 0.235456 0.219279 0.203694 0.188737 0.174434 0.160806 0.147866 0.135622 0.124076 0.113225 0.10306 0.0935699 0.0847379 0.0765448 0.0689682 0.0619838 0.0555653 0.049685 0.0443142 0.0394235 0.0349836 0.0309649 0.0273383 0.0240751 0.0211476 0.0185289 0.0161933 0.0141161 0.0122742 0.0106455 0.00920944 0.0079469 0.00684003 +0.00649984 0.00755165 0.0087514 0.010116 0.0116637 0.013414 0.0153879 0.0176074 0.0200958 0.0228777 0.0259786 0.0294249 0.0332437 0.0374628 0.0421102 0.0472139 0.0528017 0.058901 0.065538 0.0727377 0.0805234 0.0889161 0.0979343 0.107593 0.117905 0.128876 0.140511 0.152808 0.165758 0.17935 0.193563 0.208373 0.223746 0.239643 0.256018 0.272817 0.28998 0.307441 0.325126 0.342955 0.360843 0.3787 0.396432 0.41394 0.431124 0.447882 0.464109 0.479703 0.494563 0.508588 0.521683 0.533757 0.544723 0.554503 0.563026 0.570228 0.576057 0.580467 0.583426 0.584911 0.584911 0.583426 0.580467 0.576057 0.570228 0.563026 0.554503 0.544723 0.533757 0.521683 0.508588 0.494563 0.479703 0.464109 0.447882 0.431124 0.41394 0.396432 0.3787 0.360843 0.342955 0.325126 0.307441 0.28998 0.272817 0.256018 0.239643 0.223746 0.208373 0.193563 0.17935 0.165758 0.152808 0.140511 0.128876 0.117905 0.107593 0.0979343 0.0889161 0.0805234 0.0727377 0.065538 0.058901 0.0528017 0.0472139 0.0421102 0.0374628 0.0332437 0.0294249 0.0259786 0.0228777 0.0200958 0.0176074 0.0153879 0.013414 0.0116637 0.010116 0.0087514 0.00755165 0.00649984 +0.00616083 0.00715778 0.00829495 0.00958839 0.0110554 0.0127144 0.0145853 0.016689 0.0190477 0.0216845 0.0246236 0.0278902 0.0315098 0.0355089 0.0399138 0.0447514 0.0500478 0.0558289 0.0621198 0.068944 0.0763236 0.0842786 0.0928264 0.101982 0.111755 0.122155 0.133183 0.144838 0.157113 0.169996 0.183468 0.197505 0.212076 0.227144 0.242665 0.258588 0.274856 0.291406 0.308168 0.325067 0.342022 0.358948 0.375755 0.39235 0.408638 0.424522 0.439903 0.454684 0.468768 0.482062 0.494474 0.505918 0.516312 0.525582 0.533661 0.540487 0.546011 0.550192 0.552996 0.554404 0.554404 0.552996 0.550192 0.546011 0.540487 0.533661 0.525582 0.516312 0.505918 0.494474 0.482062 0.468768 0.454684 0.439903 0.424522 0.408638 0.39235 0.375755 0.358948 0.342022 0.325067 0.308168 0.291406 0.274856 0.258588 0.242665 0.227144 0.212076 0.197505 0.183468 0.169996 0.157113 0.144838 0.133183 0.122155 0.111755 0.101982 0.0928264 0.0842786 0.0763236 0.068944 0.0621198 0.0558289 0.0500478 0.0447514 0.0399138 0.0355089 0.0315098 0.0278902 0.0246236 0.0216845 0.0190477 0.016689 0.0145853 0.0127144 0.0110554 0.00958839 0.00829495 0.00715778 0.00616083 +0.00582462 0.00676717 0.00784229 0.00906514 0.0104521 0.0120206 0.0137894 0.0157783 0.0180082 0.0205011 0.0232799 0.0263682 0.0297903 0.0335711 0.0377357 0.0423092 0.0473166 0.0527823 0.0587298 0.0651816 0.0721585 0.0796794 0.0877608 0.0964163 0.105657 0.115489 0.125915 0.136934 0.148539 0.160719 0.173456 0.186727 0.200503 0.214748 0.229422 0.244476 0.259857 0.275504 0.291351 0.307328 0.323358 0.33936 0.35525 0.370939 0.386338 0.401355 0.415897 0.429871 0.443187 0.455755 0.46749 0.478309 0.488136 0.496901 0.504538 0.510992 0.516215 0.520167 0.522818 0.524149 0.524149 0.522818 0.520167 0.516215 0.510992 0.504538 0.496901 0.488136 0.478309 0.46749 0.455755 0.443187 0.429871 0.415897 0.401355 0.386338 0.370939 0.35525 0.33936 0.323358 0.307328 0.291351 0.275504 0.259857 0.244476 0.229422 0.214748 0.200503 0.186727 0.173456 0.160719 0.148539 0.136934 0.125915 0.115489 0.105657 0.0964163 0.0877608 0.0796794 0.0721585 0.0651816 0.0587298 0.0527823 0.0473166 0.0423092 0.0377357 0.0335711 0.0297903 0.0263682 0.0232799 0.0205011 0.0180082 0.0157783 0.0137894 0.0120206 0.0104521 0.00906514 0.00784229 0.00676717 0.00582462 +0.00549274 0.00638158 0.00739544 0.00854861 0.0098565 0.0113356 0.0130037 0.0148792 0.0169821 0.019333 0.0219534 0.0248657 0.0280928 0.0316582 0.0355855 0.0398984 0.0446205 0.0497748 0.0553834 0.0614676 0.068047 0.0751393 0.0827602 0.0909226 0.0996363 0.108908 0.11874 0.129131 0.140075 0.151561 0.163572 0.176087 0.189078 0.202512 0.21635 0.230546 0.24505 0.259806 0.27475 0.289817 0.304933 0.320023 0.335008 0.349803 0.364325 0.378486 0.392199 0.405377 0.417934 0.429786 0.440852 0.451055 0.460323 0.468587 0.47579 0.481876 0.486801 0.490528 0.493028 0.494283 0.494283 0.493028 0.490528 0.486801 0.481876 0.47579 0.468587 0.460323 0.451055 0.440852 0.429786 0.417934 0.405377 0.392199 0.378486 0.364325 0.349803 0.335008 0.320023 0.304933 0.289817 0.27475 0.259806 0.24505 0.230546 0.21635 0.202512 0.189078 0.176087 0.163572 0.151561 0.140075 0.129131 0.11874 0.108908 0.0996363 0.0909226 0.0827602 0.0751393 0.068047 0.0614676 0.0553834 0.0497748 0.0446205 0.0398984 0.0355855 0.0316582 0.0280928 0.0248657 0.0219534 0.019333 0.0169821 0.0148792 0.0130037 0.0113356 0.0098565 0.00854861 0.00739544 0.00638158 0.00549274 +0.00516657 0.00600263 0.00695628 0.00804097 0.0092712 0.0106625 0.0122315 0.0139957 0.0159737 0.0181849 0.0206498 0.0233891 0.0264246 0.0297783 0.0334724 0.0375292 0.0419709 0.046819 0.0520946 0.0578175 0.0640062 0.0706774 0.0778457 0.0855234 0.0937197 0.102441 0.111689 0.121463 0.131757 0.142561 0.153859 0.165631 0.17785 0.190486 0.203502 0.216856 0.230499 0.244378 0.258435 0.272607 0.286825 0.30102 0.315114 0.329031 0.34269 0.356011 0.368909 0.381305 0.393116 0.404265 0.414674 0.424271 0.432988 0.440762 0.447536 0.453261 0.457894 0.461399 0.463751 0.464932 0.464932 0.463751 0.461399 0.457894 0.453261 0.447536 0.440762 0.432988 0.424271 0.414674 0.404265 0.393116 0.381305 0.368909 0.356011 0.34269 0.329031 0.315114 0.30102 0.286825 0.272607 0.258435 0.244378 0.230499 0.216856 0.203502 0.190486 0.17785 0.165631 0.153859 0.142561 0.131757 0.121463 0.111689 0.102441 0.0937197 0.0855234 0.0778457 0.0706774 0.0640062 0.0578175 0.0520946 0.046819 0.0419709 0.0375292 0.0334724 0.0297783 0.0264246 0.0233891 0.0206498 0.0181849 0.0159737 0.0139957 0.0122315 0.0106625 0.0092712 0.00804097 0.00695628 0.00600263 0.00516657 +0.00484739 0.0056318 0.00652653 0.00754421 0.00869844 0.0100038 0.0114758 0.013131 0.0149869 0.0170615 0.0193741 0.0219442 0.0247922 0.0279386 0.0314045 0.0352107 0.039378 0.0439266 0.0488763 0.0542457 0.060052 0.066311 0.0730365 0.0802399 0.0879299 0.0961122 0.104789 0.11396 0.123618 0.133754 0.144354 0.155398 0.166863 0.178718 0.19093 0.203459 0.216259 0.229281 0.242469 0.255766 0.269106 0.282423 0.295647 0.308704 0.32152 0.334017 0.346119 0.357749 0.36883 0.37929 0.389056 0.39806 0.406238 0.413532 0.419888 0.42526 0.429606 0.432895 0.435102 0.436209 0.436209 0.435102 0.432895 0.429606 0.42526 0.419888 0.413532 0.406238 0.39806 0.389056 0.37929 0.36883 0.357749 0.346119 0.334017 0.32152 0.308704 0.295647 0.282423 0.269106 0.255766 0.242469 0.229281 0.216259 0.203459 0.19093 0.178718 0.166863 0.155398 0.144354 0.133754 0.123618 0.11396 0.104789 0.0961122 0.0879299 0.0802399 0.0730365 0.066311 0.060052 0.0542457 0.0488763 0.0439266 0.039378 0.0352107 0.0314045 0.0279386 0.0247922 0.0219442 0.0193741 0.0170615 0.0149869 0.013131 0.0114758 0.0100038 0.00869844 0.00754421 0.00652653 0.0056318 0.00484739 +0.00453634 0.00527041 0.00610774 0.00706011 0.00814028 0.00936187 0.0107394 0.0122884 0.0140252 0.0159667 0.0181309 0.0205361 0.0232013 0.0261459 0.0293893 0.0329513 0.0368512 0.0411079 0.04574 0.0507648 0.0561986 0.062056 0.0683499 0.075091 0.0822876 0.0899449 0.0980651 0.106647 0.115685 0.125171 0.135091 0.145427 0.156156 0.16725 0.178679 0.190403 0.202382 0.214568 0.22691 0.239353 0.251838 0.264301 0.276676 0.288895 0.300888 0.312583 0.323909 0.334792 0.345163 0.354952 0.364091 0.372517 0.380171 0.386997 0.392945 0.397971 0.402039 0.405117 0.407182 0.408218 0.408218 0.407182 0.405117 0.402039 0.397971 0.392945 0.386997 0.380171 0.372517 0.364091 0.354952 0.345163 0.334792 0.323909 0.312583 0.300888 0.288895 0.276676 0.264301 0.251838 0.239353 0.22691 0.214568 0.202382 0.190403 0.178679 0.16725 0.156156 0.145427 0.135091 0.125171 0.115685 0.106647 0.0980651 0.0899449 0.0822876 0.075091 0.0683499 0.062056 0.0561986 0.0507648 0.04574 0.0411079 0.0368512 0.0329513 0.0293893 0.0261459 0.0232013 0.0205361 0.0181309 0.0159667 0.0140252 0.0122884 0.0107394 0.00936187 0.00814028 0.00706011 0.00610774 0.00527041 0.00453634 +0.00423443 0.00491966 0.00570125 0.00659025 0.00759852 0.00873881 0.0100247 0.0114706 0.0130918 0.0149041 0.0169242 0.0191693 0.0216572 0.0244058 0.0274334 0.0307583 0.0343986 0.0383721 0.0426959 0.0473863 0.0524584 0.057926 0.063801 0.0700935 0.0768111 0.0839588 0.0915386 0.0995493 0.107986 0.116841 0.1261 0.135748 0.145763 0.156119 0.166787 0.177731 0.188913 0.200288 0.211809 0.223424 0.235077 0.246711 0.258262 0.269668 0.280863 0.29178 0.302352 0.312511 0.322192 0.331329 0.33986 0.347725 0.354869 0.361241 0.366793 0.371485 0.375282 0.378155 0.380083 0.38105 0.38105 0.380083 0.378155 0.375282 0.371485 0.366793 0.361241 0.354869 0.347725 0.33986 0.331329 0.322192 0.312511 0.302352 0.29178 0.280863 0.269668 0.258262 0.246711 0.235077 0.223424 0.211809 0.200288 0.188913 0.177731 0.166787 0.156119 0.145763 0.135748 0.1261 0.116841 0.107986 0.0995493 0.0915386 0.0839588 0.0768111 0.0700935 0.063801 0.057926 0.0524584 0.0473863 0.0426959 0.0383721 0.0343986 0.0307583 0.0274334 0.0244058 0.0216572 0.0191693 0.0169242 0.0149041 0.0130918 0.0114706 0.0100247 0.00873881 0.00759852 0.00659025 0.00570125 0.00491966 0.00423443 +0.00394255 0.00458054 0.00530826 0.00613598 0.00707475 0.00813644 0.0093337 0.0106799 0.0121893 0.0138767 0.0157576 0.017848 0.0201643 0.0227235 0.0255424 0.0286381 0.0320275 0.0357271 0.0397529 0.0441199 0.0488424 0.0539331 0.0594032 0.065262 0.0715165 0.0781715 0.0852288 0.0926873 0.100543 0.108787 0.117408 0.126391 0.135716 0.145358 0.15529 0.16548 0.175891 0.186482 0.197209 0.208023 0.218873 0.229705 0.24046 0.25108 0.261503 0.271668 0.281511 0.29097 0.299983 0.30849 0.316433 0.323756 0.330408 0.33634 0.34151 0.345879 0.349414 0.352089 0.353884 0.354784 0.354784 0.353884 0.352089 0.349414 0.345879 0.34151 0.33634 0.330408 0.323756 0.316433 0.30849 0.299983 0.29097 0.281511 0.271668 0.261503 0.25108 0.24046 0.229705 0.218873 0.208023 0.197209 0.186482 0.175891 0.16548 0.15529 0.145358 0.135716 0.126391 0.117408 0.108787 0.100543 0.0926873 0.0852288 0.0781715 0.0715165 0.065262 0.0594032 0.0539331 0.0488424 0.0441199 0.0397529 0.0357271 0.0320275 0.0286381 0.0255424 0.0227235 0.0201643 0.017848 0.0157576 0.0138767 0.0121893 0.0106799 0.0093337 0.00813644 0.00707475 0.00613598 0.00530826 0.00458054 0.00394255 +0.00366144 0.00425394 0.00492977 0.00569847 0.0065703 0.00755629 0.00866818 0.00991844 0.0113202 0.0128873 0.0146341 0.0165754 0.0187266 0.0211032 0.0237212 0.0265962 0.0297439 0.0331797 0.0369184 0.0409741 0.0453598 0.0500876 0.0551676 0.0606086 0.0664172 0.0725977 0.0791518 0.0860785 0.0933737 0.10103 0.109037 0.117379 0.126039 0.134994 0.144218 0.153681 0.16335 0.173185 0.183147 0.193191 0.203267 0.213326 0.223315 0.233177 0.242857 0.252297 0.261438 0.270223 0.278593 0.286494 0.293871 0.300672 0.306849 0.312359 0.31716 0.321217 0.3245 0.326984 0.328651 0.329487 0.329487 0.328651 0.326984 0.3245 0.321217 0.31716 0.312359 0.306849 0.300672 0.293871 0.286494 0.278593 0.270223 0.261438 0.252297 0.242857 0.233177 0.223315 0.213326 0.203267 0.193191 0.183147 0.173185 0.16335 0.153681 0.144218 0.134994 0.126039 0.117379 0.109037 0.10103 0.0933737 0.0860785 0.0791518 0.0725977 0.0664172 0.0606086 0.0551676 0.0500876 0.0453598 0.0409741 0.0369184 0.0331797 0.0297439 0.0265962 0.0237212 0.0211032 0.0187266 0.0165754 0.0146341 0.0128873 0.0113202 0.00991844 0.00866818 0.00755629 0.0065703 0.00569847 0.00492977 0.00425394 0.00366144 +0.00339171 0.00394056 0.0045666 0.00527867 0.00608628 0.00699964 0.00802961 0.00918777 0.0104863 0.0119379 0.013556 0.0153543 0.017347 0.0195486 0.0219737 0.0246369 0.0275527 0.0307354 0.0341987 0.0379556 0.0420183 0.0463977 0.0511035 0.0561437 0.0615244 0.0672495 0.0733208 0.0797373 0.0864951 0.0935874 0.101004 0.108732 0.116754 0.125049 0.133594 0.14236 0.151316 0.160427 0.169655 0.178959 0.188293 0.197611 0.206864 0.216 0.224967 0.233711 0.242179 0.250316 0.25807 0.265388 0.272222 0.278522 0.284244 0.289348 0.293795 0.297553 0.300594 0.302896 0.30444 0.305215 0.305215 0.30444 0.302896 0.300594 0.297553 0.293795 0.289348 0.284244 0.278522 0.272222 0.265388 0.25807 0.250316 0.242179 0.233711 0.224967 0.216 0.206864 0.197611 0.188293 0.178959 0.169655 0.160427 0.151316 0.14236 0.133594 0.125049 0.116754 0.108732 0.101004 0.0935874 0.0864951 0.0797373 0.0733208 0.0672495 0.0615244 0.0561437 0.0511035 0.0463977 0.0420183 0.0379556 0.0341987 0.0307354 0.0275527 0.0246369 0.0219737 0.0195486 0.017347 0.0153543 0.013556 0.0119379 0.0104863 0.00918777 0.00802961 0.00699964 0.00608628 0.00527867 0.0045666 0.00394056 0.00339171 +0.00313384 0.00364097 0.00421941 0.00487735 0.00562355 0.00646747 0.00741914 0.00848924 0.00968902 0.0110303 0.0125254 0.0141869 0.0160282 0.0180624 0.0203031 0.0227638 0.0254579 0.0283986 0.0315986 0.0350699 0.0388237 0.0428702 0.0472182 0.0518752 0.0568468 0.0621367 0.0677464 0.073675 0.079919 0.0864721 0.093325 0.100465 0.107877 0.115542 0.123437 0.131536 0.139812 0.14823 0.156757 0.165353 0.173977 0.182587 0.191136 0.199578 0.207863 0.215942 0.223766 0.231285 0.238449 0.245211 0.251525 0.257346 0.262634 0.267349 0.271458 0.274931 0.277741 0.279867 0.281294 0.28201 0.28201 0.281294 0.279867 0.277741 0.274931 0.271458 0.267349 0.262634 0.257346 0.251525 0.245211 0.238449 0.231285 0.223766 0.215942 0.207863 0.199578 0.191136 0.182587 0.173977 0.165353 0.156757 0.14823 0.139812 0.131536 0.123437 0.115542 0.107877 0.100465 0.093325 0.0864721 0.079919 0.073675 0.0677464 0.0621367 0.0568468 0.0518752 0.0472182 0.0428702 0.0388237 0.0350699 0.0315986 0.0283986 0.0254579 0.0227638 0.0203031 0.0180624 0.0160282 0.0141869 0.0125254 0.0110303 0.00968902 0.00848924 0.00741914 0.00646747 0.00562355 0.00487735 0.00421941 0.00364097 0.00313384 +0.0028882 0.00335558 0.00388869 0.00449505 0.00518277 0.00596053 0.00683761 0.00782384 0.00892958 0.0101657 0.0115436 0.0130749 0.0147718 0.0166466 0.0187117 0.0209795 0.0234625 0.0261727 0.0291219 0.0323211 0.0357806 0.0395099 0.0435172 0.0478091 0.052391 0.0572663 0.0624363 0.0679002 0.0736548 0.0796942 0.08601 0.0925905 0.0994215 0.106485 0.113762 0.121226 0.128853 0.136612 0.14447 0.152392 0.160341 0.168275 0.176155 0.183934 0.19157 0.199016 0.206227 0.213156 0.219759 0.225991 0.23181 0.237175 0.242048 0.246394 0.250181 0.253381 0.255971 0.257931 0.259245 0.259905 0.259905 0.259245 0.257931 0.255971 0.253381 0.250181 0.246394 0.242048 0.237175 0.23181 0.225991 0.219759 0.213156 0.206227 0.199016 0.19157 0.183934 0.176155 0.168275 0.160341 0.152392 0.14447 0.136612 0.128853 0.121226 0.113762 0.106485 0.0994215 0.0925905 0.08601 0.0796942 0.0736548 0.0679002 0.0624363 0.0572663 0.052391 0.0478091 0.0435172 0.0395099 0.0357806 0.0323211 0.0291219 0.0261727 0.0234625 0.0209795 0.0187117 0.0166466 0.0147718 0.0130749 0.0115436 0.0101657 0.00892958 0.00782384 0.00683761 0.00596053 0.00518277 0.00449505 0.00388869 0.00335558 0.0028882 +0.00265504 0.00308468 0.00357475 0.00413217 0.00476437 0.00547934 0.00628561 0.00719222 0.00820869 0.00934504 0.0106117 0.0120194 0.0135793 0.0153027 0.0172011 0.0192858 0.0215684 0.0240598 0.0267709 0.0297118 0.0328921 0.0363203 0.040004 0.0439495 0.0481615 0.0526432 0.0573958 0.0624187 0.0677087 0.0732606 0.0790664 0.0851157 0.0913952 0.0978888 0.104578 0.11144 0.118451 0.125583 0.132807 0.14009 0.147396 0.154691 0.161934 0.169085 0.176105 0.18295 0.189578 0.195948 0.202018 0.207747 0.213096 0.218028 0.222508 0.226503 0.229984 0.232926 0.235307 0.237108 0.238317 0.238923 0.238923 0.238317 0.237108 0.235307 0.232926 0.229984 0.226503 0.222508 0.218028 0.213096 0.207747 0.202018 0.195948 0.189578 0.18295 0.176105 0.169085 0.161934 0.154691 0.147396 0.14009 0.132807 0.125583 0.118451 0.11144 0.104578 0.0978888 0.0913952 0.0851157 0.0790664 0.0732606 0.0677087 0.0624187 0.0573958 0.0526432 0.0481615 0.0439495 0.040004 0.0363203 0.0328921 0.0297118 0.0267709 0.0240598 0.0215684 0.0192858 0.0172011 0.0153027 0.0135793 0.0120194 0.0106117 0.00934504 0.00820869 0.00719222 0.00628561 0.00547934 0.00476437 0.00413217 0.00357475 0.00308468 0.00265504 +0.00243448 0.00282844 0.00327779 0.0037889 0.00436858 0.00502416 0.00576346 0.00659475 0.00752679 0.00856873 0.00973015 0.0110209 0.0124513 0.0140315 0.0157722 0.0176837 0.0197766 0.0220611 0.024547 0.0272436 0.0301597 0.0333031 0.0366808 0.0402986 0.0441607 0.0482701 0.0526279 0.0572334 0.062084 0.0671747 0.0724982 0.078045 0.0838029 0.089757 0.0958902 0.102182 0.108611 0.115151 0.121774 0.128452 0.135152 0.14184 0.148482 0.155039 0.161475 0.167752 0.17383 0.179671 0.185236 0.190489 0.195394 0.199916 0.204023 0.207687 0.210879 0.213576 0.215759 0.217411 0.218519 0.219075 0.219075 0.218519 0.217411 0.215759 0.213576 0.210879 0.207687 0.204023 0.199916 0.195394 0.190489 0.185236 0.179671 0.17383 0.167752 0.161475 0.155039 0.148482 0.14184 0.135152 0.128452 0.121774 0.115151 0.108611 0.102182 0.0958902 0.089757 0.0838029 0.078045 0.0724982 0.0671747 0.062084 0.0572334 0.0526279 0.0482701 0.0441607 0.0402986 0.0366808 0.0333031 0.0301597 0.0272436 0.024547 0.0220611 0.0197766 0.0176837 0.0157722 0.0140315 0.0124513 0.0110209 0.00973015 0.00856873 0.00752679 0.00659475 0.00576346 0.00502416 0.00436858 0.0037889 0.00327779 0.00282844 0.00243448 +0.00222656 0.00258687 0.00299785 0.0034653 0.00399547 0.00459506 0.00527122 0.00603151 0.00688394 0.0078369 0.00889912 0.0100797 0.0113878 0.0128331 0.0144251 0.0161734 0.0180876 0.0201769 0.0224505 0.0249168 0.0275838 0.0304588 0.033548 0.0368568 0.040389 0.0441474 0.0481331 0.0523453 0.0567816 0.0614375 0.0663064 0.0713794 0.0766455 0.0820911 0.0877004 0.0934552 0.0993346 0.105316 0.111374 0.117481 0.123609 0.129726 0.1358 0.141798 0.147684 0.153425 0.158983 0.164325 0.169416 0.17422 0.178706 0.182842 0.186598 0.189949 0.192868 0.195335 0.197332 0.198843 0.199856 0.200365 0.200365 0.199856 0.198843 0.197332 0.195335 0.192868 0.189949 0.186598 0.182842 0.178706 0.17422 0.169416 0.164325 0.158983 0.153425 0.147684 0.141798 0.1358 0.129726 0.123609 0.117481 0.111374 0.105316 0.0993346 0.0934552 0.0877004 0.0820911 0.0766455 0.0713794 0.0663064 0.0614375 0.0567816 0.0523453 0.0481331 0.0441474 0.040389 0.0368568 0.033548 0.0304588 0.0275838 0.0249168 0.0224505 0.0201769 0.0180876 0.0161734 0.0144251 0.0128331 0.0113878 0.0100797 0.00889912 0.0078369 0.00688394 0.00603151 0.00527122 0.00459506 0.00399547 0.0034653 0.00299785 0.00258687 0.00222656 +0.00203121 0.0023599 0.00273482 0.00316126 0.00364492 0.00419191 0.00480873 0.00550232 0.00627996 0.00714931 0.00811834 0.00919531 0.0103887 0.0117072 0.0131595 0.0147544 0.0165006 0.0184066 0.0204807 0.0227306 0.0251637 0.0277864 0.0306046 0.033623 0.0368454 0.0402741 0.04391 0.0477526 0.0517997 0.0560471 0.0604888 0.0651168 0.0699208 0.0748887 0.0800058 0.0852557 0.0906193 0.0960758 0.101602 0.107174 0.112764 0.118344 0.123885 0.129357 0.134727 0.139964 0.145035 0.149908 0.154552 0.158934 0.163027 0.1668 0.170227 0.173283 0.175946 0.178197 0.180018 0.181397 0.182321 0.182785 0.182785 0.182321 0.181397 0.180018 0.178197 0.175946 0.173283 0.170227 0.1668 0.163027 0.158934 0.154552 0.149908 0.145035 0.139964 0.134727 0.129357 0.123885 0.118344 0.112764 0.107174 0.101602 0.0960758 0.0906193 0.0852557 0.0800058 0.0748887 0.0699208 0.0651168 0.0604888 0.0560471 0.0517997 0.0477526 0.04391 0.0402741 0.0368454 0.033623 0.0306046 0.0277864 0.0251637 0.0227306 0.0204807 0.0184066 0.0165006 0.0147544 0.0131595 0.0117072 0.0103887 0.00919531 0.00811834 0.00714931 0.00627996 0.00550232 0.00480873 0.00419191 0.00364492 0.00316126 0.00273482 0.0023599 0.00203121 +0.00184827 0.00214736 0.00248852 0.00287656 0.00331665 0.00381438 0.00437565 0.00500677 0.00571438 0.00650543 0.00738719 0.00836716 0.00945308 0.0106528 0.0119743 0.0134256 0.0150145 0.0167489 0.0186362 0.0206835 0.0228974 0.0252839 0.0278483 0.0305949 0.033527 0.0366469 0.0399554 0.043452 0.0471346 0.0509994 0.0550411 0.0592523 0.0636236 0.0681441 0.0728004 0.0775774 0.082458 0.0874231 0.0924518 0.0975216 0.102608 0.107686 0.112728 0.117707 0.122593 0.127358 0.131973 0.136407 0.140632 0.144621 0.148344 0.151777 0.154896 0.157677 0.1601 0.162148 0.163806 0.16506 0.165901 0.166323 0.166323 0.165901 0.16506 0.163806 0.162148 0.1601 0.157677 0.154896 0.151777 0.148344 0.144621 0.140632 0.136407 0.131973 0.127358 0.122593 0.117707 0.112728 0.107686 0.102608 0.0975216 0.0924518 0.0874231 0.082458 0.0775774 0.0728004 0.0681441 0.0636236 0.0592523 0.0550411 0.0509994 0.0471346 0.043452 0.0399554 0.0366469 0.033527 0.0305949 0.0278483 0.0252839 0.0228974 0.0206835 0.0186362 0.0167489 0.0150145 0.0134256 0.0119743 0.0106528 0.00945308 0.00836716 0.00738719 0.00650543 0.00571438 0.00500677 0.00437565 0.00381438 0.00331665 0.00287656 0.00248852 0.00214736 0.00184827 +0.00167753 0.00194899 0.00225863 0.00261082 0.00301026 0.00346201 0.00397143 0.00454425 0.00518649 0.00590447 0.00670477 0.00759421 0.00857981 0.00966871 0.0108681 0.0121853 0.0136275 0.0152017 0.0169146 0.0187728 0.0207822 0.0229482 0.0252757 0.0277686 0.0304298 0.0332615 0.0362643 0.0394379 0.0427803 0.0462881 0.0499564 0.0537786 0.0577461 0.061849 0.0660751 0.0704109 0.0748406 0.079347 0.0839112 0.0885126 0.0931293 0.097738 0.102314 0.106833 0.111268 0.115593 0.119781 0.123806 0.127641 0.131261 0.13464 0.137756 0.140587 0.143111 0.14531 0.147169 0.148673 0.149812 0.150575 0.150959 0.150959 0.150575 0.149812 0.148673 0.147169 0.14531 0.143111 0.140587 0.137756 0.13464 0.131261 0.127641 0.123806 0.119781 0.115593 0.111268 0.106833 0.102314 0.097738 0.0931293 0.0885126 0.0839112 0.079347 0.0748406 0.0704109 0.0660751 0.061849 0.0577461 0.0537786 0.0499564 0.0462881 0.0427803 0.0394379 0.0362643 0.0332615 0.0304298 0.0277686 0.0252757 0.0229482 0.0207822 0.0187728 0.0169146 0.0152017 0.0136275 0.0121853 0.0108681 0.00966871 0.00857981 0.00759421 0.00670477 0.00590447 0.00518649 0.00454425 0.00397143 0.00346201 0.00301026 0.00261082 0.00225863 0.00194899 0.00167753 +0.00151868 0.00176444 0.00204476 0.0023636 0.00272522 0.00313418 0.00359537 0.00411395 0.00469537 0.00534536 0.00606988 0.0068751 0.00776737 0.00875316 0.00983902 0.0110315 0.0123371 0.0137622 0.0153129 0.0169951 0.0188143 0.0207752 0.0228823 0.0251391 0.0275484 0.0301119 0.0328304 0.0357035 0.0387294 0.041905 0.045226 0.0486862 0.0522781 0.0559924 0.0598184 0.0637435 0.0677538 0.0718335 0.0759655 0.0801312 0.0843107 0.088483 0.092626 0.0967168 0.100732 0.104647 0.108439 0.112082 0.115554 0.118831 0.121891 0.124712 0.127274 0.129559 0.131551 0.133234 0.134595 0.135626 0.136317 0.136664 0.136664 0.136317 0.135626 0.134595 0.133234 0.131551 0.129559 0.127274 0.124712 0.121891 0.118831 0.115554 0.112082 0.108439 0.104647 0.100732 0.0967168 0.092626 0.088483 0.0843107 0.0801312 0.0759655 0.0718335 0.0677538 0.0637435 0.0598184 0.0559924 0.0522781 0.0486862 0.045226 0.041905 0.0387294 0.0357035 0.0328304 0.0301119 0.0275484 0.0251391 0.0228823 0.0207752 0.0188143 0.0169951 0.0153129 0.0137622 0.0123371 0.0110315 0.00983902 0.00875316 0.00776737 0.0068751 0.00606988 0.00534536 0.00469537 0.00411395 0.00359537 0.00313418 0.00272522 0.0023636 0.00204476 0.00176444 0.00151868 +0.00137137 0.00159329 0.00184642 0.00213433 0.00246088 0.00283017 0.00324663 0.0037149 0.00423993 0.00482687 0.00548111 0.00620823 0.00701395 0.00790412 0.00888465 0.00996146 0.0111404 0.0124273 0.0138276 0.0153466 0.0169893 0.0187601 0.0206628 0.0227007 0.0248763 0.0271911 0.0296459 0.0322403 0.0349727 0.0378403 0.0408392 0.0439637 0.0472072 0.0505613 0.0540161 0.0575605 0.0611818 0.0648658 0.068597 0.0723586 0.0761328 0.0799004 0.0836415 0.0873355 0.0909611 0.0944967 0.0979205 0.101211 0.104346 0.107305 0.110068 0.112615 0.114929 0.116992 0.118791 0.12031 0.12154 0.12247 0.123095 0.123408 0.123408 0.123095 0.12247 0.12154 0.12031 0.118791 0.116992 0.114929 0.112615 0.110068 0.107305 0.104346 0.101211 0.0979205 0.0944967 0.0909611 0.0873355 0.0836415 0.0799004 0.0761328 0.0723586 0.068597 0.0648658 0.0611818 0.0575605 0.0540161 0.0505613 0.0472072 0.0439637 0.0408392 0.0378403 0.0349727 0.0322403 0.0296459 0.0271911 0.0248763 0.0227007 0.0206628 0.0187601 0.0169893 0.0153466 0.0138276 0.0124273 0.0111404 0.00996146 0.00888465 0.00790412 0.00701395 0.00620823 0.00548111 0.00482687 0.00423993 0.0037149 0.00324663 0.00283017 0.00246088 0.00213433 0.00184642 0.00159329 0.00137137 +0.0012352 0.00143508 0.00166308 0.0019224 0.00221652 0.00254914 0.00292424 0.00334602 0.00381891 0.00434757 0.00493685 0.00559176 0.00631748 0.00711926 0.00800242 0.00897231 0.0100342 0.0111933 0.0124545 0.0138227 0.0153023 0.0168972 0.018611 0.0204465 0.0224061 0.0244911 0.0267021 0.0290389 0.0315 0.0340828 0.0367839 0.0395982 0.0425196 0.0455406 0.0486524 0.0518449 0.0551066 0.0584247 0.0617854 0.0651735 0.0685729 0.0719664 0.075336 0.0786632 0.0819288 0.0851133 0.0881971 0.0911606 0.0939845 0.0966497 0.0991383 0.101433 0.103517 0.105375 0.106995 0.108364 0.109471 0.110309 0.110872 0.111154 0.111154 0.110872 0.110309 0.109471 0.108364 0.106995 0.105375 0.103517 0.101433 0.0991383 0.0966497 0.0939845 0.0911606 0.0881971 0.0851133 0.0819288 0.0786632 0.075336 0.0719664 0.0685729 0.0651735 0.0617854 0.0584247 0.0551066 0.0518449 0.0486524 0.0455406 0.0425196 0.0395982 0.0367839 0.0340828 0.0315 0.0290389 0.0267021 0.0244911 0.0224061 0.0204465 0.018611 0.0168972 0.0153023 0.0138227 0.0124545 0.0111933 0.0100342 0.00897231 0.00800242 0.00711926 0.00631748 0.00559176 0.00493685 0.00434757 0.00381891 0.00334602 0.00292424 0.00254914 0.00221652 0.0019224 0.00166308 0.00143508 0.0012352 +0.00110971 0.00128929 0.00149412 0.0017271 0.00199133 0.00229017 0.00262716 0.00300609 0.00343094 0.00390589 0.0044353 0.00502368 0.00567567 0.00639599 0.00718943 0.00806078 0.0090148 0.0100561 0.0111893 0.0124185 0.0137477 0.0151806 0.0167202 0.0183693 0.0201298 0.022003 0.0239894 0.0260887 0.0282998 0.0306203 0.0330469 0.0355753 0.0381999 0.040914 0.0437097 0.0465778 0.0495081 0.0524892 0.0555085 0.0585524 0.0616064 0.0646551 0.0676824 0.0706716 0.0736054 0.0764664 0.0792369 0.0818994 0.0844363 0.0868308 0.0890665 0.0911278 0.0930001 0.0946699 0.096125 0.0973546 0.0983497 0.0991026 0.0996078 0.0998613 0.0998613 0.0996078 0.0991026 0.0983497 0.0973546 0.096125 0.0946699 0.0930001 0.0911278 0.0890665 0.0868308 0.0844363 0.0818994 0.0792369 0.0764664 0.0736054 0.0706716 0.0676824 0.0646551 0.0616064 0.0585524 0.0555085 0.0524892 0.0495081 0.0465778 0.0437097 0.040914 0.0381999 0.0355753 0.0330469 0.0306203 0.0282998 0.0260887 0.0239894 0.022003 0.0201298 0.0183693 0.0167202 0.0151806 0.0137477 0.0124185 0.0111893 0.0100561 0.0090148 0.00806078 0.00718943 0.00639599 0.00567567 0.00502368 0.0044353 0.00390589 0.00343094 0.00300609 0.00262716 0.00229017 0.00199133 0.0017271 0.00149412 0.00128929 0.00110971 +0.000994433 0.00115535 0.00133891 0.00154768 0.00178447 0.00205226 0.00235425 0.00269381 0.00307453 0.00350014 0.00397455 0.00450181 0.00508607 0.00573156 0.00644258 0.00722342 0.00807833 0.00901148 0.0100269 0.0111284 0.0123196 0.0136036 0.0149833 0.0164611 0.0180387 0.0197173 0.0214973 0.0233786 0.02536 0.0274394 0.0296139 0.0318797 0.0342316 0.0366638 0.039169 0.0417392 0.0443651 0.0470365 0.0497422 0.0524699 0.0552066 0.0579386 0.0606515 0.0633301 0.0659592 0.068523 0.0710057 0.0733915 0.0756649 0.0778107 0.0798142 0.0816613 0.0833391 0.0848355 0.0861394 0.0872413 0.0881329 0.0888077 0.0892604 0.0894876 0.0894876 0.0892604 0.0888077 0.0881329 0.0872413 0.0861394 0.0848355 0.0833391 0.0816613 0.0798142 0.0778107 0.0756649 0.0733915 0.0710057 0.068523 0.0659592 0.0633301 0.0606515 0.0579386 0.0552066 0.0524699 0.0497422 0.0470365 0.0443651 0.0417392 0.039169 0.0366638 0.0342316 0.0318797 0.0296139 0.0274394 0.02536 0.0233786 0.0214973 0.0197173 0.0180387 0.0164611 0.0149833 0.0136036 0.0123196 0.0111284 0.0100269 0.00901148 0.00807833 0.00722342 0.00644258 0.00573156 0.00508607 0.00450181 0.00397455 0.00350014 0.00307453 0.00269381 0.00235425 0.00205226 0.00178447 0.00154768 0.00133891 0.00115535 0.000994433 +0.00088886 0.0010327 0.00119676 0.00138337 0.00159502 0.00183438 0.00210431 0.00240782 0.00274812 0.00312855 0.0035526 0.00402388 0.00454611 0.00512308 0.00575861 0.00645655 0.0072207 0.00805478 0.0089624 0.00994697 0.0110117 0.0121594 0.0133926 0.0147135 0.0161236 0.017624 0.0192151 0.0208966 0.0226676 0.0245263 0.02647 0.0284952 0.0305975 0.0327714 0.0350107 0.037308 0.0396551 0.0420429 0.0444613 0.0468994 0.0493456 0.0517876 0.0542124 0.0566067 0.0589567 0.0612483 0.0634674 0.0656 0.067632 0.06955 0.0713407 0.0729918 0.0744915 0.0758289 0.0769944 0.0779794 0.0787764 0.0793795 0.0797841 0.0799872 0.0799872 0.0797841 0.0793795 0.0787764 0.0779794 0.0769944 0.0758289 0.0744915 0.0729918 0.0713407 0.06955 0.067632 0.0656 0.0634674 0.0612483 0.0589567 0.0566067 0.0542124 0.0517876 0.0493456 0.0468994 0.0444613 0.0420429 0.0396551 0.037308 0.0350107 0.0327714 0.0305975 0.0284952 0.02647 0.0245263 0.0226676 0.0208966 0.0192151 0.017624 0.0161236 0.0147135 0.0133926 0.0121594 0.0110117 0.00994697 0.0089624 0.00805478 0.0072207 0.00645655 0.00575861 0.00512308 0.00454611 0.00402388 0.0035526 0.00312855 0.00274812 0.00240782 0.00210431 0.00183438 0.00159502 0.00138337 0.00119676 0.0010327 0.00088886 +0.00079247 0.000920709 0.00106698 0.00123336 0.00142206 0.00163546 0.00187612 0.00214672 0.00245011 0.00278929 0.00316735 0.00358753 0.00405312 0.00456752 0.00513414 0.00575639 0.00643767 0.00718131 0.0079905 0.0088683 0.00981755 0.0108408 0.0119403 0.0131179 0.0143751 0.0157128 0.0171314 0.0186306 0.0202095 0.0218666 0.0235996 0.0254051 0.0272794 0.0292176 0.0312141 0.0332623 0.0353549 0.0374837 0.0396399 0.0418136 0.0439945 0.0461717 0.0483336 0.0504682 0.0525633 0.0546064 0.0565849 0.0584862 0.0602979 0.0620079 0.0636044 0.0650765 0.0664135 0.0676059 0.0686451 0.0695232 0.0702337 0.0707714 0.0711322 0.0713132 0.0713132 0.0711322 0.0707714 0.0702337 0.0695232 0.0686451 0.0676059 0.0664135 0.0650765 0.0636044 0.0620079 0.0602979 0.0584862 0.0565849 0.0546064 0.0525633 0.0504682 0.0483336 0.0461717 0.0439945 0.0418136 0.0396399 0.0374837 0.0353549 0.0332623 0.0312141 0.0292176 0.0272794 0.0254051 0.0235996 0.0218666 0.0202095 0.0186306 0.0171314 0.0157128 0.0143751 0.0131179 0.0119403 0.0108408 0.00981755 0.0088683 0.0079905 0.00718131 0.00643767 0.00575639 0.00513414 0.00456752 0.00405312 0.00358753 0.00316735 0.00278929 0.00245011 0.00214672 0.00187612 0.00163546 0.00142206 0.00123336 0.00106698 0.000920709 0.00079247 +0.000704734 0.000818775 0.000948856 0.00109681 0.00126462 0.00145439 0.00166841 0.00190905 0.00217885 0.00248048 0.00281668 0.00319034 0.00360439 0.00406184 0.00456572 0.00511908 0.00572494 0.00638624 0.00710585 0.00788647 0.00873062 0.00964058 0.0106184 0.0116656 0.0127836 0.0139732 0.0152347 0.0165679 0.0179721 0.0194457 0.0209868 0.0225925 0.0242592 0.0259829 0.0277583 0.0295797 0.0314406 0.0333338 0.0352512 0.0371843 0.0391238 0.0410599 0.0429824 0.0448807 0.0467439 0.0485608 0.0503202 0.052011 0.0536221 0.0551428 0.0565626 0.0578717 0.0590607 0.0601211 0.0610452 0.0618261 0.062458 0.0629361 0.0632569 0.063418 0.063418 0.0632569 0.0629361 0.062458 0.0618261 0.0610452 0.0601211 0.0590607 0.0578717 0.0565626 0.0551428 0.0536221 0.052011 0.0503202 0.0485608 0.0467439 0.0448807 0.0429824 0.0410599 0.0391238 0.0371843 0.0352512 0.0333338 0.0314406 0.0295797 0.0277583 0.0259829 0.0242592 0.0225925 0.0209868 0.0194457 0.0179721 0.0165679 0.0152347 0.0139732 0.0127836 0.0116656 0.0106184 0.00964058 0.00873062 0.00788647 0.00710585 0.00638624 0.00572494 0.00511908 0.00456572 0.00406184 0.00360439 0.00319034 0.00281668 0.00248048 0.00217885 0.00190905 0.00166841 0.00145439 0.00126462 0.00109681 0.000948856 0.000818775 0.000704734 +0.000625114 0.000726271 0.000841656 0.000972895 0.00112174 0.00129008 0.00147991 0.00169337 0.00193269 0.00220024 0.00249846 0.0028299 0.00319717 0.00360294 0.0040499 0.00454074 0.00507815 0.00566474 0.00630304 0.00699547 0.00774425 0.00855141 0.00941872 0.0103477 0.0113394 0.0123945 0.0135135 0.0146961 0.0159416 0.0172488 0.0186157 0.02004 0.0215185 0.0230474 0.0246222 0.0262379 0.0278885 0.0295678 0.0312686 0.0329833 0.0347036 0.036421 0.0381264 0.0398102 0.0414629 0.0430745 0.0446351 0.0461349 0.047564 0.0489129 0.0501723 0.0513334 0.0523881 0.0533287 0.0541484 0.0548411 0.0554016 0.0558257 0.0561103 0.0562531 0.0562531 0.0561103 0.0558257 0.0554016 0.0548411 0.0541484 0.0533287 0.0523881 0.0513334 0.0501723 0.0489129 0.047564 0.0461349 0.0446351 0.0430745 0.0414629 0.0398102 0.0381264 0.036421 0.0347036 0.0329833 0.0312686 0.0295678 0.0278885 0.0262379 0.0246222 0.0230474 0.0215185 0.02004 0.0186157 0.0172488 0.0159416 0.0146961 0.0135135 0.0123945 0.0113394 0.0103477 0.00941872 0.00855141 0.00774425 0.00699547 0.00630304 0.00566474 0.00507815 0.00454074 0.0040499 0.00360294 0.00319717 0.0028299 0.00249846 0.00220024 0.00193269 0.00169337 0.00147991 0.00129008 0.00112174 0.000972895 0.000841656 0.000726271 0.000625114 +0.000553077 0.000642577 0.000744665 0.00086078 0.000992475 0.00114141 0.00130937 0.00149823 0.00170997 0.00194669 0.00221054 0.00250379 0.00282874 0.00318774 0.00358319 0.00401747 0.00449295 0.00501195 0.00557669 0.00618933 0.00685182 0.00756596 0.00833333 0.00915522 0.0100326 0.0109662 0.0119563 0.0130026 0.0141045 0.0152611 0.0164705 0.0177306 0.0190387 0.0203914 0.0217848 0.0232143 0.0246747 0.0261605 0.0276653 0.0291824 0.0307045 0.0322239 0.0337327 0.0352225 0.0366848 0.0381107 0.0394915 0.0408184 0.0420828 0.0432763 0.0443905 0.0454179 0.046351 0.0471832 0.0479084 0.0485213 0.0490172 0.0493925 0.0496442 0.0497706 0.0497706 0.0496442 0.0493925 0.0490172 0.0485213 0.0479084 0.0471832 0.046351 0.0454179 0.0443905 0.0432763 0.0420828 0.0408184 0.0394915 0.0381107 0.0366848 0.0352225 0.0337327 0.0322239 0.0307045 0.0291824 0.0276653 0.0261605 0.0246747 0.0232143 0.0217848 0.0203914 0.0190387 0.0177306 0.0164705 0.0152611 0.0141045 0.0130026 0.0119563 0.0109662 0.0100326 0.00915522 0.00833333 0.00756596 0.00685182 0.00618933 0.00557669 0.00501195 0.00449295 0.00401747 0.00358319 0.00318774 0.00282874 0.00250379 0.00221054 0.00194669 0.00170997 0.00149823 0.00130937 0.00114141 0.000992475 0.00086078 0.000744665 0.000642577 0.000553077 +0.000488095 0.00056708 0.000657173 0.000759646 0.000875868 0.00100731 0.00115553 0.0013222 0.00150906 0.00171797 0.00195082 0.00220961 0.00249638 0.00281321 0.0031622 0.00354545 0.00396507 0.00442308 0.00492148 0.00546213 0.00604678 0.00667702 0.00735423 0.00807955 0.00885388 0.00967778 0.0105515 0.0114749 0.0124474 0.013468 0.0145353 0.0156474 0.0168018 0.0179956 0.0192253 0.0204868 0.0217756 0.0230868 0.0244148 0.0257537 0.0270969 0.0284379 0.0297694 0.0310842 0.0323746 0.033633 0.0348516 0.0360226 0.0371384 0.0381916 0.039175 0.0400816 0.0409052 0.0416396 0.0422796 0.0428204 0.0432581 0.0435893 0.0438115 0.043923 0.043923 0.0438115 0.0435893 0.0432581 0.0428204 0.0422796 0.0416396 0.0409052 0.0400816 0.039175 0.0381916 0.0371384 0.0360226 0.0348516 0.033633 0.0323746 0.0310842 0.0297694 0.0284379 0.0270969 0.0257537 0.0244148 0.0230868 0.0217756 0.0204868 0.0192253 0.0179956 0.0168018 0.0156474 0.0145353 0.013468 0.0124474 0.0114749 0.0105515 0.00967778 0.00885388 0.00807955 0.00735423 0.00667702 0.00604678 0.00546213 0.00492148 0.00442308 0.00396507 0.00354545 0.0031622 0.00281321 0.00249638 0.00220961 0.00195082 0.00171797 0.00150906 0.0013222 0.00115553 0.00100731 0.000875868 0.000759646 0.000657173 0.00056708 0.000488095 diff --git a/examples/basic/data_heatmap1.z b/examples/basic/data_heatmap1.z new file mode 100644 index 0000000..c5a0427 --- /dev/null +++ b/examples/basic/data_heatmap1.z @@ -0,0 +1,101 @@ +! nx 120 ny 100 xmin -3 xmax 3 ymin -2.5 ymax 2.5 +0.000488095 0.00056708 0.000657173 0.000759646 0.000875868 0.00100731 0.00115553 0.0013222 0.00150906 0.00171797 0.00195082 0.00220961 0.00249638 0.00281321 0.0031622 0.00354545 0.00396507 0.00442308 0.00492148 0.00546213 0.00604678 0.00667702 0.00735423 0.00807955 0.00885388 0.00967778 0.0105515 0.0114749 0.0124474 0.013468 0.0145353 0.0156474 0.0168018 0.0179956 0.0192253 0.0204868 0.0217756 0.0230868 0.0244148 0.0257537 0.0270969 0.0284379 0.0297694 0.0310842 0.0323746 0.033633 0.0348516 0.0360226 0.0371384 0.0381916 0.039175 0.0400816 0.0409052 0.0416396 0.0422796 0.0428204 0.0432581 0.0435893 0.0438115 0.043923 0.043923 0.0438115 0.0435893 0.0432581 0.0428204 0.0422796 0.0416396 0.0409052 0.0400816 0.039175 0.0381916 0.0371384 0.0360226 0.0348516 0.033633 0.0323746 0.0310842 0.0297694 0.0284379 0.0270969 0.0257537 0.0244148 0.0230868 0.0217756 0.0204868 0.0192253 0.0179956 0.0168018 0.0156474 0.0145353 0.013468 0.0124474 0.0114749 0.0105515 0.00967778 0.00885388 0.00807955 0.00735423 0.00667702 0.00604678 0.00546213 0.00492148 0.00442308 0.00396507 0.00354545 0.0031622 0.00281321 0.00249638 0.00220961 0.00195082 0.00171797 0.00150906 0.0013222 0.00115553 0.00100731 0.000875868 0.000759646 0.000657173 0.00056708 0.000488095 +0.000553077 0.000642577 0.000744665 0.00086078 0.000992475 0.00114141 0.00130937 0.00149823 0.00170997 0.00194669 0.00221054 0.00250379 0.00282874 0.00318774 0.00358319 0.00401747 0.00449295 0.00501195 0.00557669 0.00618933 0.00685182 0.00756596 0.00833333 0.00915522 0.0100326 0.0109662 0.0119563 0.0130026 0.0141045 0.0152611 0.0164705 0.0177306 0.0190387 0.0203914 0.0217848 0.0232143 0.0246747 0.0261605 0.0276653 0.0291824 0.0307045 0.0322239 0.0337327 0.0352225 0.0366848 0.0381107 0.0394915 0.0408184 0.0420828 0.0432763 0.0443905 0.0454179 0.046351 0.0471832 0.0479084 0.0485213 0.0490172 0.0493925 0.0496442 0.0497706 0.0497706 0.0496442 0.0493925 0.0490172 0.0485213 0.0479084 0.0471832 0.046351 0.0454179 0.0443905 0.0432763 0.0420828 0.0408184 0.0394915 0.0381107 0.0366848 0.0352225 0.0337327 0.0322239 0.0307045 0.0291824 0.0276653 0.0261605 0.0246747 0.0232143 0.0217848 0.0203914 0.0190387 0.0177306 0.0164705 0.0152611 0.0141045 0.0130026 0.0119563 0.0109662 0.0100326 0.00915522 0.00833333 0.00756596 0.00685182 0.00618933 0.00557669 0.00501195 0.00449295 0.00401747 0.00358319 0.00318774 0.00282874 0.00250379 0.00221054 0.00194669 0.00170997 0.00149823 0.00130937 0.00114141 0.000992475 0.00086078 0.000744665 0.000642577 0.000553077 +0.000625114 0.000726271 0.000841656 0.000972895 0.00112174 0.00129008 0.00147991 0.00169337 0.00193269 0.00220024 0.00249846 0.0028299 0.00319717 0.00360294 0.0040499 0.00454074 0.00507815 0.00566474 0.00630304 0.00699547 0.00774425 0.00855141 0.00941872 0.0103477 0.0113394 0.0123945 0.0135135 0.0146961 0.0159416 0.0172488 0.0186157 0.02004 0.0215185 0.0230474 0.0246222 0.0262379 0.0278885 0.0295678 0.0312686 0.0329833 0.0347036 0.036421 0.0381264 0.0398102 0.0414629 0.0430745 0.0446351 0.0461349 0.047564 0.0489129 0.0501723 0.0513334 0.0523881 0.0533287 0.0541484 0.0548411 0.0554016 0.0558257 0.0561103 0.0562531 0.0562531 0.0561103 0.0558257 0.0554016 0.0548411 0.0541484 0.0533287 0.0523881 0.0513334 0.0501723 0.0489129 0.047564 0.0461349 0.0446351 0.0430745 0.0414629 0.0398102 0.0381264 0.036421 0.0347036 0.0329833 0.0312686 0.0295678 0.0278885 0.0262379 0.0246222 0.0230474 0.0215185 0.02004 0.0186157 0.0172488 0.0159416 0.0146961 0.0135135 0.0123945 0.0113394 0.0103477 0.00941872 0.00855141 0.00774425 0.00699547 0.00630304 0.00566474 0.00507815 0.00454074 0.0040499 0.00360294 0.00319717 0.0028299 0.00249846 0.00220024 0.00193269 0.00169337 0.00147991 0.00129008 0.00112174 0.000972895 0.000841656 0.000726271 0.000625114 +0.000704734 0.000818775 0.000948856 0.00109681 0.00126462 0.00145439 0.00166841 0.00190905 0.00217885 0.00248048 0.00281668 0.00319034 0.00360439 0.00406184 0.00456572 0.00511908 0.00572494 0.00638624 0.00710585 0.00788647 0.00873062 0.00964058 0.0106184 0.0116656 0.0127836 0.0139732 0.0152347 0.0165679 0.0179721 0.0194457 0.0209868 0.0225925 0.0242592 0.0259829 0.0277583 0.0295797 0.0314406 0.0333338 0.0352512 0.0371843 0.0391238 0.0410599 0.0429824 0.0448807 0.0467439 0.0485608 0.0503202 0.052011 0.0536221 0.0551428 0.0565626 0.0578717 0.0590607 0.0601211 0.0610452 0.0618261 0.062458 0.0629361 0.0632569 0.063418 0.063418 0.0632569 0.0629361 0.062458 0.0618261 0.0610452 0.0601211 0.0590607 0.0578717 0.0565626 0.0551428 0.0536221 0.052011 0.0503202 0.0485608 0.0467439 0.0448807 0.0429824 0.0410599 0.0391238 0.0371843 0.0352512 0.0333338 0.0314406 0.0295797 0.0277583 0.0259829 0.0242592 0.0225925 0.0209868 0.0194457 0.0179721 0.0165679 0.0152347 0.0139732 0.0127836 0.0116656 0.0106184 0.00964058 0.00873062 0.00788647 0.00710585 0.00638624 0.00572494 0.00511908 0.00456572 0.00406184 0.00360439 0.00319034 0.00281668 0.00248048 0.00217885 0.00190905 0.00166841 0.00145439 0.00126462 0.00109681 0.000948856 0.000818775 0.000704734 +0.00079247 0.000920709 0.00106698 0.00123336 0.00142206 0.00163546 0.00187612 0.00214672 0.00245011 0.00278929 0.00316735 0.00358753 0.00405312 0.00456752 0.00513414 0.00575639 0.00643767 0.00718131 0.0079905 0.0088683 0.00981755 0.0108408 0.0119403 0.0131179 0.0143751 0.0157128 0.0171314 0.0186306 0.0202095 0.0218666 0.0235996 0.0254051 0.0272794 0.0292176 0.0312141 0.0332623 0.0353549 0.0374837 0.0396399 0.0418136 0.0439945 0.0461717 0.0483336 0.0504682 0.0525633 0.0546064 0.0565849 0.0584862 0.0602979 0.0620079 0.0636044 0.0650765 0.0664135 0.0676059 0.0686451 0.0695232 0.0702337 0.0707714 0.0711322 0.0713132 0.0713132 0.0711322 0.0707714 0.0702337 0.0695232 0.0686451 0.0676059 0.0664135 0.0650765 0.0636044 0.0620079 0.0602979 0.0584862 0.0565849 0.0546064 0.0525633 0.0504682 0.0483336 0.0461717 0.0439945 0.0418136 0.0396399 0.0374837 0.0353549 0.0332623 0.0312141 0.0292176 0.0272794 0.0254051 0.0235996 0.0218666 0.0202095 0.0186306 0.0171314 0.0157128 0.0143751 0.0131179 0.0119403 0.0108408 0.00981755 0.0088683 0.0079905 0.00718131 0.00643767 0.00575639 0.00513414 0.00456752 0.00405312 0.00358753 0.00316735 0.00278929 0.00245011 0.00214672 0.00187612 0.00163546 0.00142206 0.00123336 0.00106698 0.000920709 0.00079247 +0.00088886 0.0010327 0.00119676 0.00138337 0.00159502 0.00183438 0.00210431 0.00240782 0.00274812 0.00312855 0.0035526 0.00402388 0.00454611 0.00512308 0.00575861 0.00645655 0.0072207 0.00805478 0.0089624 0.00994697 0.0110117 0.0121594 0.0133926 0.0147135 0.0161236 0.017624 0.0192151 0.0208966 0.0226676 0.0245263 0.02647 0.0284952 0.0305975 0.0327714 0.0350107 0.037308 0.0396551 0.0420429 0.0444613 0.0468994 0.0493456 0.0517876 0.0542124 0.0566067 0.0589567 0.0612483 0.0634674 0.0656 0.067632 0.06955 0.0713407 0.0729918 0.0744915 0.0758289 0.0769944 0.0779794 0.0787764 0.0793795 0.0797841 0.0799872 0.0799872 0.0797841 0.0793795 0.0787764 0.0779794 0.0769944 0.0758289 0.0744915 0.0729918 0.0713407 0.06955 0.067632 0.0656 0.0634674 0.0612483 0.0589567 0.0566067 0.0542124 0.0517876 0.0493456 0.0468994 0.0444613 0.0420429 0.0396551 0.037308 0.0350107 0.0327714 0.0305975 0.0284952 0.02647 0.0245263 0.0226676 0.0208966 0.0192151 0.017624 0.0161236 0.0147135 0.0133926 0.0121594 0.0110117 0.00994697 0.0089624 0.00805478 0.0072207 0.00645655 0.00575861 0.00512308 0.00454611 0.00402388 0.0035526 0.00312855 0.00274812 0.00240782 0.00210431 0.00183438 0.00159502 0.00138337 0.00119676 0.0010327 0.00088886 +0.000994433 0.00115535 0.00133891 0.00154768 0.00178447 0.00205226 0.00235425 0.00269381 0.00307453 0.00350014 0.00397455 0.00450181 0.00508607 0.00573156 0.00644258 0.00722342 0.00807833 0.00901148 0.0100269 0.0111284 0.0123196 0.0136036 0.0149833 0.0164611 0.0180387 0.0197173 0.0214973 0.0233786 0.02536 0.0274394 0.0296139 0.0318797 0.0342316 0.0366638 0.039169 0.0417392 0.0443651 0.0470365 0.0497422 0.0524699 0.0552066 0.0579386 0.0606515 0.0633301 0.0659592 0.068523 0.0710057 0.0733915 0.0756649 0.0778107 0.0798142 0.0816613 0.0833391 0.0848355 0.0861394 0.0872413 0.0881329 0.0888077 0.0892604 0.0894876 0.0894876 0.0892604 0.0888077 0.0881329 0.0872413 0.0861394 0.0848355 0.0833391 0.0816613 0.0798142 0.0778107 0.0756649 0.0733915 0.0710057 0.068523 0.0659592 0.0633301 0.0606515 0.0579386 0.0552066 0.0524699 0.0497422 0.0470365 0.0443651 0.0417392 0.039169 0.0366638 0.0342316 0.0318797 0.0296139 0.0274394 0.02536 0.0233786 0.0214973 0.0197173 0.0180387 0.0164611 0.0149833 0.0136036 0.0123196 0.0111284 0.0100269 0.00901148 0.00807833 0.00722342 0.00644258 0.00573156 0.00508607 0.00450181 0.00397455 0.00350014 0.00307453 0.00269381 0.00235425 0.00205226 0.00178447 0.00154768 0.00133891 0.00115535 0.000994433 +0.00110971 0.00128929 0.00149412 0.0017271 0.00199133 0.00229017 0.00262716 0.00300609 0.00343094 0.00390589 0.0044353 0.00502368 0.00567567 0.00639599 0.00718943 0.00806078 0.0090148 0.0100561 0.0111893 0.0124185 0.0137477 0.0151806 0.0167202 0.0183693 0.0201298 0.022003 0.0239894 0.0260887 0.0282998 0.0306203 0.0330469 0.0355753 0.0381999 0.040914 0.0437097 0.0465778 0.0495081 0.0524892 0.0555085 0.0585524 0.0616064 0.0646551 0.0676824 0.0706716 0.0736054 0.0764664 0.0792369 0.0818994 0.0844363 0.0868308 0.0890665 0.0911278 0.0930001 0.0946699 0.096125 0.0973546 0.0983497 0.0991026 0.0996078 0.0998613 0.0998613 0.0996078 0.0991026 0.0983497 0.0973546 0.096125 0.0946699 0.0930001 0.0911278 0.0890665 0.0868308 0.0844363 0.0818994 0.0792369 0.0764664 0.0736054 0.0706716 0.0676824 0.0646551 0.0616064 0.0585524 0.0555085 0.0524892 0.0495081 0.0465778 0.0437097 0.040914 0.0381999 0.0355753 0.0330469 0.0306203 0.0282998 0.0260887 0.0239894 0.022003 0.0201298 0.0183693 0.0167202 0.0151806 0.0137477 0.0124185 0.0111893 0.0100561 0.0090148 0.00806078 0.00718943 0.00639599 0.00567567 0.00502368 0.0044353 0.00390589 0.00343094 0.00300609 0.00262716 0.00229017 0.00199133 0.0017271 0.00149412 0.00128929 0.00110971 +0.0012352 0.00143508 0.00166308 0.0019224 0.00221652 0.00254914 0.00292424 0.00334602 0.00381891 0.00434757 0.00493685 0.00559176 0.00631748 0.00711926 0.00800242 0.00897231 0.0100342 0.0111933 0.0124545 0.0138227 0.0153023 0.0168972 0.018611 0.0204465 0.0224061 0.0244911 0.0267021 0.0290389 0.0315 0.0340828 0.0367839 0.0395982 0.0425196 0.0455406 0.0486524 0.0518449 0.0551066 0.0584247 0.0617854 0.0651735 0.0685729 0.0719664 0.075336 0.0786632 0.0819288 0.0851133 0.0881971 0.0911606 0.0939845 0.0966497 0.0991383 0.101433 0.103517 0.105375 0.106995 0.108364 0.109471 0.110309 0.110872 0.111154 0.111154 0.110872 0.110309 0.109471 0.108364 0.106995 0.105375 0.103517 0.101433 0.0991383 0.0966497 0.0939845 0.0911606 0.0881971 0.0851133 0.0819288 0.0786632 0.075336 0.0719664 0.0685729 0.0651735 0.0617854 0.0584247 0.0551066 0.0518449 0.0486524 0.0455406 0.0425196 0.0395982 0.0367839 0.0340828 0.0315 0.0290389 0.0267021 0.0244911 0.0224061 0.0204465 0.018611 0.0168972 0.0153023 0.0138227 0.0124545 0.0111933 0.0100342 0.00897231 0.00800242 0.00711926 0.00631748 0.00559176 0.00493685 0.00434757 0.00381891 0.00334602 0.00292424 0.00254914 0.00221652 0.0019224 0.00166308 0.00143508 0.0012352 +0.00137137 0.00159329 0.00184642 0.00213433 0.00246088 0.00283017 0.00324663 0.0037149 0.00423993 0.00482687 0.00548111 0.00620823 0.00701395 0.00790412 0.00888465 0.00996146 0.0111404 0.0124273 0.0138276 0.0153466 0.0169893 0.0187601 0.0206628 0.0227007 0.0248763 0.0271911 0.0296459 0.0322403 0.0349727 0.0378403 0.0408392 0.0439637 0.0472072 0.0505613 0.0540161 0.0575605 0.0611818 0.0648658 0.068597 0.0723586 0.0761328 0.0799004 0.0836415 0.0873355 0.0909611 0.0944967 0.0979205 0.101211 0.104346 0.107305 0.110068 0.112615 0.114929 0.116992 0.118791 0.12031 0.12154 0.12247 0.123095 0.123408 0.123408 0.123095 0.12247 0.12154 0.12031 0.118791 0.116992 0.114929 0.112615 0.110068 0.107305 0.104346 0.101211 0.0979205 0.0944967 0.0909611 0.0873355 0.0836415 0.0799004 0.0761328 0.0723586 0.068597 0.0648658 0.0611818 0.0575605 0.0540161 0.0505613 0.0472072 0.0439637 0.0408392 0.0378403 0.0349727 0.0322403 0.0296459 0.0271911 0.0248763 0.0227007 0.0206628 0.0187601 0.0169893 0.0153466 0.0138276 0.0124273 0.0111404 0.00996146 0.00888465 0.00790412 0.00701395 0.00620823 0.00548111 0.00482687 0.00423993 0.0037149 0.00324663 0.00283017 0.00246088 0.00213433 0.00184642 0.00159329 0.00137137 +0.00151868 0.00176444 0.00204476 0.0023636 0.00272522 0.00313418 0.00359537 0.00411395 0.00469537 0.00534536 0.00606988 0.0068751 0.00776737 0.00875316 0.00983902 0.0110315 0.0123371 0.0137622 0.0153129 0.0169951 0.0188143 0.0207752 0.0228823 0.0251391 0.0275484 0.0301119 0.0328304 0.0357035 0.0387294 0.041905 0.045226 0.0486862 0.0522781 0.0559924 0.0598184 0.0637435 0.0677538 0.0718335 0.0759655 0.0801312 0.0843107 0.088483 0.092626 0.0967168 0.100732 0.104647 0.108439 0.112082 0.115554 0.118831 0.121891 0.124712 0.127274 0.129559 0.131551 0.133234 0.134595 0.135626 0.136317 0.136664 0.136664 0.136317 0.135626 0.134595 0.133234 0.131551 0.129559 0.127274 0.124712 0.121891 0.118831 0.115554 0.112082 0.108439 0.104647 0.100732 0.0967168 0.092626 0.088483 0.0843107 0.0801312 0.0759655 0.0718335 0.0677538 0.0637435 0.0598184 0.0559924 0.0522781 0.0486862 0.045226 0.041905 0.0387294 0.0357035 0.0328304 0.0301119 0.0275484 0.0251391 0.0228823 0.0207752 0.0188143 0.0169951 0.0153129 0.0137622 0.0123371 0.0110315 0.00983902 0.00875316 0.00776737 0.0068751 0.00606988 0.00534536 0.00469537 0.00411395 0.00359537 0.00313418 0.00272522 0.0023636 0.00204476 0.00176444 0.00151868 +0.00167753 0.00194899 0.00225863 0.00261082 0.00301026 0.00346201 0.00397143 0.00454425 0.00518649 0.00590447 0.00670477 0.00759421 0.00857981 0.00966871 0.0108681 0.0121853 0.0136275 0.0152017 0.0169146 0.0187728 0.0207822 0.0229482 0.0252757 0.0277686 0.0304298 0.0332615 0.0362643 0.0394379 0.0427803 0.0462881 0.0499564 0.0537786 0.0577461 0.061849 0.0660751 0.0704109 0.0748406 0.079347 0.0839112 0.0885126 0.0931293 0.097738 0.102314 0.106833 0.111268 0.115593 0.119781 0.123806 0.127641 0.131261 0.13464 0.137756 0.140587 0.143111 0.14531 0.147169 0.148673 0.149812 0.150575 0.150959 0.150959 0.150575 0.149812 0.148673 0.147169 0.14531 0.143111 0.140587 0.137756 0.13464 0.131261 0.127641 0.123806 0.119781 0.115593 0.111268 0.106833 0.102314 0.097738 0.0931293 0.0885126 0.0839112 0.079347 0.0748406 0.0704109 0.0660751 0.061849 0.0577461 0.0537786 0.0499564 0.0462881 0.0427803 0.0394379 0.0362643 0.0332615 0.0304298 0.0277686 0.0252757 0.0229482 0.0207822 0.0187728 0.0169146 0.0152017 0.0136275 0.0121853 0.0108681 0.00966871 0.00857981 0.00759421 0.00670477 0.00590447 0.00518649 0.00454425 0.00397143 0.00346201 0.00301026 0.00261082 0.00225863 0.00194899 0.00167753 +0.00184827 0.00214736 0.00248852 0.00287656 0.00331665 0.00381438 0.00437565 0.00500677 0.00571438 0.00650543 0.00738719 0.00836716 0.00945308 0.0106528 0.0119743 0.0134256 0.0150145 0.0167489 0.0186362 0.0206835 0.0228974 0.0252839 0.0278483 0.0305949 0.033527 0.0366469 0.0399554 0.043452 0.0471346 0.0509994 0.0550411 0.0592523 0.0636236 0.0681441 0.0728004 0.0775774 0.082458 0.0874231 0.0924518 0.0975216 0.102608 0.107686 0.112728 0.117707 0.122593 0.127358 0.131973 0.136407 0.140632 0.144621 0.148344 0.151777 0.154896 0.157677 0.1601 0.162148 0.163806 0.16506 0.165901 0.166323 0.166323 0.165901 0.16506 0.163806 0.162148 0.1601 0.157677 0.154896 0.151777 0.148344 0.144621 0.140632 0.136407 0.131973 0.127358 0.122593 0.117707 0.112728 0.107686 0.102608 0.0975216 0.0924518 0.0874231 0.082458 0.0775774 0.0728004 0.0681441 0.0636236 0.0592523 0.0550411 0.0509994 0.0471346 0.043452 0.0399554 0.0366469 0.033527 0.0305949 0.0278483 0.0252839 0.0228974 0.0206835 0.0186362 0.0167489 0.0150145 0.0134256 0.0119743 0.0106528 0.00945308 0.00836716 0.00738719 0.00650543 0.00571438 0.00500677 0.00437565 0.00381438 0.00331665 0.00287656 0.00248852 0.00214736 0.00184827 +0.00203121 0.0023599 0.00273482 0.00316126 0.00364492 0.00419191 0.00480873 0.00550232 0.00627996 0.00714931 0.00811834 0.00919531 0.0103887 0.0117072 0.0131595 0.0147544 0.0165006 0.0184066 0.0204807 0.0227306 0.0251637 0.0277864 0.0306046 0.033623 0.0368454 0.0402741 0.04391 0.0477526 0.0517997 0.0560471 0.0604888 0.0651168 0.0699208 0.0748887 0.0800058 0.0852557 0.0906193 0.0960758 0.101602 0.107174 0.112764 0.118344 0.123885 0.129357 0.134727 0.139964 0.145035 0.149908 0.154552 0.158934 0.163027 0.1668 0.170227 0.173283 0.175946 0.178197 0.180018 0.181397 0.182321 0.182785 0.182785 0.182321 0.181397 0.180018 0.178197 0.175946 0.173283 0.170227 0.1668 0.163027 0.158934 0.154552 0.149908 0.145035 0.139964 0.134727 0.129357 0.123885 0.118344 0.112764 0.107174 0.101602 0.0960758 0.0906193 0.0852557 0.0800058 0.0748887 0.0699208 0.0651168 0.0604888 0.0560471 0.0517997 0.0477526 0.04391 0.0402741 0.0368454 0.033623 0.0306046 0.0277864 0.0251637 0.0227306 0.0204807 0.0184066 0.0165006 0.0147544 0.0131595 0.0117072 0.0103887 0.00919531 0.00811834 0.00714931 0.00627996 0.00550232 0.00480873 0.00419191 0.00364492 0.00316126 0.00273482 0.0023599 0.00203121 +0.00222656 0.00258687 0.00299785 0.0034653 0.00399547 0.00459506 0.00527122 0.00603151 0.00688394 0.0078369 0.00889912 0.0100797 0.0113878 0.0128331 0.0144251 0.0161734 0.0180876 0.0201769 0.0224505 0.0249168 0.0275838 0.0304588 0.033548 0.0368568 0.040389 0.0441474 0.0481331 0.0523453 0.0567816 0.0614375 0.0663064 0.0713794 0.0766455 0.0820911 0.0877004 0.0934552 0.0993346 0.105316 0.111374 0.117481 0.123609 0.129726 0.1358 0.141798 0.147684 0.153425 0.158983 0.164325 0.169416 0.17422 0.178706 0.182842 0.186598 0.189949 0.192868 0.195335 0.197332 0.198843 0.199856 0.200365 0.200365 0.199856 0.198843 0.197332 0.195335 0.192868 0.189949 0.186598 0.182842 0.178706 0.17422 0.169416 0.164325 0.158983 0.153425 0.147684 0.141798 0.1358 0.129726 0.123609 0.117481 0.111374 0.105316 0.0993346 0.0934552 0.0877004 0.0820911 0.0766455 0.0713794 0.0663064 0.0614375 0.0567816 0.0523453 0.0481331 0.0441474 0.040389 0.0368568 0.033548 0.0304588 0.0275838 0.0249168 0.0224505 0.0201769 0.0180876 0.0161734 0.0144251 0.0128331 0.0113878 0.0100797 0.00889912 0.0078369 0.00688394 0.00603151 0.00527122 0.00459506 0.00399547 0.0034653 0.00299785 0.00258687 0.00222656 +0.00243448 0.00282844 0.00327779 0.0037889 0.00436858 0.00502416 0.00576346 0.00659475 0.00752679 0.00856873 0.00973015 0.0110209 0.0124513 0.0140315 0.0157722 0.0176837 0.0197766 0.0220611 0.024547 0.0272436 0.0301597 0.0333031 0.0366808 0.0402986 0.0441607 0.0482701 0.0526279 0.0572334 0.062084 0.0671747 0.0724982 0.078045 0.0838029 0.089757 0.0958902 0.102182 0.108611 0.115151 0.121774 0.128452 0.135152 0.14184 0.148482 0.155039 0.161475 0.167752 0.17383 0.179671 0.185236 0.190489 0.195394 0.199916 0.204023 0.207687 0.210879 0.213576 0.215759 0.217411 0.218519 0.219075 0.219075 0.218519 0.217411 0.215759 0.213576 0.210879 0.207687 0.204023 0.199916 0.195394 0.190489 0.185236 0.179671 0.17383 0.167752 0.161475 0.155039 0.148482 0.14184 0.135152 0.128452 0.121774 0.115151 0.108611 0.102182 0.0958902 0.089757 0.0838029 0.078045 0.0724982 0.0671747 0.062084 0.0572334 0.0526279 0.0482701 0.0441607 0.0402986 0.0366808 0.0333031 0.0301597 0.0272436 0.024547 0.0220611 0.0197766 0.0176837 0.0157722 0.0140315 0.0124513 0.0110209 0.00973015 0.00856873 0.00752679 0.00659475 0.00576346 0.00502416 0.00436858 0.0037889 0.00327779 0.00282844 0.00243448 +0.00265504 0.00308468 0.00357475 0.00413217 0.00476437 0.00547934 0.00628561 0.00719222 0.00820869 0.00934504 0.0106117 0.0120194 0.0135793 0.0153027 0.0172011 0.0192858 0.0215684 0.0240598 0.0267709 0.0297118 0.0328921 0.0363203 0.040004 0.0439495 0.0481615 0.0526432 0.0573958 0.0624187 0.0677087 0.0732606 0.0790664 0.0851157 0.0913952 0.0978888 0.104578 0.11144 0.118451 0.125583 0.132807 0.14009 0.147396 0.154691 0.161934 0.169085 0.176105 0.18295 0.189578 0.195948 0.202018 0.207747 0.213096 0.218028 0.222508 0.226503 0.229984 0.232926 0.235307 0.237108 0.238317 0.238923 0.238923 0.238317 0.237108 0.235307 0.232926 0.229984 0.226503 0.222508 0.218028 0.213096 0.207747 0.202018 0.195948 0.189578 0.18295 0.176105 0.169085 0.161934 0.154691 0.147396 0.14009 0.132807 0.125583 0.118451 0.11144 0.104578 0.0978888 0.0913952 0.0851157 0.0790664 0.0732606 0.0677087 0.0624187 0.0573958 0.0526432 0.0481615 0.0439495 0.040004 0.0363203 0.0328921 0.0297118 0.0267709 0.0240598 0.0215684 0.0192858 0.0172011 0.0153027 0.0135793 0.0120194 0.0106117 0.00934504 0.00820869 0.00719222 0.00628561 0.00547934 0.00476437 0.00413217 0.00357475 0.00308468 0.00265504 +0.0028882 0.00335558 0.00388869 0.00449505 0.00518277 0.00596053 0.00683761 0.00782384 0.00892958 0.0101657 0.0115436 0.0130749 0.0147718 0.0166466 0.0187117 0.0209795 0.0234625 0.0261727 0.0291219 0.0323211 0.0357806 0.0395099 0.0435172 0.0478091 0.052391 0.0572663 0.0624363 0.0679002 0.0736548 0.0796942 0.08601 0.0925905 0.0994215 0.106485 0.113762 0.121226 0.128853 0.136612 0.14447 0.152392 0.160341 0.168275 0.176155 0.183934 0.19157 0.199016 0.206227 0.213156 0.219759 0.225991 0.23181 0.237175 0.242048 0.246394 0.250181 0.253381 0.255971 0.257931 0.259245 0.259905 0.259905 0.259245 0.257931 0.255971 0.253381 0.250181 0.246394 0.242048 0.237175 0.23181 0.225991 0.219759 0.213156 0.206227 0.199016 0.19157 0.183934 0.176155 0.168275 0.160341 0.152392 0.14447 0.136612 0.128853 0.121226 0.113762 0.106485 0.0994215 0.0925905 0.08601 0.0796942 0.0736548 0.0679002 0.0624363 0.0572663 0.052391 0.0478091 0.0435172 0.0395099 0.0357806 0.0323211 0.0291219 0.0261727 0.0234625 0.0209795 0.0187117 0.0166466 0.0147718 0.0130749 0.0115436 0.0101657 0.00892958 0.00782384 0.00683761 0.00596053 0.00518277 0.00449505 0.00388869 0.00335558 0.0028882 +0.00313384 0.00364097 0.00421941 0.00487735 0.00562355 0.00646747 0.00741914 0.00848924 0.00968902 0.0110303 0.0125254 0.0141869 0.0160282 0.0180624 0.0203031 0.0227638 0.0254579 0.0283986 0.0315986 0.0350699 0.0388237 0.0428702 0.0472182 0.0518752 0.0568468 0.0621367 0.0677464 0.073675 0.079919 0.0864721 0.093325 0.100465 0.107877 0.115542 0.123437 0.131536 0.139812 0.14823 0.156757 0.165353 0.173977 0.182587 0.191136 0.199578 0.207863 0.215942 0.223766 0.231285 0.238449 0.245211 0.251525 0.257346 0.262634 0.267349 0.271458 0.274931 0.277741 0.279867 0.281294 0.28201 0.28201 0.281294 0.279867 0.277741 0.274931 0.271458 0.267349 0.262634 0.257346 0.251525 0.245211 0.238449 0.231285 0.223766 0.215942 0.207863 0.199578 0.191136 0.182587 0.173977 0.165353 0.156757 0.14823 0.139812 0.131536 0.123437 0.115542 0.107877 0.100465 0.093325 0.0864721 0.079919 0.073675 0.0677464 0.0621367 0.0568468 0.0518752 0.0472182 0.0428702 0.0388237 0.0350699 0.0315986 0.0283986 0.0254579 0.0227638 0.0203031 0.0180624 0.0160282 0.0141869 0.0125254 0.0110303 0.00968902 0.00848924 0.00741914 0.00646747 0.00562355 0.00487735 0.00421941 0.00364097 0.00313384 +0.00339171 0.00394056 0.0045666 0.00527867 0.00608628 0.00699964 0.00802961 0.00918777 0.0104863 0.0119379 0.013556 0.0153543 0.017347 0.0195486 0.0219737 0.0246369 0.0275527 0.0307354 0.0341987 0.0379556 0.0420183 0.0463977 0.0511035 0.0561437 0.0615244 0.0672495 0.0733208 0.0797373 0.0864951 0.0935874 0.101004 0.108732 0.116754 0.125049 0.133594 0.14236 0.151316 0.160427 0.169655 0.178959 0.188293 0.197611 0.206864 0.216 0.224967 0.233711 0.242179 0.250316 0.25807 0.265388 0.272222 0.278522 0.284244 0.289348 0.293795 0.297553 0.300594 0.302896 0.30444 0.305215 0.305215 0.30444 0.302896 0.300594 0.297553 0.293795 0.289348 0.284244 0.278522 0.272222 0.265388 0.25807 0.250316 0.242179 0.233711 0.224967 0.216 0.206864 0.197611 0.188293 0.178959 0.169655 0.160427 0.151316 0.14236 0.133594 0.125049 0.116754 0.108732 0.101004 0.0935874 0.0864951 0.0797373 0.0733208 0.0672495 0.0615244 0.0561437 0.0511035 0.0463977 0.0420183 0.0379556 0.0341987 0.0307354 0.0275527 0.0246369 0.0219737 0.0195486 0.017347 0.0153543 0.013556 0.0119379 0.0104863 0.00918777 0.00802961 0.00699964 0.00608628 0.00527867 0.0045666 0.00394056 0.00339171 +0.00366144 0.00425394 0.00492977 0.00569847 0.0065703 0.00755629 0.00866818 0.00991844 0.0113202 0.0128873 0.0146341 0.0165754 0.0187266 0.0211032 0.0237212 0.0265962 0.0297439 0.0331797 0.0369184 0.0409741 0.0453598 0.0500876 0.0551676 0.0606086 0.0664172 0.0725977 0.0791518 0.0860785 0.0933737 0.10103 0.109037 0.117379 0.126039 0.134994 0.144218 0.153681 0.16335 0.173185 0.183147 0.193191 0.203267 0.213326 0.223315 0.233177 0.242857 0.252297 0.261438 0.270223 0.278593 0.286494 0.293871 0.300672 0.306849 0.312359 0.31716 0.321217 0.3245 0.326984 0.328651 0.329487 0.329487 0.328651 0.326984 0.3245 0.321217 0.31716 0.312359 0.306849 0.300672 0.293871 0.286494 0.278593 0.270223 0.261438 0.252297 0.242857 0.233177 0.223315 0.213326 0.203267 0.193191 0.183147 0.173185 0.16335 0.153681 0.144218 0.134994 0.126039 0.117379 0.109037 0.10103 0.0933737 0.0860785 0.0791518 0.0725977 0.0664172 0.0606086 0.0551676 0.0500876 0.0453598 0.0409741 0.0369184 0.0331797 0.0297439 0.0265962 0.0237212 0.0211032 0.0187266 0.0165754 0.0146341 0.0128873 0.0113202 0.00991844 0.00866818 0.00755629 0.0065703 0.00569847 0.00492977 0.00425394 0.00366144 +0.00394255 0.00458054 0.00530826 0.00613598 0.00707475 0.00813644 0.0093337 0.0106799 0.0121893 0.0138767 0.0157576 0.017848 0.0201643 0.0227235 0.0255424 0.0286381 0.0320275 0.0357271 0.0397529 0.0441199 0.0488424 0.0539331 0.0594032 0.065262 0.0715165 0.0781715 0.0852288 0.0926873 0.100543 0.108787 0.117408 0.126391 0.135716 0.145358 0.15529 0.16548 0.175891 0.186482 0.197209 0.208023 0.218873 0.229705 0.24046 0.25108 0.261503 0.271668 0.281511 0.29097 0.299983 0.30849 0.316433 0.323756 0.330408 0.33634 0.34151 0.345879 0.349414 0.352089 0.353884 0.354784 0.354784 0.353884 0.352089 0.349414 0.345879 0.34151 0.33634 0.330408 0.323756 0.316433 0.30849 0.299983 0.29097 0.281511 0.271668 0.261503 0.25108 0.24046 0.229705 0.218873 0.208023 0.197209 0.186482 0.175891 0.16548 0.15529 0.145358 0.135716 0.126391 0.117408 0.108787 0.100543 0.0926873 0.0852288 0.0781715 0.0715165 0.065262 0.0594032 0.0539331 0.0488424 0.0441199 0.0397529 0.0357271 0.0320275 0.0286381 0.0255424 0.0227235 0.0201643 0.017848 0.0157576 0.0138767 0.0121893 0.0106799 0.0093337 0.00813644 0.00707475 0.00613598 0.00530826 0.00458054 0.00394255 +0.00423443 0.00491966 0.00570125 0.00659025 0.00759852 0.00873881 0.0100247 0.0114706 0.0130918 0.0149041 0.0169242 0.0191693 0.0216572 0.0244058 0.0274334 0.0307583 0.0343986 0.0383721 0.0426959 0.0473863 0.0524584 0.057926 0.063801 0.0700935 0.0768111 0.0839588 0.0915386 0.0995493 0.107986 0.116841 0.1261 0.135748 0.145763 0.156119 0.166787 0.177731 0.188913 0.200288 0.211809 0.223424 0.235077 0.246711 0.258262 0.269668 0.280863 0.29178 0.302352 0.312511 0.322192 0.331329 0.33986 0.347725 0.354869 0.361241 0.366793 0.371485 0.375282 0.378155 0.380083 0.38105 0.38105 0.380083 0.378155 0.375282 0.371485 0.366793 0.361241 0.354869 0.347725 0.33986 0.331329 0.322192 0.312511 0.302352 0.29178 0.280863 0.269668 0.258262 0.246711 0.235077 0.223424 0.211809 0.200288 0.188913 0.177731 0.166787 0.156119 0.145763 0.135748 0.1261 0.116841 0.107986 0.0995493 0.0915386 0.0839588 0.0768111 0.0700935 0.063801 0.057926 0.0524584 0.0473863 0.0426959 0.0383721 0.0343986 0.0307583 0.0274334 0.0244058 0.0216572 0.0191693 0.0169242 0.0149041 0.0130918 0.0114706 0.0100247 0.00873881 0.00759852 0.00659025 0.00570125 0.00491966 0.00423443 +0.00453634 0.00527041 0.00610774 0.00706011 0.00814028 0.00936187 0.0107394 0.0122884 0.0140252 0.0159667 0.0181309 0.0205361 0.0232013 0.0261459 0.0293893 0.0329513 0.0368512 0.0411079 0.04574 0.0507648 0.0561986 0.062056 0.0683499 0.075091 0.0822876 0.0899449 0.0980651 0.106647 0.115685 0.125171 0.135091 0.145427 0.156156 0.16725 0.178679 0.190403 0.202382 0.214568 0.22691 0.239353 0.251838 0.264301 0.276676 0.288895 0.300888 0.312583 0.323909 0.334792 0.345163 0.354952 0.364091 0.372517 0.380171 0.386997 0.392945 0.397971 0.402039 0.405117 0.407182 0.408218 0.408218 0.407182 0.405117 0.402039 0.397971 0.392945 0.386997 0.380171 0.372517 0.364091 0.354952 0.345163 0.334792 0.323909 0.312583 0.300888 0.288895 0.276676 0.264301 0.251838 0.239353 0.22691 0.214568 0.202382 0.190403 0.178679 0.16725 0.156156 0.145427 0.135091 0.125171 0.115685 0.106647 0.0980651 0.0899449 0.0822876 0.075091 0.0683499 0.062056 0.0561986 0.0507648 0.04574 0.0411079 0.0368512 0.0329513 0.0293893 0.0261459 0.0232013 0.0205361 0.0181309 0.0159667 0.0140252 0.0122884 0.0107394 0.00936187 0.00814028 0.00706011 0.00610774 0.00527041 0.00453634 +0.00484739 0.0056318 0.00652653 0.00754421 0.00869844 0.0100038 0.0114758 0.013131 0.0149869 0.0170615 0.0193741 0.0219442 0.0247922 0.0279386 0.0314045 0.0352107 0.039378 0.0439266 0.0488763 0.0542457 0.060052 0.066311 0.0730365 0.0802399 0.0879299 0.0961122 0.104789 0.11396 0.123618 0.133754 0.144354 0.155398 0.166863 0.178718 0.19093 0.203459 0.216259 0.229281 0.242469 0.255766 0.269106 0.282423 0.295647 0.308704 0.32152 0.334017 0.346119 0.357749 0.36883 0.37929 0.389056 0.39806 0.406238 0.413532 0.419888 0.42526 0.429606 0.432895 0.435102 0.436209 0.436209 0.435102 0.432895 0.429606 0.42526 0.419888 0.413532 0.406238 0.39806 0.389056 0.37929 0.36883 0.357749 0.346119 0.334017 0.32152 0.308704 0.295647 0.282423 0.269106 0.255766 0.242469 0.229281 0.216259 0.203459 0.19093 0.178718 0.166863 0.155398 0.144354 0.133754 0.123618 0.11396 0.104789 0.0961122 0.0879299 0.0802399 0.0730365 0.066311 0.060052 0.0542457 0.0488763 0.0439266 0.039378 0.0352107 0.0314045 0.0279386 0.0247922 0.0219442 0.0193741 0.0170615 0.0149869 0.013131 0.0114758 0.0100038 0.00869844 0.00754421 0.00652653 0.0056318 0.00484739 +0.00516657 0.00600263 0.00695628 0.00804097 0.0092712 0.0106625 0.0122315 0.0139957 0.0159737 0.0181849 0.0206498 0.0233891 0.0264246 0.0297783 0.0334724 0.0375292 0.0419709 0.046819 0.0520946 0.0578175 0.0640062 0.0706774 0.0778457 0.0855234 0.0937197 0.102441 0.111689 0.121463 0.131757 0.142561 0.153859 0.165631 0.17785 0.190486 0.203502 0.216856 0.230499 0.244378 0.258435 0.272607 0.286825 0.30102 0.315114 0.329031 0.34269 0.356011 0.368909 0.381305 0.393116 0.404265 0.414674 0.424271 0.432988 0.440762 0.447536 0.453261 0.457894 0.461399 0.463751 0.464932 0.464932 0.463751 0.461399 0.457894 0.453261 0.447536 0.440762 0.432988 0.424271 0.414674 0.404265 0.393116 0.381305 0.368909 0.356011 0.34269 0.329031 0.315114 0.30102 0.286825 0.272607 0.258435 0.244378 0.230499 0.216856 0.203502 0.190486 0.17785 0.165631 0.153859 0.142561 0.131757 0.121463 0.111689 0.102441 0.0937197 0.0855234 0.0778457 0.0706774 0.0640062 0.0578175 0.0520946 0.046819 0.0419709 0.0375292 0.0334724 0.0297783 0.0264246 0.0233891 0.0206498 0.0181849 0.0159737 0.0139957 0.0122315 0.0106625 0.0092712 0.00804097 0.00695628 0.00600263 0.00516657 +0.00549274 0.00638158 0.00739544 0.00854861 0.0098565 0.0113356 0.0130037 0.0148792 0.0169821 0.019333 0.0219534 0.0248657 0.0280928 0.0316582 0.0355855 0.0398984 0.0446205 0.0497748 0.0553834 0.0614676 0.068047 0.0751393 0.0827602 0.0909226 0.0996363 0.108908 0.11874 0.129131 0.140075 0.151561 0.163572 0.176087 0.189078 0.202512 0.21635 0.230546 0.24505 0.259806 0.27475 0.289817 0.304933 0.320023 0.335008 0.349803 0.364325 0.378486 0.392199 0.405377 0.417934 0.429786 0.440852 0.451055 0.460323 0.468587 0.47579 0.481876 0.486801 0.490528 0.493028 0.494283 0.494283 0.493028 0.490528 0.486801 0.481876 0.47579 0.468587 0.460323 0.451055 0.440852 0.429786 0.417934 0.405377 0.392199 0.378486 0.364325 0.349803 0.335008 0.320023 0.304933 0.289817 0.27475 0.259806 0.24505 0.230546 0.21635 0.202512 0.189078 0.176087 0.163572 0.151561 0.140075 0.129131 0.11874 0.108908 0.0996363 0.0909226 0.0827602 0.0751393 0.068047 0.0614676 0.0553834 0.0497748 0.0446205 0.0398984 0.0355855 0.0316582 0.0280928 0.0248657 0.0219534 0.019333 0.0169821 0.0148792 0.0130037 0.0113356 0.0098565 0.00854861 0.00739544 0.00638158 0.00549274 +0.00582462 0.00676717 0.00784229 0.00906514 0.0104521 0.0120206 0.0137894 0.0157783 0.0180082 0.0205011 0.0232799 0.0263682 0.0297903 0.0335711 0.0377357 0.0423092 0.0473166 0.0527823 0.0587298 0.0651816 0.0721585 0.0796794 0.0877608 0.0964163 0.105657 0.115489 0.125915 0.136934 0.148539 0.160719 0.173456 0.186727 0.200503 0.214748 0.229422 0.244476 0.259857 0.275504 0.291351 0.307328 0.323358 0.33936 0.35525 0.370939 0.386338 0.401355 0.415897 0.429871 0.443187 0.455755 0.46749 0.478309 0.488136 0.496901 0.504538 0.510992 0.516215 0.520167 0.522818 0.524149 0.524149 0.522818 0.520167 0.516215 0.510992 0.504538 0.496901 0.488136 0.478309 0.46749 0.455755 0.443187 0.429871 0.415897 0.401355 0.386338 0.370939 0.35525 0.33936 0.323358 0.307328 0.291351 0.275504 0.259857 0.244476 0.229422 0.214748 0.200503 0.186727 0.173456 0.160719 0.148539 0.136934 0.125915 0.115489 0.105657 0.0964163 0.0877608 0.0796794 0.0721585 0.0651816 0.0587298 0.0527823 0.0473166 0.0423092 0.0377357 0.0335711 0.0297903 0.0263682 0.0232799 0.0205011 0.0180082 0.0157783 0.0137894 0.0120206 0.0104521 0.00906514 0.00784229 0.00676717 0.00582462 +0.00616083 0.00715778 0.00829495 0.00958839 0.0110554 0.0127144 0.0145853 0.016689 0.0190477 0.0216845 0.0246236 0.0278902 0.0315098 0.0355089 0.0399138 0.0447514 0.0500478 0.0558289 0.0621198 0.068944 0.0763236 0.0842786 0.0928264 0.101982 0.111755 0.122155 0.133183 0.144838 0.157113 0.169996 0.183468 0.197505 0.212076 0.227144 0.242665 0.258588 0.274856 0.291406 0.308168 0.325067 0.342022 0.358948 0.375755 0.39235 0.408638 0.424522 0.439903 0.454684 0.468768 0.482062 0.494474 0.505918 0.516312 0.525582 0.533661 0.540487 0.546011 0.550192 0.552996 0.554404 0.554404 0.552996 0.550192 0.546011 0.540487 0.533661 0.525582 0.516312 0.505918 0.494474 0.482062 0.468768 0.454684 0.439903 0.424522 0.408638 0.39235 0.375755 0.358948 0.342022 0.325067 0.308168 0.291406 0.274856 0.258588 0.242665 0.227144 0.212076 0.197505 0.183468 0.169996 0.157113 0.144838 0.133183 0.122155 0.111755 0.101982 0.0928264 0.0842786 0.0763236 0.068944 0.0621198 0.0558289 0.0500478 0.0447514 0.0399138 0.0355089 0.0315098 0.0278902 0.0246236 0.0216845 0.0190477 0.016689 0.0145853 0.0127144 0.0110554 0.00958839 0.00829495 0.00715778 0.00616083 +0.00649984 0.00755165 0.0087514 0.010116 0.0116637 0.013414 0.0153879 0.0176074 0.0200958 0.0228777 0.0259786 0.0294249 0.0332437 0.0374628 0.0421102 0.0472139 0.0528017 0.058901 0.065538 0.0727377 0.0805234 0.0889161 0.0979343 0.107593 0.117905 0.128876 0.140511 0.152808 0.165758 0.17935 0.193563 0.208373 0.223746 0.239643 0.256018 0.272817 0.28998 0.307441 0.325126 0.342955 0.360843 0.3787 0.396432 0.41394 0.431124 0.447882 0.464109 0.479703 0.494563 0.508588 0.521683 0.533757 0.544723 0.554503 0.563026 0.570228 0.576057 0.580467 0.583426 0.584911 0.584911 0.583426 0.580467 0.576057 0.570228 0.563026 0.554503 0.544723 0.533757 0.521683 0.508588 0.494563 0.479703 0.464109 0.447882 0.431124 0.41394 0.396432 0.3787 0.360843 0.342955 0.325126 0.307441 0.28998 0.272817 0.256018 0.239643 0.223746 0.208373 0.193563 0.17935 0.165758 0.152808 0.140511 0.128876 0.117905 0.107593 0.0979343 0.0889161 0.0805234 0.0727377 0.065538 0.058901 0.0528017 0.0472139 0.0421102 0.0374628 0.0332437 0.0294249 0.0259786 0.0228777 0.0200958 0.0176074 0.0153879 0.013414 0.0116637 0.010116 0.0087514 0.00755165 0.00649984 +0.00684003 0.0079469 0.00920944 0.0106455 0.0122742 0.0141161 0.0161933 0.0185289 0.0211476 0.0240751 0.0273383 0.0309649 0.0349836 0.0394235 0.0443142 0.049685 0.0555653 0.0619838 0.0689682 0.0765448 0.0847379 0.0935699 0.10306 0.113225 0.124076 0.135622 0.147866 0.160806 0.174434 0.188737 0.203694 0.219279 0.235456 0.252185 0.269417 0.287096 0.305158 0.323532 0.342143 0.360905 0.379729 0.398521 0.41718 0.435605 0.453689 0.471323 0.4884 0.504811 0.520448 0.535207 0.548987 0.561693 0.573233 0.583526 0.592494 0.600074 0.606207 0.610848 0.613961 0.615524 0.615524 0.613961 0.610848 0.606207 0.600074 0.592494 0.583526 0.573233 0.561693 0.548987 0.535207 0.520448 0.504811 0.4884 0.471323 0.453689 0.435605 0.41718 0.398521 0.379729 0.360905 0.342143 0.323532 0.305158 0.287096 0.269417 0.252185 0.235456 0.219279 0.203694 0.188737 0.174434 0.160806 0.147866 0.135622 0.124076 0.113225 0.10306 0.0935699 0.0847379 0.0765448 0.0689682 0.0619838 0.0555653 0.049685 0.0443142 0.0394235 0.0349836 0.0309649 0.0273383 0.0240751 0.0211476 0.0185289 0.0161933 0.0141161 0.0122742 0.0106455 0.00920944 0.0079469 0.00684003 +0.00717969 0.00834152 0.00966676 0.0111741 0.0128837 0.0148171 0.0169974 0.019449 0.0221977 0.0252706 0.0286958 0.0325026 0.0367208 0.0413812 0.0465147 0.0521522 0.0583246 0.0650618 0.072393 0.0803458 0.0889459 0.0982164 0.108178 0.118847 0.130237 0.142356 0.155208 0.168791 0.183096 0.198109 0.213809 0.230168 0.247149 0.264708 0.282796 0.301352 0.320311 0.339598 0.359133 0.378826 0.398585 0.41831 0.437897 0.457236 0.476218 0.494728 0.512653 0.529878 0.546292 0.561784 0.576249 0.589585 0.601699 0.612502 0.621916 0.629872 0.63631 0.641181 0.64445 0.64609 0.64609 0.64445 0.641181 0.63631 0.629872 0.621916 0.612502 0.601699 0.589585 0.576249 0.561784 0.546292 0.529878 0.512653 0.494728 0.476218 0.457236 0.437897 0.41831 0.398585 0.378826 0.359133 0.339598 0.320311 0.301352 0.282796 0.264708 0.247149 0.230168 0.213809 0.198109 0.183096 0.168791 0.155208 0.142356 0.130237 0.118847 0.108178 0.0982164 0.0889459 0.0803458 0.072393 0.0650618 0.0583246 0.0521522 0.0465147 0.0413812 0.0367208 0.0325026 0.0286958 0.0252706 0.0221977 0.019449 0.0169974 0.0148171 0.0128837 0.0111741 0.00966676 0.00834152 0.00717969 +0.00751702 0.00873344 0.0101209 0.0116991 0.013489 0.0155133 0.017796 0.0203628 0.0232407 0.0264579 0.0300441 0.0340297 0.0384461 0.0433255 0.0487002 0.0546026 0.0610649 0.0681187 0.0757944 0.0841208 0.0931249 0.102831 0.11326 0.124431 0.136356 0.149045 0.162501 0.176721 0.191699 0.207417 0.223855 0.240982 0.258761 0.277145 0.296083 0.315511 0.335361 0.355554 0.376006 0.396625 0.417313 0.437964 0.458471 0.478719 0.498593 0.517972 0.536739 0.554774 0.571959 0.588179 0.603324 0.617287 0.629969 0.64128 0.651137 0.659466 0.666206 0.671306 0.674728 0.676446 0.676446 0.674728 0.671306 0.666206 0.659466 0.651137 0.64128 0.629969 0.617287 0.603324 0.588179 0.571959 0.554774 0.536739 0.517972 0.498593 0.478719 0.458471 0.437964 0.417313 0.396625 0.376006 0.355554 0.335361 0.315511 0.296083 0.277145 0.258761 0.240982 0.223855 0.207417 0.191699 0.176721 0.162501 0.149045 0.136356 0.124431 0.11326 0.102831 0.0931249 0.0841208 0.0757944 0.0681187 0.0610649 0.0546026 0.0487002 0.0433255 0.0384461 0.0340297 0.0300441 0.0264579 0.0232407 0.0203628 0.017796 0.0155133 0.013489 0.0116991 0.0101209 0.00873344 0.00751702 +0.00785015 0.00912048 0.0105695 0.0122176 0.0140868 0.0162008 0.0185847 0.0212652 0.0242706 0.0276305 0.0313755 0.0355378 0.0401499 0.0452455 0.0508584 0.0570224 0.0637711 0.0711375 0.0791533 0.0878488 0.0972519 0.107388 0.11828 0.129945 0.142399 0.15565 0.169702 0.184553 0.200194 0.216609 0.233775 0.251661 0.270228 0.289428 0.309204 0.329494 0.350223 0.371311 0.39267 0.414202 0.435807 0.457373 0.478789 0.499934 0.520689 0.540927 0.560526 0.57936 0.597307 0.614245 0.630061 0.644643 0.657887 0.6697 0.679993 0.688691 0.69573 0.701057 0.70463 0.706424 0.706424 0.70463 0.701057 0.69573 0.688691 0.679993 0.6697 0.657887 0.644643 0.630061 0.614245 0.597307 0.57936 0.560526 0.540927 0.520689 0.499934 0.478789 0.457373 0.435807 0.414202 0.39267 0.371311 0.350223 0.329494 0.309204 0.289428 0.270228 0.251661 0.233775 0.216609 0.200194 0.184553 0.169702 0.15565 0.142399 0.129945 0.11828 0.107388 0.0972519 0.0878488 0.0791533 0.0711375 0.0637711 0.0570224 0.0508584 0.0452455 0.0401499 0.0355378 0.0313755 0.0276305 0.0242706 0.0212652 0.0185847 0.0162008 0.0140868 0.0122176 0.0105695 0.00912048 0.00785015 +0.00817716 0.00950041 0.0110098 0.0127265 0.0146736 0.0168756 0.0193588 0.0221511 0.0252817 0.0287814 0.0326825 0.0370181 0.0418225 0.0471303 0.052977 0.0593977 0.0664276 0.0741008 0.0824506 0.0915082 0.101303 0.111862 0.123207 0.135358 0.148331 0.162134 0.176771 0.192241 0.208533 0.225632 0.243514 0.262145 0.281485 0.301484 0.322085 0.343219 0.364812 0.386779 0.409027 0.431457 0.453961 0.476426 0.498733 0.52076 0.542379 0.56346 0.583876 0.603494 0.622188 0.639833 0.656307 0.671496 0.685293 0.697597 0.708319 0.71738 0.724712 0.73026 0.733983 0.735851 0.735851 0.733983 0.73026 0.724712 0.71738 0.708319 0.697597 0.685293 0.671496 0.656307 0.639833 0.622188 0.603494 0.583876 0.56346 0.542379 0.52076 0.498733 0.476426 0.453961 0.431457 0.409027 0.386779 0.364812 0.343219 0.322085 0.301484 0.281485 0.262145 0.243514 0.225632 0.208533 0.192241 0.176771 0.162134 0.148331 0.135358 0.123207 0.111862 0.101303 0.0915082 0.0824506 0.0741008 0.0664276 0.0593977 0.052977 0.0471303 0.0418225 0.0370181 0.0326825 0.0287814 0.0252817 0.0221511 0.0193588 0.0168756 0.0146736 0.0127265 0.0110098 0.00950041 0.00817716 +0.00849609 0.00987095 0.0114392 0.0132229 0.0152459 0.0175338 0.0201139 0.023015 0.0262677 0.029904 0.0339572 0.038462 0.0434536 0.0489685 0.0550432 0.0617144 0.0690185 0.076991 0.0856664 0.0950773 0.105254 0.116224 0.128012 0.140638 0.154116 0.168458 0.183666 0.199739 0.216667 0.234433 0.253011 0.272369 0.292463 0.313243 0.334647 0.356606 0.379041 0.401864 0.42498 0.448285 0.471666 0.495008 0.518185 0.541071 0.563533 0.585437 0.606648 0.627032 0.646455 0.664788 0.681905 0.697687 0.712021 0.724805 0.735945 0.74536 0.752978 0.758742 0.76261 0.764551 0.764551 0.76261 0.758742 0.752978 0.74536 0.735945 0.724805 0.712021 0.697687 0.681905 0.664788 0.646455 0.627032 0.606648 0.585437 0.563533 0.541071 0.518185 0.495008 0.471666 0.448285 0.42498 0.401864 0.379041 0.356606 0.334647 0.313243 0.292463 0.272369 0.253011 0.234433 0.216667 0.199739 0.183666 0.168458 0.154116 0.140638 0.128012 0.116224 0.105254 0.0950773 0.0856664 0.076991 0.0690185 0.0617144 0.0550432 0.0489685 0.0434536 0.038462 0.0339572 0.029904 0.0262677 0.023015 0.0201139 0.0175338 0.0152459 0.0132229 0.0114392 0.00987095 0.00849609 +0.00880498 0.0102298 0.011855 0.0137036 0.0158002 0.0181713 0.0208451 0.0238517 0.0272227 0.0309912 0.0351918 0.0398603 0.0450334 0.0507488 0.0570444 0.0639581 0.0715277 0.0797901 0.0887809 0.0985339 0.109081 0.12045 0.132666 0.145751 0.159719 0.174582 0.190343 0.207001 0.224544 0.242956 0.26221 0.282271 0.303096 0.324631 0.346813 0.36957 0.392821 0.416474 0.440431 0.464582 0.488814 0.513004 0.537025 0.560742 0.584021 0.606721 0.628704 0.649828 0.669958 0.688957 0.706696 0.723052 0.737907 0.751156 0.762701 0.772458 0.780353 0.786327 0.790335 0.792347 0.792347 0.790335 0.786327 0.780353 0.772458 0.762701 0.751156 0.737907 0.723052 0.706696 0.688957 0.669958 0.649828 0.628704 0.606721 0.584021 0.560742 0.537025 0.513004 0.488814 0.464582 0.440431 0.416474 0.392821 0.36957 0.346813 0.324631 0.303096 0.282271 0.26221 0.242956 0.224544 0.207001 0.190343 0.174582 0.159719 0.145751 0.132666 0.12045 0.109081 0.0985339 0.0887809 0.0797901 0.0715277 0.0639581 0.0570444 0.0507488 0.0450334 0.0398603 0.0351918 0.0309912 0.0272227 0.0238517 0.0208451 0.0181713 0.0158002 0.0137036 0.011855 0.0102298 0.00880498 +0.00910185 0.0105747 0.0122548 0.0141656 0.0163329 0.0187839 0.021548 0.0246559 0.0281405 0.0320361 0.0363783 0.0412042 0.0465518 0.0524599 0.0589677 0.0661145 0.0739393 0.0824802 0.0917742 0.101856 0.112759 0.124511 0.137139 0.150665 0.165104 0.180468 0.196761 0.21398 0.232115 0.251147 0.271051 0.291788 0.313315 0.335576 0.358506 0.382031 0.406065 0.430516 0.45528 0.480246 0.505295 0.530301 0.555131 0.579648 0.603711 0.627177 0.649901 0.671738 0.692546 0.712186 0.730523 0.74743 0.762786 0.776482 0.788416 0.798502 0.806663 0.812839 0.816982 0.819062 0.819062 0.816982 0.812839 0.806663 0.798502 0.788416 0.776482 0.762786 0.74743 0.730523 0.712186 0.692546 0.671738 0.649901 0.627177 0.603711 0.579648 0.555131 0.530301 0.505295 0.480246 0.45528 0.430516 0.406065 0.382031 0.358506 0.335576 0.313315 0.291788 0.271051 0.251147 0.232115 0.21398 0.196761 0.180468 0.165104 0.150665 0.137139 0.124511 0.112759 0.101856 0.0917742 0.0824802 0.0739393 0.0661145 0.0589677 0.0524599 0.0465518 0.0412042 0.0363783 0.0320361 0.0281405 0.0246559 0.021548 0.0187839 0.0163329 0.0141656 0.0122548 0.0105747 0.00910185 +0.00938475 0.0109034 0.0126357 0.0146059 0.0168406 0.0193678 0.0222177 0.0254223 0.0290152 0.0330318 0.037509 0.0424849 0.0479987 0.0540904 0.0608005 0.0681695 0.0762375 0.0850439 0.0946267 0.105022 0.116263 0.128381 0.141402 0.155348 0.170236 0.186078 0.202877 0.220631 0.239329 0.258954 0.279475 0.300858 0.323054 0.346007 0.36965 0.393905 0.418687 0.443897 0.469431 0.495173 0.521001 0.546784 0.572386 0.597665 0.622476 0.646671 0.670101 0.692617 0.714072 0.734322 0.753229 0.770662 0.786496 0.800617 0.812922 0.823321 0.831736 0.838104 0.842376 0.84452 0.84452 0.842376 0.838104 0.831736 0.823321 0.812922 0.800617 0.786496 0.770662 0.753229 0.734322 0.714072 0.692617 0.670101 0.646671 0.622476 0.597665 0.572386 0.546784 0.521001 0.495173 0.469431 0.443897 0.418687 0.393905 0.36965 0.346007 0.323054 0.300858 0.279475 0.258954 0.239329 0.220631 0.202877 0.186078 0.170236 0.155348 0.141402 0.128381 0.116263 0.105022 0.0946267 0.0850439 0.0762375 0.0681695 0.0608005 0.0540904 0.0479987 0.0424849 0.037509 0.0330318 0.0290152 0.0254223 0.0222177 0.0193678 0.0168406 0.0146059 0.0126357 0.0109034 0.00938475 +0.0096518 0.0112137 0.0129952 0.0150216 0.0173198 0.0199189 0.0228499 0.0261457 0.0298409 0.0339718 0.0385764 0.0436939 0.0493646 0.0556296 0.0625306 0.0701093 0.0784069 0.0874639 0.0973194 0.10801 0.119572 0.132034 0.145426 0.159769 0.17508 0.191373 0.20865 0.226909 0.24614 0.266322 0.287428 0.309419 0.332247 0.355853 0.380168 0.405114 0.430601 0.456529 0.482789 0.509264 0.535826 0.562343 0.588673 0.614672 0.640189 0.665073 0.68917 0.712326 0.734391 0.755218 0.774663 0.792591 0.808876 0.823399 0.836055 0.84675 0.855404 0.861953 0.866346 0.868551 0.868551 0.866346 0.861953 0.855404 0.84675 0.836055 0.823399 0.808876 0.792591 0.774663 0.755218 0.734391 0.712326 0.68917 0.665073 0.640189 0.614672 0.588673 0.562343 0.535826 0.509264 0.482789 0.456529 0.430601 0.405114 0.380168 0.355853 0.332247 0.309419 0.287428 0.266322 0.24614 0.226909 0.20865 0.191373 0.17508 0.159769 0.145426 0.132034 0.119572 0.10801 0.0973194 0.0874639 0.0784069 0.0701093 0.0625306 0.0556296 0.0493646 0.0436939 0.0385764 0.0339718 0.0298409 0.0261457 0.0228499 0.0199189 0.0173198 0.0150216 0.0129952 0.0112137 0.0096518 +0.00990116 0.0115034 0.013331 0.0154096 0.0177672 0.0204335 0.0234403 0.0268212 0.0306118 0.0348495 0.039573 0.0448227 0.0506399 0.0570669 0.0641462 0.0719206 0.0804326 0.0897236 0.0998337 0.110801 0.122661 0.135445 0.149183 0.163896 0.179604 0.196317 0.21404 0.232771 0.252499 0.273203 0.294854 0.317413 0.340831 0.365046 0.38999 0.415581 0.441726 0.468324 0.495262 0.522421 0.54967 0.576871 0.603882 0.630552 0.656729 0.682255 0.706975 0.73073 0.753365 0.774729 0.794677 0.813069 0.829774 0.844672 0.857655 0.868626 0.877504 0.884222 0.888729 0.890991 0.890991 0.888729 0.884222 0.877504 0.868626 0.857655 0.844672 0.829774 0.813069 0.794677 0.774729 0.753365 0.73073 0.706975 0.682255 0.656729 0.630552 0.603882 0.576871 0.54967 0.522421 0.495262 0.468324 0.441726 0.415581 0.38999 0.365046 0.340831 0.317413 0.294854 0.273203 0.252499 0.232771 0.21404 0.196317 0.179604 0.163896 0.149183 0.135445 0.122661 0.110801 0.0998337 0.0897236 0.0804326 0.0719206 0.0641462 0.0570669 0.0506399 0.0448227 0.039573 0.0348495 0.0306118 0.0268212 0.0234403 0.0204335 0.0177672 0.0154096 0.013331 0.0115034 0.00990116 +0.0101311 0.0117705 0.0136405 0.0157675 0.0181798 0.020908 0.0239846 0.027444 0.0313227 0.0356588 0.040492 0.0458636 0.0518159 0.0583921 0.0656358 0.0735908 0.0823004 0.0918072 0.102152 0.113374 0.125509 0.138591 0.152647 0.167702 0.183775 0.200876 0.219011 0.238177 0.258362 0.279547 0.301701 0.324784 0.348745 0.373524 0.399047 0.425231 0.451984 0.479199 0.506764 0.534553 0.562434 0.590268 0.617906 0.645195 0.67198 0.698099 0.723392 0.747699 0.77086 0.79272 0.813131 0.83195 0.849043 0.864287 0.877571 0.888797 0.897882 0.904755 0.909367 0.911682 0.911682 0.909367 0.904755 0.897882 0.888797 0.877571 0.864287 0.849043 0.83195 0.813131 0.79272 0.77086 0.747699 0.723392 0.698099 0.67198 0.645195 0.617906 0.590268 0.562434 0.534553 0.506764 0.479199 0.451984 0.425231 0.399047 0.373524 0.348745 0.324784 0.301701 0.279547 0.258362 0.238177 0.219011 0.200876 0.183775 0.167702 0.152647 0.138591 0.125509 0.113374 0.102152 0.0918072 0.0823004 0.0735908 0.0656358 0.0583921 0.0518159 0.0458636 0.040492 0.0356588 0.0313227 0.027444 0.0239846 0.020908 0.0181798 0.0157675 0.0136405 0.0117705 0.0101311 +0.01034 0.0120132 0.0139217 0.0160926 0.0185546 0.0213391 0.0244791 0.0280098 0.0319684 0.0363939 0.0413268 0.0468091 0.0528841 0.0595959 0.0669889 0.0751079 0.0839971 0.0936999 0.104258 0.115711 0.128097 0.141448 0.155794 0.17116 0.187563 0.205017 0.223526 0.243087 0.263689 0.28531 0.307921 0.33148 0.355935 0.381224 0.407273 0.433998 0.461302 0.489078 0.517211 0.545573 0.574029 0.602437 0.630644 0.658496 0.685833 0.712491 0.738306 0.763113 0.786752 0.809063 0.829895 0.849101 0.866547 0.882105 0.895663 0.907121 0.916392 0.923408 0.928115 0.930477 0.930477 0.928115 0.923408 0.916392 0.907121 0.895663 0.882105 0.866547 0.849101 0.829895 0.809063 0.786752 0.763113 0.738306 0.712491 0.685833 0.658496 0.630644 0.602437 0.574029 0.545573 0.517211 0.489078 0.461302 0.433998 0.407273 0.381224 0.355935 0.33148 0.307921 0.28531 0.263689 0.243087 0.223526 0.205017 0.187563 0.17116 0.155794 0.141448 0.128097 0.115711 0.104258 0.0936999 0.0839971 0.0751079 0.0669889 0.0595959 0.0528841 0.0468091 0.0413268 0.0363939 0.0319684 0.0280098 0.0244791 0.0213391 0.0185546 0.0160926 0.0139217 0.0120132 0.01034 +0.0105262 0.0122296 0.0141726 0.0163825 0.0188889 0.0217235 0.0249201 0.0285144 0.0325444 0.0370495 0.0420713 0.0476524 0.0538369 0.0606695 0.0681958 0.076461 0.0855104 0.0953879 0.106136 0.117796 0.130405 0.143996 0.158601 0.174243 0.190942 0.20871 0.227553 0.247466 0.268439 0.29045 0.313468 0.337452 0.362347 0.388092 0.414611 0.441817 0.469612 0.497889 0.526529 0.555402 0.584371 0.61329 0.642006 0.67036 0.698189 0.725327 0.751607 0.776861 0.800926 0.823639 0.844846 0.864398 0.882158 0.897997 0.911799 0.923463 0.932901 0.940043 0.944835 0.94724 0.94724 0.944835 0.940043 0.932901 0.923463 0.911799 0.897997 0.882158 0.864398 0.844846 0.823639 0.800926 0.776861 0.751607 0.725327 0.698189 0.67036 0.642006 0.61329 0.584371 0.555402 0.526529 0.497889 0.469612 0.441817 0.414611 0.388092 0.362347 0.337452 0.313468 0.29045 0.268439 0.247466 0.227553 0.20871 0.190942 0.174243 0.158601 0.143996 0.130405 0.117796 0.106136 0.0953879 0.0855104 0.076461 0.0681958 0.0606695 0.0538369 0.0476524 0.0420713 0.0370495 0.0325444 0.0285144 0.0249201 0.0217235 0.0188889 0.0163825 0.0141726 0.0122296 0.0105262 +0.0106886 0.0124182 0.0143911 0.0166351 0.0191802 0.0220585 0.0253044 0.0289542 0.0330463 0.0376209 0.0427201 0.0483873 0.0546672 0.0616052 0.0692475 0.0776402 0.0868291 0.096859 0.107773 0.119613 0.132416 0.146217 0.161047 0.17693 0.193887 0.211929 0.231062 0.251283 0.272579 0.29493 0.318303 0.342656 0.367936 0.394077 0.421005 0.44863 0.476855 0.505568 0.534649 0.563968 0.593383 0.622748 0.651907 0.680698 0.708956 0.736513 0.763198 0.788842 0.813278 0.836341 0.857875 0.877729 0.895763 0.911846 0.925861 0.937705 0.947289 0.954541 0.959407 0.961849 0.961849 0.959407 0.954541 0.947289 0.937705 0.925861 0.911846 0.895763 0.877729 0.857875 0.836341 0.813278 0.788842 0.763198 0.736513 0.708956 0.680698 0.651907 0.622748 0.593383 0.563968 0.534649 0.505568 0.476855 0.44863 0.421005 0.394077 0.367936 0.342656 0.318303 0.29493 0.272579 0.251283 0.231062 0.211929 0.193887 0.17693 0.161047 0.146217 0.132416 0.119613 0.107773 0.096859 0.0868291 0.0776402 0.0692475 0.0616052 0.0546672 0.0483873 0.0427201 0.0376209 0.0330463 0.0289542 0.0253044 0.0220585 0.0191802 0.0166351 0.0143911 0.0124182 0.0106886 +0.0108258 0.0125776 0.0145758 0.0168486 0.0194264 0.0223417 0.0256292 0.0293258 0.0334704 0.0381038 0.0432685 0.0490084 0.0553688 0.0623959 0.0701363 0.0786368 0.0879436 0.0981023 0.109156 0.121148 0.134115 0.148094 0.163114 0.179201 0.196376 0.214649 0.234028 0.254508 0.276078 0.298715 0.322388 0.347054 0.372658 0.399136 0.426409 0.454389 0.482975 0.512057 0.541512 0.571206 0.601 0.630741 0.660274 0.689435 0.718056 0.745967 0.772994 0.798967 0.823716 0.847076 0.868886 0.888995 0.907261 0.92355 0.937745 0.949741 0.959448 0.966793 0.971721 0.974195 0.974195 0.971721 0.966793 0.959448 0.949741 0.937745 0.92355 0.907261 0.888995 0.868886 0.847076 0.823716 0.798967 0.772994 0.745967 0.718056 0.689435 0.660274 0.630741 0.601 0.571206 0.541512 0.512057 0.482975 0.454389 0.426409 0.399136 0.372658 0.347054 0.322388 0.298715 0.276078 0.254508 0.234028 0.214649 0.196376 0.179201 0.163114 0.148094 0.134115 0.121148 0.109156 0.0981023 0.0879436 0.0786368 0.0701363 0.0623959 0.0553688 0.0490084 0.0432685 0.0381038 0.0334704 0.0293258 0.0256292 0.0223417 0.0194264 0.0168486 0.0145758 0.0125776 0.0108258 +0.0109368 0.0127066 0.0147253 0.0170214 0.0196256 0.0225708 0.025892 0.0296266 0.0338137 0.0384946 0.0437122 0.049511 0.0559367 0.0630358 0.0708556 0.0794432 0.0888455 0.0991083 0.110276 0.12239 0.135491 0.149613 0.164787 0.181039 0.19839 0.216851 0.236428 0.257118 0.278909 0.301779 0.325695 0.350613 0.37648 0.403229 0.430782 0.459049 0.487928 0.517308 0.547065 0.577064 0.607163 0.63721 0.667046 0.696506 0.72542 0.753617 0.780921 0.807161 0.832164 0.855763 0.877797 0.898112 0.916565 0.933021 0.947362 0.959481 0.969287 0.976708 0.981686 0.984185 0.984185 0.981686 0.976708 0.969287 0.959481 0.947362 0.933021 0.916565 0.898112 0.877797 0.855763 0.832164 0.807161 0.780921 0.753617 0.72542 0.696506 0.667046 0.63721 0.607163 0.577064 0.547065 0.517308 0.487928 0.459049 0.430782 0.403229 0.37648 0.350613 0.325695 0.301779 0.278909 0.257118 0.236428 0.216851 0.19839 0.181039 0.164787 0.149613 0.135491 0.12239 0.110276 0.0991083 0.0888455 0.0794432 0.0708556 0.0630358 0.0559367 0.049511 0.0437122 0.0384946 0.0338137 0.0296266 0.025892 0.0225708 0.0196256 0.0170214 0.0147253 0.0127066 0.0109368 +0.0110208 0.0128042 0.0148384 0.0171522 0.0197764 0.0227442 0.0260909 0.0298542 0.0340734 0.0387903 0.044048 0.0498913 0.0563663 0.06352 0.0713999 0.0800535 0.089528 0.0998696 0.111123 0.123331 0.136532 0.150762 0.166053 0.18243 0.199913 0.218516 0.238244 0.259093 0.281052 0.304097 0.328196 0.353306 0.379372 0.406326 0.434091 0.462575 0.491677 0.521282 0.551267 0.581497 0.611827 0.642105 0.67217 0.701856 0.730992 0.759406 0.78692 0.813361 0.838556 0.862337 0.88454 0.905011 0.923605 0.940188 0.954639 0.966851 0.976733 0.984211 0.989227 0.991745 0.991745 0.989227 0.984211 0.976733 0.966851 0.954639 0.940188 0.923605 0.905011 0.88454 0.862337 0.838556 0.813361 0.78692 0.759406 0.730992 0.701856 0.67217 0.642105 0.611827 0.581497 0.551267 0.521282 0.491677 0.462575 0.434091 0.406326 0.379372 0.353306 0.328196 0.304097 0.281052 0.259093 0.238244 0.218516 0.199913 0.18243 0.166053 0.150762 0.136532 0.123331 0.111123 0.0998696 0.089528 0.0800535 0.0713999 0.06352 0.0563663 0.0498913 0.044048 0.0387903 0.0340734 0.0298542 0.0260909 0.0227442 0.0197764 0.0171522 0.0148384 0.0128042 0.0110208 +0.0110772 0.0128697 0.0149143 0.0172399 0.0198775 0.0228605 0.0262244 0.0300068 0.0342477 0.0389887 0.0442733 0.0501465 0.0566546 0.0638449 0.0717651 0.0804629 0.0899859 0.10038 0.111691 0.123961 0.13723 0.151533 0.166902 0.183363 0.200936 0.219634 0.239463 0.260418 0.282489 0.305652 0.329875 0.355113 0.381312 0.408404 0.436311 0.464941 0.494191 0.523948 0.554087 0.584471 0.614956 0.645389 0.675608 0.705446 0.734731 0.76329 0.790945 0.817521 0.842845 0.866747 0.889064 0.90964 0.928329 0.944997 0.959522 0.971796 0.981728 0.989244 0.994287 0.996818 0.996818 0.994287 0.989244 0.981728 0.971796 0.959522 0.944997 0.928329 0.90964 0.889064 0.866747 0.842845 0.817521 0.790945 0.76329 0.734731 0.705446 0.675608 0.645389 0.614956 0.584471 0.554087 0.523948 0.494191 0.464941 0.436311 0.408404 0.381312 0.355113 0.329875 0.305652 0.282489 0.260418 0.239463 0.219634 0.200936 0.183363 0.166902 0.151533 0.13723 0.123961 0.111691 0.10038 0.0899859 0.0804629 0.0717651 0.0638449 0.0566546 0.0501465 0.0442733 0.0389887 0.0342477 0.0300068 0.0262244 0.0228605 0.0198775 0.0172399 0.0149143 0.0128697 0.0110772 +0.0111055 0.0129026 0.0149524 0.0172839 0.0199283 0.0229189 0.0262913 0.0300835 0.0343352 0.0390883 0.0443863 0.0502746 0.0567993 0.064008 0.0719484 0.0806684 0.0902157 0.100637 0.111977 0.124278 0.13758 0.15192 0.167328 0.183831 0.201449 0.220195 0.240074 0.261084 0.283211 0.306433 0.330718 0.35602 0.382286 0.409448 0.437425 0.466128 0.495453 0.525286 0.555502 0.585964 0.616527 0.647037 0.677333 0.707247 0.736608 0.765239 0.792965 0.819609 0.844998 0.868961 0.891335 0.911963 0.9307 0.947411 0.961972 0.974278 0.984236 0.991771 0.996826 0.999364 0.999364 0.996826 0.991771 0.984236 0.974278 0.961972 0.947411 0.9307 0.911963 0.891335 0.868961 0.844998 0.819609 0.792965 0.765239 0.736608 0.707247 0.677333 0.647037 0.616527 0.585964 0.555502 0.525286 0.495453 0.466128 0.437425 0.409448 0.382286 0.35602 0.330718 0.306433 0.283211 0.261084 0.240074 0.220195 0.201449 0.183831 0.167328 0.15192 0.13758 0.124278 0.111977 0.100637 0.0902157 0.0806684 0.0719484 0.064008 0.0567993 0.0502746 0.0443863 0.0390883 0.0343352 0.0300835 0.0262913 0.0229189 0.0199283 0.0172839 0.0149524 0.0129026 0.0111055 +0.0111055 0.0129026 0.0149524 0.0172839 0.0199283 0.0229189 0.0262913 0.0300835 0.0343352 0.0390883 0.0443863 0.0502746 0.0567993 0.064008 0.0719484 0.0806684 0.0902157 0.100637 0.111977 0.124278 0.13758 0.15192 0.167328 0.183831 0.201449 0.220195 0.240074 0.261084 0.283211 0.306433 0.330718 0.35602 0.382286 0.409448 0.437425 0.466128 0.495453 0.525286 0.555502 0.585964 0.616527 0.647037 0.677333 0.707247 0.736608 0.765239 0.792965 0.819609 0.844998 0.868961 0.891335 0.911963 0.9307 0.947411 0.961972 0.974278 0.984236 0.991771 0.996826 0.999364 0.999364 0.996826 0.991771 0.984236 0.974278 0.961972 0.947411 0.9307 0.911963 0.891335 0.868961 0.844998 0.819609 0.792965 0.765239 0.736608 0.707247 0.677333 0.647037 0.616527 0.585964 0.555502 0.525286 0.495453 0.466128 0.437425 0.409448 0.382286 0.35602 0.330718 0.306433 0.283211 0.261084 0.240074 0.220195 0.201449 0.183831 0.167328 0.15192 0.13758 0.124278 0.111977 0.100637 0.0902157 0.0806684 0.0719484 0.064008 0.0567993 0.0502746 0.0443863 0.0390883 0.0343352 0.0300835 0.0262913 0.0229189 0.0199283 0.0172839 0.0149524 0.0129026 0.0111055 +0.0110772 0.0128697 0.0149143 0.0172399 0.0198775 0.0228605 0.0262244 0.0300068 0.0342477 0.0389887 0.0442733 0.0501465 0.0566546 0.0638449 0.0717651 0.0804629 0.0899859 0.10038 0.111691 0.123961 0.13723 0.151533 0.166902 0.183363 0.200936 0.219634 0.239463 0.260418 0.282489 0.305652 0.329875 0.355113 0.381312 0.408404 0.436311 0.464941 0.494191 0.523948 0.554087 0.584471 0.614956 0.645389 0.675608 0.705446 0.734731 0.76329 0.790945 0.817521 0.842845 0.866747 0.889064 0.90964 0.928329 0.944997 0.959522 0.971796 0.981728 0.989244 0.994287 0.996818 0.996818 0.994287 0.989244 0.981728 0.971796 0.959522 0.944997 0.928329 0.90964 0.889064 0.866747 0.842845 0.817521 0.790945 0.76329 0.734731 0.705446 0.675608 0.645389 0.614956 0.584471 0.554087 0.523948 0.494191 0.464941 0.436311 0.408404 0.381312 0.355113 0.329875 0.305652 0.282489 0.260418 0.239463 0.219634 0.200936 0.183363 0.166902 0.151533 0.13723 0.123961 0.111691 0.10038 0.0899859 0.0804629 0.0717651 0.0638449 0.0566546 0.0501465 0.0442733 0.0389887 0.0342477 0.0300068 0.0262244 0.0228605 0.0198775 0.0172399 0.0149143 0.0128697 0.0110772 +0.0110208 0.0128042 0.0148384 0.0171522 0.0197764 0.0227442 0.0260909 0.0298542 0.0340734 0.0387903 0.044048 0.0498913 0.0563663 0.06352 0.0713999 0.0800535 0.089528 0.0998696 0.111123 0.123331 0.136532 0.150762 0.166053 0.18243 0.199913 0.218516 0.238244 0.259093 0.281052 0.304097 0.328196 0.353306 0.379372 0.406326 0.434091 0.462575 0.491677 0.521282 0.551267 0.581497 0.611827 0.642105 0.67217 0.701856 0.730992 0.759406 0.78692 0.813361 0.838556 0.862337 0.88454 0.905011 0.923605 0.940188 0.954639 0.966851 0.976733 0.984211 0.989227 0.991745 0.991745 0.989227 0.984211 0.976733 0.966851 0.954639 0.940188 0.923605 0.905011 0.88454 0.862337 0.838556 0.813361 0.78692 0.759406 0.730992 0.701856 0.67217 0.642105 0.611827 0.581497 0.551267 0.521282 0.491677 0.462575 0.434091 0.406326 0.379372 0.353306 0.328196 0.304097 0.281052 0.259093 0.238244 0.218516 0.199913 0.18243 0.166053 0.150762 0.136532 0.123331 0.111123 0.0998696 0.089528 0.0800535 0.0713999 0.06352 0.0563663 0.0498913 0.044048 0.0387903 0.0340734 0.0298542 0.0260909 0.0227442 0.0197764 0.0171522 0.0148384 0.0128042 0.0110208 +0.0109368 0.0127066 0.0147253 0.0170214 0.0196256 0.0225708 0.025892 0.0296266 0.0338137 0.0384946 0.0437122 0.049511 0.0559367 0.0630358 0.0708556 0.0794432 0.0888455 0.0991083 0.110276 0.12239 0.135491 0.149613 0.164787 0.181039 0.19839 0.216851 0.236428 0.257118 0.278909 0.301779 0.325695 0.350613 0.37648 0.403229 0.430782 0.459049 0.487928 0.517308 0.547065 0.577064 0.607163 0.63721 0.667046 0.696506 0.72542 0.753617 0.780921 0.807161 0.832164 0.855763 0.877797 0.898112 0.916565 0.933021 0.947362 0.959481 0.969287 0.976708 0.981686 0.984185 0.984185 0.981686 0.976708 0.969287 0.959481 0.947362 0.933021 0.916565 0.898112 0.877797 0.855763 0.832164 0.807161 0.780921 0.753617 0.72542 0.696506 0.667046 0.63721 0.607163 0.577064 0.547065 0.517308 0.487928 0.459049 0.430782 0.403229 0.37648 0.350613 0.325695 0.301779 0.278909 0.257118 0.236428 0.216851 0.19839 0.181039 0.164787 0.149613 0.135491 0.12239 0.110276 0.0991083 0.0888455 0.0794432 0.0708556 0.0630358 0.0559367 0.049511 0.0437122 0.0384946 0.0338137 0.0296266 0.025892 0.0225708 0.0196256 0.0170214 0.0147253 0.0127066 0.0109368 +0.0108258 0.0125776 0.0145758 0.0168486 0.0194264 0.0223417 0.0256292 0.0293258 0.0334704 0.0381038 0.0432685 0.0490084 0.0553688 0.0623959 0.0701363 0.0786368 0.0879436 0.0981023 0.109156 0.121148 0.134115 0.148094 0.163114 0.179201 0.196376 0.214649 0.234028 0.254508 0.276078 0.298715 0.322388 0.347054 0.372658 0.399136 0.426409 0.454389 0.482975 0.512057 0.541512 0.571206 0.601 0.630741 0.660274 0.689435 0.718056 0.745967 0.772994 0.798967 0.823716 0.847076 0.868886 0.888995 0.907261 0.92355 0.937745 0.949741 0.959448 0.966793 0.971721 0.974195 0.974195 0.971721 0.966793 0.959448 0.949741 0.937745 0.92355 0.907261 0.888995 0.868886 0.847076 0.823716 0.798967 0.772994 0.745967 0.718056 0.689435 0.660274 0.630741 0.601 0.571206 0.541512 0.512057 0.482975 0.454389 0.426409 0.399136 0.372658 0.347054 0.322388 0.298715 0.276078 0.254508 0.234028 0.214649 0.196376 0.179201 0.163114 0.148094 0.134115 0.121148 0.109156 0.0981023 0.0879436 0.0786368 0.0701363 0.0623959 0.0553688 0.0490084 0.0432685 0.0381038 0.0334704 0.0293258 0.0256292 0.0223417 0.0194264 0.0168486 0.0145758 0.0125776 0.0108258 +0.0106886 0.0124182 0.0143911 0.0166351 0.0191802 0.0220585 0.0253044 0.0289542 0.0330463 0.0376209 0.0427201 0.0483873 0.0546672 0.0616052 0.0692475 0.0776402 0.0868291 0.096859 0.107773 0.119613 0.132416 0.146217 0.161047 0.17693 0.193887 0.211929 0.231062 0.251283 0.272579 0.29493 0.318303 0.342656 0.367936 0.394077 0.421005 0.44863 0.476855 0.505568 0.534649 0.563968 0.593383 0.622748 0.651907 0.680698 0.708956 0.736513 0.763198 0.788842 0.813278 0.836341 0.857875 0.877729 0.895763 0.911846 0.925861 0.937705 0.947289 0.954541 0.959407 0.961849 0.961849 0.959407 0.954541 0.947289 0.937705 0.925861 0.911846 0.895763 0.877729 0.857875 0.836341 0.813278 0.788842 0.763198 0.736513 0.708956 0.680698 0.651907 0.622748 0.593383 0.563968 0.534649 0.505568 0.476855 0.44863 0.421005 0.394077 0.367936 0.342656 0.318303 0.29493 0.272579 0.251283 0.231062 0.211929 0.193887 0.17693 0.161047 0.146217 0.132416 0.119613 0.107773 0.096859 0.0868291 0.0776402 0.0692475 0.0616052 0.0546672 0.0483873 0.0427201 0.0376209 0.0330463 0.0289542 0.0253044 0.0220585 0.0191802 0.0166351 0.0143911 0.0124182 0.0106886 +0.0105262 0.0122296 0.0141726 0.0163825 0.0188889 0.0217235 0.0249201 0.0285144 0.0325444 0.0370495 0.0420713 0.0476524 0.0538369 0.0606695 0.0681958 0.076461 0.0855104 0.0953879 0.106136 0.117796 0.130405 0.143996 0.158601 0.174243 0.190942 0.20871 0.227553 0.247466 0.268439 0.29045 0.313468 0.337452 0.362347 0.388092 0.414611 0.441817 0.469612 0.497889 0.526529 0.555402 0.584371 0.61329 0.642006 0.67036 0.698189 0.725327 0.751607 0.776861 0.800926 0.823639 0.844846 0.864398 0.882158 0.897997 0.911799 0.923463 0.932901 0.940043 0.944835 0.94724 0.94724 0.944835 0.940043 0.932901 0.923463 0.911799 0.897997 0.882158 0.864398 0.844846 0.823639 0.800926 0.776861 0.751607 0.725327 0.698189 0.67036 0.642006 0.61329 0.584371 0.555402 0.526529 0.497889 0.469612 0.441817 0.414611 0.388092 0.362347 0.337452 0.313468 0.29045 0.268439 0.247466 0.227553 0.20871 0.190942 0.174243 0.158601 0.143996 0.130405 0.117796 0.106136 0.0953879 0.0855104 0.076461 0.0681958 0.0606695 0.0538369 0.0476524 0.0420713 0.0370495 0.0325444 0.0285144 0.0249201 0.0217235 0.0188889 0.0163825 0.0141726 0.0122296 0.0105262 +0.01034 0.0120132 0.0139217 0.0160926 0.0185546 0.0213391 0.0244791 0.0280098 0.0319684 0.0363939 0.0413268 0.0468091 0.0528841 0.0595959 0.0669889 0.0751079 0.0839971 0.0936999 0.104258 0.115711 0.128097 0.141448 0.155794 0.17116 0.187563 0.205017 0.223526 0.243087 0.263689 0.28531 0.307921 0.33148 0.355935 0.381224 0.407273 0.433998 0.461302 0.489078 0.517211 0.545573 0.574029 0.602437 0.630644 0.658496 0.685833 0.712491 0.738306 0.763113 0.786752 0.809063 0.829895 0.849101 0.866547 0.882105 0.895663 0.907121 0.916392 0.923408 0.928115 0.930477 0.930477 0.928115 0.923408 0.916392 0.907121 0.895663 0.882105 0.866547 0.849101 0.829895 0.809063 0.786752 0.763113 0.738306 0.712491 0.685833 0.658496 0.630644 0.602437 0.574029 0.545573 0.517211 0.489078 0.461302 0.433998 0.407273 0.381224 0.355935 0.33148 0.307921 0.28531 0.263689 0.243087 0.223526 0.205017 0.187563 0.17116 0.155794 0.141448 0.128097 0.115711 0.104258 0.0936999 0.0839971 0.0751079 0.0669889 0.0595959 0.0528841 0.0468091 0.0413268 0.0363939 0.0319684 0.0280098 0.0244791 0.0213391 0.0185546 0.0160926 0.0139217 0.0120132 0.01034 +0.0101311 0.0117705 0.0136405 0.0157675 0.0181798 0.020908 0.0239846 0.027444 0.0313227 0.0356588 0.040492 0.0458636 0.0518159 0.0583921 0.0656358 0.0735908 0.0823004 0.0918072 0.102152 0.113374 0.125509 0.138591 0.152647 0.167702 0.183775 0.200876 0.219011 0.238177 0.258362 0.279547 0.301701 0.324784 0.348745 0.373524 0.399047 0.425231 0.451984 0.479199 0.506764 0.534553 0.562434 0.590268 0.617906 0.645195 0.67198 0.698099 0.723392 0.747699 0.77086 0.79272 0.813131 0.83195 0.849043 0.864287 0.877571 0.888797 0.897882 0.904755 0.909367 0.911682 0.911682 0.909367 0.904755 0.897882 0.888797 0.877571 0.864287 0.849043 0.83195 0.813131 0.79272 0.77086 0.747699 0.723392 0.698099 0.67198 0.645195 0.617906 0.590268 0.562434 0.534553 0.506764 0.479199 0.451984 0.425231 0.399047 0.373524 0.348745 0.324784 0.301701 0.279547 0.258362 0.238177 0.219011 0.200876 0.183775 0.167702 0.152647 0.138591 0.125509 0.113374 0.102152 0.0918072 0.0823004 0.0735908 0.0656358 0.0583921 0.0518159 0.0458636 0.040492 0.0356588 0.0313227 0.027444 0.0239846 0.020908 0.0181798 0.0157675 0.0136405 0.0117705 0.0101311 +0.00990116 0.0115034 0.013331 0.0154096 0.0177672 0.0204335 0.0234403 0.0268212 0.0306118 0.0348495 0.039573 0.0448227 0.0506399 0.0570669 0.0641462 0.0719206 0.0804326 0.0897236 0.0998337 0.110801 0.122661 0.135445 0.149183 0.163896 0.179604 0.196317 0.21404 0.232771 0.252499 0.273203 0.294854 0.317413 0.340831 0.365046 0.38999 0.415581 0.441726 0.468324 0.495262 0.522421 0.54967 0.576871 0.603882 0.630552 0.656729 0.682255 0.706975 0.73073 0.753365 0.774729 0.794677 0.813069 0.829774 0.844672 0.857655 0.868626 0.877504 0.884222 0.888729 0.890991 0.890991 0.888729 0.884222 0.877504 0.868626 0.857655 0.844672 0.829774 0.813069 0.794677 0.774729 0.753365 0.73073 0.706975 0.682255 0.656729 0.630552 0.603882 0.576871 0.54967 0.522421 0.495262 0.468324 0.441726 0.415581 0.38999 0.365046 0.340831 0.317413 0.294854 0.273203 0.252499 0.232771 0.21404 0.196317 0.179604 0.163896 0.149183 0.135445 0.122661 0.110801 0.0998337 0.0897236 0.0804326 0.0719206 0.0641462 0.0570669 0.0506399 0.0448227 0.039573 0.0348495 0.0306118 0.0268212 0.0234403 0.0204335 0.0177672 0.0154096 0.013331 0.0115034 0.00990116 +0.0096518 0.0112137 0.0129952 0.0150216 0.0173198 0.0199189 0.0228499 0.0261457 0.0298409 0.0339718 0.0385764 0.0436939 0.0493646 0.0556296 0.0625306 0.0701093 0.0784069 0.0874639 0.0973194 0.10801 0.119572 0.132034 0.145426 0.159769 0.17508 0.191373 0.20865 0.226909 0.24614 0.266322 0.287428 0.309419 0.332247 0.355853 0.380168 0.405114 0.430601 0.456529 0.482789 0.509264 0.535826 0.562343 0.588673 0.614672 0.640189 0.665073 0.68917 0.712326 0.734391 0.755218 0.774663 0.792591 0.808876 0.823399 0.836055 0.84675 0.855404 0.861953 0.866346 0.868551 0.868551 0.866346 0.861953 0.855404 0.84675 0.836055 0.823399 0.808876 0.792591 0.774663 0.755218 0.734391 0.712326 0.68917 0.665073 0.640189 0.614672 0.588673 0.562343 0.535826 0.509264 0.482789 0.456529 0.430601 0.405114 0.380168 0.355853 0.332247 0.309419 0.287428 0.266322 0.24614 0.226909 0.20865 0.191373 0.17508 0.159769 0.145426 0.132034 0.119572 0.10801 0.0973194 0.0874639 0.0784069 0.0701093 0.0625306 0.0556296 0.0493646 0.0436939 0.0385764 0.0339718 0.0298409 0.0261457 0.0228499 0.0199189 0.0173198 0.0150216 0.0129952 0.0112137 0.0096518 +0.00938475 0.0109034 0.0126357 0.0146059 0.0168406 0.0193678 0.0222177 0.0254223 0.0290152 0.0330318 0.037509 0.0424849 0.0479987 0.0540904 0.0608005 0.0681695 0.0762375 0.0850439 0.0946267 0.105022 0.116263 0.128381 0.141402 0.155348 0.170236 0.186078 0.202877 0.220631 0.239329 0.258954 0.279475 0.300858 0.323054 0.346007 0.36965 0.393905 0.418687 0.443897 0.469431 0.495173 0.521001 0.546784 0.572386 0.597665 0.622476 0.646671 0.670101 0.692617 0.714072 0.734322 0.753229 0.770662 0.786496 0.800617 0.812922 0.823321 0.831736 0.838104 0.842376 0.84452 0.84452 0.842376 0.838104 0.831736 0.823321 0.812922 0.800617 0.786496 0.770662 0.753229 0.734322 0.714072 0.692617 0.670101 0.646671 0.622476 0.597665 0.572386 0.546784 0.521001 0.495173 0.469431 0.443897 0.418687 0.393905 0.36965 0.346007 0.323054 0.300858 0.279475 0.258954 0.239329 0.220631 0.202877 0.186078 0.170236 0.155348 0.141402 0.128381 0.116263 0.105022 0.0946267 0.0850439 0.0762375 0.0681695 0.0608005 0.0540904 0.0479987 0.0424849 0.037509 0.0330318 0.0290152 0.0254223 0.0222177 0.0193678 0.0168406 0.0146059 0.0126357 0.0109034 0.00938475 +0.00910185 0.0105747 0.0122548 0.0141656 0.0163329 0.0187839 0.021548 0.0246559 0.0281405 0.0320361 0.0363783 0.0412042 0.0465518 0.0524599 0.0589677 0.0661145 0.0739393 0.0824802 0.0917742 0.101856 0.112759 0.124511 0.137139 0.150665 0.165104 0.180468 0.196761 0.21398 0.232115 0.251147 0.271051 0.291788 0.313315 0.335576 0.358506 0.382031 0.406065 0.430516 0.45528 0.480246 0.505295 0.530301 0.555131 0.579648 0.603711 0.627177 0.649901 0.671738 0.692546 0.712186 0.730523 0.74743 0.762786 0.776482 0.788416 0.798502 0.806663 0.812839 0.816982 0.819062 0.819062 0.816982 0.812839 0.806663 0.798502 0.788416 0.776482 0.762786 0.74743 0.730523 0.712186 0.692546 0.671738 0.649901 0.627177 0.603711 0.579648 0.555131 0.530301 0.505295 0.480246 0.45528 0.430516 0.406065 0.382031 0.358506 0.335576 0.313315 0.291788 0.271051 0.251147 0.232115 0.21398 0.196761 0.180468 0.165104 0.150665 0.137139 0.124511 0.112759 0.101856 0.0917742 0.0824802 0.0739393 0.0661145 0.0589677 0.0524599 0.0465518 0.0412042 0.0363783 0.0320361 0.0281405 0.0246559 0.021548 0.0187839 0.0163329 0.0141656 0.0122548 0.0105747 0.00910185 +0.00880498 0.0102298 0.011855 0.0137036 0.0158002 0.0181713 0.0208451 0.0238517 0.0272227 0.0309912 0.0351918 0.0398603 0.0450334 0.0507488 0.0570444 0.0639581 0.0715277 0.0797901 0.0887809 0.0985339 0.109081 0.12045 0.132666 0.145751 0.159719 0.174582 0.190343 0.207001 0.224544 0.242956 0.26221 0.282271 0.303096 0.324631 0.346813 0.36957 0.392821 0.416474 0.440431 0.464582 0.488814 0.513004 0.537025 0.560742 0.584021 0.606721 0.628704 0.649828 0.669958 0.688957 0.706696 0.723052 0.737907 0.751156 0.762701 0.772458 0.780353 0.786327 0.790335 0.792347 0.792347 0.790335 0.786327 0.780353 0.772458 0.762701 0.751156 0.737907 0.723052 0.706696 0.688957 0.669958 0.649828 0.628704 0.606721 0.584021 0.560742 0.537025 0.513004 0.488814 0.464582 0.440431 0.416474 0.392821 0.36957 0.346813 0.324631 0.303096 0.282271 0.26221 0.242956 0.224544 0.207001 0.190343 0.174582 0.159719 0.145751 0.132666 0.12045 0.109081 0.0985339 0.0887809 0.0797901 0.0715277 0.0639581 0.0570444 0.0507488 0.0450334 0.0398603 0.0351918 0.0309912 0.0272227 0.0238517 0.0208451 0.0181713 0.0158002 0.0137036 0.011855 0.0102298 0.00880498 +0.00849609 0.00987095 0.0114392 0.0132229 0.0152459 0.0175338 0.0201139 0.023015 0.0262677 0.029904 0.0339572 0.038462 0.0434536 0.0489685 0.0550432 0.0617144 0.0690185 0.076991 0.0856664 0.0950773 0.105254 0.116224 0.128012 0.140638 0.154116 0.168458 0.183666 0.199739 0.216667 0.234433 0.253011 0.272369 0.292463 0.313243 0.334647 0.356606 0.379041 0.401864 0.42498 0.448285 0.471666 0.495008 0.518185 0.541071 0.563533 0.585437 0.606648 0.627032 0.646455 0.664788 0.681905 0.697687 0.712021 0.724805 0.735945 0.74536 0.752978 0.758742 0.76261 0.764551 0.764551 0.76261 0.758742 0.752978 0.74536 0.735945 0.724805 0.712021 0.697687 0.681905 0.664788 0.646455 0.627032 0.606648 0.585437 0.563533 0.541071 0.518185 0.495008 0.471666 0.448285 0.42498 0.401864 0.379041 0.356606 0.334647 0.313243 0.292463 0.272369 0.253011 0.234433 0.216667 0.199739 0.183666 0.168458 0.154116 0.140638 0.128012 0.116224 0.105254 0.0950773 0.0856664 0.076991 0.0690185 0.0617144 0.0550432 0.0489685 0.0434536 0.038462 0.0339572 0.029904 0.0262677 0.023015 0.0201139 0.0175338 0.0152459 0.0132229 0.0114392 0.00987095 0.00849609 +0.00817716 0.00950041 0.0110098 0.0127265 0.0146736 0.0168756 0.0193588 0.0221511 0.0252817 0.0287814 0.0326825 0.0370181 0.0418225 0.0471303 0.052977 0.0593977 0.0664276 0.0741008 0.0824506 0.0915082 0.101303 0.111862 0.123207 0.135358 0.148331 0.162134 0.176771 0.192241 0.208533 0.225632 0.243514 0.262145 0.281485 0.301484 0.322085 0.343219 0.364812 0.386779 0.409027 0.431457 0.453961 0.476426 0.498733 0.52076 0.542379 0.56346 0.583876 0.603494 0.622188 0.639833 0.656307 0.671496 0.685293 0.697597 0.708319 0.71738 0.724712 0.73026 0.733983 0.735851 0.735851 0.733983 0.73026 0.724712 0.71738 0.708319 0.697597 0.685293 0.671496 0.656307 0.639833 0.622188 0.603494 0.583876 0.56346 0.542379 0.52076 0.498733 0.476426 0.453961 0.431457 0.409027 0.386779 0.364812 0.343219 0.322085 0.301484 0.281485 0.262145 0.243514 0.225632 0.208533 0.192241 0.176771 0.162134 0.148331 0.135358 0.123207 0.111862 0.101303 0.0915082 0.0824506 0.0741008 0.0664276 0.0593977 0.052977 0.0471303 0.0418225 0.0370181 0.0326825 0.0287814 0.0252817 0.0221511 0.0193588 0.0168756 0.0146736 0.0127265 0.0110098 0.00950041 0.00817716 +0.00785015 0.00912048 0.0105695 0.0122176 0.0140868 0.0162008 0.0185847 0.0212652 0.0242706 0.0276305 0.0313755 0.0355378 0.0401499 0.0452455 0.0508584 0.0570224 0.0637711 0.0711375 0.0791533 0.0878488 0.0972519 0.107388 0.11828 0.129945 0.142399 0.15565 0.169702 0.184553 0.200194 0.216609 0.233775 0.251661 0.270228 0.289428 0.309204 0.329494 0.350223 0.371311 0.39267 0.414202 0.435807 0.457373 0.478789 0.499934 0.520689 0.540927 0.560526 0.57936 0.597307 0.614245 0.630061 0.644643 0.657887 0.6697 0.679993 0.688691 0.69573 0.701057 0.70463 0.706424 0.706424 0.70463 0.701057 0.69573 0.688691 0.679993 0.6697 0.657887 0.644643 0.630061 0.614245 0.597307 0.57936 0.560526 0.540927 0.520689 0.499934 0.478789 0.457373 0.435807 0.414202 0.39267 0.371311 0.350223 0.329494 0.309204 0.289428 0.270228 0.251661 0.233775 0.216609 0.200194 0.184553 0.169702 0.15565 0.142399 0.129945 0.11828 0.107388 0.0972519 0.0878488 0.0791533 0.0711375 0.0637711 0.0570224 0.0508584 0.0452455 0.0401499 0.0355378 0.0313755 0.0276305 0.0242706 0.0212652 0.0185847 0.0162008 0.0140868 0.0122176 0.0105695 0.00912048 0.00785015 +0.00751702 0.00873344 0.0101209 0.0116991 0.013489 0.0155133 0.017796 0.0203628 0.0232407 0.0264579 0.0300441 0.0340297 0.0384461 0.0433255 0.0487002 0.0546026 0.0610649 0.0681187 0.0757944 0.0841208 0.0931249 0.102831 0.11326 0.124431 0.136356 0.149045 0.162501 0.176721 0.191699 0.207417 0.223855 0.240982 0.258761 0.277145 0.296083 0.315511 0.335361 0.355554 0.376006 0.396625 0.417313 0.437964 0.458471 0.478719 0.498593 0.517972 0.536739 0.554774 0.571959 0.588179 0.603324 0.617287 0.629969 0.64128 0.651137 0.659466 0.666206 0.671306 0.674728 0.676446 0.676446 0.674728 0.671306 0.666206 0.659466 0.651137 0.64128 0.629969 0.617287 0.603324 0.588179 0.571959 0.554774 0.536739 0.517972 0.498593 0.478719 0.458471 0.437964 0.417313 0.396625 0.376006 0.355554 0.335361 0.315511 0.296083 0.277145 0.258761 0.240982 0.223855 0.207417 0.191699 0.176721 0.162501 0.149045 0.136356 0.124431 0.11326 0.102831 0.0931249 0.0841208 0.0757944 0.0681187 0.0610649 0.0546026 0.0487002 0.0433255 0.0384461 0.0340297 0.0300441 0.0264579 0.0232407 0.0203628 0.017796 0.0155133 0.013489 0.0116991 0.0101209 0.00873344 0.00751702 +0.00717969 0.00834152 0.00966676 0.0111741 0.0128837 0.0148171 0.0169974 0.019449 0.0221977 0.0252706 0.0286958 0.0325026 0.0367208 0.0413812 0.0465147 0.0521522 0.0583246 0.0650618 0.072393 0.0803458 0.0889459 0.0982164 0.108178 0.118847 0.130237 0.142356 0.155208 0.168791 0.183096 0.198109 0.213809 0.230168 0.247149 0.264708 0.282796 0.301352 0.320311 0.339598 0.359133 0.378826 0.398585 0.41831 0.437897 0.457236 0.476218 0.494728 0.512653 0.529878 0.546292 0.561784 0.576249 0.589585 0.601699 0.612502 0.621916 0.629872 0.63631 0.641181 0.64445 0.64609 0.64609 0.64445 0.641181 0.63631 0.629872 0.621916 0.612502 0.601699 0.589585 0.576249 0.561784 0.546292 0.529878 0.512653 0.494728 0.476218 0.457236 0.437897 0.41831 0.398585 0.378826 0.359133 0.339598 0.320311 0.301352 0.282796 0.264708 0.247149 0.230168 0.213809 0.198109 0.183096 0.168791 0.155208 0.142356 0.130237 0.118847 0.108178 0.0982164 0.0889459 0.0803458 0.072393 0.0650618 0.0583246 0.0521522 0.0465147 0.0413812 0.0367208 0.0325026 0.0286958 0.0252706 0.0221977 0.019449 0.0169974 0.0148171 0.0128837 0.0111741 0.00966676 0.00834152 0.00717969 +0.00684003 0.0079469 0.00920944 0.0106455 0.0122742 0.0141161 0.0161933 0.0185289 0.0211476 0.0240751 0.0273383 0.0309649 0.0349836 0.0394235 0.0443142 0.049685 0.0555653 0.0619838 0.0689682 0.0765448 0.0847379 0.0935699 0.10306 0.113225 0.124076 0.135622 0.147866 0.160806 0.174434 0.188737 0.203694 0.219279 0.235456 0.252185 0.269417 0.287096 0.305158 0.323532 0.342143 0.360905 0.379729 0.398521 0.41718 0.435605 0.453689 0.471323 0.4884 0.504811 0.520448 0.535207 0.548987 0.561693 0.573233 0.583526 0.592494 0.600074 0.606207 0.610848 0.613961 0.615524 0.615524 0.613961 0.610848 0.606207 0.600074 0.592494 0.583526 0.573233 0.561693 0.548987 0.535207 0.520448 0.504811 0.4884 0.471323 0.453689 0.435605 0.41718 0.398521 0.379729 0.360905 0.342143 0.323532 0.305158 0.287096 0.269417 0.252185 0.235456 0.219279 0.203694 0.188737 0.174434 0.160806 0.147866 0.135622 0.124076 0.113225 0.10306 0.0935699 0.0847379 0.0765448 0.0689682 0.0619838 0.0555653 0.049685 0.0443142 0.0394235 0.0349836 0.0309649 0.0273383 0.0240751 0.0211476 0.0185289 0.0161933 0.0141161 0.0122742 0.0106455 0.00920944 0.0079469 0.00684003 +0.00649984 0.00755165 0.0087514 0.010116 0.0116637 0.013414 0.0153879 0.0176074 0.0200958 0.0228777 0.0259786 0.0294249 0.0332437 0.0374628 0.0421102 0.0472139 0.0528017 0.058901 0.065538 0.0727377 0.0805234 0.0889161 0.0979343 0.107593 0.117905 0.128876 0.140511 0.152808 0.165758 0.17935 0.193563 0.208373 0.223746 0.239643 0.256018 0.272817 0.28998 0.307441 0.325126 0.342955 0.360843 0.3787 0.396432 0.41394 0.431124 0.447882 0.464109 0.479703 0.494563 0.508588 0.521683 0.533757 0.544723 0.554503 0.563026 0.570228 0.576057 0.580467 0.583426 0.584911 0.584911 0.583426 0.580467 0.576057 0.570228 0.563026 0.554503 0.544723 0.533757 0.521683 0.508588 0.494563 0.479703 0.464109 0.447882 0.431124 0.41394 0.396432 0.3787 0.360843 0.342955 0.325126 0.307441 0.28998 0.272817 0.256018 0.239643 0.223746 0.208373 0.193563 0.17935 0.165758 0.152808 0.140511 0.128876 0.117905 0.107593 0.0979343 0.0889161 0.0805234 0.0727377 0.065538 0.058901 0.0528017 0.0472139 0.0421102 0.0374628 0.0332437 0.0294249 0.0259786 0.0228777 0.0200958 0.0176074 0.0153879 0.013414 0.0116637 0.010116 0.0087514 0.00755165 0.00649984 +0.00616083 0.00715778 0.00829495 0.00958839 0.0110554 0.0127144 0.0145853 0.016689 0.0190477 0.0216845 0.0246236 0.0278902 0.0315098 0.0355089 0.0399138 0.0447514 0.0500478 0.0558289 0.0621198 0.068944 0.0763236 0.0842786 0.0928264 0.101982 0.111755 0.122155 0.133183 0.144838 0.157113 0.169996 0.183468 0.197505 0.212076 0.227144 0.242665 0.258588 0.274856 0.291406 0.308168 0.325067 0.342022 0.358948 0.375755 0.39235 0.408638 0.424522 0.439903 0.454684 0.468768 0.482062 0.494474 0.505918 0.516312 0.525582 0.533661 0.540487 0.546011 0.550192 0.552996 0.554404 0.554404 0.552996 0.550192 0.546011 0.540487 0.533661 0.525582 0.516312 0.505918 0.494474 0.482062 0.468768 0.454684 0.439903 0.424522 0.408638 0.39235 0.375755 0.358948 0.342022 0.325067 0.308168 0.291406 0.274856 0.258588 0.242665 0.227144 0.212076 0.197505 0.183468 0.169996 0.157113 0.144838 0.133183 0.122155 0.111755 0.101982 0.0928264 0.0842786 0.0763236 0.068944 0.0621198 0.0558289 0.0500478 0.0447514 0.0399138 0.0355089 0.0315098 0.0278902 0.0246236 0.0216845 0.0190477 0.016689 0.0145853 0.0127144 0.0110554 0.00958839 0.00829495 0.00715778 0.00616083 +0.00582462 0.00676717 0.00784229 0.00906514 0.0104521 0.0120206 0.0137894 0.0157783 0.0180082 0.0205011 0.0232799 0.0263682 0.0297903 0.0335711 0.0377357 0.0423092 0.0473166 0.0527823 0.0587298 0.0651816 0.0721585 0.0796794 0.0877608 0.0964163 0.105657 0.115489 0.125915 0.136934 0.148539 0.160719 0.173456 0.186727 0.200503 0.214748 0.229422 0.244476 0.259857 0.275504 0.291351 0.307328 0.323358 0.33936 0.35525 0.370939 0.386338 0.401355 0.415897 0.429871 0.443187 0.455755 0.46749 0.478309 0.488136 0.496901 0.504538 0.510992 0.516215 0.520167 0.522818 0.524149 0.524149 0.522818 0.520167 0.516215 0.510992 0.504538 0.496901 0.488136 0.478309 0.46749 0.455755 0.443187 0.429871 0.415897 0.401355 0.386338 0.370939 0.35525 0.33936 0.323358 0.307328 0.291351 0.275504 0.259857 0.244476 0.229422 0.214748 0.200503 0.186727 0.173456 0.160719 0.148539 0.136934 0.125915 0.115489 0.105657 0.0964163 0.0877608 0.0796794 0.0721585 0.0651816 0.0587298 0.0527823 0.0473166 0.0423092 0.0377357 0.0335711 0.0297903 0.0263682 0.0232799 0.0205011 0.0180082 0.0157783 0.0137894 0.0120206 0.0104521 0.00906514 0.00784229 0.00676717 0.00582462 +0.00549274 0.00638158 0.00739544 0.00854861 0.0098565 0.0113356 0.0130037 0.0148792 0.0169821 0.019333 0.0219534 0.0248657 0.0280928 0.0316582 0.0355855 0.0398984 0.0446205 0.0497748 0.0553834 0.0614676 0.068047 0.0751393 0.0827602 0.0909226 0.0996363 0.108908 0.11874 0.129131 0.140075 0.151561 0.163572 0.176087 0.189078 0.202512 0.21635 0.230546 0.24505 0.259806 0.27475 0.289817 0.304933 0.320023 0.335008 0.349803 0.364325 0.378486 0.392199 0.405377 0.417934 0.429786 0.440852 0.451055 0.460323 0.468587 0.47579 0.481876 0.486801 0.490528 0.493028 0.494283 0.494283 0.493028 0.490528 0.486801 0.481876 0.47579 0.468587 0.460323 0.451055 0.440852 0.429786 0.417934 0.405377 0.392199 0.378486 0.364325 0.349803 0.335008 0.320023 0.304933 0.289817 0.27475 0.259806 0.24505 0.230546 0.21635 0.202512 0.189078 0.176087 0.163572 0.151561 0.140075 0.129131 0.11874 0.108908 0.0996363 0.0909226 0.0827602 0.0751393 0.068047 0.0614676 0.0553834 0.0497748 0.0446205 0.0398984 0.0355855 0.0316582 0.0280928 0.0248657 0.0219534 0.019333 0.0169821 0.0148792 0.0130037 0.0113356 0.0098565 0.00854861 0.00739544 0.00638158 0.00549274 +0.00516657 0.00600263 0.00695628 0.00804097 0.0092712 0.0106625 0.0122315 0.0139957 0.0159737 0.0181849 0.0206498 0.0233891 0.0264246 0.0297783 0.0334724 0.0375292 0.0419709 0.046819 0.0520946 0.0578175 0.0640062 0.0706774 0.0778457 0.0855234 0.0937197 0.102441 0.111689 0.121463 0.131757 0.142561 0.153859 0.165631 0.17785 0.190486 0.203502 0.216856 0.230499 0.244378 0.258435 0.272607 0.286825 0.30102 0.315114 0.329031 0.34269 0.356011 0.368909 0.381305 0.393116 0.404265 0.414674 0.424271 0.432988 0.440762 0.447536 0.453261 0.457894 0.461399 0.463751 0.464932 0.464932 0.463751 0.461399 0.457894 0.453261 0.447536 0.440762 0.432988 0.424271 0.414674 0.404265 0.393116 0.381305 0.368909 0.356011 0.34269 0.329031 0.315114 0.30102 0.286825 0.272607 0.258435 0.244378 0.230499 0.216856 0.203502 0.190486 0.17785 0.165631 0.153859 0.142561 0.131757 0.121463 0.111689 0.102441 0.0937197 0.0855234 0.0778457 0.0706774 0.0640062 0.0578175 0.0520946 0.046819 0.0419709 0.0375292 0.0334724 0.0297783 0.0264246 0.0233891 0.0206498 0.0181849 0.0159737 0.0139957 0.0122315 0.0106625 0.0092712 0.00804097 0.00695628 0.00600263 0.00516657 +0.00484739 0.0056318 0.00652653 0.00754421 0.00869844 0.0100038 0.0114758 0.013131 0.0149869 0.0170615 0.0193741 0.0219442 0.0247922 0.0279386 0.0314045 0.0352107 0.039378 0.0439266 0.0488763 0.0542457 0.060052 0.066311 0.0730365 0.0802399 0.0879299 0.0961122 0.104789 0.11396 0.123618 0.133754 0.144354 0.155398 0.166863 0.178718 0.19093 0.203459 0.216259 0.229281 0.242469 0.255766 0.269106 0.282423 0.295647 0.308704 0.32152 0.334017 0.346119 0.357749 0.36883 0.37929 0.389056 0.39806 0.406238 0.413532 0.419888 0.42526 0.429606 0.432895 0.435102 0.436209 0.436209 0.435102 0.432895 0.429606 0.42526 0.419888 0.413532 0.406238 0.39806 0.389056 0.37929 0.36883 0.357749 0.346119 0.334017 0.32152 0.308704 0.295647 0.282423 0.269106 0.255766 0.242469 0.229281 0.216259 0.203459 0.19093 0.178718 0.166863 0.155398 0.144354 0.133754 0.123618 0.11396 0.104789 0.0961122 0.0879299 0.0802399 0.0730365 0.066311 0.060052 0.0542457 0.0488763 0.0439266 0.039378 0.0352107 0.0314045 0.0279386 0.0247922 0.0219442 0.0193741 0.0170615 0.0149869 0.013131 0.0114758 0.0100038 0.00869844 0.00754421 0.00652653 0.0056318 0.00484739 +0.00453634 0.00527041 0.00610774 0.00706011 0.00814028 0.00936187 0.0107394 0.0122884 0.0140252 0.0159667 0.0181309 0.0205361 0.0232013 0.0261459 0.0293893 0.0329513 0.0368512 0.0411079 0.04574 0.0507648 0.0561986 0.062056 0.0683499 0.075091 0.0822876 0.0899449 0.0980651 0.106647 0.115685 0.125171 0.135091 0.145427 0.156156 0.16725 0.178679 0.190403 0.202382 0.214568 0.22691 0.239353 0.251838 0.264301 0.276676 0.288895 0.300888 0.312583 0.323909 0.334792 0.345163 0.354952 0.364091 0.372517 0.380171 0.386997 0.392945 0.397971 0.402039 0.405117 0.407182 0.408218 0.408218 0.407182 0.405117 0.402039 0.397971 0.392945 0.386997 0.380171 0.372517 0.364091 0.354952 0.345163 0.334792 0.323909 0.312583 0.300888 0.288895 0.276676 0.264301 0.251838 0.239353 0.22691 0.214568 0.202382 0.190403 0.178679 0.16725 0.156156 0.145427 0.135091 0.125171 0.115685 0.106647 0.0980651 0.0899449 0.0822876 0.075091 0.0683499 0.062056 0.0561986 0.0507648 0.04574 0.0411079 0.0368512 0.0329513 0.0293893 0.0261459 0.0232013 0.0205361 0.0181309 0.0159667 0.0140252 0.0122884 0.0107394 0.00936187 0.00814028 0.00706011 0.00610774 0.00527041 0.00453634 +0.00423443 0.00491966 0.00570125 0.00659025 0.00759852 0.00873881 0.0100247 0.0114706 0.0130918 0.0149041 0.0169242 0.0191693 0.0216572 0.0244058 0.0274334 0.0307583 0.0343986 0.0383721 0.0426959 0.0473863 0.0524584 0.057926 0.063801 0.0700935 0.0768111 0.0839588 0.0915386 0.0995493 0.107986 0.116841 0.1261 0.135748 0.145763 0.156119 0.166787 0.177731 0.188913 0.200288 0.211809 0.223424 0.235077 0.246711 0.258262 0.269668 0.280863 0.29178 0.302352 0.312511 0.322192 0.331329 0.33986 0.347725 0.354869 0.361241 0.366793 0.371485 0.375282 0.378155 0.380083 0.38105 0.38105 0.380083 0.378155 0.375282 0.371485 0.366793 0.361241 0.354869 0.347725 0.33986 0.331329 0.322192 0.312511 0.302352 0.29178 0.280863 0.269668 0.258262 0.246711 0.235077 0.223424 0.211809 0.200288 0.188913 0.177731 0.166787 0.156119 0.145763 0.135748 0.1261 0.116841 0.107986 0.0995493 0.0915386 0.0839588 0.0768111 0.0700935 0.063801 0.057926 0.0524584 0.0473863 0.0426959 0.0383721 0.0343986 0.0307583 0.0274334 0.0244058 0.0216572 0.0191693 0.0169242 0.0149041 0.0130918 0.0114706 0.0100247 0.00873881 0.00759852 0.00659025 0.00570125 0.00491966 0.00423443 +0.00394255 0.00458054 0.00530826 0.00613598 0.00707475 0.00813644 0.0093337 0.0106799 0.0121893 0.0138767 0.0157576 0.017848 0.0201643 0.0227235 0.0255424 0.0286381 0.0320275 0.0357271 0.0397529 0.0441199 0.0488424 0.0539331 0.0594032 0.065262 0.0715165 0.0781715 0.0852288 0.0926873 0.100543 0.108787 0.117408 0.126391 0.135716 0.145358 0.15529 0.16548 0.175891 0.186482 0.197209 0.208023 0.218873 0.229705 0.24046 0.25108 0.261503 0.271668 0.281511 0.29097 0.299983 0.30849 0.316433 0.323756 0.330408 0.33634 0.34151 0.345879 0.349414 0.352089 0.353884 0.354784 0.354784 0.353884 0.352089 0.349414 0.345879 0.34151 0.33634 0.330408 0.323756 0.316433 0.30849 0.299983 0.29097 0.281511 0.271668 0.261503 0.25108 0.24046 0.229705 0.218873 0.208023 0.197209 0.186482 0.175891 0.16548 0.15529 0.145358 0.135716 0.126391 0.117408 0.108787 0.100543 0.0926873 0.0852288 0.0781715 0.0715165 0.065262 0.0594032 0.0539331 0.0488424 0.0441199 0.0397529 0.0357271 0.0320275 0.0286381 0.0255424 0.0227235 0.0201643 0.017848 0.0157576 0.0138767 0.0121893 0.0106799 0.0093337 0.00813644 0.00707475 0.00613598 0.00530826 0.00458054 0.00394255 +0.00366144 0.00425394 0.00492977 0.00569847 0.0065703 0.00755629 0.00866818 0.00991844 0.0113202 0.0128873 0.0146341 0.0165754 0.0187266 0.0211032 0.0237212 0.0265962 0.0297439 0.0331797 0.0369184 0.0409741 0.0453598 0.0500876 0.0551676 0.0606086 0.0664172 0.0725977 0.0791518 0.0860785 0.0933737 0.10103 0.109037 0.117379 0.126039 0.134994 0.144218 0.153681 0.16335 0.173185 0.183147 0.193191 0.203267 0.213326 0.223315 0.233177 0.242857 0.252297 0.261438 0.270223 0.278593 0.286494 0.293871 0.300672 0.306849 0.312359 0.31716 0.321217 0.3245 0.326984 0.328651 0.329487 0.329487 0.328651 0.326984 0.3245 0.321217 0.31716 0.312359 0.306849 0.300672 0.293871 0.286494 0.278593 0.270223 0.261438 0.252297 0.242857 0.233177 0.223315 0.213326 0.203267 0.193191 0.183147 0.173185 0.16335 0.153681 0.144218 0.134994 0.126039 0.117379 0.109037 0.10103 0.0933737 0.0860785 0.0791518 0.0725977 0.0664172 0.0606086 0.0551676 0.0500876 0.0453598 0.0409741 0.0369184 0.0331797 0.0297439 0.0265962 0.0237212 0.0211032 0.0187266 0.0165754 0.0146341 0.0128873 0.0113202 0.00991844 0.00866818 0.00755629 0.0065703 0.00569847 0.00492977 0.00425394 0.00366144 +0.00339171 0.00394056 0.0045666 0.00527867 0.00608628 0.00699964 0.00802961 0.00918777 0.0104863 0.0119379 0.013556 0.0153543 0.017347 0.0195486 0.0219737 0.0246369 0.0275527 0.0307354 0.0341987 0.0379556 0.0420183 0.0463977 0.0511035 0.0561437 0.0615244 0.0672495 0.0733208 0.0797373 0.0864951 0.0935874 0.101004 0.108732 0.116754 0.125049 0.133594 0.14236 0.151316 0.160427 0.169655 0.178959 0.188293 0.197611 0.206864 0.216 0.224967 0.233711 0.242179 0.250316 0.25807 0.265388 0.272222 0.278522 0.284244 0.289348 0.293795 0.297553 0.300594 0.302896 0.30444 0.305215 0.305215 0.30444 0.302896 0.300594 0.297553 0.293795 0.289348 0.284244 0.278522 0.272222 0.265388 0.25807 0.250316 0.242179 0.233711 0.224967 0.216 0.206864 0.197611 0.188293 0.178959 0.169655 0.160427 0.151316 0.14236 0.133594 0.125049 0.116754 0.108732 0.101004 0.0935874 0.0864951 0.0797373 0.0733208 0.0672495 0.0615244 0.0561437 0.0511035 0.0463977 0.0420183 0.0379556 0.0341987 0.0307354 0.0275527 0.0246369 0.0219737 0.0195486 0.017347 0.0153543 0.013556 0.0119379 0.0104863 0.00918777 0.00802961 0.00699964 0.00608628 0.00527867 0.0045666 0.00394056 0.00339171 +0.00313384 0.00364097 0.00421941 0.00487735 0.00562355 0.00646747 0.00741914 0.00848924 0.00968902 0.0110303 0.0125254 0.0141869 0.0160282 0.0180624 0.0203031 0.0227638 0.0254579 0.0283986 0.0315986 0.0350699 0.0388237 0.0428702 0.0472182 0.0518752 0.0568468 0.0621367 0.0677464 0.073675 0.079919 0.0864721 0.093325 0.100465 0.107877 0.115542 0.123437 0.131536 0.139812 0.14823 0.156757 0.165353 0.173977 0.182587 0.191136 0.199578 0.207863 0.215942 0.223766 0.231285 0.238449 0.245211 0.251525 0.257346 0.262634 0.267349 0.271458 0.274931 0.277741 0.279867 0.281294 0.28201 0.28201 0.281294 0.279867 0.277741 0.274931 0.271458 0.267349 0.262634 0.257346 0.251525 0.245211 0.238449 0.231285 0.223766 0.215942 0.207863 0.199578 0.191136 0.182587 0.173977 0.165353 0.156757 0.14823 0.139812 0.131536 0.123437 0.115542 0.107877 0.100465 0.093325 0.0864721 0.079919 0.073675 0.0677464 0.0621367 0.0568468 0.0518752 0.0472182 0.0428702 0.0388237 0.0350699 0.0315986 0.0283986 0.0254579 0.0227638 0.0203031 0.0180624 0.0160282 0.0141869 0.0125254 0.0110303 0.00968902 0.00848924 0.00741914 0.00646747 0.00562355 0.00487735 0.00421941 0.00364097 0.00313384 +0.0028882 0.00335558 0.00388869 0.00449505 0.00518277 0.00596053 0.00683761 0.00782384 0.00892958 0.0101657 0.0115436 0.0130749 0.0147718 0.0166466 0.0187117 0.0209795 0.0234625 0.0261727 0.0291219 0.0323211 0.0357806 0.0395099 0.0435172 0.0478091 0.052391 0.0572663 0.0624363 0.0679002 0.0736548 0.0796942 0.08601 0.0925905 0.0994215 0.106485 0.113762 0.121226 0.128853 0.136612 0.14447 0.152392 0.160341 0.168275 0.176155 0.183934 0.19157 0.199016 0.206227 0.213156 0.219759 0.225991 0.23181 0.237175 0.242048 0.246394 0.250181 0.253381 0.255971 0.257931 0.259245 0.259905 0.259905 0.259245 0.257931 0.255971 0.253381 0.250181 0.246394 0.242048 0.237175 0.23181 0.225991 0.219759 0.213156 0.206227 0.199016 0.19157 0.183934 0.176155 0.168275 0.160341 0.152392 0.14447 0.136612 0.128853 0.121226 0.113762 0.106485 0.0994215 0.0925905 0.08601 0.0796942 0.0736548 0.0679002 0.0624363 0.0572663 0.052391 0.0478091 0.0435172 0.0395099 0.0357806 0.0323211 0.0291219 0.0261727 0.0234625 0.0209795 0.0187117 0.0166466 0.0147718 0.0130749 0.0115436 0.0101657 0.00892958 0.00782384 0.00683761 0.00596053 0.00518277 0.00449505 0.00388869 0.00335558 0.0028882 +0.00265504 0.00308468 0.00357475 0.00413217 0.00476437 0.00547934 0.00628561 0.00719222 0.00820869 0.00934504 0.0106117 0.0120194 0.0135793 0.0153027 0.0172011 0.0192858 0.0215684 0.0240598 0.0267709 0.0297118 0.0328921 0.0363203 0.040004 0.0439495 0.0481615 0.0526432 0.0573958 0.0624187 0.0677087 0.0732606 0.0790664 0.0851157 0.0913952 0.0978888 0.104578 0.11144 0.118451 0.125583 0.132807 0.14009 0.147396 0.154691 0.161934 0.169085 0.176105 0.18295 0.189578 0.195948 0.202018 0.207747 0.213096 0.218028 0.222508 0.226503 0.229984 0.232926 0.235307 0.237108 0.238317 0.238923 0.238923 0.238317 0.237108 0.235307 0.232926 0.229984 0.226503 0.222508 0.218028 0.213096 0.207747 0.202018 0.195948 0.189578 0.18295 0.176105 0.169085 0.161934 0.154691 0.147396 0.14009 0.132807 0.125583 0.118451 0.11144 0.104578 0.0978888 0.0913952 0.0851157 0.0790664 0.0732606 0.0677087 0.0624187 0.0573958 0.0526432 0.0481615 0.0439495 0.040004 0.0363203 0.0328921 0.0297118 0.0267709 0.0240598 0.0215684 0.0192858 0.0172011 0.0153027 0.0135793 0.0120194 0.0106117 0.00934504 0.00820869 0.00719222 0.00628561 0.00547934 0.00476437 0.00413217 0.00357475 0.00308468 0.00265504 +0.00243448 0.00282844 0.00327779 0.0037889 0.00436858 0.00502416 0.00576346 0.00659475 0.00752679 0.00856873 0.00973015 0.0110209 0.0124513 0.0140315 0.0157722 0.0176837 0.0197766 0.0220611 0.024547 0.0272436 0.0301597 0.0333031 0.0366808 0.0402986 0.0441607 0.0482701 0.0526279 0.0572334 0.062084 0.0671747 0.0724982 0.078045 0.0838029 0.089757 0.0958902 0.102182 0.108611 0.115151 0.121774 0.128452 0.135152 0.14184 0.148482 0.155039 0.161475 0.167752 0.17383 0.179671 0.185236 0.190489 0.195394 0.199916 0.204023 0.207687 0.210879 0.213576 0.215759 0.217411 0.218519 0.219075 0.219075 0.218519 0.217411 0.215759 0.213576 0.210879 0.207687 0.204023 0.199916 0.195394 0.190489 0.185236 0.179671 0.17383 0.167752 0.161475 0.155039 0.148482 0.14184 0.135152 0.128452 0.121774 0.115151 0.108611 0.102182 0.0958902 0.089757 0.0838029 0.078045 0.0724982 0.0671747 0.062084 0.0572334 0.0526279 0.0482701 0.0441607 0.0402986 0.0366808 0.0333031 0.0301597 0.0272436 0.024547 0.0220611 0.0197766 0.0176837 0.0157722 0.0140315 0.0124513 0.0110209 0.00973015 0.00856873 0.00752679 0.00659475 0.00576346 0.00502416 0.00436858 0.0037889 0.00327779 0.00282844 0.00243448 +0.00222656 0.00258687 0.00299785 0.0034653 0.00399547 0.00459506 0.00527122 0.00603151 0.00688394 0.0078369 0.00889912 0.0100797 0.0113878 0.0128331 0.0144251 0.0161734 0.0180876 0.0201769 0.0224505 0.0249168 0.0275838 0.0304588 0.033548 0.0368568 0.040389 0.0441474 0.0481331 0.0523453 0.0567816 0.0614375 0.0663064 0.0713794 0.0766455 0.0820911 0.0877004 0.0934552 0.0993346 0.105316 0.111374 0.117481 0.123609 0.129726 0.1358 0.141798 0.147684 0.153425 0.158983 0.164325 0.169416 0.17422 0.178706 0.182842 0.186598 0.189949 0.192868 0.195335 0.197332 0.198843 0.199856 0.200365 0.200365 0.199856 0.198843 0.197332 0.195335 0.192868 0.189949 0.186598 0.182842 0.178706 0.17422 0.169416 0.164325 0.158983 0.153425 0.147684 0.141798 0.1358 0.129726 0.123609 0.117481 0.111374 0.105316 0.0993346 0.0934552 0.0877004 0.0820911 0.0766455 0.0713794 0.0663064 0.0614375 0.0567816 0.0523453 0.0481331 0.0441474 0.040389 0.0368568 0.033548 0.0304588 0.0275838 0.0249168 0.0224505 0.0201769 0.0180876 0.0161734 0.0144251 0.0128331 0.0113878 0.0100797 0.00889912 0.0078369 0.00688394 0.00603151 0.00527122 0.00459506 0.00399547 0.0034653 0.00299785 0.00258687 0.00222656 +0.00203121 0.0023599 0.00273482 0.00316126 0.00364492 0.00419191 0.00480873 0.00550232 0.00627996 0.00714931 0.00811834 0.00919531 0.0103887 0.0117072 0.0131595 0.0147544 0.0165006 0.0184066 0.0204807 0.0227306 0.0251637 0.0277864 0.0306046 0.033623 0.0368454 0.0402741 0.04391 0.0477526 0.0517997 0.0560471 0.0604888 0.0651168 0.0699208 0.0748887 0.0800058 0.0852557 0.0906193 0.0960758 0.101602 0.107174 0.112764 0.118344 0.123885 0.129357 0.134727 0.139964 0.145035 0.149908 0.154552 0.158934 0.163027 0.1668 0.170227 0.173283 0.175946 0.178197 0.180018 0.181397 0.182321 0.182785 0.182785 0.182321 0.181397 0.180018 0.178197 0.175946 0.173283 0.170227 0.1668 0.163027 0.158934 0.154552 0.149908 0.145035 0.139964 0.134727 0.129357 0.123885 0.118344 0.112764 0.107174 0.101602 0.0960758 0.0906193 0.0852557 0.0800058 0.0748887 0.0699208 0.0651168 0.0604888 0.0560471 0.0517997 0.0477526 0.04391 0.0402741 0.0368454 0.033623 0.0306046 0.0277864 0.0251637 0.0227306 0.0204807 0.0184066 0.0165006 0.0147544 0.0131595 0.0117072 0.0103887 0.00919531 0.00811834 0.00714931 0.00627996 0.00550232 0.00480873 0.00419191 0.00364492 0.00316126 0.00273482 0.0023599 0.00203121 +0.00184827 0.00214736 0.00248852 0.00287656 0.00331665 0.00381438 0.00437565 0.00500677 0.00571438 0.00650543 0.00738719 0.00836716 0.00945308 0.0106528 0.0119743 0.0134256 0.0150145 0.0167489 0.0186362 0.0206835 0.0228974 0.0252839 0.0278483 0.0305949 0.033527 0.0366469 0.0399554 0.043452 0.0471346 0.0509994 0.0550411 0.0592523 0.0636236 0.0681441 0.0728004 0.0775774 0.082458 0.0874231 0.0924518 0.0975216 0.102608 0.107686 0.112728 0.117707 0.122593 0.127358 0.131973 0.136407 0.140632 0.144621 0.148344 0.151777 0.154896 0.157677 0.1601 0.162148 0.163806 0.16506 0.165901 0.166323 0.166323 0.165901 0.16506 0.163806 0.162148 0.1601 0.157677 0.154896 0.151777 0.148344 0.144621 0.140632 0.136407 0.131973 0.127358 0.122593 0.117707 0.112728 0.107686 0.102608 0.0975216 0.0924518 0.0874231 0.082458 0.0775774 0.0728004 0.0681441 0.0636236 0.0592523 0.0550411 0.0509994 0.0471346 0.043452 0.0399554 0.0366469 0.033527 0.0305949 0.0278483 0.0252839 0.0228974 0.0206835 0.0186362 0.0167489 0.0150145 0.0134256 0.0119743 0.0106528 0.00945308 0.00836716 0.00738719 0.00650543 0.00571438 0.00500677 0.00437565 0.00381438 0.00331665 0.00287656 0.00248852 0.00214736 0.00184827 +0.00167753 0.00194899 0.00225863 0.00261082 0.00301026 0.00346201 0.00397143 0.00454425 0.00518649 0.00590447 0.00670477 0.00759421 0.00857981 0.00966871 0.0108681 0.0121853 0.0136275 0.0152017 0.0169146 0.0187728 0.0207822 0.0229482 0.0252757 0.0277686 0.0304298 0.0332615 0.0362643 0.0394379 0.0427803 0.0462881 0.0499564 0.0537786 0.0577461 0.061849 0.0660751 0.0704109 0.0748406 0.079347 0.0839112 0.0885126 0.0931293 0.097738 0.102314 0.106833 0.111268 0.115593 0.119781 0.123806 0.127641 0.131261 0.13464 0.137756 0.140587 0.143111 0.14531 0.147169 0.148673 0.149812 0.150575 0.150959 0.150959 0.150575 0.149812 0.148673 0.147169 0.14531 0.143111 0.140587 0.137756 0.13464 0.131261 0.127641 0.123806 0.119781 0.115593 0.111268 0.106833 0.102314 0.097738 0.0931293 0.0885126 0.0839112 0.079347 0.0748406 0.0704109 0.0660751 0.061849 0.0577461 0.0537786 0.0499564 0.0462881 0.0427803 0.0394379 0.0362643 0.0332615 0.0304298 0.0277686 0.0252757 0.0229482 0.0207822 0.0187728 0.0169146 0.0152017 0.0136275 0.0121853 0.0108681 0.00966871 0.00857981 0.00759421 0.00670477 0.00590447 0.00518649 0.00454425 0.00397143 0.00346201 0.00301026 0.00261082 0.00225863 0.00194899 0.00167753 +0.00151868 0.00176444 0.00204476 0.0023636 0.00272522 0.00313418 0.00359537 0.00411395 0.00469537 0.00534536 0.00606988 0.0068751 0.00776737 0.00875316 0.00983902 0.0110315 0.0123371 0.0137622 0.0153129 0.0169951 0.0188143 0.0207752 0.0228823 0.0251391 0.0275484 0.0301119 0.0328304 0.0357035 0.0387294 0.041905 0.045226 0.0486862 0.0522781 0.0559924 0.0598184 0.0637435 0.0677538 0.0718335 0.0759655 0.0801312 0.0843107 0.088483 0.092626 0.0967168 0.100732 0.104647 0.108439 0.112082 0.115554 0.118831 0.121891 0.124712 0.127274 0.129559 0.131551 0.133234 0.134595 0.135626 0.136317 0.136664 0.136664 0.136317 0.135626 0.134595 0.133234 0.131551 0.129559 0.127274 0.124712 0.121891 0.118831 0.115554 0.112082 0.108439 0.104647 0.100732 0.0967168 0.092626 0.088483 0.0843107 0.0801312 0.0759655 0.0718335 0.0677538 0.0637435 0.0598184 0.0559924 0.0522781 0.0486862 0.045226 0.041905 0.0387294 0.0357035 0.0328304 0.0301119 0.0275484 0.0251391 0.0228823 0.0207752 0.0188143 0.0169951 0.0153129 0.0137622 0.0123371 0.0110315 0.00983902 0.00875316 0.00776737 0.0068751 0.00606988 0.00534536 0.00469537 0.00411395 0.00359537 0.00313418 0.00272522 0.0023636 0.00204476 0.00176444 0.00151868 +0.00137137 0.00159329 0.00184642 0.00213433 0.00246088 0.00283017 0.00324663 0.0037149 0.00423993 0.00482687 0.00548111 0.00620823 0.00701395 0.00790412 0.00888465 0.00996146 0.0111404 0.0124273 0.0138276 0.0153466 0.0169893 0.0187601 0.0206628 0.0227007 0.0248763 0.0271911 0.0296459 0.0322403 0.0349727 0.0378403 0.0408392 0.0439637 0.0472072 0.0505613 0.0540161 0.0575605 0.0611818 0.0648658 0.068597 0.0723586 0.0761328 0.0799004 0.0836415 0.0873355 0.0909611 0.0944967 0.0979205 0.101211 0.104346 0.107305 0.110068 0.112615 0.114929 0.116992 0.118791 0.12031 0.12154 0.12247 0.123095 0.123408 0.123408 0.123095 0.12247 0.12154 0.12031 0.118791 0.116992 0.114929 0.112615 0.110068 0.107305 0.104346 0.101211 0.0979205 0.0944967 0.0909611 0.0873355 0.0836415 0.0799004 0.0761328 0.0723586 0.068597 0.0648658 0.0611818 0.0575605 0.0540161 0.0505613 0.0472072 0.0439637 0.0408392 0.0378403 0.0349727 0.0322403 0.0296459 0.0271911 0.0248763 0.0227007 0.0206628 0.0187601 0.0169893 0.0153466 0.0138276 0.0124273 0.0111404 0.00996146 0.00888465 0.00790412 0.00701395 0.00620823 0.00548111 0.00482687 0.00423993 0.0037149 0.00324663 0.00283017 0.00246088 0.00213433 0.00184642 0.00159329 0.00137137 +0.0012352 0.00143508 0.00166308 0.0019224 0.00221652 0.00254914 0.00292424 0.00334602 0.00381891 0.00434757 0.00493685 0.00559176 0.00631748 0.00711926 0.00800242 0.00897231 0.0100342 0.0111933 0.0124545 0.0138227 0.0153023 0.0168972 0.018611 0.0204465 0.0224061 0.0244911 0.0267021 0.0290389 0.0315 0.0340828 0.0367839 0.0395982 0.0425196 0.0455406 0.0486524 0.0518449 0.0551066 0.0584247 0.0617854 0.0651735 0.0685729 0.0719664 0.075336 0.0786632 0.0819288 0.0851133 0.0881971 0.0911606 0.0939845 0.0966497 0.0991383 0.101433 0.103517 0.105375 0.106995 0.108364 0.109471 0.110309 0.110872 0.111154 0.111154 0.110872 0.110309 0.109471 0.108364 0.106995 0.105375 0.103517 0.101433 0.0991383 0.0966497 0.0939845 0.0911606 0.0881971 0.0851133 0.0819288 0.0786632 0.075336 0.0719664 0.0685729 0.0651735 0.0617854 0.0584247 0.0551066 0.0518449 0.0486524 0.0455406 0.0425196 0.0395982 0.0367839 0.0340828 0.0315 0.0290389 0.0267021 0.0244911 0.0224061 0.0204465 0.018611 0.0168972 0.0153023 0.0138227 0.0124545 0.0111933 0.0100342 0.00897231 0.00800242 0.00711926 0.00631748 0.00559176 0.00493685 0.00434757 0.00381891 0.00334602 0.00292424 0.00254914 0.00221652 0.0019224 0.00166308 0.00143508 0.0012352 +0.00110971 0.00128929 0.00149412 0.0017271 0.00199133 0.00229017 0.00262716 0.00300609 0.00343094 0.00390589 0.0044353 0.00502368 0.00567567 0.00639599 0.00718943 0.00806078 0.0090148 0.0100561 0.0111893 0.0124185 0.0137477 0.0151806 0.0167202 0.0183693 0.0201298 0.022003 0.0239894 0.0260887 0.0282998 0.0306203 0.0330469 0.0355753 0.0381999 0.040914 0.0437097 0.0465778 0.0495081 0.0524892 0.0555085 0.0585524 0.0616064 0.0646551 0.0676824 0.0706716 0.0736054 0.0764664 0.0792369 0.0818994 0.0844363 0.0868308 0.0890665 0.0911278 0.0930001 0.0946699 0.096125 0.0973546 0.0983497 0.0991026 0.0996078 0.0998613 0.0998613 0.0996078 0.0991026 0.0983497 0.0973546 0.096125 0.0946699 0.0930001 0.0911278 0.0890665 0.0868308 0.0844363 0.0818994 0.0792369 0.0764664 0.0736054 0.0706716 0.0676824 0.0646551 0.0616064 0.0585524 0.0555085 0.0524892 0.0495081 0.0465778 0.0437097 0.040914 0.0381999 0.0355753 0.0330469 0.0306203 0.0282998 0.0260887 0.0239894 0.022003 0.0201298 0.0183693 0.0167202 0.0151806 0.0137477 0.0124185 0.0111893 0.0100561 0.0090148 0.00806078 0.00718943 0.00639599 0.00567567 0.00502368 0.0044353 0.00390589 0.00343094 0.00300609 0.00262716 0.00229017 0.00199133 0.0017271 0.00149412 0.00128929 0.00110971 +0.000994433 0.00115535 0.00133891 0.00154768 0.00178447 0.00205226 0.00235425 0.00269381 0.00307453 0.00350014 0.00397455 0.00450181 0.00508607 0.00573156 0.00644258 0.00722342 0.00807833 0.00901148 0.0100269 0.0111284 0.0123196 0.0136036 0.0149833 0.0164611 0.0180387 0.0197173 0.0214973 0.0233786 0.02536 0.0274394 0.0296139 0.0318797 0.0342316 0.0366638 0.039169 0.0417392 0.0443651 0.0470365 0.0497422 0.0524699 0.0552066 0.0579386 0.0606515 0.0633301 0.0659592 0.068523 0.0710057 0.0733915 0.0756649 0.0778107 0.0798142 0.0816613 0.0833391 0.0848355 0.0861394 0.0872413 0.0881329 0.0888077 0.0892604 0.0894876 0.0894876 0.0892604 0.0888077 0.0881329 0.0872413 0.0861394 0.0848355 0.0833391 0.0816613 0.0798142 0.0778107 0.0756649 0.0733915 0.0710057 0.068523 0.0659592 0.0633301 0.0606515 0.0579386 0.0552066 0.0524699 0.0497422 0.0470365 0.0443651 0.0417392 0.039169 0.0366638 0.0342316 0.0318797 0.0296139 0.0274394 0.02536 0.0233786 0.0214973 0.0197173 0.0180387 0.0164611 0.0149833 0.0136036 0.0123196 0.0111284 0.0100269 0.00901148 0.00807833 0.00722342 0.00644258 0.00573156 0.00508607 0.00450181 0.00397455 0.00350014 0.00307453 0.00269381 0.00235425 0.00205226 0.00178447 0.00154768 0.00133891 0.00115535 0.000994433 +0.00088886 0.0010327 0.00119676 0.00138337 0.00159502 0.00183438 0.00210431 0.00240782 0.00274812 0.00312855 0.0035526 0.00402388 0.00454611 0.00512308 0.00575861 0.00645655 0.0072207 0.00805478 0.0089624 0.00994697 0.0110117 0.0121594 0.0133926 0.0147135 0.0161236 0.017624 0.0192151 0.0208966 0.0226676 0.0245263 0.02647 0.0284952 0.0305975 0.0327714 0.0350107 0.037308 0.0396551 0.0420429 0.0444613 0.0468994 0.0493456 0.0517876 0.0542124 0.0566067 0.0589567 0.0612483 0.0634674 0.0656 0.067632 0.06955 0.0713407 0.0729918 0.0744915 0.0758289 0.0769944 0.0779794 0.0787764 0.0793795 0.0797841 0.0799872 0.0799872 0.0797841 0.0793795 0.0787764 0.0779794 0.0769944 0.0758289 0.0744915 0.0729918 0.0713407 0.06955 0.067632 0.0656 0.0634674 0.0612483 0.0589567 0.0566067 0.0542124 0.0517876 0.0493456 0.0468994 0.0444613 0.0420429 0.0396551 0.037308 0.0350107 0.0327714 0.0305975 0.0284952 0.02647 0.0245263 0.0226676 0.0208966 0.0192151 0.017624 0.0161236 0.0147135 0.0133926 0.0121594 0.0110117 0.00994697 0.0089624 0.00805478 0.0072207 0.00645655 0.00575861 0.00512308 0.00454611 0.00402388 0.0035526 0.00312855 0.00274812 0.00240782 0.00210431 0.00183438 0.00159502 0.00138337 0.00119676 0.0010327 0.00088886 +0.00079247 0.000920709 0.00106698 0.00123336 0.00142206 0.00163546 0.00187612 0.00214672 0.00245011 0.00278929 0.00316735 0.00358753 0.00405312 0.00456752 0.00513414 0.00575639 0.00643767 0.00718131 0.0079905 0.0088683 0.00981755 0.0108408 0.0119403 0.0131179 0.0143751 0.0157128 0.0171314 0.0186306 0.0202095 0.0218666 0.0235996 0.0254051 0.0272794 0.0292176 0.0312141 0.0332623 0.0353549 0.0374837 0.0396399 0.0418136 0.0439945 0.0461717 0.0483336 0.0504682 0.0525633 0.0546064 0.0565849 0.0584862 0.0602979 0.0620079 0.0636044 0.0650765 0.0664135 0.0676059 0.0686451 0.0695232 0.0702337 0.0707714 0.0711322 0.0713132 0.0713132 0.0711322 0.0707714 0.0702337 0.0695232 0.0686451 0.0676059 0.0664135 0.0650765 0.0636044 0.0620079 0.0602979 0.0584862 0.0565849 0.0546064 0.0525633 0.0504682 0.0483336 0.0461717 0.0439945 0.0418136 0.0396399 0.0374837 0.0353549 0.0332623 0.0312141 0.0292176 0.0272794 0.0254051 0.0235996 0.0218666 0.0202095 0.0186306 0.0171314 0.0157128 0.0143751 0.0131179 0.0119403 0.0108408 0.00981755 0.0088683 0.0079905 0.00718131 0.00643767 0.00575639 0.00513414 0.00456752 0.00405312 0.00358753 0.00316735 0.00278929 0.00245011 0.00214672 0.00187612 0.00163546 0.00142206 0.00123336 0.00106698 0.000920709 0.00079247 +0.000704734 0.000818775 0.000948856 0.00109681 0.00126462 0.00145439 0.00166841 0.00190905 0.00217885 0.00248048 0.00281668 0.00319034 0.00360439 0.00406184 0.00456572 0.00511908 0.00572494 0.00638624 0.00710585 0.00788647 0.00873062 0.00964058 0.0106184 0.0116656 0.0127836 0.0139732 0.0152347 0.0165679 0.0179721 0.0194457 0.0209868 0.0225925 0.0242592 0.0259829 0.0277583 0.0295797 0.0314406 0.0333338 0.0352512 0.0371843 0.0391238 0.0410599 0.0429824 0.0448807 0.0467439 0.0485608 0.0503202 0.052011 0.0536221 0.0551428 0.0565626 0.0578717 0.0590607 0.0601211 0.0610452 0.0618261 0.062458 0.0629361 0.0632569 0.063418 0.063418 0.0632569 0.0629361 0.062458 0.0618261 0.0610452 0.0601211 0.0590607 0.0578717 0.0565626 0.0551428 0.0536221 0.052011 0.0503202 0.0485608 0.0467439 0.0448807 0.0429824 0.0410599 0.0391238 0.0371843 0.0352512 0.0333338 0.0314406 0.0295797 0.0277583 0.0259829 0.0242592 0.0225925 0.0209868 0.0194457 0.0179721 0.0165679 0.0152347 0.0139732 0.0127836 0.0116656 0.0106184 0.00964058 0.00873062 0.00788647 0.00710585 0.00638624 0.00572494 0.00511908 0.00456572 0.00406184 0.00360439 0.00319034 0.00281668 0.00248048 0.00217885 0.00190905 0.00166841 0.00145439 0.00126462 0.00109681 0.000948856 0.000818775 0.000704734 +0.000625114 0.000726271 0.000841656 0.000972895 0.00112174 0.00129008 0.00147991 0.00169337 0.00193269 0.00220024 0.00249846 0.0028299 0.00319717 0.00360294 0.0040499 0.00454074 0.00507815 0.00566474 0.00630304 0.00699547 0.00774425 0.00855141 0.00941872 0.0103477 0.0113394 0.0123945 0.0135135 0.0146961 0.0159416 0.0172488 0.0186157 0.02004 0.0215185 0.0230474 0.0246222 0.0262379 0.0278885 0.0295678 0.0312686 0.0329833 0.0347036 0.036421 0.0381264 0.0398102 0.0414629 0.0430745 0.0446351 0.0461349 0.047564 0.0489129 0.0501723 0.0513334 0.0523881 0.0533287 0.0541484 0.0548411 0.0554016 0.0558257 0.0561103 0.0562531 0.0562531 0.0561103 0.0558257 0.0554016 0.0548411 0.0541484 0.0533287 0.0523881 0.0513334 0.0501723 0.0489129 0.047564 0.0461349 0.0446351 0.0430745 0.0414629 0.0398102 0.0381264 0.036421 0.0347036 0.0329833 0.0312686 0.0295678 0.0278885 0.0262379 0.0246222 0.0230474 0.0215185 0.02004 0.0186157 0.0172488 0.0159416 0.0146961 0.0135135 0.0123945 0.0113394 0.0103477 0.00941872 0.00855141 0.00774425 0.00699547 0.00630304 0.00566474 0.00507815 0.00454074 0.0040499 0.00360294 0.00319717 0.0028299 0.00249846 0.00220024 0.00193269 0.00169337 0.00147991 0.00129008 0.00112174 0.000972895 0.000841656 0.000726271 0.000625114 +0.000553077 0.000642577 0.000744665 0.00086078 0.000992475 0.00114141 0.00130937 0.00149823 0.00170997 0.00194669 0.00221054 0.00250379 0.00282874 0.00318774 0.00358319 0.00401747 0.00449295 0.00501195 0.00557669 0.00618933 0.00685182 0.00756596 0.00833333 0.00915522 0.0100326 0.0109662 0.0119563 0.0130026 0.0141045 0.0152611 0.0164705 0.0177306 0.0190387 0.0203914 0.0217848 0.0232143 0.0246747 0.0261605 0.0276653 0.0291824 0.0307045 0.0322239 0.0337327 0.0352225 0.0366848 0.0381107 0.0394915 0.0408184 0.0420828 0.0432763 0.0443905 0.0454179 0.046351 0.0471832 0.0479084 0.0485213 0.0490172 0.0493925 0.0496442 0.0497706 0.0497706 0.0496442 0.0493925 0.0490172 0.0485213 0.0479084 0.0471832 0.046351 0.0454179 0.0443905 0.0432763 0.0420828 0.0408184 0.0394915 0.0381107 0.0366848 0.0352225 0.0337327 0.0322239 0.0307045 0.0291824 0.0276653 0.0261605 0.0246747 0.0232143 0.0217848 0.0203914 0.0190387 0.0177306 0.0164705 0.0152611 0.0141045 0.0130026 0.0119563 0.0109662 0.0100326 0.00915522 0.00833333 0.00756596 0.00685182 0.00618933 0.00557669 0.00501195 0.00449295 0.00401747 0.00358319 0.00318774 0.00282874 0.00250379 0.00221054 0.00194669 0.00170997 0.00149823 0.00130937 0.00114141 0.000992475 0.00086078 0.000744665 0.000642577 0.000553077 +0.000488095 0.00056708 0.000657173 0.000759646 0.000875868 0.00100731 0.00115553 0.0013222 0.00150906 0.00171797 0.00195082 0.00220961 0.00249638 0.00281321 0.0031622 0.00354545 0.00396507 0.00442308 0.00492148 0.00546213 0.00604678 0.00667702 0.00735423 0.00807955 0.00885388 0.00967778 0.0105515 0.0114749 0.0124474 0.013468 0.0145353 0.0156474 0.0168018 0.0179956 0.0192253 0.0204868 0.0217756 0.0230868 0.0244148 0.0257537 0.0270969 0.0284379 0.0297694 0.0310842 0.0323746 0.033633 0.0348516 0.0360226 0.0371384 0.0381916 0.039175 0.0400816 0.0409052 0.0416396 0.0422796 0.0428204 0.0432581 0.0435893 0.0438115 0.043923 0.043923 0.0438115 0.0435893 0.0432581 0.0428204 0.0422796 0.0416396 0.0409052 0.0400816 0.039175 0.0381916 0.0371384 0.0360226 0.0348516 0.033633 0.0323746 0.0310842 0.0297694 0.0284379 0.0270969 0.0257537 0.0244148 0.0230868 0.0217756 0.0204868 0.0192253 0.0179956 0.0168018 0.0156474 0.0145353 0.013468 0.0124474 0.0114749 0.0105515 0.00967778 0.00885388 0.00807955 0.00735423 0.00667702 0.00604678 0.00546213 0.00492148 0.00442308 0.00396507 0.00354545 0.0031622 0.00281321 0.00249638 0.00220961 0.00195082 0.00171797 0.00150906 0.0013222 0.00115553 0.00100731 0.000875868 0.000759646 0.000657173 0.00056708 0.000488095 diff --git a/examples/basic/example_heatmap_imshow.gle b/examples/basic/example_heatmap_imshow.gle new file mode 100644 index 0000000..48059d1 --- /dev/null +++ b/examples/basic/example_heatmap_imshow.gle @@ -0,0 +1,131 @@ +! GLE graphics file +! Generated by gleplot +! gleplot-meta-begin v1 +! gleplot: dpi = 100 +! gleplot: import-data = +! gleplot-meta-end + +size 20.32 15.24 +set hei 0.42328 + +sub gleplot_viridis z + if z < 0 then z = 0 + if z > 1 then z = 1 + local r = 0.267004 + local g = 0.004874 + local b = 0.329415 + if (z >= 0/17) and (z <= 1/17) then r = 0.267004+(0.281924-0.267004)*(z-0/17)*17 + if (z >= 0/17) and (z <= 1/17) then g = 0.004874+(0.089666-0.004874)*(z-0/17)*17 + if (z >= 0/17) and (z <= 1/17) then b = 0.329415+(0.412415-0.329415)*(z-0/17)*17 + if (z >= 1/17) and (z <= 2/17) then r = 0.281924+(0.280255-0.281924)*(z-1/17)*17 + if (z >= 1/17) and (z <= 2/17) then g = 0.089666+(0.165693-0.089666)*(z-1/17)*17 + if (z >= 1/17) and (z <= 2/17) then b = 0.412415+(0.476498-0.412415)*(z-1/17)*17 + if (z >= 2/17) and (z <= 3/17) then r = 0.280255+(0.263663-0.280255)*(z-2/17)*17 + if (z >= 2/17) and (z <= 3/17) then g = 0.165693+(0.237631-0.165693)*(z-2/17)*17 + if (z >= 2/17) and (z <= 3/17) then b = 0.476498+(0.518762-0.476498)*(z-2/17)*17 + if (z >= 3/17) and (z <= 4/17) then r = 0.263663+(0.237441-0.263663)*(z-3/17)*17 + if (z >= 3/17) and (z <= 4/17) then g = 0.237631+(0.305202-0.237631)*(z-3/17)*17 + if (z >= 3/17) and (z <= 4/17) then b = 0.518762+(0.541921-0.518762)*(z-3/17)*17 + if (z >= 4/17) and (z <= 5/17) then r = 0.237441+(0.208623-0.237441)*(z-4/17)*17 + if (z >= 4/17) and (z <= 5/17) then g = 0.305202+(0.367752-0.305202)*(z-4/17)*17 + if (z >= 4/17) and (z <= 5/17) then b = 0.541921+(0.552675-0.541921)*(z-4/17)*17 + if (z >= 5/17) and (z <= 6/17) then r = 0.208623+(0.182256-0.208623)*(z-5/17)*17 + if (z >= 5/17) and (z <= 6/17) then g = 0.367752+(0.426184-0.367752)*(z-5/17)*17 + if (z >= 5/17) and (z <= 6/17) then b = 0.552675+(0.557120-0.552675)*(z-5/17)*17 + if (z >= 6/17) and (z <= 7/17) then r = 0.182256+(0.159194-0.182256)*(z-6/17)*17 + if (z >= 6/17) and (z <= 7/17) then g = 0.426184+(0.482237-0.426184)*(z-6/17)*17 + if (z >= 6/17) and (z <= 7/17) then b = 0.557120+(0.558073-0.557120)*(z-6/17)*17 + if (z >= 7/17) and (z <= 8/17) then r = 0.159194+(0.137770-0.159194)*(z-7/17)*17 + if (z >= 7/17) and (z <= 8/17) then g = 0.482237+(0.537492-0.482237)*(z-7/17)*17 + if (z >= 7/17) and (z <= 8/17) then b = 0.558073+(0.554906-0.558073)*(z-7/17)*17 + if (z >= 8/17) and (z <= 9/17) then r = 0.137770+(0.121148-0.137770)*(z-8/17)*17 + if (z >= 8/17) and (z <= 9/17) then g = 0.537492+(0.592739-0.537492)*(z-8/17)*17 + if (z >= 8/17) and (z <= 9/17) then b = 0.554906+(0.544641-0.554906)*(z-8/17)*17 + if (z >= 9/17) and (z <= 10/17) then r = 0.121148+(0.128087-0.121148)*(z-9/17)*17 + if (z >= 9/17) and (z <= 10/17) then g = 0.592739+(0.647749-0.592739)*(z-9/17)*17 + if (z >= 9/17) and (z <= 10/17) then b = 0.544641+(0.523491-0.544641)*(z-9/17)*17 + if (z >= 10/17) and (z <= 11/17) then r = 0.128087+(0.180653-0.128087)*(z-10/17)*17 + if (z >= 10/17) and (z <= 11/17) then g = 0.647749+(0.701402-0.647749)*(z-10/17)*17 + if (z >= 10/17) and (z <= 11/17) then b = 0.523491+(0.488189-0.523491)*(z-10/17)*17 + if (z >= 11/17) and (z <= 12/17) then r = 0.180653+(0.274149-0.180653)*(z-11/17)*17 + if (z >= 11/17) and (z <= 12/17) then g = 0.701402+(0.751988-0.701402)*(z-11/17)*17 + if (z >= 11/17) and (z <= 12/17) then b = 0.488189+(0.436601-0.488189)*(z-11/17)*17 + if (z >= 12/17) and (z <= 13/17) then r = 0.274149+(0.395174-0.274149)*(z-12/17)*17 + if (z >= 12/17) and (z <= 13/17) then g = 0.751988+(0.797475-0.751988)*(z-12/17)*17 + if (z >= 12/17) and (z <= 13/17) then b = 0.436601+(0.367757-0.436601)*(z-12/17)*17 + if (z >= 13/17) and (z <= 14/17) then r = 0.395174+(0.535621-0.395174)*(z-13/17)*17 + if (z >= 13/17) and (z <= 14/17) then g = 0.797475+(0.835785-0.797475)*(z-13/17)*17 + if (z >= 13/17) and (z <= 14/17) then b = 0.367757+(0.281908-0.367757)*(z-13/17)*17 + if (z >= 14/17) and (z <= 15/17) then r = 0.535621+(0.688944-0.535621)*(z-14/17)*17 + if (z >= 14/17) and (z <= 15/17) then g = 0.835785+(0.865448-0.835785)*(z-14/17)*17 + if (z >= 14/17) and (z <= 15/17) then b = 0.281908+(0.182725-0.281908)*(z-14/17)*17 + if (z >= 15/17) and (z <= 16/17) then r = 0.688944+(0.845561-0.688944)*(z-15/17)*17 + if (z >= 15/17) and (z <= 16/17) then g = 0.865448+(0.887322-0.865448)*(z-15/17)*17 + if (z >= 15/17) and (z <= 16/17) then b = 0.182725+(0.099702-0.182725)*(z-15/17)*17 + if (z >= 16/17) and (z <= 17/17) then r = 0.845561+(0.993248-0.845561)*(z-16/17)*17 + if (z >= 16/17) and (z <= 17/17) then g = 0.887322+(0.906157-0.887322)*(z-16/17)*17 + if (z >= 16/17) and (z <= 17/17) then b = 0.099702+(0.143936-0.099702)*(z-16/17)*17 + return rgb(r,g,b) +end sub + +sub gleplot_colorbar_v zmin zmax zstep palette$ wd hi format$ label$ + default zstep 0 + default wd 0.5 + default format "fix 1" + default label "" + ! Guard a degenerate (constant-field) range: zmax = zmin would + ! divide by zero in the tick-position math below and abort the + ! render. Expand to a nominal unit span so the bar still draws. + if zmax = zmin then + zmax = zmin+1 + end if + if zstep = 0 then + zstep = (zmax-zmin)/5 + end if + begin box name gleplot_cbar + if palette$ = "gray" then + colormap "y" 0 1 0 1 1 200 wd hi + else if palette$ = "color" then + colormap "y" 0 1 0 1 1 200 wd hi color + else + colormap "y" 0 1 0 1 1 200 wd hi palette palette$ + end if + end box + amove pointx(gleplot_cbar.bl) pointy(gleplot_cbar.bl) + box wd hi + set just lc + local zp = zmin + while zp <= zmax+(zmax-zmin)/1e6 + local yy = pointy(gleplot_cbar.bc)+(zp-zmin)/(zmax-zmin)*hi + amove pointx(gleplot_cbar.rc) yy + rline wd/3 0 + rmove 0.1 0 + write format$(zp,format$) + zp = zp+zstep + next + if label$ <> "" then + amove pointx(gleplot_cbar.rc)+1.3 pointy(gleplot_cbar.cc) + begin rotate 90 + set just cc + write label$ + end rotate + end if +end sub + +begin contour + data "data_contour1.z" + values 0.143185 0.285881 0.428578 0.571274 0.713971 0.856667 +end contour +begin graph + size 17.5 15.24 + title "2-D Gaussian (imshow + contour)" + xtitle "x" + ytitle "y" + xaxis min -3 max 3 + yaxis min -2.5 max 2.5 + colormap "data_heatmap1.z" 200 200 palette gleplot_viridis + data "data_contour1-cdata.dat" d1=c1,c2 + d1 line color WHITE lwidth 0.024696 +end graph +amove xg(xgmax)+0.3 yg(ygmin) +gleplot_colorbar_v zmin 0.000488095 zmax 0.999364 zstep 0.199775 palette "gleplot_viridis" wd 0.5 hi yg(ygmax)-yg(ygmin) format "fix 1" label "amplitude" \ No newline at end of file diff --git a/examples/basic/heatmap_imshow.py b/examples/basic/heatmap_imshow.py new file mode 100644 index 0000000..117e2c5 --- /dev/null +++ b/examples/basic/heatmap_imshow.py @@ -0,0 +1,62 @@ +"""Heatmap (imshow) example: a gridded 2-D Gaussian with a contour overlay.""" + +import sys +import numpy as np +from pathlib import Path + +# Add src to path +sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src")) + +import gleplot as glp + + +def example_heatmap_imshow(): + """Example: imshow of a gridded 2-D Gaussian with a contour overlay and colorbar. + + A single ``add_subplot(111)`` axes -- the canonical form. gleplot reserves + room to the right of the graph for the colorbar automatically, so the bar, + its tick numbers and its rotated label are not clipped at the page edge. + """ + print("Creating example: Heatmap (imshow)...") + + nx, ny = 120, 100 + x = np.linspace(-3, 3, nx) + y = np.linspace(-2.5, 2.5, ny) + X, Y = np.meshgrid(x, y) + Z = np.exp(-(X**2 + Y**2) / 2.0) + + fig = glp.figure(figsize=(8, 6)) + ax = fig.add_subplot(111) + + # Gridded heatmap. origin='lower' (the default) puts row 0 of Z at ymin, + # matching np.meshgrid's row-0-is-y[0] layout, so no flipping is needed. + ax.imshow(Z, extent=(x[0], x[-1], y[0], y[-1]), cmap="viridis") + + # Contour lines traced on the same grid, drawn on top of the heatmap. + ax.contour(x, y, Z, levels=6, colors="white", linewidths=0.7) + + ax.set_xlabel("x") + ax.set_ylabel("y") + ax.set_title("2-D Gaussian (imshow + contour)") + + fig.colorbar(label="amplitude") + + # Save the GLE script + its .z / points sidecars next to this script (the + # tracked-example convention: the .gle and its input sidecars live beside + # the .py). PDF/PNG and GLE's own -cdata/-clabels/-cvalues derivatives are + # build products (gitignored), so compilation is optional. + output_dir = Path(__file__).parent + gle_file = output_dir / "example_heatmap_imshow.gle" + fig.savefig_gle(str(gle_file)) + print(f" Saved to {gle_file}") + + try: + png_file = output_dir / "example_heatmap_imshow.png" + fig.savefig(str(png_file), dpi=150) + print(f" Compiled to {png_file}") + except RuntimeError: + print(" GLE not available for compilation; GLE script saved only.") + + +if __name__ == "__main__": + example_heatmap_imshow() diff --git a/examples/run_all.py b/examples/run_all.py index b810ac1..c3e96dd 100644 --- a/examples/run_all.py +++ b/examples/run_all.py @@ -4,7 +4,7 @@ from pathlib import Path # Add src to path -sys.path.insert(0, str(Path(__file__).parent.parent / "src")) +sys.path.insert(0, str(Path(__file__).parent.parent / 'src')) from basic import ( example_basic_line_plot, @@ -13,6 +13,7 @@ example_symmetric_error_bars, example_asymmetric_error_bars, example_horizontal_error_bars, + example_heatmap_imshow, ) from advanced import ( example_fill_between, @@ -30,15 +31,16 @@ example_line_from_file, example_data_prefix, example_line_overlay_from_file, + example_phase_diagram, ) def main(): """Run all examples.""" - print("\n" + "=" * 60) + print("\n" + "="*60) print("gleplot Examples - Matplotlib-like API for GLE") - print("=" * 60 + "\n") - + print("="*60 + "\n") + examples = [ ("Basic Line Plot", example_basic_line_plot), ("Scatter Plot", example_scatter_plot), @@ -46,6 +48,7 @@ def main(): ("Symmetric Error Bars", example_symmetric_error_bars), ("Asymmetric Error Bars", example_asymmetric_error_bars), ("Horizontal Error Bars", example_horizontal_error_bars), + ("Heatmap (imshow)", example_heatmap_imshow), ("Fill Between", example_fill_between), ("Log Scale", example_log_scale), ("Combined Plot", example_combined_plot), @@ -61,8 +64,9 @@ def main(): ("Line From File", example_line_from_file), ("Data Prefix Naming", example_data_prefix), ("Line Overlay From File", example_line_overlay_from_file), + ("Phase Diagram (tripcolor + tricontour)", example_phase_diagram), ] - + for name, example_func in examples: try: print(f"\n[{name}]") @@ -70,15 +74,14 @@ def main(): except Exception as e: print(f" ✗ Error: {e}") import traceback - traceback.print_exc() - - print("\n" + "=" * 60) + + print("\n" + "="*60) print("All examples completed!") print("Generated GLE files: example_*.gle") print("To compile to PDF: gle example_*.gle -d PDF") - print("=" * 60 + "\n") + print("="*60 + "\n") -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/src/gleplot/__init__.py b/src/gleplot/__init__.py index a558268..fc10382 100644 --- a/src/gleplot/__init__.py +++ b/src/gleplot/__init__.py @@ -58,6 +58,7 @@ from .axes import Axes from .colors import rgb_to_gle, get_color_palette from .markers import get_gle_marker +from .mathtext import mathtext_to_gle from .compiler import GLECompiler from .config import ( GLEStyleConfig, @@ -195,6 +196,31 @@ def text(*args, **kwargs): return gca().text(*args, **kwargs) +def imshow(*args, **kwargs): + """Display gridded data as a heatmap on current axes.""" + return gca().imshow(*args, **kwargs) + + +def contour(*args, **kwargs): + """Draw contour lines on current axes.""" + return gca().contour(*args, **kwargs) + + +def tripcolor(*args, **kwargs): + """Scattered-data heatmap on current axes.""" + return gca().tripcolor(*args, **kwargs) + + +def tricontour(*args, **kwargs): + """Scattered-data contour lines on current axes.""" + return gca().tricontour(*args, **kwargs) + + +def colorbar(*args, **kwargs): + """Attach a colorbar to the current figure's heatmap axes.""" + return gcf().colorbar(*args, **kwargs) + + def subplots( nrows: int = 1, ncols: int = 1, @@ -373,6 +399,11 @@ def close(fig=None): "fill_between", "errorbar", "text", + "imshow", + "contour", + "tripcolor", + "tricontour", + "colorbar", "subplots", "xlabel", "ylabel", @@ -385,6 +416,7 @@ def close(fig=None): "rgb_to_gle", "get_color_palette", "get_gle_marker", + "mathtext_to_gle", "GLECompiler", "GLEStyleConfig", "GLEGraphConfig", diff --git a/src/gleplot/axes.py b/src/gleplot/axes.py index ce25780..3e09a3a 100644 --- a/src/gleplot/axes.py +++ b/src/gleplot/axes.py @@ -5,7 +5,10 @@ from typing import Optional, List, Union, Tuple from .colors import rgb_to_gle from .markers import get_gle_marker +from .mathtext import mathtext_to_gle +from .palettes import canonical_cmap from .parser.units import markersize_to_msize, capsize_pt_to_cm +from .parser.tables import MATPLOTLIB_TO_LSTYLE # Global counter for unique data file names across all figures in a session _global_data_file_counter = 0 @@ -56,6 +59,44 @@ def _to_float_array(value): return np.asarray(value, dtype=float) +def _require_finite(arr: np.ndarray, what: str) -> None: + """Raise ``ValueError`` if ``arr`` holds any NaN or infinity. + + GLE's ``.z`` grid and scattered-points readers have NO missing-value + support -- a ``nan``/``inf`` in a sidecar is a hard parse error at compile + time (and would silently corrupt the bitmap). matplotlib's ``imshow`` + tolerates NaN (renders transparent); GLE cannot, so we reject early with a + clear message pointing at the offending data rather than emitting a broken + sidecar. + """ + if not np.all(np.isfinite(arr)): + raise ValueError( + f"{what} contains NaN or infinite values, which GLE's colormap/" + "contour grid cannot represent; mask or fill them before plotting" + ) + + +def _require_valid_extent(ext) -> None: + """Validate an ``[xmin, xmax, ymin, ymax]`` extent for GLE. + + GLE's ``.z`` grid header and the graph axes both require strictly + ascending ranges (``xmin < xmax``, ``ymin < ymax``); a reversed or + degenerate extent otherwise emits a ``.z`` file and an ``xaxis``/``yaxis`` + range GLE rejects at compile time ("illegal range for xaxis"). Reject early + with a clear message. (Unlike matplotlib, gleplot cannot express an axis + flipped purely via ``extent``.) + """ + x0, x1, y0, y1 = ext + if not all(np.isfinite(v) for v in ext): + raise ValueError(f"extent must contain finite values; got {ext}") + if x0 >= x1 or y0 >= y1: + raise ValueError( + "extent must have xmin < xmax and ymin < ymax (GLE requires " + f"ascending axis ranges); got (xmin={x0}, xmax={x1}, ymin={y0}, " + f"ymax={y1})" + ) + + def _sanitize_data_stem(name: object) -> str: """Convert an arbitrary data name to a safe filename stem.""" text = re.sub(r"[^A-Za-z0-9_-]+", "_", str(name).strip().lower()) @@ -165,6 +206,45 @@ def _reserve_data_filename(filename: str, figure=None) -> str: suffix_idx += 1 +def _reserve_sidecar(figure, kind: str, ext: str) -> str: + """Reserve a named sidecar file ``_.`` for a figure. + + Used for the contour/heatmap raw-content sidecars whose names GLE derives + generated files from mechanically (``.z`` grids, scattered ``.dat`` + points). ``kind`` is ``'heatmap'``/``'contour'``/``'points'`` and ``ext`` + is ``'z'``/``'dat'``. ``N`` is a per-kind, 1-based counter kept on the + figure. The reserved name is recorded in ``figure._used_data_files`` so it + never collides with a generated ``data_N.dat`` (or another sidecar) and so + it round-trips through ``Figure.to_dict``/``from_dict``. + + ``figure`` is optional only for symmetry with the other reservers; in + practice a figure is always present when a heatmap/contour series is added. + """ + prefix = figure.data_prefix if figure and figure.data_prefix else "data" + + counters = getattr(figure, "_sidecar_counters", None) if figure else None + if figure is not None and counters is None: + counters = {} + figure._sidecar_counters = counters + + used = getattr(figure, "_used_data_files", None) if figure else None + if figure is not None and used is None: + used = set() + figure._used_data_files = used + + idx = (counters.get(kind, 0) + 1) if counters is not None else 1 + while True: + name = f"{prefix}_{kind}{idx}.{ext}" + if used is None or name not in used: + break + idx += 1 + if counters is not None: + counters[kind] = idx + if used is not None: + used.add(name) + return name + + def _get_next_data_file(figure=None): """Get next unique data file name. @@ -347,6 +427,8 @@ def __init__(self, figure, position: Tuple[int, int, int] = None): self.errorbars = [] # List of errorbar plot data self.file_series = [] # External-file series definitions (column references) self.texts = [] # In-plot text annotations + self.heatmaps = [] # imshow/tripcolor colormap series + self.contours = [] # contour/tricontour line series # Raw GLE lines recovered from a parsed .gle file that the recognizer # could not map onto the object model. Emitted verbatim inside this @@ -398,6 +480,7 @@ def plot( Line object (for compatibility) """ data_name = kwargs.pop("data_name", None) + label = mathtext_to_gle(label) x = np.asarray(x) y = np.asarray(y) @@ -520,6 +603,7 @@ def errorbar( >>> ax.errorbar(x, y, yerr=0.5, xerr=0.3) """ + label = mathtext_to_gle(label) x = np.asarray(x, dtype=float) y = np.asarray(y, dtype=float) @@ -670,6 +754,7 @@ def errorbar_from_file( if x_col < 1 or y_col < 1 or (yerr_col is not None and yerr_col < 1): raise ValueError("Column indices must be >= 1") + label = mathtext_to_gle(label) if color is None: gle_color = "BLUE" else: @@ -718,6 +803,7 @@ def line_from_file( if x_col < 1 or y_col < 1: raise ValueError("Column indices must be >= 1") + label = mathtext_to_gle(label) if color is None: gle_color = "BLUE" else: @@ -774,6 +860,7 @@ def scatter( ------- self """ + label = mathtext_to_gle(label) # scatter() uses 's' instead of markersize # matplotlib scatter s is area in points^2, typical range 10-100, default ~36 # Convert to markersize: since area ~ size^2, markersize ~ sqrt(s) @@ -832,6 +919,7 @@ def bar( >>> fig.savefig('bar_chart.pdf') """ data_name = kwargs.pop("data_name", None) + label = mathtext_to_gle(label) x = np.asarray(x, dtype=float) height = np.asarray(height, dtype=float) @@ -891,6 +979,7 @@ def fill_between( self """ data_name = kwargs.pop("data_name", None) + label = mathtext_to_gle(label) x = np.asarray(x) y1 = np.asarray(y1) @@ -962,7 +1051,7 @@ def text( { "x": float(x), "y": float(y), - "text": str(s), + "text": mathtext_to_gle(str(s)), "color": gle_color, "fontsize": float(fontsize) if fontsize is not None else None, "ha": str(ha), @@ -972,9 +1061,420 @@ def text( ) return self + # -- heatmaps & contours -------------------------------------------- + + def _resolve_cmap(self, cmap: Optional[str]) -> str: + """Return the canonical cmap name, falling back to the graph default.""" + if cmap is None: + cmap = self.figure.graph.default_cmap + return canonical_cmap(cmap) + + def _resolve_pixels(self, pixels) -> List[int]: + """Normalize the ``pixels`` argument to a stored ``[px, py]`` int pair.""" + if pixels is None: + px = int(self.figure.graph.colormap_pixels) + return [px, px] + if isinstance(pixels, (list, tuple)): + px, py = int(pixels[0]), int(pixels[1]) + return [px, py] + px = int(pixels) + return [px, px] + + @staticmethod + def _linestyle_to_lstyle(linestyle: Optional[str]) -> Optional[int]: + """Map a matplotlib linestyle to a GLE ``lstyle`` int (None = solid).""" + if linestyle in ("-", None, "", "solid"): + return None + return MATPLOTLIB_TO_LSTYLE.get(linestyle) + + def imshow( + self, + Z, + extent=None, + origin: str = "lower", + cmap: Optional[str] = None, + vmin: Optional[float] = None, + vmax: Optional[float] = None, + interpolation: str = "bicubic", + pixels=None, + invert: bool = False, + label: Optional[str] = None, + **kwargs, + ): + """Display gridded 2-D data ``Z`` as a colour map (heatmap). + + Parameters + ---------- + Z : array-like, shape (ny, nx) + Gridded scalar field. + extent : tuple, optional + ``(xmin, xmax, ymin, ymax)`` mapping the grid onto data + coordinates. Default ``(0, nx, 0, ny)``. + origin : {'lower', 'upper'} + ``'lower'`` (default) puts row 0 of ``Z`` at ``ymin`` (the + scientific convention; note this differs from matplotlib's + ``'upper'`` default). ``'upper'`` flips the rows when writing the + ``.z`` sidecar. + cmap : str, optional + Palette name (see :data:`gleplot.palettes.SUPPORTED_CMAPS`). When + ``None``, uses the figure graph config's ``default_cmap``. + vmin, vmax : float, optional + Colour normalization range (GLE ``zmin``/``zmax``). ``None`` uses + GLE's data-range default. + interpolation : {'bicubic', 'nearest'} + Sampling interpolation for the ``.z`` grid. + pixels : int or (px, py), optional + Bitmap resolution. Default from graph config ``colormap_pixels``. + invert : bool + Invert the colour mapping. + label : str, optional + Series label (not drawn by the colormap itself; kept for the GUI). + + Returns + ------- + dict + The stored heatmap series dict. + """ + label = mathtext_to_gle(label) + if origin not in ("lower", "upper"): + raise ValueError("origin must be 'lower' or 'upper'") + z = np.asarray(Z, dtype=float) + if z.ndim != 2: + raise ValueError("imshow requires a 2-D array Z") + _require_finite(z, "imshow Z") + ny, nx = z.shape + if extent is None: + ext = [0.0, float(nx), 0.0, float(ny)] + else: + ext = [float(v) for v in extent] + if len(ext) != 4: + raise ValueError("extent must be (xmin, xmax, ymin, ymax)") + _require_valid_extent(ext) + + if self.heatmaps: + raise ValueError( + "GLE supports at most one heatmap (colormap) per axes; " + "this axes already has one" + ) + + data_file = _reserve_sidecar(self.figure, "heatmap", "z") + hm = { + "type": "heatmap", + "source": "grid", + "z": z, + "x": None, + "y": None, + "zpts": None, + "extent": ext, + "origin": origin, + "cmap": self._resolve_cmap(cmap), + "vmin": None if vmin is None else float(vmin), + "vmax": None if vmax is None else float(vmax), + "interpolation": "nearest" if interpolation == "nearest" else "bicubic", + "pixels": self._resolve_pixels(pixels), + "invert": bool(invert), + "gridsize": None, + "ncontour": None, + "label": label, + "data_file": data_file, + "colorbar": None, + } + self.heatmaps.append(hm) + return hm + + def contour( + self, + *args, + levels=None, + colors: str = "black", + linewidths: float = 1.0, + linestyles: str = "-", + clabel: bool = False, + clabel_fmt: str = "fix 1", + label: Optional[str] = None, + **kwargs, + ): + """Draw contour lines of gridded data. + + Signatures: ``contour(Z)`` or ``contour(x, y, Z)`` with 1-D ``x`` (nx), + 1-D ``y`` (ny), 2-D ``Z`` (ny, nx). ``x``/``y`` must be uniformly + spaced. + + Parameters + ---------- + levels : None, int, or sequence + ``None`` uses GLE's default 10 levels. An int ``n`` emits + ``values from zmin to zmax step (zmax-zmin)/n``. A sequence emits + ``values v1 v2 ...``. + colors : str + Contour line colour. + linewidths : float + Line width (matplotlib points). + linestyles : str + Line style ('-', '--', ':', '-.'). + clabel : bool + Draw inline contour labels from the generated ``-clabels.dat``. + clabel_fmt : str + GLE ``format$`` string for the labels. + + Returns + ------- + dict + The stored contour series dict. + """ + label = mathtext_to_gle(label) + z, ext = self._grid_from_args(args) + levels_resolved = self._resolve_levels(levels, z) + # Explicit levels that all lie outside the data range would make GLE's + # ``begin contour`` emit an EMPTY ``-cdata.dat`` (no crossings), and the + # ``data "...-cdata.dat"`` line then aborts the whole compile with a + # cryptic "column index out of range". We have the grid here, so reject + # early with a clear message (a partially in-range level set is fine). + if levels_resolved: + zmn = float(np.min(z)) + zmx = float(np.max(z)) + if not any(zmn < lv < zmx for lv in levels_resolved): + raise ValueError( + f"contour levels {levels_resolved} all lie outside the data " + f"range ({zmn}, {zmx}); no contour lines would be drawn" + ) + data_file = _reserve_sidecar(self.figure, "contour", "z") + ct = { + "type": "contour", + "source": "grid", + "z": z, + "x": None, + "y": None, + "zpts": None, + "extent": ext, + "levels": levels_resolved, + "color": rgb_to_gle(colors), + "linewidth": float(linewidths), + "linestyle": self._linestyle_to_lstyle(linestyles), + "clabel": bool(clabel), + "clabel_fmt": str(clabel_fmt), + "gridsize": None, + "ncontour": None, + "label": label, + "data_file": data_file, + } + self.contours.append(ct) + return ct + + def _grid_from_args(self, args): + """Parse ``contour`` positional args into ``(z_2d, extent)``.""" + if len(args) == 1: + z = np.asarray(args[0], dtype=float) + if z.ndim != 2: + raise ValueError("contour(Z) requires a 2-D array") + _require_finite(z, "contour Z") + ny, nx = z.shape + return z, [0.0, float(nx), 0.0, float(ny)] + if len(args) == 3: + x = np.asarray(args[0], dtype=float) + y = np.asarray(args[1], dtype=float) + z = np.asarray(args[2], dtype=float) + if x.ndim != 1 or y.ndim != 1 or z.ndim != 2: + raise ValueError("contour(x, y, Z) requires 1-D x, 1-D y, 2-D Z") + if z.shape != (len(y), len(x)): + raise ValueError( + f"Z shape {z.shape} does not match (len(y), len(x)) = " + f"({len(y)}, {len(x)})" + ) + _require_finite(x, "contour x") + _require_finite(y, "contour y") + _require_finite(z, "contour Z") + self._check_uniform(x, "x") + self._check_uniform(y, "y") + ext = [float(x[0]), float(x[-1]), float(y[0]), float(y[-1])] + _require_valid_extent(ext) + return z, ext + raise ValueError("contour expects contour(Z) or contour(x, y, Z)") + + @staticmethod + def _check_uniform(v, name): + """Validate that a 1-D coordinate array is uniformly spaced.""" + if len(v) < 2: + return + diffs = np.diff(v) + step = diffs[0] + if step == 0 or not np.allclose(diffs, step, rtol=1e-6, atol=1e-12): + raise ValueError( + f"contour requires uniformly spaced {name} (a .z grid is " + "uniform); got non-uniform spacing" + ) + + @staticmethod + def _resolve_levels(levels, z): + """Resolve the ``levels`` argument to ``None`` or a list of floats. + + ``None`` -> ``None`` (GLE's default 10 levels). An int ``n`` is resolved + at store time to ``n`` explicit levels evenly spaced strictly between the + data's min and max -- emitted as an explicit ``values`` list rather than + the GLE ``values from a to b step s`` form, because the recognizer models + only the explicit-list form (round-trip safety). An explicit sequence is + stored verbatim as floats. + """ + if levels is None: + return None + if isinstance(levels, (int, np.integer)) and not isinstance(levels, bool): + n = int(levels) + if n < 1: + raise ValueError("levels count must be >= 1") + zmin = float(np.nanmin(z)) + zmax = float(np.nanmax(z)) + return [float(v) for v in np.linspace(zmin, zmax, n + 2)[1:-1]] + return [float(v) for v in levels] + + def tripcolor( + self, + x, + y, + z, + gridsize=(50, 50), + extent=None, + cmap: Optional[str] = None, + vmin: Optional[float] = None, + vmax: Optional[float] = None, + interpolation: str = "bicubic", + pixels=None, + invert: bool = False, + label: Optional[str] = None, + **kwargs, + ): + """Heatmap from scattered ``(x, y, z)`` samples via GLE ``fitz`` gridding. + + Writes a points sidecar (raw ``x y z`` triples) and emits a + ``begin fitz`` block that grids the data (Akima interpolation) to a + ``.z`` file at GLE compile time, then a ``colormap`` of that grid. + + Parameters + ---------- + x, y, z : array-like + Equal-length 1-D scattered samples. + gridsize : (nx, ny) + Interpolation grid resolution. + extent : tuple, optional + ``(xmin, xmax, ymin, ymax)``. Default: data bounds. + (remaining kwargs as :meth:`imshow`). + """ + label = mathtext_to_gle(label) + if self.heatmaps: + raise ValueError( + "GLE supports at most one heatmap (colormap) per axes; " + "this axes already has one" + ) + xa, ya, za, ext, gs = self._points_from_args(x, y, z, gridsize, extent) + data_file = _reserve_sidecar(self.figure, "points", "dat") + hm = { + "type": "heatmap", + "source": "points", + "z": None, + "x": xa, + "y": ya, + "zpts": za, + "extent": ext, + "origin": "lower", + "cmap": self._resolve_cmap(cmap), + "vmin": None if vmin is None else float(vmin), + "vmax": None if vmax is None else float(vmax), + "interpolation": "nearest" if interpolation == "nearest" else "bicubic", + "pixels": self._resolve_pixels(pixels), + "invert": bool(invert), + "gridsize": gs, + "ncontour": None, + "label": label, + "data_file": data_file, + "colorbar": None, + } + self.heatmaps.append(hm) + return hm + + def tricontour( + self, + x, + y, + z, + gridsize=(50, 50), + extent=None, + ncontour: int = 3, + levels=None, + colors: str = "black", + linewidths: float = 1.0, + linestyles: str = "-", + clabel: bool = False, + clabel_fmt: str = "fix 1", + label: Optional[str] = None, + **kwargs, + ): + """Contour lines from scattered ``(x, y, z)`` samples via GLE ``fitz``. + + Writes a points sidecar and emits a ``begin fitz`` block (gridding at + compile time) followed by a ``begin contour`` block on the generated + ``.z`` grid. + + Parameters + ---------- + ncontour : int + ``fitz`` neighbour-point count per interpolation node. + (remaining kwargs as :meth:`contour`). + """ + label = mathtext_to_gle(label) + xa, ya, za, ext, gs = self._points_from_args(x, y, z, gridsize, extent) + # For explicit-level or count resolution we approximate the grid range + # from the scattered z-values (GLE grids at compile time). + levels_resolved = self._resolve_levels(levels, za) + data_file = _reserve_sidecar(self.figure, "points", "dat") + ct = { + "type": "contour", + "source": "points", + "z": None, + "x": xa, + "y": ya, + "zpts": za, + "extent": ext, + "levels": levels_resolved, + "color": rgb_to_gle(colors), + "linewidth": float(linewidths), + "linestyle": self._linestyle_to_lstyle(linestyles), + "clabel": bool(clabel), + "clabel_fmt": str(clabel_fmt), + "gridsize": gs, + "ncontour": int(ncontour), + "label": label, + "data_file": data_file, + } + self.contours.append(ct) + return ct + + @staticmethod + def _points_from_args(x, y, z, gridsize, extent): + """Validate scattered inputs; return (x, y, z, extent, [nx, ny]).""" + xa = np.asarray(x, dtype=float).ravel() + ya = np.asarray(y, dtype=float).ravel() + za = np.asarray(z, dtype=float).ravel() + if not (len(xa) == len(ya) == len(za)): + raise ValueError("x, y, z must have equal length") + _require_finite(xa, "scattered x") + _require_finite(ya, "scattered y") + _require_finite(za, "scattered z") + if len(xa) < 3: + raise ValueError("scattered gridding needs at least 3 points") + gs = [int(gridsize[0]), int(gridsize[1])] + if gs[0] < 2 or gs[1] < 2: + raise ValueError("gridsize entries must be >= 2") + if extent is None: + ext = [float(xa.min()), float(xa.max()), float(ya.min()), float(ya.max())] + else: + ext = [float(v) for v in extent] + if len(ext) != 4: + raise ValueError("extent must be (xmin, xmax, ymin, ymax)") + _require_valid_extent(ext) + return xa, ya, za, ext, gs + def set_xlabel(self, label: str): """Set x-axis label.""" - self.xlabel_text = label + self.xlabel_text = mathtext_to_gle(label) return self def set_ylabel(self, label: str, axis: str = "y"): @@ -987,6 +1487,7 @@ def set_ylabel(self, label: str, axis: str = "y"): axis : str, optional Which axis: 'y' (left, default) or 'y2' (right) """ + label = mathtext_to_gle(label) if axis == "y2": self.y2label_text = label else: @@ -995,7 +1496,7 @@ def set_ylabel(self, label: str, axis: str = "y"): def set_title(self, label: str): """Set subplot title.""" - self.title_text = label + self.title_text = mathtext_to_gle(label) return self def set_xscale(self, scale: str): @@ -1089,6 +1590,8 @@ def has_plots(self) -> bool: or self.fills or self.errorbars or self.file_series + or self.heatmaps + or self.contours ) def has_y2_plots(self) -> bool: @@ -1112,6 +1615,8 @@ def has_y2_plots(self) -> bool: "errorbars": ("x", "y", "yerr_up", "yerr_down", "xerr_left", "xerr_right"), "file_series": (), "texts": (), + "heatmaps": ("z", "x", "y", "zpts"), + "contours": ("z", "x", "y", "zpts"), } # Series list attributes serialized on every axes, in a stable order. @@ -1123,6 +1628,8 @@ def has_y2_plots(self) -> bool: "errorbars", "file_series", "texts", + "heatmaps", + "contours", ) @staticmethod @@ -1201,6 +1708,8 @@ def to_dict(self) -> dict: "errorbars": [_to_jsonable(d) for d in self.errorbars], "file_series": [_to_jsonable(d) for d in self.file_series], "texts": [_to_jsonable(d) for d in self.texts], + "heatmaps": [_to_jsonable(d) for d in self.heatmaps], + "contours": [_to_jsonable(d) for d in self.contours], "passthrough": list(self.passthrough), } diff --git a/src/gleplot/config.py b/src/gleplot/config.py index 44b5321..2bfcc80 100644 --- a/src/gleplot/config.py +++ b/src/gleplot/config.py @@ -91,6 +91,15 @@ class GLEGraphConfig: show_grid : bool Show background grid. Default: False + + default_cmap : str + Default colour map used by ``imshow``/``tripcolor`` when ``cmap`` is + not passed. One of the names in :data:`gleplot.palettes.SUPPORTED_CMAPS`. + Default: 'viridis' + + colormap_pixels : int + Default bitmap resolution (pixels per side) for ``colormap`` rendering + when ``imshow(pixels=...)`` is not given. Default: 200 """ scale_mode: str = "auto" # 'auto', 'fixed', 'fullsize' @@ -102,6 +111,8 @@ class GLEGraphConfig: legend_offset_y: float = 0.0 smooth_curves: bool = True show_grid: bool = False + default_cmap: str = "viridis" + colormap_pixels: int = 200 def to_dict(self) -> Dict[str, Any]: """Convert config to dictionary.""" diff --git a/src/gleplot/figure.py b/src/gleplot/figure.py index ca2b5a6..988c321 100644 --- a/src/gleplot/figure.py +++ b/src/gleplot/figure.py @@ -7,6 +7,7 @@ from .writer import GLEWriter from .compiler import GLECompiler, SUFFIX_TO_COMPILE_FORMAT from .colors import rgb_to_gle +from .mathtext import mathtext_to_gle from .config import GLEStyleConfig, GLEGraphConfig, GLEMarkerConfig, GlobalConfig from .parser import metadata as _gle_metadata @@ -285,6 +286,96 @@ def text(self, x, y, s, **kwargs): """Add text on current axes.""" return self.gca().text(x, y, s, **kwargs) + def imshow(self, Z, **kwargs): + """Display gridded data as a heatmap on current axes.""" + return self.gca().imshow(Z, **kwargs) + + def contour(self, *args, **kwargs): + """Draw contour lines on current axes.""" + return self.gca().contour(*args, **kwargs) + + def tripcolor(self, x, y, z, **kwargs): + """Scattered-data heatmap on current axes.""" + return self.gca().tripcolor(x, y, z, **kwargs) + + def tricontour(self, x, y, z, **kwargs): + """Scattered-data contour lines on current axes.""" + return self.gca().tricontour(x, y, z, **kwargs) + + def colorbar( + self, + label: Optional[str] = None, + format: str = "fix 1", + nticks: Optional[int] = None, + width: float = 0.5, + sep: float = 0.3, + ): + """Attach a vertical colorbar to the figure's single heatmap axes. + + Parameters + ---------- + label : str, optional + Colorbar axis label (rotated text to the right of the bar). + format : str + GLE ``format$`` string for the tick labels (e.g. ``'fix 1'``). + nticks : int, optional + Approximate number of tick intervals. Default: 5. + width : float + Colorbar width in cm. + sep : float + Gap (cm) between the graph's right edge and the colorbar. + + Returns + ------- + dict + The stored colorbar dict (also attached to the heatmap under + ``'colorbar'``). + + Raises + ------ + ValueError + If no axes has a heatmap, or if more than one does (ambiguous). + """ + bearing = [ax for ax in self.axes_list if ax.heatmaps] + if not bearing: + raise ValueError( + "colorbar() requires a heatmap (imshow/tripcolor) on some axes" + ) + if len(bearing) > 1: + raise ValueError( + "colorbar() is ambiguous: more than one axes has a heatmap" + ) + hm = bearing[0].heatmaps[0] + + if hm["vmin"] is not None: + zmin = float(hm["vmin"]) + elif hm["source"] == "grid": + zmin = float(np.nanmin(hm["z"])) + else: + zmin = float(np.nanmin(hm["zpts"])) + if hm["vmax"] is not None: + zmax = float(hm["vmax"]) + elif hm["source"] == "grid": + zmax = float(np.nanmax(hm["z"])) + else: + zmax = float(np.nanmax(hm["zpts"])) + + divisions = int(nticks) if nticks else 5 + span = zmax - zmin + zstep = (span / divisions) if span > 0 else 1.0 + + cb = { + "label": mathtext_to_gle(label), + "format": str(format), + "width": float(width), + "sep": float(sep), + "zmin": zmin, + "zmax": zmax, + "zstep": zstep, + } + hm["colorbar"] = cb + return cb + def xlabel(self, label: str): """Set x label on current axes.""" return self.gca().set_xlabel(label) @@ -459,7 +550,7 @@ def _generate_gle(self) -> str: content, _ = self._generate_gle_with_files() return content - def _build_metadata_dict(self, data_files: dict) -> dict: + def _build_metadata_dict(self, data_files: dict, raw_sidecars=None) -> dict: """Assemble the ``! gleplot:`` metadata payload for this save. Parameters @@ -481,12 +572,16 @@ def _build_metadata_dict(self, data_files: dict) -> dict: differ from the documented defaults. Any ``metadata_extra`` keys recovered from a parsed file are passed through verbatim. """ + raw = set(raw_sidecars or ()) data = { "dpi": self.dpi, "sharex": self.sharex, "sharey": self.sharey, "msize_scale": self.marker_config.msize_scale, - "import-data": sorted(data_files.keys()), + # Raw-content sidecars (heatmap/contour ``.z`` grids, scattered + # ``points.dat`` triples) are not columnar imports and must not be + # vouched for as such -- excluded from ``import-data``. + "import-data": sorted(k for k in data_files.keys() if k not in raw), } data.update(self.metadata_extra) return data @@ -528,6 +623,10 @@ def _generate_gle_with_files(self) -> tuple: self.passthrough_header or self.passthrough_trailer ) + # Palette / colorbar / contour-label subs needed by any axes. Emitted + # once, right after the preamble, before any graph uses them. + sub_texts = self._collect_sub_texts() + if is_single and no_fabricate: writer.add_preamble( include_graph_begin=False, passthrough_header=self.passthrough_header @@ -536,11 +635,14 @@ def _generate_gle_with_files(self) -> tuple: include_graph_end=False, passthrough_trailer=self.passthrough_trailer ) elif is_single: - # Single plot — backward-compatible simple layout + # Single plot — backward-compatible simple layout. 'begin graph' is + # emitted explicitly (not by the preamble) so palette subs and the + # fitz/contour pre-graph blocks can precede it. For a figure with + # neither, output is byte-identical to the historical layout. writer.add_preamble( - include_graph_begin=True, passthrough_header=self.passthrough_header + include_graph_begin=False, passthrough_header=self.passthrough_header ) - writer.add_graph_size() + writer.add_sub_defs(sub_texts) if self.axes_list: ax = self.axes_list[0] @@ -559,21 +661,36 @@ def _generate_gle_with_files(self) -> tuple: if ax.ymax is None: ax.ymax = data_ymax + self._emit_pre_graph_blocks(writer, ax) + writer.begin_graph() + # A colorbar is drawn to the right of the graph, outside its + # box. In the default 'auto' scale mode the graph fills the + # whole page and the bar clips off-page, so when this axes has + # a colorbar, pin the graph box to a width that reserves room + # for it. Figures without a colorbar keep the historical + # 'scale auto' output byte-for-byte. + reserved = self._axes_colorbar_reserved_cm(ax) + if reserved > 0: + graph_w = max(writer.width_cm - reserved, writer.width_cm * 0.3) + writer.add_graph_box_size(graph_w, writer.height_cm) + else: + writer.add_graph_size() self._write_axes_content(writer, ax) - graph_passthrough = ax.passthrough + writer.end_graph(passthrough=ax.passthrough) + self._emit_post_graph_calls(writer, ax) else: - graph_passthrough = None + writer.begin_graph() + writer.add_graph_size() + writer.end_graph() - writer.finalize( - include_graph_end=True, - graph_passthrough=graph_passthrough, - passthrough_trailer=self.passthrough_trailer, - ) + if self.passthrough_trailer: + writer.lines_gle.extend(self.passthrough_trailer) else: # Multi-subplot layout writer.add_preamble( include_graph_begin=False, passthrough_header=self.passthrough_header ) + writer.add_sub_defs(sub_texts) # Determine grid dimensions from axes positions max_rows = max(ax.position[0] for ax in self.axes_list) @@ -633,6 +750,21 @@ def _generate_gle_with_files(self) -> tuple: if "top" in self._subplot_adjust: margin_top = (1.0 - self._subplot_adjust["top"]) * writer.height_cm + # Reserve extra room on the right for a colorbar, if any axes has + # one. colorbar() enforces exactly one heatmap-bearing axes per + # figure, so at most one colorbar exists here; it is drawn at the + # right edge of its (rightmost, in the common 1-row layout) axes. + # This keeps simple grids correct; a colorbar on an axes that is + # NOT in the rightmost column could still overlap its neighbour + # (documented limitation). With no colorbar, margin_right is + # unchanged and non-colorbar layouts stay byte-identical. + cbar_reserved = max( + (self._axes_colorbar_reserved_cm(ax) for ax in self.axes_list), + default=0.0, + ) + if cbar_reserved > 0: + margin_right = max(margin_right, cbar_reserved) + avail_w = writer.width_cm - margin_left - margin_right avail_h = writer.height_cm - margin_bottom - margin_top @@ -672,6 +804,7 @@ def _generate_gle_with_files(self) -> tuple: writer.height_cm - margin_top - (row + 1) * cell_h - row * vspace ) + self._emit_pre_graph_blocks(writer, ax) writer.add_amove(x_pos, y_pos) writer.begin_graph() writer.add_graph_size( @@ -681,6 +814,7 @@ def _generate_gle_with_files(self) -> tuple: self._write_axes_content(writer, ax) writer.end_graph(passthrough=ax.passthrough) + self._emit_post_graph_calls(writer, ax) writer.lines_gle.append("") # Blank line between subplots writer.finalize( @@ -691,13 +825,211 @@ def _generate_gle_with_files(self) -> tuple: # ('! GLE graphics file' / '! Generated by gleplot') and before the # 'size ...' line -- add_preamble always emits exactly those two # lines first, so index 2 is the fixed, stable insertion point. - metadata_dict = self._build_metadata_dict(writer.data_files) + metadata_dict = self._build_metadata_dict( + writer.data_files, writer.raw_sidecars + ) metadata_lines = _gle_metadata.emit_metadata(metadata_dict) if metadata_lines: writer.lines_gle[2:2] = metadata_lines return writer.get_gle_content(), writer.data_files + # -- contour / heatmap helpers -------------------------------------- + + @staticmethod + def _heatmap_z_file(hm: dict) -> str: + """The ``.z`` grid file a heatmap's ``colormap`` references. + + Grid heatmaps reference their written ``.z`` sidecar directly; scattered + (points) heatmaps reference the ``.z`` file GLE's ``fitz`` generates + (points base with the ``.dat`` extension replaced by ``.z``). + """ + df = hm["data_file"] + if hm["source"] == "grid": + return df + return df[:-4] + ".z" if df.endswith(".dat") else df + ".z" + + @staticmethod + def _contour_z_file(ct: dict) -> str: + """The ``.z`` grid file a contour block reads (see :meth:`_heatmap_z_file`).""" + df = ct["data_file"] + if ct["source"] == "grid": + return df + return df[:-4] + ".z" if df.endswith(".dat") else df + ".z" + + @staticmethod + def _cmap_mode(cmap: str): + """Map a canonical cmap name to a ``colormap`` emission mode tuple. + + Returns ``('gray', None)`` (grayscale, no clause), ``('color', None)`` + (GLE built-in rainbow), or ``('palette', 'gleplot_')``. + """ + if cmap == "gray": + return ("gray", None) + if cmap == "rainbow": + return ("color", None) + return ("palette", f"gleplot_{cmap}") + + # -- colorbar layout reservation ------------------------------------ + # + # A vertical colorbar is drawn AFTER the graph, at ``xg(xgmax)+sep``, so it + # falls outside the graph box. On the single-plot path GLE's ``scale auto`` + # sizes the graph to fill the whole page, leaving no room to its right and + # clipping the bar. To fix that we shrink the graph box by a reserved + # right-hand margin computed here, purely from the colorbar dict (which the + # recognizer recovers verbatim) -- so the reserved size recomputes + # identically on a writer -> recognizer -> writer round trip and stays + # byte-stable (never parsed back from the emitted ``size`` line). + + #: Nominal tick-label text height (cm) used only to size the reserved + #: colorbar margin. A fixed constant (not derived from ``style.fontsize``) + #: keeps the reservation a pure function of the round-tripping colorbar + #: dict; a little slack here only widens the margin slightly. + _CBAR_TEXT_HEI_CM = 0.42 + + @staticmethod + def _estimate_tick_chars(zmin, zmax, fmt) -> int: + """Widest tick-number character count for a GLE ``fix N`` format. + + Deterministic estimate from the z-range and the ``format$`` string; + used only to size the reserved colorbar margin (see + :meth:`_colorbar_reserved_cm`). + """ + decimals = 1 + parts = str(fmt).strip().lower().split() + if len(parts) == 2 and parts[0] == "fix": + try: + decimals = max(int(parts[1]), 0) + except ValueError: + decimals = 1 + + def width_of(v) -> int: + v = float(v) + n = len(f"{abs(v):.{decimals}f}") + if v < 0: + n += 1 # minus sign + return n + + return max(width_of(zmin), width_of(zmax), 3) + + @classmethod + def _colorbar_reserved_cm(cls, cb: dict) -> float: + """Right-hand space (cm), measured from ``xgmax``, a colorbar needs. + + Sized honestly from how ``gleplot_colorbar_v`` lays out (see + :func:`gleplot.palettes.colorbar_sub_text`): the ``sep`` gap, the bar + (``wd``), then whichever is wider -- the tick marks + numbers to the + bar's right, or the rotated axis ``label`` (drawn at ``rc + 1.3``). + """ + wd = float(cb["width"]) + sep = float(cb["sep"]) + hei = cls._CBAR_TEXT_HEI_CM + charw = 0.6 * hei + nchars = cls._estimate_tick_chars(cb["zmin"], cb["zmax"], cb.get("format")) + # Both extents are measured from the bar's right edge. + tick_extent = wd / 3.0 + 0.1 + nchars * charw + label_extent = (1.3 + hei) if cb.get("label") else 0.0 + return sep + wd + max(tick_extent, label_extent) + 0.3 # + safety pad + + def _axes_colorbar_reserved_cm(self, ax: Axes) -> float: + """Max reserved colorbar margin (cm) over an axes' heatmaps (0 if none).""" + reserved = 0.0 + for hm in ax.heatmaps: + cb = hm.get("colorbar") + if cb: + reserved = max(reserved, self._colorbar_reserved_cm(cb)) + return reserved + + def _collect_sub_texts(self): + """Gather the palette/colorbar/clabel sub definitions this figure needs. + + Returns the deterministic ordered list of sub-definition texts: used + palette subs sorted by name, then the colorbar sub (if any colorbar), + then the contour-labels sub (if any clabel). + """ + from . import palettes as _pal + + used_cmaps = set() + any_colorbar = False + any_clabel = False + for ax in self.axes_list: + for hm in ax.heatmaps: + if _pal.cmap_needs_sub(hm["cmap"]): + used_cmaps.add(hm["cmap"]) + if hm.get("colorbar"): + any_colorbar = True + for ct in ax.contours: + if ct.get("clabel"): + any_clabel = True + + subs = [] + for cmap in sorted(used_cmaps): + text = _pal.palette_sub_text(cmap) + if text: + subs.append(text) + if any_colorbar: + subs.append(_pal.colorbar_sub_text()) + if any_clabel: + subs.append(_pal.contour_labels_sub_text()) + return subs + + def _emit_pre_graph_blocks(self, writer: GLEWriter, ax: Axes): + """Write sidecars + ``begin fitz``/``begin contour`` blocks for an axes. + + These execute before the graph reads the (generated) grid/contour + files, so they are emitted immediately before the axes' ``begin graph``. + """ + for hm in ax.heatmaps: + if hm["source"] == "points": + writer.add_points_sidecar(hm["data_file"], hm["x"], hm["y"], hm["zpts"]) + # tripcolor's fitz omits ncontour (GLE default), keeping the + # heatmap model's ncontour honestly None. + writer.add_fitz_block( + hm["data_file"], hm["extent"], hm["gridsize"], None + ) + else: + writer.add_z_sidecar( + hm["data_file"], hm["z"], hm["extent"], hm["origin"] + ) + + for ct in ax.contours: + if ct["source"] == "points": + writer.add_points_sidecar(ct["data_file"], ct["x"], ct["y"], ct["zpts"]) + writer.add_fitz_block( + ct["data_file"], ct["extent"], ct["gridsize"], ct["ncontour"] + ) + else: + writer.add_z_sidecar(ct["data_file"], ct["z"], ct["extent"], "lower") + writer.add_contour_block(self._contour_z_file(ct), ct["levels"]) + + def _emit_post_graph_calls(self, writer: GLEWriter, ax: Axes): + """Write the post-graph colorbar and contour-label sub calls.""" + for hm in ax.heatmaps: + cb = hm.get("colorbar") + if not cb: + continue + palette_call = self._palette_call_for(hm["cmap"]) + writer.add_colorbar_call( + sep=cb["sep"], + zmin=cb["zmin"], + zmax=cb["zmax"], + zstep=cb["zstep"], + palette_call=palette_call, + width=cb["width"], + fmt=cb["format"], + label=cb.get("label"), + ) + for ct in ax.contours: + if ct.get("clabel"): + clabels = self._contour_z_file(ct)[:-2] + "-clabels.dat" + writer.add_clabel_call(clabels, ct["clabel_fmt"]) + + @staticmethod + def _palette_call_for(cmap: str) -> str: + from . import palettes as _pal + + return _pal.palette_call_name(cmap) + def _write_axes_content(self, writer: GLEWriter, ax: Axes): """ Write all plot content for a single Axes into the current graph block. @@ -736,6 +1068,25 @@ def _write_axes_content(self, writer: GLEWriter, ax: Axes): remove_first_ytick=getattr(ax, "_remove_first_ytick", False), ) + # Heatmap colormap (drawn behind everything as the background) and + # contour polylines, before the ordinary series (fills, bars, ...). + for hm in ax.heatmaps: + writer.add_colormap( + self._heatmap_z_file(hm), + hm["pixels"], + self._cmap_mode(hm["cmap"]), + hm["vmin"], + hm["vmax"], + hm["invert"], + hm["interpolation"], + ) + + for ct in ax.contours: + cdata = self._contour_z_file(ct)[:-2] + "-cdata.dat" + writer.add_contour_line( + cdata, ct["color"], ct["linewidth"], ct["linestyle"] + ) + # Add fill regions (background) for fill_data in ax.fills: writer.add_fill_between( @@ -946,6 +1297,13 @@ def _get_data_xlim(self, ax: Axes) -> Tuple[Optional[float], Optional[float]]: if xmax is None or x.max() > xmax: xmax = float(x.max()) + for series in list(ax.heatmaps) + list(ax.contours): + x0, x1 = series["extent"][0], series["extent"][1] + if xmin is None or x0 < xmin: + xmin = float(x0) + if xmax is None or x1 > xmax: + xmax = float(x1) + return xmin, xmax def _get_data_ylim(self, ax: Axes) -> Tuple[Optional[float], Optional[float]]: @@ -1004,6 +1362,13 @@ def _get_data_ylim(self, ax: Axes) -> Tuple[Optional[float], Optional[float]]: if ymax is None or y.max() > ymax: ymax = float(y.max()) + for series in list(ax.heatmaps) + list(ax.contours): + y0, y1 = series["extent"][2], series["extent"][3] + if ymin is None or y0 < ymin: + ymin = float(y0) + if ymax is None or y1 > ymax: + ymax = float(y1) + return ymin, ymax def view(self, dpi: Optional[int] = None, format: str = "png") -> Optional[object]: diff --git a/src/gleplot/gui/data/panel.py b/src/gleplot/gui/data/panel.py index 166eb8b..e0545d0 100644 --- a/src/gleplot/gui/data/panel.py +++ b/src/gleplot/gui/data/panel.py @@ -79,8 +79,19 @@ #: Maximum number of rows shown in the preview table. _MAX_PREVIEW_ROWS = 100 -#: Plot-type combo entries. -_PLOT_TYPES = ["Line", "Scatter", "Line+markers", "Error bars"] +#: Plot-type combo entries for the standard x/y series. +_XY_PLOT_TYPES = ["Line", "Scatter", "Line+markers", "Error bars"] + +#: Scattered-data (x, y, z) plot types. These grid the points at GLE compile +#: time via ``fitz`` and can only be created in Import mode (``fitz`` reads a +#: raw x-y-z triples sidecar that gleplot writes, so there is no reference-mode +#: equivalent -- see :meth:`DataPanel.add_series`). +_HEATMAP_TYPE = "Heatmap (scattered x,y,z)" +_CONTOUR_TYPE = "Contour (scattered x,y,z)" +_XYZ_PLOT_TYPES = [_HEATMAP_TYPE, _CONTOUR_TYPE] + +#: All plot-type combo entries, in display order. +_PLOT_TYPES = _XY_PLOT_TYPES + _XYZ_PLOT_TYPES #: Sentinel text for "no Y-error column selected". _NONE_LABEL = "(none)" @@ -187,6 +198,13 @@ def _build_ui(self) -> None: self.yerr_combo = QComboBox(form_box) form.addRow("Y error", self.yerr_combo) + # Z column: shown only for the scattered (x, y, z) heatmap/contour + # plot types; hidden (along with the Y-error row) otherwise. + self.z_combo = QComboBox(form_box) + form.addRow("Z column", self.z_combo) + + self._form = form + self.label_edit = QLineEdit(form_box) form.addRow("Label", self.label_edit) @@ -205,6 +223,8 @@ def _build_ui(self) -> None: layout.addWidget(form_box) self._label_user_edited = False + # Start on the default (x/y) plot type: Z hidden, Y-error shown. + self._apply_plot_type_visibility(self.plot_type_combo.currentText()) def _connect_signals(self) -> None: self.load_button.clicked.connect(self._on_load_file_clicked) @@ -213,6 +233,7 @@ def _connect_signals(self) -> None: self.label_edit.textEdited.connect(self._on_label_edited) self.add_series_button.clicked.connect(self.add_series) self.mode_combo.currentTextChanged.connect(self._on_mode_changed) + self.plot_type_combo.currentTextChanged.connect(self._on_plot_type_changed) self.preview_table.horizontalHeader().sectionDoubleClicked.connect( self._on_header_double_clicked ) @@ -388,6 +409,7 @@ def _populate_column_combos(self) -> None: self.x_combo.clear() self.y_combo.clear() self.yerr_combo.clear() + self.z_combo.clear() self.yerr_combo.addItem(_NONE_LABEL, userData=-1) table = self._current_table @@ -403,6 +425,7 @@ def _populate_column_combos(self) -> None: self.x_combo.addItem(name, userData=idx) self.y_combo.addItem(name, userData=idx) self.yerr_combo.addItem(name, userData=idx) + self.z_combo.addItem(name, userData=idx) if self.x_combo.count() > 0: self.x_combo.setCurrentIndex(0) @@ -410,6 +433,11 @@ def _populate_column_combos(self) -> None: self.y_combo.setCurrentIndex(1) elif self.y_combo.count() > 0: self.y_combo.setCurrentIndex(0) + # Default Z to the third numeric column when available (x, y, z). + if self.z_combo.count() > 2: + self.z_combo.setCurrentIndex(2) + elif self.z_combo.count() > 0: + self.z_combo.setCurrentIndex(self.z_combo.count() - 1) self._label_user_edited = False self._update_default_label() @@ -433,6 +461,31 @@ def _update_default_label(self) -> None: def _on_mode_changed(self, text: str) -> None: self.mode_combo.setToolTip(_MODE_TOOLTIPS.get(text, "")) + def _on_plot_type_changed(self, text: str) -> None: + self._apply_plot_type_visibility(text) + + def _apply_plot_type_visibility(self, plot_type: str) -> None: + """Show the Z-column (and hide Y-error) for scattered heatmap/contour. + + The scattered (x, y, z) types grid at compile time via ``fitz`` and + only work in Import mode, so the Mode combo is forced to Import and + disabled while one of them is selected; the standard x/y types restore + the Y-error row and re-enable the mode choice. + """ + is_xyz = plot_type in _XYZ_PLOT_TYPES + self._set_row_visible(self.z_combo, is_xyz) + self._set_row_visible(self.yerr_combo, not is_xyz) + if is_xyz: + self.mode_combo.setCurrentText(_MODE_IMPORT) + self.mode_combo.setEnabled(not is_xyz) + + def _set_row_visible(self, field: QWidget, visible: bool) -> None: + """Show/hide a QFormLayout field together with its label widget.""" + field.setVisible(visible) + label = self._form.labelForField(field) + if label is not None: + label.setVisible(visible) + # ------------------------------------------------------------------ # Header editing (Track G1) # ------------------------------------------------------------------ @@ -727,6 +780,10 @@ def add_series(self) -> None: ax = figure.gca() + if plot_type in _XYZ_PLOT_TYPES: + self._add_xyz_series(ax, table, x_idx, y_idx, plot_type, label) + return + if mode == _MODE_REFERENCE: # gleplot's line_from_file / errorbar_from_file use 1-based # column indices (GLE convention); our combos store 0-based @@ -776,3 +833,48 @@ def add_series(self) -> None: self._document.notify_changed() self.series_added.emit(label) + + def _add_xyz_series( + self, ax, table, x_idx, y_idx, plot_type: str, label: str + ) -> None: + """Create a scattered heatmap/contour from x/y/z columns (Import only). + + Reads the Z column from :attr:`z_combo`, then calls + ``Axes.tripcolor`` (heatmap) or ``Axes.tricontour`` (contour). Both + write their own raw x-y-z triples sidecar for GLE ``fitz`` gridding, so + there is no reference-mode path. GLE allows at most one heatmap per + axes: a second heatmap request is refused with an explanatory message + rather than raising. ``ValueError`` from the core call (e.g. fewer than + three points, or the one-heatmap guard racing us) is surfaced the same + way, and no ``notify_changed`` fires on a refusal. + """ + z_idx = self.z_combo.currentData() + if z_idx is None: + return + + is_heatmap = plot_type == _HEATMAP_TYPE + if is_heatmap and ax.heatmaps: + QMessageBox.information( + self, + "Heatmap already present", + "GLE supports at most one heatmap (colormap) per axes, and " + "this axes already has one. Remove the existing heatmap (in " + "the Series tab) before adding another.", + ) + return + + x = table.columns[x_idx] + y = table.columns[y_idx] + z = table.columns[z_idx] + + try: + if is_heatmap: + ax.tripcolor(x, y, z, label=label) + else: + ax.tricontour(x, y, z, label=label) + except ValueError as exc: + QMessageBox.warning(self, "Could not add series", str(exc)) + return + + self._document.notify_changed() + self.series_added.emit(label) diff --git a/src/gleplot/gui/panels/axes_panel.py b/src/gleplot/gui/panels/axes_panel.py index c9912d8..2a10778 100644 --- a/src/gleplot/gui/panels/axes_panel.py +++ b/src/gleplot/gui/panels/axes_panel.py @@ -25,6 +25,8 @@ QWidget, ) +from gleplot.mathtext import mathtext_to_gle + #: Scale values accepted by Axes.set_xscale/set_yscale (see axes.py). _SCALE_VALUES = ("linear", "log") @@ -246,16 +248,16 @@ def _set_combo_text(combo: QComboBox, text: str) -> None: # UI -> Model: labels # ------------------------------------------------------------------ def _on_title_edited(self) -> None: - self._write_attr("title_text", self.title_edit.text()) + self._write_attr("title_text", mathtext_to_gle(self.title_edit.text())) def _on_xlabel_edited(self) -> None: - self._write_attr("xlabel_text", self.xlabel_edit.text()) + self._write_attr("xlabel_text", mathtext_to_gle(self.xlabel_edit.text())) def _on_ylabel_edited(self) -> None: - self._write_attr("ylabel_text", self.ylabel_edit.text()) + self._write_attr("ylabel_text", mathtext_to_gle(self.ylabel_edit.text())) def _on_y2label_edited(self) -> None: - self._write_attr("y2label_text", self.y2label_edit.text()) + self._write_attr("y2label_text", mathtext_to_gle(self.y2label_edit.text())) # ------------------------------------------------------------------ # UI -> Model: limits diff --git a/src/gleplot/gui/panels/series_panel.py b/src/gleplot/gui/panels/series_panel.py index 8fc00af..cda8e2b 100644 --- a/src/gleplot/gui/panels/series_panel.py +++ b/src/gleplot/gui/panels/series_panel.py @@ -42,8 +42,30 @@ bar yes* no no no yes no fill yes no no no yes yes file_series yes depends depends depends yes no +heatmap no no no no yes no +contour yes no no yes yes no =========== ===== ======= ========== ========= ===== ====== +``heatmap`` (``imshow``/``tripcolor``) and ``contour`` +(``contour``/``tricontour``) additionally expose their own dedicated +controls, applicable only to that kind: + +=========== ================================================================== +kind extra controls +=========== ================================================================== +heatmap palette, colour min/max (blank = auto), pixels, interpolation, + invert, colorbar toggle + colorbar label/format +contour levels ("0.1 0.2 0.3" explicit list, or "n=10" for n auto levels, + blank = GLE default), contour labels toggle + label format +=========== ================================================================== + +For ``heatmap`` the ``color`` control is disabled (a colormap has no single +line colour); ``contour`` reuses the shared ``color`` and ``line width`` +controls for its line styling. Both kinds keep ``label`` editable. Because +GLE allows at most one heatmap per axes, this panel never *creates* a +heatmap (that happens in the Data dock, which guards the limit); it only +edits an existing one. + ``bar`` stores a per-point ``colors`` list rather than a single ``color`` key (GLE only supports one color per bar chart in practice, so all entries are kept equal — see ``Axes.bar``); this panel edits index 0 and rewrites @@ -59,9 +81,11 @@ import os from typing import Optional +import numpy as np from PySide6.QtCore import Signal from PySide6.QtGui import QColor from PySide6.QtWidgets import ( + QCheckBox, QColorDialog, QComboBox, QDoubleSpinBox, @@ -73,12 +97,15 @@ QListWidget, QListWidgetItem, QPushButton, + QSpinBox, QVBoxLayout, QWidget, ) from gleplot.colors import rgb_to_gle from gleplot.markers import get_gle_marker +from gleplot.mathtext import mathtext_to_gle +from gleplot.palettes import SUPPORTED_CMAPS, canonical_cmap #: Marker prefixed onto the label of a ``file_series`` entry carrying a #: ``data_error`` (broken data reference), both in the aggregated list and @@ -95,8 +122,14 @@ ("fill", "fills"), ("errorbar", "errorbars"), ("file_series", "file_series"), + ("heatmap", "heatmaps"), + ("contour", "contours"), ) +#: Interpolation modes shown in the heatmap interpolation combo, matching the +#: values stored on a heatmap dict by ``Axes.imshow``/``Axes.tripcolor``. +_INTERPOLATION_MODES = ("bicubic", "nearest") + #: matplotlib-style marker codes shown in the combo box, plus 'none'. _MARKER_CODES = ("none", "o", "s", "^", "v", "D", "*", "p", "+", "x", ".") @@ -293,6 +326,64 @@ def _build_ui(self) -> None: form.addRow("Marker size", self.markersize_spin) form.addRow("Offset", self.offset_spin) + # ---- heatmap-only controls (imshow/tripcolor) ----------------- + self.palette_combo = QComboBox(self) + self.palette_combo.addItems(SUPPORTED_CMAPS) + + # vmin/vmax as free-text so an empty field means "auto" (GLE's + # data-range default); a spin box has no blank state. + self.vmin_edit = QLineEdit(self) + self.vmin_edit.setPlaceholderText("auto") + self.vmax_edit = QLineEdit(self) + self.vmax_edit.setPlaceholderText("auto") + + self.pixels_spin = QSpinBox(self) + self.pixels_spin.setRange(2, 4000) + self.pixels_spin.setSingleStep(50) + self.pixels_spin.setToolTip( + "Bitmap resolution of the colormap (applied to both axes)." + ) + + self.interp_combo = QComboBox(self) + self.interp_combo.addItems(_INTERPOLATION_MODES) + + self.invert_check = QCheckBox("Invert colours", self) + + self.colorbar_check = QCheckBox("Show colorbar", self) + self.cbar_label_edit = QLineEdit(self) + self.cbar_format_edit = QLineEdit(self) + self.cbar_format_edit.setToolTip( + 'GLE format$ string for the colorbar tick labels, e.g. "fix 1".' + ) + + form.addRow("Palette", self.palette_combo) + form.addRow("Colour min", self.vmin_edit) + form.addRow("Colour max", self.vmax_edit) + form.addRow("Pixels", self.pixels_spin) + form.addRow("Interpolation", self.interp_combo) + form.addRow("", self.invert_check) + form.addRow("", self.colorbar_check) + form.addRow("Colorbar label", self.cbar_label_edit) + form.addRow("Colorbar format", self.cbar_format_edit) + + # ---- contour-only controls (contour/tricontour) --------------- + self.levels_edit = QLineEdit(self) + self.levels_edit.setPlaceholderText("auto (e.g. 0.1 0.2 0.3 or n=10)") + self.levels_edit.setToolTip( + "Explicit contour levels ('0.1 0.2 0.3'), or 'n=10' for n " + "evenly-spaced auto levels, or blank for GLE's default." + ) + + self.clabel_check = QCheckBox("Label contours", self) + self.clabel_format_edit = QLineEdit(self) + self.clabel_format_edit.setToolTip( + 'GLE format$ string for the inline contour labels, e.g. "fix 1".' + ) + + form.addRow("Levels", self.levels_edit) + form.addRow("", self.clabel_check) + form.addRow("Label format", self.clabel_format_edit) + outer.addLayout(form) # Keep references to (widget, form-row-label-widget) so we can hide @@ -320,6 +411,22 @@ def _connect_signals(self) -> None: self.offset_spin.editingFinished.connect(self._on_offset_edited) self.locate_button.clicked.connect(self._on_locate_clicked) + # heatmap controls + self.palette_combo.currentTextChanged.connect(self._on_palette_changed) + self.vmin_edit.editingFinished.connect(self._on_vmin_edited) + self.vmax_edit.editingFinished.connect(self._on_vmax_edited) + self.pixels_spin.editingFinished.connect(self._on_pixels_edited) + self.interp_combo.currentTextChanged.connect(self._on_interp_changed) + self.invert_check.toggled.connect(self._on_invert_toggled) + self.colorbar_check.toggled.connect(self._on_colorbar_toggled) + self.cbar_label_edit.editingFinished.connect(self._on_cbar_label_edited) + self.cbar_format_edit.editingFinished.connect(self._on_cbar_format_edited) + + # contour controls + self.levels_edit.editingFinished.connect(self._on_levels_edited) + self.clabel_check.toggled.connect(self._on_clabel_toggled) + self.clabel_format_edit.editingFinished.connect(self._on_clabel_format_edited) + def _on_figure_replaced(self) -> None: """Handle a brand-new figure being installed (New/Open/undo/redo). @@ -447,6 +554,42 @@ def _populate_style_editor( if applicable.get("offset"): self.offset_spin.setValue(float(series.get("offset") or 0.0)) + + if applicable.get("palette"): + self._set_combo_text( + self.palette_combo, series.get("cmap") or "viridis" + ) + + if applicable.get("vmin"): + self.vmin_edit.setText(_float_to_text(series.get("vmin"))) + if applicable.get("vmax"): + self.vmax_edit.setText(_float_to_text(series.get("vmax"))) + + if applicable.get("pixels"): + pixels = series.get("pixels") or [200, 200] + self.pixels_spin.setValue(int(pixels[0])) + + if applicable.get("interpolation"): + self._set_combo_text( + self.interp_combo, series.get("interpolation") or "bicubic" + ) + + if applicable.get("invert"): + self.invert_check.setChecked(bool(series.get("invert"))) + + if applicable.get("colorbar"): + cb = series.get("colorbar") + self.colorbar_check.setChecked(cb is not None) + self.cbar_label_edit.setText((cb or {}).get("label") or "") + self.cbar_format_edit.setText((cb or {}).get("format") or "fix 1") + self._sync_colorbar_fields_enabled() + + if applicable.get("levels"): + self.levels_edit.setText(_levels_to_text(series.get("levels"))) + + if applicable.get("clabel"): + self.clabel_check.setChecked(bool(series.get("clabel"))) + self.clabel_format_edit.setText(series.get("clabel_fmt") or "fix 1") finally: self._updating = was_updating @@ -476,16 +619,44 @@ def _set_style_controls_enabled( ) -> None: applicable = applicable or {} self.label_edit.setEnabled(enabled) - self.color_button.setEnabled(enabled) + # ``color`` applies to every historical kind (default True), but is + # disabled for a heatmap, which has no single line colour. + self.color_button.setEnabled(enabled and applicable.get("color", True)) self.linestyle_combo.setEnabled(enabled and applicable.get("linestyle", False)) self.marker_combo.setEnabled(enabled and applicable.get("marker", False)) self.linewidth_spin.setEnabled(enabled and applicable.get("linewidth", False)) self.markersize_spin.setEnabled(enabled and applicable.get("markersize", False)) self.offset_spin.setEnabled(enabled and applicable.get("offset", False)) + + # heatmap-only controls + self.palette_combo.setEnabled(enabled and applicable.get("palette", False)) + self.vmin_edit.setEnabled(enabled and applicable.get("vmin", False)) + self.vmax_edit.setEnabled(enabled and applicable.get("vmax", False)) + self.pixels_spin.setEnabled(enabled and applicable.get("pixels", False)) + self.interp_combo.setEnabled(enabled and applicable.get("interpolation", False)) + self.invert_check.setEnabled(enabled and applicable.get("invert", False)) + self.colorbar_check.setEnabled(enabled and applicable.get("colorbar", False)) + # colorbar label/format follow the colorbar toggle (see + # _sync_colorbar_fields_enabled); disable outright off-heatmap. + if not (enabled and applicable.get("colorbar", False)): + self.cbar_label_edit.setEnabled(False) + self.cbar_format_edit.setEnabled(False) + + # contour-only controls + self.levels_edit.setEnabled(enabled and applicable.get("levels", False)) + self.clabel_check.setEnabled(enabled and applicable.get("clabel", False)) + self.clabel_format_edit.setEnabled(enabled and applicable.get("clabel", False)) + self.remove_button.setEnabled(enabled) self.up_button.setEnabled(enabled) self.down_button.setEnabled(enabled) + def _sync_colorbar_fields_enabled(self) -> None: + """Enable colorbar label/format only while the colorbar toggle is on.""" + on = self.colorbar_check.isEnabled() and self.colorbar_check.isChecked() + self.cbar_label_edit.setEnabled(on) + self.cbar_format_edit.setEnabled(on) + def _update_color_swatch(self, rgb: tuple) -> None: color = QColor(*rgb) self.color_button.setStyleSheet( @@ -559,7 +730,7 @@ def _on_label_edited(self) -> None: series = self._selected_series_dict() if series is None: return - series["label"] = self.label_edit.text() or None + series["label"] = mathtext_to_gle(self.label_edit.text() or None) self._document.notify_changed() self._refresh_selected_list_text() @@ -655,6 +826,221 @@ def _on_offset_edited(self) -> None: series["offset"] = float(self.offset_spin.value()) self._document.notify_changed() + # ------------------------------------------------------------------ + # UI -> Model: heatmap edits + # ------------------------------------------------------------------ + def _on_palette_changed(self, text: str) -> None: + if self._updating: + return + series = self._selected_series_dict() + if series is None or not self.palette_combo.isEnabled(): + return + series["cmap"] = canonical_cmap(text) + self._document.notify_changed() + + def _on_vmin_edited(self) -> None: + self._edit_optional_float("vmin", self.vmin_edit) + + def _on_vmax_edited(self) -> None: + self._edit_optional_float("vmax", self.vmax_edit) + + def _edit_optional_float(self, key: str, edit: QLineEdit) -> None: + """Store a blank-means-``None`` float from ``edit`` under ``key``. + + Reverts the field to the stored value if the text can't be parsed as a + float (rather than silently coercing garbage to a number). + """ + if self._updating: + return + series = self._selected_series_dict() + if series is None or not edit.isEnabled(): + return + text = edit.text().strip() + if not text: + value = None + else: + try: + value = float(text) + except ValueError: + edit.setText(_float_to_text(series.get(key))) + return + series[key] = value + self._document.notify_changed() + + def _on_pixels_edited(self) -> None: + if self._updating: + return + series = self._selected_series_dict() + if series is None or not self.pixels_spin.isEnabled(): + return + px = int(self.pixels_spin.value()) + series["pixels"] = [px, px] + self._document.notify_changed() + + def _on_interp_changed(self, text: str) -> None: + if self._updating: + return + series = self._selected_series_dict() + if series is None or not self.interp_combo.isEnabled(): + return + series["interpolation"] = "nearest" if text == "nearest" else "bicubic" + self._document.notify_changed() + + def _on_invert_toggled(self, checked: bool) -> None: + if self._updating: + return + series = self._selected_series_dict() + if series is None or not self.invert_check.isEnabled(): + return + series["invert"] = bool(checked) + self._document.notify_changed() + + def _on_colorbar_toggled(self, checked: bool) -> None: + if self._updating: + return + series = self._selected_series_dict() + if series is None or not self.colorbar_check.isEnabled(): + return + if checked: + if series.get("colorbar") is None: + series["colorbar"] = self._make_colorbar_dict( + series, + self.cbar_label_edit.text() or None, + self.cbar_format_edit.text() or "fix 1", + ) + else: + series["colorbar"] = None + self._sync_colorbar_fields_enabled() + self._document.notify_changed() + + def _on_cbar_label_edited(self) -> None: + if self._updating: + return + series = self._selected_series_dict() + if series is None or not self.cbar_label_edit.isEnabled(): + return + cb = series.get("colorbar") + if cb is None: + return + cb["label"] = mathtext_to_gle(self.cbar_label_edit.text() or None) + self._document.notify_changed() + + def _on_cbar_format_edited(self) -> None: + if self._updating: + return + series = self._selected_series_dict() + if series is None or not self.cbar_format_edit.isEnabled(): + return + cb = series.get("colorbar") + if cb is None: + return + cb["format"] = self.cbar_format_edit.text() or "fix 1" + self._document.notify_changed() + + @staticmethod + def _series_z_range(series: dict) -> tuple: + """Return ``(zmin, zmax)`` for a heatmap/contour from its stored data. + + Mirrors ``Figure.colorbar``/``Axes._resolve_levels``: a ``vmin``/ + ``vmax`` override wins; otherwise the range is taken from the gridded + ``z`` (grid source) or the scattered ``zpts`` (points source). + """ + vmin = series.get("vmin") + vmax = series.get("vmax") + data = series.get("z") if series.get("source") == "grid" else series.get("zpts") + if data is None: + arr = None + else: + arr = np.asarray(data, dtype=float) + if vmin is not None: + zmin = float(vmin) + elif arr is not None and arr.size: + zmin = float(np.nanmin(arr)) + else: + zmin = 0.0 + if vmax is not None: + zmax = float(vmax) + elif arr is not None and arr.size: + zmax = float(np.nanmax(arr)) + else: + zmax = 1.0 + return zmin, zmax + + def _make_colorbar_dict(self, series: dict, label, fmt: str) -> dict: + """Build a colorbar dict for ``series`` matching ``Figure.colorbar``. + + Targets the selected heatmap directly (rather than calling + ``Figure.colorbar``, which errors when more than one axes has a + heatmap) so the panel works regardless of sibling axes. + """ + zmin, zmax = self._series_z_range(series) + span = zmax - zmin + zstep = (span / 5.0) if span > 0 else 1.0 + return { + "label": mathtext_to_gle(label), + "format": fmt or "fix 1", + "width": 0.5, + "sep": 0.3, + "zmin": zmin, + "zmax": zmax, + "zstep": zstep, + } + + # ------------------------------------------------------------------ + # UI -> Model: contour edits + # ------------------------------------------------------------------ + def _on_levels_edited(self) -> None: + if self._updating: + return + series = self._selected_series_dict() + if series is None or not self.levels_edit.isEnabled(): + return + try: + levels = self._parse_levels_text(self.levels_edit.text(), series) + except ValueError: + self.levels_edit.setText(_levels_to_text(series.get("levels"))) + return + series["levels"] = levels + self._document.notify_changed() + + def _parse_levels_text(self, text: str, series: dict): + """Parse the levels field to ``None`` or a list of floats. + + Accepts a blank field (``None`` -> GLE's default 10 levels), an + ``"n="`` form (``n`` evenly-spaced levels strictly between the + data's min and max, matching ``Axes._resolve_levels``), or an explicit + whitespace/comma-separated list of numbers. + """ + text = text.strip() + if not text: + return None + low = text.lower() + if low.startswith("n="): + n = int(low[2:].strip()) + if n < 1: + raise ValueError("levels count must be >= 1") + zmin, zmax = self._series_z_range(series) + return [float(v) for v in np.linspace(zmin, zmax, n + 2)[1:-1]] + return [float(tok) for tok in text.replace(",", " ").split()] + + def _on_clabel_toggled(self, checked: bool) -> None: + if self._updating: + return + series = self._selected_series_dict() + if series is None or not self.clabel_check.isEnabled(): + return + series["clabel"] = bool(checked) + self._document.notify_changed() + + def _on_clabel_format_edited(self) -> None: + if self._updating: + return + series = self._selected_series_dict() + if series is None or not self.clabel_format_edit.isEnabled(): + return + series["clabel_fmt"] = self.clabel_format_edit.text() or "fix 1" + self._document.notify_changed() + def _on_locate_clicked(self) -> None: """Repoint a broken ``file_series`` entry at a real file on disk. @@ -801,6 +1187,37 @@ def _applicable_controls(kind: str, series: dict) -> dict: "linewidth": True, "markersize": False, } + if kind == "heatmap": + # A colormap has no single line colour; it exposes palette + range + + # sampling controls plus an optional colorbar instead. + return { + "color": False, + "marker": False, + "linestyle": False, + "linewidth": False, + "markersize": False, + "offset": False, + "palette": True, + "vmin": True, + "vmax": True, + "pixels": True, + "interpolation": True, + "invert": True, + "colorbar": True, + } + if kind == "contour": + # Contour lines reuse the shared color + line-width controls, and add + # a levels editor and inline-label toggle. + return { + "color": True, + "marker": False, + "linestyle": False, + "linewidth": True, + "markersize": False, + "offset": False, + "levels": True, + "clabel": True, + } return { "color": False, "marker": False, @@ -808,3 +1225,17 @@ def _applicable_controls(kind: str, series: dict) -> dict: "linewidth": False, "markersize": False, } + + +def _float_to_text(value) -> str: + """Render an optional float for a blank-means-``None`` line edit.""" + if value is None: + return "" + return repr(float(value)) + + +def _levels_to_text(levels) -> str: + """Render a contour ``levels`` value (``None`` or list) as edit text.""" + if not levels: + return "" + return " ".join(repr(float(v)) for v in levels) diff --git a/src/gleplot/mathtext.py b/src/gleplot/mathtext.py new file mode 100644 index 0000000..e314d0e --- /dev/null +++ b/src/gleplot/mathtext.py @@ -0,0 +1,514 @@ +"""Translate matplotlib-style ``$...$`` mathtext into GLE's native text markup. + +Matplotlib users write axis labels, titles and legend keys such as +``r"$\\chi$ (emu/mol)"`` or ``r"emu mol$^{-1}$"``. GLE's text engine speaks a +TeX-like markup of its own that natively understands LaTeX symbol macros +(``\\alpha`` ... ``\\Omega``, ``\\chi``, ``\\times`` ...), braced sub/superscripts +(``^{}`` / ``_{}``) and a handful of spacing/font commands. This module bridges +the two so matplotlib users get Greek/math rendering without learning GLE +markup. + +Design (see the feature brief / CLAUDE.md conventions): + +* **Translate at STORE time**, exactly like the colour/marker mappings: the + object model stores the *translated* GLE-markup string and the writer stays + untouched. A ``.gle`` file re-parsed by the recognizer already contains GLE + markup, so writer -> recognizer -> writer stays a byte-identical fixed point. +* **Idempotent.** A string with no ``$`` is returned unchanged (round-tripped + labels are already GLE markup), so ``translate(translate(s)) == translate(s)``. +* **Graceful degradation.** An odd number of unescaped ``$`` (which matplotlib + would reject) leaves the string completely unchanged rather than guessing. + +Only the text *inside* ``$...$`` segments is translated; text outside passes +through verbatim (it may already be GLE markup). ``\\$`` is a literal dollar +sign everywhere and never opens/closes a math segment. + +The supported-macro tables below are derived from the GLE 4.3.10 manual: + +* LaTeX symbol macros (Greek etc.): ``appendix/fig/symbols.csv`` / + ``appendix/sym.tex`` ("LaTeX Symbols"). +* Text commands (``^{}`` ``_{}`` ``\\,`` ``\\:`` ``\\;`` ``\\!`` ``\\_`` ``{\\rm}`` + ``{\\it}`` ``{\\bf}`` ``{\\tt}``): ``appendix/sym.tex`` ("LaTeX Macros") and + ``primitives/cmds.tex`` (the ``text`` primitive). + +Examples +-------- +>>> mathtext_to_gle(r"$\\chi$ (emu/mol)") +'\\\\chi{} (emu/mol)' +>>> mathtext_to_gle(r"emu mol$^{-1}$") +'emu mol^{-1}' +>>> mathtext_to_gle(r"$x_i^2$") +'x_{i}^{2}' +>>> mathtext_to_gle(r"$T$ (\\degree C)") # already GLE markup outside math +'T (\\\\degree C)' +>>> mathtext_to_gle(r"cost \\$5") # escaped dollar -> literal +'cost $5' +>>> mathtext_to_gle(r"$x = 5") # unmatched $ -> unchanged +'$x = 5' +>>> mathtext_to_gle(r"\\chi{} (emu/mol)") # no $ -> identity +'\\\\chi{} (emu/mol)' +""" + +import re +from typing import List, Optional, Tuple + +__all__ = ["mathtext_to_gle"] + + +# --------------------------------------------------------------------------- +# Supported-macro tables (provenance: GLE 4.3.10 manual) +# --------------------------------------------------------------------------- + +# LaTeX symbol macros GLE renders natively -> passed through verbatim as +# ``\name``. Verbatim copy of the manual's symbol table +# (gle-manual/appendix/fig/symbols.csv, rendered by appendix/sym.tex). +# Includes Greek letters plus the common math symbols matplotlib users reach +# for (\times, \cdot, \pm, \infty, \degree, \circ, \approx, \leq, \geq ...). +GLE_SYMBOL_MACROS = frozenset( + { + "AA", + "AE", + "Delta", + "Downarrow", + "Gamma", + "Im", + "L", + "Lambda", + "Leftarrow", + "Leftrightarrow", + "O", + "OE", + "Omega", + "P", + "Phi", + "Pi", + "Psi", + "Re", + "Rightarrow", + "S", + "Sigma", + "Theta", + "Uparrow", + "Updownarrow", + "Upsilon", + "Xi", + "aa", + "ae", + "aleph", + "alpha", + "amalg", + "approx", + "ast", + "asymp", + "backslash", + "beta", + "bigcap", + "bigcirc", + "bigcup", + "bigodot", + "bigoplus", + "bigotimes", + "bigsqcup", + "bigtriangledown", + "bigtriangleup", + "biguplus", + "bigvee", + "bigwedge", + "bot", + "bullet", + "cap", + "cdot", + "chi", + "circ", + "clubsuit", + "coprod", + "cup", + "dag", + "dagger", + "dashv", + "ddag", + "ddagger", + "degree", + "delta", + "diamond", + "diamondsuit", + "div", + "downarrow", + "ell", + "emptyset", + "epsilon", + "equiv", + "eta", + "exists", + "flat", + "forall", + "frown", + "gamma", + "geq", + "gg", + "heartsuit", + "i", + "imath", + "in", + "infty", + "intop", + "iota", + "j", + "jmath", + "kappa", + "l", + "lambda", + "land", + "leftarrow", + "leftharpoondown", + "leftharpoonup", + "leftrightarrow", + "leq", + "lhook", + "ll", + "lor", + "mapsto", + "mapstochar", + "mid", + "minus", + "mp", + "mu", + "nabla", + "natural", + "nearrow", + "neg", + "neq", + "ni", + "not", + "nu", + "nwarrow", + "o", + "odot", + "oe", + "ointop", + "omega", + "ominus", + "oplus", + "oslash", + "otimes", + "owns", + "parallel", + "partial", + "perp", + "phi", + "pi", + "pm", + "prec", + "preceq", + "prime", + "prod", + "propto", + "psi", + "rho", + "rhook", + "rightarrow", + "rightharpoondown", + "rightharpoonup", + "searrow", + "setminus", + "sharp", + "sigma", + "sim", + "simeq", + "smallint", + "smile", + "spadesuit", + "sqcap", + "sqcup", + "sqsubseteq", + "sqsupseteq", + "ss", + "star", + "subset", + "subseteq", + "succ", + "succeq", + "sum", + "supset", + "supseteq", + "swarrow", + "tau", + "theta", + "times", + "top", + "triangle", + "triangleleft", + "triangleright", + "uparrow", + "updownarrow", + "uplus", + "upsilon", + "varepsilon", + "varphi", + "varpi", + "varrho", + "varsigma", + "vartheta", + "vdash", + "vee", + "wedge", + "wp", + "wr", + "xi", + "zeta", + } +) + +# ``\mathXX{...}`` / ``\textXX{...}`` font macros -> GLE inline font groups +# ``{\rm ...}`` / ``{\it ...}`` / ``{\bf ...}`` / ``{\tt ...}`` +# (gle-manual/appendix/sym.tex "LaTeX Macros"; primitives/cmds.tex text cmds). +# matplotlib's default math font is italic, so ``\mathrm``/``\text`` force +# upright roman. Families GLE has no simple inline equivalent for (sans-serif) +# degrade to their contents (braces stripped) to preserve the text. +FONT_MACROS = { + "mathrm": "rm", + "mathit": "it", + "mathbf": "bf", + "mathtt": "tt", + "text": "rm", + "textrm": "rm", + "textit": "it", + "textbf": "bf", + "texttt": "tt", + "mathsf": None, # no inline sans-serif in GLE -> strip to contents + "mathcal": None, # no calligraphic font -> strip to contents + "mathbb": None, # no blackboard-bold font -> strip to contents + "operatorname": "rm", +} + +# Backslash + single non-letter symbol macros handled explicitly inside math. +# GLE natively supports the spacing macros ``\, \: \; \!`` and the literal +# underscore ``\_`` (appendix/sym.tex). Others map to the bare character. +_SYMBOL_ESCAPES = { + ",": "\\,", # thin space (GLE: 0.5em) + ":": "\\:", # medium space (GLE: 1em) + ";": "\\;", # thick space (GLE: 2em) + "!": "\\!", # negative thin space (GLE: -0.5em) + " ": "\\,", # LaTeX control space -> thin space + "_": "\\_", # literal underscore (GLE native escape) + "%": "%", + "&": "&", + "#": "#", + "{": "{", + "}": "}", + "$": "$", +} + +# A GLE token that ends in a bare macro name (``\word``). Used to decide whether +# a math->text boundary needs a ``{}`` terminator so GLE does not swallow the +# following space or merge the macro with following letters. +_TRAILING_MACRO = re.compile(r"\\[A-Za-z]+$") + + +def mathtext_to_gle(s: Optional[str]) -> Optional[str]: + """Translate matplotlib mathtext (``$...$``) in *s* into GLE text markup. + + Parameters + ---------- + s : str or None + A display string as a matplotlib user would write it. Non-strings + (including ``None``) are returned unchanged, so callers may pass an + optional label straight through. + + Returns + ------- + str or None + The string with every ``$...$`` math segment rewritten in GLE markup. + Text outside math segments is preserved verbatim. Returned unchanged + when *s* contains no ``$`` (idempotent on already-translated GLE + markup) or when the unescaped ``$`` count is odd (matplotlib would + error; we degrade gracefully rather than guess). + """ + if not isinstance(s, str) or "$" not in s: + return s + + segments = _split_segments(s) + if segments is None: + # Unbalanced ``$`` -> leave the caller's string exactly as given. + return s + + # Translate each segment, then stitch, inserting ``{}`` after a math + # segment that ends in a bare macro when the *next emitted character* + # (across any following segments) is a letter or whitespace. + rendered: List[Tuple[str, bool]] = [] + for kind, text in segments: + if kind == "math": + gle = _translate_math(text) + rendered.append((gle, bool(_TRAILING_MACRO.search(gle)))) + else: + rendered.append((text, False)) + + out: List[str] = [] + for idx, (text, trailing_macro) in enumerate(rendered): + out.append(text) + if trailing_macro: + nxt = _next_char(rendered, idx + 1) + if nxt is not None and (nxt.isalpha() or nxt.isspace()): + out.append("{}") + return "".join(out) + + +def _next_char(rendered: List[Tuple[str, bool]], start: int) -> Optional[str]: + """First character of the concatenation of segments from *start* onward.""" + for text, _ in rendered[start:]: + if text: + return text[0] + return None + + +def _split_segments(s: str) -> Optional[List[Tuple[str, str]]]: + """Split *s* into alternating ``('text'|'math', content)`` segments. + + ``\\$`` is treated as a literal ``$`` (in both text and math) and never + acts as a delimiter. Returns ``None`` when the unescaped ``$`` count is + odd (an unterminated math segment). + """ + segments: List[Tuple[str, str]] = [] + buf: List[str] = [] + in_math = False + i, n = 0, len(s) + while i < n: + c = s[i] + if c == "\\" and i + 1 < n and s[i + 1] == "$": + buf.append("$") # literal dollar, not a delimiter + i += 2 + continue + if c == "$": + segments.append(("math" if in_math else "text", "".join(buf))) + buf = [] + in_math = not in_math + i += 1 + continue + buf.append(c) + i += 1 + if in_math: + return None + segments.append(("text", "".join(buf))) + return segments + + +def _translate_math(m: str) -> str: + """Translate the interior of one ``$...$`` segment into GLE markup.""" + out: List[str] = [] + i, n = 0, len(m) + while i < n: + c = m[i] + if c == "\\": + token, i = _read_macro(m, i) + out.append(token) + elif c in "^_": + token, i = _read_script(m, i) + out.append(token) + elif c == "{": + # Bare TeX grouping (invisible in matplotlib). Strip the braces so + # GLE does not render literal braces; keep the translated contents. + group, i = _read_brace_group(m, i) + out.append(_translate_math(group)) + elif c == "}": + i += 1 # stray closing brace: drop it + else: + out.append(c) + i += 1 + return "".join(out) + + +def _read_macro(m: str, i: int) -> Tuple[str, int]: + """Read a ``\\...`` macro starting at index *i* (m[i] == '\\').""" + j = i + 1 + if j >= len(m): + return "", j # trailing backslash -> drop + # Backslash + non-letter: spacing / escaped-symbol macro. + if not m[j].isalpha(): + ch = m[j] + return _SYMBOL_ESCAPES.get(ch, ch), j + 1 + # Backslash + letters: a named macro. + k = j + while k < len(m) and m[k].isalpha(): + k += 1 + name = m[j:k] + + if name == "frac": + return _read_frac(m, k) + + if name in FONT_MACROS: + group, after = _read_optional_group(m, k) + if group is None: + # No brace group followed: emit as a bare pass-through macro. + return "\\" + name, k + inner = _translate_math(group) + font = FONT_MACROS[name] + if font is None: + return inner, after # unsupported family -> strip to contents + return "{\\" + font + " " + inner + "}", after + + # Known GLE symbol macro, or an unknown macro we pass through verbatim + # (GLE understands more than we enumerate; passing through preserves + # user intent). Either way emit ``\name``. + return "\\" + name, k + + +def _read_script(m: str, i: int) -> Tuple[str, int]: + """Read a ``^``/``_`` script at index *i* and return a braced GLE form.""" + op = m[i] # '^' or '_' + j = i + 1 + if j >= len(m): + return op, j # dangling ^ or _ : emit literally + c = m[j] + if c == "{": + group, after = _read_brace_group(m, j) + return op + "{" + _translate_math(group) + "}", after + if c == "\\": + token, after = _read_macro(m, j) + return op + "{" + token + "}", after + # Single character token. + return op + "{" + c + "}", j + 1 + + +def _read_frac(m: str, i: int) -> Tuple[str, int]: + """Degrade ``\\frac{a}{b}`` (no GLE inline equivalent) to ``a/b``.""" + num, i = _read_optional_group(m, i) + den, i = _read_optional_group(m, i) + num_s = _translate_math(num) if num is not None else "" + den_s = _translate_math(den) if den is not None else "" + return num_s + "/" + den_s, i + + +def _read_optional_group(m: str, i: int) -> Tuple[Optional[str], int]: + """If m[i] opens a ``{...}`` group return (contents, index_after); skips + leading whitespace. Otherwise return (None, i).""" + j = i + while j < len(m) and m[j].isspace(): + j += 1 + if j < len(m) and m[j] == "{": + return _read_brace_group(m, j) + return None, i + + +def _read_brace_group(m: str, i: int) -> Tuple[str, int]: + """Read a balanced ``{...}`` group at index *i* (m[i] == '{'). + + Returns (inner_contents_without_outer_braces, index_after_closing_brace). + Tolerates an unclosed group by consuming to end of string. + """ + assert m[i] == "{" + depth = 0 + j = i + n = len(m) + while j < n: + ch = m[j] + if ch == "\\": + j += 2 # skip escaped char + continue + if ch == "{": + depth += 1 + elif ch == "}": + depth -= 1 + if depth == 0: + return m[i + 1 : j], j + 1 + j += 1 + return m[i + 1 :], n # unterminated -> take the rest diff --git a/src/gleplot/palettes.py b/src/gleplot/palettes.py new file mode 100644 index 0000000..7b8444b --- /dev/null +++ b/src/gleplot/palettes.py @@ -0,0 +1,437 @@ +"""Self-contained GLE colour palettes for gleplot's heatmap/contour support. + +gleplot emits **self-contained** palette subroutines directly into the ``.gle`` +script (no ``include "palettes.gle"`` / ``include "color.gle"`` dependency), so +a generated script compiles anywhere GLE is installed without the gle-library +on the include path. + +Palette RGB stop tables +----------------------- +The perceptually-uniform ramps (``viridis``, ``magma``, ``inferno``, +``plasma``, ``cividis``) are transcribed VERBATIM -- 18 RGB nodes each -- from +gle-library ``include/palettes.gle`` (palettes ported by Francois Tonneau, +2025; original data from https://github.com/BIDS/colormap and, for cividis, +https://github.com/pnnl/cmaputil). The emitted sub reproduces that file's +linear-interpolation-in-RGB semantics (``palettes_r``/``_g``/``_b`` + +``palettes_hue``) but inlined as a self-contained piecewise-linear function so +no shared globals/helpers are required. + +``coolwarm`` is the ``palette_blue_white_red`` subroutine transcribed from +gle-library ``include/color.gle`` (Modified BSD, (C) 2009 GLE) -- a +blue->white->red diverging ramp defined by an explicit formula rather than a +stop table, so it is emitted as a near-verbatim copy of that formula. + +``gray`` (GLE grayscale default) and ``rainbow``/``jet`` (GLE built-in +``color`` rainbow) need no subroutine at all. + +Canonical text +-------------- +:func:`palette_sub_text` is a pure function of the palette name, so the emitted +sub body is deterministic -- essential for the writer -> recognizer -> writer +byte-identical fixed point (the recognizer recognizes ``gleplot_`` subs +by name and regenerates their bodies from here rather than trying to preserve +the original bytes). +""" + +from __future__ import annotations + +from typing import Dict, List, Optional, Tuple + +__all__ = [ + "SUPPORTED_CMAPS", + "PALETTE_STOPS", + "canonical_cmap", + "cmap_needs_sub", + "palette_sub_name", + "palette_call_name", + "palette_sub_text", + "colorbar_sub_name", + "colorbar_sub_text", + "contour_labels_sub_name", + "contour_labels_sub_text", +] + + +# --------------------------------------------------------------------------- +# Supported colour-map names (matplotlib-facing) and their canonical form. +# --------------------------------------------------------------------------- +# +# "jet" is an alias for "rainbow" (both map to GLE's built-in ``color`` +# rainbow); it is canonicalized to "rainbow" at store time so the object model +# only ever holds canonical names. +_CMAP_ALIASES: Dict[str, str] = {"jet": "rainbow"} + +#: Every cmap name accepted by ``imshow``/``tripcolor`` (before alias +#: canonicalization). Canonical names are the values of :data:`_CMAP_ALIASES` +#: plus every key of :data:`PALETTE_STOPS` plus ``gray``/``rainbow``/``coolwarm``. +SUPPORTED_CMAPS: Tuple[str, ...] = ( + "gray", + "rainbow", + "jet", + "viridis", + "magma", + "inferno", + "plasma", + "cividis", + "coolwarm", +) + + +# --------------------------------------------------------------------------- +# RGB stop tables (18 nodes each), transcribed verbatim from +# gle-library/include/palettes.gle lines 56-136 (inferno/magma/plasma/viridis) +# and 140-157 (cividis). Each entry is (r, g, b) with components in [0, 1]. +# --------------------------------------------------------------------------- +PALETTE_STOPS: Dict[str, List[Tuple[str, str, str]]] = { + # palettes.gle lines 56-73 + "inferno": [ + ("0.001462", "0.000466", "0.013866"), + ("0.037668", "0.025921", "0.132232"), + ("0.116656", "0.047574", "0.272321"), + ("0.217949", "0.036615", "0.383522"), + ("0.316282", "0.053490", "0.425116"), + ("0.410113", "0.087896", "0.433098"), + ("0.503493", "0.121575", "0.423356"), + ("0.596940", "0.154848", "0.398125"), + ("0.688653", "0.192239", "0.357603"), + ("0.775059", "0.239667", "0.303526"), + ("0.851384", "0.302260", "0.239636"), + ("0.912966", "0.381636", "0.169755"), + ("0.956852", "0.475356", "0.094695"), + ("0.981895", "0.579392", "0.026250"), + ("0.987464", "0.690366", "0.079990"), + ("0.973088", "0.805409", "0.216877"), + ("0.947594", "0.917399", "0.410665"), + ("0.988362", "0.998364", "0.644924"), + ], + # palettes.gle lines 77-94 + "magma": [ + ("0.001462", "0.000466", "0.013866"), + ("0.035520", "0.028397", "0.125209"), + ("0.102815", "0.063010", "0.257854"), + ("0.191460", "0.064818", "0.396152"), + ("0.291366", "0.064553", "0.475462"), + ("0.384299", "0.097855", "0.501002"), + ("0.475780", "0.134577", "0.507921"), + ("0.569172", "0.167454", "0.504105"), + ("0.664915", "0.198075", "0.488836"), + ("0.761077", "0.231214", "0.460162"), + ("0.852126", "0.276106", "0.418573"), + ("0.925937", "0.346844", "0.374959"), + ("0.969680", "0.446936", "0.360311"), + ("0.989363", "0.557873", "0.391671"), + ("0.996580", "0.668256", "0.456192"), + ("0.996727", "0.776795", "0.541039"), + ("0.992440", "0.884330", "0.640099"), + ("0.987053", "0.991438", "0.749504"), + ], + # palettes.gle lines 98-115 + "plasma": [ + ("0.050383", "0.029803", "0.527975"), + ("0.186213", "0.018803", "0.587228"), + ("0.287076", "0.010855", "0.627295"), + ("0.381047", "0.001814", "0.653068"), + ("0.471457", "0.005678", "0.659897"), + ("0.557243", "0.047331", "0.643443"), + ("0.636008", "0.112092", "0.605205"), + ("0.706178", "0.178437", "0.553657"), + ("0.768090", "0.244817", "0.498465"), + ("0.823132", "0.311261", "0.444806"), + ("0.872303", "0.378774", "0.393355"), + ("0.915471", "0.448807", "0.342890"), + ("0.951344", "0.522850", "0.292275"), + ("0.977856", "0.602051", "0.241387"), + ("0.992541", "0.687030", "0.192170"), + ("0.992505", "0.777967", "0.152855"), + ("0.974443", "0.874622", "0.144061"), + ("0.940015", "0.975158", "0.131326"), + ], + # palettes.gle lines 119-136 + "viridis": [ + ("0.267004", "0.004874", "0.329415"), + ("0.281924", "0.089666", "0.412415"), + ("0.280255", "0.165693", "0.476498"), + ("0.263663", "0.237631", "0.518762"), + ("0.237441", "0.305202", "0.541921"), + ("0.208623", "0.367752", "0.552675"), + ("0.182256", "0.426184", "0.557120"), + ("0.159194", "0.482237", "0.558073"), + ("0.137770", "0.537492", "0.554906"), + ("0.121148", "0.592739", "0.544641"), + ("0.128087", "0.647749", "0.523491"), + ("0.180653", "0.701402", "0.488189"), + ("0.274149", "0.751988", "0.436601"), + ("0.395174", "0.797475", "0.367757"), + ("0.535621", "0.835785", "0.281908"), + ("0.688944", "0.865448", "0.182725"), + ("0.845561", "0.887322", "0.099702"), + ("0.993248", "0.906157", "0.143936"), + ], + # palettes.gle lines 140-157 + "cividis": [ + ("0.000000", "0.126200", "0.301500"), + ("0.000000", "0.168500", "0.403100"), + ("0.000000", "0.207300", "0.432900"), + ("0.156600", "0.249800", "0.423600"), + ("0.237500", "0.292000", "0.420000"), + ("0.301400", "0.334000", "0.422400"), + ("0.358200", "0.376300", "0.430200"), + ("0.411400", "0.418900", "0.443000"), + ("0.462200", "0.462200", "0.462000"), + ("0.515800", "0.506500", "0.473600"), + ("0.573500", "0.552200", "0.472000"), + ("0.632800", "0.599300", "0.464100"), + ("0.693600", "0.647800", "0.449900"), + ("0.756000", "0.697900", "0.429000"), + ("0.820000", "0.749700", "0.400700"), + ("0.885800", "0.803500", "0.362700"), + ("0.953600", "0.859300", "0.311600"), + ("1.000000", "0.916900", "0.273100"), + ], +} + + +# --------------------------------------------------------------------------- +# cmap -> emission-mode helpers +# --------------------------------------------------------------------------- + + +def canonical_cmap(cmap: str) -> str: + """Canonicalize a user-facing cmap name (lowercase; ``jet`` -> ``rainbow``). + + Raises + ------ + ValueError + If ``cmap`` is not one of :data:`SUPPORTED_CMAPS`. + """ + key = str(cmap).strip().lower() + key = _CMAP_ALIASES.get(key, key) + valid = {_CMAP_ALIASES.get(c, c) for c in SUPPORTED_CMAPS} + if key not in valid: + supported = ", ".join(sorted(valid)) + raise ValueError(f"Unknown cmap {cmap!r}; supported palettes: {supported}") + return key + + +def cmap_needs_sub(cmap: str) -> bool: + """True if ``cmap`` (canonical) requires an emitted ``gleplot_`` sub. + + ``gray`` (grayscale default) and ``rainbow`` (built-in ``color``) do not. + """ + return canonical_cmap(cmap) not in ("gray", "rainbow") + + +def palette_sub_name(cmap: str) -> Optional[str]: + """GLE subroutine name for ``cmap``'s palette, or ``None`` if none is needed.""" + c = canonical_cmap(cmap) + if not cmap_needs_sub(c): + return None + return f"gleplot_{c}" + + +def palette_call_name(cmap: str) -> str: + """The ``palette$`` value passed to the colorbar sub for ``cmap``. + + ``gray`` -> ``"gray"`` (grayscale branch), ``rainbow`` -> ``"color"`` + (GLE built-in rainbow branch), everything else -> ``"gleplot_"``. + """ + c = canonical_cmap(cmap) + if c == "gray": + return "gray" + if c == "rainbow": + return "color" + return f"gleplot_{c}" + + +# --------------------------------------------------------------------------- +# Palette subroutine text (deterministic, canonical) +# --------------------------------------------------------------------------- + + +def _stops_sub_text(name: str, stops: List[Tuple[str, str, str]]) -> str: + """Build a self-contained ``sub gleplot_ z`` from an RGB stop table. + + Reproduces palettes.gle's linear-interpolation-in-RGB semantics inline: + for ``z`` in [0, 1], position ``p = z * (N-1)`` selects segment + ``k = floor(p)`` and fraction ``f`` within it; each RGB component is + ``stop[k] + (stop[k+1] - stop[k]) * f``. Emitted as disjoint single-line + ``if`` guards (matching gle-library ``color.gle`` style, known to compile), + with the first stop as the default (covers ``z = 0``). + """ + n = len(stops) + seg = n - 1 + lines: List[str] = [] + lines.append(f"sub gleplot_{name} z") + # Clamp z into [0, 1] before the piecewise-linear lookup. GLE's fitz/Akima + # gridding can overshoot the data range, and a normalized z outside [0, 1] + # would otherwise fall through every segment guard below and stick at the + # first stop (the darkest colour) -- rendering as stray dark speckles. With + # the clamp, out-of-range values saturate at the nearest extreme stop, like + # matplotlib's default colormap "over"/"under" -> end-colour behaviour. + lines.append(" if z < 0 then z = 0") + lines.append(" if z > 1 then z = 1") + r0, g0, b0 = stops[0] + lines.append(f" local r = {r0}") + lines.append(f" local g = {g0}") + lines.append(f" local b = {b0}") + for k in range(seg): + rk, gk, bk = stops[k] + rk1, gk1, bk1 = stops[k + 1] + lo = k + cond = f"(z >= {lo}/{seg}) and (z <= {k + 1}/{seg})" + frac = f"(z-{lo}/{seg})*{seg}" + lines.append(f" if {cond} then r = {rk}+({rk1}-{rk})*{frac}") + lines.append(f" if {cond} then g = {gk}+({gk1}-{gk})*{frac}") + lines.append(f" if {cond} then b = {bk}+({bk1}-{bk})*{frac}") + lines.append(" return rgb(r,g,b)") + lines.append("end sub") + return "\n".join(lines) + + +#: The ``coolwarm`` palette, transcribed near-verbatim from gle-library +#: ``include/color.gle`` (``sub palette_blue_white_red``; Modified BSD, +#: (C) 2009 GLE) -- a blue->white->red diverging ramp defined by formula. +_COOLWARM_SUB = "\n".join( + [ + "sub gleplot_coolwarm z", + " ! blue->white->red diverging ramp", + " ! transcribed from gle-library include/color.gle palette_blue_white_red", + " ! clamp z into [0,1] so fitz/Akima overshoot saturates at the end", + " ! colour instead of falling through to the default (see palettes.py)", + " if z < 0 then z = 0", + " if z > 1 then z = 1", + " local r = 0", + " local g = 0", + " local b = 0", + " if (z > 0.25) and (z <= 0.50) then r = (z-0.25)*4", + " if (z > 0.50) and (z <= 0.75) then r = 1", + " if (z > 0.75) then r = 1-(123/255)*4*(z-0.75)", + " if (z > 0.25) and (z <= 0.50) then g = (z-0.25)*4", + " if (z > 0.50) and (z <= 0.75) then g = 1-4*(z-0.5)", + " if (z > 0.75) then g = 0", + " if (z <= 0.25) then b = 132/255+(123/255)*4*z", + " if (z > 0.25) and (z <= 0.50) then b = 1", + " if (z > 0.50) and (z <= 0.75) then b = 1-4*(z-0.5)", + " if (z > 0.75) then b = 0", + " return rgb(r,g,b)", + "end sub", + ] +) + + +def palette_sub_text(cmap: str) -> Optional[str]: + """Canonical ``sub gleplot_ z ... end sub`` text for ``cmap``. + + Returns ``None`` for ``gray``/``rainbow`` (no sub needed). + """ + c = canonical_cmap(cmap) + if not cmap_needs_sub(c): + return None + if c == "coolwarm": + return _COOLWARM_SUB + return _stops_sub_text(c, PALETTE_STOPS[c]) + + +# --------------------------------------------------------------------------- +# Colorbar subroutine (vertical colour range drawn after the graph) +# --------------------------------------------------------------------------- + + +def colorbar_sub_name() -> str: + return "gleplot_colorbar_v" + + +def colorbar_sub_text() -> str: + """Canonical ``sub gleplot_colorbar_v ... end sub`` text. + + A self-contained vertical colour range adapted from gle-library + ``include/color.gle`` ``color_range_vertical`` (Modified BSD, (C) 2009 GLE), + extended with an optional rotated axis ``label$``. Called with GLE + named-argument style (``zmin V zmax V ...``) so its arguments are directly + recoverable from the call line. + """ + return "\n".join( + [ + "sub gleplot_colorbar_v zmin zmax zstep palette$ wd hi format$ label$", + " default zstep 0", + " default wd 0.5", + ' default format "fix 1"', + ' default label ""', + " ! Guard a degenerate (constant-field) range: zmax = zmin would", + " ! divide by zero in the tick-position math below and abort the", + " ! render. Expand to a nominal unit span so the bar still draws.", + " if zmax = zmin then", + " zmax = zmin+1", + " end if", + " if zstep = 0 then", + " zstep = (zmax-zmin)/5", + " end if", + " begin box name gleplot_cbar", + ' if palette$ = "gray" then', + ' colormap "y" 0 1 0 1 1 200 wd hi', + ' else if palette$ = "color" then', + ' colormap "y" 0 1 0 1 1 200 wd hi color', + " else", + ' colormap "y" 0 1 0 1 1 200 wd hi palette palette$', + " end if", + " end box", + " amove pointx(gleplot_cbar.bl) pointy(gleplot_cbar.bl)", + " box wd hi", + " set just lc", + " local zp = zmin", + " while zp <= zmax+(zmax-zmin)/1e6", + " local yy = pointy(gleplot_cbar.bc)+(zp-zmin)/(zmax-zmin)*hi", + " amove pointx(gleplot_cbar.rc) yy", + " rline wd/3 0", + " rmove 0.1 0", + " write format$(zp,format$)", + " zp = zp+zstep", + " next", + ' if label$ <> "" then', + " amove pointx(gleplot_cbar.rc)+1.3 pointy(gleplot_cbar.cc)", + " begin rotate 90", + " set just cc", + " write label$", + " end rotate", + " end if", + "end sub", + ] + ) + + +# --------------------------------------------------------------------------- +# Contour-label subroutine +# --------------------------------------------------------------------------- + + +def contour_labels_sub_name() -> str: + return "gleplot_contour_labels" + + +def contour_labels_sub_text() -> str: + """Canonical ``sub gleplot_contour_labels file$ format$ ... end sub`` text. + + Self-contained copy of gle-library ``include/contour.gle`` + ``sub contour_labels`` (Modified BSD, (C) 2009 GLE): reads ``x y i v`` rows + from a ``-clabels.dat`` file and writes the formatted level value in a + white-filled box at each label position. + """ + return "\n".join( + [ + "sub gleplot_contour_labels file$ format$", + ' default format "fix 1"', + " set just cc hei 0.25 color black", + " fopen file$ f1 read", + " until feof(f1)", + " fread f1 x y i v", + " amove xg(x) yg(y)", + " s$ = format$(v,format$)", + " begin box add 0.05 fill white", + " write s$", + " end box", + " write s$", + " next", + " fclose f1", + "end sub", + ] + ) diff --git a/src/gleplot/parser/recognizer.py b/src/gleplot/parser/recognizer.py index e66c4ce..1ade764 100644 --- a/src/gleplot/parser/recognizer.py +++ b/src/gleplot/parser/recognizer.py @@ -158,6 +158,7 @@ from .tables import ( KEY_POSITIONS_SHORT_TO_LONG, LSTYLE_TO_MATPLOTLIB, + PALETTE_SUB_TO_CMAP, ) from .units import ( capsize_cm_to_pt, @@ -204,6 +205,34 @@ def _words_and_values(stmt: Statement) -> List[Token]: return [t for t in stmt.tokens if t.type is not TokenType.COMMENT] +def _first_statement_of(source_line) -> Statement: + """Build a throwaway :class:`Statement` from a raw block inner line. + + Opaque-block inner lines are stored as bare :class:`SourceLine`s (never + tokenized into statements). The contour/fitz block parsers need their + tokens, so this tokenizes one line's text into a Statement wrapper. + """ + from .lexer import tokenize_line + + return Statement( + tokens=tokenize_line(source_line.text), + raw=source_line.text, + line_no=source_line.line_no, + sub_index=0, + source_line=source_line, + ) + + +def _as_float(value, default: float) -> float: + """Best-effort float from a recovered named-argument value.""" + if value is None: + return default + try: + return float(value) + except (TypeError, ValueError): + return default + + def _string_value(tok: Token) -> str: """Unwrap a STRING token's value: strip quotes, unescape ``\\"``/``\\'``.""" v = tok.value @@ -311,12 +340,34 @@ def __init__(self, text: str, gle_path: Path): self._text_fontsize: Optional[float] = None self._text_color: str = "BLACK" self._text_just: str = "left" + # Contour/heatmap recognition state. + # Line numbers spanned by gleplot_ subroutine definitions + # (palette / colorbar / contour-labels). Dropped everywhere, like the + # metadata block, and regenerated canonically from the model on re-save. + self._gleplot_sub_lines: set = set() + # fitz output ``.z`` file -> {points_file, extent, gridsize, ncontour} + self._fitz_blocks: Dict[str, dict] = {} + # contour ``.z`` file -> {levels} + self._contour_blocks: Dict[str, dict] = {} + # Line numbers of fitz/contour blocks recognized as gleplot-authored + # (consumed into a series), dropped from passthrough. + self._consumed_block_lines: set = set() # -- public driver --------------------------------------------------- def run(self) -> RecognizedFigure: doc = parse_gle_source(self.text) + + # Line ranges of gleplot_ subs (palette/colorbar/clabel), dropped + # like metadata and regenerated canonically from the model on re-save. + self._gleplot_sub_lines = self._scan_gleplot_sub_lines() + + # Structure warnings from within our own generated subs (e.g. an ``end + # if`` the flat parser sees as an unmatched ``end``) are noise -- those + # lines are dropped and regenerated. Suppress them; keep the rest. for w in doc.warnings: + if w.line_no in self._gleplot_sub_lines: + continue self.warnings.append(f"structure: {w}") # 1-based line numbers spanned by the '! gleplot' metadata block; these @@ -325,6 +376,12 @@ def run(self) -> RecognizedFigure: # and re-emit as a stale duplicate block. self._meta_lines = self._metadata_line_numbers() + # Pre-scan fitz/contour blocks and cross-reference them against the + # colormap / cdata references in the graph blocks, so a gleplot-authored + # heatmap/contour can be reconstructed and its blocks dropped from + # passthrough (foreign/unreferenced blocks stay opaque passthrough). + self._prescan_contour_heatmap(doc.nodes) + # Guard: files using GLE programming constructs are not safe to edit. self._check_programmatic_constructs(doc.nodes) @@ -410,6 +467,9 @@ def run(self) -> RecognizedFigure: # Greedily consume deferred text cluster that follows. texts, consumed = self._consume_text_cluster(nodes, i + 1) axes_info["texts"] = texts + # Then consume any post-graph colorbar / contour-label sub + # calls belonging to this axes. + consumed = self._consume_post_graph_calls(nodes, consumed, axes_info) parsed_axes.append(axes_info) i = consumed continue @@ -428,6 +488,16 @@ def run(self) -> RecognizedFigure: i += 1 continue + # fitz/contour blocks consumed into a recognized series (their + # geometry lives on the model; regenerated on re-save). + if ( + isinstance(node, OpaqueBlock) + and node.block_type in ("fitz", "contour") + and node.begin.line_no in self._consumed_block_lines + ): + i += 1 + continue + # Anything else after the graphs -> trailer passthrough. trailer.extend(self._raw_lines(node)) i += 1 @@ -487,6 +557,11 @@ def _check_programmatic_constructs(self, nodes) -> None: for node in nodes: if not isinstance(node, Statement): continue + # gleplot_ subroutine bodies use sub/if/return/else etc., but + # are our own self-contained palettes/colorbar/labels -- not a + # user-authored programmatic file. Don't flag them. + if node.source_line is not None and node.line_no in self._gleplot_sub_lines: + continue kw = node.keyword if kw is not None and kw in self._PROGRAMMATIC_KEYWORDS: self.warnings.append( @@ -526,10 +601,24 @@ def _parse_preamble( fontsize = 12.0 passthrough: List[str] = [] - # Track which lines belong to the metadata block so we drop them. - meta_line_nos = self._meta_lines + # Track which lines belong to the metadata block, or to a gleplot_ + # sub definition, so we drop them (both are regenerated canonically). + meta_line_nos = self._meta_lines | self._gleplot_sub_lines for node in pre_nodes: + if isinstance(node, OpaqueBlock): + # begin box / begin rotate nested inside a gleplot_ sub + # body (e.g. the colorbar sub) -> drop with the sub. + if node.begin.line_no in self._gleplot_sub_lines: + continue + # Consumed fitz/contour blocks: their geometry lives on the + # model, regenerated on re-save; drop the raw block here. + if ( + node.block_type in ("fitz", "contour") + and node.begin.line_no in self._consumed_block_lines + ): + continue + if isinstance(node, BlankOrComment): stmt = node.statement if stmt.line_no in meta_line_nos: @@ -623,6 +712,8 @@ def _parse_graph_block(self, block: GraphBlock, marker_cfg, smooth_flags) -> dic "fills": [], "errorbars": [], "file_series": [], + "heatmaps": [], + "contours": [], "passthrough": [], "series_order": [], # to preserve ordering info if needed # Dataset names (e.g. 'd1') consumed by a 'bar'/'fill' command. @@ -844,6 +935,9 @@ def _dispatch_graph_statement(self, stmt, info, datasets, marker_cfg, smooth_fla if kw == "key": self._parse_key_command(toks, info, stmt) return + if kw == "colormap": + self._parse_colormap(toks, info, stmt) + return # Unrecognized statement inside a graph block -> axes passthrough. info["passthrough"].append(self._stmt_text(stmt)) @@ -1292,6 +1386,18 @@ def _parse_series_command(self, toks, datasets, info, marker_cfg, smooth_flags): return data_file, xcol, ycol = datasets[d_name] + # A ``data "-cdata.dat" dN=c1,c2`` + ``dN line ...`` pair is a + # contour series' generated polyline, not a broken file line series -- + # but only when the prescan actually recognized (and will reconstruct) + # the feeding contour/fitz block. A ``-cdata.dat`` reference with no + # recognized block falls through to ordinary file-series handling so the + # ``data`` command and its display line are preserved verbatim. + if isinstance(data_file, str) and data_file.endswith("-cdata.dat"): + zfile = data_file[: -len("-cdata.dat")] + ".z" + if zfile in self._contour_blocks or zfile in self._fitz_blocks: + self._build_contour_from_cdata(data_file, toks, info) + return + has_line = attrs["has_line"] has_marker = attrs["marker"] is not None is_errorbar = bool(attrs["err_refs"]) or bool(attrs["err_consts"]) @@ -1747,6 +1853,724 @@ def _build_file_series( # -- data loading ---------------------------------------------------- + # -- contour / heatmap recognition ---------------------------------- + + def _scan_gleplot_sub_lines(self) -> set: + """1-based line numbers spanned by ``sub gleplot_`` definitions. + + gleplot emits self-contained palette / colorbar / contour-label subs + (see :mod:`gleplot.palettes`). Their bodies use ``sub``/``if``/``return`` + etc.; they are dropped wherever they appear (like the metadata block) and + regenerated canonically from the model on re-save, so they never leak + into passthrough nor trip the programmatic-construct guard. + """ + nums: set = set() + in_sub = False + for idx, line in enumerate(self.text.splitlines(), start=1): + low = line.strip().lower() + if not in_sub: + if low.startswith("sub gleplot_"): + in_sub = True + nums.add(idx) + continue + nums.add(idx) + if low == "end sub" or low.startswith("end sub "): + in_sub = False + return nums + + def _prescan_contour_heatmap(self, nodes) -> None: + """Parse fitz/contour blocks and decide which are gleplot-authored. + + Builds ``self._fitz_blocks`` (keyed by the ``.z`` file the block + generates) and ``self._contour_blocks`` (keyed by the ``.z`` file the + block reads), then cross-references them against the ``colormap`` / + ``data "*-cdata.dat"`` references inside the graph blocks. A block is + recorded as *consumed* (its lines dropped from passthrough, its geometry + reconstructed onto the model) only when it is actually referenced -- a + foreign or unreferenced ``begin fitz``/``begin contour`` stays opaque + passthrough with no loss. + """ + raw_fitz: Dict[str, Tuple[dict, OpaqueBlock]] = {} + raw_contour: Dict[str, Tuple[dict, OpaqueBlock]] = {} + for node in nodes: + if not isinstance(node, OpaqueBlock): + continue + if node.block_type == "fitz": + info = self._parse_fitz_block(node) + if info is not None: + raw_fitz[info["zfile"]] = (info, node) + elif node.block_type == "contour": + info = self._parse_contour_block(node) + if info is not None: + raw_contour[info["zfile"]] = (info, node) + + colormap_files, cdata_bases = self._referenced_grid_files(nodes) + + # A contour block is ours iff its cdata polyline is drawn in a graph AND + # its data source can actually be reconstructed (scattered points via a + # feeding fitz block, or the grid ``.z`` on disk). If the data can't be + # read the reconstruction would bail to passthrough, so we must NOT drop + # the block here -- keep it opaque and lose nothing. + for zfile, (info, node) in raw_contour.items(): + base = zfile[:-2] if zfile.endswith(".z") else zfile + if base not in cdata_bases: + continue + fitz_pair = raw_fitz.get(zfile) + if fitz_pair is not None: + if self._read_points(fitz_pair[0]["points_file"]) is None: + continue + elif self._read_z_grid(zfile) is None: + continue + self._contour_blocks[zfile] = info + self._mark_block_consumed(node) + + # A fitz block is ours iff its generated .z feeds a recognized colormap + # or a recognized contour block, AND its scattered points are readable. + for zfile, (info, node) in raw_fitz.items(): + if zfile not in colormap_files and zfile not in self._contour_blocks: + continue + if self._read_points(info["points_file"]) is None: + continue + self._fitz_blocks[zfile] = info + self._mark_block_consumed(node) + + def _mark_block_consumed(self, node: OpaqueBlock) -> None: + if node.begin.source_line is not None: + self._consumed_block_lines.add(node.begin.line_no) + + def _referenced_grid_files(self, nodes) -> Tuple[set, set]: + """Scan graph blocks for ``colormap`` files and ``-cdata.dat`` bases. + + Only colormaps that are *structurally recognizable* as gleplot heatmaps + (known/built-in palette, ``.z``/``.gz`` file, no unrecognized options) + contribute their grid file. A foreign colormap (unknown palette, + function expression, extra options) is re-emitted verbatim as + passthrough and must NOT cause the ``begin fitz`` block that generates + its ``.z`` to be dropped -- otherwise the preserved colormap line would + reference a grid nothing produces (content loss + broken output). + """ + colormap_files: set = set() + cdata_bases: set = set() + for node in nodes: + if not isinstance(node, GraphBlock): + continue + for child in node.body: + if not isinstance(child, Statement): + continue + kw = child.keyword + toks = _words_and_values(child) + if kw == "colormap": + zfile = self._colormap_recognizable_zfile(toks) + if zfile is not None: + colormap_files.add(zfile) + elif kw == "data" and len(toks) >= 2: + fname, _ = self._read_filename(toks, 1) + if fname.endswith("-cdata.dat"): + cdata_bases.add(fname[: -len("-cdata.dat")]) + return colormap_files, cdata_bases + + def _colormap_recognizable_zfile(self, toks) -> Optional[str]: + """Return the grid ``.z`` file iff this ``colormap`` is reconstructable. + + Mirrors the accept criteria of :meth:`_parse_colormap` (palette known + or ``color``/grayscale, a ``.z``/``.gz`` file rather than a function + expression, and no unrecognized options) but WITHOUT touching disk -- + data readability is handled separately. Returns ``None`` for any + colormap that :meth:`_parse_colormap` would send to passthrough, so the + prescan never drops the fitz block feeding a foreign colormap. + """ + if len(toks) < 4: + return None + zfile, idx = self._read_filename(toks, 1) + px = _num(toks[idx]) if idx < len(toks) else None + py = _num(toks[idx + 1]) if idx + 1 < len(toks) else None + if px is None or py is None: + return None + i = idx + 2 + m = len(toks) + color = False + palette = None + unknown = False + while i < m: + w = toks[i].value.lower() + if w == "color": + color = True + i += 1 + elif w == "invert": + i += 1 + elif w in ("zmin", "zmax") and i + 1 < m: + _, nxt = _collect_value(toks, i + 1) + i = nxt if nxt > i + 1 else i + 2 + elif w == "palette" and i + 1 < m: + palette = toks[i + 1].value + i += 2 + elif w == "interpolate" and i + 1 < m: + i += 2 + else: + unknown = True + i += 1 + if unknown: + return None + if not color and palette is not None: + if PALETTE_SUB_TO_CMAP.get(palette.lower()) is None: + return None + if not zfile.lower().endswith((".z", ".gz")): + return None + return zfile + + def _parse_fitz_block(self, node: OpaqueBlock) -> Optional[dict]: + """Parse a ``begin fitz`` block matching gleplot's shape, or ``None``. + + Recovers ``points_file``, ``extent`` (x0,x1,y0,y1), ``gridsize`` + (nx,ny from the ``from..to..step`` ranges) and optional ``ncontour``. + ``zfile`` is the generated grid name (points base + ``.z``). + """ + data_file = None + xr = yr = None + ncontour = None + for sl in node.inner_lines: + toks = _words_and_values(_first_statement_of(sl)) + if not toks: + continue + kw = toks[0].value.lower() + if kw == "data" and len(toks) >= 2: + data_file, _ = self._read_filename(toks, 1) + elif kw in ("x", "y") and len(toks) >= 6: + rng = self._parse_from_to_step(toks) + if rng is not None: + if kw == "x": + xr = rng + else: + yr = rng + elif kw == "ncontour" and len(toks) >= 2: + n = _num(toks[1]) + if n is not None: + ncontour = int(n) + if data_file is None or xr is None or yr is None: + return None + x0, x1, xstep = xr + y0, y1, ystep = yr + nx = int(round((x1 - x0) / xstep)) + 1 if xstep else 2 + ny = int(round((y1 - y0) / ystep)) + 1 if ystep else 2 + zfile = ( + data_file[:-4] + ".z" if data_file.endswith(".dat") else data_file + ".z" + ) + return { + "points_file": data_file, + "zfile": zfile, + "extent": [x0, x1, y0, y1], + "gridsize": [nx, ny], + "ncontour": ncontour, + } + + def _parse_contour_block(self, node: OpaqueBlock) -> Optional[dict]: + """Parse a ``begin contour`` block matching gleplot's shape, or ``None``. + + Recovers the ``.z`` file it reads and the explicit ``values`` list + (``None`` when a bare block using GLE's default 10 levels). + """ + zfile = None + levels = None + for sl in node.inner_lines: + toks = _words_and_values(_first_statement_of(sl)) + if not toks: + continue + kw = toks[0].value.lower() + if kw == "data" and len(toks) >= 2: + zfile, _ = self._read_filename(toks, 1) + elif kw == "values": + # Only the explicit ``values v1 v2 ...`` form is modeled; + # ``values from a to b step s`` stays a foreign block. + if len(toks) >= 2 and toks[1].value.lower() == "from": + return None + vals: List[float] = [] + k = 1 + while k < len(toks): + v, nxt = _collect_value(toks, k) + if v is None: + k += 1 + continue + vals.append(v) + k = nxt + if vals: + levels = vals + if zfile is None: + return None + return {"zfile": zfile, "levels": levels} + + @staticmethod + def _parse_from_to_step(toks) -> Optional[Tuple[float, float, float]]: + """Parse `` from A to B step C`` -> (A, B, C).""" + words = {t.value.lower(): idx for idx, t in enumerate(toks)} + try: + fi, ti, si = words["from"], words["to"], words["step"] + except KeyError: + return None + a = eval_gle_number(toks[fi + 1 : ti]) + b = eval_gle_number(toks[ti + 1 : si]) + c = eval_gle_number(toks[si + 1 :]) + if a is None or b is None or c is None: + return None + return (a, b, c) + + def _parse_colormap(self, toks, info, stmt) -> None: + """Parse a ``colormap`` statement into a heatmap series (or passthrough).""" + if len(toks) < 4: + info["passthrough"].append(self._stmt_text(stmt)) + return + zfile, idx = self._read_filename(toks, 1) + px = _num(toks[idx]) if idx < len(toks) else None + py = _num(toks[idx + 1]) if idx + 1 < len(toks) else None + if px is None or py is None: + info["passthrough"].append(self._stmt_text(stmt)) + return + i = idx + 2 + m = len(toks) + color = invert = False + vmin = vmax = None + palette = None + interpolation = "bicubic" + unknown = False + while i < m: + w = toks[i].value.lower() + if w == "color": + color = True + i += 1 + elif w == "invert": + invert = True + i += 1 + elif w == "zmin" and i + 1 < m: + v, nxt = _collect_value(toks, i + 1) + vmin = v + i = nxt if v is not None else i + 2 + elif w == "zmax" and i + 1 < m: + v, nxt = _collect_value(toks, i + 1) + vmax = v + i = nxt if v is not None else i + 2 + elif w == "palette" and i + 1 < m: + palette = toks[i + 1].value + i += 2 + elif w == "interpolate" and i + 1 < m: + interpolation = toks[i + 1].value.lower() + i += 2 + else: + unknown = True + i += 1 + + # cmap resolution. + if color: + cmap = "rainbow" + elif palette is not None: + cmap = PALETTE_SUB_TO_CMAP.get(palette.lower()) + if cmap is None: + # Foreign / unknown palette sub -> keep the whole colormap raw. + info["passthrough"].append(self._stmt_text(stmt)) + self.warnings.append( + f"structure: colormap uses unknown palette {palette!r}; " + "kept as raw GLE, not editable" + ) + return + else: + cmap = "gray" + + if unknown: + info["passthrough"].append(self._stmt_text(stmt)) + self.warnings.append( + "structure: colormap has unrecognized options; kept as raw GLE" + ) + return + + # A colormap of a FUNCTION expression (not a .z/.gz file) can't be + # modeled -> passthrough. + if not zfile.lower().endswith((".z", ".gz")): + info["passthrough"].append(self._stmt_text(stmt)) + self.warnings.append( + "structure: colormap of a function expression; kept as raw GLE" + ) + return + + fitz = self._fitz_blocks.get(zfile) + if fitz is not None: + pts = self._read_points(fitz["points_file"]) + if pts is None: + info["passthrough"].append(self._stmt_text(stmt)) + self.warnings.append( + f"data: could not read scattered points {fitz['points_file']!r} " + "for heatmap; kept colormap as raw GLE" + ) + return + hm = { + "type": "heatmap", + "source": "points", + "z": None, + "x": pts[0], + "y": pts[1], + "zpts": pts[2], + "extent": list(fitz["extent"]), + "origin": "lower", + "cmap": cmap, + "vmin": vmin, + "vmax": vmax, + "interpolation": "nearest" if interpolation == "nearest" else "bicubic", + "pixels": [int(px), int(py)], + "invert": invert, + "gridsize": list(fitz["gridsize"]), + "ncontour": None, + "label": None, + "data_file": fitz["points_file"], + "colorbar": None, + } + else: + grid = self._read_z_grid(zfile) + if grid is None: + info["passthrough"].append(self._stmt_text(stmt)) + self.warnings.append( + f"data: could not read grid {zfile!r} for heatmap; " + "kept colormap as raw GLE" + ) + return + z, extent = grid + hm = { + "type": "heatmap", + "source": "grid", + "z": z, + "x": None, + "y": None, + "zpts": None, + "extent": extent, + "origin": "lower", + "cmap": cmap, + "vmin": vmin, + "vmax": vmax, + "interpolation": "nearest" if interpolation == "nearest" else "bicubic", + "pixels": [int(px), int(py)], + "invert": invert, + "gridsize": None, + "ncontour": None, + "label": None, + "data_file": zfile, + "colorbar": None, + } + info["heatmaps"].append(hm) + + def _build_contour_from_cdata(self, cdata_file, toks, info) -> None: + """Build a contour series from its ``dN line`` and the fitz/contour blocks.""" + base = cdata_file[: -len("-cdata.dat")] + zfile = base + ".z" + attrs = self._scan_series_attrs(toks[1:]) + color = attrs["color"] or "BLACK" + linewidth = ( + linewidth_cm_to_pt(attrs["lwidth"]) if attrs["lwidth"] is not None else 1.0 + ) + lstyle = attrs["lstyle"] # int or None (None = solid) + + cblock = self._contour_blocks.get(zfile, {}) + levels = cblock.get("levels") + + fitz = self._fitz_blocks.get(zfile) + if fitz is not None: + pts = self._read_points(fitz["points_file"]) + if pts is None: + info["passthrough"].append(" " + " ".join(t.value for t in toks)) + self.warnings.append( + f"data: could not read scattered points {fitz['points_file']!r} " + "for contour; kept line as raw GLE" + ) + return + ct = { + "type": "contour", + "source": "points", + "z": None, + "x": pts[0], + "y": pts[1], + "zpts": pts[2], + "extent": list(fitz["extent"]), + "levels": levels, + "color": color, + "linewidth": linewidth, + "linestyle": lstyle, + "clabel": False, + "clabel_fmt": "fix 1", + "gridsize": list(fitz["gridsize"]), + "ncontour": fitz["ncontour"], + "label": None, + "data_file": fitz["points_file"], + } + else: + grid = self._read_z_grid(zfile) + if grid is None: + info["passthrough"].append(" " + " ".join(t.value for t in toks)) + self.warnings.append( + f"data: could not read grid {zfile!r} for contour; " + "kept line as raw GLE" + ) + return + z, extent = grid + ct = { + "type": "contour", + "source": "grid", + "z": z, + "x": None, + "y": None, + "zpts": None, + "extent": extent, + "levels": levels, + "color": color, + "linewidth": linewidth, + "linestyle": lstyle, + "clabel": False, + "clabel_fmt": "fix 1", + "gridsize": None, + "ncontour": None, + "label": None, + "data_file": zfile, + } + info["contours"].append(ct) + + def _read_z_grid(self, filename) -> Optional[Tuple[np.ndarray, List[float]]]: + """Read a ``.z`` grid sidecar -> (z ndarray (ny,nx), [x0,x1,y0,y1]). + + Returns ``None`` when the file cannot be resolved/parsed. The array is + returned y-increasing (row 0 = ymin), matching GLE's ``.z`` convention; + the recovered heatmap/contour therefore always uses ``origin='lower'`` + (a documented, rendering-neutral normalization). + """ + text = self._read_sidecar_text(filename) + if text is None: + return None + header = None + values: List[float] = [] + for raw in text.splitlines(): + s = raw.strip() + if not s: + continue + if s.startswith("!"): + if header is None: + header = s + continue + for tok in s.replace(",", " ").split(): + try: + values.append(float(tok)) + except ValueError: + return None + if header is None: + return None + meta = self._parse_z_header(header) + if meta is None: + return None + nx, ny, x0, x1, y0, y1 = meta + if len(values) != nx * ny: + return None + z = np.asarray(values, dtype=float).reshape(ny, nx) + return z, [x0, x1, y0, y1] + + @staticmethod + def _parse_z_header(header: str): + """Parse ``! nx N ny N xmin V xmax V ymin V ymax V`` -> tuple or None.""" + parts = header.lstrip("!").replace(",", " ").split() + kv = {} + i = 0 + while i + 1 < len(parts): + key = parts[i].lower() + try: + kv[key] = float(parts[i + 1]) + except ValueError: + return None + i += 2 + try: + nx = int(kv["nx"]) + ny = int(kv["ny"]) + x0 = kv["xmin"] + x1 = kv["xmax"] + y0 = kv["ymin"] + y1 = kv["ymax"] + except (KeyError, ValueError): + return None + return nx, ny, x0, x1, y0, y1 + + def _read_points(self, filename): + """Read a scattered ``x y z`` triples sidecar -> (x, y, z) ndarrays.""" + text = self._read_sidecar_text(filename) + if text is None: + return None + xs, ys, zs = [], [], [] + for raw in text.splitlines(): + s = raw.strip() + if not s or s.startswith("!"): + continue + parts = s.split() + if len(parts) < 3: + return None + try: + xs.append(float(parts[0])) + ys.append(float(parts[1])) + zs.append(float(parts[2])) + except ValueError: + return None + if not xs: + return None + return ( + np.asarray(xs, dtype=float), + np.asarray(ys, dtype=float), + np.asarray(zs, dtype=float), + ) + + def _read_sidecar_text(self, filename) -> Optional[str]: + """Read a raw sidecar file's text, resolving relative to the .gle path.""" + p = Path(filename) + if not p.is_absolute(): + p = self.gle_path.parent / p + try: + return p.read_text(encoding="utf-8", errors="replace") + except OSError: + return None + + # -- post-graph colorbar / contour-label calls ---------------------- + + def _consume_post_graph_calls(self, nodes, start, axes_info) -> int: + """Consume ``gleplot_colorbar_v`` / ``gleplot_contour_labels`` calls. + + Each call is preceded by its own ``amove`` (``xg(xgmax)+S yg(ygmin)`` for + the colorbar, ``0 0`` for the labels). Attaches the recovered colorbar + to this axes' heatmap and sets ``clabel`` on the matching contour. + Returns the index of the first node not consumed. + """ + i = start + n = len(nodes) + while i < n: + consumed = self._try_one_post_call(nodes, i, axes_info) + if consumed is None: + break + i = consumed + return i + + def _try_one_post_call(self, nodes, i, axes_info) -> Optional[int]: + # optional leading amove, then the sub call. + amove_sep = None + j = i + st = self._as_statement(nodes[j]) if j < len(nodes) else None + if st is not None and st.keyword == "amove": + amove_sep = self._amove_colorbar_sep(_words_and_values(st)) + j += 1 + st = self._as_statement(nodes[j]) if j < len(nodes) else None + if st is None: + return None + kw = st.keyword + toks = _words_and_values(st) + if kw == "gleplot_colorbar_v": + self._apply_colorbar_call(toks, amove_sep, axes_info) + return j + 1 + if kw == "gleplot_contour_labels": + self._apply_clabel_call(toks, axes_info) + return j + 1 + return None + + @staticmethod + def _amove_colorbar_sep(toks) -> Optional[float]: + """Recover ``S`` from ``amove xg(xgmax)+S yg(ygmin)`` (else None).""" + text = " ".join(t.value for t in toks) + m = re.search(r"xgmax\s*\)\s*\+\s*([0-9.eE+-]+)", text) + if m: + try: + return float(m.group(1)) + except ValueError: + return None + return None + + def _apply_colorbar_call(self, toks, sep, axes_info) -> None: + args = self._named_args(toks[1:]) + heatmaps = axes_info.get("heatmaps") or [] + if not heatmaps: + return + hm = heatmaps[0] + cb = { + "label": args.get("label") or None, + "format": args.get("format") or "fix 1", + "width": _as_float(args.get("wd"), 0.5), + "sep": sep if sep is not None else 0.3, + "zmin": _as_float(args.get("zmin"), 0.0), + "zmax": _as_float(args.get("zmax"), 1.0), + "zstep": _as_float(args.get("zstep"), 0.0), + } + hm["colorbar"] = cb + + def _apply_clabel_call(self, toks, axes_info) -> None: + args = self._named_args(toks[1:]) + fname = args.get("file") + fmt = args.get("format") or "fix 1" + contours = axes_info.get("contours") or [] + if not fname or not fname.endswith("-clabels.dat"): + if contours: + contours[0]["clabel"] = True + contours[0]["clabel_fmt"] = fmt + return + base = fname[: -len("-clabels.dat")] + for ct in contours: + df = ct["data_file"] + ct_base = ( + df[:-4] + if df.endswith(".dat") + else (df[:-2] if df.endswith(".z") else df) + ) + if ct_base == base: + ct["clabel"] = True + ct["clabel_fmt"] = fmt + return + if contours: + contours[0]["clabel"] = True + contours[0]["clabel_fmt"] = fmt + + #: Recognized colorbar/clabel call argument names (used to delimit the + #: unmodeled ``hi `` height argument). + _CALL_ARG_NAMES = frozenset( + {"zmin", "zmax", "zstep", "palette", "wd", "hi", "format", "label", "file"} + ) + + def _named_args(self, toks) -> dict: + """Parse GLE named-argument call tokens ``name value name value ...``. + + String values (quoted) are unwrapped; numeric values are collected with + the expression-tolerant :func:`_collect_value` (so signed numbers like + ``-1`` are read whole). The ``hi yg(...)-yg(...)`` height argument is a + multi-token expression that is not modeled (the colorbar always spans + the graph height); it is skipped up to the next recognized arg name. + """ + args: dict = {} + i = 0 + m = len(toks) + while i < m: + name = toks[i].value.lower() + i += 1 + if i >= m: + break + if name == "hi": + while i < m and toks[i].value.lower() not in self._CALL_ARG_NAMES: + i += 1 + continue + val_tok = toks[i] + if val_tok.type is TokenType.STRING: + args[name] = _string_value(val_tok) + i += 1 + continue + if name in ("palette", "format", "label", "file"): + # A word/filename value (single token, or an unquoted + # hyphenated filename run). + if name == "file": + fname, nxt = self._read_filename(toks, i) + args[name] = fname + i = nxt + else: + args[name] = val_tok.value + i += 1 + continue + v, nxt = _collect_value(toks, i) + if v is not None: + args[name] = v + i = nxt + else: + i += 1 + return args + def _resolve_table(self, data_file): if data_file in self._table_cache: return self._table_cache[data_file] @@ -2095,6 +2919,8 @@ def _populate_axes(self, ax, info): ax.errorbars = info["errorbars"] ax.file_series = info["file_series"] ax.texts = info["texts"] + ax.heatmaps = info["heatmaps"] + ax.contours = info["contours"] ax.passthrough = info["passthrough"] # A series whose sidecar had no real header row (hand-written .dat, @@ -2190,19 +3016,54 @@ def _apply_smooth(self, fig, graph_cfg, smooth_flags): def _finalize_data_state(self, fig): """Set data_prefix / counters / used-files so re-save reuses names.""" used = set() + sidecars = set() for ax in fig.axes_list: for s in ax.lines + ax.scatters + ax.bars + ax.fills + ax.errorbars: df = s.get("data_file") if df: used.add(df) - fig._used_data_files = set(used) - - # Derive prefix from the sidecar naming convention: _.dat. + for s in ax.heatmaps + ax.contours: + df = s.get("data_file") + if df: + sidecars.add(df) + # Every reserved file (columnar sidecars + heatmap/contour/points raw + # sidecars) is tracked so collision avoidance and to_dict round-trip see + # the full set. + fig._used_data_files = set(used) | set(sidecars) + + # Derive prefix from the columnar sidecar convention _.dat + # first; fall back to the heatmap/contour/points sidecar convention + # _. when there are no columnar series. prefix, max_idx = self._derive_prefix(used) - if prefix is not None: + if prefix is None: + prefix, _ = self._derive_prefix_from_sidecars(sidecars) + if prefix is not None: + fig.data_prefix = prefix + elif prefix is not None: fig.data_prefix = prefix fig._local_data_counter = max_idx + 1 + @staticmethod + def _derive_prefix_from_sidecars(names) -> Tuple[Optional[str], int]: + """Return the shared prefix of ``_.`` sidecars. + + Recognizes ``heatmap``/``contour``/``points`` kinds with ``.z``/``.dat`` + extensions. Returns ``(None, -1)`` when there is no single confident + prefix (or it is the default ``data``). + """ + pat = re.compile(r"^(.*)_(?:heatmap|contour|points)\d+\.(?:z|dat)$") + prefixes = set() + for name in names: + mobj = pat.match(name) + if mobj: + prefixes.add(mobj.group(1)) + if len(prefixes) == 1: + pfx = next(iter(prefixes)) + if pfx == "data": + return None, -1 + return pfx, 0 + return None, -1 + @staticmethod def _derive_prefix(used) -> Tuple[Optional[str], int]: """Return (prefix, max_index) from generated sidecar names, or (None,-1). diff --git a/src/gleplot/parser/tables.py b/src/gleplot/parser/tables.py index 75a87f1..bab1fc7 100644 --- a/src/gleplot/parser/tables.py +++ b/src/gleplot/parser/tables.py @@ -41,6 +41,8 @@ "MATPLOTLIB_TO_LSTYLE", "KEY_POSITIONS_LONG_TO_SHORT", "KEY_POSITIONS_SHORT_TO_LONG", + "PALETTE_SUB_TO_CMAP", + "CMAP_TO_PALETTE_SUB", ] @@ -385,6 +387,30 @@ def _build_gle_marker_to_matplotlib() -> Dict[str, str]: "center": "cc", } +# --------------------------------------------------------------------------- +# Palette subroutine names (contour/heatmap support) +# --------------------------------------------------------------------------- +# +# gleplot emits self-contained palette subroutines named ``gleplot_`` +# (see :mod:`gleplot.palettes`). The recognizer maps a ``colormap ... palette +# gleplot_`` clause back to the canonical cmap name via this table (and +# treats any OTHER palette sub name as an unknown/foreign palette -> passthrough +# with a warning). Provenance: the ```` keys mirror +# ``gleplot.palettes.PALETTE_STOPS`` plus ``coolwarm`` -- the palettes that +# require an emitted sub. ``gray``/``rainbow`` need no sub and never appear as a +# ``palette`` clause (grayscale = no clause; rainbow = the ``color`` switch). +PALETTE_SUB_TO_CMAP: Dict[str, str] = { + "gleplot_viridis": "viridis", + "gleplot_magma": "magma", + "gleplot_inferno": "inferno", + "gleplot_plasma": "plasma", + "gleplot_cividis": "cividis", + "gleplot_coolwarm": "coolwarm", +} + +CMAP_TO_PALETTE_SUB: Dict[str, str] = {v: k for k, v in PALETTE_SUB_TO_CMAP.items()} + + KEY_POSITIONS_SHORT_TO_LONG: Dict[str, str] = { v: k for k, v in KEY_POSITIONS_LONG_TO_SHORT.items() } diff --git a/src/gleplot/writer.py b/src/gleplot/writer.py index f28fecd..42710ce 100644 --- a/src/gleplot/writer.py +++ b/src/gleplot/writer.py @@ -26,6 +26,17 @@ def _format_data_filename(name: str) -> str: return name +def _quote_filename(name: str) -> str: + """Always quote a filename for GLE. + + Used for the contour/heatmap grid and generated files: their names contain + hyphens (``-cdata.dat``/``-clabels.dat``) which GLE would otherwise parse + as subtraction ("left hand side contains unquoted string"). Quoting is + always valid, so it is applied unconditionally in these contexts. + """ + return '"' + str(name).replace('"', '\\"') + '"' + + class GLEWriter: """Writer for GLE script files. @@ -65,6 +76,11 @@ def __init__( self.lines_gle = [] # GLE script lines self.data_files = {} # {filename: data_content} + # Raw-content sidecars (heatmap/contour ``.z`` grids and scattered + # ``points.dat`` triples). They are written like any data file but are + # NOT columnar imports, so they are excluded from the ``import-data`` + # metadata list (see Figure._build_metadata_dict). + self.raw_sidecars: set = set() self.dataset_index = ( 1 # Counter for unique dataset names (d1, d2, d3, ...) - GLE is 1-indexed ) @@ -229,6 +245,21 @@ def add_graph_size( # Auto-size and center axes within graph box self.lines_gle.append(" scale auto") + def add_graph_box_size(self, width_cm: float, height_cm: float): + """Emit a bare graph ``size W H`` (no ``scale``) for the single-plot path. + + Unlike :meth:`add_graph_size` (which, in ``auto`` mode, emits + ``scale auto`` and lets GLE fill the whole page), this pins the graph + box to ``width_cm`` x ``height_cm`` while still letting GLE auto-inset + the axis labels/ticks inside that box. Used only when a colorbar needs + reserved space to the right of an otherwise single (1,1,1) graph, so + the bar + ticks + rotated label are not clipped off the page. + """ + self.lines_gle.append( + f" size {self._format_number(width_cm)} " + f"{self._format_number(height_cm)}" + ) + def add_axes( self, xlabel: Optional[str] = None, @@ -1152,6 +1183,191 @@ def write_files(self, output_dir: str, base_filename: str = "plot"): return files + # -- contour / heatmap emission ------------------------------------- + + def add_z_sidecar(self, filename: str, z: np.ndarray, extent, origin: str): + """Write a raw ``.z`` grid sidecar. + + Header ``! nx ny xmin xmax ymin ymax `` + followed by ``ny`` lines of ``nx`` values, ROW ymin FIRST (GLE's ``.z`` + format is y-increasing). For ``origin='upper'`` the array rows are + flipped so row 0 (the top) is written last. Numbers use the writer's + canonical formatter, single-space separated -- deterministic, so the + sidecar is fixed-point safe. + """ + z = np.asarray(z, dtype=float) + ny, nx = z.shape + x0, x1, y0, y1 = (self._format_number(v) for v in extent) + header = f"! nx {nx} ny {ny} xmin {x0} xmax {x1} ymin {y0} ymax {y1}" + rows = z[::-1] if origin == "upper" else z + lines = [header] + for row in rows: + lines.append(" ".join(self._format_number(v) for v in row)) + content = "\n".join(lines) + "\n" + self.data_files[filename] = content + self.raw_sidecars.add(filename) + + def add_points_sidecar( + self, filename: str, x: np.ndarray, y: np.ndarray, z: np.ndarray + ): + """Write a raw scattered ``x y z`` triples sidecar (no header). + + GLE's ``fitz`` reads raw whitespace-separated triples, one per line, in + the given order (no sorting). Deterministic / fixed-point safe. + """ + x = np.asarray(x, dtype=float) + y = np.asarray(y, dtype=float) + z = np.asarray(z, dtype=float) + lines = [] + for xi, yi, zi in zip(x, y, z): + lines.append( + f"{self._format_number(xi)} {self._format_number(yi)} " + f"{self._format_number(zi)}" + ) + content = "\n".join(lines) + "\n" + self.data_files[filename] = content + self.raw_sidecars.add(filename) + + def add_sub_defs(self, sub_texts: List[str]): + """Append self-contained subroutine definitions to the script. + + Each entry is a full multi-line ``sub ... end sub`` block. A blank + line separates consecutive subs (and follows the last), matching the + script's readable block spacing. + """ + for text in sub_texts: + self.lines_gle.extend(text.split("\n")) + self.lines_gle.append("") + + def add_fitz_block(self, points_file: str, extent, gridsize, ncontour): + """Emit a ``begin fitz`` .. ``end fitz`` block (before ``begin graph``). + + ``step = (hi - lo) / (n - 1)`` for each axis so the grid spans the + extent inclusively at ``gridsize`` nodes. + """ + x0, x1, y0, y1 = extent + nx, ny = gridsize + xstep = (x1 - x0) / (nx - 1) + ystep = (y1 - y0) / (ny - 1) + self.lines_gle.append("begin fitz") + self.lines_gle.append(f" data {_quote_filename(points_file)}") + self.lines_gle.append( + f" x from {self._format_number(x0)} to {self._format_number(x1)} " + f"step {self._format_number(xstep)}" + ) + self.lines_gle.append( + f" y from {self._format_number(y0)} to {self._format_number(y1)} " + f"step {self._format_number(ystep)}" + ) + if ncontour is not None: + self.lines_gle.append(f" ncontour {int(ncontour)}") + self.lines_gle.append("end fitz") + + def add_contour_block(self, z_file: str, levels): + """Emit a ``begin contour`` .. ``end contour`` block. + + ``levels`` is ``None`` (GLE default 10 levels) or an explicit list of + z-values emitted as ``values v1 v2 ...``. + """ + self.lines_gle.append("begin contour") + self.lines_gle.append(f" data {_quote_filename(z_file)}") + if levels: + vals = " ".join(self._format_number(v) for v in levels) + self.lines_gle.append(f" values {vals}") + self.lines_gle.append("end contour") + + def add_colormap( + self, + z_file: str, + pixels, + cmap_mode, + vmin, + vmax, + invert: bool, + interpolation: str, + ): + """Emit the ``colormap`` statement inside a graph block. + + Clause order (fixed): file, px, py, [color], [invert], [zmin v], + [zmax v], [palette name], [interpolate mode]. ``cmap_mode`` is + ``('color', None)`` for rainbow, ``('palette', 'gleplot_x')`` for a + named palette, or ``('gray', None)`` for grayscale (no clause). + """ + px, py = pixels + cmd = f" colormap {_quote_filename(z_file)} {int(px)} {int(py)}" + mode, name = cmap_mode + if mode == "color": + cmd += " color" + if invert: + cmd += " invert" + if vmin is not None: + cmd += f" zmin {self._format_number(vmin)}" + if vmax is not None: + cmd += f" zmax {self._format_number(vmax)}" + if mode == "palette": + cmd += f" palette {name}" + if interpolation == "nearest": + cmd += " interpolate nearest" + self.lines_gle.append(cmd) + + def add_contour_line( + self, cdata_file: str, color: str, linewidth: float, lstyle: Optional[int] + ): + """Emit the ``data``/``dN line`` pair that draws a contour's polylines. + + ``cdata_file`` is the ``-cdata.dat`` file GLE generates from the + contour block; it is plotted as a ``line`` dataset. + """ + d_name = f"d{self.dataset_index}" + self.dataset_index += 1 + self.lines_gle.append(f" data {_quote_filename(cdata_file)} {d_name}=c1,c2") + if linewidth == 0 or linewidth == 1: + gle_lwidth = linewidth_pt_to_cm(self.style.default_linewidth) + else: + gle_lwidth = linewidth_pt_to_cm(linewidth) + cmd = ( + f" {d_name} line color {color} " + f"lwidth {self._format_number(gle_lwidth)}" + ) + if lstyle is not None: + cmd += f" lstyle {int(lstyle)}" + self.lines_gle.append(cmd) + + def add_colorbar_call( + self, + sep: float, + zmin: float, + zmax: float, + zstep: float, + palette_call: str, + width: float, + fmt: str, + label: Optional[str], + ): + """Emit the post-graph vertical-colorbar sub call. + + Positioned at ``xg(xgmax)+sep yg(ygmin)`` with height spanning the + graph. Named-argument call style (``zmin V zmax V ...``); note GLE + matches named args by the parameter name WITHOUT the ``$`` suffix. + """ + self.lines_gle.append(f"amove xg(xgmax)+{self._format_number(sep)} yg(ygmin)") + lbl = self._escape_gle_string(label or "") + self.lines_gle.append( + f"gleplot_colorbar_v zmin {self._format_number(zmin)} " + f"zmax {self._format_number(zmax)} " + f"zstep {self._format_number(zstep)} " + f'palette "{palette_call}" wd {self._format_number(width)} ' + f'hi yg(ygmax)-yg(ygmin) format "{fmt}" label "{lbl}"' + ) + + def add_clabel_call(self, clabels_file: str, fmt: str): + """Emit the post-graph contour-label sub call.""" + self.lines_gle.append("amove 0 0") + self.lines_gle.append( + f"gleplot_contour_labels file {_quote_filename(clabels_file)} " + f'format "{fmt}"' + ) + @staticmethod def _format_number(val: float, precision: int = 6) -> str: """Format number for GLE output.""" diff --git a/tests/gui/test_data_panel.py b/tests/gui/test_data_panel.py index 023ebd5..401ad48 100644 --- a/tests/gui/test_data_panel.py +++ b/tests/gui/test_data_panel.py @@ -387,3 +387,122 @@ def test_populate_runs_on_figure_replaced(self, qapp, tmp_path): # Simulate the open flow: installing a figure emits figure_replaced. doc.figure_replaced.emit() assert panel.file_list.count() == 1 + + +# ---------------------------------------------------------------------- +# Scattered (x, y, z) heatmap / contour series (Phase B) +# ---------------------------------------------------------------------- +def _write_xyz_csv(tmp_path: Path, name: str = "xyz.csv") -> Path: + p = tmp_path / name + p.write_text( + "x,y,z\n0,0,1\n1,0,2\n0,1,3\n1,1,4\n0.5,0.5,2.5\n", + ) + return p + + +class TestScatteredHeatmapContour: + def test_plot_types_include_heatmap_and_contour(self, qapp, document): + panel = DataPanel(document) + items = [ + panel.plot_type_combo.itemText(i) + for i in range(panel.plot_type_combo.count()) + ] + assert "Heatmap (scattered x,y,z)" in items + assert "Contour (scattered x,y,z)" in items + + def test_z_row_hidden_by_default_visible_for_xyz(self, qapp, document, tmp_path): + panel = DataPanel(document) + panel.load_file(str(_write_xyz_csv(tmp_path))) + # Default plot type is "Line": Z hidden, Y-error visible, mode enabled. + # (The panel is never shown top-level in an offscreen test, so + # isVisible() is always False; isHidden() reflects the explicit + # setVisible state we drive.) + assert panel.z_combo.isHidden() is True + assert panel.yerr_combo.isHidden() is False + assert panel.mode_combo.isEnabled() is True + + panel.plot_type_combo.setCurrentText("Heatmap (scattered x,y,z)") + assert panel.z_combo.isHidden() is False + assert panel.yerr_combo.isHidden() is True + # fitz gridding needs the raw triples we write, so import mode only. + assert panel.mode_combo.isEnabled() is False + assert panel.mode_combo.currentText() == "Import data" + + def test_z_combo_populated(self, qapp, document, tmp_path): + panel = DataPanel(document) + panel.load_file(str(_write_xyz_csv(tmp_path))) + z_items = [panel.z_combo.itemText(i) for i in range(panel.z_combo.count())] + assert z_items == ["x", "y", "z"] + # Defaults to the third numeric column. + assert panel.z_combo.currentText() == "z" + + def test_add_tripcolor_series(self, qapp, document, figure, tmp_path): + panel = DataPanel(document) + panel.load_file(str(_write_xyz_csv(tmp_path))) + panel.plot_type_combo.setCurrentText("Heatmap (scattered x,y,z)") + panel.label_edit.setText("field") + + panel.add_series() + + ax = figure.gca() + assert len(ax.heatmaps) == 1 + hm = ax.heatmaps[0] + assert hm["source"] == "points" + assert hm["label"] == "field" + assert list(hm["zpts"]) == [1.0, 2.0, 3.0, 4.0, 2.5] + assert document.notify_changed_count == 1 + + def test_add_tricontour_series(self, qapp, document, figure, tmp_path): + panel = DataPanel(document) + panel.load_file(str(_write_xyz_csv(tmp_path))) + panel.plot_type_combo.setCurrentText("Contour (scattered x,y,z)") + panel.label_edit.setText("levels") + + panel.add_series() + + ax = figure.gca() + assert len(ax.contours) == 1 + ct = ax.contours[0] + assert ct["source"] == "points" + assert ct["label"] == "levels" + assert len(ax.heatmaps) == 0 + assert document.notify_changed_count == 1 + + def test_one_heatmap_per_axes_guard( + self, qapp, document, figure, tmp_path, monkeypatch + ): + from gleplot.gui.data import panel as panel_mod + + seen = [] + monkeypatch.setattr( + panel_mod.QMessageBox, + "information", + lambda *a, **k: seen.append(a), + ) + + panel = DataPanel(document) + panel.load_file(str(_write_xyz_csv(tmp_path))) + panel.plot_type_combo.setCurrentText("Heatmap (scattered x,y,z)") + panel.add_series() + assert len(figure.gca().heatmaps) == 1 + count_after_first = document.notify_changed_count + + # A second heatmap on the same axes is refused with a message, not a + # crash, and does not mutate the figure. + panel.add_series() + assert len(figure.gca().heatmaps) == 1 + assert document.notify_changed_count == count_after_first + assert len(seen) == 1 + + def test_tricontour_does_not_trip_heatmap_guard( + self, qapp, document, figure, tmp_path + ): + panel = DataPanel(document) + panel.load_file(str(_write_xyz_csv(tmp_path))) + # A heatmap plus a contour on the same axes is allowed (separate lists). + panel.plot_type_combo.setCurrentText("Heatmap (scattered x,y,z)") + panel.add_series() + panel.plot_type_combo.setCurrentText("Contour (scattered x,y,z)") + panel.add_series() + assert len(figure.gca().heatmaps) == 1 + assert len(figure.gca().contours) == 1 diff --git a/tests/gui/test_panels.py b/tests/gui/test_panels.py index fbbc7d7..5edc6b7 100644 --- a/tests/gui/test_panels.py +++ b/tests/gui/test_panels.py @@ -152,6 +152,22 @@ def test_write_back_title(self, document): assert ax.title_text == "Changed title" assert document.notify_count == before + 1 + def test_ylabel_mathtext_stored_translated(self, document): + # A label typed with $...$ mathtext is translated to GLE markup on store. + panel = AxesPanel(document) + panel.ylabel_edit.setText(r"$\chi$ (emu/mol)") + panel.ylabel_edit.editingFinished.emit() + ax = document.figure.gca() + assert ax.ylabel_text == r"\chi{} (emu/mol)" + + def test_title_mathtext_roundtrip_is_idempotent(self, document): + # Re-editing an already-translated (GLE-markup) label is a no-op. + panel = AxesPanel(document) + panel.title_edit.setText(r"\chi{} (emu/mol)") + panel.title_edit.editingFinished.emit() + ax = document.figure.gca() + assert ax.title_text == r"\chi{} (emu/mol)" + def test_write_back_legend_loc(self, document): panel = AxesPanel(document) before = document.notify_count @@ -310,6 +326,15 @@ def test_write_back_linewidth(self, document): assert ax.lines[0]["linewidth"] == pytest.approx(4.0) assert document.notify_count == before + 1 + def test_write_back_label_mathtext_translated(self, document): + # A legend label typed with $...$ mathtext is stored as GLE markup. + panel = SeriesPanel(document) + panel.series_list.setCurrentRow(0) + panel.label_edit.setText(r"$\beta$ decay") + panel.label_edit.editingFinished.emit() + ax = document.figure.gca() + assert ax.lines[0]["label"] == r"\beta{} decay" + def test_write_back_markersize_uses_gle_scale(self, document): panel = SeriesPanel(document) panel.series_list.setCurrentRow(1) @@ -371,3 +396,209 @@ def test_programmatic_refresh_does_not_notify(self, document): before = document.notify_count panel.refresh() assert document.notify_count == before + + +# ------------------------------------------------------------------ +# SeriesPanel: heatmap & contour kinds (Phase B) +# ------------------------------------------------------------------ +def _make_heatmap_figure(): + import numpy as np + + fig = gleplot.figure(figsize=(8, 6), dpi=100) + ax = fig.gca() + Z = np.array([[0.0, 0.5], [0.5, 1.0]]) + ax.imshow(Z, cmap="viridis", vmin=0.0, vmax=1.0, label="hm") + ax.contour(Z, levels=[0.25, 0.75], colors="black", label="ct") + return fig + + +@pytest.fixture +def heatmap_document(qapp): + return StubDocument(_make_heatmap_figure()) + + +class TestSeriesPanelHeatmapContour: + def test_heatmap_and_contour_listed(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + texts = [ + panel.series_list.item(i).text() for i in range(panel.series_list.count()) + ] + assert texts == ["heatmap: hm", "contour: ct"] + + def test_select_heatmap_shows_correct_controls(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(0) # heatmap + assert panel.palette_combo.isEnabled() is True + assert panel.vmin_edit.isEnabled() is True + assert panel.vmax_edit.isEnabled() is True + assert panel.pixels_spin.isEnabled() is True + assert panel.interp_combo.isEnabled() is True + assert panel.invert_check.isEnabled() is True + assert panel.colorbar_check.isEnabled() is True + # A colormap has no single line colour, and contour-only / xy-only + # controls stay disabled. + assert panel.color_button.isEnabled() is False + assert panel.levels_edit.isEnabled() is False + assert panel.linestyle_combo.isEnabled() is False + # Populated from the stored dict. + assert panel.palette_combo.currentText() == "viridis" + assert panel.vmin_edit.text() == "0.0" + assert panel.vmax_edit.text() == "1.0" + + def test_select_contour_shows_correct_controls(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(1) # contour + assert panel.color_button.isEnabled() is True + assert panel.linewidth_spin.isEnabled() is True + assert panel.levels_edit.isEnabled() is True + assert panel.clabel_check.isEnabled() is True + assert panel.clabel_format_edit.isEnabled() is True + # heatmap-only controls stay disabled. + assert panel.palette_combo.isEnabled() is False + assert panel.colorbar_check.isEnabled() is False + # Explicit levels shown back as text. + assert panel.levels_edit.text() == "0.25 0.75" + + def test_change_palette_updates_dict(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(0) + before = heatmap_document.notify_count + panel.palette_combo.setCurrentText("magma") + ax = heatmap_document.figure.gca() + assert ax.heatmaps[0]["cmap"] == "magma" + assert heatmap_document.notify_count == before + 1 + + def test_change_palette_undo_restores(self, heatmap_document): + from gleplot.gui.undo import UndoStack + + undo = UndoStack(heatmap_document) + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(0) + + panel.palette_combo.setCurrentText("magma") + assert heatmap_document.figure.gca().heatmaps[0]["cmap"] == "magma" + + assert undo.can_undo is True + undo.undo() + # Snapshot-based undo restores the whole figure; the palette reverts. + assert heatmap_document.figure.gca().heatmaps[0]["cmap"] == "viridis" + + def test_vmin_blank_stores_none(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(0) + panel.vmin_edit.setText("") + panel.vmin_edit.editingFinished.emit() + assert heatmap_document.figure.gca().heatmaps[0]["vmin"] is None + + def test_vmin_invalid_text_reverts(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(0) + panel.vmin_edit.setText("not-a-number") + panel.vmin_edit.editingFinished.emit() + # Stored value unchanged; field reverted to it. + assert heatmap_document.figure.gca().heatmaps[0]["vmin"] == pytest.approx(0.0) + assert panel.vmin_edit.text() == "0.0" + + def test_toggle_colorbar_creates_and_removes_dict(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(0) + hm = heatmap_document.figure.gca().heatmaps[0] + assert hm["colorbar"] is None + + panel.colorbar_check.setChecked(True) + assert isinstance(hm["colorbar"], dict) + assert hm["colorbar"]["zmin"] == pytest.approx(0.0) + assert hm["colorbar"]["zmax"] == pytest.approx(1.0) + assert panel.cbar_label_edit.isEnabled() is True + + panel.colorbar_check.setChecked(False) + assert hm["colorbar"] is None + assert panel.cbar_label_edit.isEnabled() is False + + def test_colorbar_label_edit(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(0) + panel.colorbar_check.setChecked(True) + panel.cbar_label_edit.setText("Intensity") + panel.cbar_label_edit.editingFinished.emit() + hm = heatmap_document.figure.gca().heatmaps[0] + assert hm["colorbar"]["label"] == "Intensity" + + def test_pixels_edit(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(0) + panel.pixels_spin.setValue(300) + panel.pixels_spin.editingFinished.emit() + assert heatmap_document.figure.gca().heatmaps[0]["pixels"] == [300, 300] + + def test_invert_toggle(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(0) + panel.invert_check.setChecked(True) + assert heatmap_document.figure.gca().heatmaps[0]["invert"] is True + + def test_interpolation_change(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(0) + panel.interp_combo.setCurrentText("nearest") + assert heatmap_document.figure.gca().heatmaps[0]["interpolation"] == "nearest" + + def test_contour_levels_explicit_list(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(1) + panel.levels_edit.setText("0.1 0.2 0.3") + panel.levels_edit.editingFinished.emit() + assert heatmap_document.figure.gca().contours[0]["levels"] == [0.1, 0.2, 0.3] + + def test_contour_levels_n_form(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(1) + panel.levels_edit.setText("n=3") + panel.levels_edit.editingFinished.emit() + levels = heatmap_document.figure.gca().contours[0]["levels"] + assert levels is not None + assert len(levels) == 3 # 3 evenly-spaced levels strictly inside [0, 1] + + def test_contour_levels_blank_is_none(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(1) + panel.levels_edit.setText("") + panel.levels_edit.editingFinished.emit() + assert heatmap_document.figure.gca().contours[0]["levels"] is None + + def test_contour_levels_invalid_reverts(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(1) + panel.levels_edit.setText("abc def") + panel.levels_edit.editingFinished.emit() + # Unchanged; field reverts to the stored explicit list. + assert heatmap_document.figure.gca().contours[0]["levels"] == [0.25, 0.75] + assert panel.levels_edit.text() == "0.25 0.75" + + def test_contour_color_and_linewidth(self, heatmap_document): + from gleplot.colors import rgb_to_gle + + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(1) + # color_button opens a modal dialog; exercise the same write path. + ct = heatmap_document.figure.gca().contours[0] + ct["color"] = rgb_to_gle((1.0, 0.0, 0.0)) + assert ct["color"] == "RED" + panel.linewidth_spin.setValue(3.0) + panel.linewidth_spin.editingFinished.emit() + assert ct["linewidth"] == pytest.approx(3.0) + + def test_contour_clabel_toggle_and_format(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + panel.series_list.setCurrentRow(1) + panel.clabel_check.setChecked(True) + assert heatmap_document.figure.gca().contours[0]["clabel"] is True + panel.clabel_format_edit.setText("fix 2") + panel.clabel_format_edit.editingFinished.emit() + assert heatmap_document.figure.gca().contours[0]["clabel_fmt"] == "fix 2" + + def test_programmatic_refresh_does_not_notify(self, heatmap_document): + panel = SeriesPanel(heatmap_document) + before = heatmap_document.notify_count + panel.refresh() + assert heatmap_document.notify_count == before diff --git a/tests/integration/test_contour_compilation.py b/tests/integration/test_contour_compilation.py new file mode 100644 index 0000000..fd60e48 --- /dev/null +++ b/tests/integration/test_contour_compilation.py @@ -0,0 +1,104 @@ +"""End-to-end compilation of a contour/heatmap figure to PNG with real GLE. + +Exercises the full pipeline (imshow/tripcolor + tricontour + colorbar + palette +subs + fitz/contour blocks) through the actual ``gle`` binary and asserts a +nonempty raster is produced. Skipped when GLE is not installed. +""" + +from __future__ import annotations + +import numpy as np +import pytest + +import gleplot as glp +from gleplot.compiler import GLECompiler + + +def _gle_available() -> bool: + try: + GLECompiler() + return True + except RuntimeError: + return False + + +pytestmark = pytest.mark.skipif(not _gle_available(), reason="GLE binary not available") + + +def test_phase_diagram_like_tripcolor_tricontour_colorbar_compiles(tmp_path): + # Synthetic susceptibility-like field chi(T, H): a Neel boundary + # T_N(H) = T_N0 * sqrt(1 - (H/Hc)^2) with a ridge in chi at the boundary. + rng = np.random.default_rng(3) + T = rng.uniform(0.2, 5.0, 400) + H = rng.uniform(0.0, 3.0, 400) + Hc, TN0 = 3.2, 5.0 + boundary = TN0 * np.sqrt(np.clip(1.0 - (H / Hc) ** 2, 0.0, None)) + chi = np.exp(-((T - boundary) ** 2) / 0.4) + 0.05 * T + + fig = glp.figure(figsize=(9, 6), data_prefix="phase") + ax = fig.add_subplot(111) + ax.tripcolor( + T, H, chi, gridsize=(60, 40), extent=(0.2, 5.0, 0.0, 3.0), cmap="viridis" + ) + ax.tricontour( + T, + H, + chi, + gridsize=(60, 40), + extent=(0.2, 5.0, 0.0, 3.0), + ncontour=4, + levels=[0.4, 0.7, 0.9], + colors="white", + clabel=True, + ) + ax.set_xlabel("Temperature (K)") + ax.set_ylabel("Magnetic field (T)") + fig.colorbar(label="chi (emu/mol)", format="fix 1") + + out = fig.savefig(str(tmp_path / "phase.png")) + assert out.exists() + assert out.stat().st_size > 0 + + +def test_imshow_colorbar_compiles(tmp_path): + y, x = np.mgrid[0:40, 0:50] + Z = np.sin(x / 6.0) * np.cos(y / 5.0) + + fig = glp.figure(figsize=(7, 6), data_prefix="im") + ax = fig.add_subplot(111) + ax.imshow(Z, extent=(0, 10, 0, 8), cmap="magma", vmin=-1, vmax=1) + ax.set_xlabel("x") + ax.set_ylabel("y") + fig.colorbar(label="signal") + + out = fig.savefig(str(tmp_path / "im.png")) + assert out.exists() + assert out.stat().st_size > 0 + + +def test_constant_field_colorbar_compiles(tmp_path): + """A constant Z (zmax == zmin) must not divide-by-zero in the colorbar sub + and abort the render (regression: the degenerate-range guard).""" + fig = glp.figure(figsize=(7, 6), data_prefix="const") + ax = fig.add_subplot(111) + ax.imshow(np.full((20, 25), 7.0), extent=(0, 10, 0, 8), cmap="viridis") + fig.colorbar(label="constant") + + out = fig.savefig(str(tmp_path / "const.png")) + assert out.exists() + assert out.stat().st_size > 0 + + +def test_all_palettes_compile(tmp_path): + """Every supported palette's emitted sub compiles as a heatmap + colorbar.""" + from gleplot.palettes import SUPPORTED_CMAPS + + y, x = np.mgrid[0:20, 0:24] + Z = np.sin(x / 5.0) * np.cos(y / 4.0) + for cmap in SUPPORTED_CMAPS: + fig = glp.figure(figsize=(6, 5), data_prefix=f"pal_{cmap}") + ax = fig.add_subplot(111) + ax.imshow(Z, extent=(0, 10, 0, 8), cmap=cmap) + fig.colorbar(label=cmap) + out = fig.savefig(str(tmp_path / f"{cmap}.png")) + assert out.exists() and out.stat().st_size > 0, cmap diff --git a/tests/parser/_golden_battery.py b/tests/parser/_golden_battery.py index 5d88639..d5371d0 100644 --- a/tests/parser/_golden_battery.py +++ b/tests/parser/_golden_battery.py @@ -200,6 +200,67 @@ def custom_figsize_and_dpi(): return fig +def heatmap_imshow_colorbar(): + """imshow (grid .z sidecar) + a vertical colorbar.""" + fig = glp.figure(figsize=(7, 6), data_prefix="golden") + ax = fig.add_subplot(111) + y, x = np.mgrid[0:16, 0:21] + Z = np.sin(x / 6.0) * np.cos(y / 5.0) + ax.imshow(Z, extent=(0, 10, 0, 8), cmap="viridis", vmin=-1, vmax=1) + ax.set_xlabel("x") + ax.set_ylabel("y") + fig.colorbar(label="signal", format="fix 1") + return fig + + +def contour_grid_levels_clabel(): + """Gridded contour (contour(x, y, Z)) with explicit levels + clabels.""" + fig = glp.figure(figsize=(7, 6), data_prefix="golden") + ax = fig.add_subplot(111) + x = np.linspace(0, 10, 26) + y = np.linspace(0, 8, 21) + Z = np.sin(x[None, :] / 6.0) * np.cos(y[:, None] / 5.0) + ax.contour( + x, + y, + Z, + levels=[-0.5, 0.0, 0.5], + colors="black", + linewidths=1.0, + clabel=True, + clabel_fmt="fix 2", + ) + ax.set_xlabel("x") + ax.set_ylabel("y") + return fig + + +def tripcolor_tricontour_combo(): + """Scattered tripcolor + tricontour on one axes, with a colorbar.""" + fig = glp.figure(figsize=(8, 6), data_prefix="golden") + ax = fig.add_subplot(111) + rng = np.random.default_rng(7) + xs = rng.uniform(0, 10, 150) + ys = rng.uniform(0, 8, 150) + zs = np.sin(xs) * np.cos(ys) + ax.tripcolor(xs, ys, zs, gridsize=(51, 41), extent=(0, 10, 0, 8), cmap="magma") + ax.tricontour( + xs, + ys, + zs, + gridsize=(51, 41), + extent=(0, 10, 0, 8), + ncontour=3, + levels=[-0.4, 0.0, 0.4], + colors="white", + clabel=True, + ) + ax.set_xlabel("x") + ax.set_ylabel("y") + fig.colorbar(label="z", format="fix 1") + return fig + + BUILDERS = [ single_line, multi_series_styles, @@ -216,6 +277,9 @@ def custom_figsize_and_dpi(): file_series, large_markersize_and_linewidth, custom_figsize_and_dpi, + heatmap_imshow_colorbar, + contour_grid_levels_clabel, + tripcolor_tricontour_combo, ] BUILDER_IDS = [b.__name__ for b in BUILDERS] diff --git a/tests/parser/test_fixed_point.py b/tests/parser/test_fixed_point.py index 6469622..c825dcc 100644 --- a/tests/parser/test_fixed_point.py +++ b/tests/parser/test_fixed_point.py @@ -52,7 +52,13 @@ def _save(fig, directory: Path): gle_path = directory / "figure.gle" fig.savefig_gle(str(gle_path)) text = gle_path.read_text(encoding="utf-8") - data = {p.name: p.read_bytes() for p in directory.glob("*.dat")} + # Compare both columnar ``.dat`` sidecars and raw ``.z``/points sidecars + # (heatmap/contour grids and scattered points) for byte identity. + data = { + p.name: p.read_bytes() + for p in directory.iterdir() + if p.suffix in (".dat", ".z") + } return text, data diff --git a/tests/parser/test_recognizer.py b/tests/parser/test_recognizer.py index 94218fc..de4202a 100644 --- a/tests/parser/test_recognizer.py +++ b/tests/parser/test_recognizer.py @@ -115,6 +115,12 @@ def _snap_array(v): return [_snap_num(e) for e in v] return v + def _snap_deep(v): + # Snap a possibly-nested (2-D ``z`` grid) numeric structure. + if isinstance(v, list): + return [_snap_deep(e) for e in v] + return _snap_num(v) + # Data arrays are written to the .dat sidecar at 6 significant figures, so # recovered arrays are rounded. Snap BOTH sides to that emitted precision. _ARRAY_KEYS = { @@ -164,6 +170,30 @@ def _snap_array(v): for s in ax.get("fills", []): s.pop("alpha", None) + # Heatmap / contour series: grids and scattered points are written to + # ``.z``/``.dat`` sidecars at 6 significant figures, extents/levels/ + # vmin/vmax to the emitted GLE precision; the colorbar's z-range comes + # back from the emitted call. Snap BOTH sides to those precisions. The + # recovered ``z`` grid is always y-increasing origin='lower' (documented + # normalization) -- our builders use origin='lower' so no flip needed. + for group in ("heatmaps", "contours"): + for s in ax.get(group, []): + if s.get("z") is not None: + s["z"] = _snap_deep(s["z"]) + for k in ("x", "y", "zpts", "extent", "levels"): + if s.get(k) is not None: + s[k] = _snap_array(s[k]) + for k in ("vmin", "vmax"): + if s.get(k) is not None: + s[k] = _snap_num(s[k]) + if "linewidth" in s: + s["linewidth"] = _snap_linewidth(s["linewidth"]) + cb = s.get("colorbar") + if cb: + for k in ("zmin", "zmax", "zstep", "width", "sep"): + if cb.get(k) is not None: + cb[k] = _snap_num(cb[k]) + # Text annotations: fontsize round-trips through cm (snap); box_color # is accepted-but-ignored by the writer (never emitted) -> drop. # diff --git a/tests/parser/test_recognizer_contour.py b/tests/parser/test_recognizer_contour.py new file mode 100644 index 0000000..bb79f6b --- /dev/null +++ b/tests/parser/test_recognizer_contour.py @@ -0,0 +1,299 @@ +"""Recognizer tests for contour/heatmap: gleplot-authored recovery + hand-written +preservation. + +The byte-identical fixed point over gleplot-authored contour/heatmap figures is +covered by ``test_fixed_point.py`` (golden battery); ``to_dict`` equivalence by +``test_recognizer.py``. Here we assert semantic recovery of the model fields and +that hand-written / foreign contour-heatmap content is NEVER lost (preserved +verbatim through passthrough, with a warning). +""" + +from __future__ import annotations + +from pathlib import Path + +import numpy as np +import pytest + +import gleplot as glp +from gleplot.parser.recognizer import parse_gle_figure +from gleplot.parser.syntax import emit, parse_gle_source + + +def _save(fig, tmp_path: Path) -> Path: + p = tmp_path / "f.gle" + fig.savefig_gle(str(p)) + return p + + +# --------------------------------------------------------------------------- # +# Semantic recovery of gleplot-authored figures +# --------------------------------------------------------------------------- # + + +def test_imshow_colorbar_recovered(tmp_path): + fig = glp.figure(figsize=(7, 6), data_prefix="g") + ax = fig.add_subplot(111) + ax.imshow( + np.arange(12.0).reshape(3, 4), + extent=(0, 4, 0, 3), + cmap="inferno", + vmin=0, + vmax=11, + interpolation="nearest", + invert=True, + ) + fig.colorbar(label="phi", format="fix 2", width=0.4, sep=0.6) + path = _save(fig, tmp_path) + + rec = parse_gle_figure(path) + assert rec.warnings == [] + hm = rec.figure.axes_list[0].heatmaps[0] + assert hm["source"] == "grid" + assert hm["cmap"] == "inferno" + assert hm["vmin"] == 0.0 and hm["vmax"] == 11.0 + assert hm["invert"] is True + assert hm["interpolation"] == "nearest" + assert hm["extent"] == [0.0, 4.0, 0.0, 3.0] + assert hm["z"].shape == (3, 4) + cb = hm["colorbar"] + assert cb["label"] == "phi" and cb["format"] == "fix 2" + assert cb["width"] == 0.4 and cb["sep"] == 0.6 + + +def test_tricontour_recovered_with_clabel(tmp_path): + fig = glp.figure(figsize=(8, 6), data_prefix="g") + ax = fig.add_subplot(111) + x = np.linspace(0, 4, 12) + y = np.linspace(0, 4, 12) + z = np.sin(x) + y + ax.tricontour( + x, + y, + z, + gridsize=(9, 9), + extent=(0, 4, 0, 4), + ncontour=5, + levels=[1.0, 2.0, 3.0], + colors="blue", + clabel=True, + clabel_fmt="fix 0", + ) + path = _save(fig, tmp_path) + + rec = parse_gle_figure(path) + assert rec.warnings == [] + ct = rec.figure.axes_list[0].contours[0] + assert ct["source"] == "points" + assert ct["gridsize"] == [9, 9] + assert ct["ncontour"] == 5 + assert ct["levels"] == [1.0, 2.0, 3.0] + assert ct["color"] == "BLUE" + assert ct["clabel"] is True + assert ct["clabel_fmt"] == "fix 0" + + +def test_generated_files_absent_are_tolerated(tmp_path): + """The GLE-generated ``-cdata.dat``/``.z`` (fitz) files never exist at save + time; recovery must not treat their references as broken series.""" + fig = glp.figure(figsize=(7, 6), data_prefix="g") + ax = fig.add_subplot(111) + x = np.linspace(0, 4, 6) + y = np.linspace(0, 3, 5) + ax.contour(x, y, np.outer(y, x), levels=[2.0, 4.0], colors="black") + path = _save(fig, tmp_path) + # No cdata file exists next to the .gle. + assert not list(tmp_path.glob("*-cdata.dat")) + + rec = parse_gle_figure(path) + ax2 = rec.figure.axes_list[0] + assert len(ax2.contours) == 1 + assert not ax2.file_series # not a broken file series + assert not any("cdata" in p for p in ax2.passthrough) + # No "data:" broken-reference warning for the generated cdata file. + assert not any(w.startswith("data:") for w in rec.warnings) + + +# --------------------------------------------------------------------------- # +# Hand-written / foreign preservation (never lose content) +# --------------------------------------------------------------------------- # + + +def test_foreign_palette_colormap_preserved(tmp_path): + """A colormap naming a NON-gleplot palette sub is kept verbatim + warned.""" + (tmp_path / "grid.z").write_text( + "! nx 2 ny 2 xmin 0 xmax 1 ymin 0 ymax 1\n0 1\n1 2\n", encoding="utf-8" + ) + src = ( + "size 10 10\n" + "begin graph\n" + ' colormap "grid.z" 100 100 palette my_custom_pal\n' + "end graph\n" + ) + p = tmp_path / "f.gle" + p.write_text(src, encoding="utf-8") + + rec = parse_gle_figure(p) + ax = rec.figure.axes_list[0] + assert not ax.heatmaps # unknown palette -> not modeled + assert any("my_custom_pal" in line for line in ax.passthrough) + assert any("unknown palette" in w for w in rec.warnings) + # Round-trip preserves the line verbatim. + text2, _ = rec.figure._generate_gle_with_files() + assert 'colormap "grid.z" 100 100 palette my_custom_pal' in text2 + + +def test_function_colormap_preserved(tmp_path): + """A colormap of a function expression (not a .z file) is kept verbatim.""" + src = ( + "size 10 10\n" + "begin graph\n" + ' colormap "sin(x)*cos(y)" 100 100 color\n' + "end graph\n" + ) + p = tmp_path / "f.gle" + p.write_text(src, encoding="utf-8") + + rec = parse_gle_figure(p) + ax = rec.figure.axes_list[0] + assert not ax.heatmaps + assert any("sin(x)*cos(y)" in line for line in ax.passthrough) + assert any("function expression" in w for w in rec.warnings) + + +def test_unreferenced_foreign_fitz_block_preserved(tmp_path): + """A ``begin fitz`` whose output is never used stays opaque passthrough.""" + (tmp_path / "pts.dat").write_text("0 0 0\n1 1 1\n2 2 2\n", encoding="utf-8") + src = ( + "size 10 10\n" + "begin fitz\n" + ' data "pts.dat"\n' + " x from 0 to 2 step 1\n" + " y from 0 to 2 step 1\n" + "end fitz\n" + "begin graph\n" + " data pts.dat d1=c1,c2\n" + " d1 marker circle\n" + "end graph\n" + ) + p = tmp_path / "f.gle" + p.write_text(src, encoding="utf-8") + + rec = parse_gle_figure(p) + # The fitz block's output .z is never referenced by a colormap/contour, so + # it is NOT consumed -> preserved verbatim in the trailer/header passthrough. + text2, _ = rec.figure._generate_gle_with_files() + assert "begin fitz" in text2 + assert 'data "pts.dat"' in text2 + + +def test_fitz_feeding_foreign_colormap_preserved(tmp_path): + """A ``begin fitz`` whose .z feeds a FOREIGN-palette colormap is preserved. + + Regression: the prescan used to drop the fitz block whenever its generated + ``.z`` was merely *referenced* by a colormap -- even when that colormap + named an unknown palette and was itself re-emitted verbatim as passthrough. + Dropping the fitz block then left the preserved colormap line pointing at a + grid nothing produces (content loss + broken output). The block must stay + opaque when its consumer isn't reconstructed. + """ + (tmp_path / "scatter.dat").write_text("0 0 0\n1 1 1\n2 2 2\n", encoding="utf-8") + src = ( + "size 12 9\n" + "sub mypal z\n" + " return rgb(z,0,1-z)\n" + "end sub\n" + "begin fitz\n" + ' data "scatter.dat"\n' + " x from 0 to 10 step 0.5\n" + " y from 0 to 8 step 0.4\n" + " ncontour 5\n" + "end fitz\n" + "begin graph\n" + ' colormap "scatter.z" 300 300 palette mypal\n' + "end graph\n" + ) + p = tmp_path / "f.gle" + p.write_text(src, encoding="utf-8") + + rec = parse_gle_figure(p) + assert not rec.figure.axes_list[0].heatmaps # foreign palette -> not modeled + text2, _ = rec.figure._generate_gle_with_files() + assert "begin fitz" in text2 + assert 'data "scatter.dat"' in text2 + assert 'colormap "scatter.z" 300 300 palette mypal' in text2 + + +def test_contour_cdata_with_unreadable_grid_preserved(tmp_path): + """A gleplot-shaped contour whose grid ``.z`` is missing loses nothing. + + Regression: the prescan consumed the ``begin contour`` block and the graph + ``data "-cdata.dat"`` line by filename match, but reconstruction then + bailed because the ``.z`` grid could not be read -- dropping the block and + the data command and orphaning the ``dN line``. With no readable grid the + block must stay opaque and the data command + display line preserved. + """ + # NB: no ``mydata.z`` on disk -> grid unreadable. + src = ( + "size 12 9\n" + "begin contour\n" + ' data "mydata.z"\n' + " values 0.2 0.4 0.6\n" + "end contour\n" + "begin graph\n" + ' data "mydata-cdata.dat" d1=c1,c2\n' + " d1 line color red lwidth 0.05\n" + "end graph\n" + ) + p = tmp_path / "f.gle" + p.write_text(src, encoding="utf-8") + + rec = parse_gle_figure(p) + assert not rec.figure.axes_list[0].contours # unreadable grid -> not modeled + text2, _ = rec.figure._generate_gle_with_files() + assert "begin contour" in text2 + assert 'data "mydata.z"' in text2 + assert "mydata-cdata.dat" in text2 + assert "d1 line color red" in text2 + + +def test_handwritten_gleplot_authored_style_recovers(tmp_path): + """A hand-authored file using the gleplot idiom (quoted files, gleplot_ + palette) recovers as a heatmap even without a metadata block.""" + (tmp_path / "grid.z").write_text( + "! nx 2 ny 2 xmin 0 xmax 1 ymin 0 ymax 1\n0 1\n1 2\n", encoding="utf-8" + ) + from gleplot import palettes + + src = ( + "size 10 10\n" + palettes.palette_sub_text("viridis") + "\n" + "begin graph\n" + ' colormap "grid.z" 100 100 palette gleplot_viridis\n' + "end graph\n" + ) + p = tmp_path / "f.gle" + p.write_text(src, encoding="utf-8") + + rec = parse_gle_figure(p) + ax = rec.figure.axes_list[0] + assert len(ax.heatmaps) == 1 + assert ax.heatmaps[0]["cmap"] == "viridis" + # gleplot_ sub not flagged as a programmatic file. + assert not any(w.startswith("programmatic:") for w in rec.warnings) + + +def test_structural_roundtrip_of_contour_file_unchanged(tmp_path): + """The low-level structural parser round-trips a contour/heatmap file + byte-for-byte (no semantic layer).""" + fig = glp.figure(figsize=(7, 6), data_prefix="g") + ax = fig.add_subplot(111) + ax.imshow(np.arange(6.0).reshape(2, 3), extent=(0, 3, 0, 2), cmap="viridis") + ax.contour( + np.linspace(0, 3, 3), + np.linspace(0, 2, 2), + np.arange(6.0).reshape(2, 3), + levels=[1.0, 3.0], + clabel=True, + ) + fig.colorbar(label="v") + text = fig._generate_gle_with_files()[0] + assert emit(parse_gle_source(text)) == text diff --git a/tests/unit/test_contour_heatmap.py b/tests/unit/test_contour_heatmap.py new file mode 100644 index 0000000..115158f --- /dev/null +++ b/tests/unit/test_contour_heatmap.py @@ -0,0 +1,414 @@ +"""Unit tests for contour/heatmap (imshow-style) support: stored dicts + GLE. + +Mirrors the ``tests/unit/test_plotting.py`` style -- assert the object-model +dicts each API call stores AND substrings of the generated GLE. Covers +``imshow``, ``contour``, ``tripcolor``, ``tricontour``, ``Figure.colorbar``, +palette emission, the ``.z``/points sidecars, and the one-heatmap-per-axes rule. +""" + +from __future__ import annotations + +import numpy as np +import pytest + +import gleplot as glp +from gleplot import palettes + + +def _gle(fig): + text, files = fig._generate_gle_with_files() + return text, files + + +# --------------------------------------------------------------------------- # +# imshow (grid) +# --------------------------------------------------------------------------- # + + +def test_imshow_stores_heatmap_dict(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + Z = np.arange(12, dtype=float).reshape(3, 4) + hm = ax.imshow(Z, extent=(0, 4, 0, 3), cmap="viridis", vmin=0, vmax=11) + + assert hm is ax.heatmaps[0] + assert hm["type"] == "heatmap" + assert hm["source"] == "grid" + assert hm["extent"] == [0.0, 4.0, 0.0, 3.0] + assert hm["cmap"] == "viridis" + assert hm["vmin"] == 0.0 and hm["vmax"] == 11.0 + assert hm["pixels"] == [200, 200] + assert hm["origin"] == "lower" + assert hm["interpolation"] == "bicubic" + assert hm["invert"] is False + assert hm["data_file"] == "t_heatmap1.z" + assert hm["colorbar"] is None + + +def test_imshow_default_extent_and_cmap(): + fig = glp.figure() + ax = fig.add_subplot(111) + Z = np.zeros((5, 7)) + hm = ax.imshow(Z) + assert hm["extent"] == [0.0, 7.0, 0.0, 5.0] + assert hm["cmap"] == "viridis" # graph.default_cmap + + +def test_imshow_emits_colormap_and_z_sidecar(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + ax.imshow( + np.arange(6.0).reshape(2, 3), + extent=(0, 3, 0, 2), + cmap="viridis", + vmin=0, + vmax=5, + ) + text, files = _gle(fig) + + assert ( + 'colormap "t_heatmap1.z" 200 200 zmin 0 zmax 5 palette gleplot_viridis' in text + ) + assert "sub gleplot_viridis z" in text + # .z sidecar: header + ny rows of nx values, y increasing. + z = files["t_heatmap1.z"] + assert z.startswith("! nx 3 ny 2 xmin 0 xmax 3 ymin 0 ymax 2\n") + assert z.splitlines()[1] == "0 1 2" # row ymin first + assert z.splitlines()[2] == "3 4 5" + + +def test_imshow_origin_upper_flips_rows(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + ax.imshow(np.array([[0.0, 1.0], [2.0, 3.0]]), extent=(0, 2, 0, 2), origin="upper") + _, files = _gle(fig) + rows = files["t_heatmap1.z"].splitlines() + # origin='upper': row 0 (top) written last -> ymin row is [2, 3]. + assert rows[1] == "2 3" + assert rows[2] == "0 1" + + +def test_imshow_gray_has_no_palette_clause(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + ax.imshow(np.zeros((2, 2)), cmap="gray") + text, _ = _gle(fig) + assert "colormap" in text + assert "palette" not in text.split("colormap", 1)[1].split("\n", 1)[0] + assert "sub gleplot_" not in text + + +def test_imshow_rainbow_uses_color_switch(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + ax.imshow(np.zeros((2, 2)), cmap="jet") # alias -> rainbow + text, _ = _gle(fig) + line = [ln for ln in text.splitlines() if "colormap" in ln][0] + assert " color" in line + assert ax.heatmaps[0]["cmap"] == "rainbow" + + +def test_imshow_nearest_and_invert(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + ax.imshow(np.zeros((2, 2)), cmap="viridis", interpolation="nearest", invert=True) + line = [ln for ln in _gle(fig)[0].splitlines() if "colormap" in ln][0] + assert " invert" in line + assert " interpolate nearest" in line + + +def test_only_one_heatmap_per_axes(): + fig = glp.figure() + ax = fig.add_subplot(111) + ax.imshow(np.zeros((2, 2))) + with pytest.raises(ValueError, match="at most one heatmap"): + ax.imshow(np.zeros((2, 2))) + + +def test_unknown_cmap_raises(): + fig = glp.figure() + ax = fig.add_subplot(111) + with pytest.raises(ValueError, match="Unknown cmap"): + ax.imshow(np.zeros((2, 2)), cmap="turbo") + + +# --------------------------------------------------------------------------- # +# contour (grid) +# --------------------------------------------------------------------------- # + + +def test_contour_stores_dict_and_emits_blocks(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + x = np.linspace(0, 4, 5) + y = np.linspace(0, 3, 4) + Z = np.outer(y, x) + ct = ax.contour( + x, + y, + Z, + levels=[2.0, 4.0, 6.0], + colors="red", + linewidths=2.0, + clabel=True, + clabel_fmt="fix 2", + ) + + assert ct is ax.contours[0] + assert ct["type"] == "contour" + assert ct["source"] == "grid" + assert ct["extent"] == [0.0, 4.0, 0.0, 3.0] + assert ct["levels"] == [2.0, 4.0, 6.0] + assert ct["color"] == "RED" + assert ct["clabel"] is True + assert ct["clabel_fmt"] == "fix 2" + assert ct["data_file"] == "t_contour1.z" + + text, files = _gle(fig) + assert "begin contour" in text + assert ' data "t_contour1.z"' in text + assert " values 2 4 6" in text + assert 'data "t_contour1-cdata.dat" d1=c1,c2' in text + assert "d1 line color RED" in text + # clabel: sub + call + assert "sub gleplot_contour_labels" in text + assert 'gleplot_contour_labels file "t_contour1-clabels.dat" format "fix 2"' in text + assert "t_contour1.z" in files + + +def test_contour_non_uniform_x_raises(): + fig = glp.figure() + ax = fig.add_subplot(111) + x = np.array([0.0, 1.0, 3.0, 4.0]) # non-uniform + y = np.linspace(0, 1, 3) + with pytest.raises(ValueError, match="uniformly spaced"): + ax.contour(x, y, np.zeros((3, 4))) + + +def test_contour_int_levels_resolves_to_explicit_list(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + Z = np.arange(12, dtype=float).reshape(3, 4) # min 0, max 11 + ct = ax.contour(Z, levels=3) + # 3 levels evenly spaced strictly inside (0, 11). + assert ct["levels"] == pytest.approx([2.75, 5.5, 8.25]) + text, _ = _gle(fig) + assert "values 2.75 5.5 8.25" in text + + +def test_contour_default_levels_omits_values(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + ax.contour(np.zeros((3, 4))) + assert ax.contours[0]["levels"] is None + text, _ = _gle(fig) + block = text.split("begin contour", 1)[1].split("end contour", 1)[0] + assert "values" not in block + + +# --------------------------------------------------------------------------- # +# tripcolor / tricontour (scattered) +# --------------------------------------------------------------------------- # + + +def test_tripcolor_stores_points_and_fitz(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + x = np.array([0.0, 1.0, 2.0, 3.0]) + y = np.array([0.0, 1.0, 2.0, 3.0]) + z = np.array([1.0, 2.0, 3.0, 4.0]) + hm = ax.tripcolor(x, y, z, gridsize=(5, 5), extent=(0, 4, 0, 4), cmap="magma") + + assert hm["source"] == "points" + assert hm["gridsize"] == [5, 5] + assert hm["data_file"] == "t_points1.dat" + assert np.allclose(hm["zpts"], z) + + text, files = _gle(fig) + assert "begin fitz" in text + assert ' data "t_points1.dat"' in text + assert " x from 0 to 4 step 1" in text + assert " y from 0 to 4 step 1" in text + # points sidecar: raw triples, no header. + pts = files["t_points1.dat"] + assert pts.splitlines()[0] == "0 0 1" + # colormap references the fitz-generated .z. + assert 'colormap "t_points1.z" 200 200' in text + + +def test_tricontour_stores_ncontour_and_emits_contour_on_fitz_z(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + x = np.linspace(0, 4, 6) + y = np.linspace(0, 4, 6) + z = x + y + ct = ax.tricontour( + x, y, z, gridsize=(5, 5), extent=(0, 4, 0, 4), ncontour=4, levels=[2.0, 4.0] + ) + + assert ct["source"] == "points" + assert ct["ncontour"] == 4 + assert ct["data_file"] == "t_points1.dat" + + text, _ = _gle(fig) + assert "begin fitz" in text + assert " ncontour 4" in text + assert ' data "t_points1.z"' in text # contour reads fitz output + assert 'data "t_points1-cdata.dat"' in text + + +# --------------------------------------------------------------------------- # +# colorbar +# --------------------------------------------------------------------------- # + + +def test_colorbar_attaches_to_heatmap_and_emits_call(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + ax.imshow( + np.arange(6.0).reshape(2, 3), + extent=(0, 3, 0, 2), + cmap="viridis", + vmin=0, + vmax=10, + ) + cb = fig.colorbar(label="χ", format="fix 1", nticks=5, width=0.4, sep=0.5) + + assert ax.heatmaps[0]["colorbar"] is cb + assert cb["zmin"] == 0.0 and cb["zmax"] == 10.0 + assert cb["zstep"] == 2.0 # (10-0)/5 + assert cb["width"] == 0.4 and cb["sep"] == 0.5 + + text, _ = _gle(fig) + assert "sub gleplot_colorbar_v" in text + assert "amove xg(xgmax)+0.5 yg(ygmin)" in text + assert ( + 'gleplot_colorbar_v zmin 0 zmax 10 zstep 2 palette "gleplot_viridis" ' + 'wd 0.4 hi yg(ygmax)-yg(ygmin) format "fix 1" label "χ"' + ) in text + + +def test_colorbar_without_vmin_uses_data_range(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + ax.imshow(np.array([[1.0, 2.0], [3.0, 7.0]])) + cb = fig.colorbar() + assert cb["zmin"] == 1.0 and cb["zmax"] == 7.0 + + +def test_colorbar_no_heatmap_raises(): + fig = glp.figure() + fig.add_subplot(111).plot([1, 2], [1, 2]) + with pytest.raises(ValueError, match="requires a heatmap"): + fig.colorbar() + + +def test_colorbar_ambiguous_raises(): + fig, axes = glp.subplots(1, 2) + axes[0].imshow(np.zeros((2, 2))) + axes[1].imshow(np.zeros((2, 2))) + with pytest.raises(ValueError, match="ambiguous"): + fig.colorbar() + + +# --------------------------------------------------------------------------- # +# autoscale from extent, module-level wrappers, palettes module +# --------------------------------------------------------------------------- # + + +def test_extent_drives_autoscale(): + fig = glp.figure(data_prefix="t") + ax = fig.add_subplot(111) + ax.imshow(np.zeros((4, 5)), extent=(2, 12, 1, 9)) + _gle(fig) # triggers limit derivation + assert ax.xmin == 2.0 and ax.xmax == 12.0 + assert ax.ymin == 1.0 and ax.ymax == 9.0 + + +def test_module_level_wrappers(): + glp.figure(data_prefix="t") + glp.imshow(np.zeros((2, 2)), cmap="viridis") + glp.colorbar(label="v") + ax = glp.gca() + assert ax.heatmaps and ax.heatmaps[0]["colorbar"] is not None + + +def test_palette_sub_text_is_deterministic(): + a = palettes.palette_sub_text("viridis") + b = palettes.palette_sub_text("viridis") + assert a == b + assert a.startswith("sub gleplot_viridis z") + assert a.rstrip().endswith("end sub") + assert palettes.palette_sub_text("gray") is None + assert palettes.palette_sub_text("rainbow") is None + + +# --------------------------------------------------------------------------- # +# invalid-input guards (GLE cannot represent these; reject early with a clear +# ValueError rather than emit a broken sidecar / .gle that aborts at compile) +# --------------------------------------------------------------------------- # + + +@pytest.mark.parametrize("bad", [np.nan, np.inf, -np.inf]) +def test_imshow_nonfinite_z_raises(bad): + fig = glp.figure() + ax = fig.add_subplot(111) + Z = np.arange(12.0).reshape(3, 4) + Z[1, 1] = bad + with pytest.raises(ValueError, match="NaN or infinite"): + ax.imshow(Z) + + +def test_contour_nonfinite_z_raises(): + fig = glp.figure() + ax = fig.add_subplot(111) + Z = np.arange(12.0).reshape(3, 4) + Z[0, 0] = np.nan + with pytest.raises(ValueError, match="NaN or infinite"): + ax.contour(Z) + + +def test_tripcolor_nonfinite_z_raises(): + fig = glp.figure() + ax = fig.add_subplot(111) + with pytest.raises(ValueError, match="NaN or infinite"): + ax.tripcolor( + [0.0, 1.0, 2.0, 3.0], [0.0, 1.0, 0.0, 1.0], [0.0, np.nan, 1.0, 2.0] + ) + + +def test_imshow_reversed_extent_raises(): + fig = glp.figure() + ax = fig.add_subplot(111) + with pytest.raises(ValueError, match="xmin < xmax"): + ax.imshow(np.arange(12.0).reshape(3, 4), extent=(10, 0, 8, 0)) + + +def test_imshow_degenerate_extent_raises(): + fig = glp.figure() + ax = fig.add_subplot(111) + with pytest.raises(ValueError, match="ascending"): + ax.imshow(np.arange(12.0).reshape(3, 4), extent=(0, 0, 0, 5)) + + +def test_contour_levels_all_outside_range_raises(): + fig = glp.figure() + ax = fig.add_subplot(111) + Z = np.arange(12.0).reshape(3, 4) # range [0, 11] + with pytest.raises(ValueError, match="outside the data range"): + ax.contour(Z, levels=[100, 200]) + + +def test_contour_levels_partially_in_range_ok(): + fig = glp.figure() + ax = fig.add_subplot(111) + Z = np.arange(12.0).reshape(3, 4) + ct = ax.contour(Z, levels=[5, 200]) # 5 is in range -> fine + assert ct["levels"] == [5.0, 200.0] + + +def test_colorbar_sub_guards_degenerate_range(): + """The colorbar sub must guard zmax == zmin (constant field) so it does not + divide by zero and abort the render.""" + text = palettes.colorbar_sub_text() + assert "if zmax = zmin then" in text diff --git a/tests/unit/test_mathtext.py b/tests/unit/test_mathtext.py new file mode 100644 index 0000000..e053328 --- /dev/null +++ b/tests/unit/test_mathtext.py @@ -0,0 +1,225 @@ +"""Tests for matplotlib-mathtext -> GLE-markup translation (``mathtext.py``). + +Covers the translation table (Greek/symbol macros, sub/superscripts, font +macros, ``\\frac``, spacing), the escaping/degradation rules (``\\$``, unmatched +``$``, unknown macros), the math->text boundary space rule, idempotence, and +integration with the API entry points (labels/titles/annotations stored +translated; emitted ``.gle`` contains the markup). +""" + +import sys +from pathlib import Path + +import pytest + +sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src")) + +import gleplot +from gleplot import mathtext_to_gle + + +# ---------------------------------------------------------------------- +# Pure translation-table cases +# ---------------------------------------------------------------------- +class TestGreekAndSymbols: + def test_bare_greek_before_space_gets_terminator(self): + # \chi followed by a real space needs {} so GLE keeps the space. + assert mathtext_to_gle(r"$\chi$ (emu/mol)") == r"\chi{} (emu/mol)" + + def test_greek_followed_by_subscript_needs_no_terminator(self): + assert mathtext_to_gle(r"$\chi_{mol}$") == r"\chi_{mol}" + + def test_symbol_macros_pass_through(self): + assert mathtext_to_gle(r"$\times$") == r"\times" + assert mathtext_to_gle(r"$\pm$") == r"\pm" + assert mathtext_to_gle(r"$\infty$") == r"\infty" + assert mathtext_to_gle(r"$\cdot$") == r"\cdot" + + def test_degree_symbol(self): + assert mathtext_to_gle(r"$^\circ$C") == r"^{\circ}C" + assert mathtext_to_gle(r"$\degree$C") == r"\degree{}C" + + def test_bare_macro_before_letter_gets_terminator(self): + # Without {} GLE would read \chimol as one macro. + assert mathtext_to_gle(r"$\alpha$mol") == r"\alpha{}mol" + + +class TestScripts: + def test_single_char_superscript_is_braced(self): + assert mathtext_to_gle(r"$x^2$") == r"x^{2}" + + def test_single_char_subscript_is_braced(self): + assert mathtext_to_gle(r"$x_i$") == r"x_{i}" + + def test_already_braced_script_passes(self): + assert mathtext_to_gle(r"emu mol$^{-1}$") == r"emu mol^{-1}" + + def test_mixed_sub_and_superscript(self): + assert mathtext_to_gle(r"$x_i^2$") == r"x_{i}^{2}" + + def test_macro_token_after_caret_is_braced(self): + assert mathtext_to_gle(r"$10^\alpha$") == r"10^{\alpha}" + + def test_only_first_token_scripted(self): + # matplotlib: x_10 subscripts only the '1'. + assert mathtext_to_gle(r"$x_10$") == r"x_{1}0" + + +class TestFontMacros: + def test_mathrm_maps_to_rm_group(self): + assert mathtext_to_gle(r"$\mathrm{d}x$") == r"{\rm d}x" + + def test_mathit_maps_to_it_group(self): + assert mathtext_to_gle(r"$\mathit{v}$") == r"{\it v}" + + def test_mathbf_maps_to_bf_group(self): + assert mathtext_to_gle(r"$\mathbf{F}$") == r"{\bf F}" + + def test_text_maps_to_rm_group(self): + assert mathtext_to_gle(r"$\text{ab}$") == r"{\rm ab}" + + def test_unsupported_family_strips_to_contents(self): + # GLE has no inline sans/calligraphic font: keep the text. + assert mathtext_to_gle(r"$\mathsf{Q}$") == "Q" + assert mathtext_to_gle(r"$\mathcal{L}$") == "L" + + def test_font_macro_contents_translated(self): + assert mathtext_to_gle(r"$\mathrm{cm}^{-1}$") == r"{\rm cm}^{-1}" + + +class TestFrac: + def test_frac_degrades_to_slash(self): + assert mathtext_to_gle(r"$\frac{a}{b}$") == "a/b" + + def test_frac_contents_translated(self): + assert mathtext_to_gle(r"$\frac{\alpha}{2}$") == r"\alpha/2" + + +class TestSpacing: + def test_thin_space_passes_through(self): + assert mathtext_to_gle(r"$a\,b$") == r"a\,b" + + def test_all_spacing_macros(self): + assert mathtext_to_gle(r"$\,\:\;\!$") == r"\,\:\;\!" + + +class TestEscapingAndDegradation: + def test_escaped_dollar_is_literal(self): + assert mathtext_to_gle(r"cost \$5") == "cost $5" + + def test_escaped_dollar_inside_math(self): + assert mathtext_to_gle(r"$a\$b$") == "a$b" + + def test_unmatched_dollar_unchanged(self): + assert mathtext_to_gle(r"$x = 5") == r"$x = 5" + assert mathtext_to_gle(r"a $ b $ c $") == r"a $ b $ c $" + + def test_unknown_macro_passes_through(self): + assert mathtext_to_gle(r"$\foobar$ x") == r"\foobar{} x" + + def test_empty_math_segment(self): + assert mathtext_to_gle(r"a$$b") == "ab" + + def test_literal_underscore_escape(self): + assert mathtext_to_gle(r"$a\_b$") == r"a\_b" + + +class TestBoundaryRule: + def test_terminator_added_only_when_needed(self): + # followed by space -> {} + assert mathtext_to_gle(r"$\chi$ x") == r"\chi{} x" + # followed by letter -> {} + assert mathtext_to_gle(r"$\chi$x") == r"\chi{}x" + # followed by punctuation -> no {} (macro name ends at non-letter) + assert mathtext_to_gle(r"$\chi$, y") == r"\chi, y" + # end of string -> no {} (nothing to swallow) + assert mathtext_to_gle(r"a $\chi$") == r"a \chi" + + +class TestIdentityAndIdempotence: + def test_no_dollar_is_identity(self): + for s in [r"\chi{} (emu/mol)", "plain text", r"T (\degree C)", ""]: + assert mathtext_to_gle(s) == s + + def test_non_string_passthrough(self): + assert mathtext_to_gle(None) is None + + @pytest.mark.parametrize( + "s", + [ + r"$\chi$ (emu/mol)", + r"$\chi_{mol}$ (emu mol$^{-1}$)", + r"$x_i^2$", + r"$\frac{a}{b}$", + r"$\mathrm{d}x$", + r"cost \$5", + r"$x = 5", # unmatched -> unchanged, still idempotent + r"$\alpha$mol", + r"plain text", + ], + ) + def test_idempotent(self, s): + once = mathtext_to_gle(s) + assert mathtext_to_gle(once) == once + + +# ---------------------------------------------------------------------- +# Integration: entry points store the translated string and emit it +# ---------------------------------------------------------------------- +class TestEntryPointsStoreTranslated: + def test_set_ylabel_stores_translated(self): + fig = gleplot.figure() + ax = fig.gca() + ax.plot([1, 2, 3], [1, 2, 3]) + ax.set_ylabel(r"$\chi$ (emu/mol)") + assert ax.ylabel_text == r"\chi{} (emu/mol)" + + def test_set_xlabel_and_title(self): + fig = gleplot.figure() + ax = fig.gca() + ax.set_xlabel(r"emu mol$^{-1}$") + ax.set_title(r"Susceptibility $\chi$ vs $T$") + assert ax.xlabel_text == r"emu mol^{-1}" + assert ax.title_text == r"Susceptibility \chi{} vs T" + + def test_y2label_stores_translated(self): + fig = gleplot.figure() + ax = fig.gca() + ax.set_ylabel(r"$\alpha$", axis="y2") + assert ax.y2label_text == r"\alpha" + + def test_series_label_stored_translated(self): + fig = gleplot.figure() + ax = fig.gca() + ax.plot([1, 2, 3], [1, 2, 3], label=r"$\beta$ decay") + assert ax.lines[0]["label"] == r"\beta{} decay" + + def test_text_annotation_translated(self): + fig = gleplot.figure() + ax = fig.gca() + ax.plot([1, 2], [1, 2]) + ax.text(1.0, 1.0, r"$\theta = 90^\circ$") + assert ax.texts[0]["text"] == r"\theta = 90^{\circ}" + + def test_colorbar_label_translated(self): + import numpy as np + + fig = gleplot.figure() + ax = fig.gca() + ax.imshow(np.arange(9).reshape(3, 3)) + cb = fig.colorbar(label=r"$\rho$ (a.u.)") + assert cb["label"] == r"\rho{} (a.u.)" + + def test_emitted_gle_contains_translated_label(self, tmp_path): + fig = gleplot.figure() + ax = fig.gca() + ax.plot([1, 2, 3], [1, 4, 9], label=r"$\chi$") + ax.set_ylabel(r"$\chi$ (emu/mol)") + out = tmp_path / "fig.gle" + fig.savefig_gle(str(out)) + text = out.read_text() + assert r"\chi{} (emu/mol)" in text + # The bare-macro label with no trailing text is emitted as-is. + assert r"\chi" in text + # No untranslated matplotlib mathtext leaks into the script. + assert "$" not in text diff --git a/tests/unit/test_serialization.py b/tests/unit/test_serialization.py index 5cc5c38..91ea05d 100644 --- a/tests/unit/test_serialization.py +++ b/tests/unit/test_serialization.py @@ -27,6 +27,77 @@ def _simple_figure(): return fig +def _heatmap_contour_figure(): + fig = glp.figure(figsize=(7, 6), data_prefix="u") + ax = fig.add_subplot(111) + Z = np.arange(12, dtype=float).reshape(3, 4) + ax.imshow(Z, extent=(0, 4, 0, 3), cmap="viridis", vmin=0, vmax=11) + ax.contour( + np.linspace(0, 4, 4), + np.linspace(0, 3, 3), + np.arange(12, dtype=float).reshape(3, 4), + levels=[2.0, 6.0], + colors="black", + clabel=True, + ) + fig.colorbar(label="signal") + return fig + + +# -- Heatmap / contour serialization --------------------------------------- + + +def test_heatmap_contour_to_from_dict_roundtrip(): + fig = _heatmap_contour_figure() + d = fig.to_dict() + # JSON-serializable (2-D z becomes nested lists; colorbar nested dict). + json.dumps(d) + fig2 = Figure.from_dict(d) + assert fig2.to_dict() == d + + +def test_heatmap_2d_z_restored_as_2d_float_array(): + fig = _heatmap_contour_figure() + fig2 = Figure.from_dict(fig.to_dict()) + hm = fig2.axes_list[0].heatmaps[0] + assert isinstance(hm["z"], np.ndarray) + assert hm["z"].ndim == 2 + assert hm["z"].shape == (3, 4) + assert hm["z"].dtype == float + np.testing.assert_array_equal(hm["z"], np.arange(12).reshape(3, 4)) + + +def test_heatmap_colorbar_dict_survives_roundtrip(): + fig = _heatmap_contour_figure() + fig2 = Figure.from_dict(fig.to_dict()) + cb = fig2.axes_list[0].heatmaps[0]["colorbar"] + assert cb is not None + assert cb["label"] == "signal" + assert cb["zmin"] == 0.0 and cb["zmax"] == 11.0 + + +def test_points_series_1d_arrays_restored(): + fig = glp.figure(data_prefix="u") + ax = fig.add_subplot(111) + x = np.array([0.0, 1.0, 2.0, 3.0]) + ax.tripcolor(x, x, x * 2, gridsize=(5, 5)) + fig2 = Figure.from_dict(fig.to_dict()) + hm = fig2.axes_list[0].heatmaps[0] + assert hm["z"] is None + for key in ("x", "y", "zpts"): + assert isinstance(hm[key], np.ndarray) + assert hm[key].ndim == 1 + + +def test_regenerated_gle_identical_after_from_dict(): + fig = _heatmap_contour_figure() + text1, files1 = fig._generate_gle_with_files() + fig2 = Figure.from_dict(fig.to_dict()) + text2, files2 = fig2._generate_gle_with_files() + assert text2 == text1 + assert files2 == files1 + + # -- Envelope shape --------------------------------------------------------- @@ -316,6 +387,8 @@ def test_global_data_counter_takes_max_with_in_process_value(monkeypatch): "errorbars", "file_series", "texts", + "heatmaps", + "contours", "passthrough", } From 27b1ebf2eb2e856249dd628da93c66817162ff62 Mon Sep 17 00:00:00 2001 From: Ben Huddart Date: Thu, 23 Jul 2026 12:15:47 +0100 Subject: [PATCH 3/4] test: xfail fitz-dependent GLE compile tests on macOS GLE 4.3.9 built on the macOS ARM CI runners aborts with no error output while rendering scripts containing begin-fitz/begin-contour blocks; the identically pinned Linux build compiles them fine and upstream GLE has no functional changes between v4.3.9 and master, so this is a platform crash in GLE's Akima/contour code rather than a gleplot emission bug. Marks the scattered-data compile test xfail (non-strict) on darwin and adds a tripcolor-only sibling with the same marker so the macOS job's XFAIL/XPASS report shows which GLE stage (fitz vs contour) crashes. Co-Authored-By: Claude Fable 5 --- tests/integration/test_contour_compilation.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/integration/test_contour_compilation.py b/tests/integration/test_contour_compilation.py index fd60e48..f61e2c5 100644 --- a/tests/integration/test_contour_compilation.py +++ b/tests/integration/test_contour_compilation.py @@ -7,6 +7,8 @@ from __future__ import annotations +import sys + import numpy as np import pytest @@ -24,7 +26,39 @@ def _gle_available() -> bool: pytestmark = pytest.mark.skipif(not _gle_available(), reason="GLE binary not available") +# GLE 4.3.9 built on the macOS ARM CI runners aborts during the render pass of +# scripts containing ``begin fitz``/``begin contour`` blocks, with no error +# output (banner "-C-R-" then a nonzero exit). The same scripts compile fine on +# Linux with the identically pinned GLE build, and upstream has no functional +# changes between v4.3.9 and master, so this is a platform-specific crash in +# GLE's Akima/contour code, not a gleplot emission problem. Non-strict xfail: +# records XPASS if a future GLE or runner image fixes it. +_darwin_gle_fitz_crash = pytest.mark.xfail( + sys.platform == "darwin", + reason="GLE 4.3.9 crashes rendering fitz/contour blocks on macOS ARM", + strict=False, +) + + +@_darwin_gle_fitz_crash +def test_tripcolor_only_compiles(tmp_path): + """fitz gridding + colormap without any contour block (isolates which of + the two GLE stages crashes on macOS — see _darwin_gle_fitz_crash).""" + rng = np.random.default_rng(7) + x = rng.uniform(0.0, 5.0, 200) + y = rng.uniform(0.0, 3.0, 200) + z = np.exp(-((x - 2.5) ** 2 + (y - 1.5) ** 2)) + + fig = glp.figure(figsize=(7, 6), data_prefix="tponly") + ax = fig.add_subplot(111) + ax.tripcolor(x, y, z, gridsize=(40, 30), extent=(0.0, 5.0, 0.0, 3.0)) + + out = fig.savefig(str(tmp_path / "tponly.png")) + assert out.exists() + assert out.stat().st_size > 0 + +@_darwin_gle_fitz_crash def test_phase_diagram_like_tripcolor_tricontour_colorbar_compiles(tmp_path): # Synthetic susceptibility-like field chi(T, H): a Neel boundary # T_N(H) = T_N0 * sqrt(1 - (H/Hc)^2) with a ridge in chi at the boundary. From f0a6c975f3636488131f4ab1dc61ee3d9c71f96d Mon Sep 17 00:00:00 2001 From: Ben Huddart Date: Thu, 23 Jul 2026 12:30:04 +0100 Subject: [PATCH 4/4] test: narrow macOS xfail to the GLE contour stage CI experiment result: fitz-only compilation XPASSes on both macOS jobs while the begin-contour script XFAILs, so the platform crash is in GLE's contour stage, not fitz. The tripcolor-only test now runs as a hard assertion everywhere; only the contour-bearing test stays xfail on darwin. Co-Authored-By: Claude Fable 5 --- tests/integration/test_contour_compilation.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tests/integration/test_contour_compilation.py b/tests/integration/test_contour_compilation.py index f61e2c5..f6c1201 100644 --- a/tests/integration/test_contour_compilation.py +++ b/tests/integration/test_contour_compilation.py @@ -27,23 +27,24 @@ def _gle_available() -> bool: pytestmark = pytest.mark.skipif(not _gle_available(), reason="GLE binary not available") # GLE 4.3.9 built on the macOS ARM CI runners aborts during the render pass of -# scripts containing ``begin fitz``/``begin contour`` blocks, with no error -# output (banner "-C-R-" then a nonzero exit). The same scripts compile fine on -# Linux with the identically pinned GLE build, and upstream has no functional -# changes between v4.3.9 and master, so this is a platform-specific crash in -# GLE's Akima/contour code, not a gleplot emission problem. Non-strict xfail: -# records XPASS if a future GLE or runner image fixes it. -_darwin_gle_fitz_crash = pytest.mark.xfail( +# scripts containing a ``begin contour`` block, with no error output (banner +# "-C-R-" then a nonzero exit). Isolated by CI experiment: fitz-only scripts +# (test_tripcolor_only_compiles) pass on the same runners, so the crash is in +# GLE's contour stage specifically. The same scripts compile fine on Linux with +# the identically pinned GLE build, and upstream has no functional changes +# between v4.3.9 and master, so this is a platform-specific crash in GLE, not a +# gleplot emission problem. Non-strict xfail: records XPASS if a future GLE or +# runner image fixes it. +_darwin_gle_contour_crash = pytest.mark.xfail( sys.platform == "darwin", - reason="GLE 4.3.9 crashes rendering fitz/contour blocks on macOS ARM", + reason="GLE 4.3.9 crashes rendering begin-contour blocks on macOS ARM", strict=False, ) -@_darwin_gle_fitz_crash def test_tripcolor_only_compiles(tmp_path): - """fitz gridding + colormap without any contour block (isolates which of - the two GLE stages crashes on macOS — see _darwin_gle_fitz_crash).""" + """fitz gridding + colormap without any contour block (runs everywhere, + including macOS — see _darwin_gle_contour_crash).""" rng = np.random.default_rng(7) x = rng.uniform(0.0, 5.0, 200) y = rng.uniform(0.0, 3.0, 200) @@ -58,7 +59,7 @@ def test_tripcolor_only_compiles(tmp_path): assert out.stat().st_size > 0 -@_darwin_gle_fitz_crash +@_darwin_gle_contour_crash def test_phase_diagram_like_tripcolor_tricontour_colorbar_compiles(tmp_path): # Synthetic susceptibility-like field chi(T, H): a Neel boundary # T_N(H) = T_N0 * sqrt(1 - (H/Hc)^2) with a ridge in chi at the boundary.