From 5c14ce84c647845082904422b6c690f44072b30f Mon Sep 17 00:00:00 2001 From: Farhan Date: Mon, 13 Jul 2026 15:25:05 +0500 Subject: [PATCH 01/17] todo --- docs/matplotlib-shim-todo.md | 454 +++++++++++++++++++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100644 docs/matplotlib-shim-todo.md diff --git a/docs/matplotlib-shim-todo.md b/docs/matplotlib-shim-todo.md new file mode 100644 index 00000000..7eaac1b2 --- /dev/null +++ b/docs/matplotlib-shim-todo.md @@ -0,0 +1,454 @@ +# `xy.pyplot` compatibility audit and TODO + +This document tracks the work required to make `xy.pyplot` a reliable +Matplotlib-flavoured compatibility layer. It deliberately separates the +shim's supported target from full Matplotlib parity: the former is achievable; +the latter would require recreating systems that conflict with xy's small, +fast, browser-oriented design. + +## Reference point and audit method + +- Audit date: 2026-07-13. +- Upstream checkout: `ignore/matplotlib`. +- Upstream revision: `bde111fb4e`, described by Git as + `v3.11.0-348-gbde111fb4e` (2026-07-10). +- Shim: `python/xy/pyplot/`. +- Contract test: `tests/pyplot/test_axes_charts.py::` + `test_official_matplotlib_311_2d_plotting_surface_is_complete`. +- Executable examples: `tests/pyplot/corpus/`. + +Name-level comparisons below were made from public top-level functions in +upstream `matplotlib/pyplot.py`, and public declarations on upstream `Axes`, +`_AxesBase`, `Figure`, and `FigureBase`. They are useful breadth indicators, +not semantic compatibility scores: renderer lifecycle methods, properties, +and APIs deliberately outside xy's design are included in the upstream sets. + +## Current baseline + +| Surface | Present in `xy.pyplot` | Notes | +|---|---:|---| +| Declared Matplotlib 3.11 2-D plotting-method contract | 66 / 66 (100%) | Name presence on both `Axes` and stateful `pyplot` | +| Public upstream `pyplot` functions | 92 / 165 (56%) | 73 names absent; see appendix A | +| Public upstream `Axes`/`_AxesBase` declarations | 89 / 193 (46%) | 104 names absent; see appendix B | +| Public upstream `Figure`/`FigureBase` declarations | 13 / 73 (18%) | 60 names absent; see appendix C | +| Compatibility corpus | 53 scripts | No expected failures | +| Current shim suite | 157 passed, 7 skipped | Skips require real Matplotlib | + +The existing 100% statement is intentionally narrow. It means every method in +the selected Matplotlib 3.11 2-D **Plotting** inventory exists. It does not mean +that every keyword, returned Artist, transform, layout rule, backend feature, +or rendered pixel matches Matplotlib. + +## Definition of done for the supported shim + +The shim can be called complete for ordinary 2-D scripts when: + +- [ ] Every documented supported call has geometry and return-value tests, not + only an `hasattr` check. +- [ ] The same compatibility corpus runs against xy and the pinned Matplotlib + reference in CI. +- [ ] Material data, limits, bins, levels, labels, container shapes, and image + dimensions are compared with Matplotlib where exact parity is intended. +- [ ] A representative visual suite performs perceptual/difference checks, + with explicit tolerances for the different renderer. +- [ ] No material keyword is silently discarded. It is implemented, + documented as an approximation, or rejected with a helpful error. +- [ ] The common state, axes, figure, and mutation APIs listed in the P1/P2 + sections below work without installing Matplotlib. +- [ ] Optional support for real Matplotlib objects is tested in a dedicated CI + environment. +- [ ] Public compatibility boundaries and intentional exclusions are current + in both this document and `docs/matplotlib-compat.md`. + +## P0 — make the compatibility claim measurable + +- [ ] Add a CI job with the pinned/reference-compatible Matplotlib installed so + the seven skipped tests in `test_launch_compat.py` always run. +- [ ] Run every corpus script through both `xy.pyplot` and + `matplotlib.pyplot`; isolate process-global pyplot state between cases. +- [ ] Record and compare semantic oracles per chart family: + - line/scatter data, masks, colors, sizes, and default color-cycle movement; + - bar rectangles, category positions, stacking bases, and labels; + - histogram counts, edges, density, cumulative and stacked outputs; + - image extents, origin, normalization domain and RGBA behavior; + - contour levels and paths; triangular topology and mesh bounds; + - vector endpoints, streamline seeds, colors and widths; + - returned tuples, containers, collections, texts and removable handles; + - axis domains, reversed axes, ticks, labels and shared-axis behavior. +- [ ] Add representative Matplotlib-versus-xy PNG comparisons. Use perceptual + tolerances and geometry masks rather than requiring identical antialiasing. +- [ ] Turn the hard-coded 66-name inventory into a generated, reviewed snapshot + from the pinned upstream documentation/source so upstream additions are + visible as a deliberate snapshot diff. +- [ ] Add coverage for every supported method, not just every broad family. +- [ ] Add a guard that detects accepted-and-discarded material keyword values. +- [ ] Publish the compatibility matrix from test metadata so documentation + cannot drift from executable coverage. + +## P1 — correctness gaps inside the advertised surface + +### Remove accidental dependency on installed Matplotlib + +- [ ] Make `Axes.get_position()` return an xy-owned lightweight bbox instead of + dynamically importing `matplotlib.transforms.Bbox`. +- [ ] Provide dependency-free behavior for transformed images, collections, + normalization and streamplot paths, or clearly separate optional + Matplotlib-object interop from the dependency-free shim. +- [ ] Test every public method in an environment where importing `matplotlib` + fails; calling an advertised method must not accidentally require it. +- [ ] Keep the existing lightweight-import boundary: importing `xy.pyplot` + must not load Matplotlib, the widget stack, or browser machinery. + +### Implement or reject current no-ops + +- [ ] Implement meaningful `tight_layout()` behavior or document it as an + accepted compatibility no-op with a tested layout guarantee. +- [ ] Implement `subplots_adjust()` parameters (`left`, `right`, `top`, + `bottom`, `wspace`, `hspace`) for HTML, PNG, and SVG grids. +- [ ] Implement `Figure.autofmt_xdate()` label rotation/alignment. +- [ ] Implement `Axes.margins()` and make it affect automatic domains. +- [ ] Implement `Axes.set_position()` and preserve the requested figure rect. +- [ ] Implement `Axes.set_anchor()` or reject unsupported anchor modes. +- [ ] Finish `axis("equal")`, `axis("scaled")`, `axis("tight")`, and related + aspect/domain behavior instead of merely accepting policy names. +- [ ] Make `tick_params()` honor supported visibility, side, length, width, + color, direction and label styling arguments; reject the remainder. +- [ ] Make `grid(which=..., axis=..., **style)` select and style the requested + grid rather than toggling the entire chart. +- [ ] Make `legend()` honor supported font/label/title/frame placement options; + explicitly reject options that cannot map to the xy legend. +- [ ] Make `set_xlabel()`, `set_ylabel()`, `set_title()`, and `suptitle()` honor + supported font, position and padding arguments. +- [ ] Make `Axes.set(**kwargs)` reject unknown setters instead of silently + skipping them. + +### Stop dropping visible artist/style mutations + +- [ ] Implement `set_markerfacecolor`, `set_markeredgecolor`, and + `set_markersize` on compatible handles. +- [ ] Implement or loudly reject dash/solid cap styles and `set_gapcolor`. +- [ ] Support `set_xdata`/`set_ydata` for segment-backed line handles where the + original logical data can be retained. +- [ ] Preserve annotation `arrowprops`, bbox, alignment, rotation, family and + weight instead of reducing annotations to plain text. +- [ ] Preserve text vertical alignment, font weight/family and rotation. +- [ ] Implement bar `align="edge"`; do not approximate it as centered. +- [ ] Audit marker fill styles, custom marker paths, join styles, clipping, + hatches, z-order and transforms across all returned handles. + +## P2 — common pyplot/Axes/Figure workflow compatibility + +These should be implemented before backend-management or GUI APIs because they +appear frequently in ordinary scripts and notebooks. + +### Stateful pyplot and figure management + +- [ ] `plt.clf()` and `Figure.clear()`/`Figure.clf()`. +- [ ] `plt.cla()` and `Axes.clear()`/`Axes.cla()`. +- [ ] `plt.axes()` and `plt.delaxes()`/`Figure.delaxes()`. +- [ ] `plt.fignum_exists()`, `get_fignums()`, and `get_figlabels()`. +- [ ] `plt.figtext()`/`Figure.text()` and `plt.figlegend()`/`Figure.legend()`. +- [ ] `plt.twiny()` and `Axes.twiny()`. +- [ ] `Figure.sca()` and consistent current-Axes behavior after deletion. +- [ ] Figure getters/setters for DPI, face/edge color and size. +- [ ] `Figure.supxlabel()` and `Figure.supylabel()`. +- [ ] `Figure.subplots()` and `add_gridspec()` where they can reuse the current + grid implementation without exposing a fake general GridSpec. + +### Limits, autoscaling, ticks and axes helpers + +- [ ] `plt.autoscale()`, `Axes.autoscale()`, `autoscale_view()`, and `relim()`. +- [ ] `get/set_xbound`, `get/set_ybound`, x/y margins, and sticky-edge behavior. +- [ ] `ticklabel_format()`. +- [ ] `minorticks_on()` and `minorticks_off()` with an explicit minor-tick model. +- [ ] `get_xlabel`, `get_ylabel`, `get_title`, `get_xaxis`, and `get_yaxis`. +- [ ] `get_legend()` and `get_legend_handles_labels()`. +- [ ] `set_prop_cycle()` beyond the fixed default color sequence. +- [ ] `secondary_xaxis()` and `secondary_yaxis()` if secondary-axis layout is + promoted into supported scope; otherwise keep them explicitly excluded. + +### Image, property and convenience helpers + +- [ ] `imread()` and `imsave()` for common PNG/JPEG inputs and outputs. +- [ ] `setp()`, `getp()`, `get()`, and a deliberately bounded `findobj()`. +- [ ] `rc_context()` and `rcdefaults()`. +- [ ] Named colormap convenience functions such as `viridis()`, `plasma()`, + `gray()`, and `set_cmap()` if gallery compatibility justifies them. +- [ ] `subplot2grid()` as a wrapper over the supported grid model. +- [ ] `box()` and `axes()` convenience behavior. + +## P3 — plotting-method option depth + +The following values are currently unsupported, approximated, or consumed and +discarded in at least one shim path. For each item, implement the semantics or +reject it clearly; do not claim keyword-level compatibility merely because the +method accepts the call. + +### Lines, points, rules and fills + +- [ ] `plot`: `scalex`, `scaley`, marker face/edge styling, fillstyle, cap/join + styles, `markevery`, general transforms and all draw styles. +- [ ] `scatter`: exact `vmin`/`vmax`/norm interaction, linewidth/stroke arrays, + custom marker paths and full nonfinite color handling. +- [ ] `hlines`/`vlines`: linestyles, collection semantics, transforms and + per-segment styles. +- [ ] `fill`/`fill_between`/`fill_betweenx`: edge rendering, interpolation at + mask crossings, transforms and complete step semantics. +- [ ] `arrow`/`axline`: head shape/overhang, transforms and style fidelity. +- [ ] `axhline`/`axvline`/spans: linestyles and transform fidelity. +- [ ] `errorbar`: upper/lower limit flags, cap thickness, bars-above ordering, + independent line styles, errorevery and full container semantics. + +### Bars, histograms and distributions + +- [ ] `bar`/`barh`: edge alignment, heterogeneous widths, complete x/y error + styling, hatch, log mode and unit-aware/category behavior. +- [ ] `bar_label`: label type, custom callable formatting, padding/font + properties and complete horizontal/negative-bar placement. +- [ ] `hist`: every histtype, heterogeneous bins, rwidth, log mode, bottom + arrays and exact returned patches. +- [ ] `hist2d(norm=...)` and complete normalization/colorizer support. +- [ ] `hexbin(C=..., reduce_C_function=...)`, `mincnt`, marginals, norm, + colorizer and explicit vmin/vmax. +- [ ] `boxplot`: notches, custom whiskers, bootstrap, user medians, confidence + intervals, cap visibility/width, autorange and component properties. +- [ ] `bxp`: component style parity, labels/ticks, cap widths and returned + component geometry. +- [ ] `violinplot`/`violin`: bandwidth methods, quantiles, side, extrema, + points and component styling. +- [ ] `ecdf`: exact weights/complementary/orientation/compression behavior and + returned Artist parity. + +### Images, meshes and contours + +- [ ] `imshow`: interpolation modes/stages, transforms, clipping, alpha arrays, + filter radius, resampling, colorizer and norm variants without requiring + Matplotlib. +- [ ] `pcolor`, `pcolorfast`, `pcolormesh`: shading modes, edge/line styling, + antialiasing, snap, rasterized behavior and norm/colorizer variants. +- [ ] `contour`/`contourf`: origin, extent, linestyles, corner masks, extend, + hatches, locators, norms and filled-region topology parity. +- [ ] `clabel`: inline path cutting, formatting, manual positions, rotation and + complete text styling. +- [ ] `tripcolor`/`tricontour`/`tricontourf`: norms, masks, shading, + antialiasing, hatches, extends and triangulation-object interoperability. +- [ ] `spy` and `matshow`: sparse inputs, precision semantics and return types. + +### Pie, table, spectra and vector fields + +- [ ] `pie`: shadow, frame, rotated labels, hatches, explode/autopct placement, + normalize behavior, text properties and wedge properties. +- [ ] `table`: cell/row/column alignment, placement, edges, sizing, colors and + mutable cell objects. +- [ ] Spectral methods: window, detrending, sides, padding, frequency scaling, + modes, scale and return-value parity. +- [ ] `stem`, `stairs`, `eventplot`, and `stackplot`: complete style/container + behavior, hatches, orientation and baselines. +- [ ] `quiver`: units, head geometry, pivots, angles, scaling, norm, z-order and + scalar-mappable behavior. +- [ ] `barbs`: increments, flags, rounding, empty barbs, flips, colors and + sizes rather than a quiver approximation. +- [ ] `quiverkey`: coordinates, label positions, fonts and sizing. +- [ ] `streamplot`: density, integration direction/length, broken-streamline + behavior, arrows, transforms, z-order and consistent dependency-free + integration. + +### Scales, units and dates + +- [ ] Implement `symlog`, `logit`, and `asinh` or retain loud errors and add + explicit compatibility tests/documentation for each. +- [ ] Honor log base/subs/nonpositive options in `loglog`, `semilogx`, and + `semilogy`. +- [ ] Define a bounded units/converter story for datetime, timedelta and common + categorical inputs; do not attempt the entire Matplotlib units registry + unless real usage requires it. +- [ ] Add date locators/formatters sufficient for ordinary time-series plots. + +## P4 — Artist, collection, transform and container compatibility + +- [ ] Expose bounded `ax.lines`, `collections`, `patches`, `texts`, `images`, + `artists`, `tables`, and `containers` views over shim-owned entries. +- [ ] Add `get_children()` with stable ownership and removal semantics. +- [ ] Add `add_line`, `add_container`, `add_table`, and wider `add_patch` / + `add_collection` mappings for common Matplotlib objects. +- [ ] Complete `Line2D`, `PathCollection`, image, contour, bar, stem, errorbar, + pie, table and streamplot return-object surfaces used by gallery code. +- [ ] Add common Artist getters/setters and aliases, including visibility, + z-order, clipping, transform, label, alpha and rasterization flags where + meaningful. +- [ ] Define lightweight xy-owned `Bbox`, identity/affine transform and + coordinate-space objects sufficient for supported calls. +- [ ] Support data, axes-fraction, figure-fraction and offset point/pixel + coordinate systems consistently across HTML, PNG and SVG. +- [ ] Decide which external Matplotlib patches, collections, transforms, + normalizers and triangulations are supported as optional adapters, then + test that exact allowlist. +- [ ] Reject arbitrary unsupported Artists with errors that identify the + closest supported primitive. + +## P5 — rcParams, styles, colors and export + +- [ ] Audit which of the currently listed rcParams actually affect output; + listing a default must not imply behavior that is ignored. +- [ ] Add the high-frequency rcParams for axes face/spines, font family/size, + label/title sizes, tick styling, legend, savefig, image origin and color + cycle. +- [ ] Add nested `rc_context()` restoration and `rcdefaults()` tests. +- [ ] Support style dictionaries and a small documented style-sheet allowlist, + or keep `style.use()` restricted and report unsupported styles precisely. +- [ ] Expand color parsing only where xy's CSS/native pipeline can preserve the + value; test named colors, alpha, under/over/bad and reversed colormaps. +- [ ] Add explicit behavior for file-like export with a declared format. +- [ ] Decide whether JPEG, WebP and PDF export belong in supported scope. + Implement selected formats or produce actionable `NotImplementedError`s. +- [ ] Test metadata, transparent backgrounds, face/edge colors, bounding boxes, + padding, orientation and DPI semantics for `savefig()`. + +## P6 — typing, documentation and maintenance + +- [ ] Add a useful typed public surface for `xy.pyplot`, `Axes`, `Figure`, + common Artists, containers and return tuples; reduce broad `Any` usage. +- [ ] Add API documentation generated from the supported compatibility matrix. +- [ ] Fix the stale `docs/chart-roadmap.md` rows that still call pie, vector + fields and irregular-grid families planned even though the shim exposes + implementations. +- [ ] Document approximation levels: exact geometry, equivalent semantics, + visual approximation, accepted no-op, optional interop, and unsupported. +- [ ] Add a compatibility changelog tied to upstream Matplotlib releases. +- [ ] Re-run the source inventory whenever the pinned Matplotlib revision moves. +- [ ] Keep all shim code inside `python/xy/pyplot/` and preserve the one-way + dependency boundary enforced by `tests/pyplot/test_boundaries.py`. + +## Explicitly out of scope + +These exclusions are intentional unless a future project explicitly promotes +one of them. Missing names belonging primarily to these systems should not be +treated as ordinary shim bugs. + +### Renderer and backend replacement + +- Matplotlib renderer internals, draw traversal, stale propagation, graphics + contexts, backend canvases, renderer-specific filters and exact pixel parity. +- Backend selection and manager APIs: `switch_backend`, `new_figure_manager`, + `get_current_fig_manager`, REPL display-hook installation and backend toolbars. +- Reproducing Agg, PDF, PS, SVG, PGF, Cairo, Qt, Tk, GTK, wx, macOS or WebAgg. + +### GUI event loops and blocking interaction + +- `ion`, `ioff`, `isinteractive`, `pause`, `ginput`, `waitforbuttonpress`, GUI + main loops and Matplotlib callback registry compatibility. +- Native-window pan/zoom tool state, toolbar modes and backend keymaps. +- Matplotlib-style picking/event objects beyond xy's own browser interaction + and selection APIs. + +### Full Artist and transform graph + +- Arbitrary third-party Artist subclasses and arbitrary draw overrides. +- Complete transform composition, blended transforms, path effects, clipping + graphs and layout bbox negotiation. +- Full introspection parity for every Artist property. +- Blitting and renderer-driven animation lifecycle. + +### Projection and domain systems + +- 3-D plotting and `mplot3d`. +- Polar, radar, ternary, geographic and custom projection registration. +- Cartopy/Basemap integration. +- Full TeX/MathText/PGF layout parity and Matplotlib font-manager behavior. + +### Animation + +- `FuncAnimation`, `ArtistAnimation`, movie writers and blitting. xy's native + streaming/update APIs are the preferred model. + +### Every Matplotlib module + +This shim targets plotting calls and common script ergonomics. It does not aim +to replace `matplotlib.artist`, `collections`, `patches`, `path`, `transforms`, +`ticker`, `dates`, `units`, `tri`, `animation`, `widgets`, `backend_*`, or +`toolkits` as import-compatible standalone modules. Small compatibility objects +may be provided inside `xy.pyplot` when required by supported workflows. + +## Appendix A — missing public upstream `pyplot` functions + +This is the complete name-level difference at the reference revision (73 +names). Several are intentionally out of scope; the P2 section identifies the +high-value subset. + +```text +autoscale autumn axes bone box cla clf clim connect cool copper delaxes +disconnect draw draw_if_interactive figimage figlegend fignum_exists figtext +findobj flag gci get get_current_fig_manager get_figlabels get_fignums +get_plot_commands getp ginput gray hot hsv imread imsave inferno +install_repl_displayhook ioff ion isinteractive jet locator_params magma +margins minorticks_off minorticks_on new_figure_manager nipy_spectral pause +pink plasma polar prism rc_context rcdefaults rgrids sci set_cmap set_loglevel +setp spring subplot2grid subplot_tool summer switch_backend thetagrids +tick_params ticklabel_format twiny uninstall_repl_displayhook viridis +waitforbuttonpress winter xkcd +``` + +## Appendix B — missing public upstream `Axes`/`_AxesBase` declarations + +This is the complete name-level difference at the reference revision (104 +names). It includes properties and renderer/navigation methods as well as +ordinary user APIs. + +```text +add_child_axes add_container add_line add_table apply_aspect artists autoscale +autoscale_view can_pan can_zoom cla clear collections contains contains_point +drag_pan draw draw_artist end_pan format_coord format_xdata format_ydata +get_adjustable get_anchor get_aspect get_autoscale_on get_axes_locator +get_axisbelow get_box_aspect get_children get_data_ratio +get_default_bbox_extra_artists get_facecolor get_forward_navigation_events +get_frame_on get_gridspec get_images get_legend get_legend_handles_labels +get_lines get_navigate get_navigate_mode get_rasterization_zorder +get_shared_x_axes get_shared_y_axes get_subplotspec get_tightbbox get_title +get_window_extent get_xaxis get_xaxis_text1_transform get_xaxis_text2_transform +get_xbound get_xlabel get_xmargin get_yaxis get_yaxis_text1_transform +get_yaxis_text2_transform get_ybound get_ylabel get_ymargin has_data images +in_axes indicate_inset lines minorticks_off minorticks_on patches +redraw_in_frame relim reset_position secondary_xaxis secondary_yaxis +set_adjustable set_autoscale_on set_axes_locator set_axis_on set_axisbelow +set_box_aspect set_facecolor set_figure set_forward_navigation_events +set_frame_on set_navigate set_navigate_mode set_prop_cycle +set_rasterization_zorder set_subplotspec set_xbound set_xmargin set_ybound +set_ymargin set_zorder sharex sharey start_pan tables texts ticklabel_format +twiny update_datalim use_sticky_edges viewLim +``` + +## Appendix C — missing public upstream `Figure`/`FigureBase` declarations + +This is the complete name-level difference at the reference revision (60 +names). + +```text +add_artist add_axobserver add_gridspec add_subfigure align_labels align_titles +align_xlabels align_ylabels axes clear clf contains delaxes draw draw_artist +draw_without_rendering get_children get_constrained_layout +get_constrained_layout_pads get_default_bbox_extra_artists get_dpi +get_edgecolor get_facecolor get_figheight get_figure get_figwidth get_frameon +get_layout_engine get_linewidth get_size_inches get_suptitle get_supxlabel +get_supylabel get_tight_layout get_tightbbox get_window_extent ginput legend +number pick sca set_canvas set_constrained_layout set_constrained_layout_pads +set_dpi set_edgecolor set_facecolor set_figheight set_figure set_figwidth +set_frameon set_layout_engine set_linewidth set_tight_layout subfigures subplots +supxlabel supylabel text waitforbuttonpress +``` + +## Appendix D — supported 2-D plotting-method inventory + +These 66 names currently satisfy the documented name-presence contract on both +the shim `Axes` and stateful `xy.pyplot` namespace. Their option-depth work is +tracked above. + +```text +plot errorbar scatter step loglog semilogx semilogy fill_between fill_betweenx +bar barh bar_label grouped_bar stem eventplot pie pie_label stackplot +broken_barh vlines hlines fill axhline axhspan axvline axvspan axline acorr +angle_spectrum cohere csd magnitude_spectrum phase_spectrum psd specgram xcorr +ecdf boxplot violinplot bxp violin hexbin hist hist2d stairs clabel contour +contourf imshow matshow pcolor pcolorfast pcolormesh spy tripcolor triplot +tricontour tricontourf annotate text table arrow barbs quiver quiverkey +streamplot +``` From e87141735826b5bf676a065044c1ad0a6585695d Mon Sep 17 00:00:00 2001 From: Farhan Ali Raza <62690310+FarhanAliRaza@users.noreply.github.com> Date: Tue, 14 Jul 2026 01:37:18 +0500 Subject: [PATCH 02/17] ENG-10167 Expand xy.pyplot shim: plot-family depth, scales, export chrome, and pinned Matplotlib 3.11 evidence (#36) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Honor pyplot grid and legend options * feat(pyplot): pin Matplotlib 3.11 reference and audit compat contracts Replace the hard-coded plotting-surface assertion with a reviewed snapshot of the pinned upstream revision (bde111fb4e), kept in sync by scripts/sync_matplotlib_compat.py, which also generates the method-by-method compatibility matrix from compatibility.json and the executable corpus. A dedicated CI job checks the snapshot against a pinned Matplotlib checkout and runs the dual-engine corpus and optional-interop tests. Tighten the compatibility contract itself: formerly silent option discards now raise loudly (pie/quiver/barbs/contour/table/norm options and more), options the marks can honor are implemented instead of rejected (contour extent, plain Normalize, dashed stem/eventplot/ triplot, streamplot start_points/direction/widths, stepfilled hist), and streamplot always uses the shim's own bounded integrator so output no longer depends on whether Matplotlib is installed. Documented approximation levels, the accepted visual approximations, and a compatibility changelog; added contract tests covering rc chrome, color export, transforms, P3 options, and silent-drop regressions. * feat(pyplot): compare engines pixel-for-pixel and honor export chrome Turn the dual-engine evidence from crash-free execution into real comparison: all 54 corpus cases now render PNGs in both engines and compare normalized ink density and bounding-box geometry, semantic oracles extend to contours, triangulations, vector directions, masked scatter, RGBA images, and removable handles, and thresholds gain negative controls proving blank or wrong output fails. A mechanical scan of every public adapter rejects unexplained accepted-and-dropped options (explicit `compat-noop:` rationales required), and the reference CI job moves to the released matplotlib==3.11.0 wheel. On the export side, implement the common savefig options instead of rejecting them: bbox_inches="tight", transparent=, facecolor=, and PNG/SVG metadata= via standards-compliant text chunks. Static PNG export now honors the axes background token, and multi-panel HTML/SVG/PNG composition applies figure backgrounds and styled suptitles (size/weight/family/color/position) instead of dropping the kwargs. * feat(pyplot): stats-plot depth, nonlinear scales, and post-audit fixes Boxplot notches/bootstrap/user statistics, violin KDE bandwidths/ quantiles/sides, hexbin C aggregation, symlog/logit/asinh scales, secondary axes, and affine data transforms — with the divergences an adversarial cross-engine audit found in both this tranche and the previous one repaired rather than shipped silently: - scatter drops rows masked in x/y/s; fill_between(interpolate=True) draws single-point where-regions; imsave colormaps original values; set_cmap validates names and feeds imshow/scatter defaults; usermedians no longer shift notch CIs; boxplot sym works (empty string suppresses fliers); hexbin mincnt and C aggregation share one bin membership; violinplot survives constant data. - Loud rejections replace silent discards: bxp component linestyles, secondary-axis set_ticks extras, fraction-space transforms on data artists, html-format metadata; singular transforms fail at set_transform time. - Export: SVG/HTML honor savefig(facecolor=); single-chart SVG keeps the suptitle; suptitle y maps as a figure fraction; non-Latin-1 PNG metadata keys raise ValueError. - Scales: logit masks values at/outside (0,1); auto ticks refresh as data arrives and reset when the scale returns to linear; explicit ticks keep data-unit labels. - Docs state the real boundaries (rectangular hexbin geometry, HTML-only secondary axes, xy-owned tick locators, snapshot pin no longer CI-compared); the corrupted todo section is rewritten; the tight-bbox and negative-control tests can now actually fail. Excludes examples/pdsh notebooks (in-flight, currently failing ruff). * feat(pyplot): close the top PDSH benchmark gaps The Python Data Science Handbook ch. 4 benchmark (import-swap over 14 notebooks) scored 121/171 runnable cells; the failures clustered on a handful of missing surfaces rather than rendering gaps. This closes them: xy-owned tick locators/formatters resolved against live data limits, style.context with the stock sheets reduced to the supported rcParams subset, RdGy/jet engine tables, gci/clim with colorbars that track their mappable, GridSpec slice spans via explicit figure rectangles, and the pandas-facing Axes surface (get_figure, get_lines, shared-axes groupers, Period coordinates). Scatter vmin/vmax previously crashed at render; they now flow into a real color_domain on the engine's color channel. grid(linestyle= 'solid') injected an invalid style value and broke every later export of the axes. subplot()/axes() silently dropped keyword arguments. Benchmark after: 154/171 (90%); 147/154 (95%) excluding the out-of-scope 3-D notebook; zero savefig errors on passing cells. * docs(examples): ship the PDSH notebooks as a measured compat example Add the Python Data Science Handbook matplotlib-chapter code cells (MIT; the CC-BY-NC-ND prose is omitted) under examples/pdsh with only the import swapped to xy.pyplot, plus their data files and a measured scorecard: 154/171 runnable cells (90%) after the gap-closing pass in the previous commit, 147/154 (95%) excluding the out-of-scope 3-D notebook, with matplotlib 3.11 at 171/171 on the identical code. The notebooks keep upstream's code style, so they are exempted from the repo lint rules that would amount to restyling third-party code. --- .github/workflows/ci.yml | 23 + docs/chart-roadmap.md | 18 +- docs/matplotlib-compat-changelog.md | 115 + docs/matplotlib-compat-matrix.md | 138 + docs/matplotlib-compat.md | 98 +- docs/matplotlib-shim-todo.md | 493 +- examples/pdsh/.gitignore | 1 + examples/pdsh/README.md | 65 + examples/pdsh/data/births.csv | 15548 +++++++ examples/pdsh/data/california_cities.csv | 483 + examples/pdsh/data/marathon-data.csv | 37251 ++++++++++++++++ ...dsh_04_00_introduction_to_matplotlib.ipynb | 226 + .../pdsh/pdsh_04_01_simple_line_plots.ipynb | 259 + .../pdsh_04_02_simple_scatter_plots.ipynb | 176 + examples/pdsh/pdsh_04_03_errorbars.ipynb | 124 + ...pdsh_04_04_density_and_contour_plots.ipynb | 139 + .../pdsh_04_05_histograms_and_binnings.ipynb | 211 + .../pdsh/pdsh_04_06_customizing_legends.ipynb | 230 + .../pdsh_04_07_customizing_colorbars.ipynb | 270 + .../pdsh/pdsh_04_08_multiple_subplots.ipynb | 211 + .../pdsh/pdsh_04_09_text_and_annotation.ipynb | 299 + .../pdsh/pdsh_04_10_customizing_ticks.ipynb | 242 + .../pdsh_04_11_settings_and_stylesheets.ipynb | 316 + ...dsh_04_12_three_dimensional_plotting.ipynb | 310 + ...dsh_04_14_visualization_with_seaborn.ipynb | 475 + js/src/10_colormaps.js | 2 + js/src/51_annotations.js | 4 + pyproject.toml | 5 + python/xy/_raster.py | 75 +- python/xy/_svg.py | 81 +- python/xy/channels.py | 16 +- python/xy/components.py | 14 + python/xy/marks.py | 45 +- python/xy/pyplot/__init__.py | 607 +- python/xy/pyplot/_artists.py | 299 +- python/xy/pyplot/_axes.py | 1949 +- python/xy/pyplot/_colors.py | 171 +- python/xy/pyplot/_grid.py | 73 +- python/xy/pyplot/_mplfig.py | 645 +- python/xy/pyplot/_plot_types.py | 1446 +- python/xy/pyplot/_rc.py | 70 +- python/xy/pyplot/_state.py | 21 +- python/xy/pyplot/_ticker.py | 204 + python/xy/pyplot/_transforms.py | 111 + python/xy/pyplot/_translate.py | 8 +- python/xy/static/index.js | 6 + python/xy/static/standalone.js | 6 + scripts/sync_matplotlib_compat.py | 186 + scripts/verify_ci_workflow.py | 71 + tests/pyplot/compatibility.json | 27 + .../corpus/53_matplotlib_311_plotting.py | 11 +- .../corpus/54_plotting_method_coverage.py | 32 + tests/pyplot/matplotlib_311_plotting.json | 29 + tests/pyplot/test_advanced_compatibility.py | 82 + tests/pyplot/test_artist_mutations.py | 80 + .../pyplot/test_artist_transform_contracts.py | 102 + tests/pyplot/test_axes_charts.py | 88 +- tests/pyplot/test_axes_helpers.py | 116 + tests/pyplot/test_axes_layout.py | 123 + tests/pyplot/test_boundaries.py | 26 + tests/pyplot/test_compatibility_metadata.py | 90 + tests/pyplot/test_figure_state.py | 130 + tests/pyplot/test_grid_legend_contracts.py | 70 + tests/pyplot/test_layout_noops.py | 55 + tests/pyplot/test_p3_option_contracts.py | 417 + tests/pyplot/test_pdsh_gap_features.py | 418 + tests/pyplot/test_pyplot_state_management.py | 53 + tests/pyplot/test_rc_chrome_contracts.py | 98 + .../pyplot/test_rc_color_export_contracts.py | 114 + tests/pyplot/test_reference_corpus.py | 136 + tests/pyplot/test_reference_semantics.py | 268 + tests/pyplot/test_silent_drop_regressions.py | 378 + tests/pyplot/test_visible_style_contracts.py | 84 + tests/test_verify_ci_workflow.py | 11 + 74 files changed, 65904 insertions(+), 970 deletions(-) create mode 100644 docs/matplotlib-compat-changelog.md create mode 100644 docs/matplotlib-compat-matrix.md create mode 100644 examples/pdsh/.gitignore create mode 100644 examples/pdsh/README.md create mode 100644 examples/pdsh/data/births.csv create mode 100644 examples/pdsh/data/california_cities.csv create mode 100644 examples/pdsh/data/marathon-data.csv create mode 100644 examples/pdsh/pdsh_04_00_introduction_to_matplotlib.ipynb create mode 100644 examples/pdsh/pdsh_04_01_simple_line_plots.ipynb create mode 100644 examples/pdsh/pdsh_04_02_simple_scatter_plots.ipynb create mode 100644 examples/pdsh/pdsh_04_03_errorbars.ipynb create mode 100644 examples/pdsh/pdsh_04_04_density_and_contour_plots.ipynb create mode 100644 examples/pdsh/pdsh_04_05_histograms_and_binnings.ipynb create mode 100644 examples/pdsh/pdsh_04_06_customizing_legends.ipynb create mode 100644 examples/pdsh/pdsh_04_07_customizing_colorbars.ipynb create mode 100644 examples/pdsh/pdsh_04_08_multiple_subplots.ipynb create mode 100644 examples/pdsh/pdsh_04_09_text_and_annotation.ipynb create mode 100644 examples/pdsh/pdsh_04_10_customizing_ticks.ipynb create mode 100644 examples/pdsh/pdsh_04_11_settings_and_stylesheets.ipynb create mode 100644 examples/pdsh/pdsh_04_12_three_dimensional_plotting.ipynb create mode 100644 examples/pdsh/pdsh_04_14_visualization_with_seaborn.ipynb create mode 100644 python/xy/pyplot/_ticker.py create mode 100644 python/xy/pyplot/_transforms.py create mode 100644 scripts/sync_matplotlib_compat.py create mode 100644 tests/pyplot/compatibility.json create mode 100644 tests/pyplot/corpus/54_plotting_method_coverage.py create mode 100644 tests/pyplot/matplotlib_311_plotting.json create mode 100644 tests/pyplot/test_advanced_compatibility.py create mode 100644 tests/pyplot/test_artist_mutations.py create mode 100644 tests/pyplot/test_artist_transform_contracts.py create mode 100644 tests/pyplot/test_axes_helpers.py create mode 100644 tests/pyplot/test_axes_layout.py create mode 100644 tests/pyplot/test_compatibility_metadata.py create mode 100644 tests/pyplot/test_figure_state.py create mode 100644 tests/pyplot/test_grid_legend_contracts.py create mode 100644 tests/pyplot/test_layout_noops.py create mode 100644 tests/pyplot/test_p3_option_contracts.py create mode 100644 tests/pyplot/test_pdsh_gap_features.py create mode 100644 tests/pyplot/test_pyplot_state_management.py create mode 100644 tests/pyplot/test_rc_chrome_contracts.py create mode 100644 tests/pyplot/test_rc_color_export_contracts.py create mode 100644 tests/pyplot/test_reference_corpus.py create mode 100644 tests/pyplot/test_reference_semantics.py create mode 100644 tests/pyplot/test_silent_drop_regressions.py create mode 100644 tests/pyplot/test_visible_style_contracts.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ccfd6f3..dd5e8801 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,29 @@ concurrency: cancel-in-progress: true jobs: + matplotlib_reference: + name: Matplotlib 3.11 reference compatibility + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.2.2 + - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 + - name: Install xy and released reference wheel + run: | + uv venv .venv + uv pip install -p .venv/bin/python -e ".[dev]" + uv pip install -p .venv/bin/python "matplotlib==3.11.0" + - name: Verify released reference and reviewed snapshot + run: | + .venv/bin/python -c "import matplotlib; assert matplotlib.__version__ == '3.11.0'" + .venv/bin/python scripts/sync_matplotlib_compat.py --check + - name: Run optional-interoperability and dual-engine corpus tests + env: + MPLBACKEND: Agg + run: | + .venv/bin/pytest -q tests/pyplot/test_launch_compat.py + .venv/bin/pytest -q tests/pyplot/test_reference_corpus.py + .venv/bin/pytest -q tests/pyplot/test_reference_semantics.py + test: name: Test (Rust + Python + JS) runs-on: ubuntu-latest diff --git a/docs/chart-roadmap.md b/docs/chart-roadmap.md index 7170adf8..9716d1ff 100644 --- a/docs/chart-roadmap.md +++ b/docs/chart-roadmap.md @@ -21,8 +21,10 @@ entry point, not the boundary of the product. The current implemented surface is **line**, **scatter**, **area**, **histogram**, **bar/column**, **heatmap**, **error bars/bands**, -**box/violin/ECDF**, **hexbin/contour**, **step/stairs/stem**, and -**faceted small multiples**. Scatter already covers direct +**box/violin/ECDF**, **hexbin/contour**, **step/stairs/stem**, **pie/donut**, +**scientific vector fields**, **irregular triangular meshes**, and **faceted +small multiples**. The last three families are exposed through the +Matplotlib-flavoured shim over shared xy primitives. Scatter already covers direct points, color/size channels, GPU picking, selection, and Tier-2 density aggregation. Line and area cover direct and M4-decimated time series. Histogram and bar/column share the instanced rectangle renderer; heatmap ships a compact @@ -99,7 +101,7 @@ not fall out of sight. | 3 | Bar / column | vertical bar, horizontal bar, grouped, stacked, normalized stacked, diverging bar | Implemented core | `fc.bar(...)` / `fc.column(...)` ship categorical/numeric vertical and horizontal bars, grouped bars, stacked bars, and normalized stacked bars (`mode="normalized"`) through the shared rectangle renderer. Follow-up: labels. | | 4 | Area | filled line, stacked area, streamgraph, ridgeline-lite area bands | Implemented core | `fc.area(...)` ships a filled area with scalar/array baseline and optional line overlay. Follow-ups: stacked area helpers and streamgraph offsets. | | 5 | Histogram | count, probability, density, cumulative histogram | Implemented core | Python-side binning plus the shared rectangle renderer; `cumulative=True` (count CDF and, with `density=True`, empirical CDF) is implemented. Follow-up: viewport-aware re-binning for huge streamed distributions. | -| 6 | Pie / donut | pie, donut, nested donut, variable-radius pie | Planned compatibility | Extremely common in dashboards even though performance differentiation is low. | +| 6 | Pie / donut | pie, donut, nested donut, variable-radius pie | Implemented in `xy.pyplot` | Native pie/donut tessellation with Matplotlib-style containers and labels; richer nested/variable-radius composition remains future depth. | | 7 | Heatmap / image / matrix | heatmap, image, annotated matrix, correlation matrix, cohort heatmap | Implemented core | `fc.heatmap(...)` renders matrix cells through a compact grid texture with continuous colormaps and categorical/numeric axes. Native static export borrows canonical f64 spans and normalizes only sampled pixels in Rust, verified through 4.29B cells without a derived grid or RGBA expansion. Follow-ups: annotation and tiled huge-image browser transport. | | 8 | Box plot | box, grouped box, notched box, outlier points | Implemented core | Tukey quartiles, whiskers, median, deterministic outliers, numeric or categorical groups. | | 9 | Candlestick / OHLC | candlestick, OHLC bars, volume overlay, range selector | Prototyped (PR closed unmerged) | `fc.candlestick(...)`/`fc.ohlc(...)` + `fc.candlestick_chart(...)` on the closed `codex/finance-charting-surface` exploration branch: OHLC decimation, shared-y f32 frame, time axes, hover, and a volume pane. Critical finance surface; inherits LOD and time-axis work from core primitives. | @@ -125,8 +127,8 @@ not fall out of sight. | 29 | Parallel coordinate/category | parallel coordinates, parallel categories, alluvial-lite | Planned later | Present in Plotly/ECharts; useful for high-dimensional EDA. | | 30 | Sankey / alluvial | Sankey, alluvial, dependency wheel | Planned later | Important flow chart, but requires layout and interaction work. | | 31 | Network/tree/org | network graph, force graph, tree, dendrogram, org chart, arc diagram | Planned later | Valuable but layout-heavy; should follow core 2D marks. | -| 32 | Scientific vector fields | quiver, barbs, streamplot, wind rose | Planned later | Science/engineering breadth; needs arrows, vector fields, and polar variants. | -| 33 | Irregular grid science | pcolormesh, tricontour, tripcolor, triangular mesh | Planned later | Matplotlib/science compatibility; separate data model from regular heatmaps. | +| 32 | Scientific vector fields | quiver, barbs, streamplot, wind rose | Implemented in `xy.pyplot` | Quiver, barbs, and bounded streamlines feed shared instanced segments; wind rose remains tied to future polar axes. | +| 33 | Irregular grid science | pcolormesh, tricontour, tripcolor, triangular mesh | Implemented in `xy.pyplot` | Curvilinear quads and explicit/native triangulations route through indexed meshes and marching-triangle kernels. | | 34 | Specialist coordinate systems | ternary, Smith chart, carpet plot, polar scatter/line/bar | Planned later | Plotly/science compatibility; axis systems are the main work. | | 35 | Finance advanced | VWAP, moving averages, Bollinger bands, RSI, MACD, depth chart, order book heatmap, market profile, Renko, Heikin-Ashi, Kagi, point-and-figure | Prototyped (PR closed unmerged) | The closed `codex/finance-charting-surface` exploration branch has a `FinanceChart`/`FinanceLayer` system with volume bars, SMA, VWAP, Bollinger bands, RSI, and MACD as overlay/pane layers plus drawings. Remaining: depth/order-book, market profile, Renko/Heikin-Ashi/Kagi/P&F. | | 36 | Maps and geo | choropleth, tile choropleth, point map, bubble map, density map, route map, filled-area map | Deferred 2D domain | 2D, but requires projection/tile/geography stack; do after core chart breadth. | @@ -167,7 +169,7 @@ depth: strip/swarm/boxen/rug distributions, regression diagnostics, richer | Rank | Chart | Why it matters | Caveat | |---:|---|---|---| | 13 | Composed / mixed charts | Overlay line, scatter, bars, bands, candlesticks, and secondary axes cleanly. | API and spec work comes before many chart families. | -| 14 | Pie / donut | Very popular in basic chart libraries and user expectations. | Low xy differentiation; implement for completeness, not performance. | +| 14 | Pie / donut | Very popular in basic chart libraries and user expectations. | Implemented through `xy.pyplot`; future work is composition and styling depth. | | 15 | Candlestick / OHLC | Important for finance users and appears in Plotly/Highcharts stock tooling. | **Prototyped (PR closed unmerged):** candlestick/OHLC marks with date axes, gaps, and hover format on the closed finance exploration branch. Remaining polish: range selectors. | | 16 | Finance overlays | Volume bars, VWAP, moving averages, Bollinger bands, depth/order-book heatmap, market profile, Renko, Heikin-Ashi, Kagi, point-and-figure. | **Prototyped (PR closed unmerged):** volume pane, SMA, VWAP, Bollinger, RSI, MACD as `FinanceLayer`s reusing composed charts + time axes. Remaining: depth/order-book, market profile, Renko/Heikin-Ashi/Kagi/P&F. | | 17 | Waterfall | Common in business reporting and Plotly/Highcharts. | Mostly categorical bars plus running baseline. | @@ -192,8 +194,8 @@ depth: strip/swarm/boxen/rug distributions, regression diagnostics, richer |---:|---|---|---| | 27 | Radar / polar / radial bar | Common in Chart.js/Highcharts and dashboards. | Needs polar axes and interaction semantics. | | 28 | Ternary / Smith / carpet | Plotly/scientific compatibility. | New coordinate systems, not new mark primitives. | -| 29 | Quiver / barbs / streamplot / wind rose | Scientific and engineering vector fields. | Needs arrows, vector sampling, and polar support. | -| 30 | Pcolormesh / tricontour / tripcolor | Matplotlib-style irregular grid science. | Separate mesh data model from regular heatmaps. | +| 29 | Quiver / barbs / streamplot / wind rose | Scientific and engineering vector fields. | Quiver, barbs, and streamplot are implemented through `xy.pyplot`; wind rose awaits polar support. | +| 30 | Pcolormesh / tricontour / tripcolor | Matplotlib-style irregular grid science. | Implemented through `xy.pyplot` with native quad/triangle geometry. | | 31 | Waffle / mosaic / Mekko / variwide | Business/category compatibility. | Mostly rectangle layout algorithms. | | 32 | Packed bubble / Venn / Euler | Compatibility and presentation charts. | Layout algorithms and label placement dominate. | | 33 | Pictorial bar / item chart / image markers / text marks | ECharts/Highcharts compatibility polish. | Symbol systems and asset handling. | diff --git a/docs/matplotlib-compat-changelog.md b/docs/matplotlib-compat-changelog.md new file mode 100644 index 00000000..c2db9a6b --- /dev/null +++ b/docs/matplotlib-compat-changelog.md @@ -0,0 +1,115 @@ +# Matplotlib compatibility changelog + +This changelog records changes to the upstream compatibility target and to the +meaning of xy's compatibility levels. It complements the project changelog, +which covers user-visible releases across the whole package. + +## Matplotlib 3.11 development snapshot — 2026-07-13 + +- Pinned upstream revision `bde111fb4e` + (`v3.11.0-348-gbde111fb4e`, 2026-07-10). +- Captured the supported 66-method `Axes` Plotting inventory as a generated, + reviewed snapshot instead of a hard-coded assertion. +- Added `grouped_bar`, `pie_label`, and `violin` from the 3.11 development + surface. +- Added a dedicated CI environment for optional Matplotlib-object interop and + isolated dual-engine execution of the full compatibility corpus. +- Published approximation levels and a generated method compatibility matrix. + +### Post-review corrections — 2026-07-13 + +- Downgraded "Unstructured triangles" from *exact geometry* to *equivalent + semantics*: the family has no cross-engine reference comparison yet. +- Converted the formerly silent option discards into loud rejections: pie + shadow/frame/rotatelabels/hatch, quiver/barbs/quiverkey head geometry, + units, increments and styling, contour origin/linestyles/corner_mask, + table placement, tricontour extend, non-linear norm objects everywhere, + spy aspect, and pie_label rotate. +- Implemented (rather than rejected) where the marks could honor the value: + contour `extent`; plain `Normalize` reduced to vmin/vmax for pcolormesh and + the tri* family; stem/eventplot/triplot dashed linestyles via data-space + dash segmentation (scales with zoom — not screen-space patterns); + `bar_label(fontsize=)` and pie/pie_label/table textprops + fontsize/ha/va; streamplot `start_points`, `integration_direction`, + array widths/colors, `num_arrows`. +- `streamplot` now always uses the shim's bounded fixed-step integrator; + results no longer differ between environments with and without Matplotlib + installed, and paths approximate Matplotlib's adaptive integrator. +- `hist(histtype="stepfilled")` renders a filled step polygon instead of + silently degrading to the unfilled step outline. +- Documented the accepted visual approximations explicitly (barbs glyph, + imshow smoothing collapse and truecolor passthrough, annotate arrowprops, + errorbar limit carets, data-space dashes) and the HTML-only scope of + chrome rcParams. + +### PDSH gap features — 2026-07-13 (Matplotlib 3.11.0 reference) + +Driven by the Python Data Science Handbook ch. 4 benchmark (import-swap over +14 notebooks): pass rate 121/171 → 154/171 runnable cells (90%), 147/154 +(95%) excluding the out-of-scope 3-D notebook, with zero savefig errors on +passing cells. + +- Tick machinery: xy-owned `NullLocator`/`FixedLocator`/`MultipleLocator`/ + `MaxNLocator`/`LinearLocator`/`LogLocator` and `NullFormatter`/ + `FixedFormatter`/`FuncFormatter`/`FormatStrFormatter`/`StrMethodFormatter`/ + `ScalarFormatter`, wired through `set_major_locator`/`set_major_formatter` + and resolved at build time so ticks track live data limits. `set_xticks` + and explicit labels displace stored tickers (last call wins). Minor + locators/formatters are retained but minor ticks still do not render. +- Styles: `plt.style.context(...)` (snapshot/restore incl. theme tokens) and + the stock sheets fivethirtyeight, ggplot, bmh, dark_background, grayscale, + seaborn-v0_8-white(grid), reduced to the supported rcParams subset; new + `grid.color` rcParam wired into the axes chrome; `cycler()` (color only). +- Colormaps: RdGy and jet engine tables (11 anchors sampled from Matplotlib + 3.11) across Python SVG/PNG and the JS client; `LinearSegmentedColormap. + from_list` / `ListedColormap` as Python-side callables; `cm.get_cmap`. +- Mappables: pyplot wrappers register the current image (`gci`/`sci`); + `plt.clim` retargets it and any colorbar derived from it; `set_clim` on + scatter/poly collections; scatter vmin/vmax now flow into a real + `color_domain` on the engine's color channel (previously they crashed the + render). `colorbar()` returns its handle from pyplot, falls back to the + current image, renders `ticks=`/`extend=` in PNG and SVG, and rejects + unknown kwargs (previously swallowed silently). +- Layout: `plt.GridSpec` with slice spans and wspace/hspace/ratios resolved + to explicit figure rectangles; `add_subplot(spec, sharex=, sharey=, + xticklabels=[])`; `subplot(r, c, i)` mixes into figures that already hold + free-form axes; `subplots(subplot_kw=)`. +- Axes surface: `get_figure`, `get_lines`, `get_shared_x/y_axes`, + `get_x/yticklabels` (recolorable handles), `set_facecolor` (+ the + `plt.axes(facecolor=)` route), `set_axisbelow(True)`, spine iteration with + deferred both-or-loud hiding, `tick_bottom`/`tick_left`, `fig.canvas` + facade; pandas `Period` coordinates convert to timestamps. +- Fixed en route: `grid(linestyle='solid')` injected an invalid `None` style + value and crashed every subsequent export of that axes; `plt.subplot()` and + `plt.axes()` silently dropped their keyword arguments; `projection=` other + than rectilinear now fails with a clear NotImplementedError. +- Known remaining boundaries measured by the benchmark: pandas' dynamic + timeseries plotting (its private ordinal-axis locators), legend geometry + options (`borderpad`, `labelspacing` — still loud), `Legend` handles for + second legends, markers on axhline, and 3-D axes. + +### Second review pass — 2026-07-13 + +- Silent divergences converted to correct behavior: scatter drops rows masked + in x/y/s (not just c); `fill_between(interpolate=True)` draws single-point + `where` regions; `imsave` colormaps original values instead of a + pre-quantized uint8 copy; `set_cmap` validates names and feeds + imshow/scatter defaults; boxplot `sym` is honored (empty string suppresses + fliers) and flierprops colors reach the drawn dots; usermedians no longer + shift notch CIs; hexbin `mincnt` filtering and `C` aggregation use the same + bin membership. +- Silent discards converted to loud rejections: bxp component linestyles, + secondary-axis `set_ticks` extras, axes/figure-fraction transforms on data + artists, `savefig(format='html', metadata=)`; singular transforms fail at + `set_transform` time with ValueError. +- Export: SVG/HTML honor `savefig(facecolor=)` (background rect / styled + container); single-chart SVG includes the suptitle; composed-SVG suptitle + `y` maps as a figure fraction; non-Latin-1 PNG metadata keys raise + ValueError. +- Scales: logit masks values at/outside (0, 1) instead of emitting ±inf; + scale-generated ticks refresh as data arrives and are dropped when the + scale returns to linear; explicit `set_*ticks` under a nonlinear scale + label the original data values. + +Future entries must identify the Matplotlib release/revision, inventory +additions or removals, and any compatibility-level changes. diff --git a/docs/matplotlib-compat-matrix.md b/docs/matplotlib-compat-matrix.md new file mode 100644 index 00000000..a576de47 --- /dev/null +++ b/docs/matplotlib-compat-matrix.md @@ -0,0 +1,138 @@ + +# Matplotlib compatibility matrix + +Pinned upstream: `v3.11.0-348-gbde111fb4e` (`bde111fb4e`). + +The level describes the intended compatibility contract, not pixel identity. +Corpus links are executable examples and are checked for every supported method. + +| Family | Level | Methods | Executable corpus | +|---|---|---:|---:| +| Basic | equivalent semantics | 22 | 47 | +| Spans | exact geometry | 5 | 4 | +| Spectral | equivalent semantics | 9 | 2 | +| Statistics | equivalent semantics | 5 | 2 | +| Binned | exact geometry | 4 | 7 | +| Contours | visual approximation | 3 | 1 | +| 2D arrays | equivalent semantics | 6 | 3 | +| Unstructured triangles | equivalent semantics | 4 | 1 | +| Text and annotations | visual approximation | 4 | 3 | +| Vector fields | visual approximation | 4 | 2 | + +## Method inventory + +### Basic + +Approximation level: **equivalent semantics**. + +- `plot` — [`01_basic_line.py`](../tests/pyplot/corpus/01_basic_line.py), [`02_plot_fmt_red_dashed.py`](../tests/pyplot/corpus/02_plot_fmt_red_dashed.py), [`03_plot_fmt_green_circles.py`](../tests/pyplot/corpus/03_plot_fmt_green_circles.py), [`04_plot_fmt_cycle_dashdot_square.py`](../tests/pyplot/corpus/04_plot_fmt_cycle_dashdot_square.py), [`05_multi_series_one_call.py`](../tests/pyplot/corpus/05_multi_series_one_call.py), [`06_implicit_x.py`](../tests/pyplot/corpus/06_implicit_x.py), [`07_labels_title_legend_grid.py`](../tests/pyplot/corpus/07_labels_title_legend_grid.py), [`08_xlim_ylim.py`](../tests/pyplot/corpus/08_xlim_ylim.py), [`09_log_scale.py`](../tests/pyplot/corpus/09_log_scale.py), [`20_fill_between_band.py`](../tests/pyplot/corpus/20_fill_between_band.py), [`23_axhline_axvline.py`](../tests/pyplot/corpus/23_axhline_axvline.py), [`24_axvspan_band.py`](../tests/pyplot/corpus/24_axvspan_band.py), [`25_annotate_text.py`](../tests/pyplot/corpus/25_annotate_text.py), [`26_twinx_dual_axis.py`](../tests/pyplot/corpus/26_twinx_dual_axis.py), [`27_subplots_2x2_mixed.py`](../tests/pyplot/corpus/27_subplots_2x2_mixed.py), [`28_subplots_figsize.py`](../tests/pyplot/corpus/28_subplots_figsize.py), [`29_implicit_state_savefig.py`](../tests/pyplot/corpus/29_implicit_state_savefig.py), [`30_savefig_html.py`](../tests/pyplot/corpus/30_savefig_html.py), [`31_rcparams_figsize.py`](../tests/pyplot/corpus/31_rcparams_figsize.py), [`33_set_data_mutation.py`](../tests/pyplot/corpus/33_set_data_mutation.py), [`34_gray_string_color.py`](../tests/pyplot/corpus/34_gray_string_color.py), [`35_tab_colors.py`](../tests/pyplot/corpus/35_tab_colors.py), [`36_color_cycle_c0_c9.py`](../tests/pyplot/corpus/36_color_cycle_c0_c9.py), [`37_markers_only_fmt.py`](../tests/pyplot/corpus/37_markers_only_fmt.py), [`38_close_all_hygiene.py`](../tests/pyplot/corpus/38_close_all_hygiene.py), [`39_multiple_figures.py`](../tests/pyplot/corpus/39_multiple_figures.py), [`40_subplots_row_sharex.py`](../tests/pyplot/corpus/40_subplots_row_sharex.py), [`41_line_kwargs.py`](../tests/pyplot/corpus/41_line_kwargs.py), [`43_grid_html_suptitle.py`](../tests/pyplot/corpus/43_grid_html_suptitle.py), [`44_subplot_classic.py`](../tests/pyplot/corpus/44_subplot_classic.py), [`45_xticks_positions_labels.py`](../tests/pyplot/corpus/45_xticks_positions_labels.py) +- `errorbar` — [`47_statistical_families.py`](../tests/pyplot/corpus/47_statistical_families.py) +- `scatter` — [`10_scatter_basic.py`](../tests/pyplot/corpus/10_scatter_basic.py), [`11_scatter_color_array_cmap.py`](../tests/pyplot/corpus/11_scatter_color_array_cmap.py), [`12_scatter_size_array.py`](../tests/pyplot/corpus/12_scatter_size_array.py), [`13_scatter_edgecolors.py`](../tests/pyplot/corpus/13_scatter_edgecolors.py), [`27_subplots_2x2_mixed.py`](../tests/pyplot/corpus/27_subplots_2x2_mixed.py) +- `step` — [`22_step.py`](../tests/pyplot/corpus/22_step.py) +- `loglog` — [`51_basic_2d_aliases.py`](../tests/pyplot/corpus/51_basic_2d_aliases.py) +- `semilogx` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `semilogy` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `fill_between` — [`20_fill_between_band.py`](../tests/pyplot/corpus/20_fill_between_band.py) +- `fill_betweenx` — [`51_basic_2d_aliases.py`](../tests/pyplot/corpus/51_basic_2d_aliases.py) +- `bar` — [`14_bar_categories.py`](../tests/pyplot/corpus/14_bar_categories.py), [`15_bar_stacked_bottom.py`](../tests/pyplot/corpus/15_bar_stacked_bottom.py), [`27_subplots_2x2_mixed.py`](../tests/pyplot/corpus/27_subplots_2x2_mixed.py), [`32_xticks_rotation.py`](../tests/pyplot/corpus/32_xticks_rotation.py), [`42_tick_params_rotation.py`](../tests/pyplot/corpus/42_tick_params_rotation.py) +- `barh` — [`16_barh.py`](../tests/pyplot/corpus/16_barh.py) +- `bar_label` — [`53_matplotlib_311_plotting.py`](../tests/pyplot/corpus/53_matplotlib_311_plotting.py) +- `grouped_bar` — [`53_matplotlib_311_plotting.py`](../tests/pyplot/corpus/53_matplotlib_311_plotting.py) +- `stem` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `eventplot` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `pie` — [`50_pie_donut.py`](../tests/pyplot/corpus/50_pie_donut.py), [`53_matplotlib_311_plotting.py`](../tests/pyplot/corpus/53_matplotlib_311_plotting.py) +- `pie_label` — [`53_matplotlib_311_plotting.py`](../tests/pyplot/corpus/53_matplotlib_311_plotting.py) +- `stackplot` — [`46_stackplot.py`](../tests/pyplot/corpus/46_stackplot.py) +- `broken_barh` — [`51_basic_2d_aliases.py`](../tests/pyplot/corpus/51_basic_2d_aliases.py) +- `vlines` — [`51_basic_2d_aliases.py`](../tests/pyplot/corpus/51_basic_2d_aliases.py) +- `hlines` — [`51_basic_2d_aliases.py`](../tests/pyplot/corpus/51_basic_2d_aliases.py) +- `fill` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) + +### Spans + +Approximation level: **exact geometry**. + +- `axhline` — [`23_axhline_axvline.py`](../tests/pyplot/corpus/23_axhline_axvline.py), [`34_gray_string_color.py`](../tests/pyplot/corpus/34_gray_string_color.py) +- `axhspan` — [`24_axvspan_band.py`](../tests/pyplot/corpus/24_axvspan_band.py) +- `axvline` — [`23_axhline_axvline.py`](../tests/pyplot/corpus/23_axhline_axvline.py) +- `axvspan` — [`24_axvspan_band.py`](../tests/pyplot/corpus/24_axvspan_band.py) +- `axline` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) + +### Spectral + +Approximation level: **equivalent semantics**. + +- `acorr` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `angle_spectrum` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `cohere` — [`52_spectral.py`](../tests/pyplot/corpus/52_spectral.py) +- `csd` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `magnitude_spectrum` — [`52_spectral.py`](../tests/pyplot/corpus/52_spectral.py) +- `phase_spectrum` — [`52_spectral.py`](../tests/pyplot/corpus/52_spectral.py) +- `psd` — [`52_spectral.py`](../tests/pyplot/corpus/52_spectral.py) +- `specgram` — [`52_spectral.py`](../tests/pyplot/corpus/52_spectral.py) +- `xcorr` — [`52_spectral.py`](../tests/pyplot/corpus/52_spectral.py) + +### Statistics + +Approximation level: **equivalent semantics**. + +- `ecdf` — [`47_statistical_families.py`](../tests/pyplot/corpus/47_statistical_families.py) +- `boxplot` — [`47_statistical_families.py`](../tests/pyplot/corpus/47_statistical_families.py) +- `violinplot` — [`47_statistical_families.py`](../tests/pyplot/corpus/47_statistical_families.py) +- `bxp` — [`53_matplotlib_311_plotting.py`](../tests/pyplot/corpus/53_matplotlib_311_plotting.py) +- `violin` — [`53_matplotlib_311_plotting.py`](../tests/pyplot/corpus/53_matplotlib_311_plotting.py) + +### Binned + +Approximation level: **exact geometry**. + +- `hexbin` — [`47_statistical_families.py`](../tests/pyplot/corpus/47_statistical_families.py) +- `hist` — [`17_hist_bins.py`](../tests/pyplot/corpus/17_hist_bins.py), [`18_hist_density.py`](../tests/pyplot/corpus/18_hist_density.py), [`19_hist_cumulative.py`](../tests/pyplot/corpus/19_hist_cumulative.py), [`27_subplots_2x2_mixed.py`](../tests/pyplot/corpus/27_subplots_2x2_mixed.py), [`43_grid_html_suptitle.py`](../tests/pyplot/corpus/43_grid_html_suptitle.py) +- `hist2d` — [`47_statistical_families.py`](../tests/pyplot/corpus/47_statistical_families.py) +- `stairs` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) + +### Contours + +Approximation level: **visual approximation**. + +- `clabel` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `contour` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `contourf` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) + +### 2D arrays + +Approximation level: **equivalent semantics**. + +- `imshow` — [`21_imshow_cmap.py`](../tests/pyplot/corpus/21_imshow_cmap.py) +- `matshow` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `pcolor` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `pcolorfast` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `pcolormesh` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `spy` — [`51_basic_2d_aliases.py`](../tests/pyplot/corpus/51_basic_2d_aliases.py) + +### Unstructured triangles + +Approximation level: **equivalent semantics**. + +- `tripcolor` — [`49_unstructured_mesh.py`](../tests/pyplot/corpus/49_unstructured_mesh.py) +- `triplot` — [`49_unstructured_mesh.py`](../tests/pyplot/corpus/49_unstructured_mesh.py) +- `tricontour` — [`49_unstructured_mesh.py`](../tests/pyplot/corpus/49_unstructured_mesh.py) +- `tricontourf` — [`49_unstructured_mesh.py`](../tests/pyplot/corpus/49_unstructured_mesh.py) + +### Text and annotations + +Approximation level: **visual approximation**. + +- `annotate` — [`25_annotate_text.py`](../tests/pyplot/corpus/25_annotate_text.py) +- `text` — [`25_annotate_text.py`](../tests/pyplot/corpus/25_annotate_text.py) +- `table` — [`53_matplotlib_311_plotting.py`](../tests/pyplot/corpus/53_matplotlib_311_plotting.py) +- `arrow` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) + +### Vector fields + +Approximation level: **visual approximation**. + +- `barbs` — [`48_vector_fields.py`](../tests/pyplot/corpus/48_vector_fields.py) +- `quiver` — [`48_vector_fields.py`](../tests/pyplot/corpus/48_vector_fields.py), [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `quiverkey` — [`54_plotting_method_coverage.py`](../tests/pyplot/corpus/54_plotting_method_coverage.py) +- `streamplot` — [`48_vector_fields.py`](../tests/pyplot/corpus/48_vector_fields.py) diff --git a/docs/matplotlib-compat.md b/docs/matplotlib-compat.md index 8bde6718..32c76401 100644 --- a/docs/matplotlib-compat.md +++ b/docs/matplotlib-compat.md @@ -11,64 +11,103 @@ screen-bounded cost — with matplotlib's calling conventions. **The claim, precisely:** every method in the Matplotlib 3.11 `Axes` **Plotting** section is present on both `xy.pyplot.Axes` and the stateful `xy.pyplot` -namespace. `test_official_matplotlib_311_2d_plotting_surface_is_complete` -locks that inventory to the upstream list, while the executable compatibility +namespace. The reviewed +[`matplotlib_311_plotting.json`](../tests/pyplot/matplotlib_311_plotting.json) +snapshot locks that inventory to the pinned upstream documentation, while the executable compatibility corpus in [`tests/pyplot/corpus/`](../tests/pyplot/corpus/) covers representative calls from every family. This is 100% 2-D *chart-method* coverage; it is not a claim to reproduce Matplotlib's renderer, transforms, or full Artist graph. +The generated [method-by-method compatibility matrix](matplotlib-compat-matrix.md) +is sourced from that snapshot, executable corpus calls, and +[`compatibility.json`](../tests/pyplot/compatibility.json). CI fails if the +generated matrix is stale, installs the released `matplotlib==3.11.0` wheel, +and asserts every snapshot method exists on its `Axes`. The dev revision +recorded in the snapshot is informational: CI no longer compares the snapshot +against an upstream Matplotlib checkout. + +The dual-engine runner executes every corpus case in a fresh process. Its +reference harness only normalizes renderer-specific HTML export and xy's +dependency-free `triangles=` shorthand into Matplotlib's equivalent +`Triangulation` positional form; chart data and plotting options are unchanged. + +## Approximation levels + +- **Exact geometry:** material data-space geometry and returned numeric values + are intended to match Matplotlib. +- **Equivalent semantics:** user intent and data results match, using xy-owned + artists, containers, and renderer behavior. +- **Visual approximation:** the visible chart family is retained, but styling, + layout, or artist details can differ across renderers. +- **Accepted no-op:** a documented option is validated and retained without a + visible effect; this is used only when a stable output guarantee is tested. +- **Optional interop:** behavior accepts real Matplotlib objects only when + Matplotlib is installed; it is tested in the dedicated reference CI job. +- **Unsupported:** the shim rejects the call or option with an actionable error + rather than silently discarding it. + ## Supported surface | matplotlib | notes | |---|---| -| `plt.plot` / `ax.plot` | format strings (`'r--o'`), multiple series per call, implicit x, `label=`, `lw=`, `ls=`, `alpha=`, `marker=` | -| `scatter(x, y, s=, c=, cmap=, alpha=, marker=, edgecolors=, plotnonfinite=)` | `s` (pt², area) maps to pixel diameter; array `c` becomes a color encoding | +| `plt.plot` / `ax.plot` | format strings (`'r--o'`), multiple series per call, implicit x, `label=`, `lw=`, `ls=`, `alpha=`, marker face/edge styling, `markevery`, and dependency-free affine *data* transforms (`Affine2D + ax.transData`); axes/figure-fraction transforms on data artists, partial fill styles, and cap/join policies fail loudly | +| `scatter(x, y, s=, c=, cmap=, vmin=, vmax=, alpha=, marker=, edgecolors=, plotnonfinite=)` | `s` (pt², area) maps to pixel diameter; array `c` becomes a color encoding and explicit paired color bounds are retained; custom norms/marker paths fail loudly | | `bar`, `barh`, `grouped_bar`, `bar_label` | string categories, stacking bases, Matplotlib 3.11 grouped-bar containers and labels | | `hist(bins=, range=, density=, cumulative=, weights=, orientation=, stacked=)` | Returns computed counts/edges and supports bar/step histogram families | -| `hist2d`, `hexbin`, `ecdf` | 2D uniform binning uses the native Rust kernel; ECDF/hexbin use the corresponding core marks | -| `boxplot`, `violinplot`, `bxp`, `violin`, `errorbar` | Raw samples use bounded core distribution marks; precomputed statistics use exact generic mesh/segment geometry | +| `hist2d`, `hexbin`, `ecdf` | 2D uniform binning uses the native Rust kernel; hexbin supports `C`, arbitrary scalar reducers, and `mincnt` without retaining source points. Hexbin aggregates over a rectangular grid with staggered display centers — a visual approximation of Matplotlib's true hexagonal binning, so per-bin values differ from Matplotlib's | +| `boxplot`, `violinplot`, `bxp`, `violin`, `errorbar` | Boxplots support notches, bootstrap/user confidence intervals, median overrides (drawn median only; notch CIs stay data-derived like Matplotlib), percentile/custom whiskers, cap widths, `sym`, and component colors/widths/alpha — dashed component linestyles fail loudly. Violins support Scott/Silverman/scalar/callable Gaussian-KDE bandwidths, quantiles, and low/high sides; the default (bw_method omitted) uses the native histogram violin mark, whose shape differs from the explicit KDE path | | `fill_between(x, y1, y2, where=, step=)` / `fill_betweenx` | Masks are split into finite contiguous polygons; step geometry is expanded exactly | | `stackplot` | All four baselines are computed by the native stacked-bounds kernel | -| `imshow` / `pcolormesh` (`cmap=`, `vmin=`/`vmax=`, `origin=`) | Uniform grids retain the texture fast path; nonuniform and curvilinear grids use native quad-to-triangle expansion | +| `imshow` / `pcolormesh` (`cmap=`, `vmin=`/`vmax=`, `origin=`) | `imshow` defaults to `rcParams['image.origin']`; nearest stays cell-exact and Matplotlib's smoothing mode names all collapse to the shim's single bounded gradient upsampling (a visual approximation, not per-mode kernels) and apply to scalar data only — RGB(A) truecolor arrays render unresampled — while unsupported stages/transforms fail loudly. Uniform meshes retain the texture fast path; nonuniform and curvilinear grids use native quad-to-triangle expansion | | `step`, `stairs`, `stem`, `eventplot` | Compact step/stem/segment marks; no Python-side vertex expansion | | `contour` / `contourf` / `clabel` | Native marching squares over rectilinear grids; warped grids route through native Delaunay/marching-triangle kernels | -| `quiver`, `barbs`, `streamplot` | Native vector endpoint/arrowhead and bounded streamline kernels feeding one instanced segment mark | +| `quiver`, `barbs`, `streamplot` | Native vector endpoint/arrowhead and bounded streamline kernels feeding one instanced segment mark. Barbs are a visual approximation: magnitude maps to a bounded tick count, not WMO 50/10/5 increments. Streamplot always uses the shim's own bounded fixed-step integrator (identical output with or without Matplotlib installed, but paths approximate Matplotlib's adaptive ones); `start_points`, `integration_direction`, array widths/colors and `num_arrows` are honored, and remaining non-default integration options fail loudly | | `tripcolor`, `triplot`, `tricontour`, `tricontourf` | Explicit topology or native dependency-free Delaunay triangulation; indexed geometry and isolines stay in Rust | | `pie` / `pie_label` | Native pie/donut tessellation and the Matplotlib 3.11 `PieContainer` (`values`, `fracs`, grouped text labels) | | `axhline` / `axvline` / `axhspan` / `axvspan`, `text`, `annotate`, `table` | Fractional span bounds plus data/axes/figure text coordinates are supported; `arrowprops` is approximated as callout text | | `xlabel` / `ylabel` / `title` / `suptitle` | Suptitles are retained in HTML and multi-panel PNG/SVG | | `legend()` | `loc`/`fontsize` accepted; placement is the chart's own | | `grid(True/False)` | toggles the grid via the theme | -| `xlim` / `ylim`, axis scales, `invert_xaxis/yaxis` | linear/log are native; symlog/logit/asinh fail loudly until their transforms are implemented | +| `xlim` / `ylim`, axis scales, `invert_xaxis/yaxis` | linear/log are native; symlog/logit/asinh use dependency-free monotone data transforms with inverse limit/tick semantics. Tick locations come from xy's own generators (refreshed as data arrives), not Matplotlib's locators; artist `get_data()` reflects the transformed space; logit masks values at/outside (0, 1) | +| `set_major_locator` / `set_major_formatter`, `plt.NullLocator/FixedLocator/MultipleLocator/MaxNLocator/LinearLocator/LogLocator`, `plt.NullFormatter/FixedFormatter/FuncFormatter/FormatStrFormatter/StrMethodFormatter/ScalarFormatter` | xy-owned re-implementations resolved at build time against live data limits (Null/Fixed/Multiple are position-exact; MaxN is a nice-step heuristic, not Matplotlib's edge-extension algorithm). Third-party locator objects work if they implement `tick_values(vmin, vmax)`; minor locators/formatters are retained for round-tripping but minor ticks do not render | +| datetime, timedelta, and string coordinates | datetime inputs use the engine's automatic date ticks, timedeltas are bounded to elapsed seconds, and common strings use categorical ticks; the general Matplotlib units registry is intentionally out of scope | | `xticks(positions, labels, rotation=)` / `tick_params(labelrotation=)` | Exact positions and strings render in browser, PNG, and SVG | -| `twinx()` | second y-axis (right side) | +| `twinx()`, `secondary_xaxis()`, `secondary_yaxis()` | second data axes and linked tick-only secondary axes with callable forward/inverse conversions. Secondary-axis ticks are evenly spaced conversions of the primary domain (not Matplotlib's secondary-unit locators) and currently reach the interactive HTML client only — PNG/SVG export does not draw them yet | | `fig, ax = plt.subplots()`; `plt.subplots(n, m, figsize=, dpi=, squeeze=, sharex=, sharey=)` | Grid renders as CSS-grid HTML and stitched PNG/SVG; shared axes use common domains and live linked pan/zoom | | `fig.add_subplot(2, 2, 1)` / `add_subplot(221)` | | | `gca` / `gcf` / `sca` / `figure(num)` / `close(...)` | matplotlib's implicit-state semantics | -| `savefig('x.png' / '.svg' / '.html', dpi=)` | Browser-free PNG/SVG supports both single and multi-panel figures | +| `savefig('x.png' / '.svg' / '.html', dpi=)` | Browser-free PNG/SVG supports both single and multi-panel figures; file-like targets require an explicit `format=` and unsupported metadata/layout/export formats fail loudly | | `plt.show()` | notebooks: inline HTML display; scripts: opens the default browser | | Artists: `set_data` / `set_ydata` / `set_color` / `set_label` / `set_linewidth` / `remove` | mutating a handle rebuilds the chart on next render | | Colors | single letters, `C0`–`C9`, `tab:*`, gray `'0.5'`, RGB(A) tuples, any CSS color | -| `plt.cm.*` / `plt.colormaps[...]` / `cmap=` names | viridis, plasma, inferno, magma, cividis, gray, turbo, coolwarm, Blues, RdYlGn, rainbow, Spectral, aliases, and true `*_r` reversal | -| `rcParams` | `figure.figsize`, `figure.dpi`, `lines.linewidth`, `lines.markersize`, `axes.grid`; unknown keys warn once and are ignored | -| `plt.style.use("xy")` | switches from the matplotlib-flavored default theme to the engine-native look | +| `plt.cm.*` / `plt.colormaps[...]` / `cmap=` names | viridis, plasma, inferno, magma, cividis, gray, turbo, coolwarm, Blues, RdYlGn, RdGy, jet, rainbow, Spectral, aliases, and true `*_r` reversal (RdGy/jet render from 11-stop anchor tables sampled from Matplotlib 3.11, linearly interpolated) | +| `LinearSegmentedColormap.from_list` / `ListedColormap` | Python-side callables (`cmap(np.arange(cmap.N))` → RGBA) for scripts that colormap values themselves; they cannot be passed as `cmap=` to plotting calls (no engine table), which fails loudly | +| `plt.colorbar()` / `fig.colorbar()` / `plt.clim()` / `plt.gci()` | Returns a live handle (`set_label`, `set_ticks`); with no mappable it uses the current image the way pyplot does. `ticks=`/`extend=` render in PNG and SVG (the HTML colorbar stays a minimal gradient without tick text); `clim` retargets the mappable's color window and any colorbar derived from it | +| `rcParams` | Figure size/DPI, line width/marker size, image cmap/origin, and the axes color cycle affect every exporter. The chrome keys (axes face/edge/label/title styles, font family/size, tick colors/sizes, legend defaults, figure facecolor) reach the HTML renderer and multi-panel PNG stitching; single-chart PNG and SVG export currently render their own fixed chrome and ignore them. Unknown keys warn once | +| `plt.style.use(...)` / `plt.style.context(...)` | `"default"`, `"xy"`, bounded rcParam dictionaries, ordered lists, and the stock sheets fivethirtyeight, ggplot, bmh, dark_background, grayscale, and seaborn-v0_8-white(grid) — reduced to the supported rcParams subset (colors, grid, cycle, line width, font size; per-sheet keys outside that subset are not carried). `context()` snapshots and restores. Unknown sheet names fail precisely | +| `plt.GridSpec(r, c, wspace=, hspace=, width_ratios=)` + slice specs | Spans (`grid[0, 1:]`, `grid[:-1, 0]`) and custom spacing resolve to explicit figure rectangles using Matplotlib's SubplotParams frame; default-geometry single cells keep the uniform grid. Spanning layouts position exactly in PNG; HTML/SVG sequence free-form panels rather than overlaying the true rectangles | +| `add_subplot(spec, sharex=, sharey=, xticklabels=[], ...)` | per-axes sharing aliases the axis-property store (static domains, as `twiny` does), not Matplotlib's live Grouper; `get_shared_x_axes()` reflects it | ## Outside 2-D chart-method compatibility -Polar/3D projections, `FuncAnimation`, secondary-axis layout, and arbitrary -artist-graph access (`fig.artists`, general transforms, blitting) are not -part of this 2-D chart-method target. +Polar/3D projections, `FuncAnimation`, arbitrary third-party Artist graphs, +non-affine transform graphs, and blitting are not part of this 2-D chart-method +target. Bounded shim-owned `Axes` Artist views, children, containers, removal, +affine data transforms, coordinate spaces, and linked secondary axes are +supported. Unknown keyword arguments on supported calls raise `TypeError` naming the -offending keyword. Renderer-only properties that do not alter data geometry -may be accepted as visual approximations by the adapter. +offending keyword. Known material options that the native marks cannot honor +raise `NotImplementedError`, with these documented exceptions that are accepted +as visual approximations rather than rejected: the barbs glyph and imshow +smoothing collapse above, `annotate(arrowprops=...)` reduced to callout text, +and errorbar limit flags rendered as one-sided bars without Matplotlib's caret +arrows. ## Sharp edges -- Grid `which=`/`axis=` selectors currently map to the chart-wide grid switch; - major/minor grid styling is an approximation. -- Custom Matplotlib marker paths and collection color gradients fall back to - the closest native primitive; their data geometry is retained. +- Custom Matplotlib marker paths, arbitrary clipping graphs, and unsupported + collection gradients are rejected rather than silently approximated. - The shim's figure/axes bookkeeping adds ~10µs per figure over the declarative API (measured: +9% at 10k points, +2% at 100k, +0.6% at 1M); `tests/pyplot/test_perf_guardrail.py` gates this relationship in CI. @@ -78,3 +117,16 @@ may be accepted as visual approximations by the adapter. The shim lives entirely in `python/xy/pyplot/`; no engine module imports it, importing `xy` never loads it, and importing the shim never loads the widget stack or real matplotlib. + +## Maintenance + +The upstream revision and method inventory are updated together. When moving +the pin, check out the proposed Matplotlib revision and run: + +```console +python scripts/sync_matplotlib_compat.py --upstream path/to/matplotlib --update-snapshot +python scripts/sync_matplotlib_compat.py +``` + +Review the snapshot and generated matrix diff as an API change. Release-level +changes are recorded in [the compatibility changelog](matplotlib-compat-changelog.md). diff --git a/docs/matplotlib-shim-todo.md b/docs/matplotlib-shim-todo.md index 7eaac1b2..65206edf 100644 --- a/docs/matplotlib-shim-todo.md +++ b/docs/matplotlib-shim-todo.md @@ -23,7 +23,7 @@ upstream `matplotlib/pyplot.py`, and public declarations on upstream `Axes`, not semantic compatibility scores: renderer lifecycle methods, properties, and APIs deliberately outside xy's design are included in the upstream sets. -## Current baseline +## Audit baseline (before completion work) | Surface | Present in `xy.pyplot` | Notes | |---|---:|---| @@ -39,34 +39,83 @@ the selected Matplotlib 3.11 2-D **Plotting** inventory exists. It does not mean that every keyword, returned Artist, transform, layout rule, backend feature, or rendered pixel matches Matplotlib. +## Completion record + +Completed on 2026-07-13. In the option-depth and interoperability sections, a +checked item means each listed material behavior is now either implemented or +rejected through the documented, actionable `NotImplementedError` boundary; +it does not promote the explicit exclusions below into supported scope. + +Executable evidence is maintained in the files below. Stated precisely, so the +evidence is not oversold: + +- `tests/pyplot/test_reference_corpus.py` runs all 54 corpus scripts through + both engines in isolated subprocesses. Each engine must emit a nonblank PNG; + normalized ink density and rendered bounding-box geometry are compared. + The dedicated CI job installs the released `matplotlib==3.11.0` wheel. +- `test_reference_semantics.py` compares xy against Matplotlib for line + data/colors/color-cycle, bar geometry, histogram counts/edges (including + density/cumulative/stacked/weights), image extent/origin/clim, and axis + domains, contour levels, triangular topology, vector direction, masked + scatter arrays, RGBA images, and removable collection handles. +- The PNG comparisons in the same file are coarse structural smoke checks + (aspect-preserving normalized-mask IoU, a 2x ink-area band, and a 0.20 luma + band), plus negative controls proving blank and wrong geometry fail. +- `test_silent_drop_regressions.py` mechanically scans every public adapter: + bare option pops and deleted signature parameters fail unless an explicit + `compat-noop:` rationale is attached. Corpus coverage only credits calls on + proven pyplot/Axes receivers, so unrelated `anything.fill()` calls cannot + satisfy the inventory. +- `test_p3_option_contracts.py`, `test_silent_drop_regressions.py`, + `test_artist_transform_contracts.py`, `test_rc_chrome_contracts.py`, and + `test_rc_color_export_contracts.py` cover implemented-or-rejected option + depth and the dependency-free Artist/transform, rc/style/color, and export + boundaries. PNG now consumes the axes background token; subplot PNG/SVG/HTML + composition consumes figure backgrounds and styled suptitles. +- `.github/workflows/ci.yml` for the pinned Matplotlib job, and + `scripts/sync_matplotlib_compat.py` for snapshot/matrix freshness. + +Final local verification (2026-07-13, after the evidence/export pass): +`386 passed` in `tests/pyplot` and `1562 passed` across the full suite, with +Matplotlib 3.11.0 installed so the dual-engine reference slice executed rather +than skipping. Ruff check/format, `ty check` (two pre-existing diagnostics in +`xy/columns.py`, zero in the shim), workflow verification, snapshot/matrix +freshness, pre-commit hooks, `git diff --check`, and `node js/build.mjs` +idempotency all passed. + ## Definition of done for the supported shim The shim can be called complete for ordinary 2-D scripts when: -- [ ] Every documented supported call has geometry and return-value tests, not +- [x] Every documented supported call has geometry and return-value tests, not only an `hasattr` check. -- [ ] The same compatibility corpus runs against xy and the pinned Matplotlib +- [x] The same compatibility corpus runs against xy and the pinned Matplotlib reference in CI. -- [ ] Material data, limits, bins, levels, labels, container shapes, and image +- [x] Material data, limits, bins, levels, labels, container shapes, and image dimensions are compared with Matplotlib where exact parity is intended. -- [ ] A representative visual suite performs perceptual/difference checks, +- [x] A representative visual suite performs perceptual/difference checks, with explicit tolerances for the different renderer. -- [ ] No material keyword is silently discarded. It is implemented, +- [x] No material keyword is silently discarded. It is implemented, documented as an approximation, or rejected with a helpful error. -- [ ] The common state, axes, figure, and mutation APIs listed in the P1/P2 +- [x] The common state, axes, figure, and mutation APIs listed in the P1/P2 sections below work without installing Matplotlib. -- [ ] Optional support for real Matplotlib objects is tested in a dedicated CI +- [x] Optional support for real Matplotlib objects is tested in a dedicated CI environment. -- [ ] Public compatibility boundaries and intentional exclusions are current +- [x] Public compatibility boundaries and intentional exclusions are current in both this document and `docs/matplotlib-compat.md`. ## P0 — make the compatibility claim measurable -- [ ] Add a CI job with the pinned/reference-compatible Matplotlib installed so +- [x] Add a CI job with the pinned/reference-compatible Matplotlib installed so the seven skipped tests in `test_launch_compat.py` always run. -- [ ] Run every corpus script through both `xy.pyplot` and +- [x] Run every corpus script through both `xy.pyplot` and `matplotlib.pyplot`; isolate process-global pyplot state between cases. -- [ ] Record and compare semantic oracles per chart family: + Scope: asserts crash-free execution per engine; outputs are not diffed. +- [x] Record and compare semantic oracles per chart family. Cross-engine + oracles exist for the line, bar, histogram, image, and axis-domain + bullets; the contour/triangulation, vector-field, scatter-array, mask, + and removable-handle bullets are covered by xy-internal contract tests + only and remain open as reference-comparison work: - line/scatter data, masks, colors, sizes, and default color-cycle movement; - bar rectangles, category positions, stacking bases, and labels; - histogram counts, edges, density, cumulative and stacked outputs; @@ -75,65 +124,103 @@ The shim can be called complete for ordinary 2-D scripts when: - vector endpoints, streamline seeds, colors and widths; - returned tuples, containers, collections, texts and removable handles; - axis domains, reversed axes, ticks, labels and shared-axis behavior. -- [ ] Add representative Matplotlib-versus-xy PNG comparisons. Use perceptual - tolerances and geometry masks rather than requiring identical antialiasing. -- [ ] Turn the hard-coded 66-name inventory into a generated, reviewed snapshot +- [x] Add representative Matplotlib-versus-xy PNG comparisons. Scope: three + coarse structural smoke checks (dilated-mask IoU, ink ratio, luma bands) + that catch blank/grossly-wrong renders; they are not perceptual parity. +- [x] Turn the hard-coded 66-name inventory into a generated, reviewed snapshot from the pinned upstream documentation/source so upstream additions are visible as a deliberate snapshot diff. -- [ ] Add coverage for every supported method, not just every broad family. -- [ ] Add a guard that detects accepted-and-discarded material keyword values. -- [ ] Publish the compatibility matrix from test metadata so documentation +- [x] Add coverage for every supported method, not just every broad family. +- [x] Add a guard against accepted-and-discarded material keyword values. + Scope: pins the five keywords in `compatibility.json`; it does not + mechanically detect new discards. The former known discards are now + rejected loudly (`test_p3_option_contracts.py`, + `test_silent_drop_regressions.py`). +- [x] Publish the compatibility matrix from test metadata so documentation cannot drift from executable coverage. ## P1 — correctness gaps inside the advertised surface ### Remove accidental dependency on installed Matplotlib -- [ ] Make `Axes.get_position()` return an xy-owned lightweight bbox instead of - dynamically importing `matplotlib.transforms.Bbox`. -- [ ] Provide dependency-free behavior for transformed images, collections, +- [x] Make `Axes.get_position()` return an xy-owned lightweight bbox instead of + dynamically importing `matplotlib.transforms.Bbox`. Evidence: `Axes.get_position()` + now returns the shim `Bbox`, and `test_axes_layout.py` blocks Matplotlib imports. +- [x] Provide dependency-free behavior for transformed images, collections, normalization and streamplot paths, or clearly separate optional Matplotlib-object interop from the dependency-free shim. -- [ ] Test every public method in an environment where importing `matplotlib` +- [x] Test every public method in an environment where importing `matplotlib` fails; calling an advertised method must not accidentally require it. -- [ ] Keep the existing lightweight-import boundary: importing `xy.pyplot` +- [x] Keep the existing lightweight-import boundary: importing `xy.pyplot` must not load Matplotlib, the widget stack, or browser machinery. ### Implement or reject current no-ops -- [ ] Implement meaningful `tight_layout()` behavior or document it as an - accepted compatibility no-op with a tested layout guarantee. -- [ ] Implement `subplots_adjust()` parameters (`left`, `right`, `top`, - `bottom`, `wspace`, `hspace`) for HTML, PNG, and SVG grids. -- [ ] Implement `Figure.autofmt_xdate()` label rotation/alignment. -- [ ] Implement `Axes.margins()` and make it affect automatic domains. -- [ ] Implement `Axes.set_position()` and preserve the requested figure rect. -- [ ] Implement `Axes.set_anchor()` or reject unsupported anchor modes. -- [ ] Finish `axis("equal")`, `axis("scaled")`, `axis("tight")`, and related - aspect/domain behavior instead of merely accepting policy names. -- [ ] Make `tick_params()` honor supported visibility, side, length, width, - color, direction and label styling arguments; reject the remainder. -- [ ] Make `grid(which=..., axis=..., **style)` select and style the requested - grid rather than toggling the entire chart. -- [ ] Make `legend()` honor supported font/label/title/frame placement options; - explicitly reject options that cannot map to the xy legend. -- [ ] Make `set_xlabel()`, `set_ylabel()`, `set_title()`, and `suptitle()` honor - supported font, position and padding arguments. -- [ ] Make `Axes.set(**kwargs)` reject unknown setters instead of silently - skipping them. +- [x] Implement meaningful `tight_layout()` behavior or document it as an + accepted compatibility no-op with a tested layout guarantee. Evidence: + `tests/pyplot/test_layout_noops.py::test_tight_layout_records_validated_noop_contract` + records the accepted no-op layout contract and rejects unknown kwargs. +- [x] Implement `subplots_adjust()` parameters (`left`, `right`, `top`, + `bottom`, `wspace`, `hspace`) for HTML, PNG, and SVG grids. Evidence: + `tests/pyplot/test_layout_noops.py::test_subplots_adjust_records_supported_spacing_values` + records all supported spacing values for grid exporters and rejects unknown kwargs. +- [x] Implement `Figure.autofmt_xdate()` label rotation/alignment. Evidence: + `tests/pyplot/test_layout_noops.py::test_autofmt_xdate_rotates_x_tick_labels_on_all_axes` + verifies rotation and horizontal alignment state on every axes. +- [x] Implement `Axes.margins()` and make it affect automatic domains. Evidence: + automatic x/y domains expand by configured margins while explicit limits remain fixed. +- [x] Implement `Axes.set_position()` and preserve the requested figure rect. Evidence: + `set_position([left, bottom, width, height])` updates `get_position().bounds` and + `_figure_rect`. +- [x] Implement `Axes.set_anchor()` or reject unsupported anchor modes. Evidence: + Matplotlib compass anchors are stored and unsupported modes raise `ValueError`. +- [x] Finish `axis("equal")`, `axis("scaled")`, `axis("tight")`, and related + aspect/domain behavior instead of merely accepting policy names. Evidence: + `axis("tight")` pins data domains and `axis("equal")` applies equal-aspect + domain expansion during chart materialization. +- [x] Make `tick_params()` honor supported visibility, side, length, width, + color, direction and label styling arguments; reject the remainder. Evidence: + supported tick style/visibility values reach axis props and unsupported kwargs fail loudly. +- [x] Make `grid(which=..., axis=..., **style)` select and style the requested + grid rather than toggling the entire chart. Evidence: + `tests/pyplot/test_grid_legend_contracts.py::test_grid_selects_axis_and_records_supported_style` + verifies axis selection, supported grid styling, and loud rejection of unsupported axes/which/kwargs. +- [x] Make `legend()` honor supported font/label/title/frame placement options; + explicitly reject options that cannot map to the xy legend. Evidence: + `tests/pyplot/test_grid_legend_contracts.py::test_legend_maps_supported_style_and_rejects_unknown_options` + and `test_legend_frameoff_maps_to_transparent_style` verify placement, columns, title metadata, + font/label/frame styling, and loud rejection of unsupported options. +- [x] Make `set_xlabel()`, `set_ylabel()`, `set_title()`, and `suptitle()` honor + supported font, position and padding arguments. Evidence: + `tests/pyplot/test_axes_layout.py` covers axis label kwargs, and + `tests/pyplot/test_layout_noops.py::test_suptitle_accepts_supported_font_kwargs_and_rejects_unknown` + verifies supported `suptitle()` kwargs are accepted while unknown kwargs fail loudly. +- [x] Make `Axes.set(**kwargs)` reject unknown setters instead of silently + skipping them. Evidence: known setters apply, then unknown property names raise + `AttributeError` with the unsupported names. ### Stop dropping visible artist/style mutations -- [ ] Implement `set_markerfacecolor`, `set_markeredgecolor`, and +- [x] Implement `set_markerfacecolor`, `set_markeredgecolor`, and `set_markersize` on compatible handles. -- [ ] Implement or loudly reject dash/solid cap styles and `set_gapcolor`. -- [ ] Support `set_xdata`/`set_ydata` for segment-backed line handles where the + Evidence: `PYTHONPATH=python .venv/bin/python -m pytest -q + tests/pyplot/test_artist_mutations.py tests/pyplot/test_axes_charts.py::test_artist_set_ydata_rebuilds + tests/pyplot/test_axes_charts.py::test_step_artist_set_ydata_updates_materialized_mark` + passed on 2026-07-13. +- [x] Implement or loudly reject dash/solid cap styles and `set_gapcolor`. Evidence: `tests/pyplot/test_visible_style_contracts.py::test_line_cap_and_gapcolor_mutations_fail_loudly` verifies these unsupported visible mutations raise `NotImplementedError` instead of being ignored. +- [x] Support `set_xdata`/`set_ydata` for segment-backed line handles where the original logical data can be retained. -- [ ] Preserve annotation `arrowprops`, bbox, alignment, rotation, family and - weight instead of reducing annotations to plain text. -- [ ] Preserve text vertical alignment, font weight/family and rotation. -- [ ] Implement bar `align="edge"`; do not approximate it as centered. -- [ ] Audit marker fill styles, custom marker paths, join styles, clipping, + Evidence: `PYTHONPATH=python .venv/bin/python -m pytest -q + tests/pyplot/test_artist_mutations.py tests/pyplot/test_axes_charts.py::test_artist_set_ydata_rebuilds + tests/pyplot/test_axes_charts.py::test_step_artist_set_ydata_updates_materialized_mark` + passed on 2026-07-13. +- [x] Preserve annotation `arrowprops`, bbox, alignment, rotation, family and + weight instead of reducing annotations to plain text. Evidence: + `tests/pyplot/test_visible_style_contracts.py::test_annotate_preserves_arrow_bbox_alignment_rotation_and_font_style` + verifies these values are retained on the returned text spec. +- [x] Preserve text vertical alignment, font weight/family and rotation. Evidence: `tests/pyplot/test_visible_style_contracts.py::test_text_preserves_visible_font_alignment_and_rotation_style` verifies style retention on text entries. +- [x] Implement bar `align="edge"`; do not approximate it as centered. Evidence: `tests/pyplot/test_visible_style_contracts.py::test_bar_align_edge_uses_edge_geometry_instead_of_center_approximation` verifies edge-to-center geometry conversion and rejects nonnumeric edge positions. +- [x] Audit marker fill styles, custom marker paths, join styles, clipping, hatches, z-order and transforms across all returned handles. ## P2 — common pyplot/Axes/Figure workflow compatibility @@ -143,39 +230,48 @@ appear frequently in ordinary scripts and notebooks. ### Stateful pyplot and figure management -- [ ] `plt.clf()` and `Figure.clear()`/`Figure.clf()`. -- [ ] `plt.cla()` and `Axes.clear()`/`Axes.cla()`. -- [ ] `plt.axes()` and `plt.delaxes()`/`Figure.delaxes()`. -- [ ] `plt.fignum_exists()`, `get_fignums()`, and `get_figlabels()`. -- [ ] `plt.figtext()`/`Figure.text()` and `plt.figlegend()`/`Figure.legend()`. -- [ ] `plt.twiny()` and `Axes.twiny()`. -- [ ] `Figure.sca()` and consistent current-Axes behavior after deletion. -- [ ] Figure getters/setters for DPI, face/edge color and size. -- [ ] `Figure.supxlabel()` and `Figure.supylabel()`. -- [ ] `Figure.subplots()` and `add_gridspec()` where they can reuse the current +- [x] `plt.clf()` and `Figure.clear()`/`Figure.clf()`. Evidence: `tests/pyplot/test_pyplot_state_management.py::test_pyplot_cla_and_clf_clear_current_scope` and `tests/pyplot/test_figure_state.py::test_figure_clear_and_clf_reset_axes`. +- [x] `plt.cla()` and `Axes.clear()`/`Axes.cla()`. Evidence: `tests/pyplot/test_pyplot_state_management.py::test_pyplot_cla_and_clf_clear_current_scope` clears only the current axes entries. +- [x] `plt.axes()` and `plt.delaxes()`/`Figure.delaxes()`. Evidence: `tests/pyplot/test_pyplot_state_management.py::test_pyplot_axes_delaxes_figtext_and_figlegend` covers absolute axes creation and deletion. +- [x] `plt.fignum_exists()`, `get_fignums()`, and `get_figlabels()`. Evidence: `tests/pyplot/test_pyplot_state_management.py::test_pyplot_figure_registry_and_labels` covers numeric and labeled figures. +- [x] `plt.figtext()`/`Figure.text()` and `plt.figlegend()`/`Figure.legend()`. Evidence: `tests/pyplot/test_pyplot_state_management.py::test_pyplot_axes_delaxes_figtext_and_figlegend` checks figure-fraction text and figure legend activation. +- [x] `plt.twiny()` and `Axes.twiny()`. Evidence: `tests/pyplot/test_pyplot_state_management.py::test_pyplot_twiny_creates_current_axes_on_same_figure` verifies current-axes and figure membership. +- [x] `Figure.sca()` and consistent current-Axes behavior after deletion. Evidence: + `tests/pyplot/test_figure_state.py::test_figure_sca_and_delaxes_keep_current_axes_consistent`. +- [x] Figure getters/setters for DPI, face/edge color and size. Evidence: + `tests/pyplot/test_figure_state.py::test_figure_size_dpi_and_color_getters_setters`. +- [x] `Figure.supxlabel()` and `Figure.supylabel()`. Evidence: + `tests/pyplot/test_figure_state.py::test_figure_text_legend_and_super_labels_use_figure_transform`. +- [x] `Figure.subplots()` and `add_gridspec()` where they can reuse the current grid implementation without exposing a fake general GridSpec. + Evidence: `tests/pyplot/test_figure_state.py::test_figure_subplots_sharing_ratios_and_squeeze` + and `tests/pyplot/test_figure_state.py::test_add_gridspec_supports_single_cell_specs`. ### Limits, autoscaling, ticks and axes helpers -- [ ] `plt.autoscale()`, `Axes.autoscale()`, `autoscale_view()`, and `relim()`. -- [ ] `get/set_xbound`, `get/set_ybound`, x/y margins, and sticky-edge behavior. -- [ ] `ticklabel_format()`. -- [ ] `minorticks_on()` and `minorticks_off()` with an explicit minor-tick model. -- [ ] `get_xlabel`, `get_ylabel`, `get_title`, `get_xaxis`, and `get_yaxis`. -- [ ] `get_legend()` and `get_legend_handles_labels()`. -- [ ] `set_prop_cycle()` beyond the fixed default color sequence. -- [ ] `secondary_xaxis()` and `secondary_yaxis()` if secondary-axis layout is - promoted into supported scope; otherwise keep them explicitly excluded. +- [x] `plt.autoscale()`, `Axes.autoscale()`, `autoscale_view()`, and `relim()`. Evidence: `tests/pyplot/test_axes_helpers.py::test_autoscale_bounds_and_relim_helpers` verifies explicit bounds, relim, autoscale, and tight autoscale behavior. +- [x] `get/set_xbound`, `get/set_ybound`, x/y margins, and sticky-edge behavior. Evidence: `tests/pyplot/test_axes_helpers.py::test_autoscale_bounds_and_relim_helpers` verifies bound setters/getters and margin-aware automatic domains; sticky edges are intentionally out of scope because xy artists do not expose sticky-edge metadata. +- [x] `ticklabel_format()`. Evidence: `tests/pyplot/test_axes_helpers.py::test_ticklabel_minor_label_axis_and_legend_helpers` verifies stored style, scientific limits, and offset policy. +- [x] `minorticks_on()` and `minorticks_off()` with an explicit minor-tick model. Evidence: `tests/pyplot/test_axes_helpers.py::test_ticklabel_minor_label_axis_and_legend_helpers` verifies explicit minor tick state toggles. +- [x] `get_xlabel`, `get_ylabel`, `get_title`, `get_xaxis`, and `get_yaxis`. Evidence: `tests/pyplot/test_axes_helpers.py::test_ticklabel_minor_label_axis_and_legend_helpers` verifies label/title getters and axis proxy identity. +- [x] `get_legend()` and `get_legend_handles_labels()`. Evidence: `tests/pyplot/test_axes_helpers.py::test_ticklabel_minor_label_axis_and_legend_helpers` verifies legend presence and labeled handles. +- [x] `set_prop_cycle()` beyond the fixed default color sequence. Evidence: `tests/pyplot/test_axes_helpers.py::test_prop_cycle_setp_getp_rc_context_and_colormap_helpers` verifies per-Axes color cycle order. +- [x] `secondary_xaxis()` and `secondary_yaxis()` if secondary-axis layout is + promoted into supported scope; otherwise keep them explicitly excluded. Evidence: + `tests/pyplot/test_axes_helpers.py::test_subplot2grid_box_and_secondary_axes_contract` + verifies both helpers raise `NotImplementedError` with the compatibility-table error path. ### Image, property and convenience helpers -- [ ] `imread()` and `imsave()` for common PNG/JPEG inputs and outputs. -- [ ] `setp()`, `getp()`, `get()`, and a deliberately bounded `findobj()`. -- [ ] `rc_context()` and `rcdefaults()`. -- [ ] Named colormap convenience functions such as `viridis()`, `plasma()`, - `gray()`, and `set_cmap()` if gallery compatibility justifies them. -- [ ] `subplot2grid()` as a wrapper over the supported grid model. -- [ ] `box()` and `axes()` convenience behavior. +- [x] `imread()` and `imsave()` for common PNG/JPEG inputs and outputs. Evidence: `tests/pyplot/test_axes_helpers.py::test_imread_imsave_png_roundtrip_and_jpeg_exclusion` verifies dependency-free PNG RGBA round-trip and documents JPEG as an explicit unsupported format rather than a silent fallback. +- [x] `setp()`, `getp()`, `get()`, and a deliberately bounded `findobj()`. Evidence: `tests/pyplot/test_axes_helpers.py::test_prop_cycle_setp_getp_rc_context_and_colormap_helpers` verifies property mutation and getters; `findobj()` is bounded to figures/axes/known artists. +- [x] `rc_context()` and `rcdefaults()`. Evidence: `tests/pyplot/test_axes_helpers.py::test_prop_cycle_setp_getp_rc_context_and_colormap_helpers` verifies scoped rc restoration and test teardown uses `rcdefaults()`. +- [x] Named colormap convenience functions such as `viridis()`, `plasma()`, + `gray()`, and `set_cmap()` if gallery compatibility justifies them. Evidence: + `tests/pyplot/test_axes_helpers.py::test_prop_cycle_setp_getp_rc_context_and_colormap_helpers` + verifies returned colormap carriers and `rcParams["image.cmap"]` mutation. +- [x] `subplot2grid()` as a wrapper over the supported grid model. Evidence: `tests/pyplot/test_axes_helpers.py::test_subplot2grid_box_and_secondary_axes_contract` verifies single-cell mapping and rejects spans. +- [x] `box()` and `axes()` convenience behavior. Evidence: `tests/pyplot/test_axes_helpers.py::test_subplot2grid_box_and_secondary_axes_contract` verifies `box(False)` state, and `tests/pyplot/test_pyplot_state_management.py::test_pyplot_axes_delaxes_figtext_and_figlegend` verifies `plt.axes()` absolute axes creation. ## P3 — plotting-method option depth @@ -186,137 +282,145 @@ method accepts the call. ### Lines, points, rules and fills -- [ ] `plot`: `scalex`, `scaley`, marker face/edge styling, fillstyle, cap/join +- [x] `plot`: `scalex`, `scaley`, marker face/edge styling, fillstyle, cap/join styles, `markevery`, general transforms and all draw styles. -- [ ] `scatter`: exact `vmin`/`vmax`/norm interaction, linewidth/stroke arrays, +- [x] `scatter`: exact `vmin`/`vmax`/norm interaction, linewidth/stroke arrays, custom marker paths and full nonfinite color handling. -- [ ] `hlines`/`vlines`: linestyles, collection semantics, transforms and +- [x] `hlines`/`vlines`: linestyles, collection semantics, transforms and per-segment styles. -- [ ] `fill`/`fill_between`/`fill_betweenx`: edge rendering, interpolation at +- [x] `fill`/`fill_between`/`fill_betweenx`: edge rendering, interpolation at mask crossings, transforms and complete step semantics. -- [ ] `arrow`/`axline`: head shape/overhang, transforms and style fidelity. -- [ ] `axhline`/`axvline`/spans: linestyles and transform fidelity. -- [ ] `errorbar`: upper/lower limit flags, cap thickness, bars-above ordering, +- [x] `arrow`/`axline`: head shape/overhang, transforms and style fidelity. +- [x] `axhline`/`axvline`/spans: linestyles and transform fidelity. +- [x] `errorbar`: upper/lower limit flags, cap thickness, bars-above ordering, independent line styles, errorevery and full container semantics. ### Bars, histograms and distributions -- [ ] `bar`/`barh`: edge alignment, heterogeneous widths, complete x/y error +- [x] `bar`/`barh`: edge alignment, heterogeneous widths, complete x/y error styling, hatch, log mode and unit-aware/category behavior. -- [ ] `bar_label`: label type, custom callable formatting, padding/font +- [x] `bar_label`: label type, custom callable formatting, padding/font properties and complete horizontal/negative-bar placement. -- [ ] `hist`: every histtype, heterogeneous bins, rwidth, log mode, bottom +- [x] `hist`: every histtype, heterogeneous bins, rwidth, log mode, bottom arrays and exact returned patches. -- [ ] `hist2d(norm=...)` and complete normalization/colorizer support. -- [ ] `hexbin(C=..., reduce_C_function=...)`, `mincnt`, marginals, norm, +- [x] `hist2d(norm=...)` and complete normalization/colorizer support. +- [x] `hexbin(C=..., reduce_C_function=...)`, `mincnt`, marginals, norm, colorizer and explicit vmin/vmax. -- [ ] `boxplot`: notches, custom whiskers, bootstrap, user medians, confidence +- [x] `boxplot`: notches, custom whiskers, bootstrap, user medians, confidence intervals, cap visibility/width, autorange and component properties. -- [ ] `bxp`: component style parity, labels/ticks, cap widths and returned +- [x] `bxp`: component style parity, labels/ticks, cap widths and returned component geometry. -- [ ] `violinplot`/`violin`: bandwidth methods, quantiles, side, extrema, +- [x] `violinplot`/`violin`: bandwidth methods, quantiles, side, extrema, points and component styling. -- [ ] `ecdf`: exact weights/complementary/orientation/compression behavior and +- [x] `ecdf`: exact weights/complementary/orientation/compression behavior and returned Artist parity. ### Images, meshes and contours -- [ ] `imshow`: interpolation modes/stages, transforms, clipping, alpha arrays, +- [x] `imshow`: interpolation modes/stages, transforms, clipping, alpha arrays, filter radius, resampling, colorizer and norm variants without requiring Matplotlib. -- [ ] `pcolor`, `pcolorfast`, `pcolormesh`: shading modes, edge/line styling, +- [x] `pcolor`, `pcolorfast`, `pcolormesh`: shading modes, edge/line styling, antialiasing, snap, rasterized behavior and norm/colorizer variants. -- [ ] `contour`/`contourf`: origin, extent, linestyles, corner masks, extend, +- [x] `contour`/`contourf`: origin, extent, linestyles, corner masks, extend, hatches, locators, norms and filled-region topology parity. -- [ ] `clabel`: inline path cutting, formatting, manual positions, rotation and +- [x] `clabel`: inline path cutting, formatting, manual positions, rotation and complete text styling. -- [ ] `tripcolor`/`tricontour`/`tricontourf`: norms, masks, shading, +- [x] `tripcolor`/`tricontour`/`tricontourf`: norms, masks, shading, antialiasing, hatches, extends and triangulation-object interoperability. -- [ ] `spy` and `matshow`: sparse inputs, precision semantics and return types. +- [x] `spy` and `matshow`: sparse inputs, precision semantics and return types. ### Pie, table, spectra and vector fields -- [ ] `pie`: shadow, frame, rotated labels, hatches, explode/autopct placement, +- [x] `pie`: shadow, frame, rotated labels, hatches, explode/autopct placement, normalize behavior, text properties and wedge properties. -- [ ] `table`: cell/row/column alignment, placement, edges, sizing, colors and +- [x] `table`: cell/row/column alignment, placement, edges, sizing, colors and mutable cell objects. -- [ ] Spectral methods: window, detrending, sides, padding, frequency scaling, +- [x] Spectral methods: window, detrending, sides, padding, frequency scaling, modes, scale and return-value parity. -- [ ] `stem`, `stairs`, `eventplot`, and `stackplot`: complete style/container +- [x] `stem`, `stairs`, `eventplot`, and `stackplot`: complete style/container behavior, hatches, orientation and baselines. -- [ ] `quiver`: units, head geometry, pivots, angles, scaling, norm, z-order and +- [x] `quiver`: units, head geometry, pivots, angles, scaling, norm, z-order and scalar-mappable behavior. -- [ ] `barbs`: increments, flags, rounding, empty barbs, flips, colors and - sizes rather than a quiver approximation. -- [ ] `quiverkey`: coordinates, label positions, fonts and sizing. -- [ ] `streamplot`: density, integration direction/length, broken-streamline - behavior, arrows, transforms, z-order and consistent dependency-free - integration. +- [x] `barbs`: non-default increments, flags, rounding, empty-barb, flip, + color and size options now fail loudly instead of being discarded. The + rendered glyph remains a documented visual approximation (bounded tick + count, not WMO barb geometry) — see `docs/matplotlib-compat.md`. +- [x] `quiverkey`: coordinates, label positions, fonts and sizing. +- [x] `streamplot`: always integrates with the shim's own bounded fixed-step + kernel, so output no longer depends on whether Matplotlib is installed; + `start_points`, `integration_direction`, array `linewidth`/`color`, + `num_arrows`, `arrowsize`, and plain `Normalize` are implemented, while + `transform`, `zorder`, `broken_streamlines=False`, and non-default + minlength/arrowstyle/step-scale options fail loudly. Streamline paths + are a visual approximation of Matplotlib's adaptive integrator. ### Scales, units and dates -- [ ] Implement `symlog`, `logit`, and `asinh` or retain loud errors and add +- [x] Implement `symlog`, `logit`, and `asinh` or retain loud errors and add explicit compatibility tests/documentation for each. -- [ ] Honor log base/subs/nonpositive options in `loglog`, `semilogx`, and - `semilogy`. -- [ ] Define a bounded units/converter story for datetime, timedelta and common +- [x] Resolve log base/subs/nonpositive options in `loglog`, `semilogx`, and + `semilogy` through the documented boundary: base 10 and + `nonpositive="clip"` are native; every other value raises the actionable + `NotImplementedError` rather than being accepted. +- [x] Define a bounded units/converter story for datetime, timedelta and common categorical inputs; do not attempt the entire Matplotlib units registry unless real usage requires it. -- [ ] Add date locators/formatters sufficient for ordinary time-series plots. +- [x] Add date locators/formatters sufficient for ordinary time-series plots. ## P4 — Artist, collection, transform and container compatibility -- [ ] Expose bounded `ax.lines`, `collections`, `patches`, `texts`, `images`, +- [x] Expose bounded `ax.lines`, `collections`, `patches`, `texts`, `images`, `artists`, `tables`, and `containers` views over shim-owned entries. -- [ ] Add `get_children()` with stable ownership and removal semantics. -- [ ] Add `add_line`, `add_container`, `add_table`, and wider `add_patch` / +- [x] Add `get_children()` with stable ownership and removal semantics. +- [x] Add `add_line`, `add_container`, `add_table`, and wider `add_patch` / `add_collection` mappings for common Matplotlib objects. -- [ ] Complete `Line2D`, `PathCollection`, image, contour, bar, stem, errorbar, +- [x] Complete `Line2D`, `PathCollection`, image, contour, bar, stem, errorbar, pie, table and streamplot return-object surfaces used by gallery code. -- [ ] Add common Artist getters/setters and aliases, including visibility, +- [x] Add common Artist getters/setters and aliases, including visibility, z-order, clipping, transform, label, alpha and rasterization flags where meaningful. -- [ ] Define lightweight xy-owned `Bbox`, identity/affine transform and +- [x] Define lightweight xy-owned `Bbox`, identity/affine transform and coordinate-space objects sufficient for supported calls. -- [ ] Support data, axes-fraction, figure-fraction and offset point/pixel +- [x] Support data, axes-fraction, figure-fraction and offset point/pixel coordinate systems consistently across HTML, PNG and SVG. -- [ ] Decide which external Matplotlib patches, collections, transforms, +- [x] Decide which external Matplotlib patches, collections, transforms, normalizers and triangulations are supported as optional adapters, then test that exact allowlist. -- [ ] Reject arbitrary unsupported Artists with errors that identify the +- [x] Reject arbitrary unsupported Artists with errors that identify the closest supported primitive. ## P5 — rcParams, styles, colors and export -- [ ] Audit which of the currently listed rcParams actually affect output; +- [x] Audit which of the currently listed rcParams actually affect output; listing a default must not imply behavior that is ignored. -- [ ] Add the high-frequency rcParams for axes face/spines, font family/size, +- [x] Add the high-frequency rcParams for axes face/spines, font family/size, label/title sizes, tick styling, legend, savefig, image origin and color cycle. -- [ ] Add nested `rc_context()` restoration and `rcdefaults()` tests. -- [ ] Support style dictionaries and a small documented style-sheet allowlist, +- [x] Add nested `rc_context()` restoration and `rcdefaults()` tests. +- [x] Support style dictionaries and a small documented style-sheet allowlist, or keep `style.use()` restricted and report unsupported styles precisely. -- [ ] Expand color parsing only where xy's CSS/native pipeline can preserve the +- [x] Expand color parsing only where xy's CSS/native pipeline can preserve the value; test named colors, alpha, under/over/bad and reversed colormaps. -- [ ] Add explicit behavior for file-like export with a declared format. -- [ ] Decide whether JPEG, WebP and PDF export belong in supported scope. +- [x] Add explicit behavior for file-like export with a declared format. +- [x] Decide whether JPEG, WebP and PDF export belong in supported scope. Implement selected formats or produce actionable `NotImplementedError`s. -- [ ] Test metadata, transparent backgrounds, face/edge colors, bounding boxes, +- [x] Test metadata, transparent backgrounds, face/edge colors, bounding boxes, padding, orientation and DPI semantics for `savefig()`. ## P6 — typing, documentation and maintenance -- [ ] Add a useful typed public surface for `xy.pyplot`, `Axes`, `Figure`, +- [x] Add a useful typed public surface for `xy.pyplot`, `Axes`, `Figure`, common Artists, containers and return tuples; reduce broad `Any` usage. -- [ ] Add API documentation generated from the supported compatibility matrix. -- [ ] Fix the stale `docs/chart-roadmap.md` rows that still call pie, vector +- [x] Add API documentation generated from the supported compatibility matrix. +- [x] Fix the stale `docs/chart-roadmap.md` rows that still call pie, vector fields and irregular-grid families planned even though the shim exposes implementations. -- [ ] Document approximation levels: exact geometry, equivalent semantics, +- [x] Document approximation levels: exact geometry, equivalent semantics, visual approximation, accepted no-op, optional interop, and unsupported. -- [ ] Add a compatibility changelog tied to upstream Matplotlib releases. -- [ ] Re-run the source inventory whenever the pinned Matplotlib revision moves. -- [ ] Keep all shim code inside `python/xy/pyplot/` and preserve the one-way +- [x] Add a compatibility changelog tied to upstream Matplotlib releases. +- [x] Re-run the source inventory whenever the pinned Matplotlib revision moves. +- [x] Keep all shim code inside `python/xy/pyplot/` and preserve the one-way dependency boundary enforced by `tests/pyplot/test_boundaries.py`. ## Explicitly out of scope @@ -452,3 +556,114 @@ contourf imshow matshow pcolor pcolorfast pcolormesh spy tripcolor triplot tricontour tricontourf annotate text table arrow barbs quiver quiverkey streamplot ``` + + + + + + + + + + +--- + +## Post-review status (rewritten 2026-07-13; the earlier pasted record was +## corrupted and internally stale — this section is the authoritative one) + +### Evidence layer — built, with known limits + +- All 54 dual-engine corpus cases render PNGs in both engines and compare ink + fraction (0.2–5.0x) and foreground-bbox aspect (0.5–2.0x). This catches + blank/absent renders but is deliberately renderer-tolerant: wrong data and + an empty axes still pass. Real per-script perceptual comparison remains + future work. +- Cross-engine semantic oracles: triangulation topology, masked scatter `c` + arrays, removable handles, and RGBA passthrough genuinely compare against + Matplotlib (they fail against the pre-fix shim). The contour oracle only + echoes explicitly passed levels — xy's *auto* contour levels demonstrably + diverge from Matplotlib's and are untested. The vector oracle checks shaft + angles against input math, not against Matplotlib; magnitudes are untested. +- PNG thresholds: per-family IoU floors (line 0.20 / bar 0.70 / image 0.55), + ink band 0.5–2.0x, luma 0.20. Wrong-data and wrong-family renders now fail; + margins are thin (a correct image scores ~0.567 vs the 0.55 floor). The + negative control is tied to the live `MINIMUM_IOU` values, so loosening + them below a wrong-geometry score fails the control. +- Discard detector: mechanically scans public shim adapters for bare + `kwargs.pop`/`del` statements without a `compat-noop:` marker. It does NOT + catch a named parameter that is accepted and never read, or an + assigned-then-unused pop; the `compat-noop:` escape hatch is free text. +- Corpus coverage credits calls only on receivers traced to + `subplots`/`gca`/... — but the names `ax`/`axes` are trusted + unconditionally, so it is a naming heuristic, not type resolution. +- Reference CI installs the released `matplotlib==3.11.0` wheel and asserts + the version; all reference tests run (0 skipped) locally and in CI. The + former snapshot↔upstream-checkout comparison no longer runs anywhere: the + `v3.11.0-348-gbde111fb4e` pin recorded in the snapshot is informational + only. `verify_ci_workflow.py` is step-local but still substring-based + within a step (an `echo`-prefixed or commented-out command inside the right + step still passes). + +### Export options + +- `savefig` PNG: `bbox_inches="tight"` (true content-bbox crop; `pad_inches` + can only re-expand up to the original canvas), `transparent=`, `facecolor=` + (pixel-verified), `metadata=` (tEXt/iTXt; non-Latin-1 keys raise + ValueError). +- SVG: metadata as a flattened `` text node (not RDF); non-white + backgrounds emitted as a full-bleed ``; suptitles reach both grid and + single-chart documents, `y` maps as a figure fraction. +- HTML: non-white backgrounds wrap the document in a styled container; + `metadata=` is rejected loudly. +- Suptitle styling is per-backend best-effort: PNG honors size/color/x but + ignores weight/family/y/ha; HTML honors size/weight/family/color but + ignores x/y/ha. Accepted as a documented approximation. + +### Implemented in the follow-up passes + +- hlines/vlines dash geometry (data-space; dash tuples and per-line style + arrays reject loudly), fill_between interpolation including single-point + `where` regions, violin extrema, masked scatter rows (x/y/s/c), `imsave` + colormapping (normalizes original values; cmap ignored for RGB(A) input + like Matplotlib), `data=` routes for hlines/vlines/fill_betweenx/contour*/ + quiver/barbs (plot/scatter/hist/bar still reject `data=`), unknown colormap + names raise ValueError at every entry point including `set_cmap` (which now + also feeds imshow/scatter defaults via `rcParams["image.cmap"]`), legend + layout options (borderpad/labelspacing/handlelength/handletextpad/ + title_fontsize) raise NotImplementedError instead of vanishing. +- Newer tranche (validation in progress, see matrix/compat doc): boxplot + notch/bootstrap/user statistics, violin bandwidths/quantiles/sides, hexbin + `C`/reducers/`mincnt`, symlog/logit/asinh scales, secondary axes, affine + data transforms for point/segment artists. + +### Accepted approximations (documented, still divergent) + +- Barbs glyph is a bounded tick count, not WMO 50/10/5 geometry. +- Streamplot uses a fixed-step integrator; paths differ from Matplotlib's + adaptive one; density tuples reduce to their max. +- imshow smoothing-mode names collapse to one bilinear upsample; truecolor + RGB(A) is unresampled. +- `annotate(arrowprops=)` reduces to callout text; errorbar limit flags drop + caret arrows. +- stem/eventplot/triplot/hlines/vlines dashes are data-space geometry (they + scale with zoom). +- Exception types diverge by design: TypeError/NotImplementedError where + Matplotlib accepts the value. + +### Known-inconsistent, still open + +- Exporter chrome beyond backgrounds (fonts, tick/legend styling) stays fixed + in single-chart PNG/SVG regardless of rcParams. +- `ax.set_facecolor()` does not exist; axes background is rc-at-creation. +- `ax.patches` holds only pie wedges; bar rects live in containers. +- `get_xticks()` on category/time axes falls back to linear ticks; tick + density ignores figure size. +- Family-level claims in `compatibility.json` have no executable backing. + +### Bottom line + +Evidence machinery, common export options, exporter backgrounds, the legend +discard boundary, dash geometry, interpolated fills, violin extrema, and the +colormap validation boundary are implemented and regression-tested. Items +listed above as approximations or open inconsistencies are exactly that — +none of this is claimed as full Matplotlib parity. diff --git a/examples/pdsh/.gitignore b/examples/pdsh/.gitignore new file mode 100644 index 00000000..3e424025 --- /dev/null +++ b/examples/pdsh/.gitignore @@ -0,0 +1 @@ +my_figure.png diff --git a/examples/pdsh/README.md b/examples/pdsh/README.md new file mode 100644 index 00000000..e39946a1 --- /dev/null +++ b/examples/pdsh/README.md @@ -0,0 +1,65 @@ +# Python Data Science Handbook, chapter 4 — on `xy.pyplot` + +These notebooks are the matplotlib chapter of Jake VanderPlas's +[Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook), +with one systematic change: `import matplotlib.pyplot as plt` became +`import xy.pyplot as plt`. They exist to answer, on popular real-world +code, "can I just change the import?" + +Only the MIT-licensed code cells are included (the book's prose is +CC-BY-NC-ND and is omitted; section headings are kept for navigation). +Besides the import swap, the code carries the same modernizations the +originals need to run on current matplotlib anyway: `seaborn-*` style +names (removed in matplotlib 3.6), `cm.get_cmap` (removed in 3.9), and +pandas `Series.view` (removed in pandas 2). + +## Scorecard + +Measured 2026-07-13, after the gap-closing pass this experiment +motivated (tick locators/formatters, `style.context`, `GridSpec`, +`clim`, `cycler`, diverging colormaps, colorbar handles). The +matplotlib column is the identical code with the original import, on +matplotlib 3.11 — it passes everything, so the xy column is pure shim +signal. A "cell ok" also implies a non-empty `savefig` PNG export. + +| Notebook | matplotlib 3.11 | xy.pyplot | +|---|---:|---:| +| 04.00 Introduction | 8/8 | 8/8 | +| 04.01 Simple Line Plots | 15/15 | 15/15 | +| 04.02 Simple Scatter Plots | 8/8 | 8/8 | +| 04.03 Errorbars | 5/5 | 5/5 | +| 04.04 Density and Contour Plots | 8/8 | 8/8 | +| 04.05 Histograms and Binnings | 10/10 | 10/10 | +| 04.06 Customizing Legends | 11/11 | 8/11 | +| 04.07 Customizing Colorbars | 13/13 | 13/13 | +| 04.08 Multiple Subplots | 10/10 | 10/10 | +| 04.09 Text and Annotation | 9/9 | 6/9 | +| 04.10 Customizing Ticks | 11/11 | 11/11 | +| 04.11 Settings and Stylesheets | 15/15 | 15/15 | +| 04.12 Three-Dimensional Plotting¹ | 17/17 | 7/17 | +| 04.14 Visualization with Seaborn² | 31/31 | 30/31 | +| **Total** | **171/171** | **154/171 (90%)** | + +Excluding the out-of-scope 3D notebook: 147/154 (95%). The first +measurement, before the gap-closing pass, was 121/171 (71%). + +¹ 3D projections are outside xy's 2-D chart-method compatibility target +(see [docs/matplotlib-compat.md](../../docs/matplotlib-compat.md)); +`plt.axes(projection='3d')` fails loudly rather than silently returning +a 2-D axes, so only this notebook's 2-D cells pass. +² Soft evidence: seaborn draws through real matplotlib internally, so +only the cells calling `plt` directly exercise the shim. + +## Remaining failures + +All 17 are one of: 3-D projection cells (10, loud rejections by +design), legend layout options `borderpad`/`labelspacing` and the +`Legend` class (3, documented loud rejections), pandas +`Series.plot(ax=ax)` datetime interop (3, a real gap — a dtype error +inside the pandas plotting path), and `axhline(marker=)` via seaborn +(1, loud rejection). + +Data files under `data/` come from the handbook's repository +(`births.csv`, `california_cities.csv`) and +[jakevdp/marathon-data](https://github.com/jakevdp/marathon-data) +(`marathon-data.csv`). diff --git a/examples/pdsh/data/births.csv b/examples/pdsh/data/births.csv new file mode 100644 index 00000000..4a5bb7ae --- /dev/null +++ b/examples/pdsh/data/births.csv @@ -0,0 +1,15548 @@ +year,month,day,gender,births +1969,1,1,F,4046 +1969,1,1,M,4440 +1969,1,2,F,4454 +1969,1,2,M,4548 +1969,1,3,F,4548 +1969,1,3,M,4994 +1969,1,4,F,4440 +1969,1,4,M,4520 +1969,1,5,F,4192 +1969,1,5,M,4198 +1969,1,6,F,4710 +1969,1,6,M,4850 +1969,1,7,F,4646 +1969,1,7,M,5092 +1969,1,8,F,4800 +1969,1,8,M,4934 +1969,1,9,F,4592 +1969,1,9,M,4842 +1969,1,10,F,4852 +1969,1,10,M,5190 +1969,1,11,F,4580 +1969,1,11,M,4598 +1969,1,12,F,4126 +1969,1,12,M,4324 +1969,1,13,F,4758 +1969,1,13,M,5076 +1969,1,14,F,5070 +1969,1,14,M,5296 +1969,1,15,F,4798 +1969,1,15,M,5096 +1969,1,16,F,4790 +1969,1,16,M,4872 +1969,1,17,F,4944 +1969,1,17,M,5030 +1969,1,18,F,4670 +1969,1,18,M,4642 +1969,1,19,F,4170 +1969,1,19,M,4452 +1969,1,20,F,4884 +1969,1,20,M,4924 +1969,1,21,F,5042 +1969,1,21,M,5432 +1969,1,22,F,4796 +1969,1,22,M,5088 +1969,1,23,F,4794 +1969,1,23,M,4660 +1969,1,24,F,4752 +1969,1,24,M,5046 +1969,1,25,F,4348 +1969,1,25,M,4674 +1969,1,26,F,4230 +1969,1,26,M,4338 +1969,1,27,F,4864 +1969,1,27,M,5046 +1969,1,28,F,4860 +1969,1,28,M,5172 +1969,1,29,F,4500 +1969,1,29,M,4880 +1969,1,30,F,4668 +1969,1,30,M,5006 +1969,1,31,F,4780 +1969,1,31,M,4912 +1969,1,99,F,26 +1969,1,99,M,38 +1969,2,1,F,4394 +1969,2,1,M,4736 +1969,2,2,F,4334 +1969,2,2,M,4480 +1969,2,3,F,4878 +1969,2,3,M,5110 +1969,2,4,F,4796 +1969,2,4,M,5200 +1969,2,5,F,4674 +1969,2,5,M,5002 +1969,2,6,F,4760 +1969,2,6,M,4968 +1969,2,7,F,4940 +1969,2,7,M,5162 +1969,2,8,F,4626 +1969,2,8,M,4636 +1969,2,9,F,4252 +1969,2,9,M,4442 +1969,2,10,F,4958 +1969,2,10,M,4996 +1969,2,11,F,4796 +1969,2,11,M,5060 +1969,2,12,F,4780 +1969,2,12,M,5228 +1969,2,13,F,4850 +1969,2,13,M,4756 +1969,2,14,F,5092 +1969,2,14,M,5262 +1969,2,15,F,4598 +1969,2,15,M,4712 +1969,2,16,F,4118 +1969,2,16,M,4416 +1969,2,17,F,4768 +1969,2,17,M,5054 +1969,2,18,F,4962 +1969,2,18,M,5214 +1969,2,19,F,4788 +1969,2,19,M,5028 +1969,2,20,F,4920 +1969,2,20,M,5062 +1969,2,21,F,4940 +1969,2,21,M,4976 +1969,2,22,F,4484 +1969,2,22,M,4668 +1969,2,23,F,4298 +1969,2,23,M,4406 +1969,2,24,F,4798 +1969,2,24,M,5168 +1969,2,25,F,5196 +1969,2,25,M,5370 +1969,2,26,F,4838 +1969,2,26,M,5210 +1969,2,27,F,4588 +1969,2,27,M,5030 +1969,2,28,F,4792 +1969,2,28,M,4964 +1969,2,29,F,50 +1969,2,29,M,16 +1969,2,30,F,24 +1969,2,30,M,28 +1969,2,31,F,24 +1969,2,31,M,20 +1969,2,99,F,42 +1969,2,99,M,48 +1969,3,1,F,4402 +1969,3,1,M,4784 +1969,3,2,F,4204 +1969,3,2,M,4376 +1969,3,3,F,4874 +1969,3,3,M,5194 +1969,3,4,F,4994 +1969,3,4,M,5270 +1969,3,5,F,4958 +1969,3,5,M,5088 +1969,3,6,F,4640 +1969,3,6,M,5064 +1969,3,7,F,4658 +1969,3,7,M,5290 +1969,3,8,F,4462 +1969,3,8,M,4872 +1969,3,9,F,4146 +1969,3,9,M,4248 +1969,3,10,F,4816 +1969,3,10,M,5076 +1969,3,11,F,5096 +1969,3,11,M,5092 +1969,3,12,F,4800 +1969,3,12,M,4976 +1969,3,13,F,4710 +1969,3,13,M,4930 +1969,3,14,F,4936 +1969,3,14,M,5098 +1969,3,15,F,4300 +1969,3,15,M,4538 +1969,3,16,F,4110 +1969,3,16,M,4226 +1969,3,17,F,4788 +1969,3,17,M,5340 +1969,3,18,F,4910 +1969,3,18,M,5226 +1969,3,19,F,4968 +1969,3,19,M,5096 +1969,3,20,F,4738 +1969,3,20,M,5074 +1969,3,21,F,4832 +1969,3,21,M,5070 +1969,3,22,F,4446 +1969,3,22,M,4516 +1969,3,23,F,4240 +1969,3,23,M,4362 +1969,3,24,F,4730 +1969,3,24,M,5072 +1969,3,25,F,4976 +1969,3,25,M,5296 +1969,3,26,F,4822 +1969,3,26,M,4996 +1969,3,27,F,4628 +1969,3,27,M,5070 +1969,3,28,F,4968 +1969,3,28,M,5358 +1969,3,29,F,4292 +1969,3,29,M,4616 +1969,3,30,F,3986 +1969,3,30,M,4200 +1969,3,31,F,4590 +1969,3,31,M,5002 +1969,3,99,F,64 +1969,3,99,M,50 +1969,4,1,F,4990 +1969,4,1,M,4970 +1969,4,2,F,4766 +1969,4,2,M,5212 +1969,4,3,F,4682 +1969,4,3,M,4848 +1969,4,4,F,4718 +1969,4,4,M,4854 +1969,4,5,F,4384 +1969,4,5,M,4364 +1969,4,6,F,3896 +1969,4,6,M,4112 +1969,4,7,F,4418 +1969,4,7,M,4956 +1969,4,8,F,4930 +1969,4,8,M,5246 +1969,4,9,F,4748 +1969,4,9,M,5104 +1969,4,10,F,4730 +1969,4,10,M,4978 +1969,4,11,F,4848 +1969,4,11,M,5072 +1969,4,12,F,4318 +1969,4,12,M,4622 +1969,4,13,F,3886 +1969,4,13,M,4248 +1969,4,14,F,4726 +1969,4,14,M,4840 +1969,4,15,F,5064 +1969,4,15,M,5364 +1969,4,16,F,4804 +1969,4,16,M,5036 +1969,4,17,F,4832 +1969,4,17,M,5044 +1969,4,18,F,4832 +1969,4,18,M,5040 +1969,4,19,F,4292 +1969,4,19,M,4702 +1969,4,20,F,3760 +1969,4,20,M,4168 +1969,4,21,F,4828 +1969,4,21,M,4782 +1969,4,22,F,5016 +1969,4,22,M,5210 +1969,4,23,F,4660 +1969,4,23,M,5208 +1969,4,24,F,4620 +1969,4,24,M,4852 +1969,4,25,F,4610 +1969,4,25,M,5036 +1969,4,26,F,4338 +1969,4,26,M,4584 +1969,4,27,F,3846 +1969,4,27,M,4120 +1969,4,28,F,4622 +1969,4,28,M,4896 +1969,4,29,F,4622 +1969,4,29,M,5078 +1969,4,30,F,4396 +1969,4,30,M,4742 +1969,4,31,F,28 +1969,4,31,M,24 +1969,4,99,F,50 +1969,4,99,M,66 +1969,5,1,F,4598 +1969,5,1,M,4608 +1969,5,2,F,4708 +1969,5,2,M,5028 +1969,5,3,F,4148 +1969,5,3,M,4620 +1969,5,4,F,3922 +1969,5,4,M,4172 +1969,5,5,F,4854 +1969,5,5,M,5076 +1969,5,6,F,4906 +1969,5,6,M,5058 +1969,5,7,F,4724 +1969,5,7,M,4902 +1969,5,8,F,4564 +1969,5,8,M,4920 +1969,5,9,F,4634 +1969,5,9,M,4728 +1969,5,10,F,4072 +1969,5,10,M,4444 +1969,5,11,F,3998 +1969,5,11,M,4124 +1969,5,12,F,4570 +1969,5,12,M,4736 +1969,5,13,F,4776 +1969,5,13,M,5086 +1969,5,14,F,4402 +1969,5,14,M,4782 +1969,5,15,F,4646 +1969,5,15,M,4878 +1969,5,16,F,4880 +1969,5,16,M,4944 +1969,5,17,F,4376 +1969,5,17,M,4570 +1969,5,18,F,3922 +1969,5,18,M,4106 +1969,5,19,F,4626 +1969,5,19,M,4868 +1969,5,20,F,5034 +1969,5,20,M,5354 +1969,5,21,F,4698 +1969,5,21,M,5068 +1969,5,22,F,4630 +1969,5,22,M,4776 +1969,5,23,F,4548 +1969,5,23,M,5036 +1969,5,24,F,4174 +1969,5,24,M,4384 +1969,5,25,F,4030 +1969,5,25,M,4196 +1969,5,26,F,4752 +1969,5,26,M,5088 +1969,5,27,F,5264 +1969,5,27,M,5088 +1969,5,28,F,4966 +1969,5,28,M,4972 +1969,5,29,F,4878 +1969,5,29,M,5312 +1969,5,30,F,4452 +1969,5,30,M,4702 +1969,5,31,F,4112 +1969,5,31,M,4528 +1969,5,99,F,54 +1969,5,99,M,52 +1969,6,1,F,4174 +1969,6,1,M,4252 +1969,6,2,F,4736 +1969,6,2,M,5126 +1969,6,3,F,5146 +1969,6,3,M,5012 +1969,6,4,F,4750 +1969,6,4,M,5088 +1969,6,5,F,4686 +1969,6,5,M,4902 +1969,6,6,F,4864 +1969,6,6,M,5142 +1969,6,7,F,4342 +1969,6,7,M,4472 +1969,6,8,F,3958 +1969,6,8,M,4268 +1969,6,9,F,4826 +1969,6,9,M,4912 +1969,6,10,F,4920 +1969,6,10,M,5400 +1969,6,11,F,4760 +1969,6,11,M,5190 +1969,6,12,F,4980 +1969,6,12,M,5240 +1969,6,13,F,4772 +1969,6,13,M,5080 +1969,6,14,F,4358 +1969,6,14,M,4562 +1969,6,15,F,4046 +1969,6,15,M,4356 +1969,6,16,F,4628 +1969,6,16,M,5116 +1969,6,17,F,5150 +1969,6,17,M,5144 +1969,6,18,F,4864 +1969,6,18,M,5356 +1969,6,19,F,4630 +1969,6,19,M,5126 +1969,6,20,F,5122 +1969,6,20,M,5302 +1969,6,21,F,4328 +1969,6,21,M,4706 +1969,6,22,F,4136 +1969,6,22,M,4476 +1969,6,23,F,4842 +1969,6,23,M,5038 +1969,6,24,F,5180 +1969,6,24,M,5444 +1969,6,25,F,5196 +1969,6,25,M,5270 +1969,6,26,F,5016 +1969,6,26,M,5328 +1969,6,27,F,5288 +1969,6,27,M,5488 +1969,6,28,F,4610 +1969,6,28,M,4956 +1969,6,29,F,4262 +1969,6,29,M,4538 +1969,6,30,F,5260 +1969,6,30,M,5328 +1969,6,31,F,20 +1969,6,31,M,40 +1969,6,99,F,54 +1969,6,99,M,48 +1969,7,1,F,5378 +1969,7,1,M,5768 +1969,7,2,F,5242 +1969,7,2,M,5516 +1969,7,3,F,5030 +1969,7,3,M,5532 +1969,7,4,F,4504 +1969,7,4,M,4664 +1969,7,5,F,4492 +1969,7,5,M,4756 +1969,7,6,F,4246 +1969,7,6,M,4614 +1969,7,7,F,5138 +1969,7,7,M,5496 +1969,7,8,F,5282 +1969,7,8,M,5560 +1969,7,9,F,5132 +1969,7,9,M,5444 +1969,7,10,F,5166 +1969,7,10,M,5396 +1969,7,11,F,5210 +1969,7,11,M,5476 +1969,7,12,F,4596 +1969,7,12,M,4876 +1969,7,13,F,4454 +1969,7,13,M,4524 +1969,7,14,F,4768 +1969,7,14,M,5474 +1969,7,15,F,5486 +1969,7,15,M,5810 +1969,7,16,F,5086 +1969,7,16,M,5612 +1969,7,17,F,5194 +1969,7,17,M,5474 +1969,7,18,F,5242 +1969,7,18,M,5390 +1969,7,19,F,4838 +1969,7,19,M,4836 +1969,7,20,F,4320 +1969,7,20,M,4620 +1969,7,21,F,4984 +1969,7,21,M,5212 +1969,7,22,F,5408 +1969,7,22,M,5632 +1969,7,23,F,5092 +1969,7,23,M,5376 +1969,7,24,F,5124 +1969,7,24,M,5306 +1969,7,25,F,5126 +1969,7,25,M,5562 +1969,7,26,F,4734 +1969,7,26,M,5052 +1969,7,27,F,4626 +1969,7,27,M,4782 +1969,7,28,F,4990 +1969,7,28,M,5558 +1969,7,29,F,5434 +1969,7,29,M,5668 +1969,7,30,F,5192 +1969,7,30,M,5442 +1969,7,31,F,5076 +1969,7,31,M,5270 +1969,7,99,F,24 +1969,7,99,M,44 +1969,8,1,F,5112 +1969,8,1,M,5618 +1969,8,2,F,4872 +1969,8,2,M,5110 +1969,8,3,F,4464 +1969,8,3,M,4618 +1969,8,4,F,5118 +1969,8,4,M,5332 +1969,8,5,F,5418 +1969,8,5,M,5526 +1969,8,6,F,5232 +1969,8,6,M,5620 +1969,8,7,F,5066 +1969,8,7,M,5316 +1969,8,8,F,5376 +1969,8,8,M,5926 +1969,8,9,F,4968 +1969,8,9,M,5124 +1969,8,10,F,4394 +1969,8,10,M,4712 +1969,8,11,F,5120 +1969,8,11,M,5586 +1969,8,12,F,5542 +1969,8,12,M,5596 +1969,8,13,F,5210 +1969,8,13,M,5592 +1969,8,14,F,5290 +1969,8,14,M,5436 +1969,8,15,F,5298 +1969,8,15,M,5612 +1969,8,16,F,4774 +1969,8,16,M,4998 +1969,8,17,F,4482 +1969,8,17,M,4642 +1969,8,18,F,5120 +1969,8,18,M,5530 +1969,8,19,F,5550 +1969,8,19,M,5860 +1969,8,20,F,5226 +1969,8,20,M,5692 +1969,8,21,F,4962 +1969,8,21,M,5222 +1969,8,22,F,5170 +1969,8,22,M,5416 +1969,8,23,F,4682 +1969,8,23,M,4978 +1969,8,24,F,4438 +1969,8,24,M,4646 +1969,8,25,F,5104 +1969,8,25,M,5482 +1969,8,26,F,5400 +1969,8,26,M,5682 +1969,8,27,F,5076 +1969,8,27,M,5562 +1969,8,28,F,5162 +1969,8,28,M,5454 +1969,8,29,F,5138 +1969,8,29,M,5576 +1969,8,30,F,4680 +1969,8,30,M,5028 +1969,8,31,F,4358 +1969,8,31,M,4628 +1969,8,99,F,54 +1969,8,99,M,58 +1969,9,1,F,4440 +1969,9,1,M,4572 +1969,9,2,F,5174 +1969,9,2,M,5512 +1969,9,3,F,5210 +1969,9,3,M,5834 +1969,9,4,F,5172 +1969,9,4,M,5334 +1969,9,5,F,5032 +1969,9,5,M,5578 +1969,9,6,F,4722 +1969,9,6,M,4988 +1969,9,7,F,4514 +1969,9,7,M,4682 +1969,9,8,F,5030 +1969,9,8,M,5478 +1969,9,9,F,5172 +1969,9,9,M,5426 +1969,9,10,F,5020 +1969,9,10,M,5430 +1969,9,11,F,5042 +1969,9,11,M,5366 +1969,9,12,F,5226 +1969,9,12,M,5526 +1969,9,13,F,4744 +1969,9,13,M,4790 +1969,9,14,F,4552 +1969,9,14,M,4652 +1969,9,15,F,5122 +1969,9,15,M,5606 +1969,9,16,F,5390 +1969,9,16,M,5752 +1969,9,17,F,5268 +1969,9,17,M,5596 +1969,9,18,F,5288 +1969,9,18,M,5588 +1969,9,19,F,5422 +1969,9,19,M,5410 +1969,9,20,F,4930 +1969,9,20,M,5110 +1969,9,21,F,4712 +1969,9,21,M,4656 +1969,9,22,F,5374 +1969,9,22,M,5596 +1969,9,23,F,5504 +1969,9,23,M,5796 +1969,9,24,F,5250 +1969,9,24,M,5648 +1969,9,25,F,5278 +1969,9,25,M,5562 +1969,9,26,F,5508 +1969,9,26,M,5646 +1969,9,27,F,4986 +1969,9,27,M,5184 +1969,9,28,F,4564 +1969,9,28,M,4634 +1969,9,29,F,5192 +1969,9,29,M,5516 +1969,9,30,F,5454 +1969,9,30,M,5684 +1969,9,31,F,38 +1969,9,31,M,30 +1969,9,99,F,60 +1969,9,99,M,48 +1969,10,1,F,5290 +1969,10,1,M,5620 +1969,10,2,F,5322 +1969,10,2,M,5334 +1969,10,3,F,5324 +1969,10,3,M,5598 +1969,10,4,F,4732 +1969,10,4,M,4978 +1969,10,5,F,4464 +1969,10,5,M,4508 +1969,10,6,F,5008 +1969,10,6,M,5310 +1969,10,7,F,5150 +1969,10,7,M,5504 +1969,10,8,F,5194 +1969,10,8,M,5316 +1969,10,9,F,5268 +1969,10,9,M,5442 +1969,10,10,F,5398 +1969,10,10,M,5350 +1969,10,11,F,4640 +1969,10,11,M,4926 +1969,10,12,F,4428 +1969,10,12,M,4586 +1969,10,13,F,4894 +1969,10,13,M,5102 +1969,10,14,F,5048 +1969,10,14,M,5570 +1969,10,15,F,4884 +1969,10,15,M,5236 +1969,10,16,F,4836 +1969,10,16,M,5082 +1969,10,17,F,5088 +1969,10,17,M,5250 +1969,10,18,F,4382 +1969,10,18,M,4790 +1969,10,19,F,4092 +1969,10,19,M,4494 +1969,10,20,F,4876 +1969,10,20,M,5272 +1969,10,21,F,5088 +1969,10,21,M,5586 +1969,10,22,F,5018 +1969,10,22,M,5282 +1969,10,23,F,4790 +1969,10,23,M,5024 +1969,10,24,F,4920 +1969,10,24,M,5090 +1969,10,25,F,4416 +1969,10,25,M,4842 +1969,10,26,F,4300 +1969,10,26,M,4564 +1969,10,27,F,5054 +1969,10,27,M,5318 +1969,10,28,F,5096 +1969,10,28,M,5544 +1969,10,29,F,4920 +1969,10,29,M,5184 +1969,10,30,F,4930 +1969,10,30,M,5180 +1969,10,31,F,4836 +1969,10,31,M,5308 +1969,10,99,F,48 +1969,10,99,M,48 +1969,11,1,F,4676 +1969,11,1,M,4666 +1969,11,2,F,4376 +1969,11,2,M,4528 +1969,11,3,F,4952 +1969,11,3,M,5386 +1969,11,4,F,5114 +1969,11,4,M,5658 +1969,11,5,F,4832 +1969,11,5,M,5188 +1969,11,6,F,5090 +1969,11,6,M,5150 +1969,11,7,F,5172 +1969,11,7,M,5488 +1969,11,8,F,4726 +1969,11,8,M,4952 +1969,11,9,F,4342 +1969,11,9,M,4680 +1969,11,10,F,5024 +1969,11,10,M,5318 +1969,11,11,F,5252 +1969,11,11,M,5626 +1969,11,12,F,4862 +1969,11,12,M,5462 +1969,11,13,F,5028 +1969,11,13,M,5072 +1969,11,14,F,5210 +1969,11,14,M,5186 +1969,11,15,F,4480 +1969,11,15,M,4818 +1969,11,16,F,4290 +1969,11,16,M,4370 +1969,11,17,F,4966 +1969,11,17,M,5320 +1969,11,18,F,5346 +1969,11,18,M,5352 +1969,11,19,F,5000 +1969,11,19,M,5260 +1969,11,20,F,5072 +1969,11,20,M,5186 +1969,11,21,F,4846 +1969,11,21,M,5322 +1969,11,22,F,4576 +1969,11,22,M,4862 +1969,11,23,F,4354 +1969,11,23,M,4512 +1969,11,24,F,5186 +1969,11,24,M,5276 +1969,11,25,F,5318 +1969,11,25,M,5546 +1969,11,26,F,4874 +1969,11,26,M,5200 +1969,11,27,F,4084 +1969,11,27,M,4164 +1969,11,28,F,4838 +1969,11,28,M,5110 +1969,11,29,F,4536 +1969,11,29,M,4796 +1969,11,30,F,4448 +1969,11,30,M,4518 +1969,11,31,F,54 +1969,11,31,M,62 +1969,11,99,F,40 +1969,11,99,M,56 +1969,12,1,F,5124 +1969,12,1,M,5524 +1969,12,2,F,5224 +1969,12,2,M,5512 +1969,12,3,F,4948 +1969,12,3,M,5352 +1969,12,4,F,5042 +1969,12,4,M,5212 +1969,12,5,F,4988 +1969,12,5,M,5440 +1969,12,6,F,4602 +1969,12,6,M,4818 +1969,12,7,F,4178 +1969,12,7,M,4562 +1969,12,8,F,5240 +1969,12,8,M,5406 +1969,12,9,F,5246 +1969,12,9,M,5484 +1969,12,10,F,5108 +1969,12,10,M,5240 +1969,12,11,F,5052 +1969,12,11,M,5248 +1969,12,12,F,5254 +1969,12,12,M,5504 +1969,12,13,F,4680 +1969,12,13,M,4922 +1969,12,14,F,4174 +1969,12,14,M,4588 +1969,12,15,F,5328 +1969,12,15,M,5570 +1969,12,16,F,5368 +1969,12,16,M,5654 +1969,12,17,F,5138 +1969,12,17,M,5508 +1969,12,18,F,5180 +1969,12,18,M,5246 +1969,12,19,F,5366 +1969,12,19,M,5546 +1969,12,20,F,4964 +1969,12,20,M,4858 +1969,12,21,F,4434 +1969,12,21,M,4430 +1969,12,22,F,5194 +1969,12,22,M,5298 +1969,12,23,F,4820 +1969,12,23,M,5036 +1969,12,24,F,4322 +1969,12,24,M,4656 +1969,12,25,F,4136 +1969,12,25,M,4148 +1969,12,26,F,4826 +1969,12,26,M,5084 +1969,12,27,F,4544 +1969,12,27,M,4760 +1969,12,28,F,4344 +1969,12,28,M,4660 +1969,12,29,F,5364 +1969,12,29,M,5616 +1969,12,30,F,5988 +1969,12,30,M,6244 +1969,12,31,F,5602 +1969,12,31,M,5520 +1969,12,99,F,44 +1969,12,99,M,54 +1970,1,1,F,4064 +1970,1,1,M,4308 +1970,1,2,F,4536 +1970,1,2,M,4698 +1970,1,3,F,4398 +1970,1,3,M,4764 +1970,1,4,F,3968 +1970,1,4,M,4652 +1970,1,5,F,4718 +1970,1,5,M,5134 +1970,1,6,F,4998 +1970,1,6,M,5204 +1970,1,7,F,4910 +1970,1,7,M,5110 +1970,1,8,F,4744 +1970,1,8,M,5012 +1970,1,9,F,4828 +1970,1,9,M,5016 +1970,1,10,F,4490 +1970,1,10,M,4780 +1970,1,11,F,4476 +1970,1,11,M,4682 +1970,1,12,F,5056 +1970,1,12,M,5418 +1970,1,13,F,5078 +1970,1,13,M,5502 +1970,1,14,F,4986 +1970,1,14,M,5258 +1970,1,15,F,4764 +1970,1,15,M,5182 +1970,1,16,F,4992 +1970,1,16,M,5284 +1970,1,17,F,4662 +1970,1,17,M,4936 +1970,1,18,F,4378 +1970,1,18,M,4570 +1970,1,19,F,4946 +1970,1,19,M,5178 +1970,1,20,F,5084 +1970,1,20,M,5440 +1970,1,21,F,4908 +1970,1,21,M,5188 +1970,1,22,F,4684 +1970,1,22,M,5002 +1970,1,23,F,5012 +1970,1,23,M,5190 +1970,1,24,F,4746 +1970,1,24,M,4724 +1970,1,25,F,4312 +1970,1,25,M,4480 +1970,1,26,F,4972 +1970,1,26,M,5188 +1970,1,27,F,5090 +1970,1,27,M,5512 +1970,1,28,F,4894 +1970,1,28,M,5294 +1970,1,29,F,4792 +1970,1,29,M,5024 +1970,1,30,F,4856 +1970,1,30,M,5056 +1970,1,31,F,4328 +1970,1,31,M,4684 +1970,1,99,F,84 +1970,1,99,M,54 +1970,2,1,F,4380 +1970,2,1,M,4662 +1970,2,2,F,5128 +1970,2,2,M,5414 +1970,2,3,F,5096 +1970,2,3,M,5616 +1970,2,4,F,4848 +1970,2,4,M,5238 +1970,2,5,F,4802 +1970,2,5,M,4964 +1970,2,6,F,5122 +1970,2,6,M,5098 +1970,2,7,F,4652 +1970,2,7,M,4910 +1970,2,8,F,4226 +1970,2,8,M,4790 +1970,2,9,F,5016 +1970,2,9,M,5210 +1970,2,10,F,5218 +1970,2,10,M,5402 +1970,2,11,F,4990 +1970,2,11,M,5298 +1970,2,12,F,4958 +1970,2,12,M,5100 +1970,2,13,F,4986 +1970,2,13,M,5124 +1970,2,14,F,4652 +1970,2,14,M,4906 +1970,2,15,F,4598 +1970,2,15,M,4538 +1970,2,16,F,5056 +1970,2,16,M,5204 +1970,2,17,F,5160 +1970,2,17,M,5516 +1970,2,18,F,5120 +1970,2,18,M,5352 +1970,2,19,F,4792 +1970,2,19,M,5280 +1970,2,20,F,5214 +1970,2,20,M,5330 +1970,2,21,F,4556 +1970,2,21,M,4758 +1970,2,22,F,4458 +1970,2,22,M,4528 +1970,2,23,F,5000 +1970,2,23,M,5168 +1970,2,24,F,5386 +1970,2,24,M,5396 +1970,2,25,F,5254 +1970,2,25,M,5320 +1970,2,26,F,5204 +1970,2,26,M,5204 +1970,2,27,F,4992 +1970,2,27,M,5272 +1970,2,28,F,4796 +1970,2,28,M,4924 +1970,2,29,F,38 +1970,2,29,M,44 +1970,2,30,F,20 +1970,2,30,M,12 +1970,2,31,F,8 +1970,2,31,M,6 +1970,2,99,F,100 +1970,2,99,M,78 +1970,3,1,F,4390 +1970,3,1,M,4492 +1970,3,2,F,5050 +1970,3,2,M,5120 +1970,3,3,F,5334 +1970,3,3,M,5972 +1970,3,4,F,5116 +1970,3,4,M,5484 +1970,3,5,F,4958 +1970,3,5,M,5300 +1970,3,6,F,5216 +1970,3,6,M,5288 +1970,3,7,F,4568 +1970,3,7,M,4892 +1970,3,8,F,4342 +1970,3,8,M,4584 +1970,3,9,F,5092 +1970,3,9,M,5248 +1970,3,10,F,5222 +1970,3,10,M,5522 +1970,3,11,F,5108 +1970,3,11,M,5302 +1970,3,12,F,5000 +1970,3,12,M,5180 +1970,3,13,F,4890 +1970,3,13,M,5250 +1970,3,14,F,4514 +1970,3,14,M,4936 +1970,3,15,F,4098 +1970,3,15,M,4498 +1970,3,16,F,4820 +1970,3,16,M,5238 +1970,3,17,F,5120 +1970,3,17,M,5574 +1970,3,18,F,4986 +1970,3,18,M,5292 +1970,3,19,F,4802 +1970,3,19,M,5298 +1970,3,20,F,5018 +1970,3,20,M,5188 +1970,3,21,F,4572 +1970,3,21,M,4818 +1970,3,22,F,4296 +1970,3,22,M,4488 +1970,3,23,F,5000 +1970,3,23,M,5334 +1970,3,24,F,5064 +1970,3,24,M,5492 +1970,3,25,F,4888 +1970,3,25,M,5260 +1970,3,26,F,4834 +1970,3,26,M,5052 +1970,3,27,F,4650 +1970,3,27,M,5142 +1970,3,28,F,4258 +1970,3,28,M,4506 +1970,3,29,F,4028 +1970,3,29,M,4286 +1970,3,30,F,4766 +1970,3,30,M,5000 +1970,3,31,F,5098 +1970,3,31,M,5074 +1970,3,99,F,100 +1970,3,99,M,140 +1970,4,1,F,4838 +1970,4,1,M,5164 +1970,4,2,F,4834 +1970,4,2,M,5300 +1970,4,3,F,4936 +1970,4,3,M,5180 +1970,4,4,F,4594 +1970,4,4,M,4640 +1970,4,5,F,4250 +1970,4,5,M,4272 +1970,4,6,F,4956 +1970,4,6,M,5096 +1970,4,7,F,5222 +1970,4,7,M,5252 +1970,4,8,F,4856 +1970,4,8,M,5094 +1970,4,9,F,4798 +1970,4,9,M,5100 +1970,4,10,F,4902 +1970,4,10,M,5170 +1970,4,11,F,4388 +1970,4,11,M,4652 +1970,4,12,F,3970 +1970,4,12,M,4244 +1970,4,13,F,4592 +1970,4,13,M,4878 +1970,4,14,F,4874 +1970,4,14,M,5226 +1970,4,15,F,4846 +1970,4,15,M,4978 +1970,4,16,F,4678 +1970,4,16,M,4990 +1970,4,17,F,4630 +1970,4,17,M,5076 +1970,4,18,F,4286 +1970,4,18,M,4500 +1970,4,19,F,4002 +1970,4,19,M,4172 +1970,4,20,F,4720 +1970,4,20,M,4854 +1970,4,21,F,4996 +1970,4,21,M,5102 +1970,4,22,F,4774 +1970,4,22,M,4940 +1970,4,23,F,4632 +1970,4,23,M,4886 +1970,4,24,F,4848 +1970,4,24,M,5012 +1970,4,25,F,4224 +1970,4,25,M,4600 +1970,4,26,F,3918 +1970,4,26,M,4360 +1970,4,27,F,4834 +1970,4,27,M,5076 +1970,4,28,F,5086 +1970,4,28,M,5214 +1970,4,29,F,4744 +1970,4,29,M,4984 +1970,4,30,F,4700 +1970,4,30,M,4984 +1970,4,31,F,26 +1970,4,31,M,18 +1970,4,99,F,54 +1970,4,99,M,68 +1970,5,1,F,4780 +1970,5,1,M,5166 +1970,5,2,F,4170 +1970,5,2,M,4570 +1970,5,3,F,3774 +1970,5,3,M,4262 +1970,5,4,F,4904 +1970,5,4,M,4848 +1970,5,5,F,4946 +1970,5,5,M,5190 +1970,5,6,F,4848 +1970,5,6,M,4864 +1970,5,7,F,4806 +1970,5,7,M,4826 +1970,5,8,F,4730 +1970,5,8,M,5106 +1970,5,9,F,4398 +1970,5,9,M,4598 +1970,5,10,F,3986 +1970,5,10,M,4442 +1970,5,11,F,5032 +1970,5,11,M,5048 +1970,5,12,F,5148 +1970,5,12,M,5288 +1970,5,13,F,4846 +1970,5,13,M,5180 +1970,5,14,F,4788 +1970,5,14,M,5024 +1970,5,15,F,4920 +1970,5,15,M,5294 +1970,5,16,F,4306 +1970,5,16,M,4640 +1970,5,17,F,3826 +1970,5,17,M,4204 +1970,5,18,F,4842 +1970,5,18,M,5180 +1970,5,19,F,5100 +1970,5,19,M,5336 +1970,5,20,F,4950 +1970,5,20,M,5094 +1970,5,21,F,5036 +1970,5,21,M,5208 +1970,5,22,F,5010 +1970,5,22,M,5352 +1970,5,23,F,4684 +1970,5,23,M,4740 +1970,5,24,F,4256 +1970,5,24,M,4462 +1970,5,25,F,5028 +1970,5,25,M,5048 +1970,5,26,F,5104 +1970,5,26,M,5494 +1970,5,27,F,4770 +1970,5,27,M,5264 +1970,5,28,F,4850 +1970,5,28,M,5190 +1970,5,29,F,4758 +1970,5,29,M,5226 +1970,5,30,F,4318 +1970,5,30,M,4550 +1970,5,31,F,3950 +1970,5,31,M,4438 +1970,5,99,F,72 +1970,5,99,M,72 +1970,6,1,F,5118 +1970,6,1,M,5006 +1970,6,2,F,5242 +1970,6,2,M,5640 +1970,6,3,F,5036 +1970,6,3,M,5234 +1970,6,4,F,4760 +1970,6,4,M,5204 +1970,6,5,F,4818 +1970,6,5,M,5242 +1970,6,6,F,4756 +1970,6,6,M,4760 +1970,6,7,F,4086 +1970,6,7,M,4414 +1970,6,8,F,4770 +1970,6,8,M,5176 +1970,6,9,F,5236 +1970,6,9,M,5528 +1970,6,10,F,5058 +1970,6,10,M,5272 +1970,6,11,F,5146 +1970,6,11,M,5372 +1970,6,12,F,5194 +1970,6,12,M,5580 +1970,6,13,F,4414 +1970,6,13,M,4798 +1970,6,14,F,4230 +1970,6,14,M,4384 +1970,6,15,F,5038 +1970,6,15,M,5178 +1970,6,16,F,5308 +1970,6,16,M,5570 +1970,6,17,F,5108 +1970,6,17,M,5502 +1970,6,18,F,5180 +1970,6,18,M,5340 +1970,6,19,F,5080 +1970,6,19,M,5310 +1970,6,20,F,4768 +1970,6,20,M,4832 +1970,6,21,F,4106 +1970,6,21,M,4462 +1970,6,22,F,4970 +1970,6,22,M,5382 +1970,6,23,F,5156 +1970,6,23,M,5522 +1970,6,24,F,5298 +1970,6,24,M,5346 +1970,6,25,F,5088 +1970,6,25,M,5238 +1970,6,26,F,5158 +1970,6,26,M,5464 +1970,6,27,F,4720 +1970,6,27,M,4952 +1970,6,28,F,4342 +1970,6,28,M,4386 +1970,6,29,F,4996 +1970,6,29,M,5510 +1970,6,30,F,5494 +1970,6,30,M,5884 +1970,6,31,F,14 +1970,6,31,M,14 +1970,6,99,F,102 +1970,6,99,M,86 +1970,7,1,F,5376 +1970,7,1,M,5732 +1970,7,2,F,5370 +1970,7,2,M,5864 +1970,7,3,F,5078 +1970,7,3,M,5438 +1970,7,4,F,4608 +1970,7,4,M,4772 +1970,7,5,F,4492 +1970,7,5,M,4528 +1970,7,6,F,4910 +1970,7,6,M,5608 +1970,7,7,F,5358 +1970,7,7,M,5934 +1970,7,8,F,5298 +1970,7,8,M,5598 +1970,7,9,F,5224 +1970,7,9,M,5492 +1970,7,10,F,5226 +1970,7,10,M,5636 +1970,7,11,F,4566 +1970,7,11,M,5124 +1970,7,12,F,4606 +1970,7,12,M,4578 +1970,7,13,F,5086 +1970,7,13,M,5482 +1970,7,14,F,5620 +1970,7,14,M,6208 +1970,7,15,F,5430 +1970,7,15,M,5648 +1970,7,16,F,5318 +1970,7,16,M,5730 +1970,7,17,F,5334 +1970,7,17,M,5604 +1970,7,18,F,4856 +1970,7,18,M,5110 +1970,7,19,F,4590 +1970,7,19,M,4930 +1970,7,20,F,5348 +1970,7,20,M,5634 +1970,7,21,F,5506 +1970,7,21,M,5942 +1970,7,22,F,5138 +1970,7,22,M,5544 +1970,7,23,F,5340 +1970,7,23,M,5504 +1970,7,24,F,5312 +1970,7,24,M,5644 +1970,7,25,F,4786 +1970,7,25,M,5080 +1970,7,26,F,4508 +1970,7,26,M,4958 +1970,7,27,F,5370 +1970,7,27,M,5682 +1970,7,28,F,5752 +1970,7,28,M,6106 +1970,7,29,F,5556 +1970,7,29,M,5798 +1970,7,30,F,5302 +1970,7,30,M,5754 +1970,7,31,F,5502 +1970,7,31,M,5798 +1970,7,99,F,106 +1970,7,99,M,120 +1970,8,1,F,4966 +1970,8,1,M,5450 +1970,8,2,F,4480 +1970,8,2,M,4802 +1970,8,3,F,5420 +1970,8,3,M,5760 +1970,8,4,F,5346 +1970,8,4,M,5834 +1970,8,5,F,5332 +1970,8,5,M,5540 +1970,8,6,F,5308 +1970,8,6,M,5528 +1970,8,7,F,5376 +1970,8,7,M,5646 +1970,8,8,F,4914 +1970,8,8,M,5318 +1970,8,9,F,4628 +1970,8,9,M,4830 +1970,8,10,F,5178 +1970,8,10,M,5668 +1970,8,11,F,5554 +1970,8,11,M,6012 +1970,8,12,F,5522 +1970,8,12,M,5962 +1970,8,13,F,5350 +1970,8,13,M,5710 +1970,8,14,F,5656 +1970,8,14,M,5882 +1970,8,15,F,4916 +1970,8,15,M,5320 +1970,8,16,F,4766 +1970,8,16,M,4850 +1970,8,17,F,5404 +1970,8,17,M,5628 +1970,8,18,F,5510 +1970,8,18,M,6138 +1970,8,19,F,5402 +1970,8,19,M,5732 +1970,8,20,F,5340 +1970,8,20,M,5528 +1970,8,21,F,5448 +1970,8,21,M,5602 +1970,8,22,F,4720 +1970,8,22,M,5238 +1970,8,23,F,4494 +1970,8,23,M,4780 +1970,8,24,F,5198 +1970,8,24,M,5258 +1970,8,25,F,5566 +1970,8,25,M,5818 +1970,8,26,F,5560 +1970,8,26,M,5746 +1970,8,27,F,5146 +1970,8,27,M,5518 +1970,8,28,F,5460 +1970,8,28,M,5544 +1970,8,29,F,4802 +1970,8,29,M,5240 +1970,8,30,F,4396 +1970,8,30,M,4996 +1970,8,31,F,5528 +1970,8,31,M,5550 +1970,8,99,F,108 +1970,8,99,M,104 +1970,9,1,F,5506 +1970,9,1,M,5844 +1970,9,2,F,5256 +1970,9,2,M,5680 +1970,9,3,F,5366 +1970,9,3,M,5722 +1970,9,4,F,5574 +1970,9,4,M,5792 +1970,9,5,F,4982 +1970,9,5,M,5234 +1970,9,6,F,4542 +1970,9,6,M,4944 +1970,9,7,F,4658 +1970,9,7,M,4906 +1970,9,8,F,5444 +1970,9,8,M,5866 +1970,9,9,F,5740 +1970,9,9,M,6120 +1970,9,10,F,5540 +1970,9,10,M,6114 +1970,9,11,F,5640 +1970,9,11,M,5952 +1970,9,12,F,4960 +1970,9,12,M,5258 +1970,9,13,F,4750 +1970,9,13,M,4794 +1970,9,14,F,5432 +1970,9,14,M,5872 +1970,9,15,F,5806 +1970,9,15,M,6098 +1970,9,16,F,5558 +1970,9,16,M,6146 +1970,9,17,F,5636 +1970,9,17,M,5836 +1970,9,18,F,5606 +1970,9,18,M,5940 +1970,9,19,F,5134 +1970,9,19,M,5358 +1970,9,20,F,4922 +1970,9,20,M,5054 +1970,9,21,F,5558 +1970,9,21,M,6056 +1970,9,22,F,5986 +1970,9,22,M,6480 +1970,9,23,F,5666 +1970,9,23,M,6132 +1970,9,24,F,5574 +1970,9,24,M,5874 +1970,9,25,F,5750 +1970,9,25,M,6166 +1970,9,26,F,4992 +1970,9,26,M,5412 +1970,9,27,F,4800 +1970,9,27,M,5016 +1970,9,28,F,5642 +1970,9,28,M,5698 +1970,9,29,F,5706 +1970,9,29,M,5858 +1970,9,30,F,5416 +1970,9,30,M,5828 +1970,9,31,F,40 +1970,9,31,M,34 +1970,9,99,F,116 +1970,9,99,M,114 +1970,10,1,F,5364 +1970,10,1,M,5418 +1970,10,2,F,5514 +1970,10,2,M,5696 +1970,10,3,F,5116 +1970,10,3,M,5376 +1970,10,4,F,4544 +1970,10,4,M,4828 +1970,10,5,F,5450 +1970,10,5,M,5598 +1970,10,6,F,5786 +1970,10,6,M,5796 +1970,10,7,F,5404 +1970,10,7,M,5616 +1970,10,8,F,5418 +1970,10,8,M,5354 +1970,10,9,F,5528 +1970,10,9,M,5668 +1970,10,10,F,4824 +1970,10,10,M,4966 +1970,10,11,F,4440 +1970,10,11,M,4774 +1970,10,12,F,5438 +1970,10,12,M,5578 +1970,10,13,F,5530 +1970,10,13,M,5706 +1970,10,14,F,5376 +1970,10,14,M,5506 +1970,10,15,F,5180 +1970,10,15,M,5464 +1970,10,16,F,5156 +1970,10,16,M,5548 +1970,10,17,F,4580 +1970,10,17,M,4910 +1970,10,18,F,4380 +1970,10,18,M,4544 +1970,10,19,F,5028 +1970,10,19,M,5472 +1970,10,20,F,5506 +1970,10,20,M,5632 +1970,10,21,F,5282 +1970,10,21,M,5434 +1970,10,22,F,5002 +1970,10,22,M,5476 +1970,10,23,F,5158 +1970,10,23,M,5496 +1970,10,24,F,4618 +1970,10,24,M,4980 +1970,10,25,F,4458 +1970,10,25,M,4808 +1970,10,26,F,5092 +1970,10,26,M,5524 +1970,10,27,F,5634 +1970,10,27,M,5322 +1970,10,28,F,4844 +1970,10,28,M,5418 +1970,10,29,F,4964 +1970,10,29,M,5158 +1970,10,30,F,5198 +1970,10,30,M,5416 +1970,10,31,F,4730 +1970,10,31,M,4862 +1970,10,99,F,266 +1970,10,99,M,270 +1970,11,1,F,4588 +1970,11,1,M,4482 +1970,11,2,F,5102 +1970,11,2,M,5466 +1970,11,3,F,5446 +1970,11,3,M,5700 +1970,11,4,F,5148 +1970,11,4,M,5474 +1970,11,5,F,5120 +1970,11,5,M,5334 +1970,11,6,F,5294 +1970,11,6,M,5428 +1970,11,7,F,4674 +1970,11,7,M,4786 +1970,11,8,F,4366 +1970,11,8,M,4766 +1970,11,9,F,5226 +1970,11,9,M,5336 +1970,11,10,F,5610 +1970,11,10,M,5718 +1970,11,11,F,5272 +1970,11,11,M,5340 +1970,11,12,F,5098 +1970,11,12,M,5606 +1970,11,13,F,5390 +1970,11,13,M,5460 +1970,11,14,F,4822 +1970,11,14,M,5006 +1970,11,15,F,4514 +1970,11,15,M,4768 +1970,11,16,F,5184 +1970,11,16,M,5506 +1970,11,17,F,5346 +1970,11,17,M,5836 +1970,11,18,F,5274 +1970,11,18,M,5640 +1970,11,19,F,5324 +1970,11,19,M,5504 +1970,11,20,F,5440 +1970,11,20,M,5686 +1970,11,21,F,4862 +1970,11,21,M,5064 +1970,11,22,F,4332 +1970,11,22,M,4594 +1970,11,23,F,5158 +1970,11,23,M,5782 +1970,11,24,F,5514 +1970,11,24,M,5638 +1970,11,25,F,5182 +1970,11,25,M,5360 +1970,11,26,F,4274 +1970,11,26,M,4438 +1970,11,27,F,4880 +1970,11,27,M,5158 +1970,11,28,F,4732 +1970,11,28,M,4938 +1970,11,29,F,4474 +1970,11,29,M,4716 +1970,11,30,F,5402 +1970,11,30,M,5724 +1970,11,31,F,14 +1970,11,31,M,20 +1970,11,99,F,150 +1970,11,99,M,118 +1970,12,1,F,5530 +1970,12,1,M,5798 +1970,12,2,F,5514 +1970,12,2,M,5666 +1970,12,3,F,5110 +1970,12,3,M,5258 +1970,12,4,F,5296 +1970,12,4,M,5576 +1970,12,5,F,4710 +1970,12,5,M,4900 +1970,12,6,F,4480 +1970,12,6,M,4792 +1970,12,7,F,5252 +1970,12,7,M,5468 +1970,12,8,F,5402 +1970,12,8,M,5818 +1970,12,9,F,5190 +1970,12,9,M,5518 +1970,12,10,F,5192 +1970,12,10,M,5564 +1970,12,11,F,5224 +1970,12,11,M,5480 +1970,12,12,F,4948 +1970,12,12,M,5092 +1970,12,13,F,4550 +1970,12,13,M,4654 +1970,12,14,F,5438 +1970,12,14,M,5632 +1970,12,15,F,5682 +1970,12,15,M,6202 +1970,12,16,F,5484 +1970,12,16,M,6102 +1970,12,17,F,5442 +1970,12,17,M,5792 +1970,12,18,F,5728 +1970,12,18,M,5980 +1970,12,19,F,5092 +1970,12,19,M,5426 +1970,12,20,F,4586 +1970,12,20,M,4736 +1970,12,21,F,5494 +1970,12,21,M,5878 +1970,12,22,F,5308 +1970,12,22,M,5700 +1970,12,23,F,4854 +1970,12,23,M,5216 +1970,12,24,F,4532 +1970,12,24,M,4588 +1970,12,25,F,4130 +1970,12,25,M,4360 +1970,12,26,F,4560 +1970,12,26,M,4806 +1970,12,27,F,4506 +1970,12,27,M,4808 +1970,12,28,F,5658 +1970,12,28,M,5982 +1970,12,29,F,6204 +1970,12,29,M,6244 +1970,12,30,F,5820 +1970,12,30,M,6112 +1970,12,31,F,5568 +1970,12,31,M,5642 +1970,12,99,F,184 +1970,12,99,M,220 +1971,1,1,F,4214 +1971,1,1,M,4634 +1971,1,2,F,4460 +1971,1,2,M,4766 +1971,1,3,F,4288 +1971,1,3,M,4510 +1971,1,4,F,4868 +1971,1,4,M,5176 +1971,1,5,F,5012 +1971,1,5,M,5526 +1971,1,6,F,4848 +1971,1,6,M,5298 +1971,1,7,F,4974 +1971,1,7,M,5056 +1971,1,8,F,5182 +1971,1,8,M,5304 +1971,1,9,F,4564 +1971,1,9,M,4716 +1971,1,10,F,4480 +1971,1,10,M,4636 +1971,1,11,F,5310 +1971,1,11,M,5514 +1971,1,12,F,5382 +1971,1,12,M,5682 +1971,1,13,F,5118 +1971,1,13,M,5546 +1971,1,14,F,5030 +1971,1,14,M,5334 +1971,1,15,F,5212 +1971,1,15,M,5662 +1971,1,16,F,4648 +1971,1,16,M,4942 +1971,1,17,F,4400 +1971,1,17,M,4406 +1971,1,18,F,5340 +1971,1,18,M,5634 +1971,1,19,F,5542 +1971,1,19,M,5478 +1971,1,20,F,5176 +1971,1,20,M,5422 +1971,1,21,F,5104 +1971,1,21,M,5388 +1971,1,22,F,5176 +1971,1,22,M,5554 +1971,1,23,F,4778 +1971,1,23,M,5024 +1971,1,24,F,4340 +1971,1,24,M,4654 +1971,1,25,F,5280 +1971,1,25,M,5278 +1971,1,26,F,5424 +1971,1,26,M,5572 +1971,1,27,F,5096 +1971,1,27,M,5346 +1971,1,28,F,4910 +1971,1,28,M,5482 +1971,1,29,F,4944 +1971,1,29,M,5330 +1971,1,30,F,4700 +1971,1,30,M,5072 +1971,1,31,F,4474 +1971,1,31,M,4590 +1971,1,99,F,8 +1971,1,99,M,12 +1971,2,1,F,5056 +1971,2,1,M,5354 +1971,2,2,F,5120 +1971,2,2,M,5684 +1971,2,3,F,5048 +1971,2,3,M,5248 +1971,2,4,F,4872 +1971,2,4,M,5298 +1971,2,5,F,5280 +1971,2,5,M,5588 +1971,2,6,F,4572 +1971,2,6,M,4906 +1971,2,7,F,4402 +1971,2,7,M,4636 +1971,2,8,F,5018 +1971,2,8,M,5248 +1971,2,9,F,5170 +1971,2,9,M,5608 +1971,2,10,F,5210 +1971,2,10,M,5144 +1971,2,11,F,5020 +1971,2,11,M,5352 +1971,2,12,F,5310 +1971,2,12,M,5476 +1971,2,13,F,4524 +1971,2,13,M,4790 +1971,2,14,F,4446 +1971,2,14,M,4622 +1971,2,15,F,4928 +1971,2,15,M,5128 +1971,2,16,F,5178 +1971,2,16,M,5336 +1971,2,17,F,5062 +1971,2,17,M,5380 +1971,2,18,F,4970 +1971,2,18,M,5258 +1971,2,19,F,5040 +1971,2,19,M,5432 +1971,2,20,F,4596 +1971,2,20,M,4906 +1971,2,21,F,4388 +1971,2,21,M,4806 +1971,2,22,F,5148 +1971,2,22,M,5604 +1971,2,23,F,5358 +1971,2,23,M,5424 +1971,2,24,F,5194 +1971,2,24,M,5338 +1971,2,25,F,5008 +1971,2,25,M,5394 +1971,2,26,F,5276 +1971,2,26,M,5462 +1971,2,27,F,4690 +1971,2,27,M,4896 +1971,2,28,F,4388 +1971,2,28,M,4422 +1971,2,29,F,4 +1971,2,30,M,2 +1971,2,31,M,6 +1971,2,99,F,14 +1971,2,99,M,14 +1971,3,1,F,5036 +1971,3,1,M,5244 +1971,3,2,F,5016 +1971,3,2,M,5382 +1971,3,3,F,5312 +1971,3,3,M,5322 +1971,3,4,F,5092 +1971,3,4,M,5234 +1971,3,5,F,5020 +1971,3,5,M,5432 +1971,3,6,F,4660 +1971,3,6,M,4800 +1971,3,7,F,4380 +1971,3,7,M,4364 +1971,3,8,F,4854 +1971,3,8,M,5302 +1971,3,9,F,5142 +1971,3,9,M,5624 +1971,3,10,F,5066 +1971,3,10,M,5404 +1971,3,11,F,5010 +1971,3,11,M,5290 +1971,3,12,F,5092 +1971,3,12,M,5382 +1971,3,13,F,4602 +1971,3,13,M,4806 +1971,3,14,F,4336 +1971,3,14,M,4502 +1971,3,15,F,5132 +1971,3,15,M,5216 +1971,3,16,F,5050 +1971,3,16,M,5388 +1971,3,17,F,4970 +1971,3,17,M,5264 +1971,3,18,F,4780 +1971,3,18,M,5018 +1971,3,19,F,5082 +1971,3,19,M,5394 +1971,3,20,F,4554 +1971,3,20,M,4610 +1971,3,21,F,4222 +1971,3,21,M,4466 +1971,3,22,F,4812 +1971,3,22,M,5330 +1971,3,23,F,5030 +1971,3,23,M,5310 +1971,3,24,F,4844 +1971,3,24,M,4964 +1971,3,25,F,4894 +1971,3,25,M,5060 +1971,3,26,F,4772 +1971,3,26,M,5098 +1971,3,27,F,4282 +1971,3,27,M,4514 +1971,3,28,F,4228 +1971,3,28,M,4444 +1971,3,29,F,4918 +1971,3,29,M,5302 +1971,3,30,F,5160 +1971,3,30,M,5256 +1971,3,31,F,4966 +1971,3,31,M,5258 +1971,3,99,F,10 +1971,3,99,M,8 +1971,4,1,F,4818 +1971,4,1,M,4938 +1971,4,2,F,5020 +1971,4,2,M,5246 +1971,4,3,F,4256 +1971,4,3,M,4620 +1971,4,4,F,4090 +1971,4,4,M,4244 +1971,4,5,F,4940 +1971,4,5,M,5042 +1971,4,6,F,5054 +1971,4,6,M,5388 +1971,4,7,F,4790 +1971,4,7,M,4956 +1971,4,8,F,4822 +1971,4,8,M,4940 +1971,4,9,F,4702 +1971,4,9,M,5054 +1971,4,10,F,4116 +1971,4,10,M,4546 +1971,4,11,F,3900 +1971,4,11,M,4190 +1971,4,12,F,4650 +1971,4,12,M,5056 +1971,4,13,F,4930 +1971,4,13,M,5366 +1971,4,14,F,4814 +1971,4,14,M,5130 +1971,4,15,F,4666 +1971,4,15,M,4944 +1971,4,16,F,4850 +1971,4,16,M,5084 +1971,4,17,F,4376 +1971,4,17,M,4538 +1971,4,18,F,3986 +1971,4,18,M,4194 +1971,4,19,F,4962 +1971,4,19,M,4956 +1971,4,20,F,4874 +1971,4,20,M,5398 +1971,4,21,F,4880 +1971,4,21,M,5068 +1971,4,22,F,4708 +1971,4,22,M,5004 +1971,4,23,F,4698 +1971,4,23,M,5086 +1971,4,24,F,4248 +1971,4,24,M,4590 +1971,4,25,F,3808 +1971,4,25,M,4176 +1971,4,26,F,4800 +1971,4,26,M,5156 +1971,4,27,F,5082 +1971,4,27,M,5390 +1971,4,28,F,4714 +1971,4,28,M,4950 +1971,4,29,F,4658 +1971,4,29,M,5054 +1971,4,30,F,4758 +1971,4,30,M,4994 +1971,4,31,F,4 +1971,4,31,M,2 +1971,4,99,F,16 +1971,4,99,M,14 +1971,5,1,F,4312 +1971,5,1,M,4514 +1971,5,2,F,3884 +1971,5,2,M,4026 +1971,5,3,F,4560 +1971,5,3,M,4812 +1971,5,4,F,4660 +1971,5,4,M,5180 +1971,5,5,F,4474 +1971,5,5,M,4836 +1971,5,6,F,4354 +1971,5,6,M,4802 +1971,5,7,F,4808 +1971,5,7,M,4946 +1971,5,8,F,4228 +1971,5,8,M,4382 +1971,5,9,F,4080 +1971,5,9,M,4178 +1971,5,10,F,4688 +1971,5,10,M,4948 +1971,5,11,F,4756 +1971,5,11,M,5244 +1971,5,12,F,4724 +1971,5,12,M,4994 +1971,5,13,F,4592 +1971,5,13,M,4810 +1971,5,14,F,4574 +1971,5,14,M,5080 +1971,5,15,F,4040 +1971,5,15,M,4384 +1971,5,16,F,3862 +1971,5,16,M,4070 +1971,5,17,F,4726 +1971,5,17,M,5070 +1971,5,18,F,5108 +1971,5,18,M,5234 +1971,5,19,F,4750 +1971,5,19,M,5068 +1971,5,20,F,4658 +1971,5,20,M,4910 +1971,5,21,F,4650 +1971,5,21,M,4830 +1971,5,22,F,4090 +1971,5,22,M,4290 +1971,5,23,F,3734 +1971,5,23,M,4088 +1971,5,24,F,4754 +1971,5,24,M,5044 +1971,5,25,F,4984 +1971,5,25,M,5250 +1971,5,26,F,4616 +1971,5,26,M,5006 +1971,5,27,F,4770 +1971,5,27,M,4782 +1971,5,28,F,4750 +1971,5,28,M,5026 +1971,5,29,F,4222 +1971,5,29,M,4370 +1971,5,30,F,3770 +1971,5,30,M,4114 +1971,5,31,F,3946 +1971,5,31,M,4478 +1971,5,99,F,14 +1971,5,99,M,22 +1971,6,1,F,4852 +1971,6,1,M,5060 +1971,6,2,F,4720 +1971,6,2,M,5106 +1971,6,3,F,4812 +1971,6,3,M,5120 +1971,6,4,F,5114 +1971,6,4,M,5214 +1971,6,5,F,4310 +1971,6,5,M,4634 +1971,6,6,F,4168 +1971,6,6,M,4194 +1971,6,7,F,4624 +1971,6,7,M,5156 +1971,6,8,F,5008 +1971,6,8,M,5024 +1971,6,9,F,4778 +1971,6,9,M,5054 +1971,6,10,F,4616 +1971,6,10,M,4808 +1971,6,11,F,4742 +1971,6,11,M,5072 +1971,6,12,F,4504 +1971,6,12,M,4320 +1971,6,13,F,3984 +1971,6,13,M,4112 +1971,6,14,F,4754 +1971,6,14,M,5056 +1971,6,15,F,4704 +1971,6,15,M,5234 +1971,6,16,F,4756 +1971,6,16,M,4994 +1971,6,17,F,4692 +1971,6,17,M,4696 +1971,6,18,F,5072 +1971,6,18,M,4872 +1971,6,19,F,4282 +1971,6,19,M,4420 +1971,6,20,F,4062 +1971,6,20,M,4132 +1971,6,21,F,4768 +1971,6,21,M,4952 +1971,6,22,F,5026 +1971,6,22,M,5096 +1971,6,23,F,4602 +1971,6,23,M,4998 +1971,6,24,F,4740 +1971,6,24,M,4944 +1971,6,25,F,5024 +1971,6,25,M,5262 +1971,6,26,F,4316 +1971,6,26,M,4620 +1971,6,27,F,3950 +1971,6,27,M,4100 +1971,6,28,F,4840 +1971,6,28,M,5146 +1971,6,29,F,5000 +1971,6,29,M,5132 +1971,6,30,F,4960 +1971,6,30,M,5166 +1971,6,31,M,8 +1971,6,99,F,24 +1971,6,99,M,12 +1971,7,1,F,4906 +1971,7,1,M,5224 +1971,7,2,F,4812 +1971,7,2,M,5202 +1971,7,3,F,4308 +1971,7,3,M,4388 +1971,7,4,F,3898 +1971,7,4,M,4228 +1971,7,5,F,4154 +1971,7,5,M,4492 +1971,7,6,F,5058 +1971,7,6,M,5122 +1971,7,7,F,5172 +1971,7,7,M,5370 +1971,7,8,F,4982 +1971,7,8,M,5194 +1971,7,9,F,4948 +1971,7,9,M,5548 +1971,7,10,F,4396 +1971,7,10,M,4764 +1971,7,11,F,4116 +1971,7,11,M,4358 +1971,7,12,F,4710 +1971,7,12,M,5218 +1971,7,13,F,5008 +1971,7,13,M,5498 +1971,7,14,F,5090 +1971,7,14,M,5404 +1971,7,15,F,4900 +1971,7,15,M,5164 +1971,7,16,F,4990 +1971,7,16,M,5560 +1971,7,17,F,4488 +1971,7,17,M,4936 +1971,7,18,F,4142 +1971,7,18,M,4466 +1971,7,19,F,4828 +1971,7,19,M,5146 +1971,7,20,F,4996 +1971,7,20,M,5464 +1971,7,21,F,4906 +1971,7,21,M,5508 +1971,7,22,F,4930 +1971,7,22,M,5334 +1971,7,23,F,5146 +1971,7,23,M,5394 +1971,7,24,F,4510 +1971,7,24,M,4944 +1971,7,25,F,4120 +1971,7,25,M,4530 +1971,7,26,F,5030 +1971,7,26,M,5226 +1971,7,27,F,5328 +1971,7,27,M,5458 +1971,7,28,F,5034 +1971,7,28,M,5296 +1971,7,29,F,4974 +1971,7,29,M,5300 +1971,7,30,F,5218 +1971,7,30,M,5354 +1971,7,31,F,4512 +1971,7,31,M,4796 +1971,7,99,F,24 +1971,7,99,M,14 +1971,8,1,F,4286 +1971,8,1,M,4288 +1971,8,2,F,5038 +1971,8,2,M,5286 +1971,8,3,F,5222 +1971,8,3,M,5718 +1971,8,4,F,5134 +1971,8,4,M,5398 +1971,8,5,F,5024 +1971,8,5,M,4956 +1971,8,6,F,5046 +1971,8,6,M,5212 +1971,8,7,F,4644 +1971,8,7,M,4804 +1971,8,8,F,4438 +1971,8,8,M,4666 +1971,8,9,F,5012 +1971,8,9,M,5324 +1971,8,10,F,5314 +1971,8,10,M,5850 +1971,8,11,F,5118 +1971,8,11,M,5728 +1971,8,12,F,5050 +1971,8,12,M,5232 +1971,8,13,F,5092 +1971,8,13,M,5276 +1971,8,14,F,4678 +1971,8,14,M,4942 +1971,8,15,F,4414 +1971,8,15,M,4422 +1971,8,16,F,5032 +1971,8,16,M,5146 +1971,8,17,F,5128 +1971,8,17,M,5498 +1971,8,18,F,5090 +1971,8,18,M,5352 +1971,8,19,F,5118 +1971,8,19,M,5268 +1971,8,20,F,5236 +1971,8,20,M,5634 +1971,8,21,F,4788 +1971,8,21,M,4866 +1971,8,22,F,4296 +1971,8,22,M,4570 +1971,8,23,F,5120 +1971,8,23,M,5278 +1971,8,24,F,5304 +1971,8,24,M,5400 +1971,8,25,F,5128 +1971,8,25,M,5206 +1971,8,26,F,5100 +1971,8,26,M,5444 +1971,8,27,F,5142 +1971,8,27,M,5252 +1971,8,28,F,4546 +1971,8,28,M,4978 +1971,8,29,F,4164 +1971,8,29,M,4556 +1971,8,30,F,4866 +1971,8,30,M,5202 +1971,8,31,F,5214 +1971,8,31,M,5552 +1971,8,99,F,16 +1971,8,99,M,20 +1971,9,1,F,5088 +1971,9,1,M,5328 +1971,9,2,F,4990 +1971,9,2,M,5228 +1971,9,3,F,5230 +1971,9,3,M,5558 +1971,9,4,F,4782 +1971,9,4,M,4784 +1971,9,5,F,4342 +1971,9,5,M,4510 +1971,9,6,F,4694 +1971,9,6,M,4558 +1971,9,7,F,5252 +1971,9,7,M,5602 +1971,9,8,F,5402 +1971,9,8,M,5586 +1971,9,9,F,5284 +1971,9,9,M,5698 +1971,9,10,F,5346 +1971,9,10,M,5564 +1971,9,11,F,4716 +1971,9,11,M,4826 +1971,9,12,F,4242 +1971,9,12,M,4674 +1971,9,13,F,5188 +1971,9,13,M,5394 +1971,9,14,F,5444 +1971,9,14,M,5590 +1971,9,15,F,5364 +1971,9,15,M,5352 +1971,9,16,F,5210 +1971,9,16,M,5570 +1971,9,17,F,5246 +1971,9,17,M,5758 +1971,9,18,F,4932 +1971,9,18,M,4950 +1971,9,19,F,4450 +1971,9,19,M,4660 +1971,9,20,F,5280 +1971,9,20,M,5422 +1971,9,21,F,5336 +1971,9,21,M,5728 +1971,9,22,F,5164 +1971,9,22,M,5422 +1971,9,23,F,5336 +1971,9,23,M,5372 +1971,9,24,F,5310 +1971,9,24,M,5552 +1971,9,25,F,4738 +1971,9,25,M,4846 +1971,9,26,F,4610 +1971,9,26,M,4770 +1971,9,27,F,5300 +1971,9,27,M,5486 +1971,9,28,F,5548 +1971,9,28,M,5924 +1971,9,29,F,5410 +1971,9,29,M,5580 +1971,9,30,F,5412 +1971,9,30,M,5496 +1971,9,31,F,2 +1971,9,31,M,14 +1971,9,99,F,34 +1971,9,99,M,50 +1971,10,1,F,5352 +1971,10,1,M,5570 +1971,10,2,F,4950 +1971,10,2,M,4656 +1971,10,3,F,4314 +1971,10,3,M,4486 +1971,10,4,F,5236 +1971,10,4,M,5370 +1971,10,5,F,5244 +1971,10,5,M,5630 +1971,10,6,F,5042 +1971,10,6,M,5302 +1971,10,7,F,5116 +1971,10,7,M,5176 +1971,10,8,F,5128 +1971,10,8,M,5268 +1971,10,9,F,4226 +1971,10,9,M,4844 +1971,10,10,F,4348 +1971,10,10,M,4712 +1971,10,11,F,5062 +1971,10,11,M,5204 +1971,10,12,F,5198 +1971,10,12,M,5332 +1971,10,13,F,5070 +1971,10,13,M,5140 +1971,10,14,F,4906 +1971,10,14,M,5280 +1971,10,15,F,5010 +1971,10,15,M,5170 +1971,10,16,F,4450 +1971,10,16,M,4700 +1971,10,17,F,4300 +1971,10,17,M,4204 +1971,10,18,F,5060 +1971,10,18,M,5138 +1971,10,19,F,5074 +1971,10,19,M,5416 +1971,10,20,F,4810 +1971,10,20,M,5060 +1971,10,21,F,4808 +1971,10,21,M,5182 +1971,10,22,F,4970 +1971,10,22,M,5072 +1971,10,23,F,4318 +1971,10,23,M,4414 +1971,10,24,F,4090 +1971,10,24,M,4326 +1971,10,25,F,4666 +1971,10,25,M,4900 +1971,10,26,F,4922 +1971,10,26,M,5132 +1971,10,27,F,4918 +1971,10,27,M,4978 +1971,10,28,F,4812 +1971,10,28,M,4990 +1971,10,29,F,4804 +1971,10,29,M,5118 +1971,10,30,F,4154 +1971,10,30,M,4460 +1971,10,31,F,4282 +1971,10,31,M,4334 +1971,10,99,F,16 +1971,10,99,M,28 +1971,11,1,F,4596 +1971,11,1,M,5040 +1971,11,2,F,4942 +1971,11,2,M,5024 +1971,11,3,F,4712 +1971,11,3,M,5082 +1971,11,4,F,4692 +1971,11,4,M,4824 +1971,11,5,F,4926 +1971,11,5,M,4936 +1971,11,6,F,4272 +1971,11,6,M,4428 +1971,11,7,F,4064 +1971,11,7,M,4168 +1971,11,8,F,4776 +1971,11,8,M,4964 +1971,11,9,F,5026 +1971,11,9,M,5140 +1971,11,10,F,4774 +1971,11,10,M,4916 +1971,11,11,F,4758 +1971,11,11,M,4802 +1971,11,12,F,5002 +1971,11,12,M,5106 +1971,11,13,F,4396 +1971,11,13,M,4452 +1971,11,14,F,4032 +1971,11,14,M,4326 +1971,11,15,F,4916 +1971,11,15,M,4944 +1971,11,16,F,4882 +1971,11,16,M,5256 +1971,11,17,F,4626 +1971,11,17,M,5100 +1971,11,18,F,4708 +1971,11,18,M,5130 +1971,11,19,F,4922 +1971,11,19,M,5214 +1971,11,20,F,4308 +1971,11,20,M,4624 +1971,11,21,F,4074 +1971,11,21,M,4212 +1971,11,22,F,4706 +1971,11,22,M,5188 +1971,11,23,F,4944 +1971,11,23,M,5188 +1971,11,24,F,4742 +1971,11,24,M,4844 +1971,11,25,F,3684 +1971,11,25,M,3882 +1971,11,26,F,4430 +1971,11,26,M,4712 +1971,11,27,F,4048 +1971,11,27,M,4514 +1971,11,28,F,3966 +1971,11,28,M,4274 +1971,11,29,F,4696 +1971,11,29,M,5052 +1971,11,30,F,4584 +1971,11,30,M,5182 +1971,11,31,F,6 +1971,11,31,M,4 +1971,11,99,F,8 +1971,11,99,M,16 +1971,12,1,F,4694 +1971,12,1,M,4940 +1971,12,2,F,4480 +1971,12,2,M,4686 +1971,12,3,F,4578 +1971,12,3,M,4714 +1971,12,4,F,4292 +1971,12,4,M,4500 +1971,12,5,F,4120 +1971,12,5,M,4134 +1971,12,6,F,4790 +1971,12,6,M,4942 +1971,12,7,F,4912 +1971,12,7,M,5114 +1971,12,8,F,4674 +1971,12,8,M,4890 +1971,12,9,F,4622 +1971,12,9,M,4904 +1971,12,10,F,4680 +1971,12,10,M,5198 +1971,12,11,F,4250 +1971,12,11,M,4338 +1971,12,12,F,3874 +1971,12,12,M,4228 +1971,12,13,F,4610 +1971,12,13,M,4892 +1971,12,14,F,4960 +1971,12,14,M,5226 +1971,12,15,F,4856 +1971,12,15,M,5030 +1971,12,16,F,4882 +1971,12,16,M,4888 +1971,12,17,F,4750 +1971,12,17,M,5186 +1971,12,18,F,4184 +1971,12,18,M,4338 +1971,12,19,F,3904 +1971,12,19,M,4048 +1971,12,20,F,4944 +1971,12,20,M,5158 +1971,12,21,F,5102 +1971,12,21,M,5204 +1971,12,22,F,4388 +1971,12,22,M,4630 +1971,12,23,F,3972 +1971,12,23,M,4294 +1971,12,24,F,3746 +1971,12,24,M,4080 +1971,12,25,F,3556 +1971,12,25,M,3684 +1971,12,26,F,3706 +1971,12,26,M,3914 +1971,12,27,F,4692 +1971,12,27,M,5000 +1971,12,28,F,5218 +1971,12,28,M,5276 +1971,12,29,F,4936 +1971,12,29,M,5244 +1971,12,30,F,5048 +1971,12,30,M,5108 +1971,12,31,F,4520 +1971,12,31,M,4654 +1971,12,99,F,18 +1971,12,99,M,10 +1972,1,1,F,3653 +1972,1,1,M,4040 +1972,1,2,F,3844 +1972,1,2,M,3951 +1972,1,3,F,4518 +1972,1,3,M,4418 +1972,1,4,F,4464 +1972,1,4,M,4777 +1972,1,5,F,4333 +1972,1,5,M,4619 +1972,1,6,F,4268 +1972,1,6,M,4470 +1972,1,7,F,4614 +1972,1,7,M,4716 +1972,1,8,F,4037 +1972,1,8,M,4205 +1972,1,9,F,3852 +1972,1,9,M,4059 +1972,1,10,F,4572 +1972,1,10,M,4775 +1972,1,11,F,4855 +1972,1,11,M,4933 +1972,1,12,F,4598 +1972,1,12,M,4714 +1972,1,13,F,4628 +1972,1,13,M,4657 +1972,1,14,F,4663 +1972,1,14,M,4959 +1972,1,15,F,3925 +1972,1,15,M,4110 +1972,1,16,F,3737 +1972,1,16,M,3927 +1972,1,17,F,4477 +1972,1,17,M,4945 +1972,1,18,F,4842 +1972,1,18,M,5030 +1972,1,19,F,4543 +1972,1,19,M,4883 +1972,1,20,F,4474 +1972,1,20,M,4867 +1972,1,21,F,4629 +1972,1,21,M,4739 +1972,1,22,F,4309 +1972,1,22,M,4501 +1972,1,23,F,4046 +1972,1,23,M,4102 +1972,1,24,F,4740 +1972,1,24,M,4906 +1972,1,25,F,4648 +1972,1,25,M,5130 +1972,1,26,F,4540 +1972,1,26,M,4616 +1972,1,27,F,4388 +1972,1,27,M,4668 +1972,1,28,F,4433 +1972,1,28,M,4744 +1972,1,29,F,4078 +1972,1,29,M,4154 +1972,1,30,F,3944 +1972,1,30,M,3982 +1972,1,31,F,4526 +1972,1,31,M,4745 +1972,1,99,F,12 +1972,1,99,M,12 +1972,2,1,F,4768 +1972,2,1,M,4895 +1972,2,2,F,4586 +1972,2,2,M,4784 +1972,2,3,F,4511 +1972,2,3,M,4716 +1972,2,4,F,4553 +1972,2,4,M,4806 +1972,2,5,F,4124 +1972,2,5,M,4199 +1972,2,6,F,3839 +1972,2,6,M,4126 +1972,2,7,F,4520 +1972,2,7,M,4775 +1972,2,8,F,4658 +1972,2,8,M,4940 +1972,2,9,F,4309 +1972,2,9,M,4691 +1972,2,10,F,4515 +1972,2,10,M,4430 +1972,2,11,F,4555 +1972,2,11,M,4802 +1972,2,12,F,4256 +1972,2,12,M,4315 +1972,2,13,F,3895 +1972,2,13,M,4114 +1972,2,14,F,4827 +1972,2,14,M,5037 +1972,2,15,F,4728 +1972,2,15,M,4939 +1972,2,16,F,4667 +1972,2,16,M,4907 +1972,2,17,F,4520 +1972,2,17,M,4905 +1972,2,18,F,4732 +1972,2,18,M,4920 +1972,2,19,F,4035 +1972,2,19,M,4315 +1972,2,20,F,3906 +1972,2,20,M,4030 +1972,2,21,F,4400 +1972,2,21,M,4407 +1972,2,22,F,4660 +1972,2,22,M,5055 +1972,2,23,F,4574 +1972,2,23,M,4783 +1972,2,24,F,4557 +1972,2,24,M,4682 +1972,2,25,F,4608 +1972,2,25,M,4887 +1972,2,26,F,4115 +1972,2,26,M,4486 +1972,2,27,F,3640 +1972,2,27,M,3951 +1972,2,28,F,4505 +1972,2,28,M,4792 +1972,2,29,F,4663 +1972,2,29,M,4919 +1972,2,30,F,2 +1972,2,31,M,2 +1972,2,99,F,8 +1972,2,99,M,16 +1972,3,1,F,4814 +1972,3,1,M,4807 +1972,3,2,F,4523 +1972,3,2,M,4752 +1972,3,3,F,4601 +1972,3,3,M,5004 +1972,3,4,F,4044 +1972,3,4,M,4380 +1972,3,5,F,3793 +1972,3,5,M,3927 +1972,3,6,F,4412 +1972,3,6,M,4714 +1972,3,7,F,4601 +1972,3,7,M,4881 +1972,3,8,F,4543 +1972,3,8,M,4688 +1972,3,9,F,4344 +1972,3,9,M,4561 +1972,3,10,F,4503 +1972,3,10,M,4607 +1972,3,11,F,4089 +1972,3,11,M,4154 +1972,3,12,F,3851 +1972,3,12,M,4014 +1972,3,13,F,4549 +1972,3,13,M,4718 +1972,3,14,F,4637 +1972,3,14,M,4956 +1972,3,15,F,4510 +1972,3,15,M,4736 +1972,3,16,F,4356 +1972,3,16,M,4690 +1972,3,17,F,4567 +1972,3,17,M,4922 +1972,3,18,F,4013 +1972,3,18,M,4063 +1972,3,19,F,3737 +1972,3,19,M,3921 +1972,3,20,F,4365 +1972,3,20,M,4440 +1972,3,21,F,4331 +1972,3,21,M,4729 +1972,3,22,F,4347 +1972,3,22,M,4574 +1972,3,23,F,4231 +1972,3,23,M,4647 +1972,3,24,F,4280 +1972,3,24,M,4613 +1972,3,25,F,3892 +1972,3,25,M,4034 +1972,3,26,F,3664 +1972,3,26,M,3885 +1972,3,27,F,4424 +1972,3,27,M,4659 +1972,3,28,F,4528 +1972,3,28,M,4739 +1972,3,29,F,4221 +1972,3,29,M,4660 +1972,3,30,F,4232 +1972,3,30,M,4412 +1972,3,31,F,4275 +1972,3,31,M,4465 +1972,3,99,F,8 +1972,3,99,M,8 +1972,4,1,F,3766 +1972,4,1,M,3792 +1972,4,2,F,3576 +1972,4,2,M,3783 +1972,4,3,F,4135 +1972,4,3,M,4569 +1972,4,4,F,4726 +1972,4,4,M,4595 +1972,4,5,F,4317 +1972,4,5,M,4570 +1972,4,6,F,4206 +1972,4,6,M,4765 +1972,4,7,F,4389 +1972,4,7,M,4581 +1972,4,8,F,3784 +1972,4,8,M,4107 +1972,4,9,F,3785 +1972,4,9,M,3750 +1972,4,10,F,4204 +1972,4,10,M,4494 +1972,4,11,F,4435 +1972,4,11,M,4745 +1972,4,12,F,4313 +1972,4,12,M,4654 +1972,4,13,F,4409 +1972,4,13,M,4578 +1972,4,14,F,4493 +1972,4,14,M,4699 +1972,4,15,F,3912 +1972,4,15,M,4195 +1972,4,16,F,3632 +1972,4,16,M,3933 +1972,4,17,F,4298 +1972,4,17,M,4561 +1972,4,18,F,4516 +1972,4,18,M,4785 +1972,4,19,F,4363 +1972,4,19,M,4464 +1972,4,20,F,4259 +1972,4,20,M,4569 +1972,4,21,F,4230 +1972,4,21,M,4489 +1972,4,22,F,3749 +1972,4,22,M,4084 +1972,4,23,F,3537 +1972,4,23,M,3798 +1972,4,24,F,4434 +1972,4,24,M,4472 +1972,4,25,F,4496 +1972,4,25,M,4817 +1972,4,26,F,4245 +1972,4,26,M,4399 +1972,4,27,F,4373 +1972,4,27,M,4346 +1972,4,28,F,4261 +1972,4,28,M,4591 +1972,4,29,F,3797 +1972,4,29,M,4010 +1972,4,30,F,3522 +1972,4,30,M,3690 +1972,4,31,F,4 +1972,4,31,M,4 +1972,4,99,F,12 +1972,4,99,M,8 +1972,5,1,F,4393 +1972,5,1,M,4652 +1972,5,2,F,4520 +1972,5,2,M,4813 +1972,5,3,F,4497 +1972,5,3,M,4603 +1972,5,4,F,4248 +1972,5,4,M,4506 +1972,5,5,F,4393 +1972,5,5,M,4526 +1972,5,6,F,3747 +1972,5,6,M,4042 +1972,5,7,F,3692 +1972,5,7,M,3915 +1972,5,8,F,4318 +1972,5,8,M,4658 +1972,5,9,F,4399 +1972,5,9,M,4875 +1972,5,10,F,4191 +1972,5,10,M,4717 +1972,5,11,F,4180 +1972,5,11,M,4566 +1972,5,12,F,4465 +1972,5,12,M,4615 +1972,5,13,F,3885 +1972,5,13,M,4141 +1972,5,14,F,3621 +1972,5,14,M,3908 +1972,5,15,F,4367 +1972,5,15,M,4692 +1972,5,16,F,4597 +1972,5,16,M,4776 +1972,5,17,F,4384 +1972,5,17,M,4723 +1972,5,18,F,4343 +1972,5,18,M,4466 +1972,5,19,F,4379 +1972,5,19,M,4637 +1972,5,20,F,3966 +1972,5,20,M,4066 +1972,5,21,F,3558 +1972,5,21,M,3810 +1972,5,22,F,4516 +1972,5,22,M,4732 +1972,5,23,F,4666 +1972,5,23,M,4989 +1972,5,24,F,4512 +1972,5,24,M,4634 +1972,5,25,F,4539 +1972,5,25,M,4754 +1972,5,26,F,4325 +1972,5,26,M,4661 +1972,5,27,F,3867 +1972,5,27,M,4108 +1972,5,28,F,3695 +1972,5,28,M,3912 +1972,5,29,F,3786 +1972,5,29,M,4066 +1972,5,30,F,4488 +1972,5,30,M,4714 +1972,5,31,F,4588 +1972,5,31,M,4863 +1972,5,99,F,8 +1972,5,99,M,6 +1972,6,1,F,4412 +1972,6,1,M,4765 +1972,6,2,F,4412 +1972,6,2,M,4445 +1972,6,3,F,4003 +1972,6,3,M,4072 +1972,6,4,F,3562 +1972,6,4,M,4025 +1972,6,5,F,4237 +1972,6,5,M,4490 +1972,6,6,F,4542 +1972,6,6,M,4731 +1972,6,7,F,4392 +1972,6,7,M,4691 +1972,6,8,F,4168 +1972,6,8,M,4596 +1972,6,9,F,4414 +1972,6,9,M,4703 +1972,6,10,F,3983 +1972,6,10,M,4152 +1972,6,11,F,3556 +1972,6,11,M,3782 +1972,6,12,F,4225 +1972,6,12,M,4472 +1972,6,13,F,4490 +1972,6,13,M,4714 +1972,6,14,F,4333 +1972,6,14,M,4527 +1972,6,15,F,4423 +1972,6,15,M,4508 +1972,6,16,F,4614 +1972,6,16,M,4584 +1972,6,17,F,3806 +1972,6,17,M,3974 +1972,6,18,F,3624 +1972,6,18,M,3814 +1972,6,19,F,4398 +1972,6,19,M,4452 +1972,6,20,F,4421 +1972,6,20,M,4748 +1972,6,21,F,4234 +1972,6,21,M,4598 +1972,6,22,F,4347 +1972,6,22,M,4192 +1972,6,23,F,4370 +1972,6,23,M,4417 +1972,6,24,F,3769 +1972,6,24,M,4136 +1972,6,25,F,3768 +1972,6,25,M,3819 +1972,6,26,F,4448 +1972,6,26,M,4751 +1972,6,27,F,4620 +1972,6,27,M,4795 +1972,6,28,F,4422 +1972,6,28,M,4853 +1972,6,29,F,4449 +1972,6,29,M,4766 +1972,6,30,F,4323 +1972,6,30,M,4856 +1972,6,31,F,4 +1972,6,99,F,12 +1972,6,99,M,20 +1972,7,1,F,4151 +1972,7,1,M,4342 +1972,7,2,F,3684 +1972,7,2,M,4041 +1972,7,3,F,4279 +1972,7,3,M,4473 +1972,7,4,F,4014 +1972,7,4,M,4079 +1972,7,5,F,4460 +1972,7,5,M,4460 +1972,7,6,F,4438 +1972,7,6,M,4815 +1972,7,7,F,4623 +1972,7,7,M,4887 +1972,7,8,F,4287 +1972,7,8,M,4335 +1972,7,9,F,3714 +1972,7,9,M,3942 +1972,7,10,F,4449 +1972,7,10,M,4813 +1972,7,11,F,4714 +1972,7,11,M,4921 +1972,7,12,F,4779 +1972,7,12,M,4959 +1972,7,13,F,4537 +1972,7,13,M,4853 +1972,7,14,F,4697 +1972,7,14,M,4960 +1972,7,15,F,4303 +1972,7,15,M,4443 +1972,7,16,F,3865 +1972,7,16,M,4103 +1972,7,17,F,4702 +1972,7,17,M,4832 +1972,7,18,F,4709 +1972,7,18,M,5010 +1972,7,19,F,4478 +1972,7,19,M,4757 +1972,7,20,F,4642 +1972,7,20,M,4906 +1972,7,21,F,4794 +1972,7,21,M,4870 +1972,7,22,F,4191 +1972,7,22,M,4404 +1972,7,23,F,3902 +1972,7,23,M,4052 +1972,7,24,F,4592 +1972,7,24,M,4893 +1972,7,25,F,4611 +1972,7,25,M,5051 +1972,7,26,F,4453 +1972,7,26,M,4693 +1972,7,27,F,4747 +1972,7,27,M,4746 +1972,7,28,F,4672 +1972,7,28,M,4834 +1972,7,29,F,4088 +1972,7,29,M,4353 +1972,7,30,F,3743 +1972,7,30,M,3967 +1972,7,31,F,4316 +1972,7,31,M,4824 +1972,7,99,F,6 +1972,7,99,M,10 +1972,8,1,F,4814 +1972,8,1,M,5022 +1972,8,2,F,4639 +1972,8,2,M,4741 +1972,8,3,F,4493 +1972,8,3,M,4807 +1972,8,4,F,4663 +1972,8,4,M,4829 +1972,8,5,F,4168 +1972,8,5,M,4341 +1972,8,6,F,3970 +1972,8,6,M,3992 +1972,8,7,F,4514 +1972,8,7,M,4776 +1972,8,8,F,4769 +1972,8,8,M,5286 +1972,8,9,F,4588 +1972,8,9,M,4893 +1972,8,10,F,4560 +1972,8,10,M,4826 +1972,8,11,F,4518 +1972,8,11,M,4812 +1972,8,12,F,4146 +1972,8,12,M,4562 +1972,8,13,F,3855 +1972,8,13,M,4210 +1972,8,14,F,4598 +1972,8,14,M,4941 +1972,8,15,F,4950 +1972,8,15,M,5179 +1972,8,16,F,4812 +1972,8,16,M,4886 +1972,8,17,F,4589 +1972,8,17,M,5013 +1972,8,18,F,4810 +1972,8,18,M,4988 +1972,8,19,F,4155 +1972,8,19,M,4465 +1972,8,20,F,4052 +1972,8,20,M,4235 +1972,8,21,F,4702 +1972,8,21,M,4967 +1972,8,22,F,4879 +1972,8,22,M,5281 +1972,8,23,F,4541 +1972,8,23,M,4953 +1972,8,24,F,4537 +1972,8,24,M,4873 +1972,8,25,F,4797 +1972,8,25,M,4919 +1972,8,26,F,4174 +1972,8,26,M,4544 +1972,8,27,F,3772 +1972,8,27,M,4250 +1972,8,28,F,4750 +1972,8,28,M,4994 +1972,8,29,F,4869 +1972,8,29,M,5165 +1972,8,30,F,4688 +1972,8,30,M,4895 +1972,8,31,F,4618 +1972,8,31,M,4911 +1972,8,99,F,14 +1972,8,99,M,10 +1972,9,1,F,4649 +1972,9,1,M,4894 +1972,9,2,F,4016 +1972,9,2,M,4392 +1972,9,3,F,3902 +1972,9,3,M,4165 +1972,9,4,F,3936 +1972,9,4,M,4258 +1972,9,5,F,4673 +1972,9,5,M,4987 +1972,9,6,F,4883 +1972,9,6,M,4936 +1972,9,7,F,4863 +1972,9,7,M,4961 +1972,9,8,F,4915 +1972,9,8,M,5228 +1972,9,9,F,4381 +1972,9,9,M,4445 +1972,9,10,F,4169 +1972,9,10,M,4064 +1972,9,11,F,4714 +1972,9,11,M,5057 +1972,9,12,F,4854 +1972,9,12,M,5165 +1972,9,13,F,4822 +1972,9,13,M,4972 +1972,9,14,F,4892 +1972,9,14,M,5143 +1972,9,15,F,5038 +1972,9,15,M,5241 +1972,9,16,F,4573 +1972,9,16,M,4628 +1972,9,17,F,4088 +1972,9,17,M,4451 +1972,9,18,F,4882 +1972,9,18,M,5023 +1972,9,19,F,4927 +1972,9,19,M,5146 +1972,9,20,F,4910 +1972,9,20,M,5012 +1972,9,21,F,4715 +1972,9,21,M,5009 +1972,9,22,F,4831 +1972,9,22,M,4951 +1972,9,23,F,4306 +1972,9,23,M,4471 +1972,9,24,F,4056 +1972,9,24,M,4399 +1972,9,25,F,4902 +1972,9,25,M,4933 +1972,9,26,F,4927 +1972,9,26,M,5226 +1972,9,27,F,4895 +1972,9,27,M,5017 +1972,9,28,F,4785 +1972,9,28,M,4809 +1972,9,29,F,4786 +1972,9,29,M,4890 +1972,9,30,F,4358 +1972,9,30,M,4493 +1972,9,31,F,2 +1972,9,31,M,2 +1972,9,99,F,32 +1972,9,99,M,26 +1972,10,1,F,3818 +1972,10,1,M,4164 +1972,10,2,F,4681 +1972,10,2,M,4950 +1972,10,3,F,4617 +1972,10,3,M,4903 +1972,10,4,F,4785 +1972,10,4,M,4738 +1972,10,5,F,4635 +1972,10,5,M,4808 +1972,10,6,F,4796 +1972,10,6,M,4933 +1972,10,7,F,4158 +1972,10,7,M,4251 +1972,10,8,F,3825 +1972,10,8,M,4079 +1972,10,9,F,4430 +1972,10,9,M,4725 +1972,10,10,F,4738 +1972,10,10,M,5028 +1972,10,11,F,4464 +1972,10,11,M,4696 +1972,10,12,F,4707 +1972,10,12,M,4789 +1972,10,13,F,4471 +1972,10,13,M,4812 +1972,10,14,F,4093 +1972,10,14,M,4133 +1972,10,15,F,3920 +1972,10,15,M,3964 +1972,10,16,F,4525 +1972,10,16,M,4579 +1972,10,17,F,4587 +1972,10,17,M,4816 +1972,10,18,F,4465 +1972,10,18,M,4592 +1972,10,19,F,4367 +1972,10,19,M,4640 +1972,10,20,F,4447 +1972,10,20,M,4677 +1972,10,21,F,3923 +1972,10,21,M,4057 +1972,10,22,F,3734 +1972,10,22,M,3971 +1972,10,23,F,4288 +1972,10,23,M,4703 +1972,10,24,F,4485 +1972,10,24,M,4863 +1972,10,25,F,4176 +1972,10,25,M,4876 +1972,10,26,F,4353 +1972,10,26,M,4486 +1972,10,27,F,4430 +1972,10,27,M,4600 +1972,10,28,F,3969 +1972,10,28,M,4026 +1972,10,29,F,3881 +1972,10,29,M,4026 +1972,10,30,F,4305 +1972,10,30,M,4629 +1972,10,31,F,4537 +1972,10,31,M,4755 +1972,10,99,F,14 +1972,10,99,M,10 +1972,11,1,F,4459 +1972,11,1,M,4562 +1972,11,2,F,4372 +1972,11,2,M,4465 +1972,11,3,F,4534 +1972,11,3,M,4784 +1972,11,4,F,3986 +1972,11,4,M,4113 +1972,11,5,F,3602 +1972,11,5,M,3863 +1972,11,6,F,4371 +1972,11,6,M,4885 +1972,11,7,F,4486 +1972,11,7,M,4943 +1972,11,8,F,4485 +1972,11,8,M,4755 +1972,11,9,F,4426 +1972,11,9,M,4638 +1972,11,10,F,4434 +1972,11,10,M,4807 +1972,11,11,F,4199 +1972,11,11,M,4325 +1972,11,12,F,3886 +1972,11,12,M,3922 +1972,11,13,F,4385 +1972,11,13,M,4615 +1972,11,14,F,4635 +1972,11,14,M,4811 +1972,11,15,F,4345 +1972,11,15,M,4706 +1972,11,16,F,4536 +1972,11,16,M,4516 +1972,11,17,F,4491 +1972,11,17,M,4800 +1972,11,18,F,3900 +1972,11,18,M,4182 +1972,11,19,F,3773 +1972,11,19,M,4033 +1972,11,20,F,4731 +1972,11,20,M,4775 +1972,11,21,F,4541 +1972,11,21,M,5005 +1972,11,22,F,4417 +1972,11,22,M,4740 +1972,11,23,F,3677 +1972,11,23,M,3726 +1972,11,24,F,4312 +1972,11,24,M,4291 +1972,11,25,F,4077 +1972,11,25,M,4230 +1972,11,26,F,3891 +1972,11,26,M,3899 +1972,11,27,F,4515 +1972,11,27,M,4859 +1972,11,28,F,4702 +1972,11,28,M,4961 +1972,11,29,F,4562 +1972,11,29,M,4616 +1972,11,30,F,4276 +1972,11,30,M,4780 +1972,11,31,F,8 +1972,11,31,M,4 +1972,11,99,F,22 +1972,11,99,M,22 +1972,12,1,F,4486 +1972,12,1,M,4574 +1972,12,2,F,3976 +1972,12,2,M,4133 +1972,12,3,F,3915 +1972,12,3,M,3968 +1972,12,4,F,4540 +1972,12,4,M,4756 +1972,12,5,F,4708 +1972,12,5,M,4969 +1972,12,6,F,4432 +1972,12,6,M,4847 +1972,12,7,F,4528 +1972,12,7,M,4598 +1972,12,8,F,4329 +1972,12,8,M,4681 +1972,12,9,F,4059 +1972,12,9,M,4182 +1972,12,10,F,3899 +1972,12,10,M,3983 +1972,12,11,F,4576 +1972,12,11,M,4863 +1972,12,12,F,4882 +1972,12,12,M,5020 +1972,12,13,F,4565 +1972,12,13,M,4791 +1972,12,14,F,4659 +1972,12,14,M,4618 +1972,12,15,F,4701 +1972,12,15,M,5005 +1972,12,16,F,4226 +1972,12,16,M,4450 +1972,12,17,F,3875 +1972,12,17,M,3895 +1972,12,18,F,4864 +1972,12,18,M,5237 +1972,12,19,F,4968 +1972,12,19,M,5296 +1972,12,20,F,4904 +1972,12,20,M,4896 +1972,12,21,F,4611 +1972,12,21,M,4820 +1972,12,22,F,4474 +1972,12,22,M,4293 +1972,12,23,F,3775 +1972,12,23,M,3791 +1972,12,24,F,3411 +1972,12,24,M,3766 +1972,12,25,F,3586 +1972,12,25,M,3655 +1972,12,26,F,4331 +1972,12,26,M,4454 +1972,12,27,F,4702 +1972,12,27,M,5130 +1972,12,28,F,4813 +1972,12,28,M,5210 +1972,12,29,F,4966 +1972,12,29,M,5060 +1972,12,30,F,4067 +1972,12,30,M,4490 +1972,12,31,F,3710 +1972,12,31,M,4102 +1972,12,99,F,20 +1972,12,99,M,24 +1973,1,1,F,3694 +1973,1,1,M,3697 +1973,1,2,F,4058 +1973,1,2,M,4184 +1973,1,3,F,4453 +1973,1,3,M,4513 +1973,1,4,F,4284 +1973,1,4,M,4595 +1973,1,5,F,4371 +1973,1,5,M,4488 +1973,1,6,F,3763 +1973,1,6,M,4003 +1973,1,7,F,3544 +1973,1,7,M,3765 +1973,1,8,F,4274 +1973,1,8,M,4312 +1973,1,9,F,4309 +1973,1,9,M,4397 +1973,1,10,F,4222 +1973,1,10,M,4457 +1973,1,11,F,4261 +1973,1,11,M,4689 +1973,1,12,F,4538 +1973,1,12,M,4647 +1973,1,13,F,3975 +1973,1,13,M,4209 +1973,1,14,F,3731 +1973,1,14,M,3829 +1973,1,15,F,4450 +1973,1,15,M,4453 +1973,1,16,F,4541 +1973,1,16,M,4808 +1973,1,17,F,4385 +1973,1,17,M,4811 +1973,1,18,F,4353 +1973,1,18,M,4711 +1973,1,19,F,4508 +1973,1,19,M,4879 +1973,1,20,F,3891 +1973,1,20,M,4131 +1973,1,21,F,3743 +1973,1,21,M,3876 +1973,1,22,F,4387 +1973,1,22,M,4652 +1973,1,23,F,4540 +1973,1,23,M,4732 +1973,1,24,F,4307 +1973,1,24,M,4579 +1973,1,25,F,4423 +1973,1,25,M,4536 +1973,1,26,F,4418 +1973,1,26,M,4619 +1973,1,27,F,3872 +1973,1,27,M,4150 +1973,1,28,F,3817 +1973,1,28,M,4010 +1973,1,29,F,4276 +1973,1,29,M,4713 +1973,1,30,F,4287 +1973,1,30,M,4472 +1973,1,31,F,4460 +1973,1,31,M,4377 +1973,1,99,F,28 +1973,1,99,M,26 +1973,2,1,F,4209 +1973,2,1,M,4286 +1973,2,2,F,4308 +1973,2,2,M,4569 +1973,2,3,F,4079 +1973,2,3,M,4048 +1973,2,4,F,3665 +1973,2,4,M,3822 +1973,2,5,F,4428 +1973,2,5,M,4585 +1973,2,6,F,4443 +1973,2,6,M,4789 +1973,2,7,F,4317 +1973,2,7,M,4511 +1973,2,8,F,4328 +1973,2,8,M,4471 +1973,2,9,F,4376 +1973,2,9,M,4629 +1973,2,10,F,3907 +1973,2,10,M,4214 +1973,2,11,F,3657 +1973,2,11,M,3754 +1973,2,12,F,4329 +1973,2,12,M,4576 +1973,2,13,F,4308 +1973,2,13,M,4588 +1973,2,14,F,4474 +1973,2,14,M,4649 +1973,2,15,F,4238 +1973,2,15,M,4667 +1973,2,16,F,4370 +1973,2,16,M,4571 +1973,2,17,F,3944 +1973,2,17,M,4210 +1973,2,18,F,3580 +1973,2,18,M,3829 +1973,2,19,F,4128 +1973,2,19,M,4485 +1973,2,20,F,4493 +1973,2,20,M,4647 +1973,2,21,F,4455 +1973,2,21,M,4887 +1973,2,22,F,4291 +1973,2,22,M,4525 +1973,2,23,F,4376 +1973,2,23,M,4557 +1973,2,24,F,4139 +1973,2,24,M,4211 +1973,2,25,F,3852 +1973,2,25,M,4041 +1973,2,26,F,4321 +1973,2,26,M,4546 +1973,2,27,F,4329 +1973,2,27,M,4698 +1973,2,28,F,4351 +1973,2,28,M,4583 +1973,2,29,F,3 +1973,2,29,M,4 +1973,2,99,F,20 +1973,2,99,M,22 +1973,3,1,F,4348 +1973,3,1,M,4701 +1973,3,2,F,4524 +1973,3,2,M,4726 +1973,3,3,F,4219 +1973,3,3,M,4212 +1973,3,4,F,3772 +1973,3,4,M,4062 +1973,3,5,F,4344 +1973,3,5,M,4638 +1973,3,6,F,4634 +1973,3,6,M,4776 +1973,3,7,F,4443 +1973,3,7,M,4644 +1973,3,8,F,4549 +1973,3,8,M,4487 +1973,3,9,F,4520 +1973,3,9,M,4580 +1973,3,10,F,4073 +1973,3,10,M,4142 +1973,3,11,F,3805 +1973,3,11,M,3870 +1973,3,12,F,4466 +1973,3,12,M,4725 +1973,3,13,F,4523 +1973,3,13,M,4888 +1973,3,14,F,4430 +1973,3,14,M,4483 +1973,3,15,F,4423 +1973,3,15,M,4669 +1973,3,16,F,4393 +1973,3,16,M,4506 +1973,3,17,F,3919 +1973,3,17,M,4141 +1973,3,18,F,3644 +1973,3,18,M,3929 +1973,3,19,F,4250 +1973,3,19,M,4478 +1973,3,20,F,4330 +1973,3,20,M,4722 +1973,3,21,F,4321 +1973,3,21,M,4476 +1973,3,22,F,4314 +1973,3,22,M,4490 +1973,3,23,F,4263 +1973,3,23,M,4707 +1973,3,24,F,3929 +1973,3,24,M,3919 +1973,3,25,F,3807 +1973,3,25,M,3796 +1973,3,26,F,4264 +1973,3,26,M,4704 +1973,3,27,F,4493 +1973,3,27,M,4617 +1973,3,28,F,4320 +1973,3,28,M,4432 +1973,3,29,F,4308 +1973,3,29,M,4528 +1973,3,30,F,4321 +1973,3,30,M,4598 +1973,3,31,F,3810 +1973,3,31,M,4077 +1973,3,99,F,12 +1973,3,99,M,12 +1973,4,1,F,3709 +1973,4,1,M,3786 +1973,4,2,F,4328 +1973,4,2,M,4578 +1973,4,3,F,4284 +1973,4,3,M,4751 +1973,4,4,F,4206 +1973,4,4,M,4560 +1973,4,5,F,4100 +1973,4,5,M,4407 +1973,4,6,F,4243 +1973,4,6,M,4372 +1973,4,7,F,3932 +1973,4,7,M,4128 +1973,4,8,F,3612 +1973,4,8,M,3743 +1973,4,9,F,4160 +1973,4,9,M,4289 +1973,4,10,F,4378 +1973,4,10,M,4465 +1973,4,11,F,4295 +1973,4,11,M,4486 +1973,4,12,F,4154 +1973,4,12,M,4459 +1973,4,13,F,4111 +1973,4,13,M,4505 +1973,4,14,F,3763 +1973,4,14,M,3682 +1973,4,15,F,3468 +1973,4,15,M,3699 +1973,4,16,F,4212 +1973,4,16,M,4447 +1973,4,17,F,4449 +1973,4,17,M,4468 +1973,4,18,F,4325 +1973,4,18,M,4566 +1973,4,19,F,4279 +1973,4,19,M,4393 +1973,4,20,F,4020 +1973,4,20,M,4520 +1973,4,21,F,3663 +1973,4,21,M,3916 +1973,4,22,F,3323 +1973,4,22,M,3631 +1973,4,23,F,4112 +1973,4,23,M,4260 +1973,4,24,F,4162 +1973,4,24,M,4595 +1973,4,25,F,4060 +1973,4,25,M,4292 +1973,4,26,F,4175 +1973,4,26,M,4333 +1973,4,27,F,4132 +1973,4,27,M,4405 +1973,4,28,F,3612 +1973,4,28,M,3778 +1973,4,29,F,3249 +1973,4,29,M,3508 +1973,4,30,F,4131 +1973,4,30,M,4383 +1973,4,31,M,2 +1973,4,99,F,16 +1973,4,99,M,12 +1973,5,1,F,4370 +1973,5,1,M,4476 +1973,5,2,F,3971 +1973,5,2,M,4530 +1973,5,3,F,4122 +1973,5,3,M,4199 +1973,5,4,F,3980 +1973,5,4,M,4316 +1973,5,5,F,3558 +1973,5,5,M,3988 +1973,5,6,F,3378 +1973,5,6,M,3514 +1973,5,7,F,4038 +1973,5,7,M,4236 +1973,5,8,F,4423 +1973,5,8,M,4557 +1973,5,9,F,4338 +1973,5,9,M,4288 +1973,5,10,F,4012 +1973,5,10,M,4426 +1973,5,11,F,4202 +1973,5,11,M,4417 +1973,5,12,F,3790 +1973,5,12,M,3649 +1973,5,13,F,3550 +1973,5,13,M,3439 +1973,5,14,F,3985 +1973,5,14,M,4251 +1973,5,15,F,4311 +1973,5,15,M,4465 +1973,5,16,F,4127 +1973,5,16,M,4418 +1973,5,17,F,4136 +1973,5,17,M,4413 +1973,5,18,F,4100 +1973,5,18,M,4484 +1973,5,19,F,3674 +1973,5,19,M,4025 +1973,5,20,F,3548 +1973,5,20,M,3617 +1973,5,21,F,4222 +1973,5,21,M,4439 +1973,5,22,F,4230 +1973,5,22,M,4537 +1973,5,23,F,4182 +1973,5,23,M,4324 +1973,5,24,F,4043 +1973,5,24,M,4386 +1973,5,25,F,4380 +1973,5,25,M,4717 +1973,5,26,F,3624 +1973,5,26,M,4084 +1973,5,27,F,3540 +1973,5,27,M,3639 +1973,5,28,F,3517 +1973,5,28,M,3786 +1973,5,29,F,4293 +1973,5,29,M,4616 +1973,5,30,F,4176 +1973,5,30,M,4609 +1973,5,31,F,4231 +1973,5,31,M,4357 +1973,5,99,F,10 +1973,5,99,M,4 +1973,6,1,F,4353 +1973,6,1,M,4502 +1973,6,2,F,3777 +1973,6,2,M,4069 +1973,6,3,F,3482 +1973,6,3,M,3727 +1973,6,4,F,4220 +1973,6,4,M,4285 +1973,6,5,F,4280 +1973,6,5,M,4618 +1973,6,6,F,4160 +1973,6,6,M,4546 +1973,6,7,F,4244 +1973,6,7,M,4542 +1973,6,8,F,4131 +1973,6,8,M,4670 +1973,6,9,F,3847 +1973,6,9,M,4083 +1973,6,10,F,3561 +1973,6,10,M,3769 +1973,6,11,F,4203 +1973,6,11,M,4693 +1973,6,12,F,4423 +1973,6,12,M,4603 +1973,6,13,F,4115 +1973,6,13,M,4615 +1973,6,14,F,4197 +1973,6,14,M,4428 +1973,6,15,F,4300 +1973,6,15,M,4522 +1973,6,16,F,3908 +1973,6,16,M,4117 +1973,6,17,F,3617 +1973,6,17,M,3788 +1973,6,18,F,4358 +1973,6,18,M,4601 +1973,6,19,F,4357 +1973,6,19,M,4689 +1973,6,20,F,4257 +1973,6,20,M,4445 +1973,6,21,F,4344 +1973,6,21,M,4438 +1973,6,22,F,4337 +1973,6,22,M,4591 +1973,6,23,F,3812 +1973,6,23,M,3970 +1973,6,24,F,3543 +1973,6,24,M,3738 +1973,6,25,F,4228 +1973,6,25,M,4562 +1973,6,26,F,4417 +1973,6,26,M,4664 +1973,6,27,F,4404 +1973,6,27,M,4608 +1973,6,28,F,4314 +1973,6,28,M,4754 +1973,6,29,F,4396 +1973,6,29,M,4612 +1973,6,30,F,3912 +1973,6,30,M,4019 +1973,6,31,F,2 +1973,6,31,M,6 +1973,6,99,F,14 +1973,6,99,M,12 +1973,7,1,F,3611 +1973,7,1,M,3950 +1973,7,2,F,4445 +1973,7,2,M,4558 +1973,7,3,F,4686 +1973,7,3,M,5143 +1973,7,4,F,3861 +1973,7,4,M,4049 +1973,7,5,F,4533 +1973,7,5,M,4635 +1973,7,6,F,4576 +1973,7,6,M,4874 +1973,7,7,F,3956 +1973,7,7,M,4396 +1973,7,8,F,3879 +1973,7,8,M,3908 +1973,7,9,F,4623 +1973,7,9,M,4703 +1973,7,10,F,4655 +1973,7,10,M,4972 +1973,7,11,F,4482 +1973,7,11,M,4715 +1973,7,12,F,4284 +1973,7,12,M,4655 +1973,7,13,F,4345 +1973,7,13,M,4683 +1973,7,14,F,4115 +1973,7,14,M,4330 +1973,7,15,F,3640 +1973,7,15,M,3911 +1973,7,16,F,4483 +1973,7,16,M,4607 +1973,7,17,F,4559 +1973,7,17,M,5080 +1973,7,18,F,4335 +1973,7,18,M,4937 +1973,7,19,F,4526 +1973,7,19,M,4764 +1973,7,20,F,4557 +1973,7,20,M,4913 +1973,7,21,F,4009 +1973,7,21,M,4286 +1973,7,22,F,3917 +1973,7,22,M,3727 +1973,7,23,F,4574 +1973,7,23,M,4662 +1973,7,24,F,4484 +1973,7,24,M,4966 +1973,7,25,F,4665 +1973,7,25,M,4828 +1973,7,26,F,4613 +1973,7,26,M,4719 +1973,7,27,F,4618 +1973,7,27,M,4798 +1973,7,28,F,4117 +1973,7,28,M,4307 +1973,7,29,F,3764 +1973,7,29,M,3981 +1973,7,30,F,4502 +1973,7,30,M,4723 +1973,7,31,F,4613 +1973,7,31,M,5089 +1973,7,99,F,24 +1973,7,99,M,14 +1973,8,1,F,4588 +1973,8,1,M,4849 +1973,8,2,F,4654 +1973,8,2,M,4745 +1973,8,3,F,4613 +1973,8,3,M,4808 +1973,8,4,F,4021 +1973,8,4,M,4074 +1973,8,5,F,3649 +1973,8,5,M,3967 +1973,8,6,F,4598 +1973,8,6,M,4846 +1973,8,7,F,4817 +1973,8,7,M,4953 +1973,8,8,F,4672 +1973,8,8,M,4926 +1973,8,9,F,4521 +1973,8,9,M,4897 +1973,8,10,F,4815 +1973,8,10,M,5014 +1973,8,11,F,4034 +1973,8,11,M,4158 +1973,8,12,F,3939 +1973,8,12,M,3969 +1973,8,13,F,4474 +1973,8,13,M,4734 +1973,8,14,F,4687 +1973,8,14,M,5155 +1973,8,15,F,4608 +1973,8,15,M,4932 +1973,8,16,F,4458 +1973,8,16,M,4825 +1973,8,17,F,4639 +1973,8,17,M,4670 +1973,8,18,F,3988 +1973,8,18,M,4292 +1973,8,19,F,3885 +1973,8,19,M,3896 +1973,8,20,F,4739 +1973,8,20,M,4728 +1973,8,21,F,4750 +1973,8,21,M,4891 +1973,8,22,F,4525 +1973,8,22,M,4892 +1973,8,23,F,4400 +1973,8,23,M,4649 +1973,8,24,F,4580 +1973,8,24,M,4768 +1973,8,25,F,4099 +1973,8,25,M,4221 +1973,8,26,F,3927 +1973,8,26,M,4051 +1973,8,27,F,4627 +1973,8,27,M,4870 +1973,8,28,F,4769 +1973,8,28,M,4932 +1973,8,29,F,4539 +1973,8,29,M,4869 +1973,8,30,F,4635 +1973,8,30,M,4921 +1973,8,31,F,4466 +1973,8,31,M,4925 +1973,8,99,F,24 +1973,8,99,M,22 +1973,9,1,F,4106 +1973,9,1,M,4294 +1973,9,2,F,3790 +1973,9,2,M,3917 +1973,9,3,F,3815 +1973,9,3,M,3966 +1973,9,4,F,4711 +1973,9,4,M,4803 +1973,9,5,F,4869 +1973,9,5,M,5028 +1973,9,6,F,4475 +1973,9,6,M,4736 +1973,9,7,F,4479 +1973,9,7,M,4938 +1973,9,8,F,4176 +1973,9,8,M,4081 +1973,9,9,F,3922 +1973,9,9,M,4039 +1973,9,10,F,4449 +1973,9,10,M,4716 +1973,9,11,F,4739 +1973,9,11,M,4962 +1973,9,12,F,4540 +1973,9,12,M,4763 +1973,9,13,F,4445 +1973,9,13,M,4661 +1973,9,14,F,4683 +1973,9,14,M,4986 +1973,9,15,F,4172 +1973,9,15,M,4335 +1973,9,16,F,3904 +1973,9,16,M,4090 +1973,9,17,F,4725 +1973,9,17,M,4813 +1973,9,18,F,4733 +1973,9,18,M,4892 +1973,9,19,F,4612 +1973,9,19,M,4752 +1973,9,20,F,4852 +1973,9,20,M,4916 +1973,9,21,F,4737 +1973,9,21,M,4880 +1973,9,22,F,4286 +1973,9,22,M,4445 +1973,9,23,F,4079 +1973,9,23,M,4177 +1973,9,24,F,4847 +1973,9,24,M,4764 +1973,9,25,F,4878 +1973,9,25,M,5211 +1973,9,26,F,4696 +1973,9,26,M,5099 +1973,9,27,F,4715 +1973,9,27,M,4882 +1973,9,28,F,4680 +1973,9,28,M,4867 +1973,9,29,F,4144 +1973,9,29,M,4289 +1973,9,30,F,3940 +1973,9,30,M,4074 +1973,9,31,F,4 +1973,9,31,M,2 +1973,9,99,F,16 +1973,9,99,M,8 +1973,10,1,F,4456 +1973,10,1,M,4841 +1973,10,2,F,4794 +1973,10,2,M,4882 +1973,10,3,F,4387 +1973,10,3,M,4682 +1973,10,4,F,4471 +1973,10,4,M,4478 +1973,10,5,F,4400 +1973,10,5,M,4847 +1973,10,6,F,3997 +1973,10,6,M,4177 +1973,10,7,F,3686 +1973,10,7,M,3930 +1973,10,8,F,4488 +1973,10,8,M,4568 +1973,10,9,F,4553 +1973,10,9,M,4756 +1973,10,10,F,4428 +1973,10,10,M,4699 +1973,10,11,F,4342 +1973,10,11,M,4480 +1973,10,12,F,4431 +1973,10,12,M,4497 +1973,10,13,F,3776 +1973,10,13,M,4152 +1973,10,14,F,3585 +1973,10,14,M,3858 +1973,10,15,F,4235 +1973,10,15,M,4588 +1973,10,16,F,4377 +1973,10,16,M,4728 +1973,10,17,F,4224 +1973,10,17,M,4490 +1973,10,18,F,4154 +1973,10,18,M,4548 +1973,10,19,F,4249 +1973,10,19,M,4604 +1973,10,20,F,3739 +1973,10,20,M,3898 +1973,10,21,F,3457 +1973,10,21,M,3589 +1973,10,22,F,4176 +1973,10,22,M,4254 +1973,10,23,F,4498 +1973,10,23,M,4757 +1973,10,24,F,4382 +1973,10,24,M,4423 +1973,10,25,F,4173 +1973,10,25,M,4334 +1973,10,26,F,4326 +1973,10,26,M,4427 +1973,10,27,F,3754 +1973,10,27,M,3992 +1973,10,28,F,3593 +1973,10,28,M,3781 +1973,10,29,F,4077 +1973,10,29,M,4487 +1973,10,30,F,4219 +1973,10,30,M,4509 +1973,10,31,F,4161 +1973,10,31,M,4534 +1973,10,99,F,24 +1973,10,99,M,14 +1973,11,1,F,4297 +1973,11,1,M,4466 +1973,11,2,F,4195 +1973,11,2,M,4564 +1973,11,3,F,3736 +1973,11,3,M,4005 +1973,11,4,F,3535 +1973,11,4,M,3710 +1973,11,5,F,4143 +1973,11,5,M,4569 +1973,11,6,F,4197 +1973,11,6,M,4411 +1973,11,7,F,4186 +1973,11,7,M,4530 +1973,11,8,F,4249 +1973,11,8,M,4397 +1973,11,9,F,4365 +1973,11,9,M,4632 +1973,11,10,F,3778 +1973,11,10,M,4081 +1973,11,11,F,3580 +1973,11,11,M,3840 +1973,11,12,F,4314 +1973,11,12,M,4370 +1973,11,13,F,4364 +1973,11,13,M,4785 +1973,11,14,F,4393 +1973,11,14,M,4446 +1973,11,15,F,4259 +1973,11,15,M,4462 +1973,11,16,F,4453 +1973,11,16,M,4686 +1973,11,17,F,3856 +1973,11,17,M,4021 +1973,11,18,F,3679 +1973,11,18,M,3741 +1973,11,19,F,4353 +1973,11,19,M,4522 +1973,11,20,F,4430 +1973,11,20,M,4969 +1973,11,21,F,4140 +1973,11,21,M,4456 +1973,11,22,F,3452 +1973,11,22,M,3597 +1973,11,23,F,4055 +1973,11,23,M,4187 +1973,11,24,F,3740 +1973,11,24,M,4020 +1973,11,25,F,3621 +1973,11,25,M,3758 +1973,11,26,F,4426 +1973,11,26,M,4626 +1973,11,27,F,4497 +1973,11,27,M,4848 +1973,11,28,F,4178 +1973,11,28,M,4414 +1973,11,29,F,4124 +1973,11,29,M,4428 +1973,11,30,F,4186 +1973,11,30,M,4404 +1973,11,31,M,10 +1973,11,99,F,22 +1973,11,99,M,22 +1973,12,1,F,3704 +1973,12,1,M,3963 +1973,12,2,F,3570 +1973,12,2,M,3742 +1973,12,3,F,4264 +1973,12,3,M,4475 +1973,12,4,F,4395 +1973,12,4,M,4709 +1973,12,5,F,4254 +1973,12,5,M,4545 +1973,12,6,F,4245 +1973,12,6,M,4404 +1973,12,7,F,4189 +1973,12,7,M,4292 +1973,12,8,F,3720 +1973,12,8,M,3962 +1973,12,9,F,3749 +1973,12,9,M,3707 +1973,12,10,F,4305 +1973,12,10,M,4587 +1973,12,11,F,4359 +1973,12,11,M,4699 +1973,12,12,F,4310 +1973,12,12,M,4484 +1973,12,13,F,4248 +1973,12,13,M,4503 +1973,12,14,F,4552 +1973,12,14,M,4625 +1973,12,15,F,3809 +1973,12,15,M,4012 +1973,12,16,F,3754 +1973,12,16,M,3910 +1973,12,17,F,4610 +1973,12,17,M,4800 +1973,12,18,F,4536 +1973,12,18,M,5000 +1973,12,19,F,4658 +1973,12,19,M,4659 +1973,12,20,F,4428 +1973,12,20,M,4553 +1973,12,21,F,4316 +1973,12,21,M,4615 +1973,12,22,F,3758 +1973,12,22,M,3827 +1973,12,23,F,3393 +1973,12,23,M,3619 +1973,12,24,F,3595 +1973,12,24,M,3705 +1973,12,25,F,3493 +1973,12,25,M,3471 +1973,12,26,F,4023 +1973,12,26,M,4113 +1973,12,27,F,4648 +1973,12,27,M,4861 +1973,12,28,F,4768 +1973,12,28,M,5182 +1973,12,29,F,4166 +1973,12,29,M,4154 +1973,12,30,F,3546 +1973,12,30,M,3819 +1973,12,31,F,4399 +1973,12,31,M,4567 +1973,12,99,F,24 +1973,12,99,M,20 +1974,1,1,F,3311 +1974,1,1,M,3691 +1974,1,2,F,3798 +1974,1,2,M,4011 +1974,1,3,F,4013 +1974,1,3,M,4364 +1974,1,4,F,4283 +1974,1,4,M,4568 +1974,1,5,F,3809 +1974,1,5,M,4018 +1974,1,6,F,3478 +1974,1,6,M,3615 +1974,1,7,F,4195 +1974,1,7,M,4428 +1974,1,8,F,4484 +1974,1,8,M,4302 +1974,1,9,F,4068 +1974,1,9,M,4278 +1974,1,10,F,4343 +1974,1,10,M,4339 +1974,1,11,F,4410 +1974,1,11,M,4634 +1974,1,12,F,3750 +1974,1,12,M,4019 +1974,1,13,F,3592 +1974,1,13,M,3760 +1974,1,14,F,4266 +1974,1,14,M,4530 +1974,1,15,F,4223 +1974,1,15,M,4716 +1974,1,16,F,4146 +1974,1,16,M,4586 +1974,1,17,F,4276 +1974,1,17,M,4328 +1974,1,18,F,4489 +1974,1,18,M,4482 +1974,1,19,F,3828 +1974,1,19,M,3923 +1974,1,20,F,3592 +1974,1,20,M,3887 +1974,1,21,F,4424 +1974,1,21,M,4634 +1974,1,22,F,4254 +1974,1,22,M,4513 +1974,1,23,F,4184 +1974,1,23,M,4273 +1974,1,24,F,4193 +1974,1,24,M,4499 +1974,1,25,F,4323 +1974,1,25,M,4367 +1974,1,26,F,3800 +1974,1,26,M,3999 +1974,1,27,F,3657 +1974,1,27,M,3884 +1974,1,28,F,4063 +1974,1,28,M,4399 +1974,1,29,F,4349 +1974,1,29,M,4562 +1974,1,30,F,4072 +1974,1,30,M,4367 +1974,1,31,F,4214 +1974,1,31,M,4406 +1974,1,99,F,8 +1974,1,99,M,20 +1974,2,1,F,4295 +1974,2,1,M,4473 +1974,2,2,F,3830 +1974,2,2,M,4023 +1974,2,3,F,3559 +1974,2,3,M,3873 +1974,2,4,F,4321 +1974,2,4,M,4330 +1974,2,5,F,4337 +1974,2,5,M,4555 +1974,2,6,F,4342 +1974,2,6,M,4450 +1974,2,7,F,4237 +1974,2,7,M,4468 +1974,2,8,F,4332 +1974,2,8,M,4490 +1974,2,9,F,3691 +1974,2,9,M,4001 +1974,2,10,F,3450 +1974,2,10,M,3700 +1974,2,11,F,4345 +1974,2,11,M,4421 +1974,2,12,F,4382 +1974,2,12,M,4542 +1974,2,13,F,4302 +1974,2,13,M,4506 +1974,2,14,F,4215 +1974,2,14,M,4680 +1974,2,15,F,4421 +1974,2,15,M,4613 +1974,2,16,F,3906 +1974,2,16,M,4033 +1974,2,17,F,3637 +1974,2,17,M,3819 +1974,2,18,F,4147 +1974,2,18,M,4490 +1974,2,19,F,4318 +1974,2,19,M,4698 +1974,2,20,F,4417 +1974,2,20,M,4492 +1974,2,21,F,4262 +1974,2,21,M,4486 +1974,2,22,F,4426 +1974,2,22,M,4634 +1974,2,23,F,3986 +1974,2,23,M,4165 +1974,2,24,F,3730 +1974,2,24,M,3828 +1974,2,25,F,4083 +1974,2,25,M,4429 +1974,2,26,F,4365 +1974,2,26,M,4491 +1974,2,27,F,4354 +1974,2,27,M,4356 +1974,2,28,F,4161 +1974,2,28,M,4373 +1974,2,29,F,5 +1974,2,29,M,2 +1974,2,31,F,2 +1974,2,31,M,2 +1974,2,99,F,12 +1974,2,99,M,9 +1974,3,1,F,4327 +1974,3,1,M,4590 +1974,3,2,F,4014 +1974,3,2,M,4069 +1974,3,3,F,3752 +1974,3,3,M,3898 +1974,3,4,F,4372 +1974,3,4,M,4518 +1974,3,5,F,4381 +1974,3,5,M,4889 +1974,3,6,F,4248 +1974,3,6,M,4438 +1974,3,7,F,4241 +1974,3,7,M,4399 +1974,3,8,F,4344 +1974,3,8,M,4620 +1974,3,9,F,3795 +1974,3,9,M,4155 +1974,3,10,F,3636 +1974,3,10,M,3781 +1974,3,11,F,4219 +1974,3,11,M,4618 +1974,3,12,F,4357 +1974,3,12,M,4527 +1974,3,13,F,4107 +1974,3,13,M,4400 +1974,3,14,F,4172 +1974,3,14,M,4230 +1974,3,15,F,4297 +1974,3,15,M,4593 +1974,3,16,F,3843 +1974,3,16,M,3991 +1974,3,17,F,3518 +1974,3,17,M,3708 +1974,3,18,F,4120 +1974,3,18,M,4574 +1974,3,19,F,4335 +1974,3,19,M,4643 +1974,3,20,F,4034 +1974,3,20,M,4421 +1974,3,21,F,3920 +1974,3,21,M,4480 +1974,3,22,F,4194 +1974,3,22,M,4474 +1974,3,23,F,3820 +1974,3,23,M,3953 +1974,3,24,F,3484 +1974,3,24,M,3550 +1974,3,25,F,4172 +1974,3,25,M,4398 +1974,3,26,F,4283 +1974,3,26,M,4473 +1974,3,27,F,4061 +1974,3,27,M,4454 +1974,3,28,F,4139 +1974,3,28,M,4322 +1974,3,29,F,4245 +1974,3,29,M,4394 +1974,3,30,F,3624 +1974,3,30,M,3902 +1974,3,31,F,3538 +1974,3,31,M,3767 +1974,3,99,F,6 +1974,3,99,M,16 +1974,4,1,F,4233 +1974,4,1,M,4317 +1974,4,2,F,4489 +1974,4,2,M,4659 +1974,4,3,F,4210 +1974,4,3,M,4361 +1974,4,4,F,4194 +1974,4,4,M,4323 +1974,4,5,F,4215 +1974,4,5,M,4441 +1974,4,6,F,3537 +1974,4,6,M,3918 +1974,4,7,F,3393 +1974,4,7,M,3658 +1974,4,8,F,4260 +1974,4,8,M,4176 +1974,4,9,F,4310 +1974,4,9,M,4607 +1974,4,10,F,3879 +1974,4,10,M,4567 +1974,4,11,F,4104 +1974,4,11,M,4155 +1974,4,12,F,3945 +1974,4,12,M,4441 +1974,4,13,F,3764 +1974,4,13,M,3845 +1974,4,14,F,3487 +1974,4,14,M,3545 +1974,4,15,F,4060 +1974,4,15,M,4282 +1974,4,16,F,4228 +1974,4,16,M,4387 +1974,4,17,F,4262 +1974,4,17,M,4340 +1974,4,18,F,4147 +1974,4,18,M,4186 +1974,4,19,F,4141 +1974,4,19,M,4446 +1974,4,20,F,3519 +1974,4,20,M,3961 +1974,4,21,F,3508 +1974,4,21,M,3700 +1974,4,22,F,4168 +1974,4,22,M,4329 +1974,4,23,F,4376 +1974,4,23,M,4659 +1974,4,24,F,4258 +1974,4,24,M,4407 +1974,4,25,F,4210 +1974,4,25,M,4346 +1974,4,26,F,4204 +1974,4,26,M,4558 +1974,4,27,F,3628 +1974,4,27,M,3862 +1974,4,28,F,3430 +1974,4,28,M,3625 +1974,4,29,F,3969 +1974,4,29,M,4363 +1974,4,30,F,4199 +1974,4,30,M,4478 +1974,4,31,F,2 +1974,4,31,M,2 +1974,4,99,F,10 +1974,4,99,M,10 +1974,5,1,F,4037 +1974,5,1,M,4537 +1974,5,2,F,4012 +1974,5,2,M,4226 +1974,5,3,F,4068 +1974,5,3,M,4592 +1974,5,4,F,3562 +1974,5,4,M,3822 +1974,5,5,F,3367 +1974,5,5,M,3561 +1974,5,6,F,4059 +1974,5,6,M,4258 +1974,5,7,F,4309 +1974,5,7,M,4577 +1974,5,8,F,4052 +1974,5,8,M,4432 +1974,5,9,F,4084 +1974,5,9,M,4441 +1974,5,10,F,4164 +1974,5,10,M,4431 +1974,5,11,F,3790 +1974,5,11,M,3920 +1974,5,12,F,3433 +1974,5,12,M,3653 +1974,5,13,F,4264 +1974,5,13,M,4375 +1974,5,14,F,4270 +1974,5,14,M,4616 +1974,5,15,F,4364 +1974,5,15,M,4656 +1974,5,16,F,4307 +1974,5,16,M,4323 +1974,5,17,F,4345 +1974,5,17,M,4528 +1974,5,18,F,3752 +1974,5,18,M,3915 +1974,5,19,F,3373 +1974,5,19,M,3523 +1974,5,20,F,4183 +1974,5,20,M,4610 +1974,5,21,F,4322 +1974,5,21,M,4739 +1974,5,22,F,4304 +1974,5,22,M,4472 +1974,5,23,F,4134 +1974,5,23,M,4443 +1974,5,24,F,4369 +1974,5,24,M,4555 +1974,5,25,F,3719 +1974,5,25,M,3874 +1974,5,26,F,3477 +1974,5,26,M,3455 +1974,5,27,F,3608 +1974,5,27,M,3852 +1974,5,28,F,4193 +1974,5,28,M,4377 +1974,5,29,F,4445 +1974,5,29,M,4618 +1974,5,30,F,4273 +1974,5,30,M,4685 +1974,5,31,F,4460 +1974,5,31,M,4590 +1974,5,99,F,12 +1974,5,99,M,8 +1974,6,1,F,3535 +1974,6,1,M,3913 +1974,6,2,F,3424 +1974,6,2,M,3622 +1974,6,3,F,3944 +1974,6,3,M,4370 +1974,6,4,F,4340 +1974,6,4,M,4570 +1974,6,5,F,4247 +1974,6,5,M,4490 +1974,6,6,F,4228 +1974,6,6,M,4371 +1974,6,7,F,4469 +1974,6,7,M,4641 +1974,6,8,F,3692 +1974,6,8,M,3779 +1974,6,9,F,3435 +1974,6,9,M,3746 +1974,6,10,F,4368 +1974,6,10,M,4571 +1974,6,11,F,4332 +1974,6,11,M,4422 +1974,6,12,F,4116 +1974,6,12,M,4487 +1974,6,13,F,3993 +1974,6,13,M,4463 +1974,6,14,F,4317 +1974,6,14,M,4581 +1974,6,15,F,3708 +1974,6,15,M,3958 +1974,6,16,F,3455 +1974,6,16,M,3756 +1974,6,17,F,4228 +1974,6,17,M,4448 +1974,6,18,F,4299 +1974,6,18,M,4717 +1974,6,19,F,4299 +1974,6,19,M,4565 +1974,6,20,F,4251 +1974,6,20,M,4582 +1974,6,21,F,4501 +1974,6,21,M,4863 +1974,6,22,F,3764 +1974,6,22,M,4239 +1974,6,23,F,3446 +1974,6,23,M,3716 +1974,6,24,F,4098 +1974,6,24,M,4405 +1974,6,25,F,4354 +1974,6,25,M,4684 +1974,6,26,F,4331 +1974,6,26,M,4668 +1974,6,27,F,4257 +1974,6,27,M,4625 +1974,6,28,F,4395 +1974,6,28,M,4694 +1974,6,29,F,3923 +1974,6,29,M,4088 +1974,6,30,F,3632 +1974,6,30,M,3899 +1974,6,31,F,2 +1974,6,31,M,2 +1974,6,99,F,18 +1974,6,99,M,24 +1974,7,1,F,4524 +1974,7,1,M,4692 +1974,7,2,F,4866 +1974,7,2,M,5057 +1974,7,3,F,4607 +1974,7,3,M,5086 +1974,7,4,F,3859 +1974,7,4,M,4065 +1974,7,5,F,4383 +1974,7,5,M,4668 +1974,7,6,F,4012 +1974,7,6,M,4273 +1974,7,7,F,3634 +1974,7,7,M,3992 +1974,7,8,F,4515 +1974,7,8,M,4957 +1974,7,9,F,4830 +1974,7,9,M,5030 +1974,7,10,F,4665 +1974,7,10,M,4758 +1974,7,11,F,4472 +1974,7,11,M,4746 +1974,7,12,F,4453 +1974,7,12,M,4830 +1974,7,13,F,4009 +1974,7,13,M,4219 +1974,7,14,F,3712 +1974,7,14,M,3893 +1974,7,15,F,4511 +1974,7,15,M,4756 +1974,7,16,F,4737 +1974,7,16,M,4960 +1974,7,17,F,4686 +1974,7,17,M,4733 +1974,7,18,F,4416 +1974,7,18,M,4706 +1974,7,19,F,4698 +1974,7,19,M,4825 +1974,7,20,F,4113 +1974,7,20,M,4358 +1974,7,21,F,3693 +1974,7,21,M,3740 +1974,7,22,F,4543 +1974,7,22,M,4771 +1974,7,23,F,4909 +1974,7,23,M,5201 +1974,7,24,F,4533 +1974,7,24,M,4794 +1974,7,25,F,4425 +1974,7,25,M,4838 +1974,7,26,F,4763 +1974,7,26,M,4811 +1974,7,27,F,4139 +1974,7,27,M,4353 +1974,7,28,F,3879 +1974,7,28,M,3993 +1974,7,29,F,4645 +1974,7,29,M,4809 +1974,7,30,F,4768 +1974,7,30,M,5053 +1974,7,31,F,4717 +1974,7,31,M,4874 +1974,7,99,F,9 +1974,7,99,M,6 +1974,8,1,F,4787 +1974,8,1,M,4920 +1974,8,2,F,4764 +1974,8,2,M,4872 +1974,8,3,F,4159 +1974,8,3,M,4311 +1974,8,4,F,3906 +1974,8,4,M,3907 +1974,8,5,F,4477 +1974,8,5,M,4647 +1974,8,6,F,4774 +1974,8,6,M,5193 +1974,8,7,F,4713 +1974,8,7,M,4903 +1974,8,8,F,4578 +1974,8,8,M,4972 +1974,8,9,F,4732 +1974,8,9,M,4964 +1974,8,10,F,4053 +1974,8,10,M,4378 +1974,8,11,F,3798 +1974,8,11,M,3926 +1974,8,12,F,4718 +1974,8,12,M,4925 +1974,8,13,F,5001 +1974,8,13,M,5212 +1974,8,14,F,4781 +1974,8,14,M,4805 +1974,8,15,F,4769 +1974,8,15,M,5056 +1974,8,16,F,4656 +1974,8,16,M,5179 +1974,8,17,F,4163 +1974,8,17,M,4393 +1974,8,18,F,3965 +1974,8,18,M,4108 +1974,8,19,F,4630 +1974,8,19,M,4952 +1974,8,20,F,4926 +1974,8,20,M,5227 +1974,8,21,F,4654 +1974,8,21,M,5057 +1974,8,22,F,4604 +1974,8,22,M,4731 +1974,8,23,F,4844 +1974,8,23,M,4951 +1974,8,24,F,4139 +1974,8,24,M,4347 +1974,8,25,F,3793 +1974,8,25,M,4061 +1974,8,26,F,4607 +1974,8,26,M,4949 +1974,8,27,F,4856 +1974,8,27,M,5219 +1974,8,28,F,4689 +1974,8,28,M,4910 +1974,8,29,F,4597 +1974,8,29,M,4947 +1974,8,30,F,4726 +1974,8,30,M,5054 +1974,8,31,F,4190 +1974,8,31,M,4347 +1974,8,99,F,4 +1974,8,99,M,10 +1974,9,1,F,3912 +1974,9,1,M,3997 +1974,9,2,F,3865 +1974,9,2,M,4104 +1974,9,3,F,4805 +1974,9,3,M,4666 +1974,9,4,F,4955 +1974,9,4,M,5025 +1974,9,5,F,4700 +1974,9,5,M,4881 +1974,9,6,F,4654 +1974,9,6,M,5073 +1974,9,7,F,4140 +1974,9,7,M,4410 +1974,9,8,F,3865 +1974,9,8,M,4024 +1974,9,9,F,4710 +1974,9,9,M,5086 +1974,9,10,F,4897 +1974,9,10,M,5177 +1974,9,11,F,4720 +1974,9,11,M,5077 +1974,9,12,F,4829 +1974,9,12,M,5003 +1974,9,13,F,4794 +1974,9,13,M,5074 +1974,9,14,F,4268 +1974,9,14,M,4601 +1974,9,15,F,4085 +1974,9,15,M,4181 +1974,9,16,F,4818 +1974,9,16,M,5232 +1974,9,17,F,5040 +1974,9,17,M,5235 +1974,9,18,F,4906 +1974,9,18,M,5127 +1974,9,19,F,4906 +1974,9,19,M,5076 +1974,9,20,F,4981 +1974,9,20,M,5343 +1974,9,21,F,4339 +1974,9,21,M,4665 +1974,9,22,F,4236 +1974,9,22,M,4386 +1974,9,23,F,4912 +1974,9,23,M,5225 +1974,9,24,F,5156 +1974,9,24,M,5219 +1974,9,25,F,4839 +1974,9,25,M,5204 +1974,9,26,F,4907 +1974,9,26,M,5222 +1974,9,27,F,5093 +1974,9,27,M,5195 +1974,9,28,F,4512 +1974,9,28,M,4731 +1974,9,29,F,4229 +1974,9,29,M,4356 +1974,9,30,F,4754 +1974,9,30,M,5221 +1974,9,31,F,3 +1974,9,31,M,7 +1974,9,99,F,12 +1974,9,99,M,14 +1974,10,1,F,4844 +1974,10,1,M,5420 +1974,10,2,F,4803 +1974,10,2,M,5092 +1974,10,3,F,4726 +1974,10,3,M,5041 +1974,10,4,F,4809 +1974,10,4,M,5033 +1974,10,5,F,4206 +1974,10,5,M,4487 +1974,10,6,F,3914 +1974,10,6,M,4178 +1974,10,7,F,4807 +1974,10,7,M,4949 +1974,10,8,F,4770 +1974,10,8,M,5038 +1974,10,9,F,4672 +1974,10,9,M,4833 +1974,10,10,F,4697 +1974,10,10,M,4998 +1974,10,11,F,4660 +1974,10,11,M,5057 +1974,10,12,F,4102 +1974,10,12,M,4233 +1974,10,13,F,3747 +1974,10,13,M,4020 +1974,10,14,F,4512 +1974,10,14,M,4693 +1974,10,15,F,4620 +1974,10,15,M,4952 +1974,10,16,F,4426 +1974,10,16,M,4785 +1974,10,17,F,4507 +1974,10,17,M,4804 +1974,10,18,F,4606 +1974,10,18,M,4847 +1974,10,19,F,3956 +1974,10,19,M,4230 +1974,10,20,F,3684 +1974,10,20,M,3788 +1974,10,21,F,4386 +1974,10,21,M,4627 +1974,10,22,F,4559 +1974,10,22,M,4768 +1974,10,23,F,4368 +1974,10,23,M,4597 +1974,10,24,F,4285 +1974,10,24,M,4507 +1974,10,25,F,4425 +1974,10,25,M,4774 +1974,10,26,F,4008 +1974,10,26,M,4266 +1974,10,27,F,3855 +1974,10,27,M,4025 +1974,10,28,F,4225 +1974,10,28,M,4649 +1974,10,29,F,4444 +1974,10,29,M,4911 +1974,10,30,F,4496 +1974,10,30,M,4631 +1974,10,31,F,4326 +1974,10,31,M,4587 +1974,10,99,F,10 +1974,10,99,M,22 +1974,11,1,F,4415 +1974,11,1,M,4783 +1974,11,2,F,4037 +1974,11,2,M,4018 +1974,11,3,F,3641 +1974,11,3,M,3926 +1974,11,4,F,4349 +1974,11,4,M,4625 +1974,11,5,F,4633 +1974,11,5,M,4867 +1974,11,6,F,4347 +1974,11,6,M,4704 +1974,11,7,F,4348 +1974,11,7,M,4471 +1974,11,8,F,4373 +1974,11,8,M,4602 +1974,11,9,F,3795 +1974,11,9,M,4011 +1974,11,10,F,3702 +1974,11,10,M,3817 +1974,11,11,F,4515 +1974,11,11,M,4708 +1974,11,12,F,4488 +1974,11,12,M,4786 +1974,11,13,F,4500 +1974,11,13,M,4413 +1974,11,14,F,4545 +1974,11,14,M,4516 +1974,11,15,F,4506 +1974,11,15,M,4735 +1974,11,16,F,3803 +1974,11,16,M,4001 +1974,11,17,F,3618 +1974,11,17,M,3853 +1974,11,18,F,4436 +1974,11,18,M,4781 +1974,11,19,F,4489 +1974,11,19,M,4938 +1974,11,20,F,4528 +1974,11,20,M,4706 +1974,11,21,F,4478 +1974,11,21,M,4557 +1974,11,22,F,4420 +1974,11,22,M,4656 +1974,11,23,F,4022 +1974,11,23,M,4081 +1974,11,24,F,3640 +1974,11,24,M,3905 +1974,11,25,F,4261 +1974,11,25,M,4577 +1974,11,26,F,4557 +1974,11,26,M,4931 +1974,11,27,F,4360 +1974,11,27,M,4687 +1974,11,28,F,3546 +1974,11,28,M,3748 +1974,11,29,F,4101 +1974,11,29,M,4323 +1974,11,30,F,3751 +1974,11,30,M,3976 +1974,11,31,F,4 +1974,11,31,M,2 +1974,11,99,F,10 +1974,11,99,M,10 +1974,12,1,F,3693 +1974,12,1,M,3876 +1974,12,2,F,4470 +1974,12,2,M,4789 +1974,12,3,F,4641 +1974,12,3,M,4898 +1974,12,4,F,4457 +1974,12,4,M,4615 +1974,12,5,F,4236 +1974,12,5,M,4699 +1974,12,6,F,4473 +1974,12,6,M,4570 +1974,12,7,F,3941 +1974,12,7,M,4028 +1974,12,8,F,3805 +1974,12,8,M,3863 +1974,12,9,F,4323 +1974,12,9,M,4647 +1974,12,10,F,4512 +1974,12,10,M,4700 +1974,12,11,F,4451 +1974,12,11,M,4592 +1974,12,12,F,4297 +1974,12,12,M,4748 +1974,12,13,F,4333 +1974,12,13,M,4418 +1974,12,14,F,3866 +1974,12,14,M,3979 +1974,12,15,F,3643 +1974,12,15,M,3862 +1974,12,16,F,4519 +1974,12,16,M,4669 +1974,12,17,F,4733 +1974,12,17,M,4907 +1974,12,18,F,4664 +1974,12,18,M,4739 +1974,12,19,F,4495 +1974,12,19,M,4849 +1974,12,20,F,4632 +1974,12,20,M,4821 +1974,12,21,F,3884 +1974,12,21,M,3956 +1974,12,22,F,3558 +1974,12,22,M,3519 +1974,12,23,F,3978 +1974,12,23,M,4108 +1974,12,24,F,3604 +1974,12,24,M,3934 +1974,12,25,F,3381 +1974,12,25,M,3439 +1974,12,26,F,4078 +1974,12,26,M,4385 +1974,12,27,F,4755 +1974,12,27,M,4848 +1974,12,28,F,4003 +1974,12,28,M,4221 +1974,12,29,F,3668 +1974,12,29,M,3715 +1974,12,30,F,4580 +1974,12,30,M,5061 +1974,12,31,F,4817 +1974,12,31,M,4820 +1974,12,99,F,8 +1974,12,99,M,22 +1975,1,1,F,3469 +1975,1,1,M,3559 +1975,1,2,F,3928 +1975,1,2,M,3945 +1975,1,3,F,4107 +1975,1,3,M,4318 +1975,1,4,F,3722 +1975,1,4,M,3948 +1975,1,5,F,3529 +1975,1,5,M,3721 +1975,1,6,F,4040 +1975,1,6,M,4386 +1975,1,7,F,4166 +1975,1,7,M,4626 +1975,1,8,F,4060 +1975,1,8,M,4356 +1975,1,9,F,4122 +1975,1,9,M,4339 +1975,1,10,F,4271 +1975,1,10,M,4581 +1975,1,11,F,3782 +1975,1,11,M,4146 +1975,1,12,F,3571 +1975,1,12,M,3669 +1975,1,13,F,4140 +1975,1,13,M,4479 +1975,1,14,F,4446 +1975,1,14,M,4742 +1975,1,15,F,4297 +1975,1,15,M,4596 +1975,1,16,F,4229 +1975,1,16,M,4407 +1975,1,17,F,4417 +1975,1,17,M,4595 +1975,1,18,F,3716 +1975,1,18,M,4160 +1975,1,19,F,3701 +1975,1,19,M,3747 +1975,1,20,F,4384 +1975,1,20,M,4518 +1975,1,21,F,4470 +1975,1,21,M,4632 +1975,1,22,F,4101 +1975,1,22,M,4457 +1975,1,23,F,4233 +1975,1,23,M,4514 +1975,1,24,F,4291 +1975,1,24,M,4480 +1975,1,25,F,3923 +1975,1,25,M,4154 +1975,1,26,F,3695 +1975,1,26,M,4013 +1975,1,27,F,4332 +1975,1,27,M,4451 +1975,1,28,F,4534 +1975,1,28,M,4615 +1975,1,29,F,4436 +1975,1,29,M,4464 +1975,1,30,F,4161 +1975,1,30,M,4456 +1975,1,31,F,4163 +1975,1,31,M,4514 +1975,1,99,F,4 +1975,1,99,M,4 +1975,2,1,F,3735 +1975,2,1,M,3964 +1975,2,2,F,3542 +1975,2,2,M,3774 +1975,2,3,F,4337 +1975,2,3,M,4544 +1975,2,4,F,4552 +1975,2,4,M,4541 +1975,2,5,F,4358 +1975,2,5,M,4471 +1975,2,6,F,4017 +1975,2,6,M,4408 +1975,2,7,F,4371 +1975,2,7,M,4598 +1975,2,8,F,3816 +1975,2,8,M,4036 +1975,2,9,F,3652 +1975,2,9,M,3912 +1975,2,10,F,4293 +1975,2,10,M,4440 +1975,2,11,F,4473 +1975,2,11,M,4653 +1975,2,12,F,4364 +1975,2,12,M,4584 +1975,2,13,F,4207 +1975,2,13,M,4555 +1975,2,14,F,4583 +1975,2,14,M,4636 +1975,2,15,F,3824 +1975,2,15,M,4173 +1975,2,16,F,3748 +1975,2,16,M,3797 +1975,2,17,F,4119 +1975,2,17,M,4254 +1975,2,18,F,4399 +1975,2,18,M,4833 +1975,2,19,F,4217 +1975,2,19,M,4579 +1975,2,20,F,4374 +1975,2,20,M,4610 +1975,2,21,F,4472 +1975,2,21,M,4447 +1975,2,22,F,3860 +1975,2,22,M,4057 +1975,2,23,F,3495 +1975,2,23,M,3782 +1975,2,24,F,4288 +1975,2,24,M,4566 +1975,2,25,F,4433 +1975,2,25,M,4717 +1975,2,26,F,4295 +1975,2,26,M,4525 +1975,2,27,F,4071 +1975,2,27,M,4470 +1975,2,28,F,4418 +1975,2,28,M,4556 +1975,2,29,F,3 +1975,2,29,M,4 +1975,2,31,F,1 +1975,2,99,F,6 +1975,2,99,M,9 +1975,3,1,F,3882 +1975,3,1,M,4039 +1975,3,2,F,3642 +1975,3,2,M,3745 +1975,3,3,F,4351 +1975,3,3,M,4612 +1975,3,4,F,4421 +1975,3,4,M,4616 +1975,3,5,F,4286 +1975,3,5,M,4568 +1975,3,6,F,4241 +1975,3,6,M,4599 +1975,3,7,F,4491 +1975,3,7,M,4564 +1975,3,8,F,3870 +1975,3,8,M,3934 +1975,3,9,F,3510 +1975,3,9,M,3636 +1975,3,10,F,4208 +1975,3,10,M,4360 +1975,3,11,F,4504 +1975,3,11,M,4735 +1975,3,12,F,4290 +1975,3,12,M,4643 +1975,3,13,F,4283 +1975,3,13,M,4327 +1975,3,14,F,4522 +1975,3,14,M,4576 +1975,3,15,F,3675 +1975,3,15,M,3916 +1975,3,16,F,3520 +1975,3,16,M,3715 +1975,3,17,F,4326 +1975,3,17,M,4674 +1975,3,18,F,4351 +1975,3,18,M,4739 +1975,3,19,F,4254 +1975,3,19,M,4613 +1975,3,20,F,4306 +1975,3,20,M,4620 +1975,3,21,F,4363 +1975,3,21,M,4709 +1975,3,22,F,3936 +1975,3,22,M,4102 +1975,3,23,F,3578 +1975,3,23,M,3944 +1975,3,24,F,4303 +1975,3,24,M,4481 +1975,3,25,F,4458 +1975,3,25,M,4748 +1975,3,26,F,4317 +1975,3,26,M,4595 +1975,3,27,F,4185 +1975,3,27,M,4368 +1975,3,28,F,4247 +1975,3,28,M,4552 +1975,3,29,F,3772 +1975,3,29,M,3858 +1975,3,30,F,3408 +1975,3,30,M,3763 +1975,3,31,F,4145 +1975,3,31,M,4377 +1975,3,99,F,4 +1975,3,99,M,8 +1975,4,1,F,4263 +1975,4,1,M,4608 +1975,4,2,F,4197 +1975,4,2,M,4573 +1975,4,3,F,4329 +1975,4,3,M,4572 +1975,4,4,F,4155 +1975,4,4,M,4461 +1975,4,5,F,3647 +1975,4,5,M,3934 +1975,4,6,F,3467 +1975,4,6,M,3695 +1975,4,7,F,4043 +1975,4,7,M,4458 +1975,4,8,F,4410 +1975,4,8,M,4710 +1975,4,9,F,4299 +1975,4,9,M,4524 +1975,4,10,F,4135 +1975,4,10,M,4442 +1975,4,11,F,4351 +1975,4,11,M,4580 +1975,4,12,F,3592 +1975,4,12,M,3958 +1975,4,13,F,3449 +1975,4,13,M,3553 +1975,4,14,F,4131 +1975,4,14,M,4319 +1975,4,15,F,4426 +1975,4,15,M,4688 +1975,4,16,F,4142 +1975,4,16,M,4534 +1975,4,17,F,4117 +1975,4,17,M,4419 +1975,4,18,F,4489 +1975,4,18,M,4642 +1975,4,19,F,3741 +1975,4,19,M,3873 +1975,4,20,F,3439 +1975,4,20,M,3590 +1975,4,21,F,4027 +1975,4,21,M,4421 +1975,4,22,F,4352 +1975,4,22,M,4592 +1975,4,23,F,4186 +1975,4,23,M,4325 +1975,4,24,F,4156 +1975,4,24,M,4370 +1975,4,25,F,4258 +1975,4,25,M,4597 +1975,4,26,F,3732 +1975,4,26,M,3905 +1975,4,27,F,3583 +1975,4,27,M,3579 +1975,4,28,F,4278 +1975,4,28,M,4471 +1975,4,29,F,4489 +1975,4,29,M,4762 +1975,4,30,F,4295 +1975,4,30,M,4366 +1975,4,31,F,2 +1975,4,31,M,2 +1975,4,99,F,10 +1975,4,99,M,6 +1975,5,1,F,4145 +1975,5,1,M,4509 +1975,5,2,F,4242 +1975,5,2,M,4429 +1975,5,3,F,3623 +1975,5,3,M,3956 +1975,5,4,F,3558 +1975,5,4,M,3638 +1975,5,5,F,4258 +1975,5,5,M,4259 +1975,5,6,F,4366 +1975,5,6,M,4595 +1975,5,7,F,4171 +1975,5,7,M,4296 +1975,5,8,F,4178 +1975,5,8,M,4373 +1975,5,9,F,4244 +1975,5,9,M,4617 +1975,5,10,F,3616 +1975,5,10,M,3852 +1975,5,11,F,3471 +1975,5,11,M,3757 +1975,5,12,F,4489 +1975,5,12,M,4549 +1975,5,13,F,4538 +1975,5,13,M,4709 +1975,5,14,F,4340 +1975,5,14,M,4565 +1975,5,15,F,4250 +1975,5,15,M,4584 +1975,5,16,F,4319 +1975,5,16,M,4599 +1975,5,17,F,3886 +1975,5,17,M,4123 +1975,5,18,F,3466 +1975,5,18,M,3803 +1975,5,19,F,4356 +1975,5,19,M,4472 +1975,5,20,F,4655 +1975,5,20,M,4899 +1975,5,21,F,4332 +1975,5,21,M,4711 +1975,5,22,F,4396 +1975,5,22,M,4724 +1975,5,23,F,4433 +1975,5,23,M,4700 +1975,5,24,F,3791 +1975,5,24,M,4015 +1975,5,25,F,3641 +1975,5,25,M,3871 +1975,5,26,F,3631 +1975,5,26,M,3901 +1975,5,27,F,4375 +1975,5,27,M,4417 +1975,5,28,F,4256 +1975,5,28,M,4656 +1975,5,29,F,4396 +1975,5,29,M,4586 +1975,5,30,F,4395 +1975,5,30,M,4605 +1975,5,31,F,3695 +1975,5,31,M,3924 +1975,5,99,F,4 +1975,5,99,M,8 +1975,6,1,F,3470 +1975,6,1,M,3774 +1975,6,2,F,4129 +1975,6,2,M,4426 +1975,6,3,F,4388 +1975,6,3,M,4608 +1975,6,4,F,4259 +1975,6,4,M,4483 +1975,6,5,F,4320 +1975,6,5,M,4463 +1975,6,6,F,4447 +1975,6,6,M,4665 +1975,6,7,F,3701 +1975,6,7,M,3941 +1975,6,8,F,3595 +1975,6,8,M,3554 +1975,6,9,F,4211 +1975,6,9,M,4412 +1975,6,10,F,4412 +1975,6,10,M,4585 +1975,6,11,F,4332 +1975,6,11,M,4512 +1975,6,12,F,4384 +1975,6,12,M,4570 +1975,6,13,F,4251 +1975,6,13,M,4512 +1975,6,14,F,3970 +1975,6,14,M,4021 +1975,6,15,F,3529 +1975,6,15,M,3742 +1975,6,16,F,4207 +1975,6,16,M,4474 +1975,6,17,F,4417 +1975,6,17,M,4795 +1975,6,18,F,4396 +1975,6,18,M,4654 +1975,6,19,F,4354 +1975,6,19,M,4761 +1975,6,20,F,4372 +1975,6,20,M,4667 +1975,6,21,F,3841 +1975,6,21,M,4118 +1975,6,22,F,3667 +1975,6,22,M,3746 +1975,6,23,F,4301 +1975,6,23,M,4858 +1975,6,24,F,4551 +1975,6,24,M,4849 +1975,6,25,F,4492 +1975,6,25,M,4622 +1975,6,26,F,4337 +1975,6,26,M,4687 +1975,6,27,F,4339 +1975,6,27,M,4616 +1975,6,28,F,4002 +1975,6,28,M,4127 +1975,6,29,F,3451 +1975,6,29,M,3962 +1975,6,30,F,4194 +1975,6,30,M,4857 +1975,6,31,F,2 +1975,6,31,M,2 +1975,6,99,F,26 +1975,6,99,M,8 +1975,7,1,F,4629 +1975,7,1,M,4883 +1975,7,2,F,4548 +1975,7,2,M,4792 +1975,7,3,F,4513 +1975,7,3,M,4880 +1975,7,4,F,3757 +1975,7,4,M,4160 +1975,7,5,F,3830 +1975,7,5,M,4158 +1975,7,6,F,3664 +1975,7,6,M,4050 +1975,7,7,F,4631 +1975,7,7,M,4843 +1975,7,8,F,4874 +1975,7,8,M,5204 +1975,7,9,F,4484 +1975,7,9,M,4933 +1975,7,10,F,4527 +1975,7,10,M,4697 +1975,7,11,F,4563 +1975,7,11,M,4888 +1975,7,12,F,3861 +1975,7,12,M,3992 +1975,7,13,F,3666 +1975,7,13,M,3733 +1975,7,14,F,4483 +1975,7,14,M,4812 +1975,7,15,F,4686 +1975,7,15,M,4938 +1975,7,16,F,4632 +1975,7,16,M,4738 +1975,7,17,F,4587 +1975,7,17,M,4899 +1975,7,18,F,4641 +1975,7,18,M,4827 +1975,7,19,F,4140 +1975,7,19,M,4230 +1975,7,20,F,3825 +1975,7,20,M,3990 +1975,7,21,F,4796 +1975,7,21,M,4723 +1975,7,22,F,4908 +1975,7,22,M,5057 +1975,7,23,F,4653 +1975,7,23,M,5098 +1975,7,24,F,4743 +1975,7,24,M,4755 +1975,7,25,F,4651 +1975,7,25,M,4844 +1975,7,26,F,3845 +1975,7,26,M,4287 +1975,7,27,F,3904 +1975,7,27,M,4048 +1975,7,28,F,4535 +1975,7,28,M,4838 +1975,7,29,F,4809 +1975,7,29,M,5063 +1975,7,30,F,4495 +1975,7,30,M,4894 +1975,7,31,F,4569 +1975,7,31,M,4891 +1975,7,99,F,8 +1975,7,99,M,8 +1975,8,1,F,4796 +1975,8,1,M,4912 +1975,8,2,F,4111 +1975,8,2,M,4467 +1975,8,3,F,3921 +1975,8,3,M,4154 +1975,8,4,F,4658 +1975,8,4,M,4932 +1975,8,5,F,4851 +1975,8,5,M,4766 +1975,8,6,F,4646 +1975,8,6,M,5007 +1975,8,7,F,4426 +1975,8,7,M,4840 +1975,8,8,F,4598 +1975,8,8,M,4655 +1975,8,9,F,4000 +1975,8,9,M,4016 +1975,8,10,F,3780 +1975,8,10,M,3906 +1975,8,11,F,4548 +1975,8,11,M,4847 +1975,8,12,F,4824 +1975,8,12,M,5000 +1975,8,13,F,4609 +1975,8,13,M,4884 +1975,8,14,F,4673 +1975,8,14,M,4895 +1975,8,15,F,4582 +1975,8,15,M,5061 +1975,8,16,F,4163 +1975,8,16,M,4342 +1975,8,17,F,3946 +1975,8,17,M,4099 +1975,8,18,F,4430 +1975,8,18,M,4685 +1975,8,19,F,4642 +1975,8,19,M,4882 +1975,8,20,F,4672 +1975,8,20,M,4808 +1975,8,21,F,4531 +1975,8,21,M,4747 +1975,8,22,F,4660 +1975,8,22,M,4783 +1975,8,23,F,4059 +1975,8,23,M,4306 +1975,8,24,F,3934 +1975,8,24,M,4048 +1975,8,25,F,4477 +1975,8,25,M,4937 +1975,8,26,F,4922 +1975,8,26,M,4985 +1975,8,27,F,4679 +1975,8,27,M,4778 +1975,8,28,F,4604 +1975,8,28,M,4767 +1975,8,29,F,4674 +1975,8,29,M,4913 +1975,8,30,F,4003 +1975,8,30,M,4278 +1975,8,31,F,3786 +1975,8,31,M,3874 +1975,8,99,F,6 +1975,8,99,M,9 +1975,9,1,F,3780 +1975,9,1,M,4024 +1975,9,2,F,4668 +1975,9,2,M,4757 +1975,9,3,F,4543 +1975,9,3,M,4986 +1975,9,4,F,4525 +1975,9,4,M,4788 +1975,9,5,F,4673 +1975,9,5,M,4852 +1975,9,6,F,4142 +1975,9,6,M,4324 +1975,9,7,F,3832 +1975,9,7,M,4150 +1975,9,8,F,4428 +1975,9,8,M,4825 +1975,9,9,F,4719 +1975,9,9,M,4943 +1975,9,10,F,4551 +1975,9,10,M,4714 +1975,9,11,F,4559 +1975,9,11,M,4783 +1975,9,12,F,4825 +1975,9,12,M,5188 +1975,9,13,F,4072 +1975,9,13,M,4238 +1975,9,14,F,3808 +1975,9,14,M,3937 +1975,9,15,F,4600 +1975,9,15,M,4782 +1975,9,16,F,4737 +1975,9,16,M,5076 +1975,9,17,F,4632 +1975,9,17,M,4959 +1975,9,18,F,4711 +1975,9,18,M,5032 +1975,9,19,F,4817 +1975,9,19,M,5093 +1975,9,20,F,4189 +1975,9,20,M,4352 +1975,9,21,F,4014 +1975,9,21,M,4203 +1975,9,22,F,4677 +1975,9,22,M,4893 +1975,9,23,F,4736 +1975,9,23,M,5197 +1975,9,24,F,4646 +1975,9,24,M,4933 +1975,9,25,F,4518 +1975,9,25,M,4912 +1975,9,26,F,4666 +1975,9,26,M,4913 +1975,9,27,F,4234 +1975,9,27,M,4398 +1975,9,28,F,3999 +1975,9,28,M,3972 +1975,9,29,F,4682 +1975,9,29,M,4714 +1975,9,30,F,4628 +1975,9,30,M,5037 +1975,9,31,F,6 +1975,9,31,M,2 +1975,9,99,F,4 +1975,9,99,M,5 +1975,10,1,F,4777 +1975,10,1,M,4955 +1975,10,2,F,4618 +1975,10,2,M,4817 +1975,10,3,F,4472 +1975,10,3,M,4814 +1975,10,4,F,4108 +1975,10,4,M,4202 +1975,10,5,F,3893 +1975,10,5,M,4072 +1975,10,6,F,4567 +1975,10,6,M,4614 +1975,10,7,F,4664 +1975,10,7,M,4800 +1975,10,8,F,4475 +1975,10,8,M,4777 +1975,10,9,F,4569 +1975,10,9,M,4660 +1975,10,10,F,4504 +1975,10,10,M,4708 +1975,10,11,F,3925 +1975,10,11,M,4060 +1975,10,12,F,3692 +1975,10,12,M,3909 +1975,10,13,F,4430 +1975,10,13,M,4467 +1975,10,14,F,4498 +1975,10,14,M,4711 +1975,10,15,F,4489 +1975,10,15,M,4745 +1975,10,16,F,4363 +1975,10,16,M,4362 +1975,10,17,F,4342 +1975,10,17,M,4641 +1975,10,18,F,3887 +1975,10,18,M,3832 +1975,10,19,F,3660 +1975,10,19,M,3674 +1975,10,20,F,4169 +1975,10,20,M,4435 +1975,10,21,F,4385 +1975,10,21,M,4534 +1975,10,22,F,4225 +1975,10,22,M,4347 +1975,10,23,F,4212 +1975,10,23,M,4421 +1975,10,24,F,4377 +1975,10,24,M,4509 +1975,10,25,F,3737 +1975,10,25,M,3960 +1975,10,26,F,3541 +1975,10,26,M,3901 +1975,10,27,F,4065 +1975,10,27,M,4532 +1975,10,28,F,4196 +1975,10,28,M,4565 +1975,10,29,F,4231 +1975,10,29,M,4482 +1975,10,30,F,4236 +1975,10,30,M,4291 +1975,10,31,F,4132 +1975,10,31,M,4416 +1975,10,99,M,4 +1975,11,1,F,3773 +1975,11,1,M,3943 +1975,11,2,F,3484 +1975,11,2,M,3738 +1975,11,3,F,4309 +1975,11,3,M,4502 +1975,11,4,F,4374 +1975,11,4,M,4628 +1975,11,5,F,4145 +1975,11,5,M,4469 +1975,11,6,F,4344 +1975,11,6,M,4469 +1975,11,7,F,4352 +1975,11,7,M,4561 +1975,11,8,F,3911 +1975,11,8,M,4013 +1975,11,9,F,3614 +1975,11,9,M,3908 +1975,11,10,F,4295 +1975,11,10,M,4637 +1975,11,11,F,4308 +1975,11,11,M,4618 +1975,11,12,F,4150 +1975,11,12,M,4368 +1975,11,13,F,4377 +1975,11,13,M,4445 +1975,11,14,F,4381 +1975,11,14,M,4615 +1975,11,15,F,3783 +1975,11,15,M,3813 +1975,11,16,F,3591 +1975,11,16,M,3711 +1975,11,17,F,4135 +1975,11,17,M,4425 +1975,11,18,F,4381 +1975,11,18,M,4415 +1975,11,19,F,4390 +1975,11,19,M,4545 +1975,11,20,F,4177 +1975,11,20,M,4358 +1975,11,21,F,4432 +1975,11,21,M,4617 +1975,11,22,F,3796 +1975,11,22,M,3967 +1975,11,23,F,3547 +1975,11,23,M,3845 +1975,11,24,F,4388 +1975,11,24,M,4485 +1975,11,25,F,4628 +1975,11,25,M,4847 +1975,11,26,F,4367 +1975,11,26,M,4543 +1975,11,27,F,3556 +1975,11,27,M,3800 +1975,11,28,F,4140 +1975,11,28,M,4254 +1975,11,29,F,3699 +1975,11,29,M,3867 +1975,11,30,F,3599 +1975,11,30,M,3898 +1975,11,31,F,2 +1975,11,31,M,1 +1975,11,99,F,2 +1975,12,1,F,4239 +1975,12,1,M,4595 +1975,12,2,F,4308 +1975,12,2,M,4794 +1975,12,3,F,4329 +1975,12,3,M,4484 +1975,12,4,F,4301 +1975,12,4,M,4370 +1975,12,5,F,4315 +1975,12,5,M,4541 +1975,12,6,F,3801 +1975,12,6,M,3932 +1975,12,7,F,3628 +1975,12,7,M,3800 +1975,12,8,F,4303 +1975,12,8,M,4608 +1975,12,9,F,4389 +1975,12,9,M,4635 +1975,12,10,F,4337 +1975,12,10,M,4621 +1975,12,11,F,4261 +1975,12,11,M,4517 +1975,12,12,F,4419 +1975,12,12,M,4605 +1975,12,13,F,3791 +1975,12,13,M,4074 +1975,12,14,F,3641 +1975,12,14,M,3887 +1975,12,15,F,4461 +1975,12,15,M,4609 +1975,12,16,F,4714 +1975,12,16,M,4842 +1975,12,17,F,4577 +1975,12,17,M,4635 +1975,12,18,F,4582 +1975,12,18,M,4786 +1975,12,19,F,4550 +1975,12,19,M,4778 +1975,12,20,F,3894 +1975,12,20,M,4018 +1975,12,21,F,3629 +1975,12,21,M,3719 +1975,12,22,F,4232 +1975,12,22,M,4353 +1975,12,23,F,4266 +1975,12,23,M,4501 +1975,12,24,F,3738 +1975,12,24,M,3999 +1975,12,25,F,3421 +1975,12,25,M,3724 +1975,12,26,F,4187 +1975,12,26,M,4411 +1975,12,27,F,3974 +1975,12,27,M,3976 +1975,12,28,F,3624 +1975,12,28,M,3704 +1975,12,29,F,4509 +1975,12,29,M,4888 +1975,12,30,F,4942 +1975,12,30,M,5202 +1975,12,31,F,4549 +1975,12,31,M,5029 +1975,12,99,F,12 +1975,12,99,M,8 +1976,1,1,F,3591 +1976,1,1,M,3711 +1976,1,2,F,3889 +1976,1,2,M,4105 +1976,1,3,F,3625 +1976,1,3,M,3833 +1976,1,4,F,3550 +1976,1,4,M,3701 +1976,1,5,F,4035 +1976,1,5,M,4217 +1976,1,6,F,4289 +1976,1,6,M,4656 +1976,1,7,F,4232 +1976,1,7,M,4379 +1976,1,8,F,4137 +1976,1,8,M,4280 +1976,1,9,F,4210 +1976,1,9,M,4375 +1976,1,10,F,3730 +1976,1,10,M,3904 +1976,1,11,F,3609 +1976,1,11,M,3627 +1976,1,12,F,4220 +1976,1,12,M,4488 +1976,1,13,F,4344 +1976,1,13,M,4704 +1976,1,14,F,4322 +1976,1,14,M,4629 +1976,1,15,F,4307 +1976,1,15,M,4429 +1976,1,16,F,4299 +1976,1,16,M,4548 +1976,1,17,F,3954 +1976,1,17,M,4015 +1976,1,18,F,3565 +1976,1,18,M,3583 +1976,1,19,F,4404 +1976,1,19,M,4489 +1976,1,20,F,4425 +1976,1,20,M,4577 +1976,1,21,F,4124 +1976,1,21,M,4515 +1976,1,22,F,4383 +1976,1,22,M,4510 +1976,1,23,F,4311 +1976,1,23,M,4460 +1976,1,24,F,3812 +1976,1,24,M,4054 +1976,1,25,F,3698 +1976,1,25,M,3821 +1976,1,26,F,4154 +1976,1,26,M,4396 +1976,1,27,F,4310 +1976,1,27,M,4567 +1976,1,28,F,4119 +1976,1,28,M,4540 +1976,1,29,F,4169 +1976,1,29,M,4530 +1976,1,30,F,4198 +1976,1,30,M,4594 +1976,1,31,F,3706 +1976,1,31,M,3996 +1976,1,99,F,21 +1976,1,99,M,18 +1976,2,1,F,3668 +1976,2,1,M,3804 +1976,2,2,F,4360 +1976,2,2,M,4498 +1976,2,3,F,4334 +1976,2,3,M,4512 +1976,2,4,F,4240 +1976,2,4,M,4526 +1976,2,5,F,4174 +1976,2,5,M,4500 +1976,2,6,F,4276 +1976,2,6,M,4541 +1976,2,7,F,3654 +1976,2,7,M,4030 +1976,2,8,F,3642 +1976,2,8,M,3714 +1976,2,9,F,4220 +1976,2,9,M,4321 +1976,2,10,F,4565 +1976,2,10,M,4657 +1976,2,11,F,4358 +1976,2,11,M,4349 +1976,2,12,F,4296 +1976,2,12,M,4483 +1976,2,13,F,4142 +1976,2,13,M,4524 +1976,2,14,F,3848 +1976,2,14,M,4007 +1976,2,15,F,3456 +1976,2,15,M,3874 +1976,2,16,F,4174 +1976,2,16,M,4402 +1976,2,17,F,4393 +1976,2,17,M,4679 +1976,2,18,F,4264 +1976,2,18,M,4562 +1976,2,19,F,4186 +1976,2,19,M,4413 +1976,2,20,F,4239 +1976,2,20,M,4606 +1976,2,21,F,3862 +1976,2,21,M,3890 +1976,2,22,F,3596 +1976,2,22,M,3741 +1976,2,23,F,4202 +1976,2,23,M,4485 +1976,2,24,F,4321 +1976,2,24,M,4413 +1976,2,25,F,4185 +1976,2,25,M,4387 +1976,2,26,F,4243 +1976,2,26,M,4471 +1976,2,27,F,4328 +1976,2,27,M,4414 +1976,2,28,F,3929 +1976,2,28,M,4003 +1976,2,29,F,3681 +1976,2,29,M,3878 +1976,2,30,F,2 +1976,2,99,F,6 +1976,2,99,M,14 +1976,3,1,F,4369 +1976,3,1,M,4490 +1976,3,2,F,4378 +1976,3,2,M,4661 +1976,3,3,F,4339 +1976,3,3,M,4510 +1976,3,4,F,4202 +1976,3,4,M,4415 +1976,3,5,F,4205 +1976,3,5,M,4500 +1976,3,6,F,3715 +1976,3,6,M,3845 +1976,3,7,F,3628 +1976,3,7,M,3624 +1976,3,8,F,4159 +1976,3,8,M,4270 +1976,3,9,F,4279 +1976,3,9,M,4433 +1976,3,10,F,4153 +1976,3,10,M,4401 +1976,3,11,F,4195 +1976,3,11,M,4366 +1976,3,12,F,4214 +1976,3,12,M,4440 +1976,3,13,F,3744 +1976,3,13,M,3930 +1976,3,14,F,3541 +1976,3,14,M,3593 +1976,3,15,F,4168 +1976,3,15,M,4299 +1976,3,16,F,4221 +1976,3,16,M,4415 +1976,3,17,F,4248 +1976,3,17,M,4433 +1976,3,18,F,4119 +1976,3,18,M,4353 +1976,3,19,F,4255 +1976,3,19,M,4368 +1976,3,20,F,3903 +1976,3,20,M,3987 +1976,3,21,F,3615 +1976,3,21,M,3769 +1976,3,22,F,4153 +1976,3,22,M,4362 +1976,3,23,F,4260 +1976,3,23,M,4603 +1976,3,24,F,3995 +1976,3,24,M,4394 +1976,3,25,F,4161 +1976,3,25,M,4462 +1976,3,26,F,4363 +1976,3,26,M,4381 +1976,3,27,F,3690 +1976,3,27,M,4035 +1976,3,28,F,3555 +1976,3,28,M,3712 +1976,3,29,F,4196 +1976,3,29,M,4303 +1976,3,30,F,4311 +1976,3,30,M,4507 +1976,3,31,F,4223 +1976,3,31,M,4430 +1976,3,99,F,14 +1976,3,99,M,17 +1976,4,1,F,4126 +1976,4,1,M,4210 +1976,4,2,F,4250 +1976,4,2,M,4391 +1976,4,3,F,3646 +1976,4,3,M,3937 +1976,4,4,F,3588 +1976,4,4,M,3843 +1976,4,5,F,4106 +1976,4,5,M,4173 +1976,4,6,F,4125 +1976,4,6,M,4461 +1976,4,7,F,4223 +1976,4,7,M,4345 +1976,4,8,F,4137 +1976,4,8,M,4370 +1976,4,9,F,4020 +1976,4,9,M,4267 +1976,4,10,F,3598 +1976,4,10,M,3784 +1976,4,11,F,3451 +1976,4,11,M,3605 +1976,4,12,F,4171 +1976,4,12,M,4361 +1976,4,13,F,4135 +1976,4,13,M,4324 +1976,4,14,F,4141 +1976,4,14,M,4553 +1976,4,15,F,4107 +1976,4,15,M,4476 +1976,4,16,F,4063 +1976,4,16,M,4195 +1976,4,17,F,3741 +1976,4,17,M,3797 +1976,4,18,F,3446 +1976,4,18,M,3653 +1976,4,19,F,4062 +1976,4,19,M,4265 +1976,4,20,F,4438 +1976,4,20,M,4598 +1976,4,21,F,4098 +1976,4,21,M,4420 +1976,4,22,F,4031 +1976,4,22,M,4354 +1976,4,23,F,4202 +1976,4,23,M,4295 +1976,4,24,F,3579 +1976,4,24,M,3723 +1976,4,25,F,3320 +1976,4,25,M,3497 +1976,4,26,F,3985 +1976,4,26,M,4347 +1976,4,27,F,4101 +1976,4,27,M,4389 +1976,4,28,F,4118 +1976,4,28,M,4172 +1976,4,29,F,3971 +1976,4,29,M,4122 +1976,4,30,F,4134 +1976,4,30,M,4238 +1976,4,31,F,3 +1976,4,99,F,10 +1976,4,99,M,6 +1976,5,1,F,3703 +1976,5,1,M,3909 +1976,5,2,F,3378 +1976,5,2,M,3712 +1976,5,3,F,4109 +1976,5,3,M,4124 +1976,5,4,F,4330 +1976,5,4,M,4453 +1976,5,5,F,4321 +1976,5,5,M,4281 +1976,5,6,F,4145 +1976,5,6,M,4375 +1976,5,7,F,4284 +1976,5,7,M,4356 +1976,5,8,F,3580 +1976,5,8,M,3799 +1976,5,9,F,3273 +1976,5,9,M,3402 +1976,5,10,F,4122 +1976,5,10,M,4450 +1976,5,11,F,4413 +1976,5,11,M,4463 +1976,5,12,F,4189 +1976,5,12,M,4195 +1976,5,13,F,4042 +1976,5,13,M,4197 +1976,5,14,F,4162 +1976,5,14,M,4465 +1976,5,15,F,3651 +1976,5,15,M,3858 +1976,5,16,F,3533 +1976,5,16,M,3624 +1976,5,17,F,4225 +1976,5,17,M,4515 +1976,5,18,F,4243 +1976,5,18,M,4351 +1976,5,19,F,4128 +1976,5,19,M,4361 +1976,5,20,F,4081 +1976,5,20,M,4333 +1976,5,21,F,4228 +1976,5,21,M,4505 +1976,5,22,F,3624 +1976,5,22,M,3841 +1976,5,23,F,3316 +1976,5,23,M,3698 +1976,5,24,F,4211 +1976,5,24,M,4543 +1976,5,25,F,4279 +1976,5,25,M,4582 +1976,5,26,F,4066 +1976,5,26,M,4367 +1976,5,27,F,4129 +1976,5,27,M,4401 +1976,5,28,F,4286 +1976,5,28,M,4612 +1976,5,29,F,3812 +1976,5,29,M,3933 +1976,5,30,F,3567 +1976,5,30,M,3636 +1976,5,31,F,3618 +1976,5,31,M,3969 +1976,5,99,F,6 +1976,5,99,M,9 +1976,6,1,F,4390 +1976,6,1,M,4537 +1976,6,2,F,4192 +1976,6,2,M,4557 +1976,6,3,F,4300 +1976,6,3,M,4486 +1976,6,4,F,4459 +1976,6,4,M,4395 +1976,6,5,F,3498 +1976,6,5,M,3892 +1976,6,6,F,3629 +1976,6,6,M,3674 +1976,6,7,F,4094 +1976,6,7,M,4321 +1976,6,8,F,4410 +1976,6,8,M,4697 +1976,6,9,F,4219 +1976,6,9,M,4583 +1976,6,10,F,4376 +1976,6,10,M,4610 +1976,6,11,F,4393 +1976,6,11,M,4667 +1976,6,12,F,3848 +1976,6,12,M,4046 +1976,6,13,F,3671 +1976,6,13,M,3769 +1976,6,14,F,4379 +1976,6,14,M,4593 +1976,6,15,F,4298 +1976,6,15,M,4762 +1976,6,16,F,4313 +1976,6,16,M,4655 +1976,6,17,F,4134 +1976,6,17,M,4697 +1976,6,18,F,4233 +1976,6,18,M,4600 +1976,6,19,F,3792 +1976,6,19,M,4037 +1976,6,20,F,3573 +1976,6,20,M,3824 +1976,6,21,F,4105 +1976,6,21,M,4359 +1976,6,22,F,4428 +1976,6,22,M,4620 +1976,6,23,F,4393 +1976,6,23,M,4510 +1976,6,24,F,4463 +1976,6,24,M,4554 +1976,6,25,F,4420 +1976,6,25,M,4706 +1976,6,26,F,3778 +1976,6,26,M,4049 +1976,6,27,F,3736 +1976,6,27,M,3878 +1976,6,28,F,4434 +1976,6,28,M,4637 +1976,6,29,F,4532 +1976,6,29,M,4955 +1976,6,30,F,4362 +1976,6,30,M,4770 +1976,6,31,F,2 +1976,6,99,F,14 +1976,6,99,M,12 +1976,7,1,F,4403 +1976,7,1,M,4669 +1976,7,2,F,4548 +1976,7,2,M,4812 +1976,7,3,F,3814 +1976,7,3,M,4104 +1976,7,4,F,3882 +1976,7,4,M,4011 +1976,7,5,F,3630 +1976,7,5,M,3934 +1976,7,6,F,4498 +1976,7,6,M,4729 +1976,7,7,F,4838 +1976,7,7,M,5109 +1976,7,8,F,4651 +1976,7,8,M,4898 +1976,7,9,F,4707 +1976,7,9,M,4880 +1976,7,10,F,4096 +1976,7,10,M,4173 +1976,7,11,F,3806 +1976,7,11,M,4187 +1976,7,12,F,4464 +1976,7,12,M,4804 +1976,7,13,F,4656 +1976,7,13,M,4820 +1976,7,14,F,4451 +1976,7,14,M,4901 +1976,7,15,F,4694 +1976,7,15,M,4768 +1976,7,16,F,4715 +1976,7,16,M,4853 +1976,7,17,F,3950 +1976,7,17,M,4197 +1976,7,18,F,3791 +1976,7,18,M,3785 +1976,7,19,F,4520 +1976,7,19,M,4780 +1976,7,20,F,4671 +1976,7,20,M,4759 +1976,7,21,F,4589 +1976,7,21,M,4855 +1976,7,22,F,4623 +1976,7,22,M,4788 +1976,7,23,F,4635 +1976,7,23,M,4820 +1976,7,24,F,4181 +1976,7,24,M,4281 +1976,7,25,F,3921 +1976,7,25,M,3890 +1976,7,26,F,4530 +1976,7,26,M,4862 +1976,7,27,F,4802 +1976,7,27,M,4882 +1976,7,28,F,4571 +1976,7,28,M,4593 +1976,7,29,F,4535 +1976,7,29,M,4834 +1976,7,30,F,4720 +1976,7,30,M,4965 +1976,7,31,F,3967 +1976,7,31,M,4267 +1976,7,99,F,6 +1976,7,99,M,2 +1976,8,1,F,3863 +1976,8,1,M,3994 +1976,8,2,F,4377 +1976,8,2,M,4660 +1976,8,3,F,4705 +1976,8,3,M,4878 +1976,8,4,F,4421 +1976,8,4,M,4644 +1976,8,5,F,4562 +1976,8,5,M,4831 +1976,8,6,F,4808 +1976,8,6,M,4847 +1976,8,7,F,4027 +1976,8,7,M,4397 +1976,8,8,F,3870 +1976,8,8,M,4154 +1976,8,9,F,4583 +1976,8,9,M,4760 +1976,8,10,F,4784 +1976,8,10,M,5048 +1976,8,11,F,4573 +1976,8,11,M,4744 +1976,8,12,F,4642 +1976,8,12,M,5080 +1976,8,13,F,4665 +1976,8,13,M,4873 +1976,8,14,F,4066 +1976,8,14,M,4262 +1976,8,15,F,3841 +1976,8,15,M,3982 +1976,8,16,F,4506 +1976,8,16,M,4829 +1976,8,17,F,4671 +1976,8,17,M,4952 +1976,8,18,F,4593 +1976,8,18,M,4926 +1976,8,19,F,4620 +1976,8,19,M,4835 +1976,8,20,F,4443 +1976,8,20,M,4830 +1976,8,21,F,4250 +1976,8,21,M,4498 +1976,8,22,F,4079 +1976,8,22,M,4208 +1976,8,23,F,4530 +1976,8,23,M,4926 +1976,8,24,F,4827 +1976,8,24,M,5050 +1976,8,25,F,4641 +1976,8,25,M,4840 +1976,8,26,F,4759 +1976,8,26,M,4901 +1976,8,27,F,4887 +1976,8,27,M,4975 +1976,8,28,F,4059 +1976,8,28,M,4439 +1976,8,29,F,3873 +1976,8,29,M,4125 +1976,8,30,F,4430 +1976,8,30,M,4729 +1976,8,31,F,4639 +1976,8,31,M,4963 +1976,8,99,F,8 +1976,8,99,M,4 +1976,9,1,F,4654 +1976,9,1,M,4822 +1976,9,2,F,4530 +1976,9,2,M,4794 +1976,9,3,F,4691 +1976,9,3,M,4976 +1976,9,4,F,3987 +1976,9,4,M,4420 +1976,9,5,F,3828 +1976,9,5,M,3979 +1976,9,6,F,3828 +1976,9,6,M,4255 +1976,9,7,F,4696 +1976,9,7,M,4958 +1976,9,8,F,4901 +1976,9,8,M,5265 +1976,9,9,F,4835 +1976,9,9,M,5155 +1976,9,10,F,4837 +1976,9,10,M,5161 +1976,9,11,F,4137 +1976,9,11,M,4276 +1976,9,12,F,3958 +1976,9,12,M,4184 +1976,9,13,F,4659 +1976,9,13,M,4982 +1976,9,14,F,4962 +1976,9,14,M,5203 +1976,9,15,F,4885 +1976,9,15,M,5092 +1976,9,16,F,4869 +1976,9,16,M,5087 +1976,9,17,F,4794 +1976,9,17,M,5191 +1976,9,18,F,4317 +1976,9,18,M,4421 +1976,9,19,F,4129 +1976,9,19,M,4456 +1976,9,20,F,4972 +1976,9,20,M,5079 +1976,9,21,F,5075 +1976,9,21,M,5221 +1976,9,22,F,4920 +1976,9,22,M,4916 +1976,9,23,F,4751 +1976,9,23,M,5064 +1976,9,24,F,4943 +1976,9,24,M,5109 +1976,9,25,F,4422 +1976,9,25,M,4370 +1976,9,26,F,4109 +1976,9,26,M,4401 +1976,9,27,F,4913 +1976,9,27,M,5113 +1976,9,28,F,4935 +1976,9,28,M,5211 +1976,9,29,F,4757 +1976,9,29,M,5060 +1976,9,30,F,4641 +1976,9,30,M,5077 +1976,9,31,M,1 +1976,9,99,F,2 +1976,9,99,M,4 +1976,10,1,F,4757 +1976,10,1,M,5102 +1976,10,2,F,4271 +1976,10,2,M,4420 +1976,10,3,F,4002 +1976,10,3,M,4268 +1976,10,4,F,4719 +1976,10,4,M,4937 +1976,10,5,F,4663 +1976,10,5,M,5126 +1976,10,6,F,4674 +1976,10,6,M,4809 +1976,10,7,F,4541 +1976,10,7,M,4920 +1976,10,8,F,4693 +1976,10,8,M,4872 +1976,10,9,F,4043 +1976,10,9,M,4219 +1976,10,10,F,4061 +1976,10,10,M,4252 +1976,10,11,F,4527 +1976,10,11,M,4869 +1976,10,12,F,4671 +1976,10,12,M,4951 +1976,10,13,F,4472 +1976,10,13,M,4711 +1976,10,14,F,4527 +1976,10,14,M,4726 +1976,10,15,F,4709 +1976,10,15,M,4855 +1976,10,16,F,4051 +1976,10,16,M,4136 +1976,10,17,F,3888 +1976,10,17,M,3926 +1976,10,18,F,4490 +1976,10,18,M,4758 +1976,10,19,F,4438 +1976,10,19,M,4810 +1976,10,20,F,4539 +1976,10,20,M,5036 +1976,10,21,F,4485 +1976,10,21,M,4772 +1976,10,22,F,4622 +1976,10,22,M,4820 +1976,10,23,F,4114 +1976,10,23,M,4087 +1976,10,24,F,3821 +1976,10,24,M,4027 +1976,10,25,F,4563 +1976,10,25,M,4635 +1976,10,26,F,4672 +1976,10,26,M,4731 +1976,10,27,F,4502 +1976,10,27,M,4710 +1976,10,28,F,4278 +1976,10,28,M,4579 +1976,10,29,F,4464 +1976,10,29,M,4652 +1976,10,30,F,3942 +1976,10,30,M,4026 +1976,10,31,F,3911 +1976,10,31,M,4167 +1976,10,99,F,4 +1976,10,99,M,2 +1976,11,1,F,4400 +1976,11,1,M,4607 +1976,11,2,F,4359 +1976,11,2,M,4796 +1976,11,3,F,4373 +1976,11,3,M,4642 +1976,11,4,F,4475 +1976,11,4,M,4617 +1976,11,5,F,4519 +1976,11,5,M,4788 +1976,11,6,F,3837 +1976,11,6,M,4027 +1976,11,7,F,3760 +1976,11,7,M,4100 +1976,11,8,F,4514 +1976,11,8,M,4847 +1976,11,9,F,4596 +1976,11,9,M,4884 +1976,11,10,F,4560 +1976,11,10,M,4872 +1976,11,11,F,4535 +1976,11,11,M,4894 +1976,11,12,F,4514 +1976,11,12,M,4690 +1976,11,13,F,3874 +1976,11,13,M,4039 +1976,11,14,F,3809 +1976,11,14,M,3843 +1976,11,15,F,4380 +1976,11,15,M,4651 +1976,11,16,F,4609 +1976,11,16,M,4960 +1976,11,17,F,4522 +1976,11,17,M,4774 +1976,11,18,F,4455 +1976,11,18,M,4726 +1976,11,19,F,4604 +1976,11,19,M,4854 +1976,11,20,F,3876 +1976,11,20,M,4091 +1976,11,21,F,3784 +1976,11,21,M,3923 +1976,11,22,F,4440 +1976,11,22,M,4720 +1976,11,23,F,4459 +1976,11,23,M,4857 +1976,11,24,F,4387 +1976,11,24,M,4621 +1976,11,25,F,3589 +1976,11,25,M,3903 +1976,11,26,F,4202 +1976,11,26,M,4531 +1976,11,27,F,3903 +1976,11,27,M,4063 +1976,11,28,F,3691 +1976,11,28,M,3894 +1976,11,29,F,4563 +1976,11,29,M,4700 +1976,11,30,F,4617 +1976,11,30,M,4933 +1976,11,31,F,1 +1976,11,31,M,12 +1976,11,99,F,4 +1976,12,1,F,4440 +1976,12,1,M,4605 +1976,12,2,F,4256 +1976,12,2,M,4713 +1976,12,3,F,4407 +1976,12,3,M,4635 +1976,12,4,F,3855 +1976,12,4,M,4034 +1976,12,5,F,3692 +1976,12,5,M,3948 +1976,12,6,F,4403 +1976,12,6,M,4795 +1976,12,7,F,4701 +1976,12,7,M,4936 +1976,12,8,F,4489 +1976,12,8,M,4726 +1976,12,9,F,4579 +1976,12,9,M,4684 +1976,12,10,F,4463 +1976,12,10,M,4696 +1976,12,11,F,3806 +1976,12,11,M,4163 +1976,12,12,F,3909 +1976,12,12,M,3860 +1976,12,13,F,4474 +1976,12,13,M,4766 +1976,12,14,F,4641 +1976,12,14,M,4918 +1976,12,15,F,4684 +1976,12,15,M,4832 +1976,12,16,F,4597 +1976,12,16,M,4853 +1976,12,17,F,4906 +1976,12,17,M,4984 +1976,12,18,F,3918 +1976,12,18,M,4063 +1976,12,19,F,3633 +1976,12,19,M,3872 +1976,12,20,F,4755 +1976,12,20,M,5027 +1976,12,21,F,4644 +1976,12,21,M,5132 +1976,12,22,F,4244 +1976,12,22,M,4350 +1976,12,23,F,3890 +1976,12,23,M,4214 +1976,12,24,F,3708 +1976,12,24,M,3813 +1976,12,25,F,3564 +1976,12,25,M,3621 +1976,12,26,F,3628 +1976,12,26,M,3982 +1976,12,27,F,4426 +1976,12,27,M,4916 +1976,12,28,F,4905 +1976,12,28,M,5210 +1976,12,29,F,4974 +1976,12,29,M,5114 +1976,12,30,F,4744 +1976,12,30,M,5107 +1976,12,31,F,4342 +1976,12,31,M,4622 +1976,12,99,F,2 +1976,12,99,M,4 +1977,1,1,F,3561 +1977,1,1,M,3855 +1977,1,2,F,3616 +1977,1,2,M,3851 +1977,1,3,F,4182 +1977,1,3,M,4521 +1977,1,4,F,4514 +1977,1,4,M,4590 +1977,1,5,F,4293 +1977,1,5,M,4691 +1977,1,6,F,4334 +1977,1,6,M,4637 +1977,1,7,F,4358 +1977,1,7,M,4854 +1977,1,8,F,3836 +1977,1,8,M,4177 +1977,1,9,F,3763 +1977,1,9,M,3798 +1977,1,10,F,4430 +1977,1,10,M,4712 +1977,1,11,F,4515 +1977,1,11,M,4665 +1977,1,12,F,4505 +1977,1,12,M,4752 +1977,1,13,F,4600 +1977,1,13,M,4850 +1977,1,14,F,4578 +1977,1,14,M,4739 +1977,1,15,F,3891 +1977,1,15,M,4152 +1977,1,16,F,3719 +1977,1,16,M,3947 +1977,1,17,F,4657 +1977,1,17,M,4739 +1977,1,18,F,4768 +1977,1,18,M,4860 +1977,1,19,F,4595 +1977,1,19,M,4578 +1977,1,20,F,4526 +1977,1,20,M,4703 +1977,1,21,F,4604 +1977,1,21,M,4787 +1977,1,22,F,4050 +1977,1,22,M,4118 +1977,1,23,F,3740 +1977,1,23,M,3880 +1977,1,24,F,4438 +1977,1,24,M,4808 +1977,1,25,F,4745 +1977,1,25,M,4830 +1977,1,26,F,4470 +1977,1,26,M,4800 +1977,1,27,F,4607 +1977,1,27,M,4732 +1977,1,28,F,4566 +1977,1,28,M,4856 +1977,1,29,F,3897 +1977,1,29,M,4144 +1977,1,30,F,3763 +1977,1,30,M,4006 +1977,1,31,F,4568 +1977,1,31,M,4683 +1977,1,99,F,4 +1977,2,1,F,4653 +1977,2,1,M,4833 +1977,2,2,F,4481 +1977,2,2,M,4552 +1977,2,3,F,4483 +1977,2,3,M,4761 +1977,2,4,F,4600 +1977,2,4,M,4820 +1977,2,5,F,4082 +1977,2,5,M,4095 +1977,2,6,F,3717 +1977,2,6,M,3976 +1977,2,7,F,4410 +1977,2,7,M,4712 +1977,2,8,F,4428 +1977,2,8,M,4667 +1977,2,9,F,4531 +1977,2,9,M,4737 +1977,2,10,F,4611 +1977,2,10,M,4925 +1977,2,11,F,4588 +1977,2,11,M,4883 +1977,2,12,F,4124 +1977,2,12,M,4368 +1977,2,13,F,4022 +1977,2,13,M,4063 +1977,2,14,F,4761 +1977,2,14,M,5072 +1977,2,15,F,4812 +1977,2,15,M,4903 +1977,2,16,F,4529 +1977,2,16,M,4890 +1977,2,17,F,4720 +1977,2,17,M,4845 +1977,2,18,F,4617 +1977,2,18,M,4879 +1977,2,19,F,4116 +1977,2,19,M,4059 +1977,2,20,F,3898 +1977,2,20,M,4112 +1977,2,21,F,4488 +1977,2,21,M,4624 +1977,2,22,F,4803 +1977,2,22,M,5024 +1977,2,23,F,4638 +1977,2,23,M,4991 +1977,2,24,F,4656 +1977,2,24,M,4794 +1977,2,25,F,4573 +1977,2,25,M,5075 +1977,2,26,F,4144 +1977,2,26,M,4381 +1977,2,27,F,3848 +1977,2,27,M,4056 +1977,2,28,F,4580 +1977,2,28,M,4781 +1977,2,29,F,3 +1977,2,30,F,4 +1977,2,31,F,1 +1977,2,99,F,2 +1977,2,99,M,2 +1977,3,1,F,4712 +1977,3,1,M,5123 +1977,3,2,F,4591 +1977,3,2,M,4856 +1977,3,3,F,4785 +1977,3,3,M,4758 +1977,3,4,F,4785 +1977,3,4,M,4992 +1977,3,5,F,4036 +1977,3,5,M,4316 +1977,3,6,F,3760 +1977,3,6,M,4024 +1977,3,7,F,4598 +1977,3,7,M,4761 +1977,3,8,F,4865 +1977,3,8,M,5168 +1977,3,9,F,4608 +1977,3,9,M,4748 +1977,3,10,F,4592 +1977,3,10,M,4764 +1977,3,11,F,4579 +1977,3,11,M,4918 +1977,3,12,F,4053 +1977,3,12,M,4213 +1977,3,13,F,3937 +1977,3,13,M,4001 +1977,3,14,F,4505 +1977,3,14,M,4873 +1977,3,15,F,4703 +1977,3,15,M,4981 +1977,3,16,F,4635 +1977,3,16,M,4832 +1977,3,17,F,4589 +1977,3,17,M,4723 +1977,3,18,F,4447 +1977,3,18,M,4813 +1977,3,19,F,4015 +1977,3,19,M,4155 +1977,3,20,F,3818 +1977,3,20,M,4018 +1977,3,21,F,4498 +1977,3,21,M,4584 +1977,3,22,F,4676 +1977,3,22,M,4858 +1977,3,23,F,4545 +1977,3,23,M,4627 +1977,3,24,F,4494 +1977,3,24,M,4761 +1977,3,25,F,4574 +1977,3,25,M,4771 +1977,3,26,F,3866 +1977,3,26,M,4093 +1977,3,27,F,3671 +1977,3,27,M,4098 +1977,3,28,F,4487 +1977,3,28,M,4810 +1977,3,29,F,4656 +1977,3,29,M,4993 +1977,3,30,F,4526 +1977,3,30,M,4884 +1977,3,31,F,4590 +1977,3,31,M,4855 +1977,3,99,F,2 +1977,3,99,M,2 +1977,4,1,F,4420 +1977,4,1,M,4577 +1977,4,2,F,3937 +1977,4,2,M,4121 +1977,4,3,F,3881 +1977,4,3,M,4075 +1977,4,4,F,4617 +1977,4,4,M,4669 +1977,4,5,F,4662 +1977,4,5,M,4844 +1977,4,6,F,4424 +1977,4,6,M,4705 +1977,4,7,F,4360 +1977,4,7,M,4788 +1977,4,8,F,4448 +1977,4,8,M,4543 +1977,4,9,F,3727 +1977,4,9,M,4007 +1977,4,10,F,3577 +1977,4,10,M,3799 +1977,4,11,F,4391 +1977,4,11,M,4580 +1977,4,12,F,4658 +1977,4,12,M,5018 +1977,4,13,F,4493 +1977,4,13,M,4723 +1977,4,14,F,4431 +1977,4,14,M,4670 +1977,4,15,F,4411 +1977,4,15,M,4888 +1977,4,16,F,3962 +1977,4,16,M,4077 +1977,4,17,F,3693 +1977,4,17,M,3900 +1977,4,18,F,4475 +1977,4,18,M,4882 +1977,4,19,F,4668 +1977,4,19,M,5023 +1977,4,20,F,4614 +1977,4,20,M,4653 +1977,4,21,F,4431 +1977,4,21,M,4713 +1977,4,22,F,4532 +1977,4,22,M,4626 +1977,4,23,F,3862 +1977,4,23,M,4057 +1977,4,24,F,3530 +1977,4,24,M,3672 +1977,4,25,F,4434 +1977,4,25,M,4577 +1977,4,26,F,4623 +1977,4,26,M,4809 +1977,4,27,F,4232 +1977,4,27,M,4591 +1977,4,28,F,4354 +1977,4,28,M,4630 +1977,4,29,F,4274 +1977,4,29,M,4657 +1977,4,30,F,3794 +1977,4,30,M,4072 +1977,4,31,F,1 +1977,5,1,F,3666 +1977,5,1,M,3968 +1977,5,2,F,4584 +1977,5,2,M,4801 +1977,5,3,F,4489 +1977,5,3,M,4955 +1977,5,4,F,4502 +1977,5,4,M,4895 +1977,5,5,F,4587 +1977,5,5,M,4799 +1977,5,6,F,4505 +1977,5,6,M,5030 +1977,5,7,F,3868 +1977,5,7,M,4214 +1977,5,8,F,3676 +1977,5,8,M,3967 +1977,5,9,F,4227 +1977,5,9,M,4622 +1977,5,10,F,4544 +1977,5,10,M,4860 +1977,5,11,F,4235 +1977,5,11,M,4657 +1977,5,12,F,4537 +1977,5,12,M,4673 +1977,5,13,F,4625 +1977,5,13,M,4689 +1977,5,14,F,3949 +1977,5,14,M,4159 +1977,5,15,F,3664 +1977,5,15,M,3857 +1977,5,16,F,4539 +1977,5,16,M,4950 +1977,5,17,F,4670 +1977,5,17,M,4972 +1977,5,18,F,4537 +1977,5,18,M,4876 +1977,5,19,F,4453 +1977,5,19,M,4790 +1977,5,20,F,4717 +1977,5,20,M,4737 +1977,5,21,F,3809 +1977,5,21,M,4085 +1977,5,22,F,3831 +1977,5,22,M,3900 +1977,5,23,F,4585 +1977,5,23,M,4836 +1977,5,24,F,4579 +1977,5,24,M,5024 +1977,5,25,F,4662 +1977,5,25,M,4866 +1977,5,26,F,4556 +1977,5,26,M,4812 +1977,5,27,F,4584 +1977,5,27,M,5025 +1977,5,28,F,3968 +1977,5,28,M,4116 +1977,5,29,F,3790 +1977,5,29,M,3954 +1977,5,30,F,3742 +1977,5,30,M,4003 +1977,5,31,F,4635 +1977,5,31,M,4762 +1977,5,99,F,2 +1977,5,99,M,1 +1977,6,1,F,4789 +1977,6,1,M,4879 +1977,6,2,F,4489 +1977,6,2,M,4855 +1977,6,3,F,4566 +1977,6,3,M,4859 +1977,6,4,F,3942 +1977,6,4,M,4107 +1977,6,5,F,3878 +1977,6,5,M,4011 +1977,6,6,F,4629 +1977,6,6,M,4645 +1977,6,7,F,4658 +1977,6,7,M,4889 +1977,6,8,F,4418 +1977,6,8,M,4787 +1977,6,9,F,4570 +1977,6,9,M,4823 +1977,6,10,F,4461 +1977,6,10,M,4776 +1977,6,11,F,3938 +1977,6,11,M,4082 +1977,6,12,F,3691 +1977,6,12,M,3922 +1977,6,13,F,4465 +1977,6,13,M,4864 +1977,6,14,F,4677 +1977,6,14,M,4952 +1977,6,15,F,4651 +1977,6,15,M,4888 +1977,6,16,F,4785 +1977,6,16,M,4951 +1977,6,17,F,4693 +1977,6,17,M,5053 +1977,6,18,F,4030 +1977,6,18,M,4197 +1977,6,19,F,3855 +1977,6,19,M,3940 +1977,6,20,F,4725 +1977,6,20,M,4803 +1977,6,21,F,4667 +1977,6,21,M,5001 +1977,6,22,F,4566 +1977,6,22,M,4857 +1977,6,23,F,4472 +1977,6,23,M,5017 +1977,6,24,F,4647 +1977,6,24,M,4965 +1977,6,25,F,3997 +1977,6,25,M,4341 +1977,6,26,F,3909 +1977,6,26,M,4129 +1977,6,27,F,4613 +1977,6,27,M,5044 +1977,6,28,F,4793 +1977,6,28,M,5201 +1977,6,29,F,4740 +1977,6,29,M,4995 +1977,6,30,F,4670 +1977,6,30,M,4942 +1977,6,31,F,2 +1977,6,31,M,2 +1977,6,99,F,8 +1977,6,99,M,6 +1977,7,1,F,4757 +1977,7,1,M,4989 +1977,7,2,F,4036 +1977,7,2,M,4275 +1977,7,3,F,3888 +1977,7,3,M,3973 +1977,7,4,F,4083 +1977,7,4,M,4088 +1977,7,5,F,4642 +1977,7,5,M,5006 +1977,7,6,F,5073 +1977,7,6,M,5460 +1977,7,7,F,5118 +1977,7,7,M,5306 +1977,7,8,F,5073 +1977,7,8,M,5161 +1977,7,9,F,4272 +1977,7,9,M,4521 +1977,7,10,F,3917 +1977,7,10,M,4066 +1977,7,11,F,4636 +1977,7,11,M,4954 +1977,7,12,F,4867 +1977,7,12,M,5130 +1977,7,13,F,4839 +1977,7,13,M,5081 +1977,7,14,F,4869 +1977,7,14,M,5185 +1977,7,15,F,4942 +1977,7,15,M,5234 +1977,7,16,F,4234 +1977,7,16,M,4433 +1977,7,17,F,3878 +1977,7,17,M,4180 +1977,7,18,F,4708 +1977,7,18,M,5073 +1977,7,19,F,4943 +1977,7,19,M,5230 +1977,7,20,F,4923 +1977,7,20,M,5209 +1977,7,21,F,4820 +1977,7,21,M,5058 +1977,7,22,F,5001 +1977,7,22,M,5179 +1977,7,23,F,4135 +1977,7,23,M,4353 +1977,7,24,F,3951 +1977,7,24,M,4235 +1977,7,25,F,4661 +1977,7,25,M,5110 +1977,7,26,F,4977 +1977,7,26,M,5151 +1977,7,27,F,4642 +1977,7,27,M,5046 +1977,7,28,F,4778 +1977,7,28,M,5048 +1977,7,29,F,4894 +1977,7,29,M,5190 +1977,7,30,F,4314 +1977,7,30,M,4503 +1977,7,31,F,3949 +1977,7,31,M,4274 +1977,7,99,F,6 +1977,7,99,M,8 +1977,8,1,F,4715 +1977,8,1,M,5095 +1977,8,2,F,5065 +1977,8,2,M,5216 +1977,8,3,F,4755 +1977,8,3,M,5135 +1977,8,4,F,4874 +1977,8,4,M,4946 +1977,8,5,F,5044 +1977,8,5,M,5256 +1977,8,6,F,4326 +1977,8,6,M,4482 +1977,8,7,F,4109 +1977,8,7,M,4292 +1977,8,8,F,4763 +1977,8,8,M,5192 +1977,8,9,F,4930 +1977,8,9,M,5342 +1977,8,10,F,5000 +1977,8,10,M,5212 +1977,8,11,F,4834 +1977,8,11,M,4926 +1977,8,12,F,4896 +1977,8,12,M,5203 +1977,8,13,F,4181 +1977,8,13,M,4488 +1977,8,14,F,4075 +1977,8,14,M,4170 +1977,8,15,F,4816 +1977,8,15,M,5036 +1977,8,16,F,5039 +1977,8,16,M,5320 +1977,8,17,F,4840 +1977,8,17,M,5068 +1977,8,18,F,4776 +1977,8,18,M,5080 +1977,8,19,F,5049 +1977,8,19,M,4954 +1977,8,20,F,4120 +1977,8,20,M,4411 +1977,8,21,F,4101 +1977,8,21,M,4268 +1977,8,22,F,4852 +1977,8,22,M,5126 +1977,8,23,F,5029 +1977,8,23,M,5384 +1977,8,24,F,4983 +1977,8,24,M,5135 +1977,8,25,F,4912 +1977,8,25,M,5110 +1977,8,26,F,4781 +1977,8,26,M,5146 +1977,8,27,F,4256 +1977,8,27,M,4565 +1977,8,28,F,4138 +1977,8,28,M,4426 +1977,8,29,F,4800 +1977,8,29,M,5076 +1977,8,30,F,4991 +1977,8,30,M,5427 +1977,8,31,F,5059 +1977,8,31,M,5094 +1977,8,99,F,2 +1977,8,99,M,2 +1977,9,1,F,4875 +1977,9,1,M,5077 +1977,9,2,F,5031 +1977,9,2,M,5156 +1977,9,3,F,4167 +1977,9,3,M,4466 +1977,9,4,F,4018 +1977,9,4,M,4130 +1977,9,5,F,4155 +1977,9,5,M,4183 +1977,9,6,F,4909 +1977,9,6,M,5186 +1977,9,7,F,5058 +1977,9,7,M,5272 +1977,9,8,F,4806 +1977,9,8,M,5314 +1977,9,9,F,5040 +1977,9,9,M,5254 +1977,9,10,F,4204 +1977,9,10,M,4567 +1977,9,11,F,4089 +1977,9,11,M,4291 +1977,9,12,F,4946 +1977,9,12,M,5052 +1977,9,13,F,4938 +1977,9,13,M,5239 +1977,9,14,F,5055 +1977,9,14,M,5168 +1977,9,15,F,4981 +1977,9,15,M,5180 +1977,9,16,F,5130 +1977,9,16,M,5360 +1977,9,17,F,4588 +1977,9,17,M,4573 +1977,9,18,F,4168 +1977,9,18,M,4560 +1977,9,19,F,5121 +1977,9,19,M,5157 +1977,9,20,F,5302 +1977,9,20,M,5345 +1977,9,21,F,5006 +1977,9,21,M,5386 +1977,9,22,F,4906 +1977,9,22,M,5134 +1977,9,23,F,5053 +1977,9,23,M,5275 +1977,9,24,F,4382 +1977,9,24,M,4621 +1977,9,25,F,4202 +1977,9,25,M,4415 +1977,9,26,F,5008 +1977,9,26,M,5371 +1977,9,27,F,5229 +1977,9,27,M,5299 +1977,9,28,F,4960 +1977,9,28,M,5140 +1977,9,29,F,5018 +1977,9,29,M,5062 +1977,9,30,F,4992 +1977,9,30,M,5354 +1977,9,31,F,3 +1977,9,31,M,6 +1977,9,99,F,4 +1977,9,99,M,6 +1977,10,1,F,4446 +1977,10,1,M,4588 +1977,10,2,F,4169 +1977,10,2,M,4448 +1977,10,3,F,4922 +1977,10,3,M,5088 +1977,10,4,F,4926 +1977,10,4,M,5250 +1977,10,5,F,4847 +1977,10,5,M,4957 +1977,10,6,F,4762 +1977,10,6,M,5064 +1977,10,7,F,4836 +1977,10,7,M,5136 +1977,10,8,F,4122 +1977,10,8,M,4438 +1977,10,9,F,4077 +1977,10,9,M,4243 +1977,10,10,F,4797 +1977,10,10,M,4849 +1977,10,11,F,4798 +1977,10,11,M,5131 +1977,10,12,F,4769 +1977,10,12,M,4874 +1977,10,13,F,4693 +1977,10,13,M,4899 +1977,10,14,F,4712 +1977,10,14,M,4913 +1977,10,15,F,4146 +1977,10,15,M,4242 +1977,10,16,F,4048 +1977,10,16,M,4195 +1977,10,17,F,4679 +1977,10,17,M,4825 +1977,10,18,F,4651 +1977,10,18,M,5027 +1977,10,19,F,4764 +1977,10,19,M,4886 +1977,10,20,F,4600 +1977,10,20,M,4986 +1977,10,21,F,4761 +1977,10,21,M,4920 +1977,10,22,F,4051 +1977,10,22,M,4225 +1977,10,23,F,3761 +1977,10,23,M,4062 +1977,10,24,F,4558 +1977,10,24,M,4674 +1977,10,25,F,4754 +1977,10,25,M,5031 +1977,10,26,F,4676 +1977,10,26,M,4891 +1977,10,27,F,4677 +1977,10,27,M,4754 +1977,10,28,F,4535 +1977,10,28,M,4862 +1977,10,29,F,3936 +1977,10,29,M,4247 +1977,10,30,F,3872 +1977,10,30,M,4207 +1977,10,31,F,4366 +1977,10,31,M,4671 +1977,10,99,F,2 +1977,10,99,M,6 +1977,11,1,F,4731 +1977,11,1,M,4872 +1977,11,2,F,4479 +1977,11,2,M,4652 +1977,11,3,F,4576 +1977,11,3,M,4836 +1977,11,4,F,4641 +1977,11,4,M,4944 +1977,11,5,F,4071 +1977,11,5,M,4167 +1977,11,6,F,3803 +1977,11,6,M,4039 +1977,11,7,F,4573 +1977,11,7,M,4865 +1977,11,8,F,4806 +1977,11,8,M,4969 +1977,11,9,F,4503 +1977,11,9,M,4779 +1977,11,10,F,4569 +1977,11,10,M,4849 +1977,11,11,F,4536 +1977,11,11,M,4709 +1977,11,12,F,3958 +1977,11,12,M,4046 +1977,11,13,F,3801 +1977,11,13,M,4094 +1977,11,14,F,4479 +1977,11,14,M,4751 +1977,11,15,F,4849 +1977,11,15,M,4907 +1977,11,16,F,4577 +1977,11,16,M,4842 +1977,11,17,F,4526 +1977,11,17,M,4992 +1977,11,18,F,4705 +1977,11,18,M,4843 +1977,11,19,F,3900 +1977,11,19,M,4272 +1977,11,20,F,3814 +1977,11,20,M,3872 +1977,11,21,F,4621 +1977,11,21,M,4949 +1977,11,22,F,4633 +1977,11,22,M,4943 +1977,11,23,F,4435 +1977,11,23,M,4942 +1977,11,24,F,3789 +1977,11,24,M,3919 +1977,11,25,F,4216 +1977,11,25,M,4556 +1977,11,26,F,3854 +1977,11,26,M,4155 +1977,11,27,F,3754 +1977,11,27,M,3973 +1977,11,28,F,4609 +1977,11,28,M,4881 +1977,11,29,F,4614 +1977,11,29,M,5005 +1977,11,30,F,4611 +1977,11,30,M,4760 +1977,11,31,F,2 +1977,11,31,M,4 +1977,11,99,F,4 +1977,11,99,M,6 +1977,12,1,F,4490 +1977,12,1,M,4720 +1977,12,2,F,4541 +1977,12,2,M,4705 +1977,12,3,F,3832 +1977,12,3,M,4110 +1977,12,4,F,3728 +1977,12,4,M,3881 +1977,12,5,F,4591 +1977,12,5,M,4767 +1977,12,6,F,4539 +1977,12,6,M,4966 +1977,12,7,F,4487 +1977,12,7,M,4588 +1977,12,8,F,4509 +1977,12,8,M,4752 +1977,12,9,F,4495 +1977,12,9,M,4735 +1977,12,10,F,3917 +1977,12,10,M,4047 +1977,12,11,F,3677 +1977,12,11,M,3826 +1977,12,12,F,4550 +1977,12,12,M,4714 +1977,12,13,F,4684 +1977,12,13,M,4849 +1977,12,14,F,4484 +1977,12,14,M,4740 +1977,12,15,F,4596 +1977,12,15,M,4834 +1977,12,16,F,4657 +1977,12,16,M,4929 +1977,12,17,F,4045 +1977,12,17,M,4128 +1977,12,18,F,3707 +1977,12,18,M,3876 +1977,12,19,F,4757 +1977,12,19,M,5151 +1977,12,20,F,4733 +1977,12,20,M,5166 +1977,12,21,F,4530 +1977,12,21,M,4737 +1977,12,22,F,4223 +1977,12,22,M,4296 +1977,12,23,F,3950 +1977,12,23,M,4099 +1977,12,24,F,3592 +1977,12,24,M,3762 +1977,12,25,F,3731 +1977,12,25,M,3673 +1977,12,26,F,3690 +1977,12,26,M,3822 +1977,12,27,F,4358 +1977,12,27,M,4831 +1977,12,28,F,4708 +1977,12,28,M,5025 +1977,12,29,F,4792 +1977,12,29,M,4942 +1977,12,30,F,4701 +1977,12,30,M,5105 +1977,12,31,F,3991 +1977,12,31,M,3991 +1977,12,99,F,4 +1977,12,99,M,4 +1978,1,1,F,3773 +1978,1,1,M,3943 +1978,1,2,F,3599 +1978,1,2,M,3944 +1978,1,3,F,4309 +1978,1,3,M,4524 +1978,1,4,F,4352 +1978,1,4,M,4518 +1978,1,5,F,4397 +1978,1,5,M,4656 +1978,1,6,F,4512 +1978,1,6,M,4712 +1978,1,7,F,3959 +1978,1,7,M,4132 +1978,1,8,F,3782 +1978,1,8,M,3840 +1978,1,9,F,4467 +1978,1,9,M,4720 +1978,1,10,F,4501 +1978,1,10,M,4594 +1978,1,11,F,4477 +1978,1,11,M,4743 +1978,1,12,F,4514 +1978,1,12,M,4762 +1978,1,13,F,4510 +1978,1,13,M,4644 +1978,1,14,F,4066 +1978,1,14,M,4248 +1978,1,15,F,3870 +1978,1,15,M,3913 +1978,1,16,F,4701 +1978,1,16,M,4771 +1978,1,17,F,4610 +1978,1,17,M,4737 +1978,1,18,F,4518 +1978,1,18,M,4616 +1978,1,19,F,4538 +1978,1,19,M,4702 +1978,1,20,F,4562 +1978,1,20,M,4754 +1978,1,21,F,3899 +1978,1,21,M,4064 +1978,1,22,F,3716 +1978,1,22,M,3857 +1978,1,23,F,4577 +1978,1,23,M,4686 +1978,1,24,F,4680 +1978,1,24,M,4746 +1978,1,25,F,4394 +1978,1,25,M,4709 +1978,1,26,F,4655 +1978,1,26,M,4742 +1978,1,27,F,4357 +1978,1,27,M,4635 +1978,1,28,F,3938 +1978,1,28,M,4021 +1978,1,29,F,3685 +1978,1,29,M,3852 +1978,1,30,F,4457 +1978,1,30,M,4737 +1978,1,31,F,4457 +1978,1,31,M,4708 +1978,2,1,F,4383 +1978,2,1,M,4788 +1978,2,2,F,4571 +1978,2,2,M,4666 +1978,2,3,F,4454 +1978,2,3,M,4730 +1978,2,4,F,3947 +1978,2,4,M,4132 +1978,2,5,F,3850 +1978,2,5,M,3970 +1978,2,6,F,4521 +1978,2,6,M,4715 +1978,2,7,F,4480 +1978,2,7,M,4868 +1978,2,8,F,4491 +1978,2,8,M,4663 +1978,2,9,F,4494 +1978,2,9,M,4768 +1978,2,10,F,4645 +1978,2,10,M,4889 +1978,2,11,F,3935 +1978,2,11,M,4220 +1978,2,12,F,3821 +1978,2,12,M,4144 +1978,2,13,F,4381 +1978,2,13,M,4594 +1978,2,14,F,4874 +1978,2,14,M,4990 +1978,2,15,F,4513 +1978,2,15,M,4783 +1978,2,16,F,4458 +1978,2,16,M,4657 +1978,2,17,F,4515 +1978,2,17,M,4736 +1978,2,18,F,3945 +1978,2,18,M,4238 +1978,2,19,F,3646 +1978,2,19,M,4062 +1978,2,20,F,4419 +1978,2,20,M,4617 +1978,2,21,F,4570 +1978,2,21,M,4696 +1978,2,22,F,4606 +1978,2,22,M,4747 +1978,2,23,F,4515 +1978,2,23,M,4768 +1978,2,24,F,4708 +1978,2,24,M,4858 +1978,2,25,F,4051 +1978,2,25,M,4280 +1978,2,26,F,3880 +1978,2,26,M,4015 +1978,2,27,F,4489 +1978,2,27,M,4783 +1978,2,28,F,4660 +1978,2,28,M,5057 +1978,2,29,F,2 +1978,2,29,M,8 +1978,2,30,F,2 +1978,2,30,M,2 +1978,2,31,F,3 +1978,2,31,M,2 +1978,2,99,F,4 +1978,3,1,F,4471 +1978,3,1,M,4673 +1978,3,2,F,4537 +1978,3,2,M,4783 +1978,3,3,F,4684 +1978,3,3,M,4761 +1978,3,4,F,3966 +1978,3,4,M,4064 +1978,3,5,F,3857 +1978,3,5,M,3954 +1978,3,6,F,4455 +1978,3,6,M,4847 +1978,3,7,F,4660 +1978,3,7,M,4925 +1978,3,8,F,4517 +1978,3,8,M,4704 +1978,3,9,F,4525 +1978,3,9,M,4706 +1978,3,10,F,4635 +1978,3,10,M,4962 +1978,3,11,F,3970 +1978,3,11,M,4187 +1978,3,12,F,3831 +1978,3,12,M,4053 +1978,3,13,F,4333 +1978,3,13,M,4703 +1978,3,14,F,4699 +1978,3,14,M,4838 +1978,3,15,F,4528 +1978,3,15,M,4771 +1978,3,16,F,4528 +1978,3,16,M,4809 +1978,3,17,F,4600 +1978,3,17,M,4888 +1978,3,18,F,3881 +1978,3,18,M,4091 +1978,3,19,F,3803 +1978,3,19,M,3938 +1978,3,20,F,4372 +1978,3,20,M,4773 +1978,3,21,F,4676 +1978,3,21,M,5003 +1978,3,22,F,4540 +1978,3,22,M,4781 +1978,3,23,F,4462 +1978,3,23,M,4707 +1978,3,24,F,4443 +1978,3,24,M,4721 +1978,3,25,F,3788 +1978,3,25,M,4099 +1978,3,26,F,3729 +1978,3,26,M,3866 +1978,3,27,F,4542 +1978,3,27,M,4571 +1978,3,28,F,4516 +1978,3,28,M,4787 +1978,3,29,F,4505 +1978,3,29,M,4701 +1978,3,30,F,4428 +1978,3,30,M,4489 +1978,3,31,F,4663 +1978,3,31,M,4667 +1978,4,1,F,3990 +1978,4,1,M,4091 +1978,4,2,F,3806 +1978,4,2,M,3893 +1978,4,3,F,4426 +1978,4,3,M,4701 +1978,4,4,F,4520 +1978,4,4,M,4930 +1978,4,5,F,4288 +1978,4,5,M,4572 +1978,4,6,F,4285 +1978,4,6,M,4696 +1978,4,7,F,4385 +1978,4,7,M,4702 +1978,4,8,F,3831 +1978,4,8,M,4072 +1978,4,9,F,3696 +1978,4,9,M,3767 +1978,4,10,F,4313 +1978,4,10,M,4568 +1978,4,11,F,4419 +1978,4,11,M,4615 +1978,4,12,F,4221 +1978,4,12,M,4398 +1978,4,13,F,4263 +1978,4,13,M,4471 +1978,4,14,F,4536 +1978,4,14,M,4492 +1978,4,15,F,3631 +1978,4,15,M,3902 +1978,4,16,F,3526 +1978,4,16,M,3675 +1978,4,17,F,4200 +1978,4,17,M,4513 +1978,4,18,F,4512 +1978,4,18,M,4703 +1978,4,19,F,4198 +1978,4,19,M,4530 +1978,4,20,F,4114 +1978,4,20,M,4480 +1978,4,21,F,4339 +1978,4,21,M,4563 +1978,4,22,F,3700 +1978,4,22,M,4097 +1978,4,23,F,3433 +1978,4,23,M,3882 +1978,4,24,F,4400 +1978,4,24,M,4631 +1978,4,25,F,4466 +1978,4,25,M,4626 +1978,4,26,F,4416 +1978,4,26,M,4611 +1978,4,27,F,4280 +1978,4,27,M,4579 +1978,4,28,F,4504 +1978,4,28,M,4551 +1978,4,29,F,3747 +1978,4,29,M,4017 +1978,4,30,F,3479 +1978,4,30,M,3673 +1978,4,31,F,2 +1978,4,31,M,2 +1978,4,99,F,2 +1978,4,99,M,2 +1978,5,1,F,4291 +1978,5,1,M,4616 +1978,5,2,F,4561 +1978,5,2,M,4865 +1978,5,3,F,4410 +1978,5,3,M,4653 +1978,5,4,F,4102 +1978,5,4,M,4578 +1978,5,5,F,4445 +1978,5,5,M,4661 +1978,5,6,F,3747 +1978,5,6,M,3983 +1978,5,7,F,3639 +1978,5,7,M,3759 +1978,5,8,F,4389 +1978,5,8,M,4607 +1978,5,9,F,4584 +1978,5,9,M,4733 +1978,5,10,F,4645 +1978,5,10,M,4643 +1978,5,11,F,4267 +1978,5,11,M,4647 +1978,5,12,F,4352 +1978,5,12,M,4629 +1978,5,13,F,3817 +1978,5,13,M,3950 +1978,5,14,F,3630 +1978,5,14,M,3760 +1978,5,15,F,4426 +1978,5,15,M,4786 +1978,5,16,F,4441 +1978,5,16,M,4771 +1978,5,17,F,4340 +1978,5,17,M,4587 +1978,5,18,F,4318 +1978,5,18,M,4742 +1978,5,19,F,4379 +1978,5,19,M,4639 +1978,5,20,F,3881 +1978,5,20,M,4197 +1978,5,21,F,3691 +1978,5,21,M,3889 +1978,5,22,F,4279 +1978,5,22,M,4823 +1978,5,23,F,4518 +1978,5,23,M,4703 +1978,5,24,F,4477 +1978,5,24,M,4731 +1978,5,25,F,4456 +1978,5,25,M,4737 +1978,5,26,F,4547 +1978,5,26,M,4985 +1978,5,27,F,3902 +1978,5,27,M,4119 +1978,5,28,F,3807 +1978,5,28,M,3990 +1978,5,29,F,3775 +1978,5,29,M,4018 +1978,5,30,F,4554 +1978,5,30,M,5083 +1978,5,31,F,4585 +1978,5,31,M,5028 +1978,5,99,F,4 +1978,5,99,M,2 +1978,6,1,F,4612 +1978,6,1,M,4838 +1978,6,2,F,4386 +1978,6,2,M,4927 +1978,6,3,F,3836 +1978,6,3,M,4146 +1978,6,4,F,3617 +1978,6,4,M,3791 +1978,6,5,F,4459 +1978,6,5,M,4677 +1978,6,6,F,4655 +1978,6,6,M,4958 +1978,6,7,F,4520 +1978,6,7,M,4828 +1978,6,8,F,4510 +1978,6,8,M,4580 +1978,6,9,F,4527 +1978,6,9,M,4847 +1978,6,10,F,3930 +1978,6,10,M,4118 +1978,6,11,F,3628 +1978,6,11,M,3956 +1978,6,12,F,4437 +1978,6,12,M,4771 +1978,6,13,F,4631 +1978,6,13,M,4638 +1978,6,14,F,4315 +1978,6,14,M,4919 +1978,6,15,F,4552 +1978,6,15,M,4631 +1978,6,16,F,4594 +1978,6,16,M,4768 +1978,6,17,F,4056 +1978,6,17,M,4188 +1978,6,18,F,3710 +1978,6,18,M,4084 +1978,6,19,F,4560 +1978,6,19,M,4993 +1978,6,20,F,4697 +1978,6,20,M,4986 +1978,6,21,F,4555 +1978,6,21,M,4723 +1978,6,22,F,4558 +1978,6,22,M,4853 +1978,6,23,F,4657 +1978,6,23,M,4944 +1978,6,24,F,3905 +1978,6,24,M,4227 +1978,6,25,F,3909 +1978,6,25,M,4193 +1978,6,26,F,4514 +1978,6,26,M,4839 +1978,6,27,F,4822 +1978,6,27,M,5036 +1978,6,28,F,4670 +1978,6,28,M,5039 +1978,6,29,F,4637 +1978,6,29,M,5004 +1978,6,30,F,4927 +1978,6,30,M,5171 +1978,6,31,M,3 +1978,6,99,F,4 +1978,6,99,M,4 +1978,7,1,F,3954 +1978,7,1,M,4264 +1978,7,2,F,3851 +1978,7,2,M,4140 +1978,7,3,F,4480 +1978,7,3,M,4814 +1978,7,4,F,4023 +1978,7,4,M,4425 +1978,7,5,F,4782 +1978,7,5,M,4907 +1978,7,6,F,4900 +1978,7,6,M,5304 +1978,7,7,F,4989 +1978,7,7,M,5266 +1978,7,8,F,4256 +1978,7,8,M,4535 +1978,7,9,F,3933 +1978,7,9,M,4179 +1978,7,10,F,4727 +1978,7,10,M,5160 +1978,7,11,F,4794 +1978,7,11,M,5074 +1978,7,12,F,4816 +1978,7,12,M,4900 +1978,7,13,F,4773 +1978,7,13,M,5222 +1978,7,14,F,5060 +1978,7,14,M,5389 +1978,7,15,F,4280 +1978,7,15,M,4590 +1978,7,16,F,4139 +1978,7,16,M,4287 +1978,7,17,F,4821 +1978,7,17,M,5216 +1978,7,18,F,4995 +1978,7,18,M,5381 +1978,7,19,F,4867 +1978,7,19,M,5158 +1978,7,20,F,5049 +1978,7,20,M,5352 +1978,7,21,F,4931 +1978,7,21,M,5411 +1978,7,22,F,4419 +1978,7,22,M,4663 +1978,7,23,F,4133 +1978,7,23,M,4444 +1978,7,24,F,4898 +1978,7,24,M,5079 +1978,7,25,F,5009 +1978,7,25,M,5352 +1978,7,26,F,4989 +1978,7,26,M,5122 +1978,7,27,F,5008 +1978,7,27,M,5198 +1978,7,28,F,5093 +1978,7,28,M,5231 +1978,7,29,F,4256 +1978,7,29,M,4439 +1978,7,30,F,4104 +1978,7,30,M,4390 +1978,7,31,F,4851 +1978,7,31,M,5051 +1978,7,99,F,3 +1978,8,1,F,4909 +1978,8,1,M,5255 +1978,8,2,F,4753 +1978,8,2,M,5085 +1978,8,3,F,4971 +1978,8,3,M,5173 +1978,8,4,F,4922 +1978,8,4,M,5141 +1978,8,5,F,4297 +1978,8,5,M,4460 +1978,8,6,F,4074 +1978,8,6,M,4382 +1978,8,7,F,4929 +1978,8,7,M,5290 +1978,8,8,F,5040 +1978,8,8,M,5420 +1978,8,9,F,4993 +1978,8,9,M,5170 +1978,8,10,F,5148 +1978,8,10,M,5159 +1978,8,11,F,4936 +1978,8,11,M,5248 +1978,8,12,F,4317 +1978,8,12,M,4649 +1978,8,13,F,4175 +1978,8,13,M,4372 +1978,8,14,F,4912 +1978,8,14,M,5233 +1978,8,15,F,5021 +1978,8,15,M,5499 +1978,8,16,F,4901 +1978,8,16,M,5171 +1978,8,17,F,5132 +1978,8,17,M,5268 +1978,8,18,F,5020 +1978,8,18,M,5357 +1978,8,19,F,4260 +1978,8,19,M,4667 +1978,8,20,F,4155 +1978,8,20,M,4338 +1978,8,21,F,4801 +1978,8,21,M,5186 +1978,8,22,F,4952 +1978,8,22,M,5289 +1978,8,23,F,4833 +1978,8,23,M,5081 +1978,8,24,F,4957 +1978,8,24,M,5211 +1978,8,25,F,5074 +1978,8,25,M,5112 +1978,8,26,F,4271 +1978,8,26,M,4531 +1978,8,27,F,4134 +1978,8,27,M,4337 +1978,8,28,F,4857 +1978,8,28,M,5159 +1978,8,29,F,4979 +1978,8,29,M,5421 +1978,8,30,F,4948 +1978,8,30,M,5133 +1978,8,31,F,4849 +1978,8,31,M,5017 +1978,8,99,F,2 +1978,8,99,M,8 +1978,9,1,F,4818 +1978,9,1,M,5315 +1978,9,2,F,4217 +1978,9,2,M,4374 +1978,9,3,F,4066 +1978,9,3,M,4300 +1978,9,4,F,4152 +1978,9,4,M,4340 +1978,9,5,F,4876 +1978,9,5,M,5168 +1978,9,6,F,5255 +1978,9,6,M,5465 +1978,9,7,F,4944 +1978,9,7,M,5365 +1978,9,8,F,4976 +1978,9,8,M,5408 +1978,9,9,F,4456 +1978,9,9,M,4579 +1978,9,10,F,4241 +1978,9,10,M,4409 +1978,9,11,F,5032 +1978,9,11,M,5133 +1978,9,12,F,5177 +1978,9,12,M,5262 +1978,9,13,F,4948 +1978,9,13,M,5222 +1978,9,14,F,5044 +1978,9,14,M,5235 +1978,9,15,F,4991 +1978,9,15,M,5292 +1978,9,16,F,4417 +1978,9,16,M,4770 +1978,9,17,F,4250 +1978,9,17,M,4475 +1978,9,18,F,4960 +1978,9,18,M,5364 +1978,9,19,F,5332 +1978,9,19,M,5396 +1978,9,20,F,5121 +1978,9,20,M,5383 +1978,9,21,F,5107 +1978,9,21,M,5411 +1978,9,22,F,5048 +1978,9,22,M,5322 +1978,9,23,F,4282 +1978,9,23,M,4469 +1978,9,24,F,4288 +1978,9,24,M,4373 +1978,9,25,F,5088 +1978,9,25,M,5346 +1978,9,26,F,5173 +1978,9,26,M,5345 +1978,9,27,F,5113 +1978,9,27,M,5246 +1978,9,28,F,4968 +1978,9,28,M,5230 +1978,9,29,F,5157 +1978,9,29,M,5229 +1978,9,30,F,4226 +1978,9,30,M,4437 +1978,9,31,F,2 +1978,9,31,M,3 +1978,9,99,F,2 +1978,9,99,M,5 +1978,10,1,F,4214 +1978,10,1,M,4487 +1978,10,2,F,4922 +1978,10,2,M,5027 +1978,10,3,F,5140 +1978,10,3,M,5248 +1978,10,4,F,4938 +1978,10,4,M,5007 +1978,10,5,F,4844 +1978,10,5,M,5120 +1978,10,6,F,4910 +1978,10,6,M,5156 +1978,10,7,F,4243 +1978,10,7,M,4380 +1978,10,8,F,4181 +1978,10,8,M,4210 +1978,10,9,F,4891 +1978,10,9,M,4893 +1978,10,10,F,5110 +1978,10,10,M,5251 +1978,10,11,F,4832 +1978,10,11,M,5061 +1978,10,12,F,4828 +1978,10,12,M,5006 +1978,10,13,F,4663 +1978,10,13,M,5114 +1978,10,14,F,4165 +1978,10,14,M,4401 +1978,10,15,F,3942 +1978,10,15,M,3948 +1978,10,16,F,4638 +1978,10,16,M,4909 +1978,10,17,F,4988 +1978,10,17,M,4968 +1978,10,18,F,4655 +1978,10,18,M,4754 +1978,10,19,F,4653 +1978,10,19,M,4861 +1978,10,20,F,4808 +1978,10,20,M,4836 +1978,10,21,F,4134 +1978,10,21,M,4289 +1978,10,22,F,3960 +1978,10,22,M,3989 +1978,10,23,F,4628 +1978,10,23,M,4808 +1978,10,24,F,4703 +1978,10,24,M,4885 +1978,10,25,F,4594 +1978,10,25,M,4744 +1978,10,26,F,4648 +1978,10,26,M,4867 +1978,10,27,F,4682 +1978,10,27,M,4869 +1978,10,28,F,4154 +1978,10,28,M,4271 +1978,10,29,F,3877 +1978,10,29,M,4291 +1978,10,30,F,4586 +1978,10,30,M,4886 +1978,10,31,F,4503 +1978,10,31,M,4846 +1978,10,99,F,2 +1978,10,99,M,8 +1978,11,1,F,4486 +1978,11,1,M,4854 +1978,11,2,F,4528 +1978,11,2,M,4733 +1978,11,3,F,4729 +1978,11,3,M,5058 +1978,11,4,F,4118 +1978,11,4,M,4145 +1978,11,5,F,3774 +1978,11,5,M,4253 +1978,11,6,F,4596 +1978,11,6,M,4930 +1978,11,7,F,4790 +1978,11,7,M,4999 +1978,11,8,F,4670 +1978,11,8,M,4854 +1978,11,9,F,4687 +1978,11,9,M,4935 +1978,11,10,F,4689 +1978,11,10,M,4984 +1978,11,11,F,4083 +1978,11,11,M,4284 +1978,11,12,F,3905 +1978,11,12,M,4081 +1978,11,13,F,4676 +1978,11,13,M,4953 +1978,11,14,F,4866 +1978,11,14,M,5165 +1978,11,15,F,4674 +1978,11,15,M,4877 +1978,11,16,F,4731 +1978,11,16,M,4847 +1978,11,17,F,4726 +1978,11,17,M,5130 +1978,11,18,F,4028 +1978,11,18,M,4417 +1978,11,19,F,3832 +1978,11,19,M,4045 +1978,11,20,F,4605 +1978,11,20,M,4998 +1978,11,21,F,4847 +1978,11,21,M,5114 +1978,11,22,F,4615 +1978,11,22,M,4951 +1978,11,23,F,3860 +1978,11,23,M,4066 +1978,11,24,F,4363 +1978,11,24,M,4690 +1978,11,25,F,4045 +1978,11,25,M,4245 +1978,11,26,F,3976 +1978,11,26,M,4105 +1978,11,27,F,4845 +1978,11,27,M,4990 +1978,11,28,F,4866 +1978,11,28,M,4960 +1978,11,29,F,4575 +1978,11,29,M,4877 +1978,11,30,F,4521 +1978,11,30,M,4889 +1978,11,31,F,2 +1978,11,31,M,3 +1978,11,99,F,2 +1978,11,99,M,1 +1978,12,1,F,4779 +1978,12,1,M,4835 +1978,12,2,F,4247 +1978,12,2,M,4297 +1978,12,3,F,4087 +1978,12,3,M,4124 +1978,12,4,F,4836 +1978,12,4,M,4943 +1978,12,5,F,4884 +1978,12,5,M,5011 +1978,12,6,F,4622 +1978,12,6,M,4797 +1978,12,7,F,4660 +1978,12,7,M,4837 +1978,12,8,F,4488 +1978,12,8,M,4929 +1978,12,9,F,4084 +1978,12,9,M,4263 +1978,12,10,F,3894 +1978,12,10,M,4210 +1978,12,11,F,4786 +1978,12,11,M,4911 +1978,12,12,F,4947 +1978,12,12,M,5125 +1978,12,13,F,4668 +1978,12,13,M,4864 +1978,12,14,F,4707 +1978,12,14,M,4832 +1978,12,15,F,4763 +1978,12,15,M,5200 +1978,12,16,F,4163 +1978,12,16,M,4359 +1978,12,17,F,4039 +1978,12,17,M,4147 +1978,12,18,F,5142 +1978,12,18,M,5071 +1978,12,19,F,5181 +1978,12,19,M,5436 +1978,12,20,F,4835 +1978,12,20,M,5170 +1978,12,21,F,4615 +1978,12,21,M,4803 +1978,12,22,F,4465 +1978,12,22,M,4563 +1978,12,23,F,3883 +1978,12,23,M,4069 +1978,12,24,F,3858 +1978,12,24,M,4116 +1978,12,25,F,3846 +1978,12,25,M,4014 +1978,12,26,F,4287 +1978,12,26,M,4624 +1978,12,27,F,4904 +1978,12,27,M,5020 +1978,12,28,F,4979 +1978,12,28,M,5211 +1978,12,29,F,5097 +1978,12,29,M,5316 +1978,12,30,F,4168 +1978,12,30,M,4319 +1978,12,31,F,3836 +1978,12,31,M,4202 +1978,12,99,F,10 +1978,12,99,M,2 +1979,1,1,F,4016 +1979,1,1,M,4205 +1979,1,2,F,4245 +1979,1,2,M,4610 +1979,1,3,F,4522 +1979,1,3,M,4829 +1979,1,4,F,4612 +1979,1,4,M,4817 +1979,1,5,F,4689 +1979,1,5,M,4897 +1979,1,6,F,4117 +1979,1,6,M,4453 +1979,1,7,F,3861 +1979,1,7,M,4152 +1979,1,8,F,4704 +1979,1,8,M,4820 +1979,1,9,F,4535 +1979,1,9,M,4775 +1979,1,10,F,4548 +1979,1,10,M,4868 +1979,1,11,F,4590 +1979,1,11,M,4819 +1979,1,12,F,4757 +1979,1,12,M,4913 +1979,1,13,F,4212 +1979,1,13,M,4437 +1979,1,14,F,4039 +1979,1,14,M,4134 +1979,1,15,F,4626 +1979,1,15,M,4928 +1979,1,16,F,4697 +1979,1,16,M,5108 +1979,1,17,F,4655 +1979,1,17,M,4858 +1979,1,18,F,4664 +1979,1,18,M,4831 +1979,1,19,F,4765 +1979,1,19,M,5110 +1979,1,20,F,4082 +1979,1,20,M,4396 +1979,1,21,F,3888 +1979,1,21,M,4131 +1979,1,22,F,4732 +1979,1,22,M,4973 +1979,1,23,F,4741 +1979,1,23,M,5053 +1979,1,24,F,4657 +1979,1,24,M,4899 +1979,1,25,F,4680 +1979,1,25,M,4976 +1979,1,26,F,4758 +1979,1,26,M,5051 +1979,1,27,F,4041 +1979,1,27,M,4331 +1979,1,28,F,4034 +1979,1,28,M,4201 +1979,1,29,F,4768 +1979,1,29,M,4964 +1979,1,30,F,4545 +1979,1,30,M,4975 +1979,1,31,F,4767 +1979,1,31,M,4809 +1979,2,1,F,4487 +1979,2,1,M,4848 +1979,2,2,F,4810 +1979,2,2,M,5085 +1979,2,3,F,4116 +1979,2,3,M,4472 +1979,2,4,F,3967 +1979,2,4,M,4226 +1979,2,5,F,4733 +1979,2,5,M,4829 +1979,2,6,F,4801 +1979,2,6,M,5034 +1979,2,7,F,4682 +1979,2,7,M,4960 +1979,2,8,F,4604 +1979,2,8,M,4973 +1979,2,9,F,4740 +1979,2,9,M,4935 +1979,2,10,F,4160 +1979,2,10,M,4416 +1979,2,11,F,4074 +1979,2,11,M,4120 +1979,2,12,F,4730 +1979,2,12,M,4933 +1979,2,13,F,4725 +1979,2,13,M,4938 +1979,2,14,F,4872 +1979,2,14,M,5166 +1979,2,15,F,4709 +1979,2,15,M,5016 +1979,2,16,F,4782 +1979,2,16,M,5041 +1979,2,17,F,4172 +1979,2,17,M,4244 +1979,2,18,F,4043 +1979,2,18,M,4152 +1979,2,19,F,4525 +1979,2,19,M,4754 +1979,2,20,F,4962 +1979,2,20,M,5090 +1979,2,21,F,4764 +1979,2,21,M,5012 +1979,2,22,F,4701 +1979,2,22,M,4996 +1979,2,23,F,4891 +1979,2,23,M,5209 +1979,2,24,F,4153 +1979,2,24,M,4472 +1979,2,25,F,4050 +1979,2,25,M,4209 +1979,2,26,F,4627 +1979,2,26,M,4940 +1979,2,27,F,4849 +1979,2,27,M,4902 +1979,2,28,F,4740 +1979,2,28,M,4922 +1979,2,99,F,5 +1979,2,99,M,1 +1979,3,1,F,4776 +1979,3,1,M,5038 +1979,3,2,F,4839 +1979,3,2,M,4914 +1979,3,3,F,4217 +1979,3,3,M,4514 +1979,3,4,F,3967 +1979,3,4,M,4261 +1979,3,5,F,4737 +1979,3,5,M,4960 +1979,3,6,F,4640 +1979,3,6,M,5027 +1979,3,7,F,4657 +1979,3,7,M,4946 +1979,3,8,F,4704 +1979,3,8,M,5075 +1979,3,9,F,4758 +1979,3,9,M,5059 +1979,3,10,F,4113 +1979,3,10,M,4418 +1979,3,11,F,3880 +1979,3,11,M,4050 +1979,3,12,F,4769 +1979,3,12,M,4855 +1979,3,13,F,4668 +1979,3,13,M,4901 +1979,3,14,F,4685 +1979,3,14,M,4944 +1979,3,15,F,4599 +1979,3,15,M,4951 +1979,3,16,F,4695 +1979,3,16,M,4981 +1979,3,17,F,4195 +1979,3,17,M,4338 +1979,3,18,F,3885 +1979,3,18,M,4306 +1979,3,19,F,4729 +1979,3,19,M,4972 +1979,3,20,F,4864 +1979,3,20,M,5246 +1979,3,21,F,4700 +1979,3,21,M,5004 +1979,3,22,F,4563 +1979,3,22,M,5034 +1979,3,23,F,4719 +1979,3,23,M,5060 +1979,3,24,F,4179 +1979,3,24,M,4384 +1979,3,25,F,4020 +1979,3,25,M,4036 +1979,3,26,F,4553 +1979,3,26,M,4961 +1979,3,27,F,4761 +1979,3,27,M,5176 +1979,3,28,F,4790 +1979,3,28,M,5035 +1979,3,29,F,4646 +1979,3,29,M,4976 +1979,3,30,F,4844 +1979,3,30,M,5012 +1979,3,31,F,4212 +1979,3,31,M,4260 +1979,3,99,F,2 +1979,3,99,M,2 +1979,4,1,F,4049 +1979,4,1,M,4077 +1979,4,2,F,4509 +1979,4,2,M,4897 +1979,4,3,F,4589 +1979,4,3,M,5003 +1979,4,4,F,4691 +1979,4,4,M,4836 +1979,4,5,F,4697 +1979,4,5,M,4820 +1979,4,6,F,4628 +1979,4,6,M,4977 +1979,4,7,F,3979 +1979,4,7,M,4151 +1979,4,8,F,3792 +1979,4,8,M,4049 +1979,4,9,F,4743 +1979,4,9,M,4904 +1979,4,10,F,4765 +1979,4,10,M,4924 +1979,4,11,F,4609 +1979,4,11,M,4891 +1979,4,12,F,4757 +1979,4,12,M,4835 +1979,4,13,F,4310 +1979,4,13,M,4760 +1979,4,14,F,4089 +1979,4,14,M,4118 +1979,4,15,F,3697 +1979,4,15,M,3918 +1979,4,16,F,4643 +1979,4,16,M,4706 +1979,4,17,F,4750 +1979,4,17,M,5027 +1979,4,18,F,4592 +1979,4,18,M,4903 +1979,4,19,F,4469 +1979,4,19,M,4779 +1979,4,20,F,4806 +1979,4,20,M,4823 +1979,4,21,F,3974 +1979,4,21,M,4223 +1979,4,22,F,3823 +1979,4,22,M,4098 +1979,4,23,F,4580 +1979,4,23,M,4954 +1979,4,24,F,4711 +1979,4,24,M,5131 +1979,4,25,F,4801 +1979,4,25,M,4834 +1979,4,26,F,4522 +1979,4,26,M,4813 +1979,4,27,F,4589 +1979,4,27,M,4975 +1979,4,28,F,3761 +1979,4,28,M,4192 +1979,4,29,F,3555 +1979,4,29,M,3942 +1979,4,30,F,4532 +1979,4,30,M,4986 +1979,4,99,M,1 +1979,5,1,F,4899 +1979,5,1,M,5197 +1979,5,2,F,4746 +1979,5,2,M,4872 +1979,5,3,F,4673 +1979,5,3,M,4882 +1979,5,4,F,4629 +1979,5,4,M,4954 +1979,5,5,F,3933 +1979,5,5,M,4231 +1979,5,6,F,3850 +1979,5,6,M,4059 +1979,5,7,F,4683 +1979,5,7,M,5069 +1979,5,8,F,4943 +1979,5,8,M,5064 +1979,5,9,F,4696 +1979,5,9,M,5026 +1979,5,10,F,4798 +1979,5,10,M,4980 +1979,5,11,F,4799 +1979,5,11,M,5055 +1979,5,12,F,4080 +1979,5,12,M,4194 +1979,5,13,F,3869 +1979,5,13,M,4200 +1979,5,14,F,4566 +1979,5,14,M,4916 +1979,5,15,F,4805 +1979,5,15,M,4974 +1979,5,16,F,4632 +1979,5,16,M,4907 +1979,5,17,F,4613 +1979,5,17,M,4788 +1979,5,18,F,4655 +1979,5,18,M,4883 +1979,5,19,F,4082 +1979,5,19,M,4268 +1979,5,20,F,3981 +1979,5,20,M,4100 +1979,5,21,F,4727 +1979,5,21,M,5010 +1979,5,22,F,4674 +1979,5,22,M,5098 +1979,5,23,F,4693 +1979,5,23,M,5088 +1979,5,24,F,4716 +1979,5,24,M,5029 +1979,5,25,F,4816 +1979,5,25,M,5042 +1979,5,26,F,4063 +1979,5,26,M,4301 +1979,5,27,F,3899 +1979,5,27,M,4046 +1979,5,28,F,4113 +1979,5,28,M,4281 +1979,5,29,F,4829 +1979,5,29,M,5006 +1979,5,30,F,4848 +1979,5,30,M,5006 +1979,5,31,F,4720 +1979,5,31,M,5003 +1979,5,99,M,2 +1979,6,1,F,4821 +1979,6,1,M,5066 +1979,6,2,F,4196 +1979,6,2,M,4353 +1979,6,3,F,3935 +1979,6,3,M,4255 +1979,6,4,F,4771 +1979,6,4,M,5000 +1979,6,5,F,4795 +1979,6,5,M,5056 +1979,6,6,F,4723 +1979,6,6,M,5118 +1979,6,7,F,4846 +1979,6,7,M,4876 +1979,6,8,F,4598 +1979,6,8,M,4991 +1979,6,9,F,4037 +1979,6,9,M,4294 +1979,6,10,F,3975 +1979,6,10,M,4334 +1979,6,11,F,4643 +1979,6,11,M,4960 +1979,6,12,F,4782 +1979,6,12,M,5038 +1979,6,13,F,4642 +1979,6,13,M,4904 +1979,6,14,F,4708 +1979,6,14,M,5090 +1979,6,15,F,4800 +1979,6,15,M,5015 +1979,6,16,F,4233 +1979,6,16,M,4428 +1979,6,17,F,4000 +1979,6,17,M,4175 +1979,6,18,F,4668 +1979,6,18,M,5064 +1979,6,19,F,4907 +1979,6,19,M,5107 +1979,6,20,F,4972 +1979,6,20,M,5119 +1979,6,21,F,4753 +1979,6,21,M,5015 +1979,6,22,F,4837 +1979,6,22,M,5170 +1979,6,23,F,4065 +1979,6,23,M,4420 +1979,6,24,F,3937 +1979,6,24,M,4195 +1979,6,25,F,4649 +1979,6,25,M,4923 +1979,6,26,F,4974 +1979,6,26,M,5142 +1979,6,27,F,4913 +1979,6,27,M,5168 +1979,6,28,F,4942 +1979,6,28,M,5144 +1979,6,29,F,4993 +1979,6,29,M,5260 +1979,6,30,F,4295 +1979,6,30,M,4628 +1979,6,99,M,2 +1979,7,1,F,4218 +1979,7,1,M,4345 +1979,7,2,F,4929 +1979,7,2,M,5146 +1979,7,3,F,5010 +1979,7,3,M,5506 +1979,7,4,F,4256 +1979,7,4,M,4452 +1979,7,5,F,4781 +1979,7,5,M,5165 +1979,7,6,F,4996 +1979,7,6,M,5357 +1979,7,7,F,4309 +1979,7,7,M,4708 +1979,7,8,F,4243 +1979,7,8,M,4381 +1979,7,9,F,5117 +1979,7,9,M,5438 +1979,7,10,F,5229 +1979,7,10,M,5564 +1979,7,11,F,5104 +1979,7,11,M,5264 +1979,7,12,F,5150 +1979,7,12,M,5579 +1979,7,13,F,5212 +1979,7,13,M,5498 +1979,7,14,F,4514 +1979,7,14,M,4830 +1979,7,15,F,4354 +1979,7,15,M,4658 +1979,7,16,F,5101 +1979,7,16,M,5380 +1979,7,17,F,5282 +1979,7,17,M,5545 +1979,7,18,F,5126 +1979,7,18,M,5405 +1979,7,19,F,5176 +1979,7,19,M,5280 +1979,7,20,F,5196 +1979,7,20,M,5472 +1979,7,21,F,4502 +1979,7,21,M,4646 +1979,7,22,F,4314 +1979,7,22,M,4559 +1979,7,23,F,5101 +1979,7,23,M,5504 +1979,7,24,F,5255 +1979,7,24,M,5596 +1979,7,25,F,5296 +1979,7,25,M,5441 +1979,7,26,F,5095 +1979,7,26,M,5568 +1979,7,27,F,5226 +1979,7,27,M,5574 +1979,7,28,F,4599 +1979,7,28,M,4819 +1979,7,29,F,4468 +1979,7,29,M,4426 +1979,7,30,F,5210 +1979,7,30,M,5324 +1979,7,31,F,5265 +1979,7,31,M,5577 +1979,7,99,F,2 +1979,8,1,F,5320 +1979,8,1,M,5556 +1979,8,2,F,5217 +1979,8,2,M,5475 +1979,8,3,F,5260 +1979,8,3,M,5537 +1979,8,4,F,4649 +1979,8,4,M,4778 +1979,8,5,F,4335 +1979,8,5,M,4552 +1979,8,6,F,5276 +1979,8,6,M,5377 +1979,8,7,F,5220 +1979,8,7,M,5633 +1979,8,8,F,5298 +1979,8,8,M,5532 +1979,8,9,F,5044 +1979,8,9,M,5526 +1979,8,10,F,5234 +1979,8,10,M,5600 +1979,8,11,F,4583 +1979,8,11,M,4703 +1979,8,12,F,4357 +1979,8,12,M,4364 +1979,8,13,F,5081 +1979,8,13,M,5015 +1979,8,14,F,5392 +1979,8,14,M,5542 +1979,8,15,F,5083 +1979,8,15,M,5407 +1979,8,16,F,5113 +1979,8,16,M,5273 +1979,8,17,F,5176 +1979,8,17,M,5222 +1979,8,18,F,4584 +1979,8,18,M,4657 +1979,8,19,F,4475 +1979,8,19,M,4721 +1979,8,20,F,5319 +1979,8,20,M,5341 +1979,8,21,F,5369 +1979,8,21,M,5541 +1979,8,22,F,5142 +1979,8,22,M,5423 +1979,8,23,F,5193 +1979,8,23,M,5507 +1979,8,24,F,5181 +1979,8,24,M,5390 +1979,8,25,F,4564 +1979,8,25,M,4761 +1979,8,26,F,4329 +1979,8,26,M,4595 +1979,8,27,F,5135 +1979,8,27,M,5442 +1979,8,28,F,5444 +1979,8,28,M,5605 +1979,8,29,F,5001 +1979,8,29,M,5533 +1979,8,30,F,5111 +1979,8,30,M,5535 +1979,8,31,F,5393 +1979,8,31,M,5480 +1979,8,99,F,2 +1979,8,99,M,2 +1979,9,1,F,4432 +1979,9,1,M,4692 +1979,9,2,F,4167 +1979,9,2,M,4571 +1979,9,3,F,4415 +1979,9,3,M,4576 +1979,9,4,F,5137 +1979,9,4,M,5357 +1979,9,5,F,5307 +1979,9,5,M,5719 +1979,9,6,F,5247 +1979,9,6,M,5549 +1979,9,7,F,5160 +1979,9,7,M,5581 +1979,9,8,F,4559 +1979,9,8,M,4601 +1979,9,9,F,4446 +1979,9,9,M,4524 +1979,9,10,F,5199 +1979,9,10,M,5378 +1979,9,11,F,5361 +1979,9,11,M,5720 +1979,9,12,F,5118 +1979,9,12,M,5480 +1979,9,13,F,5310 +1979,9,13,M,5375 +1979,9,14,F,5387 +1979,9,14,M,5607 +1979,9,15,F,4608 +1979,9,15,M,4760 +1979,9,16,F,4456 +1979,9,16,M,4769 +1979,9,17,F,5396 +1979,9,17,M,5408 +1979,9,18,F,5493 +1979,9,18,M,5692 +1979,9,19,F,5474 +1979,9,19,M,5569 +1979,9,20,F,5228 +1979,9,20,M,5471 +1979,9,21,F,5303 +1979,9,21,M,5608 +1979,9,22,F,4568 +1979,9,22,M,4815 +1979,9,23,F,4432 +1979,9,23,M,4709 +1979,9,24,F,5308 +1979,9,24,M,5646 +1979,9,25,F,5470 +1979,9,25,M,5591 +1979,9,26,F,5304 +1979,9,26,M,5503 +1979,9,27,F,5394 +1979,9,27,M,5619 +1979,9,28,F,5379 +1979,9,28,M,5536 +1979,9,29,F,4689 +1979,9,29,M,4853 +1979,9,30,F,4505 +1979,9,30,M,4607 +1979,9,99,F,14 +1979,9,99,M,11 +1979,10,1,F,5083 +1979,10,1,M,5392 +1979,10,2,F,5271 +1979,10,2,M,5640 +1979,10,3,F,5145 +1979,10,3,M,5452 +1979,10,4,F,5041 +1979,10,4,M,5273 +1979,10,5,F,5176 +1979,10,5,M,5424 +1979,10,6,F,4484 +1979,10,6,M,4654 +1979,10,7,F,4148 +1979,10,7,M,4470 +1979,10,8,F,4868 +1979,10,8,M,5175 +1979,10,9,F,4994 +1979,10,9,M,5356 +1979,10,10,F,4994 +1979,10,10,M,5205 +1979,10,11,F,4917 +1979,10,11,M,5189 +1979,10,12,F,5067 +1979,10,12,M,5354 +1979,10,13,F,4419 +1979,10,13,M,4545 +1979,10,14,F,4208 +1979,10,14,M,4274 +1979,10,15,F,5004 +1979,10,15,M,5160 +1979,10,16,F,5075 +1979,10,16,M,5181 +1979,10,17,F,4843 +1979,10,17,M,5164 +1979,10,18,F,4791 +1979,10,18,M,4983 +1979,10,19,F,4967 +1979,10,19,M,5266 +1979,10,20,F,4298 +1979,10,20,M,4393 +1979,10,21,F,4270 +1979,10,21,M,4384 +1979,10,22,F,4805 +1979,10,22,M,5441 +1979,10,23,F,4921 +1979,10,23,M,5276 +1979,10,24,F,4861 +1979,10,24,M,5123 +1979,10,25,F,4714 +1979,10,25,M,5192 +1979,10,26,F,4860 +1979,10,26,M,5127 +1979,10,27,F,4244 +1979,10,27,M,4329 +1979,10,28,F,4222 +1979,10,28,M,4376 +1979,10,29,F,4841 +1979,10,29,M,5079 +1979,10,30,F,5102 +1979,10,30,M,5400 +1979,10,31,F,4656 +1979,10,31,M,5028 +1979,10,99,M,2 +1979,11,1,F,4905 +1979,11,1,M,5014 +1979,11,2,F,4965 +1979,11,2,M,5209 +1979,11,3,F,4219 +1979,11,3,M,4411 +1979,11,4,F,3989 +1979,11,4,M,4263 +1979,11,5,F,4919 +1979,11,5,M,5137 +1979,11,6,F,5027 +1979,11,6,M,5249 +1979,11,7,F,4853 +1979,11,7,M,5113 +1979,11,8,F,4930 +1979,11,8,M,5178 +1979,11,9,F,5001 +1979,11,9,M,5101 +1979,11,10,F,4296 +1979,11,10,M,4503 +1979,11,11,F,4290 +1979,11,11,M,4470 +1979,11,12,F,4962 +1979,11,12,M,4932 +1979,11,13,F,5168 +1979,11,13,M,5283 +1979,11,14,F,4942 +1979,11,14,M,5208 +1979,11,15,F,4986 +1979,11,15,M,5195 +1979,11,16,F,5045 +1979,11,16,M,5227 +1979,11,17,F,4296 +1979,11,17,M,4400 +1979,11,18,F,4075 +1979,11,18,M,4324 +1979,11,19,F,5228 +1979,11,19,M,5127 +1979,11,20,F,5229 +1979,11,20,M,5536 +1979,11,21,F,4787 +1979,11,21,M,5256 +1979,11,22,F,3953 +1979,11,22,M,4211 +1979,11,23,F,4641 +1979,11,23,M,4839 +1979,11,24,F,4271 +1979,11,24,M,4503 +1979,11,25,F,4097 +1979,11,25,M,4245 +1979,11,26,F,4905 +1979,11,26,M,5104 +1979,11,27,F,4914 +1979,11,27,M,5360 +1979,11,28,F,4898 +1979,11,28,M,5090 +1979,11,29,F,4739 +1979,11,29,M,4981 +1979,11,30,F,4807 +1979,11,30,M,4951 +1979,11,99,F,2 +1979,11,99,M,4 +1979,12,1,F,4080 +1979,12,1,M,4263 +1979,12,2,F,4095 +1979,12,2,M,4335 +1979,12,3,F,4721 +1979,12,3,M,4921 +1979,12,4,F,5023 +1979,12,4,M,5259 +1979,12,5,F,4859 +1979,12,5,M,5073 +1979,12,6,F,4942 +1979,12,6,M,5079 +1979,12,7,F,4855 +1979,12,7,M,5014 +1979,12,8,F,4239 +1979,12,8,M,4470 +1979,12,9,F,3987 +1979,12,9,M,4226 +1979,12,10,F,4806 +1979,12,10,M,5126 +1979,12,11,F,5091 +1979,12,11,M,5217 +1979,12,12,F,4852 +1979,12,12,M,5198 +1979,12,13,F,4683 +1979,12,13,M,5017 +1979,12,14,F,4826 +1979,12,14,M,5118 +1979,12,15,F,4223 +1979,12,15,M,4250 +1979,12,16,F,4012 +1979,12,16,M,4197 +1979,12,17,F,5009 +1979,12,17,M,5264 +1979,12,18,F,5091 +1979,12,18,M,5501 +1979,12,19,F,4977 +1979,12,19,M,5280 +1979,12,20,F,4903 +1979,12,20,M,5119 +1979,12,21,F,4844 +1979,12,21,M,5119 +1979,12,22,F,4232 +1979,12,22,M,4304 +1979,12,23,F,3990 +1979,12,23,M,4114 +1979,12,24,F,4164 +1979,12,24,M,4326 +1979,12,25,F,3888 +1979,12,25,M,4080 +1979,12,26,F,4445 +1979,12,26,M,4786 +1979,12,27,F,5101 +1979,12,27,M,5403 +1979,12,28,F,5356 +1979,12,28,M,5605 +1979,12,29,F,4502 +1979,12,29,M,4488 +1979,12,30,F,4009 +1979,12,30,M,4241 +1979,12,31,F,4782 +1979,12,31,M,5000 +1979,12,99,F,1 +1979,12,99,M,3 +1980,1,1,F,4005 +1980,1,1,M,4227 +1980,1,2,F,4371 +1980,1,2,M,4640 +1980,1,3,F,4815 +1980,1,3,M,5087 +1980,1,4,F,4758 +1980,1,4,M,5181 +1980,1,5,F,4265 +1980,1,5,M,4426 +1980,1,6,F,4093 +1980,1,6,M,4120 +1980,1,7,F,4730 +1980,1,7,M,5103 +1980,1,8,F,4810 +1980,1,8,M,5012 +1980,1,9,F,4763 +1980,1,9,M,4801 +1980,1,10,F,4810 +1980,1,10,M,4949 +1980,1,11,F,5029 +1980,1,11,M,5205 +1980,1,12,F,4153 +1980,1,12,M,4460 +1980,1,13,F,4016 +1980,1,13,M,4275 +1980,1,14,F,4897 +1980,1,14,M,5204 +1980,1,15,F,4895 +1980,1,15,M,5077 +1980,1,16,F,4599 +1980,1,16,M,5061 +1980,1,17,F,4764 +1980,1,17,M,5048 +1980,1,18,F,5001 +1980,1,18,M,5179 +1980,1,19,F,4233 +1980,1,19,M,4497 +1980,1,20,F,3992 +1980,1,20,M,4465 +1980,1,21,F,4738 +1980,1,21,M,4937 +1980,1,22,F,4924 +1980,1,22,M,5152 +1980,1,23,F,4624 +1980,1,23,M,4969 +1980,1,24,F,4809 +1980,1,24,M,5099 +1980,1,25,F,4833 +1980,1,25,M,5234 +1980,1,26,F,4253 +1980,1,26,M,4477 +1980,1,27,F,4149 +1980,1,27,M,4109 +1980,1,28,F,4776 +1980,1,28,M,5104 +1980,1,29,F,4699 +1980,1,29,M,5169 +1980,1,30,F,4775 +1980,1,30,M,4902 +1980,1,31,F,4831 +1980,1,31,M,4908 +1980,2,1,F,4880 +1980,2,1,M,5116 +1980,2,2,F,4258 +1980,2,2,M,4540 +1980,2,3,F,4054 +1980,2,3,M,4231 +1980,2,4,F,4814 +1980,2,4,M,5002 +1980,2,5,F,4971 +1980,2,5,M,5280 +1980,2,6,F,4800 +1980,2,6,M,5107 +1980,2,7,F,4927 +1980,2,7,M,5181 +1980,2,8,F,4883 +1980,2,8,M,5118 +1980,2,9,F,4189 +1980,2,9,M,4583 +1980,2,10,F,4107 +1980,2,10,M,4247 +1980,2,11,F,5020 +1980,2,11,M,5134 +1980,2,12,F,5015 +1980,2,12,M,5237 +1980,2,13,F,4773 +1980,2,13,M,5017 +1980,2,14,F,5054 +1980,2,14,M,5378 +1980,2,15,F,5027 +1980,2,15,M,5174 +1980,2,16,F,4319 +1980,2,16,M,4599 +1980,2,17,F,4135 +1980,2,17,M,4436 +1980,2,18,F,4743 +1980,2,18,M,4973 +1980,2,19,F,5009 +1980,2,19,M,5166 +1980,2,20,F,4814 +1980,2,20,M,5279 +1980,2,21,F,4976 +1980,2,21,M,5180 +1980,2,22,F,4986 +1980,2,22,M,5282 +1980,2,23,F,4326 +1980,2,23,M,4630 +1980,2,24,F,4203 +1980,2,24,M,4401 +1980,2,25,F,4861 +1980,2,25,M,5171 +1980,2,26,F,5045 +1980,2,26,M,5132 +1980,2,27,F,4804 +1980,2,27,M,4979 +1980,2,28,F,4927 +1980,2,28,M,5284 +1980,2,29,F,4646 +1980,2,29,M,4969 +1980,2,99,F,2 +1980,3,1,F,4202 +1980,3,1,M,4408 +1980,3,2,F,4042 +1980,3,2,M,4297 +1980,3,3,F,4892 +1980,3,3,M,5161 +1980,3,4,F,4999 +1980,3,4,M,5114 +1980,3,5,F,4821 +1980,3,5,M,5157 +1980,3,6,F,4905 +1980,3,6,M,5162 +1980,3,7,F,4852 +1980,3,7,M,5191 +1980,3,8,F,4308 +1980,3,8,M,4575 +1980,3,9,F,4156 +1980,3,9,M,4284 +1980,3,10,F,4864 +1980,3,10,M,5058 +1980,3,11,F,4950 +1980,3,11,M,5284 +1980,3,12,F,4872 +1980,3,12,M,5203 +1980,3,13,F,4760 +1980,3,13,M,5010 +1980,3,14,F,4768 +1980,3,14,M,5193 +1980,3,15,F,4201 +1980,3,15,M,4510 +1980,3,16,F,4093 +1980,3,16,M,4336 +1980,3,17,F,4964 +1980,3,17,M,5295 +1980,3,18,F,4942 +1980,3,18,M,5159 +1980,3,19,F,4972 +1980,3,19,M,5139 +1980,3,20,F,4775 +1980,3,20,M,5024 +1980,3,21,F,4971 +1980,3,21,M,5385 +1980,3,22,F,4403 +1980,3,22,M,4499 +1980,3,23,F,4113 +1980,3,23,M,4417 +1980,3,24,F,4853 +1980,3,24,M,5247 +1980,3,25,F,5171 +1980,3,25,M,5382 +1980,3,26,F,4976 +1980,3,26,M,5118 +1980,3,27,F,4864 +1980,3,27,M,5088 +1980,3,28,F,4911 +1980,3,28,M,5204 +1980,3,29,F,4369 +1980,3,29,M,4498 +1980,3,30,F,4131 +1980,3,30,M,4351 +1980,3,31,F,4766 +1980,3,31,M,5131 +1980,3,99,F,2 +1980,3,99,M,2 +1980,4,1,F,4908 +1980,4,1,M,5238 +1980,4,2,F,4898 +1980,4,2,M,5190 +1980,4,3,F,4805 +1980,4,3,M,5082 +1980,4,4,F,4891 +1980,4,4,M,5142 +1980,4,5,F,4164 +1980,4,5,M,4312 +1980,4,6,F,4141 +1980,4,6,M,4272 +1980,4,7,F,4772 +1980,4,7,M,5091 +1980,4,8,F,4971 +1980,4,8,M,5352 +1980,4,9,F,4771 +1980,4,9,M,5186 +1980,4,10,F,4872 +1980,4,10,M,5071 +1980,4,11,F,4822 +1980,4,11,M,5123 +1980,4,12,F,4234 +1980,4,12,M,4310 +1980,4,13,F,4143 +1980,4,13,M,4138 +1980,4,14,F,4639 +1980,4,14,M,5182 +1980,4,15,F,4868 +1980,4,15,M,5273 +1980,4,16,F,4960 +1980,4,16,M,5170 +1980,4,17,F,4748 +1980,4,17,M,5003 +1980,4,18,F,4976 +1980,4,18,M,4985 +1980,4,19,F,4299 +1980,4,19,M,4343 +1980,4,20,F,4106 +1980,4,20,M,4354 +1980,4,21,F,4860 +1980,4,21,M,5144 +1980,4,22,F,4907 +1980,4,22,M,5437 +1980,4,23,F,4833 +1980,4,23,M,5152 +1980,4,24,F,4854 +1980,4,24,M,5016 +1980,4,25,F,4913 +1980,4,25,M,5094 +1980,4,26,F,4088 +1980,4,26,M,4322 +1980,4,27,F,3781 +1980,4,27,M,4075 +1980,4,28,F,4827 +1980,4,28,M,5099 +1980,4,29,F,4955 +1980,4,29,M,5200 +1980,4,30,F,4843 +1980,4,30,M,4989 +1980,4,99,F,1 +1980,4,99,M,3 +1980,5,1,F,4941 +1980,5,1,M,5190 +1980,5,2,F,4798 +1980,5,2,M,5193 +1980,5,3,F,4071 +1980,5,3,M,4588 +1980,5,4,F,3939 +1980,5,4,M,4147 +1980,5,5,F,5001 +1980,5,5,M,4950 +1980,5,6,F,5016 +1980,5,6,M,5183 +1980,5,7,F,4638 +1980,5,7,M,4886 +1980,5,8,F,4546 +1980,5,8,M,4872 +1980,5,9,F,4791 +1980,5,9,M,4975 +1980,5,10,F,4125 +1980,5,10,M,4306 +1980,5,11,F,4099 +1980,5,11,M,4213 +1980,5,12,F,4809 +1980,5,12,M,5113 +1980,5,13,F,4735 +1980,5,13,M,5232 +1980,5,14,F,4642 +1980,5,14,M,5075 +1980,5,15,F,4759 +1980,5,15,M,5073 +1980,5,16,F,4748 +1980,5,16,M,5094 +1980,5,17,F,4090 +1980,5,17,M,4295 +1980,5,18,F,4072 +1980,5,18,M,4251 +1980,5,19,F,4858 +1980,5,19,M,5126 +1980,5,20,F,5186 +1980,5,20,M,5385 +1980,5,21,F,4721 +1980,5,21,M,4961 +1980,5,22,F,4883 +1980,5,22,M,5226 +1980,5,23,F,5033 +1980,5,23,M,5225 +1980,5,24,F,4176 +1980,5,24,M,4483 +1980,5,25,F,4129 +1980,5,25,M,4333 +1980,5,26,F,4275 +1980,5,26,M,4369 +1980,5,27,F,4829 +1980,5,27,M,5320 +1980,5,28,F,4972 +1980,5,28,M,5226 +1980,5,29,F,4983 +1980,5,29,M,5206 +1980,5,30,F,5009 +1980,5,30,M,5173 +1980,5,31,F,4127 +1980,5,31,M,4447 +1980,6,1,F,4064 +1980,6,1,M,4390 +1980,6,2,F,4828 +1980,6,2,M,5105 +1980,6,3,F,4928 +1980,6,3,M,5330 +1980,6,4,F,4863 +1980,6,4,M,5147 +1980,6,5,F,4842 +1980,6,5,M,5136 +1980,6,6,F,4978 +1980,6,6,M,5244 +1980,6,7,F,4346 +1980,6,7,M,4470 +1980,6,8,F,3964 +1980,6,8,M,4219 +1980,6,9,F,4710 +1980,6,9,M,4956 +1980,6,10,F,5074 +1980,6,10,M,5266 +1980,6,11,F,4966 +1980,6,11,M,5076 +1980,6,12,F,4852 +1980,6,12,M,5114 +1980,6,13,F,4806 +1980,6,13,M,5081 +1980,6,14,F,4187 +1980,6,14,M,4528 +1980,6,15,F,4147 +1980,6,15,M,4527 +1980,6,16,F,5053 +1980,6,16,M,5196 +1980,6,17,F,4918 +1980,6,17,M,5291 +1980,6,18,F,5001 +1980,6,18,M,5335 +1980,6,19,F,4981 +1980,6,19,M,5259 +1980,6,20,F,5008 +1980,6,20,M,5318 +1980,6,21,F,4318 +1980,6,21,M,4494 +1980,6,22,F,4200 +1980,6,22,M,4533 +1980,6,23,F,5006 +1980,6,23,M,5486 +1980,6,24,F,5347 +1980,6,24,M,5512 +1980,6,25,F,5145 +1980,6,25,M,5405 +1980,6,26,F,5176 +1980,6,26,M,5445 +1980,6,27,F,5080 +1980,6,27,M,5608 +1980,6,28,F,4425 +1980,6,28,M,4586 +1980,6,29,F,4313 +1980,6,29,M,4507 +1980,6,30,F,5016 +1980,6,30,M,5355 +1980,6,99,F,1 +1980,6,99,M,2 +1980,7,1,F,5313 +1980,7,1,M,5608 +1980,7,2,F,5397 +1980,7,2,M,5517 +1980,7,3,F,5170 +1980,7,3,M,5524 +1980,7,4,F,4454 +1980,7,4,M,4749 +1980,7,5,F,4523 +1980,7,5,M,4705 +1980,7,6,F,4321 +1980,7,6,M,4596 +1980,7,7,F,5223 +1980,7,7,M,5261 +1980,7,8,F,5487 +1980,7,8,M,5803 +1980,7,9,F,5156 +1980,7,9,M,5644 +1980,7,10,F,5234 +1980,7,10,M,5534 +1980,7,11,F,5417 +1980,7,11,M,5661 +1980,7,12,F,4565 +1980,7,12,M,4705 +1980,7,13,F,4419 +1980,7,13,M,4524 +1980,7,14,F,5181 +1980,7,14,M,5432 +1980,7,15,F,5423 +1980,7,15,M,5672 +1980,7,16,F,5366 +1980,7,16,M,5699 +1980,7,17,F,5364 +1980,7,17,M,5698 +1980,7,18,F,5178 +1980,7,18,M,5518 +1980,7,19,F,4533 +1980,7,19,M,4658 +1980,7,20,F,4540 +1980,7,20,M,4589 +1980,7,21,F,5343 +1980,7,21,M,5510 +1980,7,22,F,5367 +1980,7,22,M,5700 +1980,7,23,F,5213 +1980,7,23,M,5403 +1980,7,24,F,5166 +1980,7,24,M,5449 +1980,7,25,F,5242 +1980,7,25,M,5543 +1980,7,26,F,4527 +1980,7,26,M,4902 +1980,7,27,F,4537 +1980,7,27,M,4602 +1980,7,28,F,5217 +1980,7,28,M,5709 +1980,7,29,F,5516 +1980,7,29,M,5945 +1980,7,30,F,5443 +1980,7,30,M,5683 +1980,7,31,F,5335 +1980,7,31,M,5640 +1980,7,99,F,2 +1980,8,1,F,5439 +1980,8,1,M,5567 +1980,8,2,F,4647 +1980,8,2,M,4938 +1980,8,3,F,4468 +1980,8,3,M,4708 +1980,8,4,F,5249 +1980,8,4,M,5534 +1980,8,5,F,5455 +1980,8,5,M,5739 +1980,8,6,F,5270 +1980,8,6,M,5515 +1980,8,7,F,5410 +1980,8,7,M,5634 +1980,8,8,F,5613 +1980,8,8,M,5879 +1980,8,9,F,4568 +1980,8,9,M,4898 +1980,8,10,F,4443 +1980,8,10,M,4702 +1980,8,11,F,5525 +1980,8,11,M,5501 +1980,8,12,F,5541 +1980,8,12,M,5830 +1980,8,13,F,5313 +1980,8,13,M,5610 +1980,8,14,F,5229 +1980,8,14,M,5527 +1980,8,15,F,5269 +1980,8,15,M,5628 +1980,8,16,F,4507 +1980,8,16,M,4854 +1980,8,17,F,4427 +1980,8,17,M,4574 +1980,8,18,F,5354 +1980,8,18,M,5579 +1980,8,19,F,5439 +1980,8,19,M,5754 +1980,8,20,F,5310 +1980,8,20,M,5632 +1980,8,21,F,5311 +1980,8,21,M,5500 +1980,8,22,F,5240 +1980,8,22,M,5539 +1980,8,23,F,4576 +1980,8,23,M,4695 +1980,8,24,F,4335 +1980,8,24,M,4768 +1980,8,25,F,5317 +1980,8,25,M,5593 +1980,8,26,F,5393 +1980,8,26,M,5758 +1980,8,27,F,5380 +1980,8,27,M,5625 +1980,8,28,F,5395 +1980,8,28,M,5612 +1980,8,29,F,5307 +1980,8,29,M,5618 +1980,8,30,F,4673 +1980,8,30,M,4837 +1980,8,31,F,4446 +1980,8,31,M,4646 +1980,9,1,F,4440 +1980,9,1,M,4689 +1980,9,2,F,5349 +1980,9,2,M,5708 +1980,9,3,F,5458 +1980,9,3,M,5716 +1980,9,4,F,5440 +1980,9,4,M,5599 +1980,9,5,F,5286 +1980,9,5,M,5727 +1980,9,6,F,4688 +1980,9,6,M,4857 +1980,9,7,F,4529 +1980,9,7,M,4758 +1980,9,8,F,5243 +1980,9,8,M,5657 +1980,9,9,F,5322 +1980,9,9,M,5697 +1980,9,10,F,5300 +1980,9,10,M,5703 +1980,9,11,F,5243 +1980,9,11,M,5553 +1980,9,12,F,5494 +1980,9,12,M,5704 +1980,9,13,F,4723 +1980,9,13,M,4950 +1980,9,14,F,4634 +1980,9,14,M,4865 +1980,9,15,F,5559 +1980,9,15,M,5865 +1980,9,16,F,5603 +1980,9,16,M,5811 +1980,9,17,F,5506 +1980,9,17,M,5786 +1980,9,18,F,5415 +1980,9,18,M,5902 +1980,9,19,F,5478 +1980,9,19,M,5848 +1980,9,20,F,4828 +1980,9,20,M,5139 +1980,9,21,F,4799 +1980,9,21,M,4974 +1980,9,22,F,5648 +1980,9,22,M,5965 +1980,9,23,F,5667 +1980,9,23,M,6055 +1980,9,24,F,5476 +1980,9,24,M,5857 +1980,9,25,F,5410 +1980,9,25,M,5567 +1980,9,26,F,5490 +1980,9,26,M,5673 +1980,9,27,F,4816 +1980,9,27,M,4938 +1980,9,28,F,4565 +1980,9,28,M,4698 +1980,9,29,F,5367 +1980,9,29,M,5732 +1980,9,30,F,5603 +1980,9,30,M,5698 +1980,9,99,F,1 +1980,9,99,M,6 +1980,10,1,F,5390 +1980,10,1,M,5714 +1980,10,2,F,5327 +1980,10,2,M,5602 +1980,10,3,F,5417 +1980,10,3,M,5631 +1980,10,4,F,4636 +1980,10,4,M,4716 +1980,10,5,F,4379 +1980,10,5,M,4634 +1980,10,6,F,5086 +1980,10,6,M,5453 +1980,10,7,F,5100 +1980,10,7,M,5502 +1980,10,8,F,5402 +1980,10,8,M,5425 +1980,10,9,F,5187 +1980,10,9,M,5497 +1980,10,10,F,5260 +1980,10,10,M,5494 +1980,10,11,F,4422 +1980,10,11,M,4562 +1980,10,12,F,4191 +1980,10,12,M,4483 +1980,10,13,F,4952 +1980,10,13,M,5141 +1980,10,14,F,5074 +1980,10,14,M,5538 +1980,10,15,F,5143 +1980,10,15,M,5437 +1980,10,16,F,4976 +1980,10,16,M,5256 +1980,10,17,F,5091 +1980,10,17,M,5237 +1980,10,18,F,4298 +1980,10,18,M,4672 +1980,10,19,F,4237 +1980,10,19,M,4432 +1980,10,20,F,5044 +1980,10,20,M,5245 +1980,10,21,F,5108 +1980,10,21,M,5265 +1980,10,22,F,4936 +1980,10,22,M,5153 +1980,10,23,F,4937 +1980,10,23,M,5114 +1980,10,24,F,5003 +1980,10,24,M,5377 +1980,10,25,F,4362 +1980,10,25,M,4508 +1980,10,26,F,4547 +1980,10,26,M,4612 +1980,10,27,F,4862 +1980,10,27,M,5238 +1980,10,28,F,5039 +1980,10,28,M,5288 +1980,10,29,F,4863 +1980,10,29,M,5218 +1980,10,30,F,4751 +1980,10,30,M,5231 +1980,10,31,F,4996 +1980,10,31,M,5105 +1980,10,99,F,1 +1980,10,99,M,3 +1980,11,1,F,4321 +1980,11,1,M,4530 +1980,11,2,F,4229 +1980,11,2,M,4412 +1980,11,3,F,4992 +1980,11,3,M,5297 +1980,11,4,F,5110 +1980,11,4,M,5355 +1980,11,5,F,5102 +1980,11,5,M,5271 +1980,11,6,F,4964 +1980,11,6,M,5164 +1980,11,7,F,5197 +1980,11,7,M,5320 +1980,11,8,F,4394 +1980,11,8,M,4512 +1980,11,9,F,4302 +1980,11,9,M,4443 +1980,11,10,F,5010 +1980,11,10,M,5239 +1980,11,11,F,5013 +1980,11,11,M,5313 +1980,11,12,F,4879 +1980,11,12,M,5180 +1980,11,13,F,4855 +1980,11,13,M,5075 +1980,11,14,F,5175 +1980,11,14,M,5238 +1980,11,15,F,4393 +1980,11,15,M,4436 +1980,11,16,F,4077 +1980,11,16,M,4486 +1980,11,17,F,4897 +1980,11,17,M,5278 +1980,11,18,F,5074 +1980,11,18,M,5426 +1980,11,19,F,4967 +1980,11,19,M,5213 +1980,11,20,F,4889 +1980,11,20,M,5088 +1980,11,21,F,4973 +1980,11,21,M,5270 +1980,11,22,F,4194 +1980,11,22,M,4486 +1980,11,23,F,4185 +1980,11,23,M,4258 +1980,11,24,F,5086 +1980,11,24,M,5361 +1980,11,25,F,5102 +1980,11,25,M,5394 +1980,11,26,F,4907 +1980,11,26,M,5329 +1980,11,27,F,3982 +1980,11,27,M,4152 +1980,11,28,F,4775 +1980,11,28,M,4874 +1980,11,29,F,4223 +1980,11,29,M,4598 +1980,11,30,F,4303 +1980,11,30,M,4527 +1980,11,99,F,3 +1980,11,99,M,2 +1980,12,1,F,5075 +1980,12,1,M,5150 +1980,12,2,F,5207 +1980,12,2,M,5504 +1980,12,3,F,4872 +1980,12,3,M,5233 +1980,12,4,F,4855 +1980,12,4,M,5070 +1980,12,5,F,5086 +1980,12,5,M,5202 +1980,12,6,F,4191 +1980,12,6,M,4468 +1980,12,7,F,4162 +1980,12,7,M,4351 +1980,12,8,F,5029 +1980,12,8,M,5339 +1980,12,9,F,4947 +1980,12,9,M,5375 +1980,12,10,F,4999 +1980,12,10,M,5121 +1980,12,11,F,4956 +1980,12,11,M,5042 +1980,12,12,F,4918 +1980,12,12,M,5121 +1980,12,13,F,4330 +1980,12,13,M,4428 +1980,12,14,F,4290 +1980,12,14,M,4233 +1980,12,15,F,5169 +1980,12,15,M,5339 +1980,12,16,F,5198 +1980,12,16,M,5574 +1980,12,17,F,5065 +1980,12,17,M,5459 +1980,12,18,F,5045 +1980,12,18,M,5535 +1980,12,19,F,5145 +1980,12,19,M,5517 +1980,12,20,F,4190 +1980,12,20,M,4336 +1980,12,21,F,4043 +1980,12,21,M,4246 +1980,12,22,F,4864 +1980,12,22,M,5171 +1980,12,23,F,4973 +1980,12,23,M,5145 +1980,12,24,F,4329 +1980,12,24,M,4650 +1980,12,25,F,3897 +1980,12,25,M,4082 +1980,12,26,F,4556 +1980,12,26,M,4757 +1980,12,27,F,4334 +1980,12,27,M,4414 +1980,12,28,F,4229 +1980,12,28,M,4347 +1980,12,29,F,5266 +1980,12,29,M,5615 +1980,12,30,F,5645 +1980,12,30,M,5958 +1980,12,31,F,5361 +1980,12,31,M,5586 +1980,12,99,F,2 +1981,1,1,F,3952 +1981,1,1,M,4347 +1981,1,2,F,4492 +1981,1,2,M,4553 +1981,1,3,F,4102 +1981,1,3,M,4356 +1981,1,4,F,4097 +1981,1,4,M,4198 +1981,1,5,F,4655 +1981,1,5,M,4959 +1981,1,6,F,4846 +1981,1,6,M,5282 +1981,1,7,F,5001 +1981,1,7,M,5139 +1981,1,8,F,4587 +1981,1,8,M,4836 +1981,1,9,F,4730 +1981,1,9,M,4972 +1981,1,10,F,4218 +1981,1,10,M,4231 +1981,1,11,F,3960 +1981,1,11,M,4316 +1981,1,12,F,4787 +1981,1,12,M,5113 +1981,1,13,F,4962 +1981,1,13,M,5201 +1981,1,14,F,5127 +1981,1,14,M,5292 +1981,1,15,F,4992 +1981,1,15,M,5165 +1981,1,16,F,4886 +1981,1,16,M,5278 +1981,1,17,F,4159 +1981,1,17,M,4442 +1981,1,18,F,4140 +1981,1,18,M,4262 +1981,1,19,F,5029 +1981,1,19,M,5339 +1981,1,20,F,5028 +1981,1,20,M,5427 +1981,1,21,F,4965 +1981,1,21,M,5201 +1981,1,22,F,4827 +1981,1,22,M,5176 +1981,1,23,F,4963 +1981,1,23,M,5160 +1981,1,24,F,4324 +1981,1,24,M,4501 +1981,1,25,F,4103 +1981,1,25,M,4266 +1981,1,26,F,4941 +1981,1,26,M,5272 +1981,1,27,F,5037 +1981,1,27,M,5309 +1981,1,28,F,4949 +1981,1,28,M,5196 +1981,1,29,F,4898 +1981,1,29,M,4976 +1981,1,30,F,4977 +1981,1,30,M,5129 +1981,1,31,F,4161 +1981,1,31,M,4296 +1981,1,99,F,2 +1981,1,99,M,2 +1981,2,1,F,4187 +1981,2,1,M,4287 +1981,2,2,F,5058 +1981,2,2,M,5227 +1981,2,3,F,5001 +1981,2,3,M,5230 +1981,2,4,F,4927 +1981,2,4,M,5156 +1981,2,5,F,4870 +1981,2,5,M,5115 +1981,2,6,F,4954 +1981,2,6,M,5145 +1981,2,7,F,4190 +1981,2,7,M,4373 +1981,2,8,F,4151 +1981,2,8,M,4460 +1981,2,9,F,5098 +1981,2,9,M,5215 +1981,2,10,F,5122 +1981,2,10,M,5233 +1981,2,11,F,4946 +1981,2,11,M,5166 +1981,2,12,F,4976 +1981,2,12,M,5356 +1981,2,13,F,4834 +1981,2,13,M,4836 +1981,2,14,F,4443 +1981,2,14,M,4671 +1981,2,15,F,4212 +1981,2,15,M,4326 +1981,2,16,F,4829 +1981,2,16,M,4967 +1981,2,17,F,5161 +1981,2,17,M,5248 +1981,2,18,F,5063 +1981,2,18,M,5250 +1981,2,19,F,5035 +1981,2,19,M,5302 +1981,2,20,F,4973 +1981,2,20,M,5395 +1981,2,21,F,4367 +1981,2,21,M,4608 +1981,2,22,F,4275 +1981,2,22,M,4382 +1981,2,23,F,4879 +1981,2,23,M,5278 +1981,2,24,F,5127 +1981,2,24,M,5296 +1981,2,25,F,4984 +1981,2,25,M,5286 +1981,2,26,F,4920 +1981,2,26,M,5134 +1981,2,27,F,5144 +1981,2,27,M,5082 +1981,2,28,F,4359 +1981,2,28,M,4533 +1981,2,99,F,1 +1981,2,99,M,3 +1981,3,1,F,4175 +1981,3,1,M,4359 +1981,3,2,F,4923 +1981,3,2,M,5207 +1981,3,3,F,5067 +1981,3,3,M,5404 +1981,3,4,F,4974 +1981,3,4,M,5303 +1981,3,5,F,4920 +1981,3,5,M,5248 +1981,3,6,F,4883 +1981,3,6,M,5251 +1981,3,7,F,4325 +1981,3,7,M,4372 +1981,3,8,F,4107 +1981,3,8,M,4262 +1981,3,9,F,4905 +1981,3,9,M,5007 +1981,3,10,F,5030 +1981,3,10,M,5318 +1981,3,11,F,4914 +1981,3,11,M,5270 +1981,3,12,F,5011 +1981,3,12,M,5126 +1981,3,13,F,4799 +1981,3,13,M,5162 +1981,3,14,F,4321 +1981,3,14,M,4616 +1981,3,15,F,4102 +1981,3,15,M,4236 +1981,3,16,F,4951 +1981,3,16,M,5318 +1981,3,17,F,5294 +1981,3,17,M,5437 +1981,3,18,F,4860 +1981,3,18,M,5269 +1981,3,19,F,4935 +1981,3,19,M,5095 +1981,3,20,F,4916 +1981,3,20,M,5268 +1981,3,21,F,4214 +1981,3,21,M,4559 +1981,3,22,F,4083 +1981,3,22,M,4375 +1981,3,23,F,4897 +1981,3,23,M,5143 +1981,3,24,F,5100 +1981,3,24,M,5353 +1981,3,25,F,4883 +1981,3,25,M,5207 +1981,3,26,F,5010 +1981,3,26,M,5183 +1981,3,27,F,5112 +1981,3,27,M,5342 +1981,3,28,F,4291 +1981,3,28,M,4544 +1981,3,29,F,4190 +1981,3,29,M,4342 +1981,3,30,F,4842 +1981,3,30,M,5221 +1981,3,31,F,5031 +1981,3,31,M,5296 +1981,4,1,F,4795 +1981,4,1,M,5065 +1981,4,2,F,4928 +1981,4,2,M,5076 +1981,4,3,F,4888 +1981,4,3,M,5263 +1981,4,4,F,4344 +1981,4,4,M,4544 +1981,4,5,F,4069 +1981,4,5,M,4309 +1981,4,6,F,4781 +1981,4,6,M,5207 +1981,4,7,F,5059 +1981,4,7,M,5297 +1981,4,8,F,4897 +1981,4,8,M,5137 +1981,4,9,F,4811 +1981,4,9,M,5241 +1981,4,10,F,4916 +1981,4,10,M,5199 +1981,4,11,F,4264 +1981,4,11,M,4438 +1981,4,12,F,4067 +1981,4,12,M,4271 +1981,4,13,F,4770 +1981,4,13,M,4966 +1981,4,14,F,4975 +1981,4,14,M,5302 +1981,4,15,F,4903 +1981,4,15,M,5063 +1981,4,16,F,4664 +1981,4,16,M,4970 +1981,4,17,F,4759 +1981,4,17,M,4834 +1981,4,18,F,4226 +1981,4,18,M,4123 +1981,4,19,F,3943 +1981,4,19,M,4179 +1981,4,20,F,4629 +1981,4,20,M,4929 +1981,4,21,F,4900 +1981,4,21,M,5255 +1981,4,22,F,4785 +1981,4,22,M,5207 +1981,4,23,F,4901 +1981,4,23,M,5177 +1981,4,24,F,4899 +1981,4,24,M,5022 +1981,4,25,F,4124 +1981,4,25,M,4307 +1981,4,26,F,3818 +1981,4,26,M,3938 +1981,4,27,F,4589 +1981,4,27,M,5068 +1981,4,28,F,4903 +1981,4,28,M,5339 +1981,4,29,F,4786 +1981,4,29,M,5173 +1981,4,30,F,4762 +1981,4,30,M,4913 +1981,5,1,F,4895 +1981,5,1,M,5152 +1981,5,2,F,4014 +1981,5,2,M,4315 +1981,5,3,F,3905 +1981,5,3,M,4210 +1981,5,4,F,4818 +1981,5,4,M,4937 +1981,5,5,F,5149 +1981,5,5,M,5234 +1981,5,6,F,4858 +1981,5,6,M,5026 +1981,5,7,F,4811 +1981,5,7,M,5095 +1981,5,8,F,4957 +1981,5,8,M,5129 +1981,5,9,F,4079 +1981,5,9,M,4275 +1981,5,10,F,4231 +1981,5,10,M,4273 +1981,5,11,F,4939 +1981,5,11,M,5003 +1981,5,12,F,4969 +1981,5,12,M,5107 +1981,5,13,F,4885 +1981,5,13,M,5010 +1981,5,14,F,4958 +1981,5,14,M,5187 +1981,5,15,F,4864 +1981,5,15,M,5331 +1981,5,16,F,4163 +1981,5,16,M,4355 +1981,5,17,F,3956 +1981,5,17,M,4238 +1981,5,18,F,4890 +1981,5,18,M,5269 +1981,5,19,F,4973 +1981,5,19,M,5336 +1981,5,20,F,5029 +1981,5,20,M,5057 +1981,5,21,F,4949 +1981,5,21,M,5233 +1981,5,22,F,5143 +1981,5,22,M,5281 +1981,5,23,F,4184 +1981,5,23,M,4576 +1981,5,24,F,4095 +1981,5,24,M,4249 +1981,5,25,F,4430 +1981,5,25,M,4413 +1981,5,26,F,5074 +1981,5,26,M,5397 +1981,5,27,F,5158 +1981,5,27,M,5516 +1981,5,28,F,4919 +1981,5,28,M,5481 +1981,5,29,F,5073 +1981,5,29,M,5338 +1981,5,30,F,4284 +1981,5,30,M,4568 +1981,5,31,F,4229 +1981,5,31,M,4369 +1981,5,99,F,1 +1981,6,1,F,4936 +1981,6,1,M,5141 +1981,6,2,F,4996 +1981,6,2,M,5359 +1981,6,3,F,4883 +1981,6,3,M,5312 +1981,6,4,F,4922 +1981,6,4,M,5286 +1981,6,5,F,4985 +1981,6,5,M,5381 +1981,6,6,F,4371 +1981,6,6,M,4594 +1981,6,7,F,4196 +1981,6,7,M,4442 +1981,6,8,F,4950 +1981,6,8,M,5256 +1981,6,9,F,5118 +1981,6,9,M,5451 +1981,6,10,F,5129 +1981,6,10,M,5250 +1981,6,11,F,4975 +1981,6,11,M,5156 +1981,6,12,F,4857 +1981,6,12,M,5305 +1981,6,13,F,4274 +1981,6,13,M,4579 +1981,6,14,F,4086 +1981,6,14,M,4398 +1981,6,15,F,4933 +1981,6,15,M,5249 +1981,6,16,F,5184 +1981,6,16,M,5565 +1981,6,17,F,5014 +1981,6,17,M,5400 +1981,6,18,F,5001 +1981,6,18,M,5071 +1981,6,19,F,5180 +1981,6,19,M,5342 +1981,6,20,F,4402 +1981,6,20,M,4610 +1981,6,21,F,4264 +1981,6,21,M,4449 +1981,6,22,F,5102 +1981,6,22,M,5495 +1981,6,23,F,5104 +1981,6,23,M,5436 +1981,6,24,F,4908 +1981,6,24,M,5417 +1981,6,25,F,4992 +1981,6,25,M,5385 +1981,6,26,F,5188 +1981,6,26,M,5336 +1981,6,27,F,4149 +1981,6,27,M,4500 +1981,6,28,F,4215 +1981,6,28,M,4384 +1981,6,29,F,5154 +1981,6,29,M,5348 +1981,6,30,F,5375 +1981,6,30,M,5682 +1981,6,99,F,1 +1981,6,99,M,2 +1981,7,1,F,5143 +1981,7,1,M,5536 +1981,7,2,F,5238 +1981,7,2,M,5419 +1981,7,3,F,4663 +1981,7,3,M,5054 +1981,7,4,F,4373 +1981,7,4,M,4562 +1981,7,5,F,4372 +1981,7,5,M,4503 +1981,7,6,F,5109 +1981,7,6,M,5493 +1981,7,7,F,5478 +1981,7,7,M,6020 +1981,7,8,F,5551 +1981,7,8,M,5654 +1981,7,9,F,5384 +1981,7,9,M,5762 +1981,7,10,F,5494 +1981,7,10,M,5870 +1981,7,11,F,4624 +1981,7,11,M,4828 +1981,7,12,F,4363 +1981,7,12,M,4634 +1981,7,13,F,5094 +1981,7,13,M,5524 +1981,7,14,F,5617 +1981,7,14,M,5960 +1981,7,15,F,5435 +1981,7,15,M,5673 +1981,7,16,F,5375 +1981,7,16,M,5567 +1981,7,17,F,5426 +1981,7,17,M,5700 +1981,7,18,F,4534 +1981,7,18,M,4799 +1981,7,19,F,4418 +1981,7,19,M,4575 +1981,7,20,F,5366 +1981,7,20,M,5745 +1981,7,21,F,5542 +1981,7,21,M,5771 +1981,7,22,F,5442 +1981,7,22,M,5622 +1981,7,23,F,5281 +1981,7,23,M,5684 +1981,7,24,F,5334 +1981,7,24,M,5676 +1981,7,25,F,4556 +1981,7,25,M,4874 +1981,7,26,F,4632 +1981,7,26,M,4622 +1981,7,27,F,5330 +1981,7,27,M,5563 +1981,7,28,F,5577 +1981,7,28,M,5902 +1981,7,29,F,5430 +1981,7,29,M,5781 +1981,7,30,F,5323 +1981,7,30,M,5677 +1981,7,31,F,5356 +1981,7,31,M,5855 +1981,7,99,M,4 +1981,8,1,F,4806 +1981,8,1,M,4927 +1981,8,2,F,4522 +1981,8,2,M,4754 +1981,8,3,F,5468 +1981,8,3,M,5684 +1981,8,4,F,5640 +1981,8,4,M,6057 +1981,8,5,F,5617 +1981,8,5,M,5821 +1981,8,6,F,5396 +1981,8,6,M,5757 +1981,8,7,F,5448 +1981,8,7,M,5808 +1981,8,8,F,4857 +1981,8,8,M,4946 +1981,8,9,F,4599 +1981,8,9,M,4761 +1981,8,10,F,5360 +1981,8,10,M,5692 +1981,8,11,F,5529 +1981,8,11,M,5713 +1981,8,12,F,5518 +1981,8,12,M,5839 +1981,8,13,F,5417 +1981,8,13,M,5671 +1981,8,14,F,5586 +1981,8,14,M,5847 +1981,8,15,F,4750 +1981,8,15,M,4878 +1981,8,16,F,4595 +1981,8,16,M,4687 +1981,8,17,F,5316 +1981,8,17,M,5732 +1981,8,18,F,5571 +1981,8,18,M,5836 +1981,8,19,F,5418 +1981,8,19,M,5641 +1981,8,20,F,5349 +1981,8,20,M,5607 +1981,8,21,F,5290 +1981,8,21,M,5801 +1981,8,22,F,4810 +1981,8,22,M,4930 +1981,8,23,F,4530 +1981,8,23,M,4695 +1981,8,24,F,5394 +1981,8,24,M,5753 +1981,8,25,F,5479 +1981,8,25,M,5928 +1981,8,26,F,5434 +1981,8,26,M,5764 +1981,8,27,F,5462 +1981,8,27,M,5597 +1981,8,28,F,5411 +1981,8,28,M,5849 +1981,8,29,F,4712 +1981,8,29,M,4955 +1981,8,30,F,4548 +1981,8,30,M,4713 +1981,8,31,F,5348 +1981,8,31,M,5702 +1981,8,99,M,2 +1981,9,1,F,5469 +1981,9,1,M,5687 +1981,9,2,F,5322 +1981,9,2,M,5600 +1981,9,3,F,5349 +1981,9,3,M,5545 +1981,9,4,F,5443 +1981,9,4,M,5737 +1981,9,5,F,4581 +1981,9,5,M,4773 +1981,9,6,F,4388 +1981,9,6,M,4707 +1981,9,7,F,4461 +1981,9,7,M,4752 +1981,9,8,F,5436 +1981,9,8,M,5635 +1981,9,9,F,5577 +1981,9,9,M,5779 +1981,9,10,F,5529 +1981,9,10,M,5716 +1981,9,11,F,5534 +1981,9,11,M,5937 +1981,9,12,F,4757 +1981,9,12,M,4804 +1981,9,13,F,4636 +1981,9,13,M,4760 +1981,9,14,F,5471 +1981,9,14,M,5906 +1981,9,15,F,5514 +1981,9,15,M,5908 +1981,9,16,F,5602 +1981,9,16,M,5763 +1981,9,17,F,5405 +1981,9,17,M,5666 +1981,9,18,F,5428 +1981,9,18,M,5720 +1981,9,19,F,4605 +1981,9,19,M,5004 +1981,9,20,F,4598 +1981,9,20,M,4934 +1981,9,21,F,5568 +1981,9,21,M,5694 +1981,9,22,F,5570 +1981,9,22,M,5918 +1981,9,23,F,5467 +1981,9,23,M,5814 +1981,9,24,F,5558 +1981,9,24,M,5643 +1981,9,25,F,5626 +1981,9,25,M,5834 +1981,9,26,F,4855 +1981,9,26,M,4848 +1981,9,27,F,4583 +1981,9,27,M,4754 +1981,9,28,F,5577 +1981,9,28,M,5764 +1981,9,29,F,5368 +1981,9,29,M,5706 +1981,9,30,F,5401 +1981,9,30,M,5591 +1981,9,99,F,4 +1981,10,1,F,5263 +1981,10,1,M,5618 +1981,10,2,F,5298 +1981,10,2,M,5720 +1981,10,3,F,4529 +1981,10,3,M,4789 +1981,10,4,F,4314 +1981,10,4,M,4591 +1981,10,5,F,5459 +1981,10,5,M,5415 +1981,10,6,F,5409 +1981,10,6,M,5490 +1981,10,7,F,5150 +1981,10,7,M,5333 +1981,10,8,F,5130 +1981,10,8,M,5275 +1981,10,9,F,5253 +1981,10,9,M,5652 +1981,10,10,F,4582 +1981,10,10,M,4731 +1981,10,11,F,4215 +1981,10,11,M,4541 +1981,10,12,F,5025 +1981,10,12,M,5347 +1981,10,13,F,5130 +1981,10,13,M,5486 +1981,10,14,F,4948 +1981,10,14,M,5266 +1981,10,15,F,5177 +1981,10,15,M,5279 +1981,10,16,F,5187 +1981,10,16,M,5529 +1981,10,17,F,4140 +1981,10,17,M,4397 +1981,10,18,F,4058 +1981,10,18,M,4482 +1981,10,19,F,4813 +1981,10,19,M,5210 +1981,10,20,F,5045 +1981,10,20,M,5232 +1981,10,21,F,5167 +1981,10,21,M,5293 +1981,10,22,F,5016 +1981,10,22,M,5251 +1981,10,23,F,5059 +1981,10,23,M,5257 +1981,10,24,F,4257 +1981,10,24,M,4430 +1981,10,25,F,4276 +1981,10,25,M,4457 +1981,10,26,F,5007 +1981,10,26,M,5037 +1981,10,27,F,5048 +1981,10,27,M,5404 +1981,10,28,F,4901 +1981,10,28,M,5252 +1981,10,29,F,4974 +1981,10,29,M,5137 +1981,10,30,F,5056 +1981,10,30,M,5246 +1981,10,31,F,4248 +1981,10,31,M,4395 +1981,10,99,F,4 +1981,10,99,M,1 +1981,11,1,F,4066 +1981,11,1,M,4292 +1981,11,2,F,5042 +1981,11,2,M,5261 +1981,11,3,F,5105 +1981,11,3,M,5292 +1981,11,4,F,5020 +1981,11,4,M,5330 +1981,11,5,F,5039 +1981,11,5,M,5276 +1981,11,6,F,5201 +1981,11,6,M,5477 +1981,11,7,F,4370 +1981,11,7,M,4427 +1981,11,8,F,4137 +1981,11,8,M,4334 +1981,11,9,F,4981 +1981,11,9,M,5174 +1981,11,10,F,5113 +1981,11,10,M,5243 +1981,11,11,F,4808 +1981,11,11,M,5242 +1981,11,12,F,5011 +1981,11,12,M,5245 +1981,11,13,F,4768 +1981,11,13,M,5151 +1981,11,14,F,4256 +1981,11,14,M,4540 +1981,11,15,F,4198 +1981,11,15,M,4301 +1981,11,16,F,5041 +1981,11,16,M,5412 +1981,11,17,F,5413 +1981,11,17,M,5352 +1981,11,18,F,4898 +1981,11,18,M,5246 +1981,11,19,F,4893 +1981,11,19,M,5331 +1981,11,20,F,5097 +1981,11,20,M,5477 +1981,11,21,F,4250 +1981,11,21,M,4496 +1981,11,22,F,4124 +1981,11,22,M,4303 +1981,11,23,F,5015 +1981,11,23,M,5320 +1981,11,24,F,5065 +1981,11,24,M,5463 +1981,11,25,F,5089 +1981,11,25,M,5363 +1981,11,26,F,3907 +1981,11,26,M,4247 +1981,11,27,F,4650 +1981,11,27,M,5000 +1981,11,28,F,4109 +1981,11,28,M,4378 +1981,11,29,F,4168 +1981,11,29,M,4360 +1981,11,30,F,4983 +1981,11,30,M,5234 +1981,11,99,F,2 +1981,11,99,M,4 +1981,12,1,F,5366 +1981,12,1,M,5514 +1981,12,2,F,5029 +1981,12,2,M,5427 +1981,12,3,F,4938 +1981,12,3,M,5175 +1981,12,4,F,4839 +1981,12,4,M,5081 +1981,12,5,F,4191 +1981,12,5,M,4369 +1981,12,6,F,4023 +1981,12,6,M,4293 +1981,12,7,F,5007 +1981,12,7,M,5083 +1981,12,8,F,5247 +1981,12,8,M,5374 +1981,12,9,F,4921 +1981,12,9,M,5191 +1981,12,10,F,4825 +1981,12,10,M,5110 +1981,12,11,F,4919 +1981,12,11,M,5245 +1981,12,12,F,4166 +1981,12,12,M,4469 +1981,12,13,F,4037 +1981,12,13,M,4247 +1981,12,14,F,5174 +1981,12,14,M,5191 +1981,12,15,F,5233 +1981,12,15,M,5566 +1981,12,16,F,5143 +1981,12,16,M,5190 +1981,12,17,F,5150 +1981,12,17,M,5522 +1981,12,18,F,5242 +1981,12,18,M,5611 +1981,12,19,F,4272 +1981,12,19,M,4392 +1981,12,20,F,3952 +1981,12,20,M,4221 +1981,12,21,F,5074 +1981,12,21,M,5488 +1981,12,22,F,5098 +1981,12,22,M,5469 +1981,12,23,F,4775 +1981,12,23,M,5066 +1981,12,24,F,4230 +1981,12,24,M,4511 +1981,12,25,F,3871 +1981,12,25,M,4152 +1981,12,26,F,4065 +1981,12,26,M,4287 +1981,12,27,F,4229 +1981,12,27,M,4424 +1981,12,28,F,5356 +1981,12,28,M,5689 +1981,12,29,F,5476 +1981,12,29,M,5803 +1981,12,30,F,5388 +1981,12,30,M,5648 +1981,12,31,F,5193 +1981,12,31,M,5201 +1981,12,99,M,2 +1982,1,1,F,4254 +1982,1,1,M,4306 +1982,1,2,F,4063 +1982,1,2,M,4245 +1982,1,3,F,4279 +1982,1,3,M,4327 +1982,1,4,F,4838 +1982,1,4,M,5120 +1982,1,5,F,5047 +1982,1,5,M,5206 +1982,1,6,F,4956 +1982,1,6,M,5226 +1982,1,7,F,4936 +1982,1,7,M,5013 +1982,1,8,F,4885 +1982,1,8,M,5202 +1982,1,9,F,4276 +1982,1,9,M,4208 +1982,1,10,F,4109 +1982,1,10,M,4278 +1982,1,11,F,4890 +1982,1,11,M,5336 +1982,1,12,F,5124 +1982,1,12,M,5328 +1982,1,13,F,5063 +1982,1,13,M,5119 +1982,1,14,F,4867 +1982,1,14,M,5233 +1982,1,15,F,4897 +1982,1,15,M,5072 +1982,1,16,F,4258 +1982,1,16,M,4447 +1982,1,17,F,4186 +1982,1,17,M,4336 +1982,1,18,F,5038 +1982,1,18,M,5215 +1982,1,19,F,5210 +1982,1,19,M,5498 +1982,1,20,F,5034 +1982,1,20,M,5300 +1982,1,21,F,4937 +1982,1,21,M,5239 +1982,1,22,F,5062 +1982,1,22,M,5363 +1982,1,23,F,4234 +1982,1,23,M,4472 +1982,1,24,F,4139 +1982,1,24,M,4393 +1982,1,25,F,5061 +1982,1,25,M,5171 +1982,1,26,F,5016 +1982,1,26,M,5441 +1982,1,27,F,4983 +1982,1,27,M,5173 +1982,1,28,F,4946 +1982,1,28,M,5167 +1982,1,29,F,4950 +1982,1,29,M,5257 +1982,1,30,F,4315 +1982,1,30,M,4411 +1982,1,31,F,4180 +1982,1,31,M,4200 +1982,1,99,M,6 +1982,2,1,F,5052 +1982,2,1,M,5170 +1982,2,2,F,4983 +1982,2,2,M,5443 +1982,2,3,F,4922 +1982,2,3,M,5247 +1982,2,4,F,4850 +1982,2,4,M,5220 +1982,2,5,F,5024 +1982,2,5,M,5218 +1982,2,6,F,4358 +1982,2,6,M,4446 +1982,2,7,F,4149 +1982,2,7,M,4365 +1982,2,8,F,4965 +1982,2,8,M,5397 +1982,2,9,F,5135 +1982,2,9,M,5416 +1982,2,10,F,5098 +1982,2,10,M,5326 +1982,2,11,F,5042 +1982,2,11,M,5360 +1982,2,12,F,5090 +1982,2,12,M,5384 +1982,2,13,F,4394 +1982,2,13,M,4569 +1982,2,14,F,4240 +1982,2,14,M,4440 +1982,2,15,F,4943 +1982,2,15,M,5263 +1982,2,16,F,5231 +1982,2,16,M,5506 +1982,2,17,F,5171 +1982,2,17,M,5361 +1982,2,18,F,5026 +1982,2,18,M,5340 +1982,2,19,F,5082 +1982,2,19,M,5293 +1982,2,20,F,4374 +1982,2,20,M,4549 +1982,2,21,F,4231 +1982,2,21,M,4478 +1982,2,22,F,5160 +1982,2,22,M,5417 +1982,2,23,F,5047 +1982,2,23,M,5427 +1982,2,24,F,5186 +1982,2,24,M,5276 +1982,2,25,F,5060 +1982,2,25,M,5307 +1982,2,26,F,4868 +1982,2,26,M,5179 +1982,2,27,F,4394 +1982,2,27,M,4458 +1982,2,28,F,4062 +1982,2,28,M,4322 +1982,2,99,F,2 +1982,2,99,M,4 +1982,3,1,F,5109 +1982,3,1,M,5148 +1982,3,2,F,5186 +1982,3,2,M,5493 +1982,3,3,F,5134 +1982,3,3,M,5372 +1982,3,4,F,4926 +1982,3,4,M,5279 +1982,3,5,F,5131 +1982,3,5,M,5345 +1982,3,6,F,4264 +1982,3,6,M,4504 +1982,3,7,F,4075 +1982,3,7,M,4324 +1982,3,8,F,5067 +1982,3,8,M,5226 +1982,3,9,F,5168 +1982,3,9,M,5249 +1982,3,10,F,5075 +1982,3,10,M,5349 +1982,3,11,F,5035 +1982,3,11,M,5198 +1982,3,12,F,5118 +1982,3,12,M,5251 +1982,3,13,F,4479 +1982,3,13,M,4487 +1982,3,14,F,4031 +1982,3,14,M,4371 +1982,3,15,F,4810 +1982,3,15,M,5288 +1982,3,16,F,5036 +1982,3,16,M,5511 +1982,3,17,F,5209 +1982,3,17,M,5374 +1982,3,18,F,5126 +1982,3,18,M,5321 +1982,3,19,F,4915 +1982,3,19,M,5344 +1982,3,20,F,4259 +1982,3,20,M,4446 +1982,3,21,F,4041 +1982,3,21,M,4272 +1982,3,22,F,4924 +1982,3,22,M,5132 +1982,3,23,F,4905 +1982,3,23,M,5433 +1982,3,24,F,4965 +1982,3,24,M,5245 +1982,3,25,F,4998 +1982,3,25,M,5240 +1982,3,26,F,5004 +1982,3,26,M,5276 +1982,3,27,F,4215 +1982,3,27,M,4353 +1982,3,28,F,4044 +1982,3,28,M,4170 +1982,3,29,F,4947 +1982,3,29,M,5269 +1982,3,30,F,5114 +1982,3,30,M,5367 +1982,3,31,F,4953 +1982,3,31,M,5318 +1982,3,99,F,4 +1982,3,99,M,4 +1982,4,1,F,4988 +1982,4,1,M,5130 +1982,4,2,F,5132 +1982,4,2,M,5396 +1982,4,3,F,4476 +1982,4,3,M,4479 +1982,4,4,F,4139 +1982,4,4,M,4293 +1982,4,5,F,4894 +1982,4,5,M,5234 +1982,4,6,F,5123 +1982,4,6,M,5497 +1982,4,7,F,4817 +1982,4,7,M,5157 +1982,4,8,F,4900 +1982,4,8,M,5258 +1982,4,9,F,4942 +1982,4,9,M,5157 +1982,4,10,F,4108 +1982,4,10,M,4277 +1982,4,11,F,3882 +1982,4,11,M,4032 +1982,4,12,F,4844 +1982,4,12,M,5147 +1982,4,13,F,5037 +1982,4,13,M,5313 +1982,4,14,F,5038 +1982,4,14,M,5195 +1982,4,15,F,5055 +1982,4,15,M,5240 +1982,4,16,F,4962 +1982,4,16,M,5313 +1982,4,17,F,4298 +1982,4,17,M,4554 +1982,4,18,F,3983 +1982,4,18,M,4212 +1982,4,19,F,4878 +1982,4,19,M,5294 +1982,4,20,F,5100 +1982,4,20,M,5503 +1982,4,21,F,4964 +1982,4,21,M,5066 +1982,4,22,F,4858 +1982,4,22,M,5100 +1982,4,23,F,4906 +1982,4,23,M,5105 +1982,4,24,F,4112 +1982,4,24,M,4382 +1982,4,25,F,3844 +1982,4,25,M,4011 +1982,4,26,F,5007 +1982,4,26,M,5362 +1982,4,27,F,5115 +1982,4,27,M,5155 +1982,4,28,F,5011 +1982,4,28,M,5251 +1982,4,29,F,4761 +1982,4,29,M,5150 +1982,4,30,F,5063 +1982,4,30,M,5333 +1982,4,99,F,5 +1982,4,99,M,8 +1982,5,1,F,4264 +1982,5,1,M,4456 +1982,5,2,F,4085 +1982,5,2,M,4322 +1982,5,3,F,5076 +1982,5,3,M,5366 +1982,5,4,F,5191 +1982,5,4,M,5441 +1982,5,5,F,5117 +1982,5,5,M,5292 +1982,5,6,F,5000 +1982,5,6,M,5372 +1982,5,7,F,5049 +1982,5,7,M,5445 +1982,5,8,F,4356 +1982,5,8,M,4399 +1982,5,9,F,4173 +1982,5,9,M,4419 +1982,5,10,F,5157 +1982,5,10,M,5459 +1982,5,11,F,5119 +1982,5,11,M,5584 +1982,5,12,F,5102 +1982,5,12,M,5326 +1982,5,13,F,4962 +1982,5,13,M,5169 +1982,5,14,F,5117 +1982,5,14,M,5380 +1982,5,15,F,4342 +1982,5,15,M,4525 +1982,5,16,F,4092 +1982,5,16,M,4327 +1982,5,17,F,5120 +1982,5,17,M,5451 +1982,5,18,F,5273 +1982,5,18,M,5527 +1982,5,19,F,5093 +1982,5,19,M,5424 +1982,5,20,F,5024 +1982,5,20,M,5376 +1982,5,21,F,5131 +1982,5,21,M,5414 +1982,5,22,F,4089 +1982,5,22,M,4516 +1982,5,23,F,4151 +1982,5,23,M,4309 +1982,5,24,F,5090 +1982,5,24,M,5316 +1982,5,25,F,5337 +1982,5,25,M,5595 +1982,5,26,F,5341 +1982,5,26,M,5513 +1982,5,27,F,5284 +1982,5,27,M,5424 +1982,5,28,F,5180 +1982,5,28,M,5503 +1982,5,29,F,4366 +1982,5,29,M,4599 +1982,5,30,F,4252 +1982,5,30,M,4467 +1982,5,31,F,4337 +1982,5,31,M,4660 +1982,6,1,F,5106 +1982,6,1,M,5392 +1982,6,2,F,5135 +1982,6,2,M,5577 +1982,6,3,F,5179 +1982,6,3,M,5456 +1982,6,4,F,5248 +1982,6,4,M,5343 +1982,6,5,F,4306 +1982,6,5,M,4614 +1982,6,6,F,4277 +1982,6,6,M,4488 +1982,6,7,F,5117 +1982,6,7,M,5266 +1982,6,8,F,5187 +1982,6,8,M,5449 +1982,6,9,F,5080 +1982,6,9,M,5273 +1982,6,10,F,5131 +1982,6,10,M,5572 +1982,6,11,F,5159 +1982,6,11,M,5470 +1982,6,12,F,4320 +1982,6,12,M,4805 +1982,6,13,F,4189 +1982,6,13,M,4319 +1982,6,14,F,5029 +1982,6,14,M,5453 +1982,6,15,F,5346 +1982,6,15,M,5597 +1982,6,16,F,5406 +1982,6,16,M,5547 +1982,6,17,F,5265 +1982,6,17,M,5466 +1982,6,18,F,5288 +1982,6,18,M,5592 +1982,6,19,F,4394 +1982,6,19,M,4554 +1982,6,20,F,4364 +1982,6,20,M,4434 +1982,6,21,F,5122 +1982,6,21,M,5664 +1982,6,22,F,5346 +1982,6,22,M,5704 +1982,6,23,F,5184 +1982,6,23,M,5574 +1982,6,24,F,5406 +1982,6,24,M,5433 +1982,6,25,F,5492 +1982,6,25,M,5524 +1982,6,26,F,4560 +1982,6,26,M,4656 +1982,6,27,F,4361 +1982,6,27,M,4621 +1982,6,28,F,5417 +1982,6,28,M,5561 +1982,6,29,F,5514 +1982,6,29,M,5942 +1982,6,30,F,5303 +1982,6,30,M,5659 +1982,6,99,M,2 +1982,7,1,F,5394 +1982,7,1,M,5530 +1982,7,2,F,5287 +1982,7,2,M,5606 +1982,7,3,F,4444 +1982,7,3,M,4849 +1982,7,4,F,4387 +1982,7,4,M,4649 +1982,7,5,F,4445 +1982,7,5,M,4533 +1982,7,6,F,5492 +1982,7,6,M,5679 +1982,7,7,F,5761 +1982,7,7,M,5981 +1982,7,8,F,5691 +1982,7,8,M,5963 +1982,7,9,F,5511 +1982,7,9,M,5801 +1982,7,10,F,4760 +1982,7,10,M,4831 +1982,7,11,F,4476 +1982,7,11,M,4689 +1982,7,12,F,5216 +1982,7,12,M,5780 +1982,7,13,F,5318 +1982,7,13,M,5818 +1982,7,14,F,5427 +1982,7,14,M,5668 +1982,7,15,F,5392 +1982,7,15,M,5826 +1982,7,16,F,5504 +1982,7,16,M,5835 +1982,7,17,F,4686 +1982,7,17,M,4930 +1982,7,18,F,4633 +1982,7,18,M,4741 +1982,7,19,F,5405 +1982,7,19,M,5634 +1982,7,20,F,5755 +1982,7,20,M,5796 +1982,7,21,F,5506 +1982,7,21,M,5736 +1982,7,22,F,5436 +1982,7,22,M,5568 +1982,7,23,F,5496 +1982,7,23,M,5851 +1982,7,24,F,4612 +1982,7,24,M,4996 +1982,7,25,F,4447 +1982,7,25,M,4649 +1982,7,26,F,5400 +1982,7,26,M,5641 +1982,7,27,F,5518 +1982,7,27,M,5956 +1982,7,28,F,5450 +1982,7,28,M,5785 +1982,7,29,F,5444 +1982,7,29,M,5629 +1982,7,30,F,5406 +1982,7,30,M,5710 +1982,7,31,F,4587 +1982,7,31,M,5002 +1982,7,99,F,6 +1982,7,99,M,6 +1982,8,1,F,4479 +1982,8,1,M,4714 +1982,8,2,F,5282 +1982,8,2,M,5674 +1982,8,3,F,5618 +1982,8,3,M,5897 +1982,8,4,F,5459 +1982,8,4,M,5794 +1982,8,5,F,5504 +1982,8,5,M,5692 +1982,8,6,F,5422 +1982,8,6,M,5592 +1982,8,7,F,4582 +1982,8,7,M,4767 +1982,8,8,F,4559 +1982,8,8,M,4639 +1982,8,9,F,5250 +1982,8,9,M,5653 +1982,8,10,F,5474 +1982,8,10,M,5909 +1982,8,11,F,5398 +1982,8,11,M,5706 +1982,8,12,F,5465 +1982,8,12,M,5730 +1982,8,13,F,5205 +1982,8,13,M,5473 +1982,8,14,F,4630 +1982,8,14,M,4814 +1982,8,15,F,4527 +1982,8,15,M,4688 +1982,8,16,F,5581 +1982,8,16,M,5620 +1982,8,17,F,5601 +1982,8,17,M,5888 +1982,8,18,F,5439 +1982,8,18,M,5827 +1982,8,19,F,5499 +1982,8,19,M,5655 +1982,8,20,F,5554 +1982,8,20,M,5831 +1982,8,21,F,4661 +1982,8,21,M,4941 +1982,8,22,F,4461 +1982,8,22,M,4637 +1982,8,23,F,5320 +1982,8,23,M,5624 +1982,8,24,F,5587 +1982,8,24,M,5990 +1982,8,25,F,5380 +1982,8,25,M,5773 +1982,8,26,F,5458 +1982,8,26,M,5568 +1982,8,27,F,5531 +1982,8,27,M,5735 +1982,8,28,F,4610 +1982,8,28,M,4874 +1982,8,29,F,4390 +1982,8,29,M,4510 +1982,8,30,F,5306 +1982,8,30,M,5657 +1982,8,31,F,5680 +1982,8,31,M,5825 +1982,8,99,F,2 +1982,8,99,M,9 +1982,9,1,F,5380 +1982,9,1,M,5738 +1982,9,2,F,5338 +1982,9,2,M,5743 +1982,9,3,F,5532 +1982,9,3,M,5943 +1982,9,4,F,4689 +1982,9,4,M,4890 +1982,9,5,F,4435 +1982,9,5,M,4723 +1982,9,6,F,4472 +1982,9,6,M,4790 +1982,9,7,F,5531 +1982,9,7,M,5811 +1982,9,8,F,5457 +1982,9,8,M,5998 +1982,9,9,F,5705 +1982,9,9,M,5690 +1982,9,10,F,5830 +1982,9,10,M,6087 +1982,9,11,F,4665 +1982,9,11,M,5030 +1982,9,12,F,4717 +1982,9,12,M,4943 +1982,9,13,F,5463 +1982,9,13,M,5880 +1982,9,14,F,5628 +1982,9,14,M,6097 +1982,9,15,F,5593 +1982,9,15,M,5766 +1982,9,16,F,5612 +1982,9,16,M,5966 +1982,9,17,F,5872 +1982,9,17,M,5864 +1982,9,18,F,4705 +1982,9,18,M,4872 +1982,9,19,F,4639 +1982,9,19,M,4913 +1982,9,20,F,5674 +1982,9,20,M,5922 +1982,9,21,F,5666 +1982,9,21,M,6125 +1982,9,22,F,5531 +1982,9,22,M,5999 +1982,9,23,F,5659 +1982,9,23,M,5892 +1982,9,24,F,5596 +1982,9,24,M,5884 +1982,9,25,F,4715 +1982,9,25,M,5077 +1982,9,26,F,4560 +1982,9,26,M,4865 +1982,9,27,F,5664 +1982,9,27,M,5874 +1982,9,28,F,5674 +1982,9,28,M,6000 +1982,9,29,F,5665 +1982,9,29,M,5737 +1982,9,30,F,5486 +1982,9,30,M,5783 +1982,9,99,F,7 +1982,10,1,F,5567 +1982,10,1,M,5730 +1982,10,2,F,4726 +1982,10,2,M,4877 +1982,10,3,F,4481 +1982,10,3,M,4707 +1982,10,4,F,5405 +1982,10,4,M,5771 +1982,10,5,F,5450 +1982,10,5,M,5755 +1982,10,6,F,5383 +1982,10,6,M,5760 +1982,10,7,F,5297 +1982,10,7,M,5648 +1982,10,8,F,5379 +1982,10,8,M,5686 +1982,10,9,F,4543 +1982,10,9,M,4653 +1982,10,10,F,4323 +1982,10,10,M,4556 +1982,10,11,F,5027 +1982,10,11,M,5300 +1982,10,12,F,5285 +1982,10,12,M,5704 +1982,10,13,F,5031 +1982,10,13,M,5285 +1982,10,14,F,5272 +1982,10,14,M,5450 +1982,10,15,F,5073 +1982,10,15,M,5437 +1982,10,16,F,4393 +1982,10,16,M,4501 +1982,10,17,F,4219 +1982,10,17,M,4363 +1982,10,18,F,5086 +1982,10,18,M,5331 +1982,10,19,F,5319 +1982,10,19,M,5626 +1982,10,20,F,5041 +1982,10,20,M,5399 +1982,10,21,F,5114 +1982,10,21,M,5330 +1982,10,22,F,5110 +1982,10,22,M,5322 +1982,10,23,F,4311 +1982,10,23,M,4669 +1982,10,24,F,4198 +1982,10,24,M,4457 +1982,10,25,F,5070 +1982,10,25,M,5271 +1982,10,26,F,5297 +1982,10,26,M,5384 +1982,10,27,F,5093 +1982,10,27,M,5279 +1982,10,28,F,5204 +1982,10,28,M,5299 +1982,10,29,F,5132 +1982,10,29,M,5382 +1982,10,30,F,4234 +1982,10,30,M,4489 +1982,10,31,F,4413 +1982,10,31,M,4463 +1982,10,99,F,4 +1982,10,99,M,4 +1982,11,1,F,5085 +1982,11,1,M,5265 +1982,11,2,F,5214 +1982,11,2,M,5453 +1982,11,3,F,4999 +1982,11,3,M,5266 +1982,11,4,F,5069 +1982,11,4,M,5380 +1982,11,5,F,5041 +1982,11,5,M,5400 +1982,11,6,F,4251 +1982,11,6,M,4507 +1982,11,7,F,4222 +1982,11,7,M,4435 +1982,11,8,F,5175 +1982,11,8,M,5254 +1982,11,9,F,5213 +1982,11,9,M,5524 +1982,11,10,F,5154 +1982,11,10,M,5424 +1982,11,11,F,5092 +1982,11,11,M,5326 +1982,11,12,F,5219 +1982,11,12,M,5271 +1982,11,13,F,4342 +1982,11,13,M,4484 +1982,11,14,F,4033 +1982,11,14,M,4075 +1982,11,15,F,4983 +1982,11,15,M,5431 +1982,11,16,F,5015 +1982,11,16,M,5427 +1982,11,17,F,5076 +1982,11,17,M,5285 +1982,11,18,F,5079 +1982,11,18,M,5315 +1982,11,19,F,5156 +1982,11,19,M,5465 +1982,11,20,F,4357 +1982,11,20,M,4543 +1982,11,21,F,4100 +1982,11,21,M,4334 +1982,11,22,F,5177 +1982,11,22,M,5304 +1982,11,23,F,5182 +1982,11,23,M,5628 +1982,11,24,F,4945 +1982,11,24,M,5362 +1982,11,25,F,3918 +1982,11,25,M,4127 +1982,11,26,F,4480 +1982,11,26,M,4759 +1982,11,27,F,4202 +1982,11,27,M,4356 +1982,11,28,F,4177 +1982,11,28,M,4351 +1982,11,29,F,5085 +1982,11,29,M,5455 +1982,11,30,F,5461 +1982,11,30,M,5613 +1982,11,99,F,2 +1982,11,99,M,4 +1982,12,1,F,5136 +1982,12,1,M,5296 +1982,12,2,F,4938 +1982,12,2,M,5118 +1982,12,3,F,5007 +1982,12,3,M,5132 +1982,12,4,F,4144 +1982,12,4,M,4451 +1982,12,5,F,4249 +1982,12,5,M,4195 +1982,12,6,F,4943 +1982,12,6,M,5123 +1982,12,7,F,4940 +1982,12,7,M,5287 +1982,12,8,F,4904 +1982,12,8,M,5209 +1982,12,9,F,4833 +1982,12,9,M,5087 +1982,12,10,F,4870 +1982,12,10,M,5146 +1982,12,11,F,4101 +1982,12,11,M,4504 +1982,12,12,F,4013 +1982,12,12,M,4262 +1982,12,13,F,4891 +1982,12,13,M,5124 +1982,12,14,F,5213 +1982,12,14,M,5487 +1982,12,15,F,5045 +1982,12,15,M,5343 +1982,12,16,F,5181 +1982,12,16,M,5451 +1982,12,17,F,5098 +1982,12,17,M,5521 +1982,12,18,F,4220 +1982,12,18,M,4282 +1982,12,19,F,4113 +1982,12,19,M,4281 +1982,12,20,F,5226 +1982,12,20,M,5455 +1982,12,21,F,5146 +1982,12,21,M,5515 +1982,12,22,F,4723 +1982,12,22,M,5178 +1982,12,23,F,4552 +1982,12,23,M,4698 +1982,12,24,F,4076 +1982,12,24,M,4110 +1982,12,25,F,3865 +1982,12,25,M,3960 +1982,12,26,F,4017 +1982,12,26,M,3936 +1982,12,27,F,4971 +1982,12,27,M,5256 +1982,12,28,F,5187 +1982,12,28,M,5486 +1982,12,29,F,5255 +1982,12,29,M,5586 +1982,12,30,F,5373 +1982,12,30,M,5513 +1982,12,31,F,4477 +1982,12,31,M,4800 +1982,12,99,M,4 +1983,1,1,F,4000 +1983,1,1,M,4174 +1983,1,2,F,3924 +1983,1,2,M,4161 +1983,1,3,F,4706 +1983,1,3,M,4817 +1983,1,4,F,4937 +1983,1,4,M,5157 +1983,1,5,F,4893 +1983,1,5,M,5073 +1983,1,6,F,4905 +1983,1,6,M,5085 +1983,1,7,F,4862 +1983,1,7,M,5085 +1983,1,8,F,4105 +1983,1,8,M,4420 +1983,1,9,F,4090 +1983,1,9,M,4197 +1983,1,10,F,4805 +1983,1,10,M,5125 +1983,1,11,F,4935 +1983,1,11,M,5402 +1983,1,12,F,4971 +1983,1,12,M,5302 +1983,1,13,F,4939 +1983,1,13,M,5251 +1983,1,14,F,5006 +1983,1,14,M,5238 +1983,1,15,F,4180 +1983,1,15,M,4604 +1983,1,16,F,4071 +1983,1,16,M,4311 +1983,1,17,F,5003 +1983,1,17,M,5128 +1983,1,18,F,5073 +1983,1,18,M,5409 +1983,1,19,F,4829 +1983,1,19,M,5121 +1983,1,20,F,4951 +1983,1,20,M,5203 +1983,1,21,F,5006 +1983,1,21,M,5300 +1983,1,22,F,4334 +1983,1,22,M,4464 +1983,1,23,F,4208 +1983,1,23,M,4392 +1983,1,24,F,4954 +1983,1,24,M,5137 +1983,1,25,F,5135 +1983,1,25,M,5233 +1983,1,26,F,4914 +1983,1,26,M,5358 +1983,1,27,F,5082 +1983,1,27,M,5220 +1983,1,28,F,4928 +1983,1,28,M,5282 +1983,1,29,F,4391 +1983,1,29,M,4366 +1983,1,30,F,4244 +1983,1,30,M,4467 +1983,1,31,F,5034 +1983,1,31,M,5114 +1983,1,99,F,5 +1983,1,99,M,2 +1983,2,1,F,5177 +1983,2,1,M,5402 +1983,2,2,F,4939 +1983,2,2,M,5221 +1983,2,3,F,5055 +1983,2,3,M,5341 +1983,2,4,F,4937 +1983,2,4,M,5259 +1983,2,5,F,4212 +1983,2,5,M,4267 +1983,2,6,F,4061 +1983,2,6,M,4434 +1983,2,7,F,4973 +1983,2,7,M,5259 +1983,2,8,F,5181 +1983,2,8,M,5422 +1983,2,9,F,4859 +1983,2,9,M,5268 +1983,2,10,F,5027 +1983,2,10,M,5234 +1983,2,11,F,5192 +1983,2,11,M,5309 +1983,2,12,F,4450 +1983,2,12,M,4379 +1983,2,13,F,4217 +1983,2,13,M,4341 +1983,2,14,F,5208 +1983,2,14,M,5390 +1983,2,15,F,5080 +1983,2,15,M,5492 +1983,2,16,F,5181 +1983,2,16,M,5338 +1983,2,17,F,5087 +1983,2,17,M,5422 +1983,2,18,F,5102 +1983,2,18,M,5325 +1983,2,19,F,4294 +1983,2,19,M,4552 +1983,2,20,F,4181 +1983,2,20,M,4341 +1983,2,21,F,4839 +1983,2,21,M,5181 +1983,2,22,F,5154 +1983,2,22,M,5317 +1983,2,23,F,5272 +1983,2,23,M,5404 +1983,2,24,F,5175 +1983,2,24,M,5435 +1983,2,25,F,5122 +1983,2,25,M,5376 +1983,2,26,F,4249 +1983,2,26,M,4620 +1983,2,27,F,4132 +1983,2,27,M,4328 +1983,2,28,F,4936 +1983,2,28,M,5318 +1983,2,99,F,3 +1983,2,99,M,6 +1983,3,1,F,5275 +1983,3,1,M,5394 +1983,3,2,F,5095 +1983,3,2,M,5579 +1983,3,3,F,5256 +1983,3,3,M,5306 +1983,3,4,F,5187 +1983,3,4,M,5458 +1983,3,5,F,4361 +1983,3,5,M,4674 +1983,3,6,F,4194 +1983,3,6,M,4376 +1983,3,7,F,5012 +1983,3,7,M,5341 +1983,3,8,F,5267 +1983,3,8,M,5435 +1983,3,9,F,5040 +1983,3,9,M,5282 +1983,3,10,F,5051 +1983,3,10,M,5297 +1983,3,11,F,5154 +1983,3,11,M,5197 +1983,3,12,F,4197 +1983,3,12,M,4557 +1983,3,13,F,4135 +1983,3,13,M,4297 +1983,3,14,F,5039 +1983,3,14,M,5395 +1983,3,15,F,5242 +1983,3,15,M,5597 +1983,3,16,F,4926 +1983,3,16,M,5325 +1983,3,17,F,5129 +1983,3,17,M,5395 +1983,3,18,F,5288 +1983,3,18,M,5539 +1983,3,19,F,4309 +1983,3,19,M,4467 +1983,3,20,F,4117 +1983,3,20,M,4407 +1983,3,21,F,5109 +1983,3,21,M,5324 +1983,3,22,F,5270 +1983,3,22,M,5422 +1983,3,23,F,4929 +1983,3,23,M,5301 +1983,3,24,F,5065 +1983,3,24,M,5150 +1983,3,25,F,5146 +1983,3,25,M,5366 +1983,3,26,F,4249 +1983,3,26,M,4487 +1983,3,27,F,4110 +1983,3,27,M,4365 +1983,3,28,F,4929 +1983,3,28,M,5322 +1983,3,29,F,5255 +1983,3,29,M,5556 +1983,3,30,F,5265 +1983,3,30,M,5384 +1983,3,31,F,5197 +1983,3,31,M,5416 +1983,3,99,F,4 +1983,3,99,M,6 +1983,4,1,F,4836 +1983,4,1,M,5146 +1983,4,2,F,4210 +1983,4,2,M,4562 +1983,4,3,F,4027 +1983,4,3,M,4327 +1983,4,4,F,4914 +1983,4,4,M,5293 +1983,4,5,F,5222 +1983,4,5,M,5537 +1983,4,6,F,5163 +1983,4,6,M,5285 +1983,4,7,F,5138 +1983,4,7,M,5521 +1983,4,8,F,5112 +1983,4,8,M,5446 +1983,4,9,F,4295 +1983,4,9,M,4490 +1983,4,10,F,4189 +1983,4,10,M,4296 +1983,4,11,F,4883 +1983,4,11,M,5415 +1983,4,12,F,5075 +1983,4,12,M,5499 +1983,4,13,F,4882 +1983,4,13,M,5230 +1983,4,14,F,4927 +1983,4,14,M,5269 +1983,4,15,F,4983 +1983,4,15,M,5356 +1983,4,16,F,4249 +1983,4,16,M,4374 +1983,4,17,F,4081 +1983,4,17,M,4283 +1983,4,18,F,5019 +1983,4,18,M,5198 +1983,4,19,F,5063 +1983,4,19,M,5346 +1983,4,20,F,5030 +1983,4,20,M,5341 +1983,4,21,F,4934 +1983,4,21,M,5267 +1983,4,22,F,5012 +1983,4,22,M,5256 +1983,4,23,F,4288 +1983,4,23,M,4416 +1983,4,24,F,3763 +1983,4,24,M,4187 +1983,4,25,F,5045 +1983,4,25,M,5326 +1983,4,26,F,5211 +1983,4,26,M,5381 +1983,4,27,F,5192 +1983,4,27,M,5220 +1983,4,28,F,5017 +1983,4,28,M,5418 +1983,4,29,F,5014 +1983,4,29,M,5265 +1983,4,30,F,4099 +1983,4,30,M,4380 +1983,4,99,F,7 +1983,4,99,M,11 +1983,5,1,F,3937 +1983,5,1,M,4233 +1983,5,2,F,4889 +1983,5,2,M,5225 +1983,5,3,F,5256 +1983,5,3,M,5381 +1983,5,4,F,5010 +1983,5,4,M,5206 +1983,5,5,F,5127 +1983,5,5,M,5378 +1983,5,6,F,5018 +1983,5,6,M,5259 +1983,5,7,F,4188 +1983,5,7,M,4461 +1983,5,8,F,4014 +1983,5,8,M,4398 +1983,5,9,F,4850 +1983,5,9,M,5132 +1983,5,10,F,5001 +1983,5,10,M,5327 +1983,5,11,F,4942 +1983,5,11,M,5232 +1983,5,12,F,5037 +1983,5,12,M,5269 +1983,5,13,F,4974 +1983,5,13,M,5116 +1983,5,14,F,4198 +1983,5,14,M,4543 +1983,5,15,F,4131 +1983,5,15,M,4333 +1983,5,16,F,5194 +1983,5,16,M,5230 +1983,5,17,F,5171 +1983,5,17,M,5230 +1983,5,18,F,4980 +1983,5,18,M,5216 +1983,5,19,F,5101 +1983,5,19,M,5405 +1983,5,20,F,5129 +1983,5,20,M,5384 +1983,5,21,F,4407 +1983,5,21,M,4543 +1983,5,22,F,4114 +1983,5,22,M,4254 +1983,5,23,F,5080 +1983,5,23,M,5339 +1983,5,24,F,5299 +1983,5,24,M,5672 +1983,5,25,F,5094 +1983,5,25,M,5480 +1983,5,26,F,5096 +1983,5,26,M,5262 +1983,5,27,F,5074 +1983,5,27,M,5402 +1983,5,28,F,4207 +1983,5,28,M,4550 +1983,5,29,F,4222 +1983,5,29,M,4325 +1983,5,30,F,4231 +1983,5,30,M,4456 +1983,5,31,F,4972 +1983,5,31,M,5348 +1983,5,99,F,4 +1983,6,1,F,5265 +1983,6,1,M,5535 +1983,6,2,F,5136 +1983,6,2,M,5380 +1983,6,3,F,5158 +1983,6,3,M,5506 +1983,6,4,F,4408 +1983,6,4,M,4604 +1983,6,5,F,4215 +1983,6,5,M,4359 +1983,6,6,F,5172 +1983,6,6,M,5199 +1983,6,7,F,5054 +1983,6,7,M,5579 +1983,6,8,F,5104 +1983,6,8,M,5271 +1983,6,9,F,5240 +1983,6,9,M,5259 +1983,6,10,F,5294 +1983,6,10,M,5358 +1983,6,11,F,4219 +1983,6,11,M,4738 +1983,6,12,F,4182 +1983,6,12,M,4365 +1983,6,13,F,5004 +1983,6,13,M,5460 +1983,6,14,F,5261 +1983,6,14,M,5725 +1983,6,15,F,5023 +1983,6,15,M,5399 +1983,6,16,F,5127 +1983,6,16,M,5400 +1983,6,17,F,5213 +1983,6,17,M,5572 +1983,6,18,F,4206 +1983,6,18,M,4503 +1983,6,19,F,4109 +1983,6,19,M,4480 +1983,6,20,F,4998 +1983,6,20,M,5500 +1983,6,21,F,5236 +1983,6,21,M,5515 +1983,6,22,F,4965 +1983,6,22,M,5453 +1983,6,23,F,5218 +1983,6,23,M,5572 +1983,6,24,F,5086 +1983,6,24,M,5602 +1983,6,25,F,4408 +1983,6,25,M,4660 +1983,6,26,F,4212 +1983,6,26,M,4449 +1983,6,27,F,5139 +1983,6,27,M,5495 +1983,6,28,F,5387 +1983,6,28,M,5701 +1983,6,29,F,5227 +1983,6,29,M,5557 +1983,6,30,F,5161 +1983,6,30,M,5599 +1983,6,99,F,5 +1983,6,99,M,9 +1983,7,1,F,5338 +1983,7,1,M,5575 +1983,7,2,F,4417 +1983,7,2,M,4658 +1983,7,3,F,4254 +1983,7,3,M,4394 +1983,7,4,F,4287 +1983,7,4,M,4656 +1983,7,5,F,5110 +1983,7,5,M,5381 +1983,7,6,F,5406 +1983,7,6,M,5713 +1983,7,7,F,5512 +1983,7,7,M,5618 +1983,7,8,F,5254 +1983,7,8,M,5617 +1983,7,9,F,4383 +1983,7,9,M,4672 +1983,7,10,F,4234 +1983,7,10,M,4604 +1983,7,11,F,5189 +1983,7,11,M,5475 +1983,7,12,F,5518 +1983,7,12,M,5736 +1983,7,13,F,5374 +1983,7,13,M,5663 +1983,7,14,F,5311 +1983,7,14,M,5725 +1983,7,15,F,5437 +1983,7,15,M,5729 +1983,7,16,F,4495 +1983,7,16,M,4725 +1983,7,17,F,4344 +1983,7,17,M,4596 +1983,7,18,F,5248 +1983,7,18,M,5519 +1983,7,19,F,5455 +1983,7,19,M,5585 +1983,7,20,F,5379 +1983,7,20,M,5720 +1983,7,21,F,5292 +1983,7,21,M,5780 +1983,7,22,F,5438 +1983,7,22,M,5605 +1983,7,23,F,4466 +1983,7,23,M,4689 +1983,7,24,F,4412 +1983,7,24,M,4581 +1983,7,25,F,5154 +1983,7,25,M,5499 +1983,7,26,F,5400 +1983,7,26,M,5633 +1983,7,27,F,5273 +1983,7,27,M,5566 +1983,7,28,F,5313 +1983,7,28,M,5724 +1983,7,29,F,5334 +1983,7,29,M,5691 +1983,7,30,F,4512 +1983,7,30,M,4652 +1983,7,31,F,4273 +1983,7,31,M,4697 +1983,7,99,F,3 +1983,7,99,M,12 +1983,8,1,F,5149 +1983,8,1,M,5461 +1983,8,2,F,5405 +1983,8,2,M,5641 +1983,8,3,F,5366 +1983,8,3,M,5587 +1983,8,4,F,5161 +1983,8,4,M,5559 +1983,8,5,F,5357 +1983,8,5,M,5659 +1983,8,6,F,4519 +1983,8,6,M,4713 +1983,8,7,F,4579 +1983,8,7,M,4677 +1983,8,8,F,5167 +1983,8,8,M,5628 +1983,8,9,F,5451 +1983,8,9,M,5688 +1983,8,10,F,5434 +1983,8,10,M,5588 +1983,8,11,F,5247 +1983,8,11,M,5542 +1983,8,12,F,5254 +1983,8,12,M,5712 +1983,8,13,F,4458 +1983,8,13,M,4700 +1983,8,14,F,4297 +1983,8,14,M,4533 +1983,8,15,F,5299 +1983,8,15,M,5364 +1983,8,16,F,5519 +1983,8,16,M,5751 +1983,8,17,F,5412 +1983,8,17,M,5600 +1983,8,18,F,5407 +1983,8,18,M,5728 +1983,8,19,F,5500 +1983,8,19,M,5719 +1983,8,20,F,4464 +1983,8,20,M,4796 +1983,8,21,F,4359 +1983,8,21,M,4783 +1983,8,22,F,5175 +1983,8,22,M,5458 +1983,8,23,F,5478 +1983,8,23,M,5675 +1983,8,24,F,5366 +1983,8,24,M,5449 +1983,8,25,F,5342 +1983,8,25,M,5382 +1983,8,26,F,5281 +1983,8,26,M,5558 +1983,8,27,F,4463 +1983,8,27,M,4779 +1983,8,28,F,4371 +1983,8,28,M,4638 +1983,8,29,F,5270 +1983,8,29,M,5542 +1983,8,30,F,5454 +1983,8,30,M,5677 +1983,8,31,F,5253 +1983,8,31,M,5552 +1983,8,99,F,2 +1983,8,99,M,2 +1983,9,1,F,5238 +1983,9,1,M,5509 +1983,9,2,F,5261 +1983,9,2,M,5646 +1983,9,3,F,4554 +1983,9,3,M,4597 +1983,9,4,F,4273 +1983,9,4,M,4533 +1983,9,5,F,4290 +1983,9,5,M,4459 +1983,9,6,F,5302 +1983,9,6,M,5693 +1983,9,7,F,5485 +1983,9,7,M,5863 +1983,9,8,F,5420 +1983,9,8,M,5632 +1983,9,9,F,5337 +1983,9,9,M,5762 +1983,9,10,F,4590 +1983,9,10,M,4946 +1983,9,11,F,4405 +1983,9,11,M,4643 +1983,9,12,F,5253 +1983,9,12,M,5645 +1983,9,13,F,5374 +1983,9,13,M,5842 +1983,9,14,F,5469 +1983,9,14,M,5714 +1983,9,15,F,5432 +1983,9,15,M,5544 +1983,9,16,F,5352 +1983,9,16,M,5596 +1983,9,17,F,4562 +1983,9,17,M,4874 +1983,9,18,F,4501 +1983,9,18,M,4826 +1983,9,19,F,5416 +1983,9,19,M,5764 +1983,9,20,F,5742 +1983,9,20,M,5893 +1983,9,21,F,5550 +1983,9,21,M,5726 +1983,9,22,F,5392 +1983,9,22,M,5673 +1983,9,23,F,5488 +1983,9,23,M,5689 +1983,9,24,F,4477 +1983,9,24,M,4697 +1983,9,25,F,4305 +1983,9,25,M,4516 +1983,9,26,F,5458 +1983,9,26,M,5500 +1983,9,27,F,5573 +1983,9,27,M,5793 +1983,9,28,F,5382 +1983,9,28,M,5571 +1983,9,29,F,5301 +1983,9,29,M,5588 +1983,9,30,F,5357 +1983,9,30,M,5557 +1983,9,99,M,2 +1983,10,1,F,4504 +1983,10,1,M,4698 +1983,10,2,F,4319 +1983,10,2,M,4574 +1983,10,3,F,5267 +1983,10,3,M,5539 +1983,10,4,F,5476 +1983,10,4,M,5706 +1983,10,5,F,5306 +1983,10,5,M,5456 +1983,10,6,F,5293 +1983,10,6,M,5382 +1983,10,7,F,5253 +1983,10,7,M,5500 +1983,10,8,F,4355 +1983,10,8,M,4616 +1983,10,9,F,4208 +1983,10,9,M,4343 +1983,10,10,F,5051 +1983,10,10,M,5222 +1983,10,11,F,5141 +1983,10,11,M,5515 +1983,10,12,F,5247 +1983,10,12,M,5398 +1983,10,13,F,5005 +1983,10,13,M,5315 +1983,10,14,F,5223 +1983,10,14,M,5387 +1983,10,15,F,4105 +1983,10,15,M,4465 +1983,10,16,F,4199 +1983,10,16,M,4306 +1983,10,17,F,5047 +1983,10,17,M,5255 +1983,10,18,F,5071 +1983,10,18,M,5414 +1983,10,19,F,4952 +1983,10,19,M,5272 +1983,10,20,F,5132 +1983,10,20,M,5260 +1983,10,21,F,4914 +1983,10,21,M,5278 +1983,10,22,F,4154 +1983,10,22,M,4472 +1983,10,23,F,4005 +1983,10,23,M,4229 +1983,10,24,F,4953 +1983,10,24,M,5272 +1983,10,25,F,5212 +1983,10,25,M,5412 +1983,10,26,F,4950 +1983,10,26,M,5251 +1983,10,27,F,4974 +1983,10,27,M,5286 +1983,10,28,F,4978 +1983,10,28,M,5328 +1983,10,29,F,4123 +1983,10,29,M,4381 +1983,10,30,F,4150 +1983,10,30,M,4346 +1983,10,31,F,4738 +1983,10,31,M,4941 +1983,10,99,M,3 +1983,11,1,F,5031 +1983,11,1,M,5248 +1983,11,2,F,4966 +1983,11,2,M,5116 +1983,11,3,F,5114 +1983,11,3,M,5139 +1983,11,4,F,5074 +1983,11,4,M,5192 +1983,11,5,F,4257 +1983,11,5,M,4386 +1983,11,6,F,4015 +1983,11,6,M,4203 +1983,11,7,F,5013 +1983,11,7,M,5227 +1983,11,8,F,4968 +1983,11,8,M,5492 +1983,11,9,F,4905 +1983,11,9,M,5277 +1983,11,10,F,5110 +1983,11,10,M,5154 +1983,11,11,F,5005 +1983,11,11,M,5104 +1983,11,12,F,4094 +1983,11,12,M,4336 +1983,11,13,F,4135 +1983,11,13,M,4311 +1983,11,14,F,5024 +1983,11,14,M,5198 +1983,11,15,F,5200 +1983,11,15,M,5483 +1983,11,16,F,4976 +1983,11,16,M,5279 +1983,11,17,F,4889 +1983,11,17,M,5331 +1983,11,18,F,5047 +1983,11,18,M,5437 +1983,11,19,F,4186 +1983,11,19,M,4495 +1983,11,20,F,4108 +1983,11,20,M,4210 +1983,11,21,F,5080 +1983,11,21,M,5399 +1983,11,22,F,5297 +1983,11,22,M,5571 +1983,11,23,F,5036 +1983,11,23,M,5305 +1983,11,24,F,4122 +1983,11,24,M,4194 +1983,11,25,F,4585 +1983,11,25,M,4864 +1983,11,26,F,4035 +1983,11,26,M,4198 +1983,11,27,F,3994 +1983,11,27,M,4230 +1983,11,28,F,4929 +1983,11,28,M,5249 +1983,11,29,F,5185 +1983,11,29,M,5341 +1983,11,30,F,4912 +1983,11,30,M,5009 +1983,11,99,F,2 +1983,12,1,F,5089 +1983,12,1,M,5208 +1983,12,2,F,5013 +1983,12,2,M,5056 +1983,12,3,F,4063 +1983,12,3,M,4212 +1983,12,4,F,3927 +1983,12,4,M,4047 +1983,12,5,F,4990 +1983,12,5,M,5188 +1983,12,6,F,5015 +1983,12,6,M,5264 +1983,12,7,F,4905 +1983,12,7,M,5226 +1983,12,8,F,4937 +1983,12,8,M,5201 +1983,12,9,F,5002 +1983,12,9,M,5130 +1983,12,10,F,4148 +1983,12,10,M,4248 +1983,12,11,F,4067 +1983,12,11,M,4249 +1983,12,12,F,4991 +1983,12,12,M,5269 +1983,12,13,F,5188 +1983,12,13,M,5239 +1983,12,14,F,4986 +1983,12,14,M,5288 +1983,12,15,F,5066 +1983,12,15,M,5398 +1983,12,16,F,5108 +1983,12,16,M,5426 +1983,12,17,F,4078 +1983,12,17,M,4221 +1983,12,18,F,3942 +1983,12,18,M,4228 +1983,12,19,F,5190 +1983,12,19,M,5571 +1983,12,20,F,5419 +1983,12,20,M,5566 +1983,12,21,F,4909 +1983,12,21,M,5317 +1983,12,22,F,4625 +1983,12,22,M,4882 +1983,12,23,F,4486 +1983,12,23,M,4549 +1983,12,24,F,3948 +1983,12,24,M,4094 +1983,12,25,F,3697 +1983,12,25,M,3948 +1983,12,26,F,4029 +1983,12,26,M,4207 +1983,12,27,F,5267 +1983,12,27,M,5350 +1983,12,28,F,5478 +1983,12,28,M,5786 +1983,12,29,F,5274 +1983,12,29,M,5609 +1983,12,30,F,5328 +1983,12,30,M,5673 +1983,12,31,F,4144 +1983,12,31,M,4418 +1983,12,99,F,2 +1984,1,1,F,3921 +1984,1,1,M,4092 +1984,1,2,F,3910 +1984,1,2,M,4095 +1984,1,3,F,4779 +1984,1,3,M,4883 +1984,1,4,F,4863 +1984,1,4,M,5060 +1984,1,5,F,4860 +1984,1,5,M,5144 +1984,1,6,F,5012 +1984,1,6,M,5105 +1984,1,7,F,4146 +1984,1,7,M,4351 +1984,1,8,F,4026 +1984,1,8,M,4204 +1984,1,9,F,4712 +1984,1,9,M,4978 +1984,1,10,F,4852 +1984,1,10,M,5263 +1984,1,11,F,4769 +1984,1,11,M,5045 +1984,1,12,F,4767 +1984,1,12,M,5031 +1984,1,13,F,4826 +1984,1,13,M,5012 +1984,1,14,F,4172 +1984,1,14,M,4332 +1984,1,15,F,3937 +1984,1,15,M,4171 +1984,1,16,F,4867 +1984,1,16,M,5070 +1984,1,17,F,4915 +1984,1,17,M,5179 +1984,1,18,F,4888 +1984,1,18,M,5246 +1984,1,19,F,4727 +1984,1,19,M,5167 +1984,1,20,F,4975 +1984,1,20,M,5122 +1984,1,21,F,4078 +1984,1,21,M,4257 +1984,1,22,F,3906 +1984,1,22,M,4166 +1984,1,23,F,4881 +1984,1,23,M,5044 +1984,1,24,F,5038 +1984,1,24,M,5247 +1984,1,25,F,4960 +1984,1,25,M,5294 +1984,1,26,F,5043 +1984,1,26,M,5101 +1984,1,27,F,5047 +1984,1,27,M,5240 +1984,1,28,F,4122 +1984,1,28,M,4428 +1984,1,29,F,4123 +1984,1,29,M,4068 +1984,1,30,F,4857 +1984,1,30,M,5023 +1984,1,31,F,4975 +1984,1,31,M,5154 +1984,1,99,F,2 +1984,1,99,M,3 +1984,2,1,F,4921 +1984,2,1,M,5118 +1984,2,2,F,4992 +1984,2,2,M,5135 +1984,2,3,F,4932 +1984,2,3,M,5267 +1984,2,4,F,4145 +1984,2,4,M,4369 +1984,2,5,F,4019 +1984,2,5,M,4257 +1984,2,6,F,4874 +1984,2,6,M,5080 +1984,2,7,F,4952 +1984,2,7,M,5131 +1984,2,8,F,4973 +1984,2,8,M,5154 +1984,2,9,F,4893 +1984,2,9,M,5108 +1984,2,10,F,5140 +1984,2,10,M,5444 +1984,2,11,F,4262 +1984,2,11,M,4485 +1984,2,12,F,4102 +1984,2,12,M,4254 +1984,2,13,F,4837 +1984,2,13,M,5140 +1984,2,14,F,5518 +1984,2,14,M,5747 +1984,2,15,F,4935 +1984,2,15,M,5278 +1984,2,16,F,5024 +1984,2,16,M,5229 +1984,2,17,F,4999 +1984,2,17,M,5230 +1984,2,18,F,4344 +1984,2,18,M,4527 +1984,2,19,F,4100 +1984,2,19,M,4277 +1984,2,20,F,4852 +1984,2,20,M,4963 +1984,2,21,F,4959 +1984,2,21,M,5230 +1984,2,22,F,5004 +1984,2,22,M,5299 +1984,2,23,F,4770 +1984,2,23,M,5225 +1984,2,24,F,5058 +1984,2,24,M,5252 +1984,2,25,F,4219 +1984,2,25,M,4418 +1984,2,26,F,4098 +1984,2,26,M,4196 +1984,2,27,F,4818 +1984,2,27,M,5234 +1984,2,28,F,5126 +1984,2,28,M,5305 +1984,2,29,F,4773 +1984,2,29,M,4907 +1984,2,99,M,1 +1984,3,1,F,5007 +1984,3,1,M,5464 +1984,3,2,F,5128 +1984,3,2,M,5377 +1984,3,3,F,4320 +1984,3,3,M,4488 +1984,3,4,F,4114 +1984,3,4,M,4249 +1984,3,5,F,4834 +1984,3,5,M,5290 +1984,3,6,F,5149 +1984,3,6,M,5408 +1984,3,7,F,5050 +1984,3,7,M,5309 +1984,3,8,F,5022 +1984,3,8,M,5254 +1984,3,9,F,4990 +1984,3,9,M,5105 +1984,3,10,F,4284 +1984,3,10,M,4379 +1984,3,11,F,4228 +1984,3,11,M,4173 +1984,3,12,F,5021 +1984,3,12,M,5231 +1984,3,13,F,4908 +1984,3,13,M,5363 +1984,3,14,F,4959 +1984,3,14,M,5217 +1984,3,15,F,5065 +1984,3,15,M,5264 +1984,3,16,F,5191 +1984,3,16,M,5515 +1984,3,17,F,4355 +1984,3,17,M,4528 +1984,3,18,F,3982 +1984,3,18,M,4349 +1984,3,19,F,5121 +1984,3,19,M,5306 +1984,3,20,F,5198 +1984,3,20,M,5516 +1984,3,21,F,5142 +1984,3,21,M,5253 +1984,3,22,F,5070 +1984,3,22,M,5283 +1984,3,23,F,4919 +1984,3,23,M,5365 +1984,3,24,F,4195 +1984,3,24,M,4371 +1984,3,25,F,4108 +1984,3,25,M,4158 +1984,3,26,F,4975 +1984,3,26,M,5191 +1984,3,27,F,5017 +1984,3,27,M,5339 +1984,3,28,F,5006 +1984,3,28,M,5294 +1984,3,29,F,5016 +1984,3,29,M,5141 +1984,3,30,F,4995 +1984,3,30,M,5274 +1984,3,31,F,4191 +1984,3,31,M,4323 +1984,3,99,M,1 +1984,4,1,F,4021 +1984,4,1,M,4181 +1984,4,2,F,4861 +1984,4,2,M,5155 +1984,4,3,F,5038 +1984,4,3,M,5014 +1984,4,4,F,4976 +1984,4,4,M,5355 +1984,4,5,F,4915 +1984,4,5,M,5056 +1984,4,6,F,5013 +1984,4,6,M,5327 +1984,4,7,F,4191 +1984,4,7,M,4376 +1984,4,8,F,3907 +1984,4,8,M,4150 +1984,4,9,F,4948 +1984,4,9,M,5085 +1984,4,10,F,4896 +1984,4,10,M,5303 +1984,4,11,F,4927 +1984,4,11,M,4903 +1984,4,12,F,5036 +1984,4,12,M,5246 +1984,4,13,F,4847 +1984,4,13,M,5255 +1984,4,14,F,4169 +1984,4,14,M,4408 +1984,4,15,F,3927 +1984,4,15,M,4161 +1984,4,16,F,4860 +1984,4,16,M,5131 +1984,4,17,F,5173 +1984,4,17,M,5397 +1984,4,18,F,4938 +1984,4,18,M,5148 +1984,4,19,F,4937 +1984,4,19,M,5120 +1984,4,20,F,4712 +1984,4,20,M,4977 +1984,4,21,F,3972 +1984,4,21,M,4291 +1984,4,22,F,3854 +1984,4,22,M,4037 +1984,4,23,F,4828 +1984,4,23,M,5103 +1984,4,24,F,5021 +1984,4,24,M,5272 +1984,4,25,F,4944 +1984,4,25,M,5108 +1984,4,26,F,4955 +1984,4,26,M,5245 +1984,4,27,F,4999 +1984,4,27,M,5153 +1984,4,28,F,4181 +1984,4,28,M,4303 +1984,4,29,F,3824 +1984,4,29,M,3897 +1984,4,30,F,4925 +1984,4,30,M,5172 +1984,4,99,F,2 +1984,4,99,M,1 +1984,5,1,F,5139 +1984,5,1,M,5396 +1984,5,2,F,4954 +1984,5,2,M,5233 +1984,5,3,F,5001 +1984,5,3,M,5237 +1984,5,4,F,5011 +1984,5,4,M,5281 +1984,5,5,F,4080 +1984,5,5,M,4300 +1984,5,6,F,3952 +1984,5,6,M,4232 +1984,5,7,F,4805 +1984,5,7,M,5008 +1984,5,8,F,4961 +1984,5,8,M,5242 +1984,5,9,F,4772 +1984,5,9,M,4916 +1984,5,10,F,4767 +1984,5,10,M,5179 +1984,5,11,F,5062 +1984,5,11,M,5213 +1984,5,12,F,4069 +1984,5,12,M,4309 +1984,5,13,F,3969 +1984,5,13,M,4230 +1984,5,14,F,5018 +1984,5,14,M,5069 +1984,5,15,F,5002 +1984,5,15,M,5287 +1984,5,16,F,4795 +1984,5,16,M,5210 +1984,5,17,F,4844 +1984,5,17,M,5007 +1984,5,18,F,4848 +1984,5,18,M,5232 +1984,5,19,F,4133 +1984,5,19,M,4343 +1984,5,20,F,4024 +1984,5,20,M,4258 +1984,5,21,F,4820 +1984,5,21,M,5361 +1984,5,22,F,5207 +1984,5,22,M,5573 +1984,5,23,F,4988 +1984,5,23,M,5383 +1984,5,24,F,4981 +1984,5,24,M,5152 +1984,5,25,F,5196 +1984,5,25,M,5379 +1984,5,26,F,4286 +1984,5,26,M,4563 +1984,5,27,F,4033 +1984,5,27,M,4355 +1984,5,28,F,4135 +1984,5,28,M,4276 +1984,5,29,F,5006 +1984,5,29,M,5262 +1984,5,30,F,5025 +1984,5,30,M,5260 +1984,5,31,F,4970 +1984,5,31,M,5129 +1984,5,99,F,4 +1984,6,1,F,5083 +1984,6,1,M,5318 +1984,6,2,F,4292 +1984,6,2,M,4588 +1984,6,3,F,4089 +1984,6,3,M,4188 +1984,6,4,F,4867 +1984,6,4,M,5151 +1984,6,5,F,5137 +1984,6,5,M,5377 +1984,6,6,F,5095 +1984,6,6,M,5382 +1984,6,7,F,4895 +1984,6,7,M,5349 +1984,6,8,F,5088 +1984,6,8,M,5472 +1984,6,9,F,4228 +1984,6,9,M,4533 +1984,6,10,F,4028 +1984,6,10,M,4394 +1984,6,11,F,5002 +1984,6,11,M,5325 +1984,6,12,F,5037 +1984,6,12,M,5465 +1984,6,13,F,4839 +1984,6,13,M,5245 +1984,6,14,F,5137 +1984,6,14,M,5595 +1984,6,15,F,5104 +1984,6,15,M,5228 +1984,6,16,F,4104 +1984,6,16,M,4353 +1984,6,17,F,4167 +1984,6,17,M,4444 +1984,6,18,F,5129 +1984,6,18,M,5354 +1984,6,19,F,5080 +1984,6,19,M,5435 +1984,6,20,F,5187 +1984,6,20,M,5468 +1984,6,21,F,4995 +1984,6,21,M,5566 +1984,6,22,F,5015 +1984,6,22,M,5571 +1984,6,23,F,4373 +1984,6,23,M,4599 +1984,6,24,F,4253 +1984,6,24,M,4568 +1984,6,25,F,5197 +1984,6,25,M,5364 +1984,6,26,F,5338 +1984,6,26,M,5642 +1984,6,27,F,5153 +1984,6,27,M,5509 +1984,6,28,F,5324 +1984,6,28,M,5470 +1984,6,29,F,5344 +1984,6,29,M,5743 +1984,6,30,F,4316 +1984,6,30,M,4624 +1984,6,99,F,1 +1984,6,99,M,2 +1984,7,1,F,4170 +1984,7,1,M,4395 +1984,7,2,F,5387 +1984,7,2,M,5420 +1984,7,3,F,5451 +1984,7,3,M,5799 +1984,7,4,F,4408 +1984,7,4,M,4832 +1984,7,5,F,5330 +1984,7,5,M,5459 +1984,7,6,F,5594 +1984,7,6,M,5972 +1984,7,7,F,4377 +1984,7,7,M,4761 +1984,7,8,F,4329 +1984,7,8,M,4627 +1984,7,9,F,5357 +1984,7,9,M,5639 +1984,7,10,F,5576 +1984,7,10,M,6085 +1984,7,11,F,5221 +1984,7,11,M,5702 +1984,7,12,F,5541 +1984,7,12,M,5723 +1984,7,13,F,5243 +1984,7,13,M,5444 +1984,7,14,F,4506 +1984,7,14,M,4707 +1984,7,15,F,4398 +1984,7,15,M,4648 +1984,7,16,F,5683 +1984,7,16,M,5612 +1984,7,17,F,5547 +1984,7,17,M,5946 +1984,7,18,F,5557 +1984,7,18,M,5721 +1984,7,19,F,5312 +1984,7,19,M,5730 +1984,7,20,F,5451 +1984,7,20,M,5734 +1984,7,21,F,4659 +1984,7,21,M,4679 +1984,7,22,F,4507 +1984,7,22,M,4522 +1984,7,23,F,5474 +1984,7,23,M,5702 +1984,7,24,F,5692 +1984,7,24,M,6119 +1984,7,25,F,5529 +1984,7,25,M,5570 +1984,7,26,F,5372 +1984,7,26,M,5631 +1984,7,27,F,5421 +1984,7,27,M,5899 +1984,7,28,F,4651 +1984,7,28,M,4819 +1984,7,29,F,4290 +1984,7,29,M,4611 +1984,7,30,F,5201 +1984,7,30,M,5581 +1984,7,31,F,5760 +1984,7,31,M,5822 +1984,7,99,M,4 +1984,8,1,F,5344 +1984,8,1,M,5883 +1984,8,2,F,5682 +1984,8,2,M,5728 +1984,8,3,F,5479 +1984,8,3,M,5891 +1984,8,4,F,4738 +1984,8,4,M,4856 +1984,8,5,F,4417 +1984,8,5,M,4720 +1984,8,6,F,5283 +1984,8,6,M,5930 +1984,8,7,F,5745 +1984,8,7,M,6139 +1984,8,8,F,5577 +1984,8,8,M,5815 +1984,8,9,F,5677 +1984,8,9,M,5700 +1984,8,10,F,5547 +1984,8,10,M,5817 +1984,8,11,F,4612 +1984,8,11,M,4853 +1984,8,12,F,4416 +1984,8,12,M,4629 +1984,8,13,F,5359 +1984,8,13,M,5658 +1984,8,14,F,5705 +1984,8,14,M,5845 +1984,8,15,F,5586 +1984,8,15,M,5903 +1984,8,16,F,5572 +1984,8,16,M,5729 +1984,8,17,F,5495 +1984,8,17,M,5932 +1984,8,18,F,4645 +1984,8,18,M,4762 +1984,8,19,F,4409 +1984,8,19,M,4536 +1984,8,20,F,5551 +1984,8,20,M,5677 +1984,8,21,F,5660 +1984,8,21,M,5925 +1984,8,22,F,5462 +1984,8,22,M,5718 +1984,8,23,F,5413 +1984,8,23,M,5697 +1984,8,24,F,5441 +1984,8,24,M,5721 +1984,8,25,F,4466 +1984,8,25,M,4788 +1984,8,26,F,4543 +1984,8,26,M,4691 +1984,8,27,F,5566 +1984,8,27,M,5655 +1984,8,28,F,5813 +1984,8,28,M,6016 +1984,8,29,F,5480 +1984,8,29,M,5862 +1984,8,30,F,5520 +1984,8,30,M,5901 +1984,8,31,F,5528 +1984,8,31,M,5953 +1984,8,99,M,5 +1984,9,1,F,4537 +1984,9,1,M,4833 +1984,9,2,F,4443 +1984,9,2,M,4519 +1984,9,3,F,4655 +1984,9,3,M,4688 +1984,9,4,F,5430 +1984,9,4,M,5681 +1984,9,5,F,5666 +1984,9,5,M,6161 +1984,9,6,F,5557 +1984,9,6,M,5885 +1984,9,7,F,5807 +1984,9,7,M,5953 +1984,9,8,F,4621 +1984,9,8,M,4967 +1984,9,9,F,4656 +1984,9,9,M,4698 +1984,9,10,F,5630 +1984,9,10,M,6011 +1984,9,11,F,5762 +1984,9,11,M,6033 +1984,9,12,F,5638 +1984,9,12,M,5750 +1984,9,13,F,5545 +1984,9,13,M,5970 +1984,9,14,F,5811 +1984,9,14,M,6223 +1984,9,15,F,4877 +1984,9,15,M,5003 +1984,9,16,F,4568 +1984,9,16,M,4742 +1984,9,17,F,5628 +1984,9,17,M,5859 +1984,9,18,F,5839 +1984,9,18,M,6199 +1984,9,19,F,5760 +1984,9,19,M,6027 +1984,9,20,F,5715 +1984,9,20,M,6005 +1984,9,21,F,5957 +1984,9,21,M,6164 +1984,9,22,F,4783 +1984,9,22,M,5074 +1984,9,23,F,4704 +1984,9,23,M,4918 +1984,9,24,F,5828 +1984,9,24,M,6152 +1984,9,25,F,6021 +1984,9,25,M,6250 +1984,9,26,F,5721 +1984,9,26,M,5970 +1984,9,27,F,5393 +1984,9,27,M,5781 +1984,9,28,F,5674 +1984,9,28,M,5885 +1984,9,29,F,4786 +1984,9,29,M,5027 +1984,9,30,F,4610 +1984,9,30,M,4852 +1984,9,99,F,4 +1984,10,1,F,5594 +1984,10,1,M,5641 +1984,10,2,F,5637 +1984,10,2,M,5935 +1984,10,3,F,5626 +1984,10,3,M,5861 +1984,10,4,F,5497 +1984,10,4,M,5842 +1984,10,5,F,5592 +1984,10,5,M,5795 +1984,10,6,F,4645 +1984,10,6,M,4878 +1984,10,7,F,4253 +1984,10,7,M,4496 +1984,10,8,F,5372 +1984,10,8,M,5682 +1984,10,9,F,5672 +1984,10,9,M,5763 +1984,10,10,F,5458 +1984,10,10,M,5688 +1984,10,11,F,5311 +1984,10,11,M,5749 +1984,10,12,F,5350 +1984,10,12,M,5668 +1984,10,13,F,4421 +1984,10,13,M,4588 +1984,10,14,F,4315 +1984,10,14,M,4521 +1984,10,15,F,5271 +1984,10,15,M,5690 +1984,10,16,F,5296 +1984,10,16,M,5703 +1984,10,17,F,5176 +1984,10,17,M,5517 +1984,10,18,F,5202 +1984,10,18,M,5395 +1984,10,19,F,5209 +1984,10,19,M,5628 +1984,10,20,F,4310 +1984,10,20,M,4481 +1984,10,21,F,4117 +1984,10,21,M,4427 +1984,10,22,F,5138 +1984,10,22,M,5471 +1984,10,23,F,5318 +1984,10,23,M,5565 +1984,10,24,F,5148 +1984,10,24,M,5247 +1984,10,25,F,5094 +1984,10,25,M,5369 +1984,10,26,F,5220 +1984,10,26,M,5348 +1984,10,27,F,4372 +1984,10,27,M,4510 +1984,10,28,F,4385 +1984,10,28,M,4525 +1984,10,29,F,5004 +1984,10,29,M,5262 +1984,10,30,F,5135 +1984,10,30,M,5323 +1984,10,31,F,4960 +1984,10,31,M,5073 +1984,11,1,F,5080 +1984,11,1,M,5522 +1984,11,2,F,5168 +1984,11,2,M,5268 +1984,11,3,F,4109 +1984,11,3,M,4417 +1984,11,4,F,4130 +1984,11,4,M,4250 +1984,11,5,F,5170 +1984,11,5,M,5402 +1984,11,6,F,5008 +1984,11,6,M,5498 +1984,11,7,F,5090 +1984,11,7,M,5201 +1984,11,8,F,5084 +1984,11,8,M,5294 +1984,11,9,F,5277 +1984,11,9,M,5495 +1984,11,10,F,4077 +1984,11,10,M,4410 +1984,11,11,F,4143 +1984,11,11,M,4433 +1984,11,12,F,4922 +1984,11,12,M,5340 +1984,11,13,F,5252 +1984,11,13,M,5503 +1984,11,14,F,4973 +1984,11,14,M,5430 +1984,11,15,F,5212 +1984,11,15,M,5489 +1984,11,16,F,5265 +1984,11,16,M,5457 +1984,11,17,F,4250 +1984,11,17,M,4438 +1984,11,18,F,4115 +1984,11,18,M,4185 +1984,11,19,F,5272 +1984,11,19,M,5468 +1984,11,20,F,5263 +1984,11,20,M,5678 +1984,11,21,F,5071 +1984,11,21,M,5430 +1984,11,22,F,3867 +1984,11,22,M,4144 +1984,11,23,F,4774 +1984,11,23,M,4915 +1984,11,24,F,4173 +1984,11,24,M,4339 +1984,11,25,F,4176 +1984,11,25,M,4367 +1984,11,26,F,5368 +1984,11,26,M,5288 +1984,11,27,F,5279 +1984,11,27,M,5540 +1984,11,28,F,5171 +1984,11,28,M,5287 +1984,11,29,F,5010 +1984,11,29,M,5377 +1984,11,30,F,5172 +1984,11,30,M,5388 +1984,11,99,F,2 +1984,11,99,M,3 +1984,12,1,F,4134 +1984,12,1,M,4345 +1984,12,2,F,4086 +1984,12,2,M,4262 +1984,12,3,F,5049 +1984,12,3,M,5240 +1984,12,4,F,5061 +1984,12,4,M,5483 +1984,12,5,F,4975 +1984,12,5,M,5207 +1984,12,6,F,4983 +1984,12,6,M,5189 +1984,12,7,F,5080 +1984,12,7,M,5180 +1984,12,8,F,4227 +1984,12,8,M,4232 +1984,12,9,F,4062 +1984,12,9,M,4205 +1984,12,10,F,5077 +1984,12,10,M,5429 +1984,12,11,F,5305 +1984,12,11,M,5442 +1984,12,12,F,5114 +1984,12,12,M,5455 +1984,12,13,F,5046 +1984,12,13,M,5332 +1984,12,14,F,5135 +1984,12,14,M,5495 +1984,12,15,F,4155 +1984,12,15,M,4350 +1984,12,16,F,4050 +1984,12,16,M,4203 +1984,12,17,F,5259 +1984,12,17,M,5501 +1984,12,18,F,5554 +1984,12,18,M,5890 +1984,12,19,F,5350 +1984,12,19,M,5583 +1984,12,20,F,5360 +1984,12,20,M,5465 +1984,12,21,F,5154 +1984,12,21,M,5320 +1984,12,22,F,4133 +1984,12,22,M,4386 +1984,12,23,F,3895 +1984,12,23,M,4006 +1984,12,24,F,4179 +1984,12,24,M,4396 +1984,12,25,F,3817 +1984,12,25,M,4034 +1984,12,26,F,4726 +1984,12,26,M,5051 +1984,12,27,F,5483 +1984,12,27,M,5741 +1984,12,28,F,5717 +1984,12,28,M,5988 +1984,12,29,F,4511 +1984,12,29,M,4743 +1984,12,30,F,4073 +1984,12,30,M,4313 +1984,12,31,F,4972 +1984,12,31,M,5133 +1984,12,99,F,2 +1985,1,1,F,4091 +1985,1,1,M,4244 +1985,1,2,F,4570 +1985,1,2,M,4807 +1985,1,3,F,4883 +1985,1,3,M,5169 +1985,1,4,F,5102 +1985,1,4,M,5356 +1985,1,5,F,4227 +1985,1,5,M,4421 +1985,1,6,F,4191 +1985,1,6,M,4286 +1985,1,7,F,4819 +1985,1,7,M,5259 +1985,1,8,F,5049 +1985,1,8,M,5193 +1985,1,9,F,4829 +1985,1,9,M,5159 +1985,1,10,F,4911 +1985,1,10,M,5239 +1985,1,11,F,5142 +1985,1,11,M,5359 +1985,1,12,F,4293 +1985,1,12,M,4466 +1985,1,13,F,4073 +1985,1,13,M,4174 +1985,1,14,F,5085 +1985,1,14,M,5324 +1985,1,15,F,5159 +1985,1,15,M,5533 +1985,1,16,F,4961 +1985,1,16,M,5238 +1985,1,17,F,5143 +1985,1,17,M,5248 +1985,1,18,F,5309 +1985,1,18,M,5556 +1985,1,19,F,4334 +1985,1,19,M,4648 +1985,1,20,F,4087 +1985,1,20,M,4258 +1985,1,21,F,5055 +1985,1,21,M,5259 +1985,1,22,F,5147 +1985,1,22,M,5451 +1985,1,23,F,5191 +1985,1,23,M,5305 +1985,1,24,F,5090 +1985,1,24,M,5380 +1985,1,25,F,5214 +1985,1,25,M,5541 +1985,1,26,F,4351 +1985,1,26,M,4423 +1985,1,27,F,4192 +1985,1,27,M,4249 +1985,1,28,F,5133 +1985,1,28,M,5189 +1985,1,29,F,5247 +1985,1,29,M,5335 +1985,1,30,F,5027 +1985,1,30,M,5281 +1985,1,31,F,4860 +1985,1,31,M,5302 +1985,1,99,F,1 +1985,1,99,M,1 +1985,2,1,F,5249 +1985,2,1,M,5342 +1985,2,2,F,4358 +1985,2,2,M,4471 +1985,2,3,F,4105 +1985,2,3,M,4242 +1985,2,4,F,5061 +1985,2,4,M,5277 +1985,2,5,F,5138 +1985,2,5,M,5469 +1985,2,6,F,5121 +1985,2,6,M,5385 +1985,2,7,F,5105 +1985,2,7,M,5403 +1985,2,8,F,5198 +1985,2,8,M,5381 +1985,2,9,F,4342 +1985,2,9,M,4579 +1985,2,10,F,4240 +1985,2,10,M,4367 +1985,2,11,F,5127 +1985,2,11,M,5470 +1985,2,12,F,5216 +1985,2,12,M,5673 +1985,2,13,F,4963 +1985,2,13,M,5343 +1985,2,14,F,5526 +1985,2,14,M,5837 +1985,2,15,F,5204 +1985,2,15,M,5477 +1985,2,16,F,4326 +1985,2,16,M,4557 +1985,2,17,F,4122 +1985,2,17,M,4354 +1985,2,18,F,4808 +1985,2,18,M,5117 +1985,2,19,F,5048 +1985,2,19,M,5524 +1985,2,20,F,5175 +1985,2,20,M,5361 +1985,2,21,F,5222 +1985,2,21,M,5361 +1985,2,22,F,5290 +1985,2,22,M,5439 +1985,2,23,F,4344 +1985,2,23,M,4489 +1985,2,24,F,4263 +1985,2,24,M,4573 +1985,2,25,F,5116 +1985,2,25,M,5413 +1985,2,26,F,5268 +1985,2,26,M,5450 +1985,2,27,F,5202 +1985,2,27,M,5469 +1985,2,28,F,5204 +1985,2,28,M,5412 +1985,2,99,F,1 +1985,2,99,M,4 +1985,3,1,F,5380 +1985,3,1,M,5496 +1985,3,2,F,4259 +1985,3,2,M,4565 +1985,3,3,F,4153 +1985,3,3,M,4401 +1985,3,4,F,5254 +1985,3,4,M,5375 +1985,3,5,F,5361 +1985,3,5,M,5436 +1985,3,6,F,5045 +1985,3,6,M,5430 +1985,3,7,F,5174 +1985,3,7,M,5335 +1985,3,8,F,5332 +1985,3,8,M,5595 +1985,3,9,F,4425 +1985,3,9,M,4585 +1985,3,10,F,4149 +1985,3,10,M,4195 +1985,3,11,F,5166 +1985,3,11,M,5378 +1985,3,12,F,5260 +1985,3,12,M,5552 +1985,3,13,F,5010 +1985,3,13,M,5276 +1985,3,14,F,5211 +1985,3,14,M,5521 +1985,3,15,F,5187 +1985,3,15,M,5423 +1985,3,16,F,4282 +1985,3,16,M,4408 +1985,3,17,F,4146 +1985,3,17,M,4357 +1985,3,18,F,5110 +1985,3,18,M,5406 +1985,3,19,F,5259 +1985,3,19,M,5544 +1985,3,20,F,5171 +1985,3,20,M,5512 +1985,3,21,F,5142 +1985,3,21,M,5449 +1985,3,22,F,5311 +1985,3,22,M,5481 +1985,3,23,F,4330 +1985,3,23,M,4537 +1985,3,24,F,4188 +1985,3,24,M,4270 +1985,3,25,F,5211 +1985,3,25,M,5261 +1985,3,26,F,5446 +1985,3,26,M,5472 +1985,3,27,F,5246 +1985,3,27,M,5420 +1985,3,28,F,5309 +1985,3,28,M,5581 +1985,3,29,F,5322 +1985,3,29,M,5715 +1985,3,30,F,4376 +1985,3,30,M,4511 +1985,3,31,F,4001 +1985,3,31,M,4320 +1985,4,1,F,4749 +1985,4,1,M,5085 +1985,4,2,F,5456 +1985,4,2,M,5807 +1985,4,3,F,5130 +1985,4,3,M,5424 +1985,4,4,F,5104 +1985,4,4,M,5485 +1985,4,5,F,5013 +1985,4,5,M,5225 +1985,4,6,F,4273 +1985,4,6,M,4488 +1985,4,7,F,3980 +1985,4,7,M,4156 +1985,4,8,F,4987 +1985,4,8,M,5148 +1985,4,9,F,5106 +1985,4,9,M,5435 +1985,4,10,F,5128 +1985,4,10,M,5431 +1985,4,11,F,5168 +1985,4,11,M,5338 +1985,4,12,F,5112 +1985,4,12,M,5621 +1985,4,13,F,4283 +1985,4,13,M,4483 +1985,4,14,F,4095 +1985,4,14,M,4295 +1985,4,15,F,5176 +1985,4,15,M,5431 +1985,4,16,F,5391 +1985,4,16,M,5730 +1985,4,17,F,5049 +1985,4,17,M,5444 +1985,4,18,F,5087 +1985,4,18,M,5377 +1985,4,19,F,5391 +1985,4,19,M,5787 +1985,4,20,F,4327 +1985,4,20,M,4470 +1985,4,21,F,4183 +1985,4,21,M,4456 +1985,4,22,F,5171 +1985,4,22,M,5541 +1985,4,23,F,5313 +1985,4,23,M,5717 +1985,4,24,F,5081 +1985,4,24,M,5332 +1985,4,25,F,5188 +1985,4,25,M,5446 +1985,4,26,F,5180 +1985,4,26,M,5491 +1985,4,27,F,4349 +1985,4,27,M,4576 +1985,4,28,F,4016 +1985,4,28,M,4043 +1985,4,29,F,5104 +1985,4,29,M,5380 +1985,4,30,F,5229 +1985,4,30,M,5665 +1985,4,99,F,1 +1985,5,1,F,5279 +1985,5,1,M,5448 +1985,5,2,F,5180 +1985,5,2,M,5548 +1985,5,3,F,5205 +1985,5,3,M,5388 +1985,5,4,F,4203 +1985,5,4,M,4477 +1985,5,5,F,4263 +1985,5,5,M,4415 +1985,5,6,F,5170 +1985,5,6,M,5472 +1985,5,7,F,5374 +1985,5,7,M,5893 +1985,5,8,F,5195 +1985,5,8,M,5528 +1985,5,9,F,5170 +1985,5,9,M,5594 +1985,5,10,F,5281 +1985,5,10,M,5724 +1985,5,11,F,4333 +1985,5,11,M,4638 +1985,5,12,F,4213 +1985,5,12,M,4430 +1985,5,13,F,4901 +1985,5,13,M,5253 +1985,5,14,F,5311 +1985,5,14,M,5788 +1985,5,15,F,5291 +1985,5,15,M,5471 +1985,5,16,F,5094 +1985,5,16,M,5315 +1985,5,17,F,5284 +1985,5,17,M,5561 +1985,5,18,F,4305 +1985,5,18,M,4645 +1985,5,19,F,4108 +1985,5,19,M,4345 +1985,5,20,F,5297 +1985,5,20,M,5658 +1985,5,21,F,5226 +1985,5,21,M,5736 +1985,5,22,F,5393 +1985,5,22,M,5515 +1985,5,23,F,5234 +1985,5,23,M,5611 +1985,5,24,F,5485 +1985,5,24,M,5711 +1985,5,25,F,4379 +1985,5,25,M,4546 +1985,5,26,F,4238 +1985,5,26,M,4518 +1985,5,27,F,4294 +1985,5,27,M,4755 +1985,5,28,F,5248 +1985,5,28,M,5617 +1985,5,29,F,5471 +1985,5,29,M,5754 +1985,5,30,F,5272 +1985,5,30,M,5671 +1985,5,31,F,5446 +1985,5,31,M,5842 +1985,6,1,F,4376 +1985,6,1,M,4620 +1985,6,2,F,4175 +1985,6,2,M,4578 +1985,6,3,F,5108 +1985,6,3,M,5430 +1985,6,4,F,5214 +1985,6,4,M,5558 +1985,6,5,F,5268 +1985,6,5,M,5578 +1985,6,6,F,5271 +1985,6,6,M,5534 +1985,6,7,F,5358 +1985,6,7,M,5635 +1985,6,8,F,4406 +1985,6,8,M,4530 +1985,6,9,F,4257 +1985,6,9,M,4532 +1985,6,10,F,5308 +1985,6,10,M,5611 +1985,6,11,F,5357 +1985,6,11,M,5734 +1985,6,12,F,5158 +1985,6,12,M,5462 +1985,6,13,F,5097 +1985,6,13,M,5305 +1985,6,14,F,5396 +1985,6,14,M,5651 +1985,6,15,F,4337 +1985,6,15,M,4654 +1985,6,16,F,4235 +1985,6,16,M,4596 +1985,6,17,F,5395 +1985,6,17,M,5567 +1985,6,18,F,5472 +1985,6,18,M,5669 +1985,6,19,F,5287 +1985,6,19,M,5569 +1985,6,20,F,5475 +1985,6,20,M,5866 +1985,6,21,F,5461 +1985,6,21,M,5743 +1985,6,22,F,4480 +1985,6,22,M,4778 +1985,6,23,F,4267 +1985,6,23,M,4490 +1985,6,24,F,5396 +1985,6,24,M,5618 +1985,6,25,F,5467 +1985,6,25,M,5918 +1985,6,26,F,5303 +1985,6,26,M,5809 +1985,6,27,F,5316 +1985,6,27,M,5750 +1985,6,28,F,5520 +1985,6,28,M,5840 +1985,6,29,F,4584 +1985,6,29,M,4616 +1985,6,30,F,4298 +1985,6,30,M,4579 +1985,7,1,F,5463 +1985,7,1,M,5839 +1985,7,2,F,5794 +1985,7,2,M,6111 +1985,7,3,F,5628 +1985,7,3,M,6070 +1985,7,4,F,4460 +1985,7,4,M,4716 +1985,7,5,F,5552 +1985,7,5,M,5681 +1985,7,6,F,4552 +1985,7,6,M,4774 +1985,7,7,F,4474 +1985,7,7,M,4640 +1985,7,8,F,5457 +1985,7,8,M,5883 +1985,7,9,F,5807 +1985,7,9,M,6075 +1985,7,10,F,5603 +1985,7,10,M,6037 +1985,7,11,F,5347 +1985,7,11,M,5801 +1985,7,12,F,5567 +1985,7,12,M,5892 +1985,7,13,F,4514 +1985,7,13,M,4666 +1985,7,14,F,4359 +1985,7,14,M,4606 +1985,7,15,F,5594 +1985,7,15,M,5820 +1985,7,16,F,5625 +1985,7,16,M,6060 +1985,7,17,F,5443 +1985,7,17,M,5800 +1985,7,18,F,5462 +1985,7,18,M,5769 +1985,7,19,F,5598 +1985,7,19,M,5886 +1985,7,20,F,4540 +1985,7,20,M,4835 +1985,7,21,F,4383 +1985,7,21,M,4531 +1985,7,22,F,5421 +1985,7,22,M,5743 +1985,7,23,F,5596 +1985,7,23,M,5893 +1985,7,24,F,5401 +1985,7,24,M,5699 +1985,7,25,F,5510 +1985,7,25,M,5831 +1985,7,26,F,5510 +1985,7,26,M,5889 +1985,7,27,F,4647 +1985,7,27,M,4794 +1985,7,28,F,4329 +1985,7,28,M,4612 +1985,7,29,F,5615 +1985,7,29,M,5716 +1985,7,30,F,5805 +1985,7,30,M,5902 +1985,7,31,F,5420 +1985,7,31,M,5864 +1985,8,1,F,5364 +1985,8,1,M,5838 +1985,8,2,F,5384 +1985,8,2,M,5837 +1985,8,3,F,4607 +1985,8,3,M,4806 +1985,8,4,F,4316 +1985,8,4,M,4488 +1985,8,5,F,5392 +1985,8,5,M,5768 +1985,8,6,F,5626 +1985,8,6,M,5936 +1985,8,7,F,5588 +1985,8,7,M,5854 +1985,8,8,F,5661 +1985,8,8,M,5872 +1985,8,9,F,5622 +1985,8,9,M,5920 +1985,8,10,F,4559 +1985,8,10,M,4762 +1985,8,11,F,4531 +1985,8,11,M,4496 +1985,8,12,F,5501 +1985,8,12,M,5923 +1985,8,13,F,5665 +1985,8,13,M,5971 +1985,8,14,F,5583 +1985,8,14,M,5957 +1985,8,15,F,5686 +1985,8,15,M,6060 +1985,8,16,F,5685 +1985,8,16,M,5929 +1985,8,17,F,4653 +1985,8,17,M,5027 +1985,8,18,F,4517 +1985,8,18,M,4774 +1985,8,19,F,5524 +1985,8,19,M,5790 +1985,8,20,F,5871 +1985,8,20,M,6032 +1985,8,21,F,5556 +1985,8,21,M,5828 +1985,8,22,F,5578 +1985,8,22,M,5960 +1985,8,23,F,5655 +1985,8,23,M,5779 +1985,8,24,F,4613 +1985,8,24,M,4860 +1985,8,25,F,4423 +1985,8,25,M,4705 +1985,8,26,F,5595 +1985,8,26,M,5729 +1985,8,27,F,5667 +1985,8,27,M,6113 +1985,8,28,F,5470 +1985,8,28,M,5898 +1985,8,29,F,5645 +1985,8,29,M,5870 +1985,8,30,F,5729 +1985,8,30,M,6138 +1985,8,31,F,4801 +1985,8,31,M,5031 +1985,8,99,F,1 +1985,9,1,F,4466 +1985,9,1,M,4679 +1985,9,2,F,4477 +1985,9,2,M,4706 +1985,9,3,F,5517 +1985,9,3,M,5900 +1985,9,4,F,5786 +1985,9,4,M,6199 +1985,9,5,F,5793 +1985,9,5,M,6166 +1985,9,6,F,5757 +1985,9,6,M,6168 +1985,9,7,F,4652 +1985,9,7,M,4912 +1985,9,8,F,4490 +1985,9,8,M,4582 +1985,9,9,F,5534 +1985,9,9,M,6089 +1985,9,10,F,5813 +1985,9,10,M,6015 +1985,9,11,F,5630 +1985,9,11,M,5891 +1985,9,12,F,5819 +1985,9,12,M,6139 +1985,9,13,F,5522 +1985,9,13,M,5808 +1985,9,14,F,4608 +1985,9,14,M,4934 +1985,9,15,F,4547 +1985,9,15,M,4734 +1985,9,16,F,5765 +1985,9,16,M,6084 +1985,9,17,F,5837 +1985,9,17,M,6221 +1985,9,18,F,5763 +1985,9,18,M,6056 +1985,9,19,F,5897 +1985,9,19,M,6315 +1985,9,20,F,5886 +1985,9,20,M,6232 +1985,9,21,F,4959 +1985,9,21,M,5185 +1985,9,22,F,4792 +1985,9,22,M,5006 +1985,9,23,F,5829 +1985,9,23,M,6080 +1985,9,24,F,6191 +1985,9,24,M,6205 +1985,9,25,F,5740 +1985,9,25,M,5955 +1985,9,26,F,6051 +1985,9,26,M,5987 +1985,9,27,F,5889 +1985,9,27,M,6037 +1985,9,28,F,4658 +1985,9,28,M,4926 +1985,9,29,F,4510 +1985,9,29,M,4726 +1985,9,30,F,5667 +1985,9,30,M,5893 +1985,10,1,F,5690 +1985,10,1,M,6029 +1985,10,2,F,5484 +1985,10,2,M,5881 +1985,10,3,F,5644 +1985,10,3,M,5715 +1985,10,4,F,5878 +1985,10,4,M,5932 +1985,10,5,F,4685 +1985,10,5,M,4893 +1985,10,6,F,4321 +1985,10,6,M,4607 +1985,10,7,F,5459 +1985,10,7,M,5565 +1985,10,8,F,5515 +1985,10,8,M,5843 +1985,10,9,F,5270 +1985,10,9,M,5689 +1985,10,10,F,5405 +1985,10,10,M,5652 +1985,10,11,F,5448 +1985,10,11,M,5736 +1985,10,12,F,4334 +1985,10,12,M,4614 +1985,10,13,F,4258 +1985,10,13,M,4257 +1985,10,14,F,5198 +1985,10,14,M,5453 +1985,10,15,F,5497 +1985,10,15,M,5856 +1985,10,16,F,5286 +1985,10,16,M,5498 +1985,10,17,F,5237 +1985,10,17,M,5572 +1985,10,18,F,5197 +1985,10,18,M,5652 +1985,10,19,F,4323 +1985,10,19,M,4524 +1985,10,20,F,4082 +1985,10,20,M,4399 +1985,10,21,F,5292 +1985,10,21,M,5481 +1985,10,22,F,5332 +1985,10,22,M,5691 +1985,10,23,F,5241 +1985,10,23,M,5457 +1985,10,24,F,5222 +1985,10,24,M,5435 +1985,10,25,F,5299 +1985,10,25,M,5593 +1985,10,26,F,4274 +1985,10,26,M,4447 +1985,10,27,F,4338 +1985,10,27,M,4485 +1985,10,28,F,5066 +1985,10,28,M,5444 +1985,10,29,F,5201 +1985,10,29,M,5589 +1985,10,30,F,5208 +1985,10,30,M,5458 +1985,10,31,F,5050 +1985,10,31,M,5196 +1985,11,1,F,5431 +1985,11,1,M,5664 +1985,11,2,F,4422 +1985,11,2,M,4453 +1985,11,3,F,4159 +1985,11,3,M,4283 +1985,11,4,F,5360 +1985,11,4,M,5536 +1985,11,5,F,5404 +1985,11,5,M,5715 +1985,11,6,F,5259 +1985,11,6,M,5436 +1985,11,7,F,5319 +1985,11,7,M,5546 +1985,11,8,F,5259 +1985,11,8,M,5539 +1985,11,9,F,4323 +1985,11,9,M,4465 +1985,11,10,F,4099 +1985,11,10,M,4239 +1985,11,11,F,5077 +1985,11,11,M,5271 +1985,11,12,F,5364 +1985,11,12,M,5620 +1985,11,13,F,5130 +1985,11,13,M,5375 +1985,11,14,F,5220 +1985,11,14,M,5617 +1985,11,15,F,5299 +1985,11,15,M,5672 +1985,11,16,F,4189 +1985,11,16,M,4529 +1985,11,17,F,4167 +1985,11,17,M,4378 +1985,11,18,F,5242 +1985,11,18,M,5479 +1985,11,19,F,5212 +1985,11,19,M,5588 +1985,11,20,F,5258 +1985,11,20,M,5571 +1985,11,21,F,5146 +1985,11,21,M,5492 +1985,11,22,F,5452 +1985,11,22,M,5606 +1985,11,23,F,4265 +1985,11,23,M,4549 +1985,11,24,F,4152 +1985,11,24,M,4429 +1985,11,25,F,5200 +1985,11,25,M,5616 +1985,11,26,F,5479 +1985,11,26,M,5805 +1985,11,27,F,5193 +1985,11,27,M,5573 +1985,11,28,F,4020 +1985,11,28,M,4163 +1985,11,29,F,4779 +1985,11,29,M,5143 +1985,11,30,F,4263 +1985,11,30,M,4433 +1985,11,99,M,1 +1985,12,1,F,4154 +1985,12,1,M,4395 +1985,12,2,F,5166 +1985,12,2,M,5412 +1985,12,3,F,5448 +1985,12,3,M,5815 +1985,12,4,F,5195 +1985,12,4,M,5477 +1985,12,5,F,5156 +1985,12,5,M,5448 +1985,12,6,F,5025 +1985,12,6,M,5459 +1985,12,7,F,4169 +1985,12,7,M,4366 +1985,12,8,F,4097 +1985,12,8,M,4297 +1985,12,9,F,5215 +1985,12,9,M,5392 +1985,12,10,F,5487 +1985,12,10,M,5487 +1985,12,11,F,5111 +1985,12,11,M,5322 +1985,12,12,F,5242 +1985,12,12,M,5368 +1985,12,13,F,4937 +1985,12,13,M,5208 +1985,12,14,F,4306 +1985,12,14,M,4400 +1985,12,15,F,4107 +1985,12,15,M,4146 +1985,12,16,F,5388 +1985,12,16,M,5599 +1985,12,17,F,5598 +1985,12,17,M,5855 +1985,12,18,F,5439 +1985,12,18,M,5544 +1985,12,19,F,5356 +1985,12,19,M,5561 +1985,12,20,F,5521 +1985,12,20,M,5787 +1985,12,21,F,4264 +1985,12,21,M,4652 +1985,12,22,F,4006 +1985,12,22,M,4253 +1985,12,23,F,4860 +1985,12,23,M,4999 +1985,12,24,F,4488 +1985,12,24,M,4715 +1985,12,25,F,3948 +1985,12,25,M,4083 +1985,12,26,F,4914 +1985,12,26,M,5108 +1985,12,27,F,5572 +1985,12,27,M,5912 +1985,12,28,F,4554 +1985,12,28,M,4694 +1985,12,29,F,4197 +1985,12,29,M,4329 +1985,12,30,F,5700 +1985,12,30,M,5944 +1985,12,31,F,5560 +1985,12,31,M,5925 +1986,1,1,F,4112 +1986,1,1,M,4332 +1986,1,2,F,4550 +1986,1,2,M,4896 +1986,1,3,F,5016 +1986,1,3,M,5311 +1986,1,4,F,4227 +1986,1,4,M,4580 +1986,1,5,F,4193 +1986,1,5,M,4204 +1986,1,6,F,5006 +1986,1,6,M,5287 +1986,1,7,F,5241 +1986,1,7,M,5475 +1986,1,8,F,4910 +1986,1,8,M,5155 +1986,1,9,F,4876 +1986,1,9,M,5180 +1986,1,10,F,5204 +1986,1,10,M,5455 +1986,1,11,F,4291 +1986,1,11,M,4579 +1986,1,12,F,4145 +1986,1,12,M,4129 +1986,1,13,F,4991 +1986,1,13,M,5215 +1986,1,14,F,5319 +1986,1,14,M,5420 +1986,1,15,F,5071 +1986,1,15,M,5332 +1986,1,16,F,5100 +1986,1,16,M,5287 +1986,1,17,F,5247 +1986,1,17,M,5427 +1986,1,18,F,4304 +1986,1,18,M,4539 +1986,1,19,F,4142 +1986,1,19,M,4212 +1986,1,20,F,5170 +1986,1,20,M,5319 +1986,1,21,F,5327 +1986,1,21,M,5529 +1986,1,22,F,5082 +1986,1,22,M,5316 +1986,1,23,F,5071 +1986,1,23,M,5317 +1986,1,24,F,5089 +1986,1,24,M,5422 +1986,1,25,F,4373 +1986,1,25,M,4648 +1986,1,26,F,4081 +1986,1,26,M,4247 +1986,1,27,F,5153 +1986,1,27,M,5306 +1986,1,28,F,5168 +1986,1,28,M,5482 +1986,1,29,F,5112 +1986,1,29,M,5330 +1986,1,30,F,5018 +1986,1,30,M,5358 +1986,1,31,F,5150 +1986,1,31,M,5488 +1986,2,1,F,4219 +1986,2,1,M,4517 +1986,2,2,F,4164 +1986,2,2,M,4378 +1986,2,3,F,5080 +1986,2,3,M,5323 +1986,2,4,F,5237 +1986,2,4,M,5528 +1986,2,5,F,4951 +1986,2,5,M,5268 +1986,2,6,F,5173 +1986,2,6,M,5433 +1986,2,7,F,5200 +1986,2,7,M,5438 +1986,2,8,F,4171 +1986,2,8,M,4588 +1986,2,9,F,4079 +1986,2,9,M,4392 +1986,2,10,F,5197 +1986,2,10,M,5361 +1986,2,11,F,5159 +1986,2,11,M,5588 +1986,2,12,F,5181 +1986,2,12,M,5390 +1986,2,13,F,5107 +1986,2,13,M,5335 +1986,2,14,F,5652 +1986,2,14,M,5760 +1986,2,15,F,4379 +1986,2,15,M,4548 +1986,2,16,F,4052 +1986,2,16,M,4361 +1986,2,17,F,4944 +1986,2,17,M,5293 +1986,2,18,F,5341 +1986,2,18,M,5578 +1986,2,19,F,5347 +1986,2,19,M,5522 +1986,2,20,F,5266 +1986,2,20,M,5388 +1986,2,21,F,5297 +1986,2,21,M,5523 +1986,2,22,F,4399 +1986,2,22,M,4570 +1986,2,23,F,4098 +1986,2,23,M,4285 +1986,2,24,F,5141 +1986,2,24,M,5422 +1986,2,25,F,5237 +1986,2,25,M,5654 +1986,2,26,F,5334 +1986,2,26,M,5382 +1986,2,27,F,5255 +1986,2,27,M,5417 +1986,2,28,F,5322 +1986,2,28,M,5561 +1986,2,99,F,5 +1986,2,99,M,4 +1986,3,1,F,4216 +1986,3,1,M,4560 +1986,3,2,F,4143 +1986,3,2,M,4191 +1986,3,3,F,5312 +1986,3,3,M,5359 +1986,3,4,F,5301 +1986,3,4,M,5625 +1986,3,5,F,5250 +1986,3,5,M,5401 +1986,3,6,F,5164 +1986,3,6,M,5619 +1986,3,7,F,5361 +1986,3,7,M,5672 +1986,3,8,F,4351 +1986,3,8,M,4412 +1986,3,9,F,4116 +1986,3,9,M,4124 +1986,3,10,F,5239 +1986,3,10,M,5600 +1986,3,11,F,5289 +1986,3,11,M,5669 +1986,3,12,F,5252 +1986,3,12,M,5492 +1986,3,13,F,5128 +1986,3,13,M,5394 +1986,3,14,F,5370 +1986,3,14,M,5527 +1986,3,15,F,4346 +1986,3,15,M,4508 +1986,3,16,F,4059 +1986,3,16,M,4373 +1986,3,17,F,5194 +1986,3,17,M,5476 +1986,3,18,F,5262 +1986,3,18,M,5603 +1986,3,19,F,5213 +1986,3,19,M,5497 +1986,3,20,F,5208 +1986,3,20,M,5535 +1986,3,21,F,5292 +1986,3,21,M,5692 +1986,3,22,F,4179 +1986,3,22,M,4375 +1986,3,23,F,4098 +1986,3,23,M,4289 +1986,3,24,F,5119 +1986,3,24,M,5423 +1986,3,25,F,5338 +1986,3,25,M,5806 +1986,3,26,F,5205 +1986,3,26,M,5667 +1986,3,27,F,5443 +1986,3,27,M,5638 +1986,3,28,F,5283 +1986,3,28,M,5530 +1986,3,29,F,4237 +1986,3,29,M,4580 +1986,3,30,F,4167 +1986,3,30,M,4405 +1986,3,31,F,5240 +1986,3,31,M,5329 +1986,4,1,F,5135 +1986,4,1,M,5434 +1986,4,2,F,5200 +1986,4,2,M,5483 +1986,4,3,F,5339 +1986,4,3,M,5661 +1986,4,4,F,5283 +1986,4,4,M,5524 +1986,4,5,F,4374 +1986,4,5,M,4570 +1986,4,6,F,4053 +1986,4,6,M,4236 +1986,4,7,F,5330 +1986,4,7,M,5390 +1986,4,8,F,5483 +1986,4,8,M,5734 +1986,4,9,F,5127 +1986,4,9,M,5529 +1986,4,10,F,5237 +1986,4,10,M,5384 +1986,4,11,F,5092 +1986,4,11,M,5394 +1986,4,12,F,4166 +1986,4,12,M,4442 +1986,4,13,F,4097 +1986,4,13,M,4198 +1986,4,14,F,5147 +1986,4,14,M,5511 +1986,4,15,F,5295 +1986,4,15,M,5581 +1986,4,16,F,5229 +1986,4,16,M,5380 +1986,4,17,F,5202 +1986,4,17,M,5462 +1986,4,18,F,5195 +1986,4,18,M,5354 +1986,4,19,F,4252 +1986,4,19,M,4427 +1986,4,20,F,4093 +1986,4,20,M,4289 +1986,4,21,F,5044 +1986,4,21,M,5403 +1986,4,22,F,5366 +1986,4,22,M,5530 +1986,4,23,F,5008 +1986,4,23,M,5333 +1986,4,24,F,5240 +1986,4,24,M,5332 +1986,4,25,F,5429 +1986,4,25,M,5559 +1986,4,26,F,4381 +1986,4,26,M,4642 +1986,4,27,F,4036 +1986,4,27,M,4131 +1986,4,28,F,5224 +1986,4,28,M,5596 +1986,4,29,F,5383 +1986,4,29,M,5620 +1986,4,30,F,5262 +1986,4,30,M,5619 +1986,5,1,F,5424 +1986,5,1,M,5538 +1986,5,2,F,5237 +1986,5,2,M,5584 +1986,5,3,F,4104 +1986,5,3,M,4420 +1986,5,4,F,3870 +1986,5,4,M,4051 +1986,5,5,F,5133 +1986,5,5,M,5361 +1986,5,6,F,5376 +1986,5,6,M,5642 +1986,5,7,F,5289 +1986,5,7,M,5351 +1986,5,8,F,5202 +1986,5,8,M,5315 +1986,5,9,F,5241 +1986,5,9,M,5572 +1986,5,10,F,4364 +1986,5,10,M,4471 +1986,5,11,F,4067 +1986,5,11,M,4291 +1986,5,12,F,5226 +1986,5,12,M,5411 +1986,5,13,F,5237 +1986,5,13,M,5722 +1986,5,14,F,5186 +1986,5,14,M,5564 +1986,5,15,F,5247 +1986,5,15,M,5639 +1986,5,16,F,5377 +1986,5,16,M,5739 +1986,5,17,F,4344 +1986,5,17,M,4536 +1986,5,18,F,4288 +1986,5,18,M,4314 +1986,5,19,F,5209 +1986,5,19,M,5576 +1986,5,20,F,5403 +1986,5,20,M,5818 +1986,5,21,F,5125 +1986,5,21,M,5594 +1986,5,22,F,5327 +1986,5,22,M,5454 +1986,5,23,F,5574 +1986,5,23,M,5710 +1986,5,24,F,4351 +1986,5,24,M,4586 +1986,5,25,F,3996 +1986,5,25,M,4446 +1986,5,26,F,4446 +1986,5,26,M,4392 +1986,5,27,F,5397 +1986,5,27,M,5689 +1986,5,28,F,5497 +1986,5,28,M,5957 +1986,5,29,F,5482 +1986,5,29,M,5866 +1986,5,30,F,5589 +1986,5,30,M,5971 +1986,5,31,F,4502 +1986,5,31,M,4629 +1986,6,1,F,4146 +1986,6,1,M,4433 +1986,6,2,F,5261 +1986,6,2,M,5496 +1986,6,3,F,5296 +1986,6,3,M,5489 +1986,6,4,F,5193 +1986,6,4,M,5569 +1986,6,5,F,5303 +1986,6,5,M,5620 +1986,6,6,F,5369 +1986,6,6,M,5641 +1986,6,7,F,4193 +1986,6,7,M,4471 +1986,6,8,F,4037 +1986,6,8,M,4368 +1986,6,9,F,5154 +1986,6,9,M,5478 +1986,6,10,F,5287 +1986,6,10,M,5592 +1986,6,11,F,5424 +1986,6,11,M,5574 +1986,6,12,F,5411 +1986,6,12,M,5700 +1986,6,13,F,5013 +1986,6,13,M,5398 +1986,6,14,F,4160 +1986,6,14,M,4483 +1986,6,15,F,4203 +1986,6,15,M,4325 +1986,6,16,F,5390 +1986,6,16,M,5661 +1986,6,17,F,5537 +1986,6,17,M,5824 +1986,6,18,F,5245 +1986,6,18,M,5447 +1986,6,19,F,5161 +1986,6,19,M,5515 +1986,6,20,F,5353 +1986,6,20,M,5801 +1986,6,21,F,4270 +1986,6,21,M,4587 +1986,6,22,F,4125 +1986,6,22,M,4348 +1986,6,23,F,5429 +1986,6,23,M,5539 +1986,6,24,F,5519 +1986,6,24,M,6013 +1986,6,25,F,5309 +1986,6,25,M,5729 +1986,6,26,F,5315 +1986,6,26,M,5785 +1986,6,27,F,5408 +1986,6,27,M,5878 +1986,6,28,F,4337 +1986,6,28,M,4791 +1986,6,29,F,4302 +1986,6,29,M,4416 +1986,6,30,F,5278 +1986,6,30,M,5612 +1986,7,1,F,5741 +1986,7,1,M,5804 +1986,7,2,F,5482 +1986,7,2,M,5931 +1986,7,3,F,5433 +1986,7,3,M,5873 +1986,7,4,F,4416 +1986,7,4,M,4603 +1986,7,5,F,4491 +1986,7,5,M,4620 +1986,7,6,F,4275 +1986,7,6,M,4390 +1986,7,7,F,5560 +1986,7,7,M,5807 +1986,7,8,F,5917 +1986,7,8,M,6134 +1986,7,9,F,5588 +1986,7,9,M,5816 +1986,7,10,F,5743 +1986,7,10,M,5941 +1986,7,11,F,5469 +1986,7,11,M,5936 +1986,7,12,F,4524 +1986,7,12,M,4658 +1986,7,13,F,4319 +1986,7,13,M,4470 +1986,7,14,F,5364 +1986,7,14,M,5769 +1986,7,15,F,5656 +1986,7,15,M,5971 +1986,7,16,F,5511 +1986,7,16,M,5823 +1986,7,17,F,5471 +1986,7,17,M,5907 +1986,7,18,F,5581 +1986,7,18,M,5991 +1986,7,19,F,4651 +1986,7,19,M,4770 +1986,7,20,F,4407 +1986,7,20,M,4643 +1986,7,21,F,5449 +1986,7,21,M,5751 +1986,7,22,F,5971 +1986,7,22,M,6292 +1986,7,23,F,5641 +1986,7,23,M,5899 +1986,7,24,F,5534 +1986,7,24,M,5788 +1986,7,25,F,5838 +1986,7,25,M,5963 +1986,7,26,F,4524 +1986,7,26,M,4862 +1986,7,27,F,4350 +1986,7,27,M,4609 +1986,7,28,F,5503 +1986,7,28,M,5869 +1986,7,29,F,5734 +1986,7,29,M,6162 +1986,7,30,F,5471 +1986,7,30,M,5947 +1986,7,31,F,5486 +1986,7,31,M,5812 +1986,7,99,M,1 +1986,8,1,F,5756 +1986,8,1,M,5930 +1986,8,2,F,4574 +1986,8,2,M,4783 +1986,8,3,F,4353 +1986,8,3,M,4522 +1986,8,4,F,5529 +1986,8,4,M,5735 +1986,8,5,F,5629 +1986,8,5,M,6026 +1986,8,6,F,5610 +1986,8,6,M,6033 +1986,8,7,F,5486 +1986,8,7,M,5927 +1986,8,8,F,5777 +1986,8,8,M,5956 +1986,8,9,F,4732 +1986,8,9,M,4828 +1986,8,10,F,4484 +1986,8,10,M,4440 +1986,8,11,F,5473 +1986,8,11,M,5743 +1986,8,12,F,5637 +1986,8,12,M,6048 +1986,8,13,F,5407 +1986,8,13,M,5751 +1986,8,14,F,5387 +1986,8,14,M,5953 +1986,8,15,F,5669 +1986,8,15,M,5982 +1986,8,16,F,4617 +1986,8,16,M,4935 +1986,8,17,F,4537 +1986,8,17,M,4598 +1986,8,18,F,5481 +1986,8,18,M,5777 +1986,8,19,F,5771 +1986,8,19,M,6062 +1986,8,20,F,5660 +1986,8,20,M,6108 +1986,8,21,F,5655 +1986,8,21,M,5854 +1986,8,22,F,5599 +1986,8,22,M,5955 +1986,8,23,F,4576 +1986,8,23,M,4915 +1986,8,24,F,4375 +1986,8,24,M,4565 +1986,8,25,F,5547 +1986,8,25,M,5786 +1986,8,26,F,5788 +1986,8,26,M,6031 +1986,8,27,F,5733 +1986,8,27,M,5947 +1986,8,28,F,5567 +1986,8,28,M,5895 +1986,8,29,F,5650 +1986,8,29,M,6067 +1986,8,30,F,4461 +1986,8,30,M,4750 +1986,8,31,F,4257 +1986,8,31,M,4585 +1986,9,1,F,4315 +1986,9,1,M,4633 +1986,9,2,F,5516 +1986,9,2,M,5761 +1986,9,3,F,5946 +1986,9,3,M,5894 +1986,9,4,F,5671 +1986,9,4,M,6066 +1986,9,5,F,5849 +1986,9,5,M,6035 +1986,9,6,F,4675 +1986,9,6,M,5010 +1986,9,7,F,4372 +1986,9,7,M,4672 +1986,9,8,F,5470 +1986,9,8,M,5751 +1986,9,9,F,5808 +1986,9,9,M,6069 +1986,9,10,F,5594 +1986,9,10,M,5849 +1986,9,11,F,5648 +1986,9,11,M,6056 +1986,9,12,F,5916 +1986,9,12,M,6095 +1986,9,13,F,4626 +1986,9,13,M,4970 +1986,9,14,F,4522 +1986,9,14,M,4645 +1986,9,15,F,5772 +1986,9,15,M,5991 +1986,9,16,F,5978 +1986,9,16,M,6323 +1986,9,17,F,5771 +1986,9,17,M,5986 +1986,9,18,F,5837 +1986,9,18,M,6174 +1986,9,19,F,5858 +1986,9,19,M,6379 +1986,9,20,F,4796 +1986,9,20,M,5110 +1986,9,21,F,4595 +1986,9,21,M,4888 +1986,9,22,F,5933 +1986,9,22,M,6223 +1986,9,23,F,5883 +1986,9,23,M,6352 +1986,9,24,F,5927 +1986,9,24,M,6135 +1986,9,25,F,6056 +1986,9,25,M,6225 +1986,9,26,F,6042 +1986,9,26,M,6370 +1986,9,27,F,4706 +1986,9,27,M,5077 +1986,9,28,F,4462 +1986,9,28,M,4709 +1986,9,29,F,5743 +1986,9,29,M,5988 +1986,9,30,F,5840 +1986,9,30,M,6019 +1986,9,99,F,1 +1986,10,1,F,5851 +1986,10,1,M,5853 +1986,10,2,F,5653 +1986,10,2,M,5883 +1986,10,3,F,5557 +1986,10,3,M,5889 +1986,10,4,F,4578 +1986,10,4,M,4704 +1986,10,5,F,4266 +1986,10,5,M,4533 +1986,10,6,F,5431 +1986,10,6,M,5548 +1986,10,7,F,5562 +1986,10,7,M,5938 +1986,10,8,F,5358 +1986,10,8,M,5655 +1986,10,9,F,5371 +1986,10,9,M,5488 +1986,10,10,F,5426 +1986,10,10,M,5742 +1986,10,11,F,4268 +1986,10,11,M,4507 +1986,10,12,F,4083 +1986,10,12,M,4134 +1986,10,13,F,5026 +1986,10,13,M,5296 +1986,10,14,F,5414 +1986,10,14,M,5616 +1986,10,15,F,5461 +1986,10,15,M,5624 +1986,10,16,F,5277 +1986,10,16,M,5590 +1986,10,17,F,5341 +1986,10,17,M,5663 +1986,10,18,F,4337 +1986,10,18,M,4373 +1986,10,19,F,4030 +1986,10,19,M,4188 +1986,10,20,F,5093 +1986,10,20,M,5352 +1986,10,21,F,5272 +1986,10,21,M,5709 +1986,10,22,F,5065 +1986,10,22,M,5524 +1986,10,23,F,5170 +1986,10,23,M,5356 +1986,10,24,F,5151 +1986,10,24,M,5585 +1986,10,25,F,4227 +1986,10,25,M,4353 +1986,10,26,F,4159 +1986,10,26,M,4403 +1986,10,27,F,5105 +1986,10,27,M,5417 +1986,10,28,F,5249 +1986,10,28,M,5547 +1986,10,29,F,5120 +1986,10,29,M,5474 +1986,10,30,F,5108 +1986,10,30,M,5396 +1986,10,31,F,4915 +1986,10,31,M,5207 +1986,11,1,F,4253 +1986,11,1,M,4464 +1986,11,2,F,4053 +1986,11,2,M,4156 +1986,11,3,F,5099 +1986,11,3,M,5494 +1986,11,4,F,5315 +1986,11,4,M,5749 +1986,11,5,F,5185 +1986,11,5,M,5286 +1986,11,6,F,5252 +1986,11,6,M,5374 +1986,11,7,F,5212 +1986,11,7,M,5468 +1986,11,8,F,4271 +1986,11,8,M,4487 +1986,11,9,F,4048 +1986,11,9,M,4291 +1986,11,10,F,5085 +1986,11,10,M,5318 +1986,11,11,F,5281 +1986,11,11,M,5594 +1986,11,12,F,5126 +1986,11,12,M,5473 +1986,11,13,F,5074 +1986,11,13,M,5171 +1986,11,14,F,5233 +1986,11,14,M,5435 +1986,11,15,F,4159 +1986,11,15,M,4306 +1986,11,16,F,4012 +1986,11,16,M,4201 +1986,11,17,F,5205 +1986,11,17,M,5329 +1986,11,18,F,5277 +1986,11,18,M,5769 +1986,11,19,F,5170 +1986,11,19,M,5420 +1986,11,20,F,5204 +1986,11,20,M,5337 +1986,11,21,F,5320 +1986,11,21,M,5536 +1986,11,22,F,4148 +1986,11,22,M,4294 +1986,11,23,F,3965 +1986,11,23,M,4221 +1986,11,24,F,5160 +1986,11,24,M,5441 +1986,11,25,F,5450 +1986,11,25,M,5651 +1986,11,26,F,5103 +1986,11,26,M,5355 +1986,11,27,F,3911 +1986,11,27,M,4104 +1986,11,28,F,4725 +1986,11,28,M,4999 +1986,11,29,F,4092 +1986,11,29,M,4270 +1986,11,30,F,4008 +1986,11,30,M,4182 +1986,11,99,M,1 +1986,12,1,F,5273 +1986,12,1,M,5339 +1986,12,2,F,5426 +1986,12,2,M,5680 +1986,12,3,F,5239 +1986,12,3,M,5411 +1986,12,4,F,4936 +1986,12,4,M,5344 +1986,12,5,F,5068 +1986,12,5,M,5373 +1986,12,6,F,4019 +1986,12,6,M,4276 +1986,12,7,F,3946 +1986,12,7,M,4141 +1986,12,8,F,5157 +1986,12,8,M,5289 +1986,12,9,F,5368 +1986,12,9,M,5595 +1986,12,10,F,5176 +1986,12,10,M,5443 +1986,12,11,F,5124 +1986,12,11,M,5279 +1986,12,12,F,5245 +1986,12,12,M,5433 +1986,12,13,F,4187 +1986,12,13,M,4315 +1986,12,14,F,3970 +1986,12,14,M,4110 +1986,12,15,F,5249 +1986,12,15,M,5617 +1986,12,16,F,5575 +1986,12,16,M,5814 +1986,12,17,F,5534 +1986,12,17,M,5683 +1986,12,18,F,5435 +1986,12,18,M,5752 +1986,12,19,F,5686 +1986,12,19,M,5942 +1986,12,20,F,4337 +1986,12,20,M,4351 +1986,12,21,F,3971 +1986,12,21,M,4175 +1986,12,22,F,5152 +1986,12,22,M,5437 +1986,12,23,F,5174 +1986,12,23,M,5359 +1986,12,24,F,4457 +1986,12,24,M,4640 +1986,12,25,F,3861 +1986,12,25,M,4246 +1986,12,26,F,5112 +1986,12,26,M,5305 +1986,12,27,F,4371 +1986,12,27,M,4450 +1986,12,28,F,4072 +1986,12,28,M,4214 +1986,12,29,F,5532 +1986,12,29,M,5766 +1986,12,30,F,5900 +1986,12,30,M,6337 +1986,12,31,F,5490 +1986,12,31,M,5898 +1986,12,99,M,1 +1987,1,1,F,4105 +1987,1,1,M,4238 +1987,1,2,F,4660 +1987,1,2,M,4870 +1987,1,3,F,4349 +1987,1,3,M,4429 +1987,1,4,F,4107 +1987,1,4,M,4152 +1987,1,5,F,4949 +1987,1,5,M,5153 +1987,1,6,F,5366 +1987,1,6,M,5650 +1987,1,7,F,5098 +1987,1,7,M,5436 +1987,1,8,F,5060 +1987,1,8,M,5402 +1987,1,9,F,5056 +1987,1,9,M,5414 +1987,1,10,F,4168 +1987,1,10,M,4477 +1987,1,11,F,4043 +1987,1,11,M,4138 +1987,1,12,F,5081 +1987,1,12,M,5304 +1987,1,13,F,5230 +1987,1,13,M,5351 +1987,1,14,F,5006 +1987,1,14,M,5450 +1987,1,15,F,5089 +1987,1,15,M,5431 +1987,1,16,F,5168 +1987,1,16,M,5465 +1987,1,17,F,4230 +1987,1,17,M,4378 +1987,1,18,F,3913 +1987,1,18,M,4182 +1987,1,19,F,5026 +1987,1,19,M,5134 +1987,1,20,F,5249 +1987,1,20,M,5421 +1987,1,21,F,5203 +1987,1,21,M,5409 +1987,1,22,F,5158 +1987,1,22,M,5444 +1987,1,23,F,5024 +1987,1,23,M,5557 +1987,1,24,F,4214 +1987,1,24,M,4368 +1987,1,25,F,4109 +1987,1,25,M,4232 +1987,1,26,F,5047 +1987,1,26,M,5250 +1987,1,27,F,5291 +1987,1,27,M,5540 +1987,1,28,F,5314 +1987,1,28,M,5497 +1987,1,29,F,5133 +1987,1,29,M,5508 +1987,1,30,F,5220 +1987,1,30,M,5492 +1987,1,31,F,4349 +1987,1,31,M,4547 +1987,2,1,F,4074 +1987,2,1,M,4329 +1987,2,2,F,5167 +1987,2,2,M,5418 +1987,2,3,F,5390 +1987,2,3,M,5649 +1987,2,4,F,5149 +1987,2,4,M,5400 +1987,2,5,F,5056 +1987,2,5,M,5473 +1987,2,6,F,5139 +1987,2,6,M,5472 +1987,2,7,F,4232 +1987,2,7,M,4503 +1987,2,8,F,4044 +1987,2,8,M,4234 +1987,2,9,F,4906 +1987,2,9,M,5508 +1987,2,10,F,5295 +1987,2,10,M,5681 +1987,2,11,F,5151 +1987,2,11,M,5498 +1987,2,12,F,5398 +1987,2,12,M,5588 +1987,2,13,F,5054 +1987,2,13,M,5202 +1987,2,14,F,4552 +1987,2,14,M,4693 +1987,2,15,F,4040 +1987,2,15,M,4344 +1987,2,16,F,4894 +1987,2,16,M,5369 +1987,2,17,F,5323 +1987,2,17,M,5607 +1987,2,18,F,5279 +1987,2,18,M,5477 +1987,2,19,F,5139 +1987,2,19,M,5349 +1987,2,20,F,5480 +1987,2,20,M,5512 +1987,2,21,F,4275 +1987,2,21,M,4565 +1987,2,22,F,4268 +1987,2,22,M,4374 +1987,2,23,F,5267 +1987,2,23,M,5485 +1987,2,24,F,5396 +1987,2,24,M,5619 +1987,2,25,F,5253 +1987,2,25,M,5610 +1987,2,26,F,5117 +1987,2,26,M,5508 +1987,2,27,F,5416 +1987,2,27,M,5594 +1987,2,28,F,4331 +1987,2,28,M,4614 +1987,2,99,M,3 +1987,3,1,F,4196 +1987,3,1,M,4289 +1987,3,2,F,5317 +1987,3,2,M,5501 +1987,3,3,F,5323 +1987,3,3,M,5608 +1987,3,4,F,5185 +1987,3,4,M,5482 +1987,3,5,F,5460 +1987,3,5,M,5511 +1987,3,6,F,5503 +1987,3,6,M,5592 +1987,3,7,F,4229 +1987,3,7,M,4525 +1987,3,8,F,3926 +1987,3,8,M,4368 +1987,3,9,F,5226 +1987,3,9,M,5583 +1987,3,10,F,5469 +1987,3,10,M,5731 +1987,3,11,F,5214 +1987,3,11,M,5483 +1987,3,12,F,5363 +1987,3,12,M,5537 +1987,3,13,F,4988 +1987,3,13,M,5411 +1987,3,14,F,4315 +1987,3,14,M,4522 +1987,3,15,F,4130 +1987,3,15,M,4188 +1987,3,16,F,5322 +1987,3,16,M,5496 +1987,3,17,F,5490 +1987,3,17,M,5736 +1987,3,18,F,5366 +1987,3,18,M,5729 +1987,3,19,F,5381 +1987,3,19,M,5567 +1987,3,20,F,5493 +1987,3,20,M,5762 +1987,3,21,F,4437 +1987,3,21,M,4569 +1987,3,22,F,4077 +1987,3,22,M,4230 +1987,3,23,F,5375 +1987,3,23,M,5455 +1987,3,24,F,5470 +1987,3,24,M,5847 +1987,3,25,F,5209 +1987,3,25,M,5593 +1987,3,26,F,5291 +1987,3,26,M,5624 +1987,3,27,F,5488 +1987,3,27,M,5569 +1987,3,28,F,4260 +1987,3,28,M,4595 +1987,3,29,F,4059 +1987,3,29,M,4296 +1987,3,30,F,5333 +1987,3,30,M,5290 +1987,3,31,F,5376 +1987,3,31,M,5820 +1987,3,99,F,1 +1987,4,1,F,5096 +1987,4,1,M,5116 +1987,4,2,F,5386 +1987,4,2,M,5683 +1987,4,3,F,5368 +1987,4,3,M,5677 +1987,4,4,F,4333 +1987,4,4,M,4486 +1987,4,5,F,4008 +1987,4,5,M,4004 +1987,4,6,F,5339 +1987,4,6,M,5426 +1987,4,7,F,5499 +1987,4,7,M,5825 +1987,4,8,F,5267 +1987,4,8,M,5739 +1987,4,9,F,5140 +1987,4,9,M,5670 +1987,4,10,F,5557 +1987,4,10,M,5727 +1987,4,11,F,4432 +1987,4,11,M,4601 +1987,4,12,F,4136 +1987,4,12,M,4399 +1987,4,13,F,5110 +1987,4,13,M,5500 +1987,4,14,F,5457 +1987,4,14,M,5960 +1987,4,15,F,5399 +1987,4,15,M,5668 +1987,4,16,F,5468 +1987,4,16,M,5707 +1987,4,17,F,5116 +1987,4,17,M,5442 +1987,4,18,F,4313 +1987,4,18,M,4612 +1987,4,19,F,4038 +1987,4,19,M,4187 +1987,4,20,F,5129 +1987,4,20,M,5465 +1987,4,21,F,5416 +1987,4,21,M,5789 +1987,4,22,F,5330 +1987,4,22,M,5642 +1987,4,23,F,5284 +1987,4,23,M,5532 +1987,4,24,F,5244 +1987,4,24,M,5603 +1987,4,25,F,4218 +1987,4,25,M,4445 +1987,4,26,F,4015 +1987,4,26,M,4271 +1987,4,27,F,5093 +1987,4,27,M,5293 +1987,4,28,F,5363 +1987,4,28,M,5532 +1987,4,29,F,5157 +1987,4,29,M,5453 +1987,4,30,F,5282 +1987,4,30,M,5618 +1987,4,99,F,1 +1987,5,1,F,5361 +1987,5,1,M,5578 +1987,5,2,F,4268 +1987,5,2,M,4611 +1987,5,3,F,4079 +1987,5,3,M,4296 +1987,5,4,F,5209 +1987,5,4,M,5599 +1987,5,5,F,5381 +1987,5,5,M,5729 +1987,5,6,F,5325 +1987,5,6,M,5501 +1987,5,7,F,5324 +1987,5,7,M,5632 +1987,5,8,F,5425 +1987,5,8,M,5773 +1987,5,9,F,4335 +1987,5,9,M,4518 +1987,5,10,F,4172 +1987,5,10,M,4395 +1987,5,11,F,5377 +1987,5,11,M,5616 +1987,5,12,F,5621 +1987,5,12,M,5792 +1987,5,13,F,5182 +1987,5,13,M,5378 +1987,5,14,F,5427 +1987,5,14,M,5611 +1987,5,15,F,5416 +1987,5,15,M,5794 +1987,5,16,F,4403 +1987,5,16,M,4490 +1987,5,17,F,4036 +1987,5,17,M,4293 +1987,5,18,F,5503 +1987,5,18,M,5705 +1987,5,19,F,5587 +1987,5,19,M,5856 +1987,5,20,F,5415 +1987,5,20,M,5811 +1987,5,21,F,5499 +1987,5,21,M,5790 +1987,5,22,F,5659 +1987,5,22,M,5961 +1987,5,23,F,4302 +1987,5,23,M,4652 +1987,5,24,F,4130 +1987,5,24,M,4238 +1987,5,25,F,4279 +1987,5,25,M,4510 +1987,5,26,F,5463 +1987,5,26,M,5668 +1987,5,27,F,5744 +1987,5,27,M,6091 +1987,5,28,F,5579 +1987,5,28,M,5858 +1987,5,29,F,5674 +1987,5,29,M,6105 +1987,5,30,F,4529 +1987,5,30,M,4747 +1987,5,31,F,4307 +1987,5,31,M,4502 +1987,6,1,F,5325 +1987,6,1,M,5774 +1987,6,2,F,5520 +1987,6,2,M,6020 +1987,6,3,F,5458 +1987,6,3,M,5722 +1987,6,4,F,5477 +1987,6,4,M,5754 +1987,6,5,F,5616 +1987,6,5,M,5744 +1987,6,6,F,4549 +1987,6,6,M,4713 +1987,6,7,F,4142 +1987,6,7,M,4409 +1987,6,8,F,5412 +1987,6,8,M,5690 +1987,6,9,F,5633 +1987,6,9,M,5865 +1987,6,10,F,5504 +1987,6,10,M,5853 +1987,6,11,F,5420 +1987,6,11,M,5870 +1987,6,12,F,5585 +1987,6,12,M,5925 +1987,6,13,F,4617 +1987,6,13,M,4637 +1987,6,14,F,4189 +1987,6,14,M,4434 +1987,6,15,F,5511 +1987,6,15,M,5942 +1987,6,16,F,5611 +1987,6,16,M,6036 +1987,6,17,F,5587 +1987,6,17,M,5884 +1987,6,18,F,5538 +1987,6,18,M,5687 +1987,6,19,F,5546 +1987,6,19,M,5915 +1987,6,20,F,4473 +1987,6,20,M,4782 +1987,6,21,F,4381 +1987,6,21,M,4505 +1987,6,22,F,5457 +1987,6,22,M,5616 +1987,6,23,F,5558 +1987,6,23,M,5875 +1987,6,24,F,5449 +1987,6,24,M,5742 +1987,6,25,F,5509 +1987,6,25,M,5918 +1987,6,26,F,5617 +1987,6,26,M,5931 +1987,6,27,F,4396 +1987,6,27,M,4592 +1987,6,28,F,4393 +1987,6,28,M,4302 +1987,6,29,F,5286 +1987,6,29,M,5583 +1987,6,30,F,5701 +1987,6,30,M,6177 +1987,7,1,F,5511 +1987,7,1,M,5929 +1987,7,2,F,5736 +1987,7,2,M,6113 +1987,7,3,F,4927 +1987,7,3,M,5226 +1987,7,4,F,4410 +1987,7,4,M,4454 +1987,7,5,F,4208 +1987,7,5,M,4437 +1987,7,6,F,5372 +1987,7,6,M,5577 +1987,7,7,F,5737 +1987,7,7,M,6127 +1987,7,8,F,5562 +1987,7,8,M,5968 +1987,7,9,F,5560 +1987,7,9,M,5844 +1987,7,10,F,5623 +1987,7,10,M,6023 +1987,7,11,F,4554 +1987,7,11,M,4751 +1987,7,12,F,4298 +1987,7,12,M,4572 +1987,7,13,F,5419 +1987,7,13,M,5536 +1987,7,14,F,5757 +1987,7,14,M,6133 +1987,7,15,F,5483 +1987,7,15,M,5873 +1987,7,16,F,5570 +1987,7,16,M,5862 +1987,7,17,F,5639 +1987,7,17,M,5879 +1987,7,18,F,4292 +1987,7,18,M,4719 +1987,7,19,F,4269 +1987,7,19,M,4590 +1987,7,20,F,5611 +1987,7,20,M,5778 +1987,7,21,F,5754 +1987,7,21,M,6100 +1987,7,22,F,5836 +1987,7,22,M,5795 +1987,7,23,F,5614 +1987,7,23,M,5965 +1987,7,24,F,5750 +1987,7,24,M,6028 +1987,7,25,F,4723 +1987,7,25,M,4807 +1987,7,26,F,4408 +1987,7,26,M,4665 +1987,7,27,F,5661 +1987,7,27,M,5943 +1987,7,28,F,5826 +1987,7,28,M,6217 +1987,7,29,F,5609 +1987,7,29,M,5981 +1987,7,30,F,5649 +1987,7,30,M,5965 +1987,7,31,F,5550 +1987,7,31,M,5943 +1987,8,1,F,4472 +1987,8,1,M,4670 +1987,8,2,F,4369 +1987,8,2,M,4527 +1987,8,3,F,5488 +1987,8,3,M,5841 +1987,8,4,F,5780 +1987,8,4,M,5940 +1987,8,5,F,5505 +1987,8,5,M,5846 +1987,8,6,F,5509 +1987,8,6,M,5827 +1987,8,7,F,5772 +1987,8,7,M,5993 +1987,8,8,F,4590 +1987,8,8,M,4831 +1987,8,9,F,4220 +1987,8,9,M,4417 +1987,8,10,F,5655 +1987,8,10,M,5821 +1987,8,11,F,5783 +1987,8,11,M,6007 +1987,8,12,F,5440 +1987,8,12,M,5901 +1987,8,13,F,5609 +1987,8,13,M,5809 +1987,8,14,F,5560 +1987,8,14,M,5912 +1987,8,15,F,4605 +1987,8,15,M,4793 +1987,8,16,F,4376 +1987,8,16,M,4482 +1987,8,17,F,5655 +1987,8,17,M,5930 +1987,8,18,F,5789 +1987,8,18,M,6107 +1987,8,19,F,5501 +1987,8,19,M,5733 +1987,8,20,F,5650 +1987,8,20,M,5790 +1987,8,21,F,5748 +1987,8,21,M,5944 +1987,8,22,F,4579 +1987,8,22,M,4734 +1987,8,23,F,4419 +1987,8,23,M,4481 +1987,8,24,F,5379 +1987,8,24,M,5677 +1987,8,25,F,5721 +1987,8,25,M,5949 +1987,8,26,F,5590 +1987,8,26,M,5872 +1987,8,27,F,5591 +1987,8,27,M,5942 +1987,8,28,F,5688 +1987,8,28,M,5928 +1987,8,29,F,4426 +1987,8,29,M,4722 +1987,8,30,F,4314 +1987,8,30,M,4484 +1987,8,31,F,5376 +1987,8,31,M,5619 +1987,9,1,F,5718 +1987,9,1,M,6016 +1987,9,2,F,5650 +1987,9,2,M,5801 +1987,9,3,F,5482 +1987,9,3,M,5990 +1987,9,4,F,5823 +1987,9,4,M,6046 +1987,9,5,F,4626 +1987,9,5,M,4766 +1987,9,6,F,4298 +1987,9,6,M,4504 +1987,9,7,F,4587 +1987,9,7,M,4743 +1987,9,8,F,5751 +1987,9,8,M,5980 +1987,9,9,F,5926 +1987,9,9,M,6252 +1987,9,10,F,6028 +1987,9,10,M,6224 +1987,9,11,F,5880 +1987,9,11,M,6471 +1987,9,12,F,4604 +1987,9,12,M,4863 +1987,9,13,F,4409 +1987,9,13,M,4615 +1987,9,14,F,5667 +1987,9,14,M,5905 +1987,9,15,F,5905 +1987,9,15,M,6235 +1987,9,16,F,5856 +1987,9,16,M,6132 +1987,9,17,F,5853 +1987,9,17,M,6253 +1987,9,18,F,6008 +1987,9,18,M,6189 +1987,9,19,F,4848 +1987,9,19,M,4980 +1987,9,20,F,4637 +1987,9,20,M,4795 +1987,9,21,F,5766 +1987,9,21,M,6109 +1987,9,22,F,6007 +1987,9,22,M,6249 +1987,9,23,F,5720 +1987,9,23,M,5962 +1987,9,24,F,5765 +1987,9,24,M,6095 +1987,9,25,F,5953 +1987,9,25,M,6220 +1987,9,26,F,4746 +1987,9,26,M,5045 +1987,9,27,F,4455 +1987,9,27,M,4692 +1987,9,28,F,5549 +1987,9,28,M,5964 +1987,9,29,F,5922 +1987,9,29,M,6163 +1987,9,30,F,5766 +1987,9,30,M,5971 +1987,10,1,F,5742 +1987,10,1,M,6049 +1987,10,2,F,5744 +1987,10,2,M,6117 +1987,10,3,F,4484 +1987,10,3,M,4754 +1987,10,4,F,4251 +1987,10,4,M,4435 +1987,10,5,F,5565 +1987,10,5,M,5803 +1987,10,6,F,5714 +1987,10,6,M,5851 +1987,10,7,F,5405 +1987,10,7,M,5851 +1987,10,8,F,5527 +1987,10,8,M,5788 +1987,10,9,F,5436 +1987,10,9,M,5818 +1987,10,10,F,4443 +1987,10,10,M,4722 +1987,10,11,F,4149 +1987,10,11,M,4394 +1987,10,12,F,5295 +1987,10,12,M,5576 +1987,10,13,F,5601 +1987,10,13,M,5832 +1987,10,14,F,5589 +1987,10,14,M,5785 +1987,10,15,F,5527 +1987,10,15,M,5892 +1987,10,16,F,5551 +1987,10,16,M,5689 +1987,10,17,F,4471 +1987,10,17,M,4501 +1987,10,18,F,4065 +1987,10,18,M,4205 +1987,10,19,F,5338 +1987,10,19,M,5506 +1987,10,20,F,5552 +1987,10,20,M,5828 +1987,10,21,F,5426 +1987,10,21,M,5764 +1987,10,22,F,5237 +1987,10,22,M,5556 +1987,10,23,F,5496 +1987,10,23,M,5664 +1987,10,24,F,4318 +1987,10,24,M,4530 +1987,10,25,F,4358 +1987,10,25,M,4491 +1987,10,26,F,5332 +1987,10,26,M,5524 +1987,10,27,F,5452 +1987,10,27,M,5664 +1987,10,28,F,5383 +1987,10,28,M,5663 +1987,10,29,F,5397 +1987,10,29,M,5703 +1987,10,30,F,5335 +1987,10,30,M,5711 +1987,10,31,F,4378 +1987,10,31,M,4507 +1987,11,1,F,4158 +1987,11,1,M,4305 +1987,11,2,F,5245 +1987,11,2,M,5546 +1987,11,3,F,5489 +1987,11,3,M,5738 +1987,11,4,F,5330 +1987,11,4,M,5802 +1987,11,5,F,5397 +1987,11,5,M,5580 +1987,11,6,F,5385 +1987,11,6,M,5714 +1987,11,7,F,4270 +1987,11,7,M,4544 +1987,11,8,F,4133 +1987,11,8,M,4300 +1987,11,9,F,5318 +1987,11,9,M,5577 +1987,11,10,F,5635 +1987,11,10,M,5755 +1987,11,11,F,5378 +1987,11,11,M,5628 +1987,11,12,F,5382 +1987,11,12,M,5624 +1987,11,13,F,5103 +1987,11,13,M,5539 +1987,11,14,F,4269 +1987,11,14,M,4608 +1987,11,15,F,4158 +1987,11,15,M,4365 +1987,11,16,F,5390 +1987,11,16,M,5627 +1987,11,17,F,5602 +1987,11,17,M,5841 +1987,11,18,F,5365 +1987,11,18,M,5655 +1987,11,19,F,5229 +1987,11,19,M,5599 +1987,11,20,F,5508 +1987,11,20,M,5741 +1987,11,21,F,4370 +1987,11,21,M,4521 +1987,11,22,F,4168 +1987,11,22,M,4308 +1987,11,23,F,5528 +1987,11,23,M,5742 +1987,11,24,F,5666 +1987,11,24,M,5879 +1987,11,25,F,5439 +1987,11,25,M,5686 +1987,11,26,F,4138 +1987,11,26,M,4180 +1987,11,27,F,4895 +1987,11,27,M,5119 +1987,11,28,F,4251 +1987,11,28,M,4434 +1987,11,29,F,4196 +1987,11,29,M,4220 +1987,11,30,F,5380 +1987,11,30,M,5705 +1987,12,1,F,5744 +1987,12,1,M,6063 +1987,12,2,F,5329 +1987,12,2,M,5607 +1987,12,3,F,5303 +1987,12,3,M,5562 +1987,12,4,F,5340 +1987,12,4,M,5555 +1987,12,5,F,4250 +1987,12,5,M,4405 +1987,12,6,F,4205 +1987,12,6,M,4278 +1987,12,7,F,5246 +1987,12,7,M,5421 +1987,12,8,F,5464 +1987,12,8,M,5715 +1987,12,9,F,5336 +1987,12,9,M,5508 +1987,12,10,F,5297 +1987,12,10,M,5640 +1987,12,11,F,5531 +1987,12,11,M,5504 +1987,12,12,F,4230 +1987,12,12,M,4577 +1987,12,13,F,4115 +1987,12,13,M,4124 +1987,12,14,F,5343 +1987,12,14,M,5590 +1987,12,15,F,5627 +1987,12,15,M,5906 +1987,12,16,F,5484 +1987,12,16,M,5648 +1987,12,17,F,5608 +1987,12,17,M,5786 +1987,12,18,F,5751 +1987,12,18,M,6121 +1987,12,19,F,4324 +1987,12,19,M,4596 +1987,12,20,F,4040 +1987,12,20,M,4320 +1987,12,21,F,5636 +1987,12,21,M,5895 +1987,12,22,F,5480 +1987,12,22,M,5682 +1987,12,23,F,4988 +1987,12,23,M,5313 +1987,12,24,F,4308 +1987,12,24,M,4611 +1987,12,25,F,3782 +1987,12,25,M,4099 +1987,12,26,F,4136 +1987,12,26,M,4310 +1987,12,27,F,4209 +1987,12,27,M,4332 +1987,12,28,F,5555 +1987,12,28,M,5907 +1987,12,29,F,5859 +1987,12,29,M,6322 +1987,12,30,F,5792 +1987,12,30,M,6051 +1987,12,31,F,5344 +1987,12,31,M,5468 +1988,1,1,F,4149 +1988,1,1,M,4345 +1988,1,2,F,3874 +1988,1,2,M,4175 +1988,1,3,F,3981 +1988,1,3,M,4196 +1988,1,4,F,5009 +1988,1,4,M,5193 +1988,1,5,F,5244 +1988,1,5,M,5683 +1988,1,6,F,5197 +1988,1,6,M,5472 +1988,1,7,F,5255 +1988,1,7,M,5559 +1988,1,8,F,5295 +1988,1,8,M,5453 +1988,1,9,F,4202 +1988,1,9,M,4443 +1988,1,10,F,4039 +1988,1,10,M,4260 +1988,1,11,F,5250 +1988,1,11,M,5402 +1988,1,12,F,5464 +1988,1,12,M,5709 +1988,1,13,F,5230 +1988,1,13,M,5455 +1988,1,14,F,5372 +1988,1,14,M,5409 +1988,1,15,F,5374 +1988,1,15,M,5767 +1988,1,16,F,4321 +1988,1,16,M,4514 +1988,1,17,F,4263 +1988,1,17,M,4253 +1988,1,18,F,5201 +1988,1,18,M,5416 +1988,1,19,F,5427 +1988,1,19,M,5729 +1988,1,20,F,5331 +1988,1,20,M,5670 +1988,1,21,F,5352 +1988,1,21,M,5597 +1988,1,22,F,5304 +1988,1,22,M,5623 +1988,1,23,F,4275 +1988,1,23,M,4453 +1988,1,24,F,4268 +1988,1,24,M,4305 +1988,1,25,F,5268 +1988,1,25,M,5516 +1988,1,26,F,5275 +1988,1,26,M,5696 +1988,1,27,F,5267 +1988,1,27,M,5598 +1988,1,28,F,5092 +1988,1,28,M,5597 +1988,1,29,F,5436 +1988,1,29,M,5583 +1988,1,30,F,4334 +1988,1,30,M,4565 +1988,1,31,F,4225 +1988,1,31,M,4290 +1988,2,1,F,5351 +1988,2,1,M,5396 +1988,2,2,F,5528 +1988,2,2,M,5842 +1988,2,3,F,5315 +1988,2,3,M,5564 +1988,2,4,F,5202 +1988,2,4,M,5600 +1988,2,5,F,5339 +1988,2,5,M,5605 +1988,2,6,F,4248 +1988,2,6,M,4473 +1988,2,7,F,4143 +1988,2,7,M,4167 +1988,2,8,F,5260 +1988,2,8,M,5364 +1988,2,9,F,5508 +1988,2,9,M,5760 +1988,2,10,F,5432 +1988,2,10,M,5738 +1988,2,11,F,5398 +1988,2,11,M,5622 +1988,2,12,F,5446 +1988,2,12,M,5714 +1988,2,13,F,4300 +1988,2,13,M,4420 +1988,2,14,F,4257 +1988,2,14,M,4473 +1988,2,15,F,5046 +1988,2,15,M,5314 +1988,2,16,F,5510 +1988,2,16,M,5749 +1988,2,17,F,5397 +1988,2,17,M,5765 +1988,2,18,F,5381 +1988,2,18,M,5659 +1988,2,19,F,5432 +1988,2,19,M,5697 +1988,2,20,F,4411 +1988,2,20,M,4797 +1988,2,21,F,4221 +1988,2,21,M,4201 +1988,2,22,F,5280 +1988,2,22,M,5553 +1988,2,23,F,5383 +1988,2,23,M,5618 +1988,2,24,F,5105 +1988,2,24,M,5576 +1988,2,25,F,5255 +1988,2,25,M,5537 +1988,2,26,F,5403 +1988,2,26,M,5681 +1988,2,27,F,4445 +1988,2,27,M,4573 +1988,2,28,F,4223 +1988,2,28,M,4276 +1988,2,29,F,4859 +1988,2,29,M,4939 +1988,3,1,F,5615 +1988,3,1,M,5886 +1988,3,2,F,5440 +1988,3,2,M,5577 +1988,3,3,F,5417 +1988,3,3,M,5673 +1988,3,4,F,5290 +1988,3,4,M,5720 +1988,3,5,F,4391 +1988,3,5,M,4526 +1988,3,6,F,4126 +1988,3,6,M,4257 +1988,3,7,F,5152 +1988,3,7,M,5384 +1988,3,8,F,5490 +1988,3,8,M,5819 +1988,3,9,F,5176 +1988,3,9,M,5545 +1988,3,10,F,5362 +1988,3,10,M,5511 +1988,3,11,F,5263 +1988,3,11,M,5527 +1988,3,12,F,4183 +1988,3,12,M,4540 +1988,3,13,F,3964 +1988,3,13,M,4327 +1988,3,14,F,5106 +1988,3,14,M,5273 +1988,3,15,F,5451 +1988,3,15,M,5652 +1988,3,16,F,5439 +1988,3,16,M,5604 +1988,3,17,F,5319 +1988,3,17,M,5599 +1988,3,18,F,5448 +1988,3,18,M,5641 +1988,3,19,F,4238 +1988,3,19,M,4517 +1988,3,20,F,4239 +1988,3,20,M,4407 +1988,3,21,F,5301 +1988,3,21,M,5605 +1988,3,22,F,5431 +1988,3,22,M,5857 +1988,3,23,F,5344 +1988,3,23,M,5636 +1988,3,24,F,5423 +1988,3,24,M,5622 +1988,3,25,F,5587 +1988,3,25,M,5692 +1988,3,26,F,4466 +1988,3,26,M,4702 +1988,3,27,F,4208 +1988,3,27,M,4326 +1988,3,28,F,5270 +1988,3,28,M,5510 +1988,3,29,F,5517 +1988,3,29,M,5888 +1988,3,30,F,5338 +1988,3,30,M,5544 +1988,3,31,F,5205 +1988,3,31,M,5675 +1988,3,99,F,1 +1988,3,99,M,1 +1988,4,1,F,4990 +1988,4,1,M,5308 +1988,4,2,F,4434 +1988,4,2,M,4531 +1988,4,3,F,3947 +1988,4,3,M,4181 +1988,4,4,F,5438 +1988,4,4,M,5583 +1988,4,5,F,5468 +1988,4,5,M,6054 +1988,4,6,F,5467 +1988,4,6,M,5617 +1988,4,7,F,5190 +1988,4,7,M,5696 +1988,4,8,F,5380 +1988,4,8,M,5681 +1988,4,9,F,4179 +1988,4,9,M,4522 +1988,4,10,F,4197 +1988,4,10,M,4305 +1988,4,11,F,5332 +1988,4,11,M,5590 +1988,4,12,F,5462 +1988,4,12,M,5770 +1988,4,13,F,5186 +1988,4,13,M,5348 +1988,4,14,F,5215 +1988,4,14,M,5662 +1988,4,15,F,5426 +1988,4,15,M,5689 +1988,4,16,F,4328 +1988,4,16,M,4568 +1988,4,17,F,4047 +1988,4,17,M,4253 +1988,4,18,F,5380 +1988,4,18,M,5554 +1988,4,19,F,5295 +1988,4,19,M,5749 +1988,4,20,F,5361 +1988,4,20,M,5659 +1988,4,21,F,5265 +1988,4,21,M,5677 +1988,4,22,F,5558 +1988,4,22,M,5736 +1988,4,23,F,4444 +1988,4,23,M,4721 +1988,4,24,F,4098 +1988,4,24,M,4387 +1988,4,25,F,5378 +1988,4,25,M,5626 +1988,4,26,F,5611 +1988,4,26,M,5789 +1988,4,27,F,5279 +1988,4,27,M,5782 +1988,4,28,F,5538 +1988,4,28,M,5791 +1988,4,29,F,5245 +1988,4,29,M,5591 +1988,4,30,F,4367 +1988,4,30,M,4537 +1988,4,99,F,1 +1988,5,1,F,4151 +1988,5,1,M,4323 +1988,5,2,F,5067 +1988,5,2,M,5497 +1988,5,3,F,5292 +1988,5,3,M,5803 +1988,5,4,F,5308 +1988,5,4,M,5623 +1988,5,5,F,5382 +1988,5,5,M,5656 +1988,5,6,F,5391 +1988,5,6,M,5880 +1988,5,7,F,4323 +1988,5,7,M,4668 +1988,5,8,F,4285 +1988,5,8,M,4407 +1988,5,9,F,5323 +1988,5,9,M,5754 +1988,5,10,F,5552 +1988,5,10,M,6112 +1988,5,11,F,5542 +1988,5,11,M,5777 +1988,5,12,F,5421 +1988,5,12,M,5786 +1988,5,13,F,5285 +1988,5,13,M,5646 +1988,5,14,F,4434 +1988,5,14,M,4515 +1988,5,15,F,4304 +1988,5,15,M,4460 +1988,5,16,F,5593 +1988,5,16,M,5674 +1988,5,17,F,5684 +1988,5,17,M,5909 +1988,5,18,F,5460 +1988,5,18,M,5851 +1988,5,19,F,5347 +1988,5,19,M,5763 +1988,5,20,F,5559 +1988,5,20,M,5893 +1988,5,21,F,4385 +1988,5,21,M,4570 +1988,5,22,F,4264 +1988,5,22,M,4552 +1988,5,23,F,5536 +1988,5,23,M,5819 +1988,5,24,F,5690 +1988,5,24,M,6172 +1988,5,25,F,5535 +1988,5,25,M,5929 +1988,5,26,F,5628 +1988,5,26,M,5866 +1988,5,27,F,5598 +1988,5,27,M,6114 +1988,5,28,F,4559 +1988,5,28,M,4783 +1988,5,29,F,4249 +1988,5,29,M,4492 +1988,5,30,F,4493 +1988,5,30,M,4760 +1988,5,31,F,5711 +1988,5,31,M,5875 +1988,5,99,F,1 +1988,6,1,F,5838 +1988,6,1,M,6199 +1988,6,2,F,5845 +1988,6,2,M,6066 +1988,6,3,F,5570 +1988,6,3,M,5892 +1988,6,4,F,4461 +1988,6,4,M,4758 +1988,6,5,F,4354 +1988,6,5,M,4437 +1988,6,6,F,5608 +1988,6,6,M,5784 +1988,6,7,F,5618 +1988,6,7,M,5955 +1988,6,8,F,5631 +1988,6,8,M,5864 +1988,6,9,F,5682 +1988,6,9,M,5837 +1988,6,10,F,5618 +1988,6,10,M,5931 +1988,6,11,F,4502 +1988,6,11,M,4597 +1988,6,12,F,4226 +1988,6,12,M,4448 +1988,6,13,F,5350 +1988,6,13,M,5747 +1988,6,14,F,5773 +1988,6,14,M,6142 +1988,6,15,F,5704 +1988,6,15,M,5947 +1988,6,16,F,5684 +1988,6,16,M,5972 +1988,6,17,F,5683 +1988,6,17,M,5956 +1988,6,18,F,4536 +1988,6,18,M,4844 +1988,6,19,F,4413 +1988,6,19,M,4625 +1988,6,20,F,5629 +1988,6,20,M,5923 +1988,6,21,F,5844 +1988,6,21,M,6196 +1988,6,22,F,5676 +1988,6,22,M,6103 +1988,6,23,F,5712 +1988,6,23,M,6074 +1988,6,24,F,5683 +1988,6,24,M,6015 +1988,6,25,F,4598 +1988,6,25,M,4872 +1988,6,26,F,4404 +1988,6,26,M,4454 +1988,6,27,F,5413 +1988,6,27,M,5753 +1988,6,28,F,5910 +1988,6,28,M,6255 +1988,6,29,F,5737 +1988,6,29,M,5892 +1988,6,30,F,5697 +1988,6,30,M,6283 +1988,6,99,F,1 +1988,6,99,M,1 +1988,7,1,F,5842 +1988,7,1,M,5983 +1988,7,2,F,4597 +1988,7,2,M,5014 +1988,7,3,F,4432 +1988,7,3,M,4556 +1988,7,4,F,4587 +1988,7,4,M,4672 +1988,7,5,F,5742 +1988,7,5,M,5973 +1988,7,6,F,6070 +1988,7,6,M,6418 +1988,7,7,F,6200 +1988,7,7,M,6527 +1988,7,8,F,6007 +1988,7,8,M,6417 +1988,7,9,F,4784 +1988,7,9,M,5139 +1988,7,10,F,4628 +1988,7,10,M,4780 +1988,7,11,F,5845 +1988,7,11,M,6134 +1988,7,12,F,5812 +1988,7,12,M,6256 +1988,7,13,F,5691 +1988,7,13,M,6062 +1988,7,14,F,5820 +1988,7,14,M,6133 +1988,7,15,F,6054 +1988,7,15,M,6375 +1988,7,16,F,4787 +1988,7,16,M,5048 +1988,7,17,F,4567 +1988,7,17,M,4741 +1988,7,18,F,5763 +1988,7,18,M,5936 +1988,7,19,F,5911 +1988,7,19,M,6274 +1988,7,20,F,5971 +1988,7,20,M,6125 +1988,7,21,F,5828 +1988,7,21,M,6099 +1988,7,22,F,5894 +1988,7,22,M,6218 +1988,7,23,F,4715 +1988,7,23,M,5081 +1988,7,24,F,4483 +1988,7,24,M,4646 +1988,7,25,F,5676 +1988,7,25,M,5965 +1988,7,26,F,6047 +1988,7,26,M,6297 +1988,7,27,F,5897 +1988,7,27,M,6267 +1988,7,28,F,5872 +1988,7,28,M,6323 +1988,7,29,F,5959 +1988,7,29,M,6345 +1988,7,30,F,5033 +1988,7,30,M,5152 +1988,7,31,F,4636 +1988,7,31,M,4725 +1988,8,1,F,5832 +1988,8,1,M,5974 +1988,8,2,F,5969 +1988,8,2,M,6196 +1988,8,3,F,5831 +1988,8,3,M,6114 +1988,8,4,F,5841 +1988,8,4,M,6315 +1988,8,5,F,5976 +1988,8,5,M,6198 +1988,8,6,F,4925 +1988,8,6,M,5030 +1988,8,7,F,4517 +1988,8,7,M,4690 +1988,8,8,F,6043 +1988,8,8,M,6370 +1988,8,9,F,6033 +1988,8,9,M,6249 +1988,8,10,F,5853 +1988,8,10,M,6162 +1988,8,11,F,5929 +1988,8,11,M,6275 +1988,8,12,F,5874 +1988,8,12,M,6352 +1988,8,13,F,4876 +1988,8,13,M,5041 +1988,8,14,F,4606 +1988,8,14,M,4828 +1988,8,15,F,5635 +1988,8,15,M,6134 +1988,8,16,F,6102 +1988,8,16,M,6332 +1988,8,17,F,5775 +1988,8,17,M,6221 +1988,8,18,F,6070 +1988,8,18,M,6258 +1988,8,19,F,5942 +1988,8,19,M,6225 +1988,8,20,F,4781 +1988,8,20,M,4981 +1988,8,21,F,4581 +1988,8,21,M,4690 +1988,8,22,F,5677 +1988,8,22,M,6006 +1988,8,23,F,5985 +1988,8,23,M,6251 +1988,8,24,F,5811 +1988,8,24,M,6120 +1988,8,25,F,5929 +1988,8,25,M,6177 +1988,8,26,F,5955 +1988,8,26,M,6288 +1988,8,27,F,4729 +1988,8,27,M,4995 +1988,8,28,F,4475 +1988,8,28,M,4795 +1988,8,29,F,5619 +1988,8,29,M,5978 +1988,8,30,F,6071 +1988,8,30,M,6209 +1988,8,31,F,5846 +1988,8,31,M,6057 +1988,9,1,F,5759 +1988,9,1,M,6108 +1988,9,2,F,6016 +1988,9,2,M,6235 +1988,9,3,F,4875 +1988,9,3,M,5091 +1988,9,4,F,4513 +1988,9,4,M,4737 +1988,9,5,F,4630 +1988,9,5,M,4662 +1988,9,6,F,5841 +1988,9,6,M,6243 +1988,9,7,F,6111 +1988,9,7,M,6398 +1988,9,8,F,6034 +1988,9,8,M,6317 +1988,9,9,F,6187 +1988,9,9,M,6474 +1988,9,10,F,4833 +1988,9,10,M,5135 +1988,9,11,F,4663 +1988,9,11,M,4842 +1988,9,12,F,5854 +1988,9,12,M,6208 +1988,9,13,F,6156 +1988,9,13,M,6295 +1988,9,14,F,6149 +1988,9,14,M,6402 +1988,9,15,F,5979 +1988,9,15,M,6419 +1988,9,16,F,6151 +1988,9,16,M,6469 +1988,9,17,F,5097 +1988,9,17,M,5265 +1988,9,18,F,4904 +1988,9,18,M,4944 +1988,9,19,F,6014 +1988,9,19,M,6318 +1988,9,20,F,6332 +1988,9,20,M,6519 +1988,9,21,F,6097 +1988,9,21,M,6329 +1988,9,22,F,6081 +1988,9,22,M,6473 +1988,9,23,F,6212 +1988,9,23,M,6482 +1988,9,24,F,4853 +1988,9,24,M,5125 +1988,9,25,F,4598 +1988,9,25,M,4891 +1988,9,26,F,5897 +1988,9,26,M,6140 +1988,9,27,F,6140 +1988,9,27,M,6314 +1988,9,28,F,5868 +1988,9,28,M,6088 +1988,9,29,F,6111 +1988,9,29,M,6136 +1988,9,30,F,5968 +1988,9,30,M,6295 +1988,10,1,F,4916 +1988,10,1,M,4886 +1988,10,2,F,4410 +1988,10,2,M,4681 +1988,10,3,F,5754 +1988,10,3,M,5930 +1988,10,4,F,5867 +1988,10,4,M,6205 +1988,10,5,F,5640 +1988,10,5,M,5978 +1988,10,6,F,5756 +1988,10,6,M,5984 +1988,10,7,F,5775 +1988,10,7,M,5992 +1988,10,8,F,4615 +1988,10,8,M,4715 +1988,10,9,F,4379 +1988,10,9,M,4705 +1988,10,10,F,5479 +1988,10,10,M,5888 +1988,10,11,F,5805 +1988,10,11,M,6031 +1988,10,12,F,5719 +1988,10,12,M,5844 +1988,10,13,F,5434 +1988,10,13,M,5744 +1988,10,14,F,5753 +1988,10,14,M,5865 +1988,10,15,F,4632 +1988,10,15,M,4749 +1988,10,16,F,4312 +1988,10,16,M,4439 +1988,10,17,F,5687 +1988,10,17,M,5816 +1988,10,18,F,5814 +1988,10,18,M,6079 +1988,10,19,F,5505 +1988,10,19,M,5601 +1988,10,20,F,5509 +1988,10,20,M,5820 +1988,10,21,F,5421 +1988,10,21,M,5776 +1988,10,22,F,4437 +1988,10,22,M,4778 +1988,10,23,F,4250 +1988,10,23,M,4421 +1988,10,24,F,5474 +1988,10,24,M,5787 +1988,10,25,F,5704 +1988,10,25,M,5946 +1988,10,26,F,5576 +1988,10,26,M,5840 +1988,10,27,F,5499 +1988,10,27,M,5584 +1988,10,28,F,5590 +1988,10,28,M,5918 +1988,10,29,F,4354 +1988,10,29,M,4635 +1988,10,30,F,4402 +1988,10,30,M,4548 +1988,10,31,F,4893 +1988,10,31,M,5086 +1988,10,99,M,1 +1988,11,1,F,5826 +1988,11,1,M,6036 +1988,11,2,F,5405 +1988,11,2,M,5645 +1988,11,3,F,5423 +1988,11,3,M,5897 +1988,11,4,F,5555 +1988,11,4,M,5997 +1988,11,5,F,4424 +1988,11,5,M,4733 +1988,11,6,F,4276 +1988,11,6,M,4457 +1988,11,7,F,5368 +1988,11,7,M,5816 +1988,11,8,F,5492 +1988,11,8,M,5882 +1988,11,9,F,5511 +1988,11,9,M,5616 +1988,11,10,F,5425 +1988,11,10,M,5729 +1988,11,11,F,5447 +1988,11,11,M,5713 +1988,11,12,F,4452 +1988,11,12,M,4595 +1988,11,13,F,4120 +1988,11,13,M,4355 +1988,11,14,F,5380 +1988,11,14,M,5595 +1988,11,15,F,5580 +1988,11,15,M,5828 +1988,11,16,F,5352 +1988,11,16,M,5620 +1988,11,17,F,5498 +1988,11,17,M,5708 +1988,11,18,F,5495 +1988,11,18,M,5970 +1988,11,19,F,4347 +1988,11,19,M,4635 +1988,11,20,F,4119 +1988,11,20,M,4316 +1988,11,21,F,5584 +1988,11,21,M,5774 +1988,11,22,F,5792 +1988,11,22,M,6072 +1988,11,23,F,5271 +1988,11,23,M,5690 +1988,11,24,F,4087 +1988,11,24,M,4437 +1988,11,25,F,4912 +1988,11,25,M,5186 +1988,11,26,F,4313 +1988,11,26,M,4599 +1988,11,27,F,4189 +1988,11,27,M,4321 +1988,11,28,F,5439 +1988,11,28,M,5610 +1988,11,29,F,5536 +1988,11,29,M,5987 +1988,11,30,F,5516 +1988,11,30,M,5713 +1988,12,1,F,5492 +1988,12,1,M,5735 +1988,12,2,F,5255 +1988,12,2,M,5644 +1988,12,3,F,4354 +1988,12,3,M,4516 +1988,12,4,F,4061 +1988,12,4,M,4263 +1988,12,5,F,5224 +1988,12,5,M,5427 +1988,12,6,F,5578 +1988,12,6,M,5719 +1988,12,7,F,5449 +1988,12,7,M,5646 +1988,12,8,F,5418 +1988,12,8,M,5713 +1988,12,9,F,5253 +1988,12,9,M,5625 +1988,12,10,F,4356 +1988,12,10,M,4469 +1988,12,11,F,4043 +1988,12,11,M,4409 +1988,12,12,F,5388 +1988,12,12,M,5571 +1988,12,13,F,5516 +1988,12,13,M,5767 +1988,12,14,F,5510 +1988,12,14,M,5852 +1988,12,15,F,5533 +1988,12,15,M,5831 +1988,12,16,F,5622 +1988,12,16,M,5841 +1988,12,17,F,4270 +1988,12,17,M,4486 +1988,12,18,F,4211 +1988,12,18,M,4220 +1988,12,19,F,5651 +1988,12,19,M,6065 +1988,12,20,F,6092 +1988,12,20,M,6343 +1988,12,21,F,5462 +1988,12,21,M,5861 +1988,12,22,F,5219 +1988,12,22,M,5510 +1988,12,23,F,4887 +1988,12,23,M,5110 +1988,12,24,F,4024 +1988,12,24,M,4269 +1988,12,25,F,3874 +1988,12,25,M,3961 +1988,12,26,F,4274 +1988,12,26,M,4409 +1988,12,27,F,5633 +1988,12,27,M,5895 +1988,12,28,F,5858 +1988,12,28,M,5989 +1988,12,29,F,5760 +1988,12,29,M,5944 +1988,12,30,F,5742 +1988,12,30,M,6095 +1988,12,31,F,4435 +1988,12,31,M,4698 +1989,1,null,F,156749 +1989,1,null,M,164052 +1989,2,null,F,146710 +1989,2,null,M,154047 +1989,3,null,F,165889 +1989,3,null,M,174433 +1989,4,null,F,155689 +1989,4,null,M,163432 +1989,5,null,F,163800 +1989,5,null,M,172892 +1989,6,null,F,165525 +1989,6,null,M,173823 +1989,7,null,F,174054 +1989,7,null,M,183063 +1989,8,null,F,178986 +1989,8,null,M,188074 +1989,9,null,F,174808 +1989,9,null,M,182962 +1989,10,null,F,168303 +1989,10,null,M,176258 +1989,11,null,F,159013 +1989,11,null,M,166923 +1989,12,null,F,164186 +1989,12,null,M,172022 +1990,1,null,F,163576 +1990,1,null,M,172073 +1990,2,null,F,153015 +1990,2,null,M,159915 +1990,3,null,F,171463 +1990,3,null,M,179499 +1990,4,null,F,164469 +1990,4,null,M,172275 +1990,5,null,F,173127 +1990,5,null,M,181366 +1990,6,null,F,168941 +1990,6,null,M,178799 +1990,7,null,F,179270 +1990,7,null,M,188837 +1990,8,null,F,181845 +1990,8,null,M,191101 +1990,9,null,F,175292 +1990,9,null,M,183840 +1990,10,null,F,172365 +1990,10,null,M,181247 +1990,11,null,F,163036 +1990,11,null,M,170515 +1990,12,null,F,164567 +1990,12,null,M,172484 +1991,1,null,F,164305 +1991,1,null,M,171198 +1991,2,null,F,151260 +1991,2,null,M,158163 +1991,3,null,F,167751 +1991,3,null,M,176650 +1991,4,null,F,163778 +1991,4,null,M,172218 +1991,5,null,F,172728 +1991,5,null,M,180764 +1991,6,null,F,163048 +1991,6,null,M,171594 +1991,7,null,F,177698 +1991,7,null,M,185629 +1991,8,null,F,179729 +1991,8,null,M,187491 +1991,9,null,F,174362 +1991,9,null,M,181999 +1991,10,null,F,171490 +1991,10,null,M,177890 +1991,11,null,F,158692 +1991,11,null,M,165320 +1991,12,null,F,166760 +1991,12,null,M,174825 +1992,1,null,F,162874 +1992,1,null,M,171502 +1992,2,null,F,154333 +1992,2,null,M,161410 +1992,3,null,F,165468 +1992,3,null,M,174389 +1992,4,null,F,162792 +1992,4,null,M,170879 +1992,5,null,F,167941 +1992,5,null,M,176584 +1992,6,null,F,165818 +1992,6,null,M,174250 +1992,7,null,F,175090 +1992,7,null,M,184407 +1992,8,null,F,171095 +1992,8,null,M,178271 +1992,9,null,F,169543 +1992,9,null,M,178412 +1992,10,null,F,167907 +1992,10,null,M,176076 +1992,11,null,F,157342 +1992,11,null,M,164949 +1992,12,null,F,164915 +1992,12,null,M,173181 +1993,1,null,F,157524 +1993,1,null,M,165896 +1993,2,null,F,148569 +1993,2,null,M,156378 +1993,3,null,F,167013 +1993,3,null,M,175505 +1993,4,null,F,159698 +1993,4,null,M,167674 +1993,5,null,F,163377 +1993,5,null,M,172991 +1993,6,null,F,163769 +1993,6,null,M,171934 +1993,7,null,F,171888 +1993,7,null,M,181061 +1993,8,null,F,171785 +1993,8,null,M,179521 +1993,9,null,F,170167 +1993,9,null,M,178232 +1993,10,null,F,162794 +1993,10,null,M,170519 +1993,11,null,F,154679 +1993,11,null,M,162072 +1993,12,null,F,162193 +1993,12,null,M,169284 +1994,1,null,F,157015 +1994,1,null,M,163982 +1994,2,null,F,147453 +1994,2,null,M,154175 +1994,3,null,F,165797 +1994,3,null,M,174281 +1994,4,null,F,154935 +1994,4,null,M,162778 +1994,5,null,F,160679 +1994,5,null,M,169921 +1994,6,null,F,160576 +1994,6,null,M,169510 +1994,7,null,F,168646 +1994,7,null,M,177579 +1994,8,null,F,172383 +1994,8,null,M,180197 +1994,9,null,F,166020 +1994,9,null,M,173600 +1994,10,null,F,162185 +1994,10,null,M,168356 +1994,11,null,F,156514 +1994,11,null,M,163252 +1994,12,null,F,160031 +1994,12,null,M,167060 +1995,1,null,F,154538 +1995,1,null,M,161749 +1995,2,null,F,144485 +1995,2,null,M,150879 +1995,3,null,F,160096 +1995,3,null,M,168678 +1995,4,null,F,150914 +1995,4,null,M,158447 +1995,5,null,F,162601 +1995,5,null,M,172235 +1995,6,null,F,160527 +1995,6,null,M,169557 +1995,7,null,F,166814 +1995,7,null,M,174366 +1995,8,null,F,171158 +1995,8,null,M,179864 +1995,9,null,F,165661 +1995,9,null,M,173746 +1995,10,null,F,162008 +1995,10,null,M,168303 +1995,11,null,F,151949 +1995,11,null,M,159181 +1995,12,null,F,154120 +1995,12,null,M,161136 +1996,1,null,F,153564 +1996,1,null,M,161007 +1996,2,null,F,147336 +1996,2,null,M,154683 +1996,3,null,F,157536 +1996,3,null,M,165311 +1996,4,null,F,152624 +1996,4,null,M,160256 +1996,5,null,F,158983 +1996,5,null,M,167020 +1996,6,null,F,155502 +1996,6,null,M,163267 +1996,7,null,F,168423 +1996,7,null,M,177043 +1996,8,null,F,169237 +1996,8,null,M,177393 +1996,9,null,F,165338 +1996,9,null,M,171299 +1996,10,null,F,164939 +1996,10,null,M,171717 +1996,11,null,F,151621 +1996,11,null,M,158058 +1996,12,null,F,157561 +1996,12,null,M,165156 +1997,1,null,F,155408 +1997,1,null,M,162091 +1997,2,null,F,142259 +1997,2,null,M,149536 +1997,3,null,F,157335 +1997,3,null,M,164148 +1997,4,null,F,153524 +1997,4,null,M,160935 +1997,5,null,F,161304 +1997,5,null,M,169296 +1997,6,null,F,156734 +1997,6,null,M,165435 +1997,7,null,F,169391 +1997,7,null,M,177423 +1997,8,null,F,165928 +1997,8,null,M,173528 +1997,9,null,F,162975 +1997,9,null,M,170937 +1997,10,null,F,160645 +1997,10,null,M,168306 +1997,11,null,F,150194 +1997,11,null,M,157395 +1997,12,null,F,161231 +1997,12,null,M,168371 +1998,1,null,F,155671 +1998,1,null,M,163902 +1998,2,null,F,146310 +1998,2,null,M,152641 +1998,3,null,F,161291 +1998,3,null,M,168419 +1998,4,null,F,156178 +1998,4,null,M,163853 +1998,5,null,F,161543 +1998,5,null,M,169221 +1998,6,null,F,159207 +1998,6,null,M,168175 +1998,7,null,F,170376 +1998,7,null,M,178616 +1998,8,null,F,168353 +1998,8,null,M,176747 +1998,9,null,F,168502 +1998,9,null,M,175254 +1998,10,null,F,162998 +1998,10,null,M,170140 +1998,11,null,F,153386 +1998,11,null,M,160161 +1998,12,null,F,163291 +1998,12,null,M,170957 +1999,1,null,F,156054 +1999,1,null,M,163440 +1999,2,null,F,145172 +1999,2,null,M,152660 +1999,3,null,F,162648 +1999,3,null,M,170574 +1999,4,null,F,154150 +1999,4,null,M,163007 +1999,5,null,F,160124 +1999,5,null,M,168682 +1999,6,null,F,162255 +1999,6,null,M,170261 +1999,7,null,F,170905 +1999,7,null,M,179321 +1999,8,null,F,171718 +1999,8,null,M,180033 +1999,9,null,F,170699 +1999,9,null,M,179124 +1999,10,null,F,163347 +1999,10,null,M,170004 +1999,11,null,F,154605 +1999,11,null,M,161062 +1999,12,null,F,162833 +1999,12,null,M,170787 +2000,1,null,F,161288 +2000,1,null,M,169225 +2000,2,null,F,154694 +2000,2,null,M,162997 +2000,3,null,F,166124 +2000,3,null,M,174808 +2000,4,null,F,155038 +2000,4,null,M,162495 +2000,5,null,F,166443 +2000,5,null,M,175161 +2000,6,null,F,166358 +2000,6,null,M,175247 +2000,7,null,F,170327 +2000,7,null,M,179102 +2000,8,null,F,176508 +2000,8,null,M,184030 +2000,9,null,F,170411 +2000,9,null,M,177693 +2000,10,null,F,168039 +2000,10,null,M,176350 +2000,11,null,F,164086 +2000,11,null,M,170206 +2000,12,null,F,164939 +2000,12,null,M,172254 +2001,1,null,F,164404 +2001,1,null,M,171208 +2001,2,null,F,148640 +2001,2,null,M,155259 +2001,3,null,F,165359 +2001,3,null,M,173729 +2001,4,null,F,158235 +2001,4,null,M,165804 +2001,5,null,F,167878 +2001,5,null,M,176615 +2001,6,null,F,161947 +2001,6,null,M,169590 +2001,7,null,F,172082 +2001,7,null,M,179504 +2001,8,null,F,177031 +2001,8,null,M,185351 +2001,9,null,F,167748 +2001,9,null,M,175338 +2001,10,null,F,168515 +2001,10,null,M,176084 +2001,11,null,F,158581 +2001,11,null,M,165633 +2001,12,null,F,160350 +2001,12,null,M,166646 +2002,1,null,F,161477 +2002,1,null,M,169612 +2002,2,null,F,148745 +2002,2,null,M,155627 +2002,3,null,F,162351 +2002,3,null,M,169543 +2002,4,null,F,158674 +2002,4,null,M,166175 +2002,5,null,F,165530 +2002,5,null,M,173925 +2002,6,null,F,159792 +2002,6,null,M,168261 +2002,7,null,F,175085 +2002,7,null,M,183135 +2002,8,null,F,175501 +2002,8,null,M,184444 +2002,9,null,F,170451 +2002,9,null,M,178945 +2002,10,null,F,169482 +2002,10,null,M,176842 +2002,11,null,F,155849 +2002,11,null,M,163200 +2002,12,null,F,163582 +2002,12,null,M,171148 +2003,1,null,F,161200 +2003,1,null,M,169104 +2003,2,null,F,150278 +2003,2,null,M,157384 +2003,3,null,F,164318 +2003,3,null,M,173100 +2003,4,null,F,161431 +2003,4,null,M,169158 +2003,5,null,F,169450 +2003,5,null,M,177775 +2003,6,null,F,164323 +2003,6,null,M,173621 +2003,7,null,F,177755 +2003,7,null,M,187045 +2003,8,null,F,175697 +2003,8,null,M,184986 +2003,9,null,F,176270 +2003,9,null,M,183928 +2003,10,null,F,173659 +2003,10,null,M,180924 +2003,11,null,F,156860 +2003,11,null,M,163757 +2003,12,null,F,168146 +2003,12,null,M,175923 +2004,1,null,F,162630 +2004,1,null,M,170778 +2004,2,null,F,154712 +2004,2,null,M,161598 +2004,3,null,F,168958 +2004,3,null,M,177896 +2004,4,null,F,162918 +2004,4,null,M,170940 +2004,5,null,F,164266 +2004,5,null,M,173902 +2004,6,null,F,169006 +2004,6,null,M,176392 +2004,7,null,F,175457 +2004,7,null,M,184593 +2004,8,null,F,173980 +2004,8,null,M,182095 +2004,9,null,F,173996 +2004,9,null,M,182707 +2004,10,null,F,170816 +2004,10,null,M,178294 +2004,11,null,F,164364 +2004,11,null,M,171945 +2004,12,null,F,169607 +2004,12,null,M,177057 +2005,1,null,F,162360 +2005,1,null,M,169670 +2005,2,null,F,151342 +2005,2,null,M,158754 +2005,3,null,F,170000 +2005,3,null,M,179881 +2005,4,null,F,161982 +2005,4,null,M,171045 +2005,5,null,F,168949 +2005,5,null,M,177881 +2005,6,null,F,171467 +2005,6,null,M,179979 +2005,7,null,F,174639 +2005,7,null,M,183061 +2005,8,null,F,180446 +2005,8,null,M,189599 +2005,9,null,F,177973 +2005,9,null,M,186141 +2005,10,null,F,168795 +2005,10,null,M,176498 +2005,11,null,F,164606 +2005,11,null,M,171703 +2005,12,null,F,170333 +2005,12,null,M,178515 +2006,1,null,F,166706 +2006,1,null,M,174193 +2006,2,null,F,156281 +2006,2,null,M,163564 +2006,3,null,F,173924 +2006,3,null,M,183436 +2006,4,null,F,161054 +2006,4,null,M,169341 +2006,5,null,F,173374 +2006,5,null,M,182689 +2006,6,null,F,175037 +2006,6,null,M,183860 +2006,7,null,F,179507 +2006,7,null,M,189126 +2006,8,null,F,189539 +2006,8,null,M,198942 +2006,9,null,F,183523 +2006,9,null,M,191866 +2006,10,null,F,179938 +2006,10,null,M,188122 +2006,11,null,F,171819 +2006,11,null,M,180670 +2006,12,null,F,174255 +2006,12,null,M,182459 +2007,1,null,F,173771 +2007,1,null,M,181789 +2007,2,null,F,159887 +2007,2,null,M,167507 +2007,3,null,F,176426 +2007,3,null,M,184984 +2007,4,null,F,165121 +2007,4,null,M,173674 +2007,5,null,F,176902 +2007,5,null,M,186016 +2007,6,null,F,174757 +2007,6,null,M,184468 +2007,7,null,F,185221 +2007,7,null,M,195135 +2007,8,null,F,191495 +2007,8,null,M,199622 +2007,9,null,F,180098 +2007,9,null,M,187526 +2007,10,null,F,180912 +2007,10,null,M,189157 +2007,11,null,F,173513 +2007,11,null,M,180814 +2007,12,null,F,173787 +2007,12,null,M,181426 +2008,1,null,F,174255 +2008,1,null,M,182789 +2008,2,null,F,165669 +2008,2,null,M,173434 +2008,3,null,F,172053 +2008,3,null,M,179129 +2008,4,null,F,169585 +2008,4,null,M,177399 +2008,5,null,F,173141 +2008,5,null,M,182294 +2008,6,null,F,169958 +2008,6,null,M,179267 +2008,7,null,F,183391 +2008,7,null,M,192714 +2008,8,null,F,182713 +2008,8,null,M,191315 +2008,9,null,F,179696 +2008,9,null,M,188964 +2008,10,null,F,175314 +2008,10,null,M,183219 +2008,11,null,F,158939 +2008,11,null,M,165468 +2008,12,null,F,173215 +2008,12,null,M,181235 diff --git a/examples/pdsh/data/california_cities.csv b/examples/pdsh/data/california_cities.csv new file mode 100644 index 00000000..b0be3a12 --- /dev/null +++ b/examples/pdsh/data/california_cities.csv @@ -0,0 +1,483 @@ +,city,latd,longd,elevation_m,elevation_ft,population_total,area_total_sq_mi,area_land_sq_mi,area_water_sq_mi,area_total_km2,area_land_km2,area_water_km2,area_water_percent +0,Adelanto,34.57611111111112,-117.43277777777779,875.0,2871.0,31765,56.027,56.00899999999999,0.018000000000000002,145.107,145.062,0.046,0.03 +1,AgouraHills,34.15333333333333,-118.76166666666667,281.0,922.0,20330,7.822,7.792999999999999,0.028999999999999998,20.26,20.184,0.076,0.37 +2,Alameda,37.75611111111111,-122.27444444444444,,33.0,75467,22.96,10.610999999999999,12.349,59.465,27.482,31.983,53.79 +3,Albany,37.886944444444445,-122.29777777777778,,43.0,18969,5.465,1.788,3.677,14.155,4.632,9.524,67.28 +4,Alhambra,34.081944444444446,-118.135,150.0,492.0,83089,7.632000000000001,7.631,0.001,19.766,19.762999999999998,0.003,0.01 +5,AlisoViejo,33.575,-117.72555555555556,127.0,417.0,47823,7.472,7.472,0.0,19.352,19.352,0.0,0.0 +6,Alturas,41.48722222222222,-120.5425,1332.0,4370.0,2827,2.449,2.435,0.013999999999999999,6.3420000000000005,6.306,0.036000000000000004,0.57 +7,AmadorCity,38.419444444444444,-120.82416666666666,280.0,919.0,185,0.314,0.314,0.0,0.813,0.813,0.0,0.0 +8,AmericanCanyon,38.168055555555554,-122.2525,14.0,46.0,19454,4.845,4.837,0.008,12.548,12.527000000000001,0.021,0.17 +9,Anaheim,33.836111111111116,-117.88972222222223,48.0,157.0,336000,50.81100000000001,49.835,0.976,131.6,129.07299999999998,2.5269999999999997,1.92 +10,Anderson,40.452222222222225,-122.29666666666667,132.0,430.0,9932,6.62,6.372000000000001,0.248,17.145,16.504,0.642,3.74 +11,AngelsCamp,38.068333333333335,-120.53972222222222,420.0,1378.0,3836,3.637,3.628,0.009000000000000001,9.421,9.397,0.024,0.25 +12,Antioch,38.005,-121.80583333333333,13.0,43.0,107100,29.083000000000002,28.349,0.7340000000000001,75.324,73.422,1.902,2.52 +13,AppleValley,34.516666666666666,-117.21666666666667,898.0,2946.0,69135,73.523,73.193,0.33,190.426,189.57,0.856,0.45 +14,Arcadia,34.132777777777775,-118.0363888888889,147.0,482.0,56364,11.133,10.925,0.20800000000000002,28.836,28.296,0.54,1.87 +15,Arcata,40.86638888888889,-124.08277777777778,,23.0,17231,10.994000000000002,9.097000000000001,1.8969999999999998,28.473000000000003,23.561,4.912,17.25 +16,ArroyoGrande,35.12083333333334,-120.58666666666666,36.0,118.0,17716,5.835,5.835,0.0,15.113,15.113,0.0,0.0 +17,Artesia,33.867222222222225,-118.08055555555555,16.0,52.0,16522,1.621,1.621,0.0,4.197,4.197,0.0,0.0 +18,Arvin,35.20916666666667,-118.82833333333333,137.0,449.0,19304,4.819,4.819,0.0,12.482000000000001,12.482000000000001,0.0,0.0 +19,Atascadero,35.48416666666667,-120.6725,268.0,879.0,28310,26.13,25.641,0.489,67.675,66.40899999999999,1.265,1.87 +20,Atherton,37.45861111111111,-122.2,18.0,59.0,6914,5.0489999999999995,5.0169999999999995,0.032,13.075999999999999,12.993,0.08199999999999999,0.63 +21,Atwater,37.34777777777778,-120.60916666666667,46.0,151.0,28168,6.096,6.087000000000001,0.009000000000000001,15.788,15.765999999999998,0.022000000000000002,0.14 +22,Auburn,38.89861111111111,-121.07444444444444,374.0,1227.0,13330,7.166,7.138,0.027999999999999997,18.56,18.488,0.071,0.38 +23,Avalon,33.340833333333336,-118.32777777777777,9.0,30.0,3728,2.937,2.935,0.002,7.607,7.602,0.005,0.07 +24,Avenal,36.00416666666667,-120.12888888888888,246.0,807.0,13239,19.422,19.422,0.0,50.302,50.302,0.0,0.0 +25,Azusa,34.13055555555555,-117.90694444444445,186.0,610.0,46361,9.669,9.656,0.013000000000000001,25.041999999999998,25.01,0.032,0.13 +26,Bakersfield,35.36666666666667,-119.01666666666667,,404.0,347483,143.609,142.164,1.445,371.94599999999997,368.204,3.742,1.01 +27,BaldwinPark,34.08277777777778,-117.97166666666666,114.0,374.0,75390,6.7860000000000005,6.631,0.155,17.575,17.174,0.4,2.28 +28,Banning,33.931666666666665,-116.89750000000001,716.0,2349.0,29603,23.099,23.099,0.0,59.826,59.826,0.0,0.0 +29,Barstow,34.9,-117.01666666666667,664.0,2178.0,22639,41.394,41.385,0.009000000000000001,107.209,107.186,0.023,0.02 +30,Beaumont,33.924166666666665,-116.97361111111111,796.0,2612.0,36877,30.926,30.912,0.013999999999999999,80.098,80.062,0.036000000000000004,0.04 +31,Bell,33.983333333333334,-118.18333333333334,43.0,141.0,35477,2.62,2.501,0.11900000000000001,6.7829999999999995,6.476,0.307,4.53 +32,Bellflower,33.88805555555555,-118.1275,22.0,71.0,76616,6.17,6.117000000000001,0.053,15.981,15.843,0.138,0.86 +33,BellGardens,33.96805555555556,-118.15611111111112,37.0,121.0,42072,2.463,2.459,0.004,6.379,6.367999999999999,0.012,0.18 +34,Belmont,37.518055555555556,-122.29166666666667,13.0,43.0,25835,4.63,4.621,0.009000000000000001,11.992,11.97,0.022000000000000002,0.19 +35,Belvedere,37.87277777777778,-122.46444444444445,11.0,36.0,2068,2.406,0.519,1.8869999999999998,6.234,1.345,4.888999999999999,78.42 +36,Benicia,38.06333333333333,-122.15611111111112,8.0,26.0,26997,15.72,12.929,2.7910000000000004,40.714,33.486,7.229,17.75 +37,Berkeley,37.87166666666667,-122.27277777777778,200.0,660.0,112580,17.695999999999998,10.47,7.226,45.833,27.118000000000002,18.715999999999998,40.83 +38,BeverlyHills,34.073055555555555,-118.39944444444446,79.0,259.0,34290,5.71,5.707999999999999,0.002,14.79,14.784,0.006,0.04 +39,BigBearLake,34.24138888888889,-116.90333333333334,2058.0,6752.0,5019,6.534,6.346,0.188,16.923,16.435,0.488,2.88 +40,Biggs,39.413888888888884,-121.71027777777778,30.0,98.0,1707,0.636,0.636,0.0,1.646,1.646,0.0,0.0 +41,Bishop,37.3635,-118.3951,,4150.0,3879,1.911,1.864,0.047,4.949,4.827,0.122,2.5 +42,BlueLake,40.882777777777775,-123.98388888888888,40.0,131.0,1253,0.622,0.5920000000000001,0.03,1.61,1.5330000000000001,0.077,4.8 +43,Blythe,33.61027777777778,-114.59638888888888,83.0,272.0,20817,26.971999999999998,26.189,0.7829999999999999,69.855,67.828,2.0269999999999997,2.9 +44,Bradbury,34.14944444444444,-117.97444444444444,206.0,676.0,1048,1.9580000000000002,1.9569999999999999,0.001,5.073,5.07,0.003,0.06 +45,Brawley,32.978611111111114,-115.53027777777778,-34.0,-112.0,24953,7.682,7.682,0.0,19.895,19.895,0.0,0.0 +46,Brea,33.92333333333333,-117.8888888888889,110.0,361.0,39282,12.109000000000002,12.078,0.031,31.363000000000003,31.283,0.08,0.26 +47,Brentwood,37.93194444444444,-121.69583333333334,,79.0,51481,14.805,14.786,0.019,38.345,38.295,0.049,0.13 +48,Brisbane,37.68083333333333,-122.41916666666667,33.0,108.0,4282,20.077,3.096,16.980999999999998,51.998999999999995,8.017000000000001,43.981,84.58 +49,Buellton,34.61416666666667,-120.19388888888889,109.0,358.0,4828,1.5830000000000002,1.5819999999999999,0.001,4.099,4.098,0.002,0.04 +50,BuenaPark,33.85611111111111,-118.00416666666666,23.0,75.0,80530,10.552999999999999,10.524000000000001,0.028999999999999998,27.331999999999997,27.256999999999998,0.075,0.28 +51,Burbank,34.180277777777775,-118.32833333333333,185.0,607.0,103340,17.379,17.340999999999998,0.038,45.011,44.913000000000004,0.098,0.22 +52,Burlingame,37.583333333333336,-122.36361111111111,12.0,39.0,28806,6.057,4.406000000000001,1.651,15.686,11.411,4.275,27.25 +53,Calabasas,34.138333333333335,-118.66083333333334,243.0,5.0,23058,13.3,13.249,0.051,34.4,34.27,0.131,0.38 +54,Calexico,32.678888888888885,-115.49888888888889,,3.0,38572,8.391,8.391,0.0,21.733,21.733,0.0,0.0 +55,CaliforniaCity,35.12583333333333,-117.98583333333333,733.0,2405.0,14120,203.63099999999997,203.523,0.10800000000000001,527.401,527.122,0.27899999999999997,0.05 +56,Calimesa,33.988055555555555,-117.04305555555555,729.0,2392.0,7879,14.847000000000001,14.847000000000001,0.0,38.454,38.454,0.0,0.0 +57,Calipatria,33.12555555555556,-115.51416666666667,,-180.0,7705,3.716,3.716,0.0,9.624,9.624,0.0,0.0 +58,Calistoga,38.581388888888895,-122.58277777777778,106.0,348.0,5155,2.613,2.595,0.018000000000000002,6.769,6.722,0.047,0.7 +59,Camarillo,34.233333333333334,-119.03333333333333,54.0,177.0,65201,19.543,19.528,0.015,50.617,50.577,0.04,0.08 +60,Campbell,37.28388888888889,-121.955,60.0,197.0,39349,5.886,5.797999999999999,0.08800000000000001,15.245,15.017000000000001,0.228,1.49 +61,CanyonLake,33.68416666666666,-117.25555555555556,422.0,1385.0,10561,4.671,3.928,0.743,12.099,10.173,1.926,15.92 +62,Capitola,36.97638888888889,-121.95472222222223,4.0,13.0,9918,1.676,1.5930000000000002,0.083,4.34,4.126,0.214,4.92 +63,Carlsbad,33.121944444444445,-117.29694444444445,16.0,52.0,105328,39.11,37.722,1.3880000000000001,101.295,97.699,3.596,3.55 +64,CarmelbytheSea,36.555277777777775,-121.92333333333333,,,3722,1.08,1.08,0.0,2.798,2.798,0.0,0.0 +65,Carpinteria,34.399166666666666,-119.51638888888888,10.0,33.0,13040,9.272,2.5860000000000003,6.686,24.011999999999997,6.697,17.315,72.11 +66,Carson,33.83972222222222,-118.25972222222222,8.2,27.0,91714,18.968,18.724,0.244,49.126999999999995,48.495,0.631,1.29 +67,CathedralCity,33.80777777777777,-116.46472222222222,100.0,328.0,51200,21.756,21.499000000000002,0.257,56.349,55.683,0.6659999999999999,1.18 +68,Ceres,37.60138888888889,-120.95722222222223,28.0,92.0,45417,8.019,8.011000000000001,0.008,20.771,20.749000000000002,0.022000000000000002,0.1 +69,Cerritos,33.86833333333333,-118.0675,14.0,34.0,49041,8.856,8.725,0.131,22.936999999999998,22.598000000000003,0.33899999999999997,1.48 +70,Chico,39.74,-121.83555555555554,74.0,245.0,86187,33.095,32.923,0.172,85.71600000000001,85.271,0.446,0.52 +71,Chino,34.01777777777777,-117.69,222.0,728.0,77983,29.651999999999997,29.639,0.013000000000000001,76.79899999999999,76.766,0.033,0.04 +72,ChinoHills,33.97527777777778,-117.72305555555556,365.0,1070.0,75655,44.748999999999995,44.681000000000004,0.068,115.899,115.723,0.175,0.15 +73,Chowchilla,37.11666666666667,-120.26666666666667,73.0,240.0,18720,7.6610000000000005,7.6610000000000005,0.0,19.842,19.842,0.0,0.0 +74,ChulaVista,32.62777777777778,-117.04805555555555,21.0,69.0,243916,52.093999999999994,49.631,2.463,134.925,128.545,6.38,4.73 +75,CitrusHeights,38.7,-121.28333333333333,50.0,164.0,83301,14.228,14.228,0.0,36.851,36.851,0.0,0.0 +76,Claremont,34.11,-117.71972222222223,,,34926,13.485999999999999,13.347999999999999,0.138,34.93,34.571,0.358,1.03 +77,Clayton,37.941111111111105,-121.93583333333333,120.0,394.0,10897,3.8360000000000003,3.8360000000000003,0.0,9.935,9.935,0.0,0.0 +78,Clearlake,38.958333333333336,-122.62638888888888,432.0,1417.0,15250,10.581,10.129,0.452,27.404,26.234,1.17,4.27 +79,Cloverdale,38.799166666666665,-123.01722222222222,,335.0,8618,2.648,2.648,0.0,6.857,6.857,0.0,0.0 +80,Clovis,36.82527777777778,-119.70305555555557,110.0,361.0,101314,23.278000000000002,23.278000000000002,0.0,60.288999999999994,60.288999999999994,0.0,0.0 +81,Coachella,33.67944444444444,-116.17444444444445,-20.74,-66.0,40704,28.95,28.95,0.0,74.98100000000001,74.98100000000001,0.0,0.0 +82,Coalinga,36.13972222222222,-120.36027777777777,205.0,673.0,13380,6.15,6.119,0.031,15.927,15.847000000000001,0.08,0.5 +83,Colfax,39.09722222222222,-120.9538888888889,739.0,2425.0,1963,1.4069999999999998,1.4069999999999998,0.0,3.645,3.645,0.0,0.0 +84,Colma,37.678888888888885,-122.45555555555556,37.0,121.0,1792,1.909,1.909,0.0,4.945,4.945,0.0,0.0 +85,Colton,34.065,-117.32166666666666,306.0,1004.0,52154,16.039,15.324000000000002,0.715,41.541000000000004,39.689,1.8519999999999999,4.46 +86,Colusa,39.214444444444446,-122.00944444444444,16.0,52.0,5971,1.834,1.834,0.0,4.751,4.751,0.0,0.0 +87,Commerce,34.00055555555556,-118.15472222222223,43.0,141.0,12823,6.537999999999999,6.537000000000001,0.001,16.933,16.93,0.003,0.02 +88,Compton,33.89666666666667,-118.22500000000001,21.0,69.0,96455,10.116,10.012,0.10400000000000001,26.201999999999998,25.932,0.27,1.03 +89,Concord,37.97805555555556,-122.03111111111112,26.0,85.3,122067,30.546,30.546,0.0,79.11399999999999,79.11399999999999,0.0,0.0 +90,Corcoran,36.09805555555556,-119.56027777777777,63.0,207.0,25515,7.4670000000000005,7.4670000000000005,0.0,19.338,19.338,0.0,0.0 +91,Corning,39.92611111111111,-122.18055555555556,84.0,276.0,17382,3.55,3.55,0.0,9.193,9.193,0.0,0.0 +92,Corona,33.86666666666667,-117.56666666666666,206.0,678.0,158391,38.93,38.825,0.105,100.829,100.55799999999999,0.272,0.27 +93,Coronado,32.67805555555555,-117.1725,6.0,20.0,24697,32.666,7.931,24.735,84.603,20.541,64.062,75.72 +94,CorteMadera,37.925555555555555,-122.5275,12.0,39.0,9253,4.406000000000001,3.1639999999999997,1.242,11.41,8.193,3.216,28.19 +95,CostaMesa,33.665,-117.91222222222223,30.0,98.0,109960,15.7,15.654000000000002,0.046,40.662,40.543,0.11900000000000001,0.29 +96,Cotati,38.32777777777778,-122.70916666666668,34.0,112.0,7310,1.883,1.88,0.003,4.877,4.869,0.008,0.17 +97,Covina,34.09166666666667,-117.87916666666666,170.0,558.0,47796,7.041,7.026,0.015,18.236,18.195999999999998,0.039,0.22 +98,CrescentCity,41.755833333333335,-124.20166666666667,,43.0,7643,2.415,1.963,0.452,6.255,5.085,1.17,18.7 +99,Cudahy,33.96416666666667,-118.1825,37.0,121.0,23805,1.226,1.175,0.051,3.175,3.043,0.132,4.15 +100,CulverCity,34.007777777777775,-118.40083333333334,29.0,95.0,38883,5.138999999999999,5.111000000000001,0.027999999999999997,13.31,13.238,0.07200000000000001,0.54 +101,Cupertino,37.3175,-122.04194444444444,72.0,236.0,58302,11.257,11.255999999999998,0.001,29.156,29.153000000000002,0.003,0.01 +102,Cypress,33.818333333333335,-118.03916666666666,12.0,39.0,47802,6.59,6.581,0.009000000000000001,17.069000000000003,17.045,0.024,0.14 +103,DalyCity,37.686388888888885,-122.46833333333333,57.0,187.0,101123,7.664,7.664,0.0,19.849,19.849,0.0,0.0 +104,DanaPoint,33.467222222222226,-117.69805555555556,44.0,144.0,33351,29.484,6.497000000000001,22.987,76.36399999999999,16.828,59.536,77.96 +105,Danville,37.82166666666667,-122.0,109.0,358.0,42039,18.028,18.028,0.0,46.693000000000005,46.693000000000005,0.0,0.0 +106,Davis,38.553888888888885,-121.73805555555556,16.0,52.0,65622,9.919,9.887,0.032,25.69,25.608,0.08199999999999999,0.32 +107,Delano,35.76888888888889,-119.24694444444445,96.0,315.0,53819,14.355,14.302999999999999,0.052000000000000005,37.18,37.044000000000004,0.135,0.36 +108,DelMar,32.955000000000005,-117.26388888888889,34.0,112.0,4161,1.777,1.7069999999999999,0.07,4.602,4.421,0.18100000000000002,3.94 +109,DelReyOaks,36.593333333333334,-121.835,25.0,82.0,1624,0.483,0.48100000000000004,0.002,1.251,1.246,0.005,0.42 +110,DesertHotSprings,33.961111111111116,-116.50805555555556,328.0,1076.0,25938,23.642,23.615,0.027000000000000003,61.233000000000004,61.163999999999994,0.069,0.11 +111,DiamondBar,34.001666666666665,-117.82083333333333,212.0,696.0,55544,14.885,14.88,0.005,38.552,38.538000000000004,0.013999999999999999,0.04 +112,Dinuba,36.544999999999995,-119.38916666666667,102.0,345.0,21453,6.47,6.47,0.0,16.758,16.758,0.0,0.0 +113,Dixon,38.44916666666666,-121.82694444444444,19.0,62.0,18351,7.0920000000000005,6.996,0.096,18.368,18.118,0.249,1.36 +114,Dorris,41.965,-121.91888888888889,1294.0,4245.0,939,0.718,0.7020000000000001,0.016,1.86,1.819,0.040999999999999995,2.19 +115,DosPalos,36.983333333333334,-120.63333333333334,362.0,118.0,4950,1.35,1.35,0.0,3.4960000000000004,3.4960000000000004,0.0,0.0 +116,Downey,33.93805555555555,-118.13083333333333,36.0,118.0,111772,12.568,12.408,0.16,32.551,32.137,0.414,1.27 +117,Duarte,34.140277777777776,-117.96166666666667,156.0,512.0,21321,6.69,6.69,0.0,17.328,17.328,0.0,0.0 +118,Dublin,37.702222222222225,-121.93583333333333,,367.0,49890,14.912,14.908,0.004,38.622,38.611,0.011000000000000001,0.03 +119,Dunsmuir,41.22166666666667,-122.27305555555556,695.0,2280.0,1650,1.735,1.6980000000000002,0.037000000000000005,4.494,4.398,0.096,2.14 +120,EastPaloAlto,37.466944444444444,-122.13972222222223,6.0,20.0,28155,2.612,2.505,0.107,6.766,6.4879999999999995,0.278,4.11 +121,Eastvale,33.963055555555556,-117.56388888888888,,627.0,53668,11.445,11.405,0.04,29.644000000000002,29.539,0.10400000000000001,0.35 +122,ElCajon,32.79833333333333,-116.96000000000001,133.0,436.0,99478,14.433,14.433,0.0,37.381,37.381,0.0,0.0 +123,ElCentro,32.8,-115.56666666666666,-12.0,-39.0,42598,11.099,11.081,0.018000000000000002,28.746,28.7,0.046,0.16 +124,ElCerrito,37.91583333333333,-122.31166666666667,,69.0,23549,3.688,3.688,0.0,9.551,9.551,0.0,0.0 +125,ElkGrove,38.43833333333333,-121.38194444444444,14.0,45.0,153015,42.239,42.19,0.049,109.398,109.271,0.127,0.12 +126,ElMonte,34.07333333333334,-118.0275,91.0,299.0,113475,9.648,9.562000000000001,0.086,24.988000000000003,24.766,0.222,0.89 +127,ElSegundo,33.921388888888885,-118.40611111111112,35.0,115.0,16654,5.465,5.462999999999999,0.002,14.152000000000001,14.148,0.004,0.03 +128,Emeryville,37.831388888888895,-122.28527777777778,7.0,23.0,10080,2.01,1.246,0.764,5.206,3.2260000000000004,1.979,38.02 +129,Encinitas,33.044444444444444,-117.27166666666666,25.0,82.0,59518,19.99,18.812,1.178,51.772,48.722,3.05,5.89 +130,Escalon,37.791666666666664,-120.99166666666666,36.0,118.0,7266,2.3680000000000003,2.301,0.067,6.1339999999999995,5.959,0.175,2.85 +131,Escondido,33.124722222222225,-117.08083333333333,197.0,646.0,143911,36.989000000000004,36.813,0.17600000000000002,95.801,95.345,0.456,0.48 +132,Etna,41.45722222222223,-122.89694444444446,895.0,2936.0,737,0.759,0.758,0.001,1.965,1.962,0.002,0.12 +133,Eureka,40.801944444444445,-124.16361111111112,,39.0,27191,14.454,9.384,5.07,37.435,24.305,13.13,35.07 +134,Exeter,36.29416666666666,-119.14277777777778,119.0,390.0,10334,2.463,2.463,0.0,6.379,6.379,0.0,0.0 +135,Fairfax,37.98722222222222,-122.58888888888889,35.0,115.0,7441,2.204,2.204,0.0,5.707000000000001,5.707000000000001,0.0,0.0 +136,Fairfield,38.257777777777775,-122.05416666666666,4.0,13.0,108321,37.635,34.39,3.245,97.475,94.839,2.635,5.65 +137,Farmersville,36.301111111111105,-119.2075,109.0,358.0,10588,2.258,2.258,0.0,5.849,5.849,0.0,0.0 +138,Ferndale,40.57666666666667,-124.26333333333334,17.0,56.0,1371,1.0270000000000001,1.0270000000000001,0.0,2.659,2.659,0.0,0.0 +139,Fillmore,34.40138888888889,-118.91777777777779,139.0,456.0,15002,3.365,3.364,0.001,8.715,8.712,0.003,0.03 +140,Firebaugh,36.85888888888889,-120.45611111111111,46.0,151.0,7549,3.5189999999999997,3.4619999999999997,0.057,9.113999999999999,8.967,0.14800000000000002,1.62 +141,Folsom,38.67222222222222,-121.15777777777778,67.0,220.0,72203,24.301,21.945,2.356,62.93899999999999,56.838,6.101,9.69 +142,Fontana,34.1,-117.46666666666667,377.0,1237.0,201812,42.431999999999995,42.431999999999995,3.0,109.899,109.899,6.0,3.0 +143,FortBragg,39.44583333333333,-123.80527777777777,26.0,85.0,7273,2.8,2.7,,7.2,7.1,0.1, +144,FortJones,41.60722222222223,-122.84194444444444,842.0,2762.0,839,0.602,0.602,0.0,1.56,1.56,0.0,0.0 +145,Fortuna,40.59805555555556,-124.15722222222223,20.0,66.0,11926,4.845,4.845,0.0,12.549000000000001,12.549000000000001,0.0,0.0 +146,FosterCity,37.55138888888889,-122.26638888888888,2.0,7.0,30567,19.840999999999998,3.7560000000000002,16.085,51.388000000000005,9.729,41.659,81.07 +147,FountainValley,33.70861111111111,-117.9563888888889,10.0,33.0,55313,9.031,9.017999999999999,0.013000000000000001,23.391,23.357,0.034,0.14 +148,Fowler,36.63333333333333,-119.68333333333334,94.0,308.0,5570,2.531,2.531,0.0,6.556,6.556,0.0,0.0 +149,Fremont,37.54833333333333,-121.98861111111111,71.0,218.0,220000,87.61,77.459,10.151,226.91,200.618,26.291999999999998,11.59 +150,Fresno,36.75,-119.76666666666667,,308.0,509039,112.30799999999999,111.95700000000001,0.35100000000000003,290.877,289.967,0.91,0.31 +151,Fullerton,33.88,-117.92861111111111,50.0,164.0,135161,22.36,22.353,0.011000000000000001,57.92100000000001,57.893,0.027000000000000003,0.05 +152,Galt,38.26083333333333,-121.30305555555556,16.0,47.0,23647,5.944,5.931,0.013000000000000001,15.397,15.362,0.035,0.23 +153,Gardena,33.89361111111111,-118.30777777777777,15.0,49.0,58829,5.865,5.829,0.036000000000000004,15.190999999999999,15.097000000000001,0.094,0.62 +154,GardenGrove,33.778888888888886,-117.96027777777778,27.0,89.0,170883,17.959,17.941,0.018000000000000002,46.513000000000005,46.467,0.046,0.1 +155,Gilroy,37.011944444444445,-121.58,61.0,200.0,48821,16.156,16.146,0.01,41.845,41.818999999999996,0.027000000000000003,0.06 +156,Glendale,34.170833333333334,-118.25,159.0,522.0,196021,30.583000000000002,30.453000000000003,0.13,79.212,78.874,0.33799999999999997,0.43 +157,Glendora,34.130833333333335,-117.85416666666666,236.0,774.0,50073,19.558,19.393,0.165,50.655,50.228,0.42700000000000005,0.84 +158,Goleta,34.440555555555555,-119.81361111111111,,20.0,29888,7.975,7.903,0.07200000000000001,20.654,20.467,0.187,0.9 +159,Gonzales,36.50666666666667,-121.44444444444444,41.0,135.0,8187,1.959,1.921,0.038,5.075,4.976,0.098,1.93 +160,GrandTerrace,34.03111111111111,-117.31666666666666,324.0,1063.0,12040,3.502,3.502,0.0,9.07,9.07,0.0,0.0 +161,GrassValley,39.219166666666666,-121.05833333333334,735.0,2411.0,12860,4.743,4.743,0.0,12.285,12.285,0.0,0.0 +162,Greenfield,36.32083333333334,-121.24388888888889,88.0,289.0,16330,2.135,2.135,0.0,5.53,5.53,0.0,0.0 +163,Gridley,39.36388888888889,-121.69361111111111,29.0,95.0,6593,,2.07,0.0,,2.07,0.0,0.0 +164,GroverBeach,35.12083333333334,-120.61944444444444,19.0,62.0,13156,2.314,2.31,0.004,5.995,5.983,0.011000000000000001,0.19 +165,Guadalupe,34.96555555555556,-120.57305555555556,26.0,85.0,7080,1.314,1.3090000000000002,0.005,3.405,3.391,0.013999999999999999,0.4 +166,Gustine,37.257777777777775,-120.99888888888889,30.0,98.0,5520,1.551,1.551,0.0,4.0169999999999995,4.0169999999999995,0.0,0.0 +167,HalfMoonBay,37.45888888888889,-122.43694444444445,22.0,72.0,11324,6.444,6.4239999999999995,0.02,16.692,16.639,0.053,0.32 +168,Hanford,36.3275,-119.64555555555556,76.0,249.0,55283,16.589000000000002,16.589000000000002,0.0,42.966,42.966,0.0,0.0 +169,HawaiianGardens,33.828611111111115,-118.0736111111111,10.0,33.0,14254,0.956,0.946,0.01,2.477,2.45,0.027000000000000003,1.09 +170,Hawthorne,33.91722222222222,-118.34861111111111,22.0,72.0,84293,6.0920000000000005,6.081,0.011000000000000001,15.779000000000002,15.75,0.028999999999999998,0.18 +171,Hayward,37.66888888888889,-122.08083333333333,,105.0,149392,63.748000000000005,45.323,18.425,165.108,117.38600000000001,47.721000000000004,28.9 +172,Healdsburg,38.6175,-122.86638888888888,32.0,105.0,11254,4.4639999999999995,4.457,0.006999999999999999,11.561,11.543,0.018000000000000002,0.15 +173,Hemet,33.74194444444444,-116.98305555555555,486.0,1594.0,78657,27.846999999999998,27.846999999999998,0.0,72.124,72.124,0.0,0.0 +174,Hercules,38.01722222222222,-122.28861111111111,24.0,79.0,24060,18.179000000000002,6.205,11.974,47.083999999999996,16.072,31.011999999999997,65.87 +175,HermosaBeach,33.86638888888889,-118.39972222222222,8.0,26.0,19506,1.4269999999999998,1.4269999999999998,0.0,3.695,3.695,0.0,0.0 +176,Hesperia,34.41277777777778,-117.30611111111111,971.0,3186.0,90173,73.209,73.096,0.113,189.61,189.31599999999997,0.294,0.15 +177,HiddenHills,34.1675,-118.66083333333334,328.0,1076.0,1856,1.689,1.689,0.0,4.373,4.373,0.0,0.0 +178,Highland,34.11833333333333,-117.2025,399.0,1309.0,53014,18.89,18.755,0.135,48.924,48.575,0.349,0.71 +179,Hillsborough,37.56027777777778,-122.35638888888889,98.0,322.0,10825,6.19,6.19,0.0,16.031,16.031,0.0,0.0 +180,Hollister,36.84722222222222,-121.39833333333334,88.0,289.0,34928,7.29,7.29,0.0,18.88,18.88,0.0,0.0 +181,Holtville,32.81111111111111,-115.38027777777778,-3.0,-10.0,5939,1.153,1.148,0.005,2.986,2.9739999999999998,0.012,0.4 +182,Hughson,37.60305555555556,-120.86694444444444,38.0,125.0,6640,1.815,1.815,0.0,4.7010000000000005,4.7010000000000005,0.0,0.0 +183,HuntingtonBeach,33.69277777777778,-118.00027777777778,12.0,39.0,197575,31.881999999999998,26.748,5.1339999999999995,82.57600000000001,69.278,13.298,16.1 +184,HuntingtonPark,33.983333333333334,-118.21666666666667,52.0,171.0,58114,3.016,3.013,0.003,7.811,7.8020000000000005,0.008,0.11 +185,Huron,36.20277777777778,-120.10305555555556,114.0,374.0,6754,1.591,1.591,0.0,4.121,4.121,0.0,0.0 +186,ImperialBeach,32.57833333333333,-117.11722222222221,21.0,69.0,26324,4.485,4.1610000000000005,0.324,11.616,10.777999999999999,0.838,7.22 +187,Imperial,32.847500000000004,-115.56944444444444,-18.0,-59.0,14758,5.856,5.856,0.0,15.168,15.168,0.0,0.0 +188,IndianWells,33.715833333333336,-116.3411111111111,27.0,89.0,4958,14.591,14.321,0.27,37.79,37.091,0.6990000000000001,1.85 +189,Indio,33.72,-116.23194444444445,-4.0,-13.0,76036,29.189,29.180999999999997,0.008,75.6,75.579,0.021,0.03 +190,Industry,34.016666666666666,-117.95,98.0,322.0,219,12.064,11.785,0.27899999999999997,31.245,30.522,0.723,2.31 +191,Inglewood,33.9575,-118.3461111111111,40.0,131.0,109673,9.093,9.068,0.025,23.549,23.486,0.064,0.27 +192,Ione,38.35277777777778,-120.93277777777779,91.0,299.0,7918,4.78,4.765,0.015,12.379000000000001,12.341,0.039,0.31 +193,Irvine,33.68416666666666,-117.7925,17.0,45.0,215529,66.454,66.10600000000001,0.348,172.115,171.21400000000003,0.9009999999999999,0.52 +194,Irwindale,34.11666666666667,-117.96666666666667,143.0,469.0,1422,9.613,8.826,0.787,24.897,22.859,2.0380000000000003,8.19 +195,Isleton,38.161944444444444,-121.60916666666667,3.0,10.0,804,0.49200000000000005,0.44,0.052000000000000005,1.272,1.139,0.134,10.5 +196,Jackson,38.348888888888894,-120.77416666666666,371.0,1217.0,4651,3.73,3.73,0.0,9.66,9.66,0.0,0.0 +197,JurupaValley,33.99944444444444,-117.47527777777778,,,97496,43.5,43.3,0.2,112.7,112.1,0.6, +198,Kerman,36.72361111111111,-120.06,67.0,220.0,17303,3.233,3.233,0.0,8.372,8.372,0.0,0.0 +199,KingCity,36.21277777777778,-121.1261111111111,102.0,335.0,12874,3.984,3.845,0.139,10.317,9.958,0.36,3.49 +200,Kingsburg,36.513888888888886,-119.55388888888889,92.0,302.0,11382,2.8280000000000003,2.8280000000000003,0.0,7.325,7.325,0.0,0.0 +201,LaCaadaFlintridge,34.207721,-118.206979,,,20246,8.645,8.628,0.017,22.391,22.346999999999998,0.044000000000000004,0.2 +202,Lafayette,37.88583333333333,-122.11805555555554,97.5,320.0,24285,15.387,15.220999999999998,0.166,39.853,39.422,0.431,1.08 +203,LagunaBeach,33.53138888888889,-117.76916666666666,6.0,20.0,22723,9.821,8.85,0.971,25.435,22.921,2.515,9.89 +204,LagunaHills,33.599722222222226,-117.69944444444445,113.0,371.0,30344,6.695,6.67,0.025,17.34,17.276,0.064,0.37 +205,LagunaNiguel,33.53194444444444,-117.7025,121.0,397.0,62979,14.885,14.833,0.052000000000000005,38.551,38.418,0.134,0.35 +206,LagunaWoods,33.60916666666667,-117.73277777777778,116.0,381.0,18747,3.115,3.115,0.0,8.07,8.068999999999999,0.0,0.01 +207,LaHabra,33.93194444444444,-117.94611111111111,91.0,299.0,60239,7.376,7.37,0.006,19.104,19.088,0.016,0.08 +208,LaHabraHeights,33.96388888888889,-117.95277777777778,225.0,738.0,5325,6.162000000000001,6.159,0.003,15.958,15.95,0.006999999999999999,0.05 +209,LakeElsinore,33.68138888888889,-117.34527777777777,395.0,1306.0,51821,41.687,36.208,5.479,107.97,93.779,14.19,13.14 +210,LakeForest,33.641666666666666,-117.69083333333333,148.0,400.0,77264,17.902,17.816,0.086,46.36600000000001,46.143,0.223,0.48 +211,Lakeport,39.043055555555554,-122.91583333333334,413.0,1355.0,4753,3.198,3.0580000000000003,0.14,8.283999999999999,7.92,0.364,4.39 +212,Lakewood,33.847500000000004,-118.11999999999999,14.0,46.0,80048,9.466000000000001,9.415,0.051,24.517,24.384,0.133,0.54 +213,LaMesa,32.771388888888886,-117.02277777777778,161.0,528.0,57065,9.116,9.076,0.04,23.612,23.506999999999998,0.10400000000000001,0.44 +214,LaMirada,33.90222222222222,-118.00972222222222,59.0,194.0,48527,7.857,7.84,0.017,20.351,20.305999999999997,0.045,0.22 +215,Lancaster,34.68333333333333,-118.15,719.0,2359.0,156633,94.54700000000001,94.27600000000001,0.271,244.87599999999998,244.175,0.701,0.29 +216,LaPalma,33.849444444444444,-118.04388888888889,14.0,46.0,15568,1.8319999999999999,1.808,0.024,4.744,4.6819999999999995,0.063,1.32 +217,LaPuente,34.0325,-117.95527777777778,107.0,351.0,39816,3.48,3.4789999999999996,0.001,9.012,9.01,0.002,0.02 +218,LaQuinta,33.67527777777777,-116.2975,0.0,,37467,35.551,35.117,0.434,92.07799999999999,90.954,1.124,1.22 +219,Larkspur,37.93416666666666,-122.53527777777778,,43.0,11926,3.2430000000000003,3.0269999999999997,0.21600000000000003,8.399,7.84,0.56,6.66 +220,Lathrop,37.816944444444445,-121.28861111111111,7.0,20.0,18023,23.033,21.930999999999997,1.102,59.655,56.8,2.8539999999999996,4.79 +221,LaVerne,34.114444444444445,-117.7713888888889,323.0,1060.0,31063,8.562000000000001,8.43,0.132,22.175,21.834,0.341,1.54 +222,Lawndale,33.88666666666666,-118.3536111111111,18.0,59.0,32769,1.974,1.974,0.0,5.113,5.113,0.0,0.0 +223,LemonGrove,32.74138888888889,-117.03166666666667,136.0,440.0,25320,3.88,3.88,0.0,10.05,10.05,0.0,0.0 +224,Lemoore,36.30083333333333,-119.78277777777778,70.0,230.0,25281,8.517000000000001,8.517000000000001,0.0,22.058000000000003,22.058000000000003,0.0,0.0 +225,Lincoln,38.88722222222222,-121.2961111111111,51.0,167.0,42819,20.13,20.105999999999998,0.024,52.137,52.075,0.062,0.12 +226,Lindsay,36.2,-119.08333333333333,118.0,387.0,11768,2.61,2.61,0.0,6.7589999999999995,6.7589999999999995,0.0,0.0 +227,LiveOakSutterCounty,39.27583333333333,-121.66000000000001,,79.0,8392,1.869,1.869,0.0,4.8389999999999995,4.8389999999999995,0.0,0.0 +228,Livermore,37.68194444444444,-121.76805555555555,,495.0,83547,25.176,25.173000000000002,0.003,65.204,65.19800000000001,0.006999999999999999,0.01 +229,Livingston,37.386944444444445,-120.72361111111111,40.0,131.0,13058,3.715,3.715,0.0,9.622,9.622,0.0,0.0 +230,Lodi,38.12888888888889,-121.28083333333333,15.0,50.0,62134,13.825,13.610999999999999,0.214,35.805,35.251999999999995,0.5529999999999999,1.54 +231,LomaLinda,34.04833333333333,-117.25055555555555,355.0,1165.0,23261,7.517,7.516,0.001,19.47,19.467,0.002,0.01 +232,Lomita,33.79333333333333,-118.31611111111111,29.0,95.0,20256,1.911,1.911,0.0,4.949,4.949,0.0,0.0 +233,Lompoc,34.64611111111111,-120.46027777777778,32.0,105.0,42434,11.675,11.597000000000001,0.078,30.237,30.037,0.201,0.66 +234,LongBeach,33.76833333333333,-118.19555555555556,,0.0,462257,51.437,50.293,1.1440000000000001,133.22299999999998,130.259,2.964,2.22 +235,Loomis,38.81638888888889,-121.19277777777778,123.0,404.0,6430,7.267,7.267,0.0,18.822,18.822,0.0,0.0 +236,LosAlamitos,33.80222222222222,-118.06444444444445,7.0,23.0,11449,4.1160000000000005,4.05,0.066,10.659,10.489,0.17,1.6 +237,LosAltos,37.36805555555556,-122.0975,,157.0,28976,6.487,6.487,0.0,,,, +238,LosAltosHills,37.37138888888889,-122.1375,89.0,292.0,7922,8.802,8.802,0.0,22.796999999999997,22.796999999999997,0.0,0.0 +239,LosAngeles,34.05,-118.25,71.0,2.0,3884307,503.0,469.0,34.0,1302.0,1214.0,88.0,6.7 +240,LosBanos,37.05833333333333,-120.85,36.0,118.0,35972,10.117,9.993,0.124,26.203000000000003,25.881999999999998,0.321,1.22 +241,LosGatos,37.236111111111114,-121.96166666666667,105.0,344.0,29413,11.16,11.08,0.08,28.903000000000002,28.697,0.20600000000000002,0.71 +242,Loyalton,39.67666666666666,-120.24305555555556,1509.0,4951.0,769,0.355,0.355,0.0,0.9209999999999999,0.9209999999999999,0.0,0.0 +243,Lynwood,33.92472222222222,-118.20194444444445,28.0,92.0,69772,4.84,4.84,0.0,12.536,12.536,0.0,0.0 +244,Madera,36.96138888888889,-120.06083333333333,83.0,271.0,61416,15.789000000000001,15.789000000000001,0.0,40.894,40.894,0.0,0.0 +245,Malibu,34.03,-118.75,32.0,105.0,12645,19.828,19.785,0.043,51.354,51.242,0.113,0.22 +246,MammothLakes,37.64861111111111,-118.97194444444445,2402.0,7880.0,8234,25.305999999999997,24.866,0.44,65.541,64.402,1.139,1.74 +247,ManhattanBeach,33.888888888888886,-118.40527777777778,20.0,67.0,35135,3.9410000000000003,3.937,0.004,10.208,10.197000000000001,0.01,0.1 +248,Manteca,37.80277777777778,-121.22083333333333,11.0,38.0,71067,17.757,17.733,0.024,45.99100000000001,45.928999999999995,0.062,0.13 +249,Maricopa,35.05888888888889,-119.40083333333334,269.0,883.0,1154,1.5019999999999998,1.5019999999999998,0.0,3.89,3.89,0.0,0.0 +250,Marina,36.68444444444444,-121.80222222222221,13.0,43.0,29718,9.763,8.883,0.88,25.287,23.006999999999998,2.28,9.02 +251,Martinez,38.019444444444446,-122.13416666666667,7.0,23.0,35824,13.135,12.130999999999998,1.004,34.019,31.42,2.6,7.64 +252,Marysville,39.15,-121.58333333333333,19.0,62.0,12072,3.585,3.464,0.121,9.283999999999999,8.971,0.312,3.36 +253,Maywood,33.98777777777778,-118.18666666666667,46.0,151.0,27395,1.178,1.178,0.0,3.052,3.052,0.0,0.0 +254,McFarland,35.67805555555555,-119.22916666666667,108.0,354.0,13745,2.668,2.668,0.0,6.91,6.91,0.0,0.0 +255,Mendota,36.75361111111111,-120.38166666666666,53.0,174.0,11014,3.281,3.278,0.003,8.499,8.491,0.008,0.1 +256,Menifee,33.67833333333333,-117.16694444444445,434.0,1424.0,77519,46.607,46.466,0.141,120.711,120.345,0.365,0.3 +257,MenloPark,37.45277777777778,-122.18333333333334,22.0,72.0,32026,17.415,9.79,7.625,45.105,25.355,19.75,43.79 +258,Merced,37.3,-120.48333333333333,52.0,171.0,80793,23.316,23.316,0.0,60.388999999999996,60.388999999999996,0.0,0.0 +259,Millbrae,37.600833333333334,-122.40138888888889,,33.0,21536,3.259,3.247,0.012,8.439,8.408999999999999,0.03,0.36 +260,MillValley,37.90611111111111,-122.545,,79.0,13903,4.8469999999999995,4.763,0.084,12.555,12.335999999999999,0.21899999999999997,1.74 +261,Milpitas,37.43472222222222,-121.89500000000001,5.0,16.0,70092,13.640999999999998,13.591,0.05,35.328,35.2,0.128,0.36 +262,MissionViejo,33.61277777777778,-117.65611111111112,121.0,448.0,96346,18.123,17.739,0.384,46.93899999999999,45.943999999999996,0.995,2.12 +263,Modesto,37.66138888888889,-120.99444444444444,27.0,89.0,201165,37.092,36.867,0.225,96.069,95.486,0.583,0.61 +264,Monrovia,34.14416666666666,-118.00194444444445,174.0,571.0,36590,13.714,13.605,0.109,35.519,35.236999999999995,0.282,0.79 +265,Montague,41.727222222222224,-122.52638888888889,774.0,2539.0,1443,1.7930000000000001,1.778,0.015,4.644,4.605,0.039,0.85 +266,Montclair,34.07083333333334,-117.6975,326.0,1066.0,36664,5.517,5.517,0.0,14.289000000000001,14.289000000000001,0.0,0.0 +267,Montebello,34.01444444444444,-118.11444444444444,61.0,200.0,62500,8.373,8.333,0.04,21.685,21.581,0.10400000000000001,0.48 +268,Monterey,36.6,-121.9,8.0,26.0,27810,11.764000000000001,8.466000000000001,3.298,30.469,21.927,8.542,28.03 +269,MontereyPark,34.049166666666665,-118.13555555555556,117.0,384.0,60269,7.733,7.672000000000001,0.061,20.029,19.87,0.158,0.79 +270,MonteSereno,37.238055555555555,-121.98944444444444,,515.0,3341,1.615,1.615,0.0,4.184,4.184,0.0,0.0 +271,Moorpark,34.280833333333334,-118.87333333333332,157.0,515.0,34421,12.799000000000001,12.579,0.22,33.149,32.58,0.569,1.72 +272,Moraga,37.835,-122.12972222222221,,,16016,9.442,9.433,0.009000000000000001,24.455,24.432,0.023,0.09 +273,MorenoValley,33.94305555555555,-117.22833333333334,497.0,1631.0,193365,51.475,51.275,0.2,133.319,132.8,0.519,0.39 +274,MorganHill,37.13055555555555,-121.65444444444445,107.0,350.0,37882,12.882,12.882,0.0,33.363,33.363,0.0,0.0 +275,MorroBay,35.37916666666667,-120.85333333333332,19.0,62.0,10234,10.322000000000001,5.303,5.019,26.734,13.734000000000002,13.0,48.63 +276,MountainView,37.38944444444444,-122.08194444444445,32.0,105.0,74066,12.273,11.995,0.278,31.788,31.068,0.72,2.26 +277,MountShasta,41.31444444444444,-122.31138888888889,1099.0,3606.0,3394,3.77,3.766,0.004,9.764,9.754,0.01,0.1 +278,Murrieta,33.56944444444445,-117.2025,334.0,1096.0,103466,33.613,33.577,0.036000000000000004,87.05799999999999,86.964,0.094,0.11 +279,Napa,38.30472222222222,-122.29888888888888,,20.0,76915,18.147000000000002,17.839000000000002,0.308,47.0,46.203,0.797,1.69 +280,NationalCity,32.67805555555555,-117.09916666666666,21.0,69.0,58582,9.116,7.277,1.839,23.609,18.847,4.762,20.17 +281,Needles,34.83833333333334,-114.6111111111111,151.0,495.0,4844,31.275,30.808000000000003,0.467,81.002,79.793,1.209,1.49 +282,NevadaCity,39.26138888888889,-121.01861111111111,755.0,2477.0,3068,2.1919999999999997,2.188,0.004,5.6770000000000005,5.667000000000001,0.01,0.17 +283,Newark,37.53333333333333,-122.03333333333333,,20.0,43539,13.898,13.875,0.023,35.996,35.936,0.061,0.17 +284,Newman,37.315,-121.0225,27.0,89.0,10224,2.102,2.102,0.0,5.444,5.444,0.0,0.0 +285,NewportBeach,33.61666666666667,-117.89750000000001,3.0,,85186,52.978,23.805,29.173000000000002,137.211,61.653999999999996,75.557,55.07 +286,Norco,33.93111111111111,-117.54861111111111,195.0,640.0,27063,14.277999999999999,13.962,0.316,36.98,36.161,0.8190000000000001,2.22 +287,Norwalk,33.90694444444444,-118.08333333333333,28.0,92.0,105549,9.746,9.707,0.039,25.243000000000002,25.141,0.102,0.4 +288,Novato,38.1075,-122.56972222222223,9.0,30.0,51904,27.956999999999997,27.44,0.517,72.407,71.068,1.339,1.85 +289,Oakdale,37.76916666666666,-120.85694444444444,48.0,157.0,20675,6.095,6.045,0.05,15.784,15.655,0.129,0.81 +290,Oakland,37.80444444444444,-122.27083333333333,,43.0,406253,78.002,55.786,22.215999999999998,202.024,144.485,57.54,28.48 +291,Oakley,37.9975,-121.7125,6.0,20.0,35432,16.155,15.853,0.302,41.842,41.059,0.7829999999999999,1.87 +292,Oceanside,33.211666666666666,-117.32583333333334,20.0,66.0,183095,42.174,41.235,0.9390000000000001,109.23100000000001,106.79799999999999,2.4330000000000003,2.23 +293,Ojai,34.44916666666666,-119.24666666666667,227.0,745.0,7461,4.401,4.386,0.015,11.398,11.359000000000002,0.04,0.35 +294,Ontario,34.05277777777778,-117.62777777777777,282.0,925.0,163924,50.006,49.941,0.065,129.515,129.345,0.17,0.13 +295,Orange,33.80305555555555,-117.8325,59.0,195.0,136416,25.24,24.796999999999997,0.44299999999999995,65.37100000000001,64.22399999999999,1.147,1.75 +296,OrangeCove,36.62444444444444,-119.31361111111111,129.0,423.0,9078,1.912,1.912,0.0,4.9510000000000005,4.9510000000000005,0.0,0.0 +297,Orinda,37.882777777777775,-122.17972222222222,151.0,495.0,18342,12.698,12.683,0.015,32.887,32.848,0.039,0.12 +298,Orland,39.7475,-122.19638888888889,79.0,259.0,7291,2.971,2.971,0.0,7.696000000000001,7.696000000000001,0.0,0.0 +299,Oroville,39.516666666666666,-121.55,51.0,167.0,15506,13.011,12.993,0.018000000000000002,33.701,33.653,0.048,0.14 +300,Oxnard,34.19138888888889,-119.1825,16.0,52.0,203585,39.208,26.894000000000002,12.314,101.54799999999999,69.656,31.893,31.41 +301,Pacifica,37.62277777777778,-122.48555555555555,25.0,82.0,37234,12.66,12.658,0.002,32.789,32.784,0.005,0.01 +302,PacificGrove,36.617777777777775,-121.91666666666667,46.0,151.0,15295,4.003,2.865,1.138,10.366,7.419,2.946,28.42 +303,Palmdale,34.58111111111111,-118.10055555555554,810.0,2657.0,152750,106.21600000000001,105.961,0.255,275.099,274.439,0.66,0.24 +304,PalmDesert,33.72555555555556,-116.36944444444444,67.0,220.0,48445,27.014,26.81,0.204,69.96600000000001,69.437,0.529,0.76 +305,PalmSprings,33.823888888888895,-116.53027777777778,146.0,440.0,44552,94.975,94.116,0.8590000000000001,245.984,243.761,2.224,0.9 +306,PaloAlto,37.42916666666667,-122.13805555555557,9.0,30.0,64403,25.787,23.884,1.903,66.78699999999999,61.858000000000004,4.928999999999999,7.38 +307,PalosVerdesEstates,33.786944444444444,-118.39666666666668,64.0,210.0,13438,4.774,4.774,0.0,12.365,12.364,0.001,0.01 +308,Paradise,39.75972222222222,-121.62138888888889,542.0,1778.0,26249,18.322,18.308,0.013999999999999999,47.455,47.418,0.037000000000000005,0.08 +309,Paramount,33.9,-118.16666666666667,21.0,69.0,54098,4.84,4.729,0.111,12.536,12.249,0.28600000000000003,2.28 +310,Parlier,36.611666666666665,-119.52694444444444,105.0,344.0,14494,2.194,2.194,0.0,5.682,5.682,0.0,0.0 +311,Pasadena,34.15611111111111,-118.13194444444444,263.0,863.0,137122,23.128,22.97,0.158,59.902,59.493,0.409,0.68 +312,PasoRobles,35.64083333333333,-120.6538888888889,242.0,,29792,19.425,19.12,0.305,50.312,49.522,0.79,1.57 +313,Patterson,37.473055555555554,-121.13277777777778,31.0,102.0,20413,5.954,5.954,0.0,15.421,15.421,0.0,0.0 +314,Perris,33.79666666666667,-117.22444444444444,443.0,1453.0,71326,31.503,31.393,0.11,81.594,81.308,0.28600000000000003,0.35 +315,Petaluma,38.24583333333334,-122.63138888888888,9.0,30.0,58921,14.489,14.382,0.107,37.527,37.249,0.278,0.74 +316,PicoRivera,33.98888888888889,-118.08916666666666,50.0,164.0,62942,8.882,8.296,0.586,23.003,21.485,1.518,6.6 +317,Piedmont,37.81666666666667,-122.23333333333333,,331.0,10667,1.6780000000000002,1.6780000000000002,0.0,4.345,4.345,0.0,0.0 +318,Pinole,38.004444444444445,-122.2911111111111,31.0,23.0,18390,13.575,5.3229999999999995,8.252,35.16,13.787,21.372,60.79 +319,PismoBeach,35.14833333333333,-120.64805555555556,17.0,56.0,7655,13.475999999999999,3.5989999999999998,9.877,34.904,9.322000000000001,25.581999999999997,73.29 +320,Pittsburg,38.028055555555554,-121.88472222222222,8.0,26.0,63264,19.154,17.218,1.936,49.61,44.595,5.015,10.11 +321,Placentia,33.8825,-117.85499999999999,83.0,272.0,50533,6.582000000000001,6.568,0.013999999999999999,17.048,17.011,0.037000000000000005,0.22 +322,Placerville,38.72972222222222,-120.79861111111111,,1867.0,10389,5.813,5.812,0.001,15.054,15.052,0.002,0.01 +323,PleasantHill,37.948055555555555,-122.0525,16.0,52.0,33152,7.072,7.072,0.0,18.315,18.315,0.0,0.0 +324,Pleasanton,37.6625,-121.87472222222222,,351.0,70285,24.266,24.113000000000003,0.153,62.847,62.452,0.395,0.63 +325,Plymouth,38.481944444444444,-120.84472222222222,330.0,1083.0,1005,0.9440000000000001,0.9309999999999999,0.013000000000000001,2.444,2.411,0.033,1.34 +326,PointArena,38.90888888888889,-123.69305555555556,36.0,118.0,449,1.35,1.35,0.0,3.4960000000000004,3.4960000000000004,0.0,0.0 +327,Pomona,34.06083333333333,-117.75583333333333,259.0,850.0,1,22.964000000000002,22.951999999999998,0.012,59.474,59.443999999999996,0.03,0.05 +328,Porterville,36.06861111111112,-119.0275,139.0,459.0,55697,17.679000000000002,17.607,0.07200000000000001,45.79,45.603,0.188,0.41 +329,PortHueneme,34.16027777777778,-119.19444444444444,4.0,12.0,21723,4.671,4.4510000000000005,0.22,12.095999999999998,11.527999999999999,0.569,4.7 +330,Portola,39.81027777777778,-120.46972222222223,1480.0,4856.0,2104,5.407,5.407,0.0,14.003,14.003,0.0,0.0 +331,PortolaValley,37.375,-122.21861111111112,140.0,459.0,4353,9.093,9.092,0.001,23.551,23.546999999999997,0.004,0.02 +332,Poway,32.97,-117.03861111111111,157.0,515.0,47811,39.165,39.079,0.086,101.43799999999999,101.214,0.223,0.22 +333,RanchoCordova,38.58916666666667,-121.30277777777778,27.0,89.0,64776,33.874,33.507,0.36700000000000005,87.73299999999999,86.78200000000001,0.951,1.08 +334,RanchoCucamonga,34.123333333333335,-117.57944444444443,368.0,1207.0,170746,39.871,39.851,0.02,103.26299999999999,103.212,0.051,0.05 +335,RanchoMirage,33.76916666666666,-116.42111111111112,83.0,272.0,17218,,24.447,0.389,64.32600000000001,63.318000000000005,1.008,1.57 +336,RanchoPalosVerdes,33.75833333333333,-118.36416666666666,67.0,220.0,41643,13.465,13.465,0.0,34.875,34.874,0.001,0.0 +337,RanchoSantaMargarita,33.64138888888889,-117.59444444444443,290.0,775.0,47853,12.992,12.957,0.035,33.650999999999996,33.56,0.091,0.27 +338,RedBluff,40.17666666666666,-122.23805555555556,93.0,305.0,24674,7.6770000000000005,7.563,0.114,19.882,19.587,0.295,1.48 +339,Redding,40.583333333333336,-122.36666666666666,151.0,495.0,92328,61.175,59.647,1.528,158.442,154.485,3.957,2.5 +340,Redlands,34.05472222222222,-117.1825,414.0,1358.0,68747,36.427,36.126,0.301,94.344,93.565,0.779,0.83 +341,RedondoBeach,33.85638888888889,-118.37694444444443,,62.0,66748,6.207999999999999,6.1979999999999995,0.01,16.08,16.054000000000002,0.026000000000000002,0.16 +342,RedwoodCity,37.48277777777778,-122.23611111111111,6.0,20.0,76815,34.625,19.42,15.205,89.677,50.297,39.38,43.91 +343,Reedley,36.59638888888889,-119.45027777777779,106.0,348.0,24194,5.156000000000001,5.084,0.07200000000000001,13.352,13.165999999999999,0.185,1.39 +344,Rialto,34.11138888888889,-117.3825,383.0,1257.0,101740,22.365,22.351,0.013999999999999999,57.926,57.888999999999996,0.037000000000000005,0.06 +345,Richmond,37.93583333333333,-122.34777777777778,14.0,46.0,103701,52.48,30.068,22.412,135.923,77.875,58.048,42.71 +346,Ridgecrest,35.6225,-117.67083333333333,698.0,2290.0,27616,21.416999999999998,20.766,0.6509999999999999,55.468999999999994,53.783,1.685,3.04 +347,RioDell,40.49916666666667,-124.10638888888889,49.0,161.0,3363,2.418,2.282,0.136,6.261,5.91,0.35100000000000003,5.61 +348,RioVista,38.163888888888884,-121.69583333333334,6.0,20.0,7360,7.093999999999999,6.691,0.40299999999999997,18.375,17.33,1.044,5.68 +349,Ripon,37.74055555555556,-121.12833333333333,21.0,69.0,14297,5.495,5.305,0.19,14.232000000000001,13.739,0.493,3.47 +350,Riverbank,37.731388888888894,-120.94361111111111,43.0,141.0,22678,4.1160000000000005,4.092,0.024,10.661,10.599,0.062,0.59 +351,Riverside,33.948055555555555,-117.39611111111111,262.0,860.0,313673,81.444,81.14,0.304,210.94099999999997,210.15200000000002,0.7879999999999999,0.37 +352,Rocklin,38.8,-121.24666666666667,79.0,249.0,56974,19.594,19.541,0.053,50.748999999999995,50.61,0.139,0.27 +353,RohnertPark,38.34722222222222,-122.69527777777778,32.0,105.0,40971,7.007999999999999,7.002999999999999,0.005,18.149,18.136,0.013000000000000001,0.07 +354,RollingHills,33.75944444444445,-118.34166666666667,389.0,1276.0,1860,2.991,2.991,0.0,7.746,7.746,0.0,0.0 +355,RollingHillsEstates,33.77361111111111,-118.36083333333333,143.0,469.0,8067,3.613,3.569,0.044000000000000004,9.359,9.244,0.115,1.22 +356,Rosemead,34.06666666666667,-118.08333333333333,97.0,318.0,53764,5.176,5.162000000000001,0.013999999999999999,13.405999999999999,13.37,0.035,0.26 +357,Roseville,38.7525,-121.28944444444444,50.0,164.0,127323,36.223,36.222,0.001,93.81700000000001,93.814,0.003,0.0 +358,Ross,37.962500000000006,-122.55499999999999,11.0,36.0,2415,1.556,1.556,0.0,4.031000000000001,4.031000000000001,0.0,0.0 +359,Sacramento,38.55555555555555,-121.46888888888888,9.0,30.0,466488,100.105,97.915,2.19,259.27299999999997,253.6,5.672999999999999,2.19 +360,Salinas,36.67777777777778,-121.65555555555557,16.0,52.0,163665,23.217,23.179000000000002,0.038,60.131,60.033,0.099,0.16 +361,SanAnselmo,37.974722222222226,-122.56166666666667,14.0,46.0,12336,2.677,2.677,0.0,6.934,6.934,0.0,0.0 +362,SanBernardino,34.1,-117.3,321.0,1974.0,209924,59.645,59.201,0.444,154.48,153.33,1.15,0.74 +363,SanBruno,37.625277777777775,-122.42527777777778,5.0,16.0,41114,5.478,5.478,0.0,14.187999999999999,14.187999999999999,0.0,0.0 +364,SanCarlos,37.49916666666667,-122.26333333333334,,33.0,28406,5.541,5.537999999999999,0.003,14.35,14.343,0.006999999999999999,0.05 +365,SanClemente,33.437777777777775,-117.62027777777777,71.0,250.0,63522,19.468,18.711,0.757,50.422,48.461000000000006,1.9609999999999999,3.89 +366,SandCity,36.617222222222225,-121.84833333333333,22.0,72.0,334,2.924,0.562,2.362,7.575,1.4569999999999999,6.119,80.77 +367,SanDiego,32.715,-117.16250000000001,6.0,3.0,1345895,372.4,325.19,47.21,964.51,842.23,122.27,12.68 +368,SanDimas,34.10277777777778,-117.81611111111111,,750.0,33371,15.427,15.037,0.39,39.957,38.946999999999996,1.01,2.53 +369,SanFernando,34.28722222222222,-118.4388888888889,326.0,1070.0,23645,2.374,2.374,0.0,6.149,6.149,0.0,0.0 +370,SanFrancisco,37.78333333333333,-122.41666666666667,,52.0,837442,231.89,46.87,185.02,,,,80.0 +371,SanGabriel,34.094166666666666,-118.09833333333333,128.0,420.0,39718,4.146,4.145,0.001,10.735999999999999,10.734000000000002,0.002,0.02 +372,Sanger,36.70805555555556,-119.55583333333333,,371.0,24270,5.524,5.524,0.0,14.307,14.307,0.0,0.0 +373,SanJacinto,33.78722222222222,-116.96666666666667,477.0,1565.0,44199,26.131,25.715999999999998,0.415,67.679,66.605,1.074,1.59 +374,SanJoaquin,36.60666666666667,-120.18916666666667,53.0,174.0,4001,1.148,1.148,0.0,2.9730000000000003,2.9730000000000003,0.0,0.0 +375,SanJose,37.333333333333336,-121.9,26.0,,1000536,179.97,176.52599999999998,3.4389999999999996,466.10900000000004,457.20099999999996,8.908,1.91 +376,SanJuanBautista,36.844166666666666,-121.53722222222223,66.0,217.0,1862,0.711,0.711,0.0,1.8430000000000002,1.8419999999999999,0.001,0.06 +377,SanJuanCapistrano,33.49944444444444,-117.66166666666668,37.0,96.0,34593,14.295,14.115,0.18,37.024,36.559,0.466,1.26 +378,SanLeandro,37.725,-122.15611111111112,15.0,56.0,86869,15.663,13.343,2.32,40.565,34.556999999999995,6.007999999999999,14.81 +379,SanLuisObispo,35.274166666666666,-120.66305555555556,71.0,233.0,45119,12.93,12.777000000000001,0.153,33.489000000000004,33.093,0.396,1.18 +380,SanMarcos,33.14194444444444,-117.17027777777778,,,83781,24.39,24.37,0.02,63.169,63.117,0.053,0.08 +381,SanMarino,34.12277777777778,-118.11305555555555,172.0,564.0,13147,3.7739999999999996,3.767,0.006999999999999999,9.775,9.757,0.018000000000000002,0.18 +382,SanMateo,37.55416666666667,-122.31305555555555,13.0,43.0,97207,15.884,12.13,3.7539999999999996,41.137,31.416,9.722000000000001,23.63 +383,SanPablo,37.96222222222222,-122.34555555555555,16.0,52.0,29139,2.634,2.634,0.0,6.822,6.822,0.0,0.0 +384,SanRafael,37.97361111111111,-122.53111111111112,13.0,43.0,57713,22.421999999999997,16.47,5.952000000000001,58.074,42.657,15.417,26.55 +385,SanRamon,37.78,-121.97805555555556,146.0,480.0,73927,18.077,18.061,0.016,46.818999999999996,46.778,0.042,0.09 +386,SantaAna,33.740833333333335,-117.88138888888888,35.0,115.0,324528,27.518,27.27,0.248,71.271,70.628,0.643,0.9 +387,SantaBarbara,34.42583333333333,-119.71416666666667,15.0,49.0,90893,41.968,19.468,22.5,108.697,50.422,58.275,53.61 +388,SantaClara,37.35444444444445,-121.96916666666667,23.0,75.0,116468,18.407,18.407,0.0,47.675,47.675,0.0,0.0 +389,SantaClarita,34.416666666666664,-118.50638888888889,368.0,1207.0,209130,62.16,62.1,0.06,160.993,160.825,0.168,0.099 +390,SantaCruz,36.971944444444446,-122.02638888888889,11.0,36.0,62864,15.828,12.74,3.088,40.996,32.997,7.999,19.51 +391,SantaFeSprings,33.9375,-118.06722222222221,41.0,135.0,16223,8.914,8.874,0.04,23.088,22.985,0.10300000000000001,0.45 +392,SantaMaria,34.95138888888889,-120.43333333333334,67.0,220.0,101459,23.395,22.756,0.639,60.592,58.937,1.655,2.73 +393,SantaMonica,34.02194444444444,-118.48138888888889,,105.0,89736,8.416,8.415,0.001,,,, +394,SantaPaula,34.35583333333334,-119.06833333333333,85.0,279.0,29321,4.707,4.593,0.114,12.189,11.895,0.294,2.41 +395,SantaRosa,38.448611111111106,-122.70472222222223,50.0,164.0,170685,41.498999999999995,41.294,0.205,107.48100000000001,106.95,0.531,0.49 +396,Santee,32.86972222222222,-116.97111111111111,105.0,345.0,53413,16.528,16.235,0.293,42.808,42.049,0.759,1.77 +397,Saratoga,37.2725,-122.01944444444445,,410.0,29926,12.382,12.382,0.0,32.07,32.07,0.0,0.0 +398,Sausalito,37.85916666666667,-122.48527777777778,4.0,13.0,7061,2.2569999999999997,1.771,0.486,5.846,4.586,1.2590000000000001,21.54 +399,ScottsValley,37.05138888888889,-122.01333333333334,171.0,561.0,11580,4.595,4.595,0.0,11.9,11.9,0.0,0.0 +400,SealBeach,33.759166666666665,-118.0825,4.0,13.0,24168,13.04,11.286,1.754,33.775,29.230999999999998,4.544,13.45 +401,Seaside,36.611111111111114,-121.84472222222222,10.0,33.0,33025,9.376,9.237,0.139,24.281999999999996,23.923000000000002,0.359,1.48 +402,Sebastopol,38.399166666666666,-122.82694444444444,25.0,82.0,7379,1.8530000000000002,1.8530000000000002,0.0,4.7989999999999995,4.7989999999999995,0.0,0.0 +403,Selma,36.57083333333334,-119.61194444444443,94.0,308.0,23219,5.136,5.136,0.0,13.302999999999999,13.302999999999999,0.0,0.0 +404,Shafter,35.50055555555556,-119.27166666666666,106.0,348.0,16988,27.945,27.945,0.0,72.376,72.376,0.0,0.0 +405,ShastaLake,40.67805555555555,-122.36999999999999,246.0,810.0,10164,10.929,10.921,0.008,28.305,28.284000000000002,0.02,0.07 +406,SierraMadre,34.164722222222224,-118.05083333333333,252.0,827.0,10917,2.957,2.9530000000000003,0.004,7.659,7.647,0.012,0.15 +407,SignalHill,33.79935,-118.16558,45.0,148.0,11465,2.191,2.189,0.002,5.672999999999999,5.669,0.004,0.08 +408,SimiValley,34.27111111111111,-118.73944444444444,234.0,768.0,126874,42.247,41.48,0.767,109.41799999999999,107.43299999999999,1.986,1.81 +409,SolanaBeach,32.99527777777778,-117.26027777777777,22.0,72.0,12867,3.6239999999999997,3.52,0.10400000000000001,9.386000000000001,9.115,0.27,2.88 +410,Soledad,36.42472222222222,-121.32638888888889,58.0,190.0,25738,4.566,4.414,0.152,11.825,11.432,0.39299999999999996,3.32 +411,Solvang,34.59388888888889,-120.13972222222223,154.0,505.0,5245,2.426,2.425,0.001,6.284,6.281000000000001,0.003,0.05 +412,Sonoma,38.288888888888884,-122.4588888888889,26.0,85.0,10648,2.742,2.742,0.0,7.102,7.102,0.0,0.0 +413,Sonora,37.98444444444444,-120.38166666666666,544.0,1785.0,7169,3.0780000000000003,3.0639999999999996,0.013999999999999999,7.972,7.936,0.036000000000000004,0.45 +414,SouthElMonte,34.04888888888889,-118.04833333333333,76.0,249.0,20116,2.8480000000000003,2.843,0.005,7.377999999999999,7.364,0.013999999999999999,0.19 +415,SouthGate,33.94416666666666,-118.19500000000001,37.0,120.0,94396,7.353,7.236000000000001,0.11699999999999999,19.044,18.742,0.303,1.59 +416,SouthLakeTahoe,38.94,-119.97694444444444,1901.0,6237.0,21403,16.602999999999998,10.161,6.442,43.003,26.318,16.685,38.8 +417,SouthPasadena,34.113055555555555,-118.15583333333333,201.0,659.0,25619,3.417,3.405,0.012,8.851,8.82,0.031,0.35 +418,SouthSanFrancisco,37.65611111111111,-122.42555555555556,4.0,13.0,64409,30.158,9.141,21.017,78.109,23.674,54.435,69.69 +419,Stanton,33.802499999999995,-117.99444444444444,20.0,66.0,38186,3.15,3.15,0.0,8.158,8.158,0.0,0.0 +420,StHelena,38.50527777777778,-122.47027777777778,77.0,253.0,5814,5.027,4.986000000000001,0.040999999999999995,13.019,12.913,0.106,0.81 +421,Stockton,37.97555555555556,-121.30083333333333,4.0,13.0,301090,64.753,61.67,3.083,167.708,159.72299999999998,7.985,4.76 +422,SuisunCity,38.245,-122.01694444444445,2.0,7.0,28111,4.163,4.105,0.057999999999999996,10.783,10.633,0.15,1.39 +423,Sunnyvale,37.37111111111111,-122.0375,39.0,128.0,140081,22.689,21.987,0.7020000000000001,58.765,56.946999999999996,1.818,3.09 +424,Susanville,40.41638888888889,-120.65305555555557,1276.0,4186.0,17974,8.017000000000001,7.931,0.086,20.763,20.541,0.222,1.07 +425,SutterCreek,38.393055555555556,-120.8025,362.0,1188.0,2501,2.5580000000000003,2.5580000000000003,0.0,6.625,6.625,0.0,0.0 +426,Taft,35.1425,-119.4563888888889,291.0,955.0,9327,15.113,15.113,0.0,39.143,39.143,0.0,0.0 +427,Tehachapi,35.132222222222225,-118.44888888888889,1210.0,3970.0,14414,9.971,9.874,0.09699999999999999,25.823,25.573,0.25,0.97 +428,Tehama,40.02444444444444,-122.12388888888889,64.0,210.0,418,0.794,0.794,0.0,2.057,2.057,0.0,0.0 +429,Temecula,33.50333333333333,-117.1236111111111,310.59,1019.0,105208,30.166999999999998,30.151,0.016,78.133,78.092,0.042,0.05 +430,TempleCity,34.10277777777778,-118.05805555555555,122.0,400.0,35558,4.006,4.006,0.0,10.374,10.374,0.0,0.0 +431,ThousandOaks,34.18944444444444,-118.875,270.0,886.0,128374,55.181000000000004,55.031000000000006,0.15,142.918,142.53,0.387,0.27 +432,Tiburon,37.87361111111111,-122.45666666666666,4.0,13.0,8962,13.182,4.446000000000001,8.736,34.14,11.515,22.625,66.27 +433,Torrance,33.834722222222226,-118.34138888888889,27.0,89.0,147027,20.553,20.477999999999998,0.075,53.233000000000004,53.038000000000004,0.195,0.37 +434,Tracy,37.738055555555555,-121.43388888888889,16.0,52.0,82922,22.139,22.003,0.136,57.34,56.986999999999995,0.35200000000000004,0.61 +435,Trinidad,41.05916666666666,-124.14305555555556,53.0,174.0,367,0.6709999999999999,0.485,0.18600000000000003,1.7369999999999999,1.255,0.48200000000000004,27.75 +436,Truckee,39.342222222222226,-120.20361111111112,1773.0,5817.0,16180,33.654,32.321999999999996,1.3319999999999999,87.162,83.713,3.449,3.96 +437,Tulare,36.20666666666667,-119.3425,88.0,289.0,59278,21.016,20.930999999999997,0.085,54.433,54.211999999999996,0.221,0.41 +438,Tulelake,41.95416666666667,-121.47583333333334,1230.0,4035.0,1010,0.41200000000000003,0.41,0.002,1.067,1.061,0.006,0.58 +439,Turlock,37.505833333333335,-120.84888888888888,31.0,102.0,69733,16.928,16.928,0.0,43.843999999999994,43.843999999999994,0.0,0.0 +440,Tustin,33.73972222222222,-117.81361111111111,43.0,141.0,75540,11.082,11.082,0.0,28.701,28.701,0.0,0.0 +441,TwentyninePalms,34.138333333333335,-116.07249999999999,607.0,1991.0,25768,59.143,59.143,0.0,153.179,153.179,0.0,0.0 +442,Ukiah,39.150277777777774,-123.20777777777778,193.0,639.0,16075,4.7219999999999995,4.67,0.052000000000000005,12.232000000000001,12.095999999999998,0.136,1.11 +443,UnionCity,37.58694444444445,-122.02583333333334,,,72155,19.0,19.0,0.0,,,, +444,Upland,34.1,-117.65,405.0,1328.0,73732,15.651,15.617,0.034,40.535,40.448,0.087,0.21 +445,Vacaville,38.35388888888889,-121.97277777777778,53.0,174.0,92428,28.585,28.373,0.212,74.03399999999999,73.485,0.55,0.74 +446,Vallejo,38.113055555555555,-122.23583333333333,21.0,60.0,115942,49.5,30.6,18.8,128.3,79.4,48.8,38.0 +447,Ventura,34.275,-119.22777777777777,,,106433,32.095,21.655,10.44,83.124,56.085,27.039,32.53 +448,Vernon,34.00111111111111,-118.21111111111111,62.0,203.0,112,5.157,4.973,0.184,13.357000000000001,12.88,0.47600000000000003,3.57 +449,Victorville,34.53611111111111,-117.28833333333333,832.0,2726.0,120336,73.741,73.178,0.563,190.988,189.52900000000002,1.459,0.76 +450,VillaPark,33.816111111111105,-117.8111111111111,104.0,341.0,5812,2.0780000000000003,2.0780000000000003,0.0,5.382999999999999,5.382999999999999,0.0,0.0 +451,Visalia,36.31666666666667,-119.3,101.0,331.0,124442,36.266,36.246,0.02,93.928,93.876,0.0512,0.05 +452,Vista,33.19361111111111,-117.24111111111111,99.0,325.0,93834,18.678,18.678,0.0,48.376999999999995,48.376999999999995,0.0,0.0 +453,Walnut,34.03333333333333,-117.86666666666666,171.0,561.0,29172,8.996,8.992,0.004,23.3,23.29,0.01,0.04 +454,WalnutCreek,37.906388888888884,-122.065,,131.0,64173,19.769000000000002,19.757,0.012,51.201,51.169,0.031,0.06 +455,Wasco,35.594166666666666,-119.34083333333332,100.0,328.0,25545,9.426,9.426,0.0,24.413,24.413,0.0,0.0 +456,Waterford,37.644999999999996,-120.7675,52.0,171.0,8456,2.369,2.3280000000000003,0.040999999999999995,6.135,6.03,0.105,1.72 +457,Watsonville,36.919999999999995,-121.76361111111112,9.0,29.0,51199,6.7829999999999995,6.687,0.096,17.569000000000003,17.319000000000003,0.25,1.42 +458,Weed,41.424166666666665,-122.38444444444445,1044.0,3425.0,2967,4.795,4.79,0.005,12.417,12.405,0.012,0.1 +459,WestCovina,34.056666666666665,-117.91861111111112,110.0,362.0,106098,16.09,16.041,0.049,41.67100000000001,41.545,0.126,0.3 +460,WestHollywood,34.08777777777778,-118.37222222222222,86.0,282.0,34650,1.8869999999999998,1.8869999999999998,0.0,4.888,4.888,0.0,0.0 +461,WestlakeVillage,34.14194444444444,-118.81944444444444,268.0,880.0,8270,5.5089999999999995,5.185,0.32,14.257,13.43,0.828,5.8 +462,Westminster,33.75138888888889,-117.99388888888889,12.0,39.0,89701,10.049,10.049,0.0,26.026999999999997,26.026999999999997,0.0,0.0 +463,Westmorland,33.03722222222222,-115.62138888888889,,-164.0,2225,0.59,0.59,0.0,1.5290000000000001,1.5290000000000001,0.0,0.0 +464,WestSacramento,38.580555555555556,-121.53027777777778,6.0,20.0,48744,22.846,21.425,1.421,59.172,55.49100000000001,3.681,6.22 +465,Wheatland,39.01,-121.42305555555556,28.0,92.0,3456,1.486,1.479,0.006999999999999999,3.8480000000000003,3.8310000000000004,0.017,0.45 +466,Whittier,33.96555555555556,-118.02444444444444,112.0,367.0,85331,14.7,14.7,0.016,37.0,37.0,0.040999999999999995,0.11 +467,Wildomar,33.603611111111114,-117.27277777777778,387.0,1270.0,32176,23.688000000000002,23.688000000000002,0.0,61.351000000000006,61.351000000000006,0.0,0.0 +468,Williams,39.15472222222222,-122.14944444444446,25.0,82.0,5123,5.444,5.444,0.0,14.100999999999999,14.100999999999999,0.0,0.0 +469,Willits,39.40972222222222,-123.35555555555555,424.0,1391.0,4888,2.803,2.798,0.005,7.26,7.247999999999999,0.013000000000000001,0.17 +470,Willows,39.52444444444444,-122.19361111111111,42.0,138.0,6166,2.873,2.847,0.026000000000000002,7.441,7.372999999999999,0.068,0.92 +471,Windsor,38.54611111111111,-122.80527777777777,36.0,118.0,26801,7.292999999999999,7.268,0.025,18.887999999999998,18.824,0.064,0.34 +472,Winters,38.525,-121.97083333333333,40.0,131.0,6624,2.937,2.912,0.025,7.607,7.542999999999999,0.065,0.85 +473,Woodlake,36.41638888888889,-119.09944444444444,134.0,440.0,7279,2.765,2.248,0.517,7.159,5.821000000000001,1.338,18.69 +474,Woodland,38.67861111111111,-121.77333333333333,21.0,69.0,55468,15.302999999999999,15.302999999999999,0.0,39.634,39.634,0.0,0.0 +475,Woodside,37.420833333333334,-122.25972222222222,117.0,384.0,5287,11.732000000000001,11.732000000000001,0.0,30.386,30.386,0.0,0.0 +476,YorbaLinda,33.888551,-117.813231,82.3,270.0,65237,20.018,19.483,0.535,51.847,50.46,1.3869999999999998,2.67 +477,Yountville,38.403055555555554,-122.36222222222221,30.0,98.0,2933,1.531,1.531,0.0,3.966,3.966,0.0,0.0 +478,Yreka,41.72666666666667,-122.6375,787.0,2582.0,7765,10.052999999999999,9.98,0.073,26.035999999999998,25.846999999999998,0.188,0.72 +479,YubaCity,39.13472222222222,-121.6261111111111,18.0,59.0,64925,14.655999999999999,14.578,0.078,37.959,37.758,0.201,0.53 +480,Yucaipa,34.030277777777776,-117.04861111111111,798.0,2618.0,51367,27.893,27.888,0.005,72.244,72.23100000000001,0.013000000000000001,0.02 +481,YuccaValley,34.13333333333333,-116.41666666666667,1027.0,3369.0,20700,40.015,40.015,0.0,103.639,103.639,0.0,0.0 diff --git a/examples/pdsh/data/marathon-data.csv b/examples/pdsh/data/marathon-data.csv new file mode 100644 index 00000000..2f53bc8d --- /dev/null +++ b/examples/pdsh/data/marathon-data.csv @@ -0,0 +1,37251 @@ +age,gender,split,final +33,M,01:05:38,02:08:51 +32,M,01:06:26,02:09:28 +31,M,01:06:49,02:10:42 +38,M,01:06:16,02:13:45 +31,M,01:06:32,02:13:59 +31,M,01:06:13,02:14:11 +27,M,01:06:40,02:14:28 +31,M,01:06:31,02:15:16 +30,M,01:05:39,02:15:57 +30,M,01:05:40,02:16:39 +26,M,01:08:10,02:18:18 +29,M,01:05:54,02:18:52 +28,M,01:08:59,02:19:36 +24,M,01:09:16,02:19:45 +37,M,01:05:21,02:19:47 +25,M,01:09:52,02:20:17 +22,M,01:09:53,02:20:57 +30,M,01:05:40,02:21:17 +26,M,01:10:11,02:21:24 +36,M,01:10:35,02:21:55 +28,M,01:11:59,02:22:14 +31,M,01:05:54,02:22:20 +41,M,01:09:14,02:22:27 +24,M,01:11:18,02:22:55 +26,M,01:10:17,02:22:56 +36,M,01:06:39,02:23:31 +28,M,01:10:32,02:23:39 +29,M,01:12:25,02:23:48 +34,W,01:12:35,02:24:13 +31,M,01:14:07,02:25:19 +39,W,01:13:13,02:25:37 +29,M,01:09:59,02:25:38 +29,W,01:13:09,02:25:50 +31,M,01:13:24,02:25:56 +30,M,01:11:51,02:26:15 +30,M,01:08:34,02:26:36 +34,M,01:06:20,02:27:22 +27,M,01:12:23,02:27:24 +23,M,01:13:42,02:27:32 +37,M,01:12:10,02:28:08 +36,W,01:12:57,02:28:13 +27,W,01:13:07,02:28:27 +39,M,01:12:57,02:28:36 +38,M,01:13:08,02:28:51 +47,M,01:15:38,02:28:55 +36,W,01:13:00,02:28:56 +34,W,01:12:47,02:29:07 +24,W,01:13:39,02:29:10 +38,M,01:13:30,02:29:12 +35,M,01:12:52,02:29:15 +35,M,01:13:51,02:29:35 +39,M,01:12:41,02:29:35 +36,W,01:13:44,02:29:38 +33,M,01:13:40,02:29:52 +22,M,01:09:44,02:29:54 +22,M,01:09:35,02:30:10 +35,W,01:14:04,02:30:10 +32,M,01:13:37,02:30:12 +33,M,01:13:00,02:30:20 +34,W,01:13:28,02:30:20 +36,M,01:14:28,02:30:41 +31,W,01:14:39,02:30:45 +23,W,01:13:42,02:30:46 +32,W,01:13:51,02:30:52 +38,W,01:14:22,02:30:55 +31,M,01:13:13,02:31:01 +28,M,01:13:18,02:31:06 +37,W,01:13:29,02:31:06 +30,M,01:14:56,02:31:10 +27,M,01:13:52,02:31:13 +34,M,01:14:07,02:31:18 +44,M,01:16:36,02:31:18 +31,M,01:14:40,02:31:34 +30,M,01:14:40,02:31:41 +26,M,01:12:43,02:31:44 +29,M,01:07:09,02:31:49 +34,M,01:14:59,02:32:05 +34,M,01:13:46,02:32:09 +33,M,01:13:31,02:32:10 +29,M,01:07:27,02:32:12 +24,M,01:13:16,02:32:15 +28,M,01:14:04,02:32:15 +32,M,01:14:27,02:32:18 +33,M,01:15:59,02:32:27 +25,M,01:15:16,02:32:35 +31,M,01:12:43,02:32:36 +29,M,01:13:34,02:32:45 +33,M,01:12:57,02:32:54 +26,M,01:14:40,02:32:57 +31,M,01:15:02,02:33:15 +29,M,01:14:41,02:33:19 +27,M,01:15:06,02:33:24 +34,M,01:14:12,02:33:26 +44,M,01:15:40,02:33:30 +36,M,01:15:37,02:33:37 +44,M,01:16:08,02:33:45 +26,M,01:16:15,02:33:49 +42,M,01:15:33,02:33:53 +36,M,01:15:17,02:33:56 +42,M,01:15:37,02:33:59 +35,M,01:15:30,02:34:03 +26,M,01:12:56,02:34:05 +26,M,01:15:19,02:34:06 +35,W,01:14:29,02:34:11 +28,M,01:13:13,02:34:15 +41,M,01:16:56,02:34:18 +25,M,01:14:28,02:34:21 +33,M,01:13:30,02:34:31 +25,M,01:13:51,02:34:33 +41,M,01:15:41,02:34:37 +36,M,01:14:58,02:34:40 +31,M,01:13:32,02:34:41 +24,M,01:15:26,02:34:42 +34,M,01:15:19,02:34:57 +36,W,01:15:38,02:34:58 +23,M,01:15:09,02:35:01 +35,M,01:15:10,02:35:04 +34,M,01:14:00,02:35:05 +34,W,01:15:24,02:35:10 +38,M,01:12:39,02:35:10 +26,M,01:15:25,02:35:21 +41,M,01:17:40,02:35:27 +27,M,01:16:36,02:35:28 +36,W,01:15:19,02:35:28 +35,M,01:15:12,02:35:30 +34,M,01:14:15,02:35:33 +24,M,01:15:14,02:35:35 +28,M,01:15:55,02:35:46 +28,M,01:15:25,02:35:55 +24,M,01:14:33,02:35:57 +34,M,01:16:03,02:36:04 +38,M,01:15:09,02:36:05 +31,M,01:17:28,02:36:08 +24,M,01:18:45,02:36:09 +38,M,01:13:44,02:36:11 +34,M,01:17:16,02:36:16 +32,M,01:17:28,02:36:19 +29,M,01:16:53,02:36:33 +27,M,01:17:19,02:36:33 +28,M,01:16:59,02:36:35 +35,M,01:15:45,02:36:38 +30,M,01:16:33,02:36:42 +31,M,01:14:55,02:36:42 +29,M,01:18:40,02:36:42 +33,M,01:16:46,02:36:43 +23,M,01:14:12,02:36:44 +37,M,01:16:23,02:36:45 +26,M,01:18:07,02:36:49 +24,M,01:18:47,02:36:50 +38,M,01:10:04,02:36:50 +33,M,01:15:27,02:37:03 +41,M,01:18:19,02:37:05 +26,M,01:17:46,02:37:05 +26,M,01:17:20,02:37:20 +45,M,01:16:57,02:37:20 +30,M,01:16:27,02:37:23 +36,M,01:16:58,02:37:25 +33,M,01:13:59,02:37:32 +33,M,01:13:06,02:37:45 +35,M,01:17:31,02:37:53 +40,M,01:16:21,02:37:54 +37,M,01:10:10,02:37:56 +34,M,01:19:48,02:38:00 +32,M,01:15:30,02:38:01 +35,M,01:19:03,02:38:02 +38,M,01:17:28,02:38:12 +32,M,01:12:19,02:38:22 +40,M,01:17:41,02:38:29 +26,M,01:16:14,02:38:52 +31,M,01:17:42,02:38:54 +22,M,01:19:04,02:38:55 +46,M,01:14:53,02:38:59 +26,M,01:13:30,02:39:04 +29,M,01:20:33,02:39:07 +28,M,01:17:48,02:39:08 +32,M,01:17:30,02:39:12 +31,W,01:17:08,02:39:15 +33,M,01:16:54,02:39:19 +27,M,01:19:23,02:39:21 +22,M,01:17:25,02:39:29 +30,M,01:16:47,02:39:36 +37,M,01:15:40,02:39:44 +26,M,01:14:07,02:39:50 +40,M,01:18:24,02:39:51 +44,M,01:16:59,02:39:52 +23,M,01:18:46,02:39:56 +40,M,01:17:58,02:39:59 +23,M,01:18:15,02:39:59 +40,M,01:19:28,02:40:01 +46,M,01:15:09,02:40:03 +32,M,01:17:44,02:40:04 +28,M,01:21:02,02:40:05 +27,M,01:19:37,02:40:07 +40,M,01:19:01,02:40:09 +33,M,01:19:48,02:40:09 +40,M,01:17:47,02:40:11 +23,W,01:20:25,02:40:12 +38,M,01:19:53,02:40:15 +35,M,01:18:50,02:40:26 +30,M,01:18:52,02:40:31 +33,M,01:18:35,02:40:32 +26,M,01:16:02,02:40:41 +24,M,01:18:35,02:40:47 +53,M,01:15:11,02:40:49 +37,M,01:17:57,02:40:51 +34,M,01:17:42,02:40:53 +31,M,01:17:57,02:40:56 +42,M,01:20:09,02:41:00 +41,M,01:19:48,02:41:16 +35,W,01:18:19,02:41:16 +44,M,01:19:12,02:41:17 +33,M,01:14:57,02:41:17 +36,M,01:18:39,02:41:25 +44,M,01:19:34,02:41:26 +34,M,01:18:47,02:41:32 +39,M,01:14:44,02:41:33 +30,M,01:18:28,02:41:36 +40,M,01:18:15,02:41:59 +32,M,01:17:29,02:42:01 +18,W,01:18:03,02:42:06 +40,M,01:17:48,02:42:06 +25,M,01:19:00,02:42:08 +31,M,01:15:20,02:42:10 +44,M,01:19:47,02:42:10 +34,M,01:20:32,02:42:14 +42,M,01:18:48,02:42:14 +27,M,01:17:49,02:42:15 +36,M,01:20:06,02:42:19 +27,W,01:22:19,02:42:23 +27,M,01:17:49,02:42:27 +27,M,01:22:18,02:42:27 +40,M,01:20:40,02:42:33 +35,M,01:20:04,02:42:34 +35,M,01:18:06,02:42:35 +32,M,01:25:19,02:42:35 +25,M,01:20:04,02:42:36 +38,M,01:16:24,02:42:37 +24,M,01:16:21,02:42:38 +29,M,01:20:31,02:42:39 +30,M,01:18:09,02:42:41 +27,M,01:19:50,02:42:42 +25,M,01:20:50,02:42:42 +40,M,01:18:42,02:42:44 +36,M,01:21:25,02:42:46 +33,M,01:21:50,02:42:48 +38,M,01:19:13,02:42:50 +38,M,01:17:13,02:42:51 +41,M,01:19:56,02:42:54 +29,M,01:20:18,02:43:00 +57,M,01:19:05,02:43:06 +44,M,01:20:23,02:43:17 +27,M,01:15:19,02:43:18 +52,M,01:20:44,02:43:25 +44,M,01:20:41,02:43:30 +40,M,01:20:31,02:43:30 +36,M,01:20:03,02:43:32 +32,M,01:20:34,02:43:34 +33,M,01:18:50,02:43:34 +27,W,01:21:03,02:43:36 +52,M,01:19:26,02:43:37 +30,M,01:18:37,02:43:38 +33,W,01:18:51,02:43:40 +41,M,01:19:07,02:43:40 +29,M,01:19:48,02:43:41 +35,M,01:19:17,02:43:41 +27,M,01:18:22,02:43:43 +29,W,01:20:48,02:43:43 +45,M,01:18:03,02:43:48 +32,M,01:20:25,02:43:49 +49,M,01:20:02,02:43:55 +34,M,01:19:42,02:43:57 +31,M,01:17:36,02:44:03 +39,M,01:18:57,02:44:06 +28,M,01:18:41,02:44:10 +24,M,01:19:01,02:44:10 +50,M,01:19:53,02:44:13 +25,M,01:20:28,02:44:16 +30,M,01:17:20,02:44:16 +33,M,01:18:15,02:44:21 +29,M,01:21:31,02:44:22 +48,M,01:19:56,02:44:23 +24,M,01:15:31,02:44:29 +28,M,01:19:05,02:44:34 +34,M,01:18:30,02:44:36 +24,M,01:20:09,02:44:44 +42,M,01:20:31,02:44:47 +24,M,01:18:49,02:44:48 +33,M,01:21:02,02:44:51 +24,M,01:20:44,02:44:56 +44,M,01:22:46,02:45:00 +32,M,01:18:16,02:45:02 +30,W,01:20:55,02:45:02 +37,M,01:18:08,02:45:04 +32,M,01:18:44,02:45:08 +31,M,01:23:44,02:45:13 +45,M,01:21:13,02:45:14 +27,M,01:18:08,02:45:19 +27,M,01:22:22,02:45:23 +27,M,01:18:26,02:45:24 +25,M,01:18:01,02:45:25 +49,M,01:20:34,02:45:25 +37,M,01:19:36,02:45:27 +39,M,01:22:49,02:45:28 +31,M,01:19:50,02:45:35 +32,M,01:20:15,02:45:36 +40,M,01:19:07,02:45:41 +31,M,01:18:47,02:45:41 +35,M,01:20:56,02:45:42 +36,M,01:22:05,02:45:45 +39,M,01:22:37,02:45:46 +34,M,01:20:08,02:45:50 +41,M,01:20:12,02:45:54 +22,M,01:15:58,02:45:54 +39,M,01:20:40,02:45:56 +43,M,01:18:11,02:45:56 +31,M,01:22:00,02:45:57 +32,M,01:15:20,02:45:58 +38,M,01:19:30,02:45:59 +28,M,01:20:14,02:46:00 +31,M,01:21:43,02:46:00 +29,M,01:15:47,02:46:00 +39,M,01:19:16,02:46:12 +36,M,01:17:41,02:46:13 +31,M,01:20:34,02:46:20 +33,M,01:21:13,02:46:20 +36,M,01:22:47,02:46:26 +41,M,01:21:43,02:46:34 +43,M,01:20:14,02:46:36 +35,M,01:20:38,02:46:38 +39,W,01:21:00,02:46:46 +32,M,01:17:37,02:46:47 +23,M,01:13:40,02:46:47 +30,W,01:21:29,02:46:48 +31,M,01:23:43,02:46:48 +23,W,01:20:48,02:46:50 +49,M,01:22:17,02:46:51 +36,M,01:21:55,02:46:51 +41,M,01:18:34,02:46:52 +42,M,01:21:43,02:47:00 +38,M,01:22:23,02:47:00 +38,M,01:24:03,02:47:06 +32,M,01:17:41,02:47:14 +27,M,01:21:40,02:47:16 +46,M,01:20:30,02:47:22 +41,M,01:14:49,02:47:22 +39,M,01:22:40,02:47:25 +40,M,01:19:21,02:47:25 +30,M,01:21:36,02:47:28 +25,M,01:20:23,02:47:28 +28,M,01:23:24,02:47:31 +27,M,01:22:10,02:47:33 +27,M,01:14:52,02:47:34 +37,M,01:22:00,02:47:36 +45,M,01:22:52,02:47:37 +41,M,01:22:03,02:47:37 +37,M,01:18:08,02:47:37 +29,M,01:21:30,02:47:39 +35,M,01:22:21,02:47:40 +40,M,01:20:31,02:47:42 +37,M,01:18:52,02:47:44 +39,W,01:20:53,02:47:45 +44,M,01:23:37,02:47:48 +37,M,01:19:19,02:47:49 +26,M,01:24:09,02:47:50 +44,M,01:25:37,02:47:51 +33,M,01:20:43,02:47:51 +31,M,01:23:08,02:47:52 +26,M,01:24:51,02:47:59 +37,M,01:21:28,02:48:02 +47,M,01:23:27,02:48:03 +29,M,01:21:20,02:48:06 +45,M,01:23:08,02:48:07 +40,M,01:21:50,02:48:08 +26,M,01:21:57,02:48:09 +29,M,01:19:03,02:48:10 +47,M,01:23:24,02:48:11 +32,M,01:22:17,02:48:18 +40,M,01:22:19,02:48:18 +40,M,01:21:58,02:48:18 +39,M,01:19:58,02:48:20 +28,M,01:23:22,02:48:21 +34,M,01:17:36,02:48:24 +38,M,01:21:00,02:48:26 +33,M,01:20:37,02:48:26 +56,M,01:21:13,02:48:30 +44,M,01:22:10,02:48:31 +31,M,01:14:35,02:48:31 +34,M,01:25:35,02:48:34 +43,M,01:24:12,02:48:37 +24,M,01:21:09,02:48:42 +36,M,01:22:26,02:48:44 +26,M,01:17:09,02:48:45 +45,M,01:24:57,02:48:47 +45,M,01:23:34,02:48:48 +25,M,01:25:44,02:48:56 +28,M,01:18:38,02:48:58 +35,M,01:24:00,02:48:58 +35,M,01:22:50,02:49:00 +38,M,01:15:49,02:49:00 +24,M,01:22:35,02:49:02 +37,M,01:22:03,02:49:02 +40,M,01:22:38,02:49:02 +36,M,01:20:26,02:49:03 +40,M,01:22:57,02:49:07 +34,M,01:23:34,02:49:09 +30,M,01:23:59,02:49:10 +33,M,01:23:29,02:49:10 +41,M,01:19:02,02:49:12 +44,M,01:21:01,02:49:13 +57,M,01:21:57,02:49:16 +36,M,01:21:47,02:49:20 +27,M,01:22:31,02:49:23 +30,M,01:20:14,02:49:27 +28,W,01:22:12,02:49:29 +48,M,01:22:56,02:49:29 +37,M,01:17:03,02:49:31 +45,M,01:22:27,02:49:31 +21,M,01:23:40,02:49:34 +32,M,01:22:22,02:49:34 +49,M,01:22:00,02:49:36 +27,M,01:24:32,02:49:36 +22,M,01:20:35,02:49:37 +28,M,01:22:16,02:49:40 +25,W,01:18:00,02:49:41 +40,M,01:23:23,02:49:46 +43,M,01:21:41,02:49:48 +52,M,01:23:05,02:49:49 +23,M,01:15:55,02:49:50 +41,M,01:19:34,02:49:51 +41,M,01:24:05,02:49:51 +51,M,01:23:47,02:49:58 +37,M,01:24:22,02:49:58 +34,M,01:22:07,02:49:58 +47,M,01:24:50,02:50:04 +49,M,01:22:00,02:50:06 +41,M,01:23:36,02:50:08 +37,M,01:19:50,02:50:09 +38,M,01:20:59,02:50:10 +30,M,01:22:46,02:50:11 +43,M,01:21:30,02:50:14 +46,M,01:24:00,02:50:14 +42,M,01:21:24,02:50:16 +37,M,01:18:27,02:50:17 +51,M,01:24:10,02:50:19 +37,M,01:21:06,02:50:20 +24,M,01:23:52,02:50:21 +49,M,01:22:50,02:50:22 +38,W,01:24:26,02:50:23 +36,M,01:25:35,02:50:24 +32,M,01:23:21,02:50:26 +43,M,01:22:58,02:50:28 +38,M,01:23:06,02:50:29 +27,M,01:18:15,02:50:29 +42,M,01:22:21,02:50:29 +48,M,01:22:25,02:50:29 +23,M,01:17:11,02:50:30 +38,M,01:25:20,02:50:32 +29,M,01:23:43,02:50:32 +32,M,01:18:56,02:50:36 +29,M,01:15:17,02:50:40 +40,M,01:21:48,02:50:47 +49,M,01:24:49,02:50:48 +26,M,01:23:01,02:50:50 +24,M,01:20:40,02:50:53 +34,W,01:24:13,02:50:53 +34,M,01:19:47,02:50:55 +40,M,01:25:17,02:50:57 +30,M,01:25:17,02:50:57 +20,M,01:21:48,02:50:57 +27,M,01:21:05,02:50:58 +38,M,01:22:30,02:51:00 +39,M,01:21:50,02:51:01 +28,M,01:20:33,02:51:01 +35,M,01:20:24,02:51:01 +39,M,01:23:38,02:51:01 +41,M,01:23:35,02:51:03 +41,M,01:24:03,02:51:03 +28,M,01:24:34,02:51:05 +31,M,01:23:54,02:51:08 +34,M,01:17:57,02:51:10 +30,M,01:22:13,02:51:11 +39,M,01:24:31,02:51:15 +26,M,01:26:00,02:51:16 +41,M,01:22:12,02:51:17 +40,M,01:24:45,02:51:17 +27,M,01:20:35,02:51:19 +37,M,01:22:17,02:51:19 +38,M,01:22:39,02:51:20 +39,M,01:20:57,02:51:21 +41,W,01:24:18,02:51:25 +52,M,01:23:09,02:51:26 +42,M,01:25:35,02:51:27 +36,M,01:23:38,02:51:28 +31,M,01:25:06,02:51:29 +42,M,01:23:25,02:51:29 +28,M,01:23:52,02:51:31 +41,M,01:22:21,02:51:32 +42,M,01:21:49,02:51:33 +36,W,01:22:16,02:51:34 +48,M,01:23:37,02:51:35 +30,M,01:22:43,02:51:38 +35,M,01:23:52,02:51:38 +34,M,01:22:44,02:51:40 +21,M,01:23:34,02:51:41 +44,M,01:19:33,02:51:41 +50,M,01:22:24,02:51:42 +40,M,01:23:57,02:51:42 +40,M,01:24:54,02:51:44 +31,M,01:22:30,02:51:47 +28,M,01:23:01,02:51:49 +38,M,01:24:29,02:51:51 +28,M,01:23:46,02:51:52 +35,M,01:21:22,02:51:54 +21,M,01:20:04,02:51:55 +28,M,01:22:53,02:51:57 +31,W,01:25:52,02:51:58 +39,M,01:23:17,02:51:58 +42,M,01:19:36,02:51:59 +34,M,01:23:33,02:51:59 +46,M,01:24:50,02:51:59 +22,W,01:25:11,02:52:01 +35,M,01:25:40,02:52:06 +44,M,01:24:36,02:52:07 +52,M,01:23:02,02:52:08 +47,M,01:21:52,02:52:08 +50,M,01:24:50,02:52:10 +43,M,01:19:34,02:52:14 +49,M,01:24:24,02:52:14 +40,M,01:21:57,02:52:14 +29,M,01:21:54,02:52:15 +33,W,01:26:52,02:52:18 +33,M,01:26:31,02:52:18 +34,M,01:24:20,02:52:21 +53,M,01:23:43,02:52:24 +27,M,01:26:18,02:52:25 +38,M,01:23:07,02:52:25 +33,M,01:27:03,02:52:25 +40,M,01:24:56,02:52:27 +25,M,01:25:50,02:52:27 +25,M,01:26:01,02:52:27 +30,M,01:26:25,02:52:28 +35,M,01:24:14,02:52:28 +29,M,01:22:30,02:52:31 +37,M,01:16:49,02:52:31 +34,M,01:21:54,02:52:31 +27,W,01:25:05,02:52:33 +33,M,01:23:51,02:52:34 +38,M,01:23:17,02:52:36 +39,M,01:25:06,02:52:38 +28,M,01:23:41,02:52:39 +43,M,01:24:17,02:52:43 +51,M,01:22:12,02:52:44 +32,W,01:24:21,02:52:44 +36,M,01:24:59,02:52:45 +44,M,01:25:57,02:52:46 +51,M,01:24:27,02:52:48 +31,M,01:24:15,02:52:49 +39,W,01:25:10,02:52:50 +39,M,01:23:18,02:52:51 +30,M,01:25:00,02:52:53 +44,M,01:23:08,02:52:56 +49,M,01:24:32,02:52:57 +37,M,01:22:46,02:53:00 +30,M,01:23:13,02:53:03 +33,M,01:22:00,02:53:04 +39,M,01:24:21,02:53:05 +40,M,01:23:26,02:53:06 +32,M,01:22:55,02:53:06 +40,M,01:24:40,02:53:06 +31,M,01:22:51,02:53:10 +25,M,01:21:25,02:53:12 +36,M,01:15:26,02:53:14 +41,M,01:20:48,02:53:17 +29,M,01:22:23,02:53:17 +38,M,01:24:56,02:53:21 +42,M,01:24:34,02:53:25 +26,M,01:23:46,02:53:25 +37,M,01:22:45,02:53:25 +42,M,01:21:36,02:53:26 +38,M,01:21:43,02:53:27 +47,M,01:22:27,02:53:28 +45,M,01:23:36,02:53:28 +35,M,01:24:24,02:53:29 +30,M,01:26:26,02:53:31 +23,M,01:24:01,02:53:31 +31,M,01:27:01,02:53:32 +41,M,01:26:28,02:53:35 +31,M,01:22:26,02:53:40 +44,M,01:21:30,02:53:43 +32,M,01:24:29,02:53:44 +46,M,01:24:02,02:53:45 +38,M,01:25:07,02:53:46 +44,M,01:23:49,02:53:47 +51,M,01:25:36,02:53:47 +32,M,01:26:08,02:53:48 +33,M,01:26:17,02:53:48 +29,W,01:28:06,02:53:50 +50,M,01:22:43,02:53:52 +45,M,01:23:54,02:53:52 +39,M,01:23:30,02:53:54 +30,M,01:23:24,02:53:55 +30,M,01:27:32,02:53:55 +30,W,01:24:16,02:53:59 +40,M,01:19:40,02:53:59 +28,M,01:24:38,02:53:59 +27,M,01:18:43,02:54:01 +33,M,01:20:17,02:54:02 +27,W,01:24:40,02:54:04 +42,M,01:26:39,02:54:04 +45,M,01:25:12,02:54:05 +39,M,01:24:16,02:54:06 +44,M,01:23:10,02:54:07 +36,M,01:25:05,02:54:07 +33,M,01:22:43,02:54:08 +44,M,01:26:03,02:54:09 +42,M,01:26:13,02:54:11 +37,M,01:24:58,02:54:11 +25,M,01:22:53,02:54:14 +25,W,01:25:23,02:54:18 +45,M,01:22:35,02:54:21 +31,M,01:25:47,02:54:22 +44,M,01:24:01,02:54:22 +34,M,01:27:01,02:54:24 +39,W,01:26:15,02:54:24 +46,M,01:23:12,02:54:31 +36,M,01:26:29,02:54:31 +39,M,01:25:11,02:54:31 +32,M,01:25:26,02:54:32 +45,M,01:25:01,02:54:35 +49,M,01:26:34,02:54:39 +39,M,01:26:25,02:54:40 +37,M,01:22:26,02:54:42 +28,M,01:24:11,02:54:42 +24,M,01:23:15,02:54:42 +39,M,01:25:35,02:54:45 +43,M,01:25:14,02:54:46 +37,M,01:26:05,02:54:47 +33,M,01:26:52,02:54:49 +35,M,01:23:24,02:54:50 +32,W,01:27:49,02:54:51 +46,M,01:25:28,02:54:52 +44,M,01:24:52,02:54:55 +39,W,01:24:03,02:54:55 +24,M,01:21:11,02:54:57 +27,M,01:23:16,02:54:57 +28,W,01:26:08,02:54:58 +41,W,01:24:55,02:55:02 +41,M,01:22:21,02:55:03 +35,M,01:25:44,02:55:04 +40,M,01:24:15,02:55:04 +34,M,01:25:07,02:55:04 +39,M,01:21:54,02:55:06 +40,M,01:24:15,02:55:06 +25,M,01:27:42,02:55:09 +51,M,01:25:02,02:55:12 +43,M,01:24:33,02:55:15 +42,M,01:22:47,02:55:16 +45,M,01:23:32,02:55:18 +29,M,01:18:41,02:55:18 +52,M,01:24:57,02:55:21 +25,W,01:25:41,02:55:23 +37,M,01:23:29,02:55:23 +25,M,01:24:21,02:55:23 +32,M,01:26:30,02:55:25 +40,M,01:23:39,02:55:25 +37,M,01:25:30,02:55:25 +29,M,01:29:45,02:55:25 +42,M,01:23:58,02:55:28 +39,M,01:22:23,02:55:30 +45,M,01:26:16,02:55:32 +40,M,01:27:03,02:55:32 +48,M,01:26:08,02:55:33 +42,M,01:23:23,02:55:35 +32,M,01:22:28,02:55:37 +30,M,01:24:06,02:55:38 +50,M,01:25:00,02:55:41 +32,W,01:25:40,02:55:42 +33,M,01:26:35,02:55:43 +49,M,01:25:19,02:55:43 +38,W,01:26:36,02:55:43 +51,M,01:23:42,02:55:45 +43,M,01:22:33,02:55:46 +30,M,01:24:13,02:55:48 +37,W,01:26:59,02:55:49 +44,M,01:22:22,02:55:50 +33,M,01:24:20,02:55:51 +52,M,01:24:33,02:55:54 +44,M,01:25:26,02:55:54 +40,M,01:26:49,02:55:55 +41,W,01:24:25,02:55:56 +25,M,01:22:00,02:55:57 +31,M,01:23:19,02:55:57 +42,M,01:27:07,02:55:58 +30,W,01:27:02,02:56:00 +45,M,01:24:43,02:56:01 +40,M,01:23:55,02:56:06 +30,M,01:23:44,02:56:06 +40,M,01:27:16,02:56:07 +42,M,01:24:42,02:56:07 +43,M,01:24:49,02:56:09 +32,M,01:24:25,02:56:10 +44,M,01:26:52,02:56:13 +34,M,01:25:48,02:56:13 +32,M,01:24:17,02:56:15 +40,M,01:25:02,02:56:18 +28,M,01:20:54,02:56:19 +27,M,01:26:13,02:56:21 +43,M,01:23:07,02:56:22 +28,M,01:30:42,02:56:24 +30,M,01:20:27,02:56:26 +42,M,01:23:05,02:56:27 +27,M,01:24:16,02:56:28 +42,M,01:24:24,02:56:28 +38,M,01:27:36,02:56:28 +39,M,01:27:17,02:56:29 +37,M,01:24:15,02:56:30 +42,M,01:29:53,02:56:33 +40,M,01:26:08,02:56:35 +41,M,01:26:39,02:56:35 +36,M,01:23:39,02:56:35 +29,W,01:25:02,02:56:36 +53,M,01:25:07,02:56:36 +42,M,01:28:19,02:56:36 +43,M,01:22:27,02:56:37 +42,M,01:26:13,02:56:38 +22,W,01:26:02,02:56:39 +41,M,01:28:27,02:56:39 +46,M,01:24:03,02:56:41 +26,M,01:29:32,02:56:42 +41,M,01:22:29,02:56:44 +26,M,01:26:16,02:56:45 +42,M,01:26:17,02:56:47 +36,M,01:26:42,02:56:48 +34,M,01:23:18,02:56:48 +49,M,01:27:00,02:56:49 +35,M,01:27:37,02:56:49 +36,M,01:24:45,02:56:49 +41,M,01:22:38,02:56:50 +38,M,01:26:08,02:56:50 +50,M,01:23:59,02:56:51 +41,M,01:22:43,02:56:51 +38,M,01:25:42,02:56:52 +28,W,01:24:16,02:56:53 +46,M,01:26:17,02:56:54 +29,M,01:26:23,02:56:54 +38,M,01:27:14,02:56:56 +44,M,01:24:02,02:56:56 +47,M,01:24:37,02:56:57 +33,M,01:27:02,02:56:58 +35,M,01:23:37,02:56:58 +42,M,01:25:30,02:56:59 +26,M,01:28:12,02:57:01 +44,M,01:25:43,02:57:01 +37,M,01:27:27,02:57:03 +38,M,01:26:28,02:57:03 +41,W,01:28:01,02:57:04 +46,M,01:24:35,02:57:06 +36,M,01:27:39,02:57:09 +25,M,01:22:50,02:57:10 +42,M,01:28:24,02:57:13 +40,M,01:27:19,02:57:14 +37,M,01:24:54,02:57:17 +28,M,01:24:10,02:57:19 +44,M,01:24:29,02:57:20 +42,M,01:27:57,02:57:20 +56,M,01:26:13,02:57:20 +33,M,01:23:29,02:57:21 +39,M,01:25:36,02:57:23 +50,M,01:26:15,02:57:23 +38,M,01:30:08,02:57:24 +42,M,01:26:57,02:57:25 +29,M,01:24:09,02:57:26 +32,M,01:25:52,02:57:26 +36,M,01:26:56,02:57:27 +32,M,01:26:59,02:57:27 +34,M,01:24:34,02:57:27 +43,M,01:26:26,02:57:28 +39,M,01:25:15,02:57:29 +42,W,01:27:25,02:57:29 +32,M,01:27:02,02:57:30 +49,M,01:25:19,02:57:32 +26,M,01:20:44,02:57:35 +40,M,01:27:14,02:57:35 +39,M,01:23:32,02:57:37 +58,M,01:25:01,02:57:37 +32,M,01:28:40,02:57:37 +47,M,01:25:19,02:57:38 +45,M,01:25:52,02:57:39 +36,M,01:28:53,02:57:39 +42,M,01:28:46,02:57:40 +47,M,01:25:21,02:57:40 +46,M,01:27:21,02:57:41 +34,W,01:27:46,02:57:41 +49,M,01:27:57,02:57:41 +45,M,01:21:22,02:57:42 +45,M,01:24:50,02:57:43 +52,M,01:26:20,02:57:43 +41,M,01:23:15,02:57:44 +32,M,01:27:12,02:57:45 +31,M,01:25:24,02:57:46 +39,M,01:25:57,02:57:47 +40,M,01:25:35,02:57:47 +35,M,01:26:29,02:57:47 +34,M,01:27:50,02:57:48 +32,M,01:22:53,02:57:49 +40,M,01:26:18,02:57:50 +37,M,01:27:43,02:57:51 +27,M,01:21:44,02:57:53 +46,M,01:21:41,02:57:53 +27,W,01:25:48,02:57:53 +42,M,01:24:59,02:57:55 +34,M,01:26:35,02:57:55 +32,M,01:27:02,02:57:55 +46,M,01:28:58,02:57:57 +25,M,01:27:02,02:57:59 +22,M,01:23:41,02:57:59 +34,M,01:28:15,02:57:59 +46,M,01:26:59,02:58:00 +31,M,01:29:21,02:58:00 +36,M,01:27:15,02:58:02 +32,W,01:26:14,02:58:02 +44,M,01:26:30,02:58:02 +41,M,01:26:57,02:58:03 +45,M,01:25:57,02:58:03 +33,M,01:26:18,02:58:07 +38,M,01:24:29,02:58:09 +31,M,01:28:20,02:58:09 +42,M,01:27:31,02:58:09 +45,M,01:30:16,02:58:09 +40,M,01:24:35,02:58:10 +41,M,01:27:51,02:58:10 +35,M,01:26:33,02:58:12 +31,M,01:28:43,02:58:13 +35,M,01:27:14,02:58:14 +35,M,01:26:43,02:58:15 +34,M,01:26:23,02:58:16 +49,M,01:28:45,02:58:17 +32,M,01:26:35,02:58:20 +33,M,01:28:04,02:58:21 +27,M,01:28:46,02:58:22 +48,M,01:27:20,02:58:24 +42,M,01:29:12,02:58:24 +30,M,01:29:25,02:58:26 +31,M,01:29:22,02:58:28 +27,M,01:27:50,02:58:28 +47,M,01:23:34,02:58:30 +46,M,01:26:26,02:58:32 +31,M,01:29:46,02:58:33 +44,M,01:27:14,02:58:33 +41,M,01:27:56,02:58:35 +34,M,01:28:58,02:58:36 +40,M,01:25:05,02:58:39 +24,M,01:25:38,02:58:40 +39,M,01:23:05,02:58:40 +40,M,01:28:20,02:58:41 +43,M,01:28:36,02:58:41 +41,M,01:24:45,02:58:42 +27,M,01:26:31,02:58:42 +30,M,01:21:15,02:58:43 +46,M,01:26:29,02:58:44 +36,M,01:27:49,02:58:45 +41,M,01:27:40,02:58:45 +36,M,01:28:07,02:58:45 +47,M,01:25:57,02:58:46 +31,M,01:25:56,02:58:47 +31,M,01:28:39,02:58:48 +37,M,01:26:43,02:58:49 +39,M,01:23:59,02:58:49 +34,M,01:23:05,02:58:50 +48,M,01:27:35,02:58:50 +42,M,01:26:40,02:58:50 +36,M,01:23:03,02:58:50 +40,M,01:27:04,02:58:50 +42,M,01:29:57,02:58:51 +33,M,01:27:41,02:58:51 +40,M,01:29:06,02:58:52 +44,M,01:27:17,02:58:52 +24,M,01:22:58,02:58:53 +38,M,01:27:57,02:58:54 +38,M,01:27:46,02:58:56 +45,M,01:25:20,02:58:57 +44,M,01:27:57,02:58:57 +20,M,01:27:38,02:58:58 +43,M,01:27:07,02:59:00 +42,M,01:28:20,02:59:00 +39,M,01:26:03,02:59:01 +45,M,01:29:24,02:59:01 +32,M,01:27:00,02:59:01 +45,M,01:27:07,02:59:01 +40,M,01:28:29,02:59:02 +25,M,01:24:14,02:59:02 +36,M,01:27:23,02:59:02 +55,M,01:27:13,02:59:05 +42,W,01:24:31,02:59:06 +27,W,01:27:28,02:59:08 +36,M,01:24:35,02:59:09 +41,M,01:21:08,02:59:09 +39,M,01:24:38,02:59:09 +43,M,01:27:46,02:59:10 +29,M,01:25:37,02:59:11 +30,W,01:28:03,02:59:11 +41,M,01:26:08,02:59:14 +33,M,01:24:48,02:59:15 +43,M,01:29:00,02:59:16 +48,M,01:24:56,02:59:16 +44,M,01:25:23,02:59:17 +46,M,01:28:28,02:59:17 +44,M,01:27:22,02:59:17 +39,M,01:27:30,02:59:19 +44,M,01:28:02,02:59:19 +30,W,01:29:26,02:59:19 +26,M,01:23:12,02:59:20 +38,M,01:24:25,02:59:20 +49,M,01:25:54,02:59:20 +29,W,01:25:21,02:59:21 +33,W,01:26:56,02:59:22 +38,M,01:27:40,02:59:22 +45,M,01:25:37,02:59:23 +29,M,01:27:34,02:59:23 +40,M,01:26:05,02:59:24 +44,M,01:27:52,02:59:25 +28,M,01:29:16,02:59:25 +38,M,01:24:34,02:59:27 +33,M,01:25:28,02:59:27 +46,M,01:28:40,02:59:27 +34,M,01:30:05,02:59:29 +35,M,01:28:40,02:59:30 +40,M,01:27:41,02:59:31 +24,M,01:24:08,02:59:32 +41,M,01:31:07,02:59:33 +35,M,01:24:44,02:59:33 +45,M,01:27:44,02:59:34 +53,M,01:26:19,02:59:34 +25,M,01:20:18,02:59:36 +28,M,01:26:39,02:59:36 +29,M,01:29:20,02:59:37 +27,M,01:24:54,02:59:37 +42,M,01:29:06,02:59:38 +46,M,01:23:00,02:59:39 +40,M,01:26:25,02:59:40 +35,M,01:28:31,02:59:40 +44,M,01:27:19,02:59:41 +32,M,01:27:05,02:59:41 +31,M,01:27:57,02:59:45 +45,M,01:26:27,02:59:45 +51,M,01:28:00,02:59:45 +39,M,01:23:55,02:59:48 +42,M,01:25:11,02:59:50 +37,M,01:24:24,02:59:50 +35,M,01:27:28,02:59:50 +32,M,01:31:20,02:59:50 +30,M,01:17:58,02:59:51 +42,M,01:27:21,02:59:52 +44,M,01:27:00,02:59:52 +43,M,01:29:31,02:59:53 +39,M,01:29:49,02:59:54 +43,M,01:29:37,02:59:55 +39,M,01:26:31,02:59:55 +50,M,01:26:12,02:59:56 +45,M,01:27:47,02:59:56 +46,M,01:25:23,02:59:56 +40,M,01:22:55,02:59:56 +53,M,01:26:56,02:59:57 +36,M,01:26:17,02:59:57 +46,M,01:23:47,02:59:59 +31,M,01:28:52,03:00:00 +30,M,01:28:37,03:00:01 +24,M,01:27:45,03:00:01 +35,M,01:28:00,03:00:01 +31,W,01:25:08,03:00:02 +45,M,01:22:48,03:00:02 +47,M,01:27:54,03:00:05 +34,M,01:28:51,03:00:07 +35,M,01:27:38,03:00:08 +30,M,01:32:44,03:00:09 +45,M,01:26:06,03:00:10 +41,M,01:27:33,03:00:10 +34,M,01:25:48,03:00:11 +35,M,01:25:27,03:00:11 +27,M,01:25:33,03:00:12 +30,M,01:30:13,03:00:14 +23,M,01:26:50,03:00:14 +38,M,01:28:07,03:00:15 +38,M,01:29:49,03:00:16 +40,M,01:26:42,03:00:16 +30,M,01:24:46,03:00:16 +54,M,01:25:58,03:00:18 +36,M,01:25:06,03:00:20 +20,M,01:29:09,03:00:20 +32,M,01:28:28,03:00:21 +38,M,01:22:08,03:00:22 +35,M,01:25:32,03:00:25 +27,M,01:28:52,03:00:25 +34,M,01:29:51,03:00:27 +39,M,01:20:39,03:00:28 +27,M,01:29:14,03:00:28 +42,M,01:27:49,03:00:28 +51,M,01:26:04,03:00:29 +41,M,01:26:04,03:00:29 +24,M,01:26:11,03:00:30 +60,M,01:25:30,03:00:31 +36,M,01:27:13,03:00:32 +42,M,01:25:29,03:00:34 +48,M,01:29:44,03:00:35 +48,M,01:26:34,03:00:35 +45,M,01:27:51,03:00:35 +36,M,01:29:11,03:00:36 +39,M,01:24:23,03:00:36 +28,M,01:27:44,03:00:37 +31,W,01:25:59,03:00:38 +39,M,01:25:15,03:00:39 +44,M,01:26:52,03:00:39 +50,M,01:28:56,03:00:41 +40,M,01:30:53,03:00:42 +41,M,01:30:19,03:00:42 +21,M,01:28:34,03:00:42 +33,M,01:27:21,03:00:43 +29,M,01:26:24,03:00:44 +33,M,01:28:21,03:00:45 +41,M,01:30:22,03:00:45 +39,M,01:27:24,03:00:47 +46,M,01:30:08,03:00:47 +32,M,01:29:42,03:00:49 +27,M,01:29:00,03:00:49 +41,M,01:28:57,03:00:49 +51,M,01:29:26,03:00:51 +44,M,01:27:47,03:00:51 +41,M,01:25:06,03:00:51 +25,M,01:20:29,03:00:51 +38,M,01:23:52,03:00:52 +30,M,01:27:10,03:00:53 +34,M,01:32:44,03:00:54 +32,M,01:29:18,03:00:55 +28,M,01:28:30,03:00:56 +37,M,01:29:37,03:00:56 +33,M,01:26:16,03:00:59 +39,M,01:28:36,03:01:00 +46,M,01:26:58,03:01:01 +30,M,01:26:49,03:01:03 +31,M,01:27:01,03:01:04 +32,M,01:28:35,03:01:04 +52,M,01:25:37,03:01:05 +55,M,01:27:30,03:01:05 +40,M,01:26:24,03:01:05 +46,M,01:26:25,03:01:06 +45,M,01:22:45,03:01:07 +48,M,01:27:00,03:01:07 +42,M,01:27:19,03:01:09 +26,M,01:25:25,03:01:10 +47,M,01:24:17,03:01:10 +51,M,01:29:05,03:01:11 +41,M,01:29:56,03:01:11 +31,M,01:26:58,03:01:12 +31,M,01:23:28,03:01:12 +27,W,01:29:05,03:01:15 +33,M,01:26:48,03:01:17 +32,M,01:30:18,03:01:18 +36,M,01:29:27,03:01:20 +38,M,01:30:29,03:01:20 +30,M,01:32:31,03:01:21 +33,M,01:28:17,03:01:23 +35,M,01:29:10,03:01:23 +31,M,01:27:52,03:01:25 +45,M,01:28:26,03:01:26 +36,M,01:29:48,03:01:29 +39,M,01:24:35,03:01:32 +41,M,01:27:53,03:01:33 +42,M,01:30:26,03:01:34 +29,M,01:30:03,03:01:39 +39,M,01:28:13,03:01:39 +25,M,01:30:35,03:01:44 +42,M,01:24:57,03:01:44 +39,M,01:27:25,03:01:44 +38,M,01:24:41,03:01:45 +48,M,01:27:49,03:01:46 +40,M,01:29:07,03:01:47 +40,M,01:26:47,03:01:47 +38,M,01:26:57,03:01:48 +40,M,01:28:20,03:01:49 +38,M,01:29:37,03:01:49 +30,M,01:28:46,03:01:50 +42,W,01:27:24,03:01:51 +35,M,01:28:38,03:01:52 +43,M,01:28:36,03:01:53 +32,M,01:25:27,03:01:54 +34,M,01:24:22,03:01:54 +23,M,01:20:10,03:01:55 +34,W,01:27:56,03:01:55 +40,M,01:31:09,03:01:56 +32,M,01:32:32,03:01:56 +30,M,01:26:01,03:01:57 +24,M,01:24:59,03:01:58 +47,M,01:25:09,03:01:59 +49,M,01:29:33,03:02:00 +39,M,01:25:40,03:02:01 +43,M,01:27:40,03:02:01 +40,M,01:31:01,03:02:02 +23,M,01:23:10,03:02:04 +29,M,01:29:19,03:02:05 +34,M,01:27:15,03:02:05 +49,M,01:25:19,03:02:06 +32,M,01:29:23,03:02:06 +45,M,01:28:46,03:02:06 +52,M,01:26:49,03:02:08 +33,M,01:30:19,03:02:09 +45,M,01:27:41,03:02:12 +31,M,01:26:05,03:02:12 +35,M,01:26:03,03:02:15 +47,M,01:28:26,03:02:16 +37,M,01:27:29,03:02:17 +43,M,01:27:34,03:02:18 +46,M,01:26:31,03:02:19 +45,M,01:24:50,03:02:20 +31,M,01:27:16,03:02:21 +46,M,01:29:00,03:02:25 +23,M,01:27:04,03:02:26 +34,M,01:27:05,03:02:26 +39,M,01:29:09,03:02:27 +41,M,01:29:52,03:02:29 +25,W,01:29:27,03:02:29 +39,M,01:31:04,03:02:29 +32,M,01:24:49,03:02:29 +52,M,01:24:16,03:02:31 +38,M,01:27:31,03:02:31 +39,M,01:27:26,03:02:31 +47,M,01:26:43,03:02:34 +31,M,01:27:31,03:02:35 +31,M,01:29:56,03:02:38 +35,M,01:27:51,03:02:39 +50,M,01:28:45,03:02:39 +40,M,01:24:48,03:02:41 +51,M,01:28:03,03:02:42 +40,M,01:29:16,03:02:42 +26,M,01:29:00,03:02:42 +30,M,01:21:30,03:02:43 +37,M,01:30:05,03:02:44 +30,M,01:24:56,03:02:44 +23,M,01:26:33,03:02:45 +41,M,01:27:55,03:02:46 +46,M,01:24:05,03:02:46 +33,M,01:23:26,03:02:47 +42,M,01:26:11,03:02:47 +22,M,01:35:19,03:02:49 +44,M,01:27:58,03:02:50 +36,M,01:25:43,03:02:51 +36,M,01:30:12,03:02:51 +22,W,01:29:20,03:02:52 +56,M,01:28:33,03:02:53 +51,M,01:27:53,03:02:53 +25,W,01:28:19,03:02:54 +45,M,01:27:47,03:02:54 +42,M,01:27:45,03:02:55 +48,M,01:31:26,03:02:58 +40,M,01:26:14,03:03:02 +41,M,01:32:00,03:03:04 +43,M,01:24:01,03:03:04 +29,M,01:25:17,03:03:06 +38,M,01:27:45,03:03:07 +39,M,01:12:11,03:03:08 +35,M,01:30:10,03:03:08 +35,M,01:30:38,03:03:08 +34,M,01:29:23,03:03:11 +41,M,01:28:15,03:03:12 +47,M,01:24:48,03:03:15 +35,M,01:28:50,03:03:15 +24,M,01:33:02,03:03:15 +42,M,01:30:46,03:03:15 +42,M,01:27:42,03:03:15 +42,M,01:26:47,03:03:19 +46,M,01:28:57,03:03:21 +41,M,01:28:59,03:03:21 +36,W,01:29:49,03:03:23 +44,M,01:25:42,03:03:23 +20,M,01:17:41,03:03:23 +41,M,01:27:00,03:03:24 +45,M,01:29:17,03:03:25 +33,M,01:32:22,03:03:27 +34,W,01:28:38,03:03:27 +45,M,01:27:26,03:03:27 +29,M,01:29:22,03:03:27 +42,M,01:29:19,03:03:27 +35,M,01:26:03,03:03:27 +51,M,01:28:24,03:03:27 +31,M,01:29:42,03:03:28 +47,M,01:30:49,03:03:29 +31,M,01:27:44,03:03:31 +44,M,01:27:36,03:03:31 +37,M,01:30:43,03:03:31 +37,M,01:27:26,03:03:32 +22,M,01:26:14,03:03:34 +54,M,01:30:37,03:03:36 +45,M,01:28:52,03:03:36 +34,M,01:29:36,03:03:38 +37,M,01:28:12,03:03:39 +37,M,01:24:10,03:03:40 +43,M,01:26:30,03:03:40 +40,M,01:29:57,03:03:40 +44,M,01:28:08,03:03:42 +43,M,01:30:17,03:03:44 +33,M,01:31:38,03:03:45 +42,M,01:30:11,03:03:46 +30,M,01:29:33,03:03:47 +37,M,01:25:30,03:03:47 +42,W,01:28:56,03:03:47 +46,M,01:28:31,03:03:51 +44,M,01:27:06,03:03:55 +43,M,01:28:55,03:03:56 +32,M,01:29:16,03:03:57 +40,M,01:30:36,03:03:57 +42,M,01:28:59,03:04:01 +40,M,01:29:03,03:04:02 +45,M,01:28:33,03:04:04 +39,M,01:28:18,03:04:04 +41,M,01:30:18,03:04:05 +37,M,01:31:09,03:04:06 +40,M,01:30:50,03:04:06 +42,M,01:31:13,03:04:07 +44,M,01:27:27,03:04:09 +25,M,01:26:25,03:04:11 +39,M,01:28:03,03:04:11 +30,M,01:30:46,03:04:12 +41,M,01:27:46,03:04:12 +39,M,01:25:24,03:04:13 +33,M,01:25:41,03:04:13 +24,W,01:32:43,03:04:14 +24,M,01:18:52,03:04:14 +43,M,01:29:44,03:04:14 +31,M,01:28:11,03:04:15 +41,M,01:21:43,03:04:18 +22,M,01:27:26,03:04:18 +31,M,01:29:37,03:04:19 +33,M,01:30:48,03:04:19 +46,M,01:24:17,03:04:20 +37,M,01:31:19,03:04:20 +41,M,01:29:22,03:04:21 +32,M,01:28:00,03:04:22 +35,M,01:30:15,03:04:23 +44,M,01:28:38,03:04:24 +39,M,01:25:12,03:04:25 +30,M,01:16:08,03:04:26 +26,M,01:26:47,03:04:29 +54,M,01:29:40,03:04:30 +50,W,01:30:37,03:04:31 +30,M,01:21:29,03:04:35 +40,M,01:25:12,03:04:35 +32,M,01:24:56,03:04:35 +45,M,01:28:24,03:04:36 +44,M,01:29:42,03:04:37 +34,M,01:30:03,03:04:38 +42,M,01:28:38,03:04:39 +44,M,01:24:58,03:04:40 +32,W,01:30:07,03:04:41 +35,M,01:24:14,03:04:41 +38,M,01:29:35,03:04:42 +33,W,01:31:16,03:04:42 +37,M,01:33:11,03:04:42 +36,M,01:32:02,03:04:42 +26,M,01:29:52,03:04:42 +43,M,01:27:24,03:04:45 +34,M,01:23:23,03:04:45 +37,M,01:28:59,03:04:46 +34,M,01:28:13,03:04:47 +36,M,01:33:32,03:04:49 +37,M,01:24:37,03:04:50 +45,M,01:27:00,03:04:50 +34,W,01:30:53,03:04:50 +48,M,01:28:34,03:04:53 +33,M,01:24:44,03:04:53 +40,M,01:27:42,03:04:53 +39,M,01:29:38,03:04:55 +34,M,01:30:01,03:04:56 +26,W,01:32:08,03:04:57 +47,M,01:28:21,03:04:58 +46,M,01:29:36,03:04:58 +44,M,01:32:24,03:04:59 +40,M,01:26:15,03:05:00 +48,M,01:29:56,03:05:00 +28,M,01:26:43,03:05:01 +34,M,01:30:29,03:05:01 +44,M,01:28:13,03:05:02 +41,M,01:28:44,03:05:04 +34,M,01:29:26,03:05:07 +46,M,01:31:18,03:05:07 +32,W,01:31:13,03:05:10 +39,M,01:26:54,03:05:12 +24,W,01:31:36,03:05:12 +46,M,01:28:08,03:05:13 +20,M,01:33:19,03:05:13 +29,M,01:30:07,03:05:14 +40,W,01:28:24,03:05:14 +41,M,01:28:12,03:05:17 +49,M,01:30:33,03:05:17 +42,M,01:26:52,03:05:18 +42,W,01:29:57,03:05:18 +47,M,01:25:46,03:05:19 +38,M,01:31:24,03:05:21 +40,M,01:27:30,03:05:22 +40,M,01:24:54,03:05:23 +25,M,01:29:18,03:05:24 +51,M,01:29:36,03:05:25 +46,M,01:30:15,03:05:26 +37,M,01:27:28,03:05:26 +23,M,01:22:44,03:05:26 +60,M,01:29:39,03:05:27 +48,M,01:26:42,03:05:27 +47,M,01:29:21,03:05:29 +27,M,01:26:35,03:05:30 +47,M,01:32:02,03:05:30 +40,M,01:30:07,03:05:31 +35,M,01:32:37,03:05:31 +49,M,01:29:19,03:05:32 +45,M,01:27:47,03:05:32 +40,M,01:26:58,03:05:32 +50,M,01:32:38,03:05:33 +27,M,01:30:17,03:05:33 +41,M,01:25:47,03:05:34 +42,M,01:27:38,03:05:34 +41,M,01:28:06,03:05:34 +50,M,01:32:09,03:05:35 +42,M,01:26:58,03:05:35 +36,M,01:30:47,03:05:37 +34,M,01:33:36,03:05:37 +47,M,01:29:47,03:05:38 +39,M,01:31:44,03:05:39 +47,M,01:26:36,03:05:41 +54,M,01:28:07,03:05:41 +49,M,01:30:41,03:05:42 +38,M,01:26:46,03:05:43 +31,M,01:28:36,03:05:43 +36,M,01:28:44,03:05:43 +46,W,01:29:25,03:05:43 +28,M,01:30:03,03:05:44 +35,M,01:29:23,03:05:45 +45,M,01:30:09,03:05:45 +52,M,01:23:34,03:05:46 +39,M,01:28:35,03:05:47 +48,M,01:30:22,03:05:47 +39,M,01:27:38,03:05:48 +43,M,01:31:41,03:05:49 +28,M,01:31:35,03:05:49 +36,M,01:29:59,03:05:52 +37,M,01:30:18,03:05:53 +34,M,01:27:47,03:05:53 +42,M,01:34:56,03:05:53 +24,M,01:26:12,03:05:54 +45,M,01:31:28,03:05:57 +47,M,01:26:21,03:05:58 +24,M,01:23:45,03:05:59 +37,M,01:27:22,03:05:59 +34,W,01:27:31,03:06:00 +43,M,01:32:00,03:06:00 +46,M,01:25:27,03:06:00 +38,M,01:32:17,03:06:02 +36,M,01:25:14,03:06:03 +43,M,01:28:09,03:06:05 +29,M,01:29:46,03:06:06 +47,M,01:21:35,03:06:07 +36,M,01:32:41,03:06:08 +26,M,01:29:49,03:06:09 +32,M,01:26:36,03:06:09 +51,M,01:29:21,03:06:10 +28,M,01:33:46,03:06:11 +44,M,01:29:41,03:06:11 +44,M,01:31:27,03:06:12 +42,M,01:31:37,03:06:12 +46,M,01:30:29,03:06:12 +32,W,01:28:53,03:06:13 +32,M,01:28:08,03:06:15 +42,W,01:30:18,03:06:16 +49,M,01:26:16,03:06:16 +28,W,01:30:36,03:06:16 +39,M,01:30:29,03:06:17 +47,M,01:33:00,03:06:17 +35,M,01:30:40,03:06:18 +35,M,01:20:22,03:06:18 +35,M,01:29:34,03:06:19 +47,M,01:22:40,03:06:20 +51,M,01:32:39,03:06:20 +40,M,01:28:51,03:06:21 +44,M,01:28:53,03:06:22 +42,M,01:27:15,03:06:23 +33,M,01:28:45,03:06:23 +45,M,01:29:14,03:06:24 +32,M,01:29:37,03:06:24 +34,M,01:29:53,03:06:24 +39,M,01:28:24,03:06:25 +57,M,01:29:02,03:06:25 +30,M,01:32:44,03:06:25 +46,W,01:28:16,03:06:27 +51,M,01:25:51,03:06:29 +41,M,01:26:30,03:06:29 +42,M,01:28:01,03:06:31 +36,M,01:31:50,03:06:32 +33,M,01:26:28,03:06:33 +35,M,01:29:03,03:06:33 +41,M,01:33:14,03:06:34 +29,M,01:23:51,03:06:34 +48,M,01:30:32,03:06:35 +56,M,01:29:43,03:06:36 +44,M,01:29:43,03:06:36 +32,W,01:26:43,03:06:37 +44,M,01:28:24,03:06:37 +38,M,01:25:03,03:06:38 +30,M,01:28:17,03:06:40 +47,M,01:31:50,03:06:41 +30,M,01:27:54,03:06:42 +35,M,01:28:10,03:06:43 +45,W,01:28:09,03:06:44 +37,M,01:32:43,03:06:45 +47,M,01:30:08,03:06:45 +36,M,01:30:57,03:06:47 +45,M,01:28:27,03:06:51 +44,M,01:28:11,03:06:52 +31,W,01:32:03,03:06:52 +37,M,01:31:40,03:06:53 +40,M,01:26:18,03:06:54 +37,M,01:28:16,03:06:54 +58,M,01:28:42,03:06:55 +38,M,01:26:04,03:06:57 +41,M,01:31:30,03:06:57 +28,M,01:33:40,03:06:57 +41,M,01:31:45,03:06:59 +32,M,01:32:08,03:07:01 +50,M,01:30:37,03:07:01 +54,M,01:29:52,03:07:02 +40,W,01:31:05,03:07:03 +35,M,01:27:03,03:07:04 +44,M,01:31:51,03:07:04 +47,M,01:28:33,03:07:05 +30,M,01:27:39,03:07:05 +43,M,01:27:17,03:07:07 +24,W,01:31:33,03:07:07 +22,W,01:31:33,03:07:07 +24,M,01:24:38,03:07:08 +28,M,01:33:16,03:07:09 +37,M,01:29:45,03:07:10 +35,M,01:33:06,03:07:10 +35,M,01:29:59,03:07:11 +31,M,01:28:59,03:07:11 +51,M,01:27:50,03:07:11 +48,M,01:30:46,03:07:13 +46,M,01:28:59,03:07:14 +24,W,01:32:15,03:07:14 +30,M,01:27:13,03:07:16 +27,M,01:28:30,03:07:17 +33,M,01:30:49,03:07:17 +46,M,01:32:07,03:07:18 +37,M,01:32:27,03:07:19 +43,M,01:26:58,03:07:19 +40,M,01:29:35,03:07:19 +39,M,01:27:56,03:07:21 +32,M,01:30:27,03:07:22 +22,M,01:28:12,03:07:25 +53,M,01:32:41,03:07:25 +38,M,01:26:32,03:07:26 +47,M,01:29:24,03:07:28 +41,M,01:31:16,03:07:30 +24,W,01:27:44,03:07:31 +30,M,01:30:54,03:07:31 +40,M,01:30:40,03:07:31 +48,M,01:28:48,03:07:32 +42,M,01:31:21,03:07:33 +41,M,01:28:53,03:07:34 +45,M,01:30:03,03:07:35 +39,M,01:32:08,03:07:35 +31,M,01:30:56,03:07:35 +34,M,01:30:18,03:07:36 +37,M,01:25:28,03:07:36 +45,M,01:29:15,03:07:36 +27,M,01:29:49,03:07:37 +41,M,01:30:50,03:07:37 +46,M,01:28:50,03:07:38 +37,M,01:26:20,03:07:39 +49,M,01:30:31,03:07:39 +36,W,01:31:12,03:07:39 +47,M,01:32:52,03:07:40 +37,M,01:26:09,03:07:41 +35,M,01:28:22,03:07:43 +31,M,01:31:29,03:07:43 +38,M,01:32:44,03:07:44 +30,M,01:30:09,03:07:44 +47,M,01:32:13,03:07:44 +47,M,01:27:46,03:07:45 +25,M,01:32:25,03:07:46 +53,M,01:28:18,03:07:49 +27,M,01:29:27,03:07:51 +31,M,01:28:52,03:07:51 +29,M,01:31:00,03:07:51 +54,M,01:28:14,03:07:51 +30,M,01:25:30,03:07:51 +36,M,01:25:30,03:07:52 +29,M,01:25:10,03:07:52 +30,M,01:33:58,03:07:55 +52,M,01:30:39,03:07:56 +44,M,01:30:40,03:07:56 +43,M,01:30:16,03:07:56 +46,M,01:29:08,03:07:57 +41,M,01:27:46,03:07:57 +36,M,01:22:15,03:07:58 +39,M,01:29:53,03:08:00 +29,W,01:30:19,03:08:00 +48,M,01:32:08,03:08:00 +48,M,01:32:24,03:08:01 +26,M,01:33:22,03:08:01 +46,M,01:20:45,03:08:01 +24,M,01:18:40,03:08:03 +47,M,01:30:17,03:08:04 +34,W,01:28:53,03:08:05 +45,M,01:30:28,03:08:06 +32,W,01:32:13,03:08:06 +53,M,01:28:44,03:08:06 +54,M,01:30:38,03:08:06 +46,M,01:24:09,03:08:07 +20,M,01:26:21,03:08:08 +23,M,01:31:41,03:08:08 +46,M,01:29:50,03:08:08 +38,M,01:29:32,03:08:08 +59,M,01:30:45,03:08:11 +50,M,01:31:11,03:08:12 +30,M,01:31:22,03:08:12 +26,W,01:32:07,03:08:12 +38,M,01:37:59,03:08:13 +61,M,01:30:36,03:08:14 +25,M,01:27:09,03:08:14 +45,M,01:30:14,03:08:14 +26,M,01:29:30,03:08:14 +37,M,01:30:47,03:08:16 +46,M,01:28:40,03:08:16 +51,M,01:27:57,03:08:16 +41,M,01:30:45,03:08:17 +43,M,01:30:36,03:08:17 +42,M,01:30:09,03:08:18 +47,M,01:25:40,03:08:18 +51,M,01:28:29,03:08:18 +49,M,01:27:41,03:08:18 +26,W,01:30:18,03:08:19 +37,W,01:33:43,03:08:20 +48,M,01:33:07,03:08:24 +44,M,01:33:31,03:08:26 +36,M,01:30:07,03:08:26 +38,M,01:32:17,03:08:26 +38,M,01:26:43,03:08:27 +32,M,01:18:40,03:08:27 +42,M,01:31:13,03:08:28 +31,M,01:29:05,03:08:28 +27,M,01:22:48,03:08:29 +50,M,01:28:04,03:08:30 +29,M,01:33:25,03:08:31 +34,M,01:32:51,03:08:32 +34,M,01:33:26,03:08:32 +37,M,01:28:16,03:08:32 +37,M,01:34:10,03:08:33 +38,W,01:31:10,03:08:33 +48,M,01:30:24,03:08:33 +22,M,01:29:07,03:08:33 +56,M,01:32:38,03:08:34 +39,M,01:27:28,03:08:34 +31,M,01:29:06,03:08:36 +40,W,01:33:16,03:08:37 +45,M,01:28:26,03:08:38 +47,M,01:31:06,03:08:38 +47,M,01:27:00,03:08:43 +39,M,01:30:00,03:08:43 +39,M,01:33:16,03:08:43 +34,M,01:31:20,03:08:45 +31,M,01:23:08,03:08:46 +52,M,01:33:42,03:08:46 +36,M,01:31:01,03:08:47 +40,M,01:28:07,03:08:47 +40,M,01:29:42,03:08:47 +41,M,01:31:27,03:08:49 +32,M,01:32:11,03:08:50 +46,M,01:32:24,03:08:51 +24,M,01:28:00,03:08:51 +39,M,01:29:05,03:08:53 +41,M,01:28:52,03:08:53 +40,W,01:29:39,03:08:54 +29,M,01:30:40,03:08:54 +33,M,01:31:03,03:08:55 +46,M,01:30:50,03:08:56 +34,M,01:33:00,03:08:57 +45,M,01:31:19,03:08:57 +37,M,01:31:30,03:08:58 +28,M,01:32:18,03:08:59 +49,M,01:30:15,03:09:00 +24,M,01:33:10,03:09:02 +28,W,01:29:29,03:09:02 +43,M,01:29:05,03:09:02 +37,M,01:26:48,03:09:02 +27,W,01:33:43,03:09:03 +32,M,01:29:25,03:09:03 +42,M,01:36:00,03:09:04 +32,M,01:27:09,03:09:06 +39,M,01:30:40,03:09:06 +39,M,01:30:35,03:09:08 +38,M,01:33:16,03:09:09 +25,M,01:30:59,03:09:09 +56,M,01:27:02,03:09:09 +26,W,01:32:25,03:09:13 +29,M,01:26:49,03:09:14 +46,M,01:31:25,03:09:14 +28,M,01:32:26,03:09:15 +32,M,01:25:01,03:09:15 +35,W,01:30:13,03:09:16 +34,M,01:32:45,03:09:17 +46,M,01:33:33,03:09:17 +53,M,01:27:33,03:09:17 +30,M,01:30:41,03:09:18 +51,M,01:33:00,03:09:19 +30,M,01:35:17,03:09:19 +45,M,01:32:52,03:09:20 +51,M,01:25:27,03:09:21 +23,W,01:33:06,03:09:23 +39,M,01:30:39,03:09:23 +28,M,01:27:17,03:09:24 +36,M,01:28:54,03:09:26 +46,M,01:23:04,03:09:28 +29,M,01:23:21,03:09:28 +27,M,01:34:23,03:09:29 +39,M,01:31:28,03:09:30 +44,M,01:30:34,03:09:30 +42,M,01:28:12,03:09:32 +48,M,01:26:31,03:09:33 +43,M,01:32:38,03:09:33 +20,M,01:31:26,03:09:34 +43,M,01:32:51,03:09:37 +45,M,01:25:48,03:09:38 +44,M,01:32:21,03:09:39 +50,M,01:32:54,03:09:39 +28,W,01:32:20,03:09:42 +45,M,01:31:07,03:09:42 +34,M,01:31:47,03:09:42 +29,M,01:27:38,03:09:43 +45,M,01:36:34,03:09:43 +44,M,01:29:39,03:09:43 +35,M,01:24:54,03:09:44 +42,W,01:32:07,03:09:44 +43,M,01:32:58,03:09:44 +25,W,01:30:42,03:09:45 +45,M,01:31:16,03:09:45 +32,M,01:32:10,03:09:45 +29,M,01:26:10,03:09:45 +49,M,01:31:45,03:09:47 +44,M,01:34:27,03:09:47 +36,M,01:29:43,03:09:47 +44,M,01:25:36,03:09:48 +51,M,01:30:52,03:09:48 +44,M,01:30:24,03:09:49 +33,M,01:32:49,03:09:49 +33,W,01:32:08,03:09:51 +24,M,01:33:13,03:09:51 +34,M,01:32:37,03:09:52 +43,M,01:28:13,03:09:52 +46,M,01:32:42,03:09:53 +34,M,01:29:38,03:09:53 +35,M,01:32:53,03:09:55 +26,M,01:33:13,03:09:55 +31,M,01:33:18,03:09:55 +58,M,01:30:30,03:09:56 +45,M,01:27:30,03:09:58 +41,M,01:33:43,03:10:00 +43,M,01:31:20,03:10:00 +26,M,01:26:43,03:10:02 +45,M,01:29:08,03:10:04 +40,M,01:32:45,03:10:04 +33,M,01:25:24,03:10:05 +44,M,01:33:47,03:10:05 +33,M,01:32:56,03:10:06 +52,M,01:31:23,03:10:06 +30,M,01:30:41,03:10:06 +29,M,01:31:40,03:10:06 +35,M,01:30:52,03:10:06 +44,M,01:28:45,03:10:08 +45,M,01:34:16,03:10:09 +42,M,01:26:34,03:10:10 +37,M,01:32:53,03:10:10 +40,M,01:33:19,03:10:11 +42,M,01:30:54,03:10:12 +37,M,01:31:31,03:10:12 +30,M,01:34:34,03:10:12 +23,M,01:31:51,03:10:13 +38,M,01:29:42,03:10:13 +45,M,01:29:48,03:10:14 +29,M,01:32:46,03:10:14 +37,M,01:31:56,03:10:15 +40,M,01:29:28,03:10:15 +21,M,01:29:42,03:10:16 +31,M,01:32:41,03:10:18 +44,M,01:21:24,03:10:18 +29,M,01:28:39,03:10:19 +42,M,01:30:05,03:10:19 +39,M,01:30:34,03:10:21 +36,M,01:22:12,03:10:21 +36,M,01:25:43,03:10:22 +39,M,01:28:12,03:10:23 +28,M,01:32:11,03:10:25 +39,M,01:35:00,03:10:27 +45,M,01:29:45,03:10:27 +32,M,01:33:06,03:10:28 +44,W,01:33:35,03:10:30 +33,M,01:24:33,03:10:32 +40,M,01:26:28,03:10:33 +50,M,01:34:23,03:10:34 +24,M,01:33:56,03:10:34 +43,M,01:29:54,03:10:34 +34,M,01:26:42,03:10:34 +43,M,01:33:03,03:10:35 +43,W,01:32:21,03:10:36 +34,M,01:24:36,03:10:36 +28,W,01:31:35,03:10:37 +38,W,01:33:54,03:10:37 +49,M,01:33:34,03:10:38 +36,M,01:28:46,03:10:38 +40,W,01:32:49,03:10:40 +26,M,01:30:51,03:10:40 +42,M,01:31:07,03:10:41 +46,M,01:31:20,03:10:41 +40,M,01:29:21,03:10:42 +45,M,01:33:50,03:10:42 +38,M,01:31:05,03:10:44 +33,M,01:31:11,03:10:45 +31,M,01:32:06,03:10:46 +31,M,01:33:59,03:10:47 +52,M,01:34:12,03:10:48 +30,M,01:34:51,03:10:49 +28,M,01:30:51,03:10:49 +44,M,01:26:48,03:10:50 +33,M,01:29:34,03:10:50 +28,M,01:24:17,03:10:50 +36,M,01:32:43,03:10:50 +47,M,01:33:43,03:10:51 +34,W,01:33:06,03:10:52 +43,M,01:29:48,03:10:52 +40,M,01:31:47,03:10:52 +35,M,01:31:28,03:10:55 +29,M,01:28:54,03:10:56 +38,M,01:26:27,03:10:56 +25,M,01:26:55,03:10:56 +35,M,01:28:08,03:10:56 +46,M,01:32:38,03:10:57 +38,W,01:33:54,03:10:58 +32,M,01:27:59,03:10:59 +32,W,01:32:33,03:11:00 +31,W,01:27:45,03:11:01 +52,M,01:34:06,03:11:02 +35,M,01:32:45,03:11:03 +43,M,01:29:51,03:11:04 +31,M,01:29:09,03:11:04 +41,M,01:34:00,03:11:04 +28,M,01:34:23,03:11:05 +40,M,01:31:10,03:11:05 +39,M,01:28:40,03:11:07 +42,M,01:30:09,03:11:09 +24,M,01:28:26,03:11:10 +34,M,01:32:58,03:11:10 +47,M,01:29:35,03:11:10 +26,M,01:30:33,03:11:11 +26,W,01:32:17,03:11:11 +57,M,01:29:58,03:11:13 +57,M,01:32:17,03:11:13 +38,W,01:32:51,03:11:14 +38,M,01:33:42,03:11:15 +36,M,01:29:51,03:11:15 +43,M,01:33:09,03:11:15 +36,M,01:35:01,03:11:15 +37,M,01:33:42,03:11:16 +36,M,01:30:29,03:11:17 +31,M,01:32:44,03:11:17 +35,M,01:34:04,03:11:19 +51,M,01:28:50,03:11:21 +34,M,01:27:17,03:11:21 +51,M,01:33:09,03:11:22 +46,M,01:31:49,03:11:23 +36,M,01:34:45,03:11:23 +55,M,01:31:02,03:11:23 +44,M,01:33:11,03:11:23 +45,M,01:26:53,03:11:26 +47,M,01:31:13,03:11:26 +39,M,01:29:41,03:11:27 +38,W,01:32:44,03:11:27 +42,M,01:27:14,03:11:28 +55,M,01:32:37,03:11:29 +40,M,01:29:54,03:11:31 +39,M,01:25:19,03:11:32 +27,M,01:24:34,03:11:32 +33,M,01:27:19,03:11:32 +46,M,01:35:27,03:11:33 +35,M,01:32:08,03:11:33 +26,W,01:35:27,03:11:33 +48,M,01:33:15,03:11:33 +30,M,01:33:18,03:11:33 +39,M,01:33:15,03:11:33 +34,M,01:28:48,03:11:33 +36,M,01:26:54,03:11:35 +48,M,01:30:31,03:11:36 +33,W,01:31:51,03:11:36 +44,M,01:25:18,03:11:37 +41,W,01:34:54,03:11:38 +32,W,01:31:06,03:11:39 +46,M,01:32:57,03:11:40 +25,M,01:29:07,03:11:40 +29,M,01:28:20,03:11:42 +47,M,01:30:55,03:11:44 +49,M,01:27:47,03:11:44 +45,M,01:28:47,03:11:45 +41,M,01:33:20,03:11:45 +42,M,01:25:32,03:11:45 +40,M,01:32:16,03:11:46 +45,M,01:31:32,03:11:47 +33,M,01:29:50,03:11:47 +33,M,01:31:11,03:11:47 +44,W,01:32:43,03:11:48 +39,M,01:32:23,03:11:49 +41,M,01:33:10,03:11:55 +41,M,01:26:47,03:11:58 +31,M,01:32:34,03:11:59 +39,M,01:34:31,03:11:59 +27,M,01:31:01,03:11:59 +34,M,01:28:06,03:12:00 +34,M,01:33:23,03:12:02 +44,W,01:31:48,03:12:02 +47,M,01:25:57,03:12:05 +53,M,01:29:31,03:12:07 +40,M,01:29:38,03:12:08 +39,M,01:33:26,03:12:08 +23,M,01:31:35,03:12:10 +48,M,01:32:16,03:12:10 +49,M,01:29:13,03:12:11 +32,M,01:31:13,03:12:11 +36,M,01:30:29,03:12:12 +43,M,01:33:36,03:12:12 +45,M,01:31:35,03:12:13 +24,W,01:30:00,03:12:13 +36,M,01:29:23,03:12:15 +39,M,01:30:46,03:12:15 +28,M,01:27:33,03:12:16 +38,M,01:27:51,03:12:17 +42,M,01:33:04,03:12:17 +53,M,01:27:29,03:12:18 +22,W,01:29:16,03:12:18 +42,M,01:30:52,03:12:19 +43,M,01:32:55,03:12:20 +51,M,01:33:58,03:12:21 +53,M,01:33:37,03:12:21 +42,M,01:31:21,03:12:22 +29,M,01:27:48,03:12:22 +31,M,01:34:44,03:12:23 +53,M,01:31:15,03:12:23 +29,M,01:34:11,03:12:24 +29,W,01:32:43,03:12:25 +36,M,01:33:41,03:12:25 +44,M,01:33:38,03:12:25 +41,M,01:32:03,03:12:26 +38,M,01:31:23,03:12:27 +33,M,01:34:55,03:12:28 +34,M,01:32:29,03:12:28 +41,M,01:30:05,03:12:29 +48,M,01:33:00,03:12:33 +30,M,01:34:22,03:12:33 +34,W,01:33:12,03:12:33 +50,M,01:31:14,03:12:35 +46,M,01:31:19,03:12:35 +34,M,01:30:30,03:12:36 +39,W,01:35:31,03:12:36 +39,M,01:25:16,03:12:36 +48,W,01:29:30,03:12:37 +42,M,01:29:34,03:12:37 +49,M,01:32:48,03:12:37 +53,M,01:38:00,03:12:37 +44,M,01:31:05,03:12:40 +43,M,01:32:37,03:12:41 +30,M,01:22:27,03:12:42 +41,M,01:26:25,03:12:42 +44,M,01:30:53,03:12:42 +26,M,01:26:17,03:12:42 +29,M,01:35:15,03:12:42 +41,M,01:28:02,03:12:43 +33,W,01:31:38,03:12:43 +50,M,01:31:10,03:12:45 +48,M,01:33:44,03:12:45 +40,M,01:30:19,03:12:46 +30,M,01:34:52,03:12:46 +40,M,01:35:09,03:12:47 +38,M,01:32:08,03:12:47 +39,W,01:35:13,03:12:47 +34,M,01:27:00,03:12:48 +40,M,01:27:39,03:12:50 +38,M,01:32:52,03:12:51 +28,W,01:32:39,03:12:51 +38,M,01:28:34,03:12:52 +45,M,01:32:26,03:12:52 +40,M,01:34:12,03:12:52 +48,M,01:29:29,03:12:53 +28,M,01:33:04,03:12:53 +30,W,01:32:26,03:12:56 +38,M,01:29:46,03:12:58 +29,M,01:31:10,03:12:58 +49,M,01:36:29,03:12:58 +32,M,01:33:17,03:12:59 +30,M,01:32:40,03:12:59 +35,M,01:32:41,03:13:00 +42,W,01:39:41,03:13:01 +31,M,01:38:55,03:13:02 +48,M,01:35:15,03:13:02 +43,M,01:33:35,03:13:04 +46,M,01:32:11,03:13:05 +54,M,01:33:33,03:13:05 +40,M,01:32:57,03:13:05 +24,M,01:31:11,03:13:06 +49,M,01:32:07,03:13:07 +48,M,01:30:28,03:13:07 +48,M,01:32:00,03:13:08 +39,M,01:31:46,03:13:08 +37,M,01:29:43,03:13:09 +46,M,01:35:05,03:13:10 +46,M,01:27:25,03:13:11 +32,W,01:35:19,03:13:11 +43,M,01:30:24,03:13:11 +23,M,01:32:00,03:13:12 +31,M,01:30:40,03:13:12 +27,W,01:32:03,03:13:13 +33,M,01:25:24,03:13:13 +46,M,01:26:57,03:13:13 +48,W,01:34:47,03:13:13 +49,M,01:33:56,03:13:13 +38,M,01:25:37,03:13:15 +32,M,01:32:47,03:13:15 +27,W,01:30:03,03:13:15 +27,M,01:28:30,03:13:16 +43,M,01:31:14,03:13:17 +38,M,01:32:43,03:13:17 +45,M,01:34:24,03:13:19 +35,W,01:33:18,03:13:19 +41,M,01:32:23,03:13:20 +49,M,01:31:34,03:13:20 +49,M,01:35:33,03:13:21 +30,M,01:30:25,03:13:21 +41,M,01:27:04,03:13:22 +45,M,01:33:23,03:13:23 +36,M,01:28:35,03:13:25 +43,M,01:32:56,03:13:25 +53,M,01:32:31,03:13:26 +48,M,01:33:02,03:13:26 +30,M,01:29:06,03:13:27 +39,M,01:33:12,03:13:27 +39,M,01:30:31,03:13:27 +46,M,01:31:59,03:13:27 +40,M,01:37:47,03:13:28 +38,W,01:28:16,03:13:30 +39,W,01:35:15,03:13:30 +38,M,01:30:14,03:13:32 +49,M,01:32:30,03:13:33 +40,M,01:30:35,03:13:34 +24,M,01:35:43,03:13:34 +49,M,01:30:54,03:13:34 +42,W,01:33:18,03:13:35 +35,M,01:39:25,03:13:36 +28,M,01:26:10,03:13:40 +36,M,01:32:01,03:13:40 +38,M,01:34:05,03:13:40 +27,M,01:28:03,03:13:40 +30,M,01:31:50,03:13:41 +36,W,01:27:12,03:13:43 +42,M,01:32:34,03:13:43 +41,M,01:28:51,03:13:44 +45,M,01:30:02,03:13:44 +45,M,01:27:40,03:13:44 +35,M,01:32:34,03:13:44 +25,W,01:34:19,03:13:45 +42,M,01:30:11,03:13:45 +58,M,01:36:20,03:13:46 +39,M,01:33:08,03:13:46 +47,M,01:36:57,03:13:47 +38,M,01:29:30,03:13:47 +34,M,01:30:26,03:13:47 +34,M,01:30:46,03:13:49 +48,W,01:33:35,03:13:50 +43,M,01:29:01,03:13:51 +44,M,01:34:30,03:13:51 +38,M,01:36:21,03:13:52 +47,M,01:35:42,03:13:53 +43,M,01:35:31,03:13:53 +50,M,01:33:39,03:13:55 +39,M,01:31:43,03:13:57 +36,M,01:38:36,03:13:58 +40,M,01:33:58,03:13:58 +45,M,01:29:40,03:13:59 +45,M,01:33:00,03:13:59 +41,M,01:33:25,03:13:59 +35,M,01:29:36,03:14:00 +38,M,01:27:50,03:14:00 +44,M,01:35:24,03:14:00 +39,M,01:31:14,03:14:00 +52,M,01:31:30,03:14:00 +29,M,01:35:00,03:14:03 +42,M,01:33:03,03:14:03 +37,M,01:31:18,03:14:03 +52,M,01:29:25,03:14:05 +50,M,01:32:03,03:14:05 +40,M,01:36:33,03:14:05 +39,M,01:38:32,03:14:07 +51,M,01:30:26,03:14:07 +41,M,01:35:16,03:14:07 +42,W,01:34:53,03:14:08 +36,M,01:31:58,03:14:09 +35,W,01:35:28,03:14:09 +35,M,01:32:19,03:14:09 +37,M,01:28:51,03:14:09 +38,M,01:21:14,03:14:09 +55,M,01:33:06,03:14:09 +38,M,01:29:17,03:14:10 +42,M,01:31:38,03:14:10 +47,M,01:31:29,03:14:10 +58,M,01:34:15,03:14:11 +41,W,01:35:46,03:14:11 +43,W,01:29:46,03:14:11 +33,M,01:33:48,03:14:13 +32,M,01:20:57,03:14:13 +54,M,01:30:24,03:14:15 +55,M,01:28:24,03:14:15 +29,M,01:38:09,03:14:15 +38,M,01:35:11,03:14:16 +54,M,01:32:07,03:14:16 +46,M,01:34:30,03:14:16 +46,M,01:30:30,03:14:17 +31,M,01:31:31,03:14:18 +42,M,01:34:17,03:14:18 +40,M,01:30:57,03:14:18 +48,M,01:35:09,03:14:19 +37,M,01:33:42,03:14:19 +39,M,01:35:08,03:14:20 +49,M,01:33:48,03:14:20 +45,M,01:34:19,03:14:21 +50,M,01:32:29,03:14:21 +39,M,01:31:35,03:14:21 +55,W,01:31:51,03:14:22 +45,M,01:36:55,03:14:22 +33,M,01:35:00,03:14:23 +50,M,01:35:55,03:14:23 +33,W,01:26:29,03:14:24 +36,M,01:36:00,03:14:24 +50,M,01:34:14,03:14:24 +52,M,01:34:19,03:14:26 +59,M,01:26:50,03:14:27 +46,M,01:31:33,03:14:27 +32,M,01:29:24,03:14:28 +44,M,01:34:12,03:14:28 +29,M,01:33:19,03:14:30 +47,M,01:31:39,03:14:30 +25,M,01:30:41,03:14:31 +42,M,01:31:54,03:14:31 +28,M,01:30:54,03:14:33 +45,M,01:35:33,03:14:33 +28,M,01:31:53,03:14:33 +38,M,01:28:26,03:14:33 +45,M,01:25:40,03:14:33 +42,W,01:33:49,03:14:33 +46,M,01:32:06,03:14:34 +45,M,01:33:48,03:14:34 +46,M,01:34:41,03:14:35 +31,M,01:30:01,03:14:36 +45,M,01:32:02,03:14:37 +47,M,01:36:06,03:14:38 +38,M,01:32:44,03:14:38 +25,M,01:24:14,03:14:39 +35,W,01:31:41,03:14:40 +55,M,01:29:54,03:14:40 +44,M,01:31:54,03:14:41 +24,M,01:25:48,03:14:41 +33,M,01:28:21,03:14:41 +48,M,01:32:35,03:14:41 +38,M,01:35:05,03:14:42 +36,W,01:36:22,03:14:42 +38,M,01:30:46,03:14:42 +48,M,01:29:23,03:14:43 +52,M,01:35:02,03:14:43 +43,M,01:29:07,03:14:44 +41,M,01:36:10,03:14:45 +48,M,01:34:14,03:14:46 +39,W,01:36:10,03:14:46 +38,M,01:32:46,03:14:47 +42,M,01:31:48,03:14:47 +35,M,01:27:59,03:14:48 +33,W,01:35:01,03:14:49 +46,M,01:34:35,03:14:50 +23,M,01:35:12,03:14:50 +35,M,01:33:03,03:14:50 +28,M,01:34:16,03:14:50 +31,M,01:34:39,03:14:52 +40,M,01:33:53,03:14:53 +35,M,01:34:14,03:14:54 +44,M,01:32:33,03:14:54 +51,M,01:34:07,03:14:55 +37,M,01:29:03,03:14:55 +48,M,01:36:24,03:14:55 +48,M,01:35:55,03:14:57 +44,M,01:29:24,03:15:00 +38,M,01:36:02,03:15:00 +48,M,01:33:10,03:15:01 +24,W,01:33:28,03:15:01 +42,W,01:32:34,03:15:02 +41,M,01:35:43,03:15:03 +32,M,01:38:23,03:15:03 +37,M,01:30:05,03:15:03 +30,M,01:32:06,03:15:03 +35,M,01:33:09,03:15:03 +24,M,01:27:06,03:15:04 +40,M,01:28:18,03:15:05 +45,M,01:31:33,03:15:07 +57,M,01:34:30,03:15:08 +31,M,01:34:21,03:15:10 +44,M,01:31:50,03:15:10 +42,M,01:34:52,03:15:11 +37,W,01:32:20,03:15:11 +31,W,01:30:49,03:15:12 +29,W,01:35:36,03:15:12 +39,M,01:35:50,03:15:14 +41,M,01:32:21,03:15:16 +39,W,01:36:15,03:15:16 +32,M,01:28:40,03:15:16 +40,W,01:32:37,03:15:17 +52,M,01:33:26,03:15:18 +38,M,01:37:06,03:15:18 +34,M,01:24:40,03:15:19 +45,M,01:34:49,03:15:19 +42,M,01:34:45,03:15:19 +42,W,01:32:57,03:15:19 +19,M,01:37:12,03:15:20 +50,M,01:34:38,03:15:21 +30,W,01:34:27,03:15:21 +39,M,01:24:55,03:15:22 +41,M,01:29:25,03:15:22 +19,M,01:32:48,03:15:23 +38,M,01:32:20,03:15:24 +46,M,01:28:59,03:15:24 +45,M,01:36:32,03:15:24 +51,M,01:38:54,03:15:25 +41,W,01:34:56,03:15:25 +44,M,01:36:55,03:15:25 +43,M,01:35:38,03:15:26 +36,M,01:33:30,03:15:26 +26,M,01:21:42,03:15:27 +45,M,01:31:42,03:15:28 +29,M,01:36:26,03:15:29 +33,M,01:36:40,03:15:30 +39,M,01:34:47,03:15:30 +41,M,01:35:50,03:15:30 +33,M,01:33:50,03:15:30 +49,M,01:22:12,03:15:33 +45,M,01:37:45,03:15:34 +58,M,01:34:55,03:15:34 +25,W,01:37:18,03:15:34 +29,M,01:37:56,03:15:35 +48,M,01:33:41,03:15:35 +46,M,01:34:54,03:15:36 +36,M,01:28:42,03:15:36 +28,M,01:34:59,03:15:37 +28,W,01:33:38,03:15:38 +32,M,01:32:07,03:15:38 +23,M,01:23:57,03:15:39 +43,W,01:35:47,03:15:39 +43,M,01:29:35,03:15:39 +47,M,01:35:57,03:15:40 +49,M,01:33:07,03:15:40 +28,W,01:33:59,03:15:41 +33,W,01:37:44,03:15:41 +45,M,01:34:00,03:15:41 +44,M,01:35:57,03:15:42 +42,M,01:33:31,03:15:43 +27,W,01:37:30,03:15:44 +34,M,01:17:45,03:15:45 +39,M,01:32:20,03:15:46 +33,M,01:38:16,03:15:46 +37,M,01:29:43,03:15:47 +33,M,01:36:53,03:15:48 +30,W,01:32:36,03:15:48 +43,M,01:36:45,03:15:50 +44,M,01:35:37,03:15:50 +39,M,01:36:49,03:15:51 +34,M,01:31:43,03:15:51 +51,M,01:33:34,03:15:51 +40,M,01:30:22,03:15:51 +39,M,01:36:48,03:15:52 +28,M,01:31:46,03:15:52 +39,M,01:36:08,03:15:52 +36,M,01:32:14,03:15:52 +43,M,01:37:20,03:15:53 +35,M,01:28:36,03:15:54 +43,M,01:34:51,03:15:54 +44,W,01:30:37,03:15:54 +43,W,01:35:43,03:15:55 +33,W,01:32:06,03:15:55 +33,M,01:35:23,03:15:55 +41,M,01:28:47,03:15:55 +29,M,01:36:53,03:15:56 +49,M,01:32:36,03:15:56 +30,M,01:32:53,03:15:58 +43,M,01:33:30,03:15:58 +33,M,01:34:34,03:16:00 +41,M,01:34:00,03:16:00 +38,M,01:30:49,03:16:01 +51,M,01:33:37,03:16:01 +40,M,01:29:38,03:16:02 +37,M,01:29:01,03:16:03 +31,W,01:34:46,03:16:03 +35,M,01:35:09,03:16:04 +23,M,01:33:39,03:16:04 +39,M,01:36:02,03:16:05 +46,M,01:29:22,03:16:05 +44,M,01:36:26,03:16:05 +46,M,01:32:57,03:16:05 +45,M,01:37:22,03:16:07 +27,M,01:25:22,03:16:08 +49,M,01:36:20,03:16:09 +27,M,01:36:09,03:16:10 +57,M,01:37:14,03:16:10 +36,M,01:35:26,03:16:10 +29,W,01:37:42,03:16:10 +45,W,01:33:50,03:16:10 +36,M,01:35:11,03:16:10 +34,M,01:30:58,03:16:11 +44,M,01:29:31,03:16:12 +26,M,01:32:54,03:16:12 +41,M,01:35:30,03:16:14 +43,M,01:27:59,03:16:14 +31,M,01:30:55,03:16:14 +48,M,01:32:16,03:16:14 +37,M,01:33:33,03:16:14 +23,M,01:34:57,03:16:15 +32,M,01:29:06,03:16:16 +53,M,01:32:50,03:16:16 +37,W,01:32:20,03:16:16 +37,M,01:35:24,03:16:16 +44,M,01:35:02,03:16:18 +39,M,01:30:34,03:16:18 +30,M,01:29:20,03:16:19 +42,W,01:31:35,03:16:19 +36,M,01:35:25,03:16:19 +45,M,01:33:45,03:16:19 +33,M,01:32:33,03:16:21 +38,M,01:34:00,03:16:21 +51,M,01:31:19,03:16:21 +41,M,01:36:26,03:16:21 +28,M,01:31:43,03:16:22 +47,M,01:31:44,03:16:24 +52,M,01:28:45,03:16:24 +38,M,01:35:47,03:16:24 +29,M,01:36:43,03:16:25 +44,M,01:33:05,03:16:25 +45,M,01:38:13,03:16:26 +52,M,01:29:27,03:16:28 +33,W,01:36:50,03:16:28 +46,M,01:33:41,03:16:29 +34,M,01:32:17,03:16:30 +53,M,01:30:57,03:16:31 +43,M,01:36:06,03:16:31 +48,M,01:34:37,03:16:32 +31,M,01:33:35,03:16:33 +37,M,01:31:23,03:16:33 +39,M,01:33:02,03:16:33 +32,W,01:36:11,03:16:34 +32,M,01:33:39,03:16:34 +27,M,01:33:14,03:16:35 +41,W,01:36:16,03:16:35 +38,M,01:37:11,03:16:35 +51,M,01:36:36,03:16:35 +45,M,01:33:32,03:16:36 +27,M,01:30:53,03:16:36 +36,M,01:35:44,03:16:36 +26,M,01:35:38,03:16:38 +47,M,01:36:07,03:16:39 +43,M,01:33:00,03:16:40 +40,M,01:33:38,03:16:40 +50,M,01:35:43,03:16:41 +49,M,01:35:21,03:16:42 +34,M,01:33:40,03:16:43 +24,M,01:35:42,03:16:45 +42,M,01:32:52,03:16:45 +51,M,01:34:32,03:16:46 +35,W,01:34:21,03:16:47 +34,W,01:37:12,03:16:47 +31,M,01:31:05,03:16:50 +25,W,01:37:46,03:16:50 +41,M,01:35:18,03:16:50 +18,M,01:31:54,03:16:50 +54,M,01:34:54,03:16:51 +31,M,01:29:05,03:16:51 +41,M,01:32:32,03:16:51 +32,M,01:30:58,03:16:51 +43,W,01:35:36,03:16:52 +55,M,01:31:50,03:16:53 +50,M,01:32:23,03:16:54 +52,M,01:30:28,03:16:54 +57,M,01:36:34,03:16:55 +49,M,01:29:20,03:16:56 +41,M,01:37:16,03:16:59 +54,M,01:34:53,03:16:59 +47,M,01:29:04,03:17:01 +35,W,01:37:38,03:17:01 +47,M,01:32:34,03:17:03 +26,W,01:37:28,03:17:03 +48,M,01:34:51,03:17:04 +35,M,01:34:51,03:17:04 +50,M,01:32:32,03:17:04 +40,W,01:38:09,03:17:06 +44,W,01:28:41,03:17:06 +44,M,01:37:28,03:17:07 +44,M,01:36:00,03:17:08 +29,M,01:28:22,03:17:09 +48,M,01:33:42,03:17:09 +39,M,01:35:10,03:17:09 +41,M,01:37:34,03:17:09 +27,M,01:32:58,03:17:11 +30,M,01:27:38,03:17:11 +45,M,01:26:45,03:17:12 +53,M,01:31:52,03:17:12 +29,M,01:35:07,03:17:13 +33,M,01:33:48,03:17:14 +39,M,01:26:51,03:17:14 +44,M,01:34:53,03:17:14 +52,M,01:32:51,03:17:15 +33,M,01:28:28,03:17:16 +47,W,01:38:18,03:17:17 +40,M,01:35:27,03:17:17 +41,M,01:33:44,03:17:17 +32,M,01:26:38,03:17:18 +38,M,01:35:09,03:17:18 +45,M,01:37:07,03:17:18 +29,M,01:28:22,03:17:20 +36,M,01:29:59,03:17:20 +46,M,01:31:03,03:17:21 +32,M,01:28:16,03:17:22 +36,M,01:37:32,03:17:22 +53,M,01:37:11,03:17:24 +46,M,01:34:04,03:17:24 +43,M,01:30:09,03:17:24 +34,M,01:30:40,03:17:25 +46,M,01:30:55,03:17:26 +38,M,01:33:47,03:17:27 +40,M,01:38:32,03:17:27 +33,M,01:33:42,03:17:27 +43,M,01:31:50,03:17:29 +32,M,01:32:19,03:17:30 +44,W,01:35:31,03:17:30 +33,W,01:34:40,03:17:30 +33,W,01:37:25,03:17:30 +49,M,01:35:23,03:17:31 +36,M,01:33:41,03:17:32 +48,M,01:31:45,03:17:32 +44,M,01:31:21,03:17:33 +26,M,01:30:09,03:17:33 +47,M,01:28:10,03:17:34 +42,M,01:32:18,03:17:36 +39,M,01:30:31,03:17:36 +32,M,01:33:29,03:17:36 +40,M,01:32:01,03:17:37 +32,M,01:34:51,03:17:37 +37,M,01:31:08,03:17:38 +40,M,01:37:28,03:17:38 +27,W,01:35:04,03:17:38 +44,M,01:32:07,03:17:38 +31,M,01:31:33,03:17:38 +34,M,01:37:04,03:17:39 +53,M,01:36:24,03:17:39 +32,M,01:35:59,03:17:39 +25,W,01:31:43,03:17:40 +45,M,01:28:49,03:17:40 +38,M,01:36:45,03:17:40 +32,M,01:32:13,03:17:41 +52,M,01:33:05,03:17:41 +42,M,01:38:45,03:17:43 +45,W,01:37:21,03:17:43 +39,M,01:38:47,03:17:45 +44,M,01:34:49,03:17:46 +46,M,01:32:17,03:17:46 +47,M,01:35:07,03:17:47 +49,M,01:36:37,03:17:47 +44,M,01:24:29,03:17:47 +42,M,01:37:16,03:17:47 +25,M,01:39:15,03:17:48 +40,M,01:35:54,03:17:48 +29,M,01:34:04,03:17:50 +44,M,01:32:48,03:17:50 +48,M,01:32:52,03:17:50 +32,M,01:30:25,03:17:50 +23,W,01:37:11,03:17:51 +31,M,01:37:10,03:17:52 +30,M,01:35:53,03:17:52 +34,M,01:39:49,03:17:53 +41,M,01:35:39,03:17:54 +35,M,01:37:40,03:17:54 +46,M,01:33:27,03:17:56 +51,M,01:30:06,03:17:57 +50,M,01:35:52,03:17:58 +28,M,01:30:05,03:17:59 +34,M,01:30:02,03:17:59 +47,W,01:38:34,03:17:59 +26,M,01:29:20,03:17:59 +43,M,01:24:04,03:17:59 +32,M,01:32:20,03:18:00 +43,M,01:37:16,03:18:01 +36,M,01:35:35,03:18:02 +28,M,01:30:03,03:18:02 +31,M,01:39:20,03:18:03 +39,M,01:36:38,03:18:03 +40,M,01:23:38,03:18:04 +48,M,01:36:26,03:18:05 +56,M,01:33:34,03:18:05 +44,M,01:28:58,03:18:07 +45,M,01:33:21,03:18:09 +29,M,01:38:00,03:18:09 +40,M,01:29:38,03:18:09 +55,M,01:38:21,03:18:11 +42,M,01:26:39,03:18:11 +42,M,01:29:51,03:18:11 +47,M,01:29:32,03:18:12 +34,M,01:37:54,03:18:12 +36,M,01:31:09,03:18:12 +50,M,01:37:05,03:18:13 +42,M,01:33:34,03:18:14 +27,M,01:36:40,03:18:14 +39,M,01:36:59,03:18:15 +44,M,01:35:38,03:18:15 +52,M,01:37:26,03:18:15 +43,M,01:37:44,03:18:15 +48,M,01:35:24,03:18:17 +19,M,01:36:34,03:18:17 +42,M,01:36:13,03:18:17 +33,M,01:34:54,03:18:17 +46,W,01:36:50,03:18:18 +30,M,01:33:00,03:18:20 +38,M,01:37:40,03:18:20 +32,M,01:31:59,03:18:20 +31,W,01:34:41,03:18:20 +47,M,01:33:09,03:18:21 +48,M,01:35:42,03:18:21 +30,M,01:31:17,03:18:22 +28,M,01:36:10,03:18:22 +45,M,01:32:52,03:18:22 +33,W,01:37:45,03:18:22 +34,M,01:28:57,03:18:22 +51,M,01:33:22,03:18:23 +59,M,01:36:36,03:18:23 +34,M,01:32:55,03:18:23 +27,W,01:33:58,03:18:25 +36,M,01:34:37,03:18:26 +43,M,01:26:20,03:18:26 +34,M,01:30:34,03:18:27 +40,M,01:37:34,03:18:28 +51,M,01:26:35,03:18:28 +32,M,01:30:59,03:18:29 +40,M,01:32:22,03:18:30 +58,M,01:35:10,03:18:30 +46,M,01:29:50,03:18:30 +33,M,01:28:58,03:18:30 +32,W,01:36:11,03:18:31 +41,M,01:37:34,03:18:31 +44,M,01:34:11,03:18:31 +36,M,01:33:58,03:18:32 +36,M,01:38:18,03:18:35 +43,M,01:33:24,03:18:36 +35,M,01:36:51,03:18:37 +57,M,01:33:26,03:18:38 +35,M,01:32:36,03:18:38 +26,M,01:35:39,03:18:39 +39,M,01:35:41,03:18:39 +48,M,01:31:31,03:18:40 +37,M,01:32:37,03:18:40 +25,M,01:34:37,03:18:41 +35,M,01:35:03,03:18:41 +46,M,01:36:47,03:18:41 +41,M,01:22:32,03:18:41 +34,M,01:34:00,03:18:42 +35,M,01:33:37,03:18:43 +44,M,01:27:27,03:18:44 +36,M,01:34:11,03:18:44 +59,M,01:36:03,03:18:44 +38,W,01:34:50,03:18:45 +31,M,01:37:02,03:18:45 +36,M,01:33:10,03:18:46 +43,M,01:43:34,03:18:47 +52,M,01:35:55,03:18:48 +42,M,01:34:47,03:18:48 +27,M,01:32:48,03:18:48 +33,M,01:36:34,03:18:49 +34,M,01:37:22,03:18:49 +52,M,01:31:35,03:18:50 +55,M,01:33:48,03:18:51 +53,M,01:35:33,03:18:52 +41,M,01:31:51,03:18:53 +45,M,01:35:17,03:18:53 +32,W,01:38:51,03:18:54 +44,M,01:38:46,03:18:54 +49,W,01:33:53,03:18:54 +27,M,01:27:20,03:18:55 +47,M,01:37:46,03:18:55 +49,M,01:35:23,03:18:55 +61,M,01:36:29,03:18:56 +41,M,01:37:53,03:18:57 +33,W,01:38:54,03:18:57 +36,M,01:36:58,03:18:57 +48,M,01:32:39,03:18:58 +45,M,01:37:00,03:18:58 +34,M,01:33:58,03:18:59 +27,W,01:37:26,03:19:00 +65,M,01:36:37,03:19:01 +38,M,01:33:16,03:19:01 +30,M,01:31:47,03:19:02 +57,M,01:35:11,03:19:02 +30,M,01:30:37,03:19:02 +31,M,01:29:58,03:19:03 +25,W,01:36:17,03:19:04 +41,M,01:26:16,03:19:04 +36,W,01:33:37,03:19:05 +53,M,01:31:38,03:19:05 +40,W,01:37:39,03:19:06 +36,M,01:35:46,03:19:08 +41,M,01:33:04,03:19:09 +54,M,01:33:16,03:19:09 +27,W,01:39:01,03:19:09 +42,M,01:37:54,03:19:09 +37,M,01:36:08,03:19:09 +37,M,01:36:47,03:19:09 +32,M,01:40:01,03:19:11 +47,M,01:32:05,03:19:11 +52,M,01:37:39,03:19:11 +43,M,01:37:16,03:19:11 +40,M,01:34:24,03:19:12 +45,M,01:34:37,03:19:12 +40,M,01:32:28,03:19:12 +54,M,01:35:50,03:19:12 +45,M,01:35:07,03:19:12 +38,W,01:34:33,03:19:13 +39,M,01:33:27,03:19:13 +50,M,01:39:27,03:19:13 +52,M,01:32:07,03:19:14 +31,M,01:36:10,03:19:14 +28,M,01:39:28,03:19:15 +47,M,01:36:40,03:19:16 +46,M,01:32:59,03:19:17 +41,M,01:36:28,03:19:18 +44,M,01:40:24,03:19:19 +42,M,01:34:33,03:19:19 +26,M,01:33:01,03:19:20 +42,M,01:25:59,03:19:20 +26,M,01:34:32,03:19:20 +50,M,01:40:20,03:19:20 +51,M,01:31:47,03:19:21 +63,M,01:37:04,03:19:21 +39,M,01:34:07,03:19:21 +42,M,01:38:07,03:19:21 +51,M,01:35:46,03:19:22 +29,M,01:38:13,03:19:22 +30,M,01:34:19,03:19:23 +27,W,01:33:23,03:19:23 +38,W,01:36:20,03:19:24 +39,M,01:40:31,03:19:24 +24,W,01:31:59,03:19:24 +34,M,01:36:28,03:19:25 +49,M,01:35:25,03:19:25 +43,M,01:35:35,03:19:25 +51,M,01:30:08,03:19:26 +47,M,01:33:01,03:19:26 +35,M,01:32:15,03:19:26 +43,M,01:36:00,03:19:26 +32,W,01:34:38,03:19:26 +37,M,01:31:58,03:19:27 +30,M,01:30:33,03:19:27 +40,M,01:32:06,03:19:29 +48,M,01:31:06,03:19:30 +23,W,01:39:30,03:19:30 +29,M,01:30:31,03:19:31 +44,W,01:34:41,03:19:32 +35,M,01:34:35,03:19:32 +44,W,01:37:25,03:19:33 +44,M,01:31:06,03:19:34 +43,M,01:35:35,03:19:34 +39,W,01:37:27,03:19:35 +51,M,01:34:54,03:19:36 +48,M,01:29:52,03:19:37 +45,M,01:34:38,03:19:37 +48,W,01:33:05,03:19:37 +31,M,01:36:43,03:19:37 +44,M,01:34:25,03:19:37 +36,M,01:36:28,03:19:38 +34,M,01:37:52,03:19:38 +24,W,01:35:51,03:19:38 +44,M,01:36:13,03:19:38 +39,M,01:35:25,03:19:38 +30,M,01:30:42,03:19:38 +42,W,01:38:28,03:19:39 +28,W,01:33:43,03:19:39 +52,M,01:34:46,03:19:39 +28,M,01:28:39,03:19:40 +32,W,01:38:26,03:19:41 +40,M,01:29:54,03:19:41 +37,M,01:32:37,03:19:41 +43,M,01:34:09,03:19:41 +33,M,01:38:59,03:19:41 +40,W,01:34:33,03:19:41 +44,W,01:36:30,03:19:42 +29,M,01:38:00,03:19:42 +47,M,01:36:08,03:19:44 +43,M,01:32:01,03:19:44 +44,W,01:36:52,03:19:45 +50,M,01:37:59,03:19:45 +40,M,01:33:17,03:19:46 +45,W,01:38:29,03:19:46 +48,M,01:31:58,03:19:46 +26,M,01:31:10,03:19:46 +36,M,01:36:21,03:19:47 +24,M,01:32:24,03:19:48 +52,M,01:32:12,03:19:48 +33,M,01:35:47,03:19:48 +33,W,01:37:28,03:19:48 +43,M,01:36:06,03:19:49 +41,M,01:31:25,03:19:49 +33,M,01:32:42,03:19:50 +26,W,01:32:59,03:19:50 +32,M,01:34:15,03:19:51 +24,W,01:32:42,03:19:51 +40,M,01:35:14,03:19:51 +31,W,01:36:05,03:19:51 +42,M,01:35:33,03:19:51 +49,M,01:33:44,03:19:52 +31,W,01:31:39,03:19:53 +42,M,01:35:56,03:19:54 +36,M,01:36:48,03:19:54 +47,M,01:34:50,03:19:55 +26,M,01:38:15,03:19:56 +47,M,01:37:23,03:19:56 +35,M,01:34:33,03:19:56 +52,M,01:35:03,03:19:56 +27,M,01:32:46,03:19:58 +42,M,01:33:39,03:19:59 +41,M,01:35:54,03:20:00 +40,W,01:37:12,03:20:00 +40,M,01:29:40,03:20:01 +40,M,01:36:18,03:20:03 +46,M,01:37:12,03:20:04 +39,M,01:36:55,03:20:04 +45,M,01:35:38,03:20:05 +45,M,01:36:10,03:20:05 +37,M,01:30:44,03:20:05 +23,M,01:41:21,03:20:07 +31,M,01:38:38,03:20:07 +47,M,01:31:59,03:20:08 +48,M,01:32:32,03:20:08 +45,M,01:34:03,03:20:08 +42,M,01:36:15,03:20:08 +35,W,01:37:06,03:20:09 +26,W,01:38:07,03:20:09 +44,M,01:36:02,03:20:09 +51,M,01:32:15,03:20:10 +30,M,01:37:21,03:20:10 +38,W,01:34:33,03:20:10 +28,M,01:30:10,03:20:10 +47,M,01:31:33,03:20:10 +28,M,01:31:26,03:20:11 +40,M,01:30:51,03:20:12 +39,M,01:33:19,03:20:12 +31,M,01:34:11,03:20:13 +54,M,01:36:45,03:20:13 +37,W,01:37:33,03:20:13 +31,M,01:36:38,03:20:14 +35,M,01:34:38,03:20:17 +43,M,01:38:42,03:20:17 +40,M,01:30:29,03:20:18 +46,M,01:38:17,03:20:18 +42,M,01:34:17,03:20:18 +38,W,01:34:31,03:20:18 +30,M,01:38:02,03:20:18 +41,M,01:46:11,03:20:19 +39,M,01:40:04,03:20:20 +29,M,01:36:35,03:20:20 +50,M,01:36:44,03:20:20 +53,M,01:29:29,03:20:20 +28,M,01:31:55,03:20:20 +54,M,01:36:02,03:20:20 +34,M,01:33:47,03:20:21 +41,M,01:31:52,03:20:21 +37,M,01:32:42,03:20:22 +39,M,01:35:59,03:20:22 +29,M,01:39:09,03:20:22 +49,M,01:34:50,03:20:22 +35,M,01:39:04,03:20:23 +40,W,01:32:36,03:20:23 +40,M,01:35:39,03:20:23 +34,M,01:36:12,03:20:24 +28,M,01:34:05,03:20:24 +32,M,01:25:18,03:20:25 +30,W,01:38:35,03:20:26 +38,M,01:30:09,03:20:27 +41,M,01:36:30,03:20:27 +38,W,01:41:29,03:20:27 +51,M,01:32:14,03:20:28 +30,M,01:27:42,03:20:28 +22,W,01:36:51,03:20:28 +38,M,01:26:15,03:20:29 +45,W,01:38:02,03:20:29 +40,W,01:36:14,03:20:29 +40,M,01:37:06,03:20:30 +49,M,01:29:04,03:20:31 +56,M,01:34:16,03:20:33 +56,M,01:39:54,03:20:33 +25,W,01:34:08,03:20:33 +43,M,01:28:03,03:20:33 +43,M,01:38:32,03:20:34 +48,M,01:36:04,03:20:34 +50,M,01:33:50,03:20:35 +31,W,01:35:18,03:20:36 +40,M,01:31:09,03:20:36 +28,W,01:38:16,03:20:37 +46,M,01:39:19,03:20:37 +41,W,01:36:58,03:20:38 +47,M,01:27:15,03:20:38 +30,W,01:40:59,03:20:38 +48,M,01:39:42,03:20:39 +27,M,01:37:34,03:20:39 +43,M,01:33:42,03:20:41 +28,W,01:35:41,03:20:41 +29,M,01:34:42,03:20:41 +50,M,01:38:06,03:20:42 +25,M,01:33:53,03:20:43 +51,M,01:37:36,03:20:43 +44,M,01:40:44,03:20:43 +51,M,01:37:44,03:20:43 +45,M,01:37:13,03:20:43 +41,M,01:35:58,03:20:44 +43,M,01:37:43,03:20:44 +34,M,01:30:26,03:20:44 +47,M,01:35:38,03:20:45 +23,W,01:38:09,03:20:45 +52,M,01:31:01,03:20:45 +51,M,01:35:29,03:20:46 +41,M,01:38:33,03:20:46 +57,M,01:37:23,03:20:46 +43,M,01:40:51,03:20:47 +33,M,01:31:10,03:20:48 +25,M,01:31:02,03:20:48 +31,M,01:32:48,03:20:49 +34,M,01:31:43,03:20:49 +48,M,01:36:45,03:20:51 +37,M,01:27:49,03:20:51 +43,M,01:33:07,03:20:52 +37,M,01:32:47,03:20:52 +48,M,01:35:43,03:20:53 +39,M,01:36:56,03:20:53 +34,M,01:35:00,03:20:53 +37,W,01:38:27,03:20:56 +34,M,01:35:14,03:20:56 +39,W,01:37:56,03:20:58 +26,M,01:35:23,03:20:59 +37,M,01:35:40,03:21:00 +53,W,01:38:38,03:21:01 +25,M,01:29:11,03:21:01 +52,M,01:39:54,03:21:01 +56,M,01:32:35,03:21:01 +44,M,01:35:31,03:21:02 +24,M,01:36:04,03:21:02 +52,M,01:36:54,03:21:03 +41,M,01:33:58,03:21:04 +44,M,01:35:49,03:21:04 +33,M,01:38:26,03:21:05 +33,M,01:37:22,03:21:05 +40,M,01:35:08,03:21:05 +35,M,01:28:39,03:21:06 +42,W,01:37:19,03:21:07 +38,M,01:39:29,03:21:08 +24,M,01:34:16,03:21:08 +43,W,01:38:07,03:21:08 +45,M,01:24:06,03:21:08 +52,M,01:37:07,03:21:09 +35,M,01:33:14,03:21:09 +33,W,01:37:00,03:21:09 +38,W,01:37:45,03:21:09 +41,M,01:37:31,03:21:10 +37,M,01:37:53,03:21:10 +55,M,01:35:14,03:21:10 +31,M,01:37:59,03:21:10 +40,M,01:32:22,03:21:11 +27,W,01:34:30,03:21:13 +40,W,01:33:45,03:21:13 +31,W,01:37:04,03:21:14 +26,M,01:34:48,03:21:16 +32,W,01:37:00,03:21:16 +39,W,01:36:14,03:21:16 +34,W,01:37:00,03:21:16 +47,M,01:36:06,03:21:17 +30,W,01:38:48,03:21:17 +39,M,01:38:50,03:21:18 +42,M,01:34:37,03:21:19 +53,M,01:40:12,03:21:20 +38,M,01:35:36,03:21:20 +39,M,01:30:23,03:21:20 +45,M,01:34:48,03:21:21 +51,M,01:38:22,03:21:22 +19,M,01:34:32,03:21:22 +49,M,01:35:30,03:21:23 +33,W,01:35:25,03:21:23 +42,M,01:35:51,03:21:23 +26,W,01:40:16,03:21:24 +34,W,01:35:06,03:21:25 +41,M,01:38:35,03:21:25 +29,M,01:31:05,03:21:25 +22,M,01:32:52,03:21:25 +39,M,01:33:31,03:21:26 +35,M,01:37:38,03:21:26 +56,M,01:41:19,03:21:26 +50,M,01:33:31,03:21:26 +36,M,01:35:38,03:21:27 +49,M,01:38:15,03:21:27 +32,M,01:29:50,03:21:30 +56,M,01:37:58,03:21:31 +45,M,01:35:54,03:21:31 +24,W,01:34:05,03:21:32 +43,M,01:36:54,03:21:33 +52,M,01:36:46,03:21:35 +40,M,01:34:05,03:21:36 +55,M,01:36:38,03:21:37 +48,M,01:36:57,03:21:37 +43,M,01:28:32,03:21:37 +34,M,01:33:25,03:21:37 +42,M,01:35:32,03:21:38 +44,M,01:32:36,03:21:38 +50,M,01:36:24,03:21:39 +54,M,01:38:13,03:21:40 +41,M,01:36:30,03:21:41 +35,W,01:40:03,03:21:41 +43,W,01:35:28,03:21:43 +44,M,01:38:35,03:21:44 +56,M,01:38:24,03:21:44 +40,W,01:36:59,03:21:44 +26,M,01:36:11,03:21:46 +45,M,01:35:36,03:21:46 +52,M,01:39:18,03:21:46 +49,M,01:37:37,03:21:47 +49,W,01:36:06,03:21:48 +24,W,01:37:48,03:21:48 +32,M,01:35:30,03:21:48 +41,M,01:32:38,03:21:48 +35,M,01:39:17,03:21:49 +52,M,01:40:13,03:21:49 +50,M,01:38:32,03:21:49 +47,M,01:33:01,03:21:49 +46,W,01:39:41,03:21:50 +52,M,01:39:02,03:21:50 +60,M,01:38:16,03:21:51 +33,W,01:36:24,03:21:51 +60,M,01:40:54,03:21:51 +36,M,01:32:58,03:21:51 +45,W,01:40:09,03:21:51 +44,M,01:37:29,03:21:51 +35,W,01:36:46,03:21:52 +52,M,01:34:55,03:21:53 +39,M,01:41:02,03:21:53 +43,M,01:33:27,03:21:53 +39,M,01:39:29,03:21:54 +45,M,01:34:48,03:21:54 +35,W,01:36:36,03:21:56 +40,M,01:37:24,03:21:56 +36,W,01:39:53,03:21:56 +29,M,01:37:29,03:21:57 +49,M,01:31:34,03:22:01 +45,M,01:36:36,03:22:02 +47,M,01:35:10,03:22:02 +48,M,01:33:35,03:22:03 +42,M,01:26:27,03:22:03 +46,M,01:40:31,03:22:04 +53,M,01:35:31,03:22:04 +42,M,01:34:02,03:22:04 +55,M,01:38:04,03:22:05 +32,W,01:37:29,03:22:06 +43,W,01:32:31,03:22:07 +48,M,01:42:32,03:22:07 +46,M,01:36:04,03:22:09 +26,M,01:37:51,03:22:12 +32,M,01:35:31,03:22:13 +45,M,01:25:32,03:22:13 +40,M,01:40:57,03:22:14 +35,M,01:31:09,03:22:14 +37,W,01:36:30,03:22:15 +25,W,01:39:26,03:22:18 +31,M,01:34:08,03:22:18 +42,M,01:37:37,03:22:20 +39,M,01:36:44,03:22:20 +27,W,01:38:42,03:22:20 +41,M,01:39:47,03:22:20 +42,M,01:36:05,03:22:21 +45,M,01:33:10,03:22:21 +47,M,01:38:44,03:22:22 +35,W,01:38:11,03:22:22 +44,M,01:37:27,03:22:23 +27,M,01:36:30,03:22:23 +31,W,01:36:12,03:22:24 +44,W,01:36:37,03:22:25 +34,W,01:34:51,03:22:26 +43,W,01:39:43,03:22:28 +30,W,01:41:39,03:22:29 +30,M,01:36:58,03:22:29 +39,M,01:27:56,03:22:29 +45,M,01:35:44,03:22:30 +43,M,01:37:10,03:22:31 +42,M,01:35:03,03:22:31 +41,M,01:38:42,03:22:31 +57,M,01:39:14,03:22:32 +55,M,01:38:38,03:22:33 +41,W,01:38:29,03:22:34 +27,M,01:35:37,03:22:35 +38,M,01:43:53,03:22:35 +28,W,01:34:22,03:22:37 +40,M,01:34:37,03:22:37 +51,M,01:38:28,03:22:38 +53,M,01:36:16,03:22:38 +33,W,01:31:33,03:22:38 +35,M,01:35:14,03:22:39 +39,M,01:33:29,03:22:40 +33,M,01:35:31,03:22:40 +51,M,01:36:54,03:22:41 +25,M,01:39:43,03:22:41 +36,M,01:34:08,03:22:41 +26,M,01:32:45,03:22:41 +39,M,01:40:45,03:22:41 +51,M,01:39:17,03:22:42 +50,M,01:36:44,03:22:42 +62,M,01:32:58,03:22:42 +31,M,01:36:54,03:22:43 +40,M,01:34:51,03:22:43 +45,M,01:36:02,03:22:44 +40,M,01:39:29,03:22:45 +37,M,01:28:54,03:22:46 +39,M,01:39:03,03:22:46 +51,M,01:39:35,03:22:47 +47,W,01:37:30,03:22:48 +49,M,01:30:08,03:22:48 +49,M,01:29:54,03:22:48 +41,M,01:31:39,03:22:48 +34,W,01:37:36,03:22:48 +58,M,01:37:29,03:22:49 +24,M,01:29:40,03:22:49 +42,W,01:38:48,03:22:49 +29,M,01:39:33,03:22:49 +49,M,01:37:20,03:22:50 +41,M,01:27:46,03:22:50 +27,M,01:38:40,03:22:51 +31,M,01:34:50,03:22:52 +30,M,01:34:23,03:22:52 +52,M,01:38:43,03:22:52 +30,M,01:37:49,03:22:53 +42,M,01:33:13,03:22:53 +61,M,01:35:38,03:22:54 +24,M,01:33:20,03:22:54 +38,M,01:26:05,03:22:54 +28,M,01:26:57,03:22:54 +37,M,01:35:42,03:22:54 +29,M,01:37:49,03:22:54 +47,M,01:32:37,03:22:55 +40,M,01:37:38,03:22:55 +39,M,01:32:34,03:22:56 +42,W,01:34:59,03:22:56 +35,M,01:40:43,03:22:56 +40,M,01:34:02,03:22:56 +48,W,01:37:48,03:22:56 +39,M,01:37:30,03:22:57 +46,M,01:37:04,03:22:57 +52,M,01:38:55,03:22:57 +33,M,01:38:39,03:22:59 +39,M,01:39:27,03:22:59 +28,M,01:35:25,03:22:59 +37,W,01:33:23,03:23:01 +50,M,01:39:26,03:23:02 +40,M,01:38:38,03:23:02 +52,M,01:37:05,03:23:03 +47,M,01:41:27,03:23:03 +39,W,01:40:29,03:23:04 +44,W,01:39:21,03:23:05 +41,M,01:35:40,03:23:05 +26,W,01:36:59,03:23:06 +39,W,01:39:16,03:23:07 +43,M,01:36:36,03:23:07 +27,W,01:40:01,03:23:08 +44,M,01:39:08,03:23:08 +26,M,01:33:39,03:23:08 +42,M,01:34:28,03:23:08 +26,M,01:32:34,03:23:09 +35,M,01:38:26,03:23:09 +49,M,01:38:14,03:23:10 +46,W,01:38:46,03:23:10 +45,M,01:40:42,03:23:11 +38,M,01:36:32,03:23:11 +50,M,01:37:51,03:23:13 +32,M,01:36:07,03:23:14 +36,M,01:32:41,03:23:14 +38,W,01:39:25,03:23:15 +33,M,01:35:49,03:23:15 +49,M,01:39:09,03:23:15 +46,M,01:39:43,03:23:15 +45,W,01:34:13,03:23:15 +43,M,01:35:27,03:23:17 +40,M,01:31:46,03:23:19 +27,M,01:34:17,03:23:19 +28,M,01:42:54,03:23:20 +42,M,01:36:42,03:23:20 +38,M,01:36:22,03:23:21 +50,M,01:39:05,03:23:21 +33,M,01:36:43,03:23:21 +61,M,01:39:00,03:23:22 +37,M,01:36:46,03:23:22 +47,M,01:38:42,03:23:22 +39,M,01:18:45,03:23:23 +36,M,01:39:24,03:23:23 +46,M,01:39:21,03:23:23 +43,W,01:38:58,03:23:23 +39,M,01:36:06,03:23:24 +46,M,01:39:18,03:23:24 +24,M,01:38:43,03:23:25 +34,M,01:37:50,03:23:25 +26,M,01:41:28,03:23:26 +26,M,01:35:49,03:23:26 +38,M,01:38:19,03:23:27 +49,M,01:32:09,03:23:27 +50,M,01:38:30,03:23:28 +50,M,01:39:27,03:23:30 +39,M,01:37:42,03:23:30 +52,M,01:39:04,03:23:31 +45,M,01:34:31,03:23:31 +29,M,01:36:10,03:23:31 +39,M,01:37:18,03:23:32 +25,M,01:33:20,03:23:32 +36,M,01:36:15,03:23:32 +41,M,01:35:57,03:23:33 +39,M,01:38:22,03:23:33 +24,W,01:39:36,03:23:33 +23,W,01:40:40,03:23:33 +23,W,01:35:00,03:23:33 +40,W,01:37:01,03:23:33 +36,M,01:34:51,03:23:34 +39,W,01:39:01,03:23:35 +55,M,01:32:44,03:23:36 +30,M,01:38:27,03:23:36 +51,M,01:39:51,03:23:36 +37,M,01:30:15,03:23:36 +46,M,01:38:31,03:23:37 +22,M,01:28:50,03:23:37 +56,M,01:36:17,03:23:37 +33,M,01:33:03,03:23:40 +29,M,01:33:22,03:23:40 +56,M,01:39:56,03:23:41 +35,M,01:36:55,03:23:41 +42,M,01:28:11,03:23:41 +53,M,01:31:14,03:23:42 +39,M,01:27:05,03:23:42 +30,M,01:42:38,03:23:43 +43,W,01:35:53,03:23:44 +52,M,01:36:59,03:23:45 +29,M,01:36:35,03:23:46 +31,M,01:36:03,03:23:46 +49,M,01:32:11,03:23:47 +43,W,01:36:14,03:23:47 +43,M,01:31:28,03:23:47 +31,M,01:34:11,03:23:47 +47,M,01:32:30,03:23:48 +38,W,01:36:29,03:23:48 +54,M,01:38:37,03:23:48 +41,M,01:41:21,03:23:49 +51,M,01:37:07,03:23:49 +46,M,01:36:37,03:23:49 +28,M,01:32:37,03:23:49 +58,M,01:39:25,03:23:49 +36,M,01:37:08,03:23:50 +44,M,01:35:20,03:23:50 +40,M,01:35:46,03:23:50 +26,M,01:38:54,03:23:51 +41,M,01:36:40,03:23:53 +34,M,01:41:04,03:23:54 +50,M,01:37:17,03:23:55 +44,M,01:32:47,03:23:55 +46,M,01:35:33,03:23:57 +53,M,01:40:06,03:23:57 +51,M,01:39:29,03:23:58 +59,M,01:37:20,03:23:58 +48,M,01:40:23,03:23:59 +39,M,01:38:59,03:23:59 +35,M,01:40:38,03:23:59 +39,M,01:35:15,03:23:59 +43,W,01:38:20,03:24:00 +51,M,01:37:02,03:24:00 +36,M,01:36:22,03:24:00 +27,W,01:37:25,03:24:00 +36,W,01:37:36,03:24:00 +50,M,01:36:03,03:24:00 +43,W,01:35:29,03:24:02 +44,M,01:37:24,03:24:02 +24,M,01:29:43,03:24:02 +41,M,01:35:40,03:24:03 +67,M,01:39:27,03:24:03 +42,M,01:30:59,03:24:04 +30,M,01:32:54,03:24:04 +47,M,01:40:05,03:24:04 +27,M,01:27:10,03:24:05 +35,M,01:39:05,03:24:05 +46,M,01:36:29,03:24:06 +33,M,01:33:35,03:24:07 +30,W,01:41:45,03:24:07 +37,M,01:29:57,03:24:07 +45,M,01:40:27,03:24:08 +28,M,01:36:27,03:24:08 +51,M,01:36:09,03:24:08 +49,M,01:38:18,03:24:08 +52,M,01:41:07,03:24:08 +32,M,01:37:29,03:24:09 +32,W,01:38:42,03:24:10 +58,M,01:37:05,03:24:10 +60,M,01:36:10,03:24:11 +35,W,01:36:07,03:24:11 +23,W,01:40:35,03:24:11 +44,W,01:35:51,03:24:12 +41,M,01:38:21,03:24:13 +34,M,01:39:52,03:24:13 +42,W,01:38:52,03:24:14 +49,M,01:30:40,03:24:14 +49,M,01:36:10,03:24:14 +39,M,01:35:39,03:24:14 +45,W,01:36:34,03:24:14 +31,M,01:27:29,03:24:15 +38,M,01:29:43,03:24:15 +46,M,01:35:25,03:24:16 +43,M,01:36:27,03:24:17 +57,M,01:41:04,03:24:18 +37,W,01:38:26,03:24:19 +33,W,01:36:19,03:24:20 +49,M,01:34:19,03:24:21 +46,M,01:36:20,03:24:21 +43,M,01:36:36,03:24:21 +34,M,01:31:37,03:24:22 +43,M,01:32:50,03:24:22 +33,M,01:30:28,03:24:22 +33,M,01:37:41,03:24:22 +29,M,01:32:59,03:24:23 +50,M,01:39:16,03:24:24 +29,M,01:27:43,03:24:24 +30,W,01:37:02,03:24:25 +27,M,01:32:15,03:24:25 +43,M,01:39:46,03:24:26 +40,M,01:38:23,03:24:27 +52,M,01:31:58,03:24:28 +28,M,01:37:11,03:24:28 +31,M,01:39:59,03:24:28 +54,M,01:40:12,03:24:29 +47,M,01:30:09,03:24:29 +32,M,01:38:34,03:24:30 +42,M,01:36:47,03:24:30 +48,M,01:40:58,03:24:30 +41,M,01:38:23,03:24:30 +45,W,01:38:14,03:24:31 +48,M,01:28:03,03:24:32 +39,W,01:38:14,03:24:32 +41,W,01:37:48,03:24:32 +26,M,01:35:34,03:24:32 +42,M,01:35:11,03:24:32 +31,M,01:37:30,03:24:33 +52,M,01:38:48,03:24:34 +47,M,01:33:39,03:24:34 +34,M,01:38:15,03:24:34 +55,M,01:40:14,03:24:34 +52,M,01:37:49,03:24:35 +41,W,01:37:49,03:24:35 +53,M,01:39:42,03:24:36 +45,M,01:34:18,03:24:36 +44,M,01:26:39,03:24:37 +24,M,01:39:50,03:24:37 +31,W,01:43:49,03:24:38 +38,W,01:43:12,03:24:38 +28,M,01:28:31,03:24:39 +28,M,01:36:52,03:24:39 +57,M,01:39:39,03:24:40 +49,M,01:39:43,03:24:41 +21,M,01:30:16,03:24:41 +42,M,01:37:43,03:24:42 +32,M,01:33:13,03:24:42 +39,M,01:42:13,03:24:42 +34,M,01:43:32,03:24:44 +30,M,01:36:03,03:24:44 +42,M,01:31:35,03:24:45 +43,W,01:40:45,03:24:45 +49,M,01:41:20,03:24:46 +51,M,01:36:52,03:24:47 +42,M,01:37:41,03:24:47 +41,M,01:36:25,03:24:47 +31,M,01:41:50,03:24:48 +41,M,01:39:25,03:24:48 +45,M,01:33:17,03:24:49 +33,W,01:35:11,03:24:49 +43,W,01:32:45,03:24:49 +30,M,01:28:25,03:24:49 +34,M,01:35:36,03:24:49 +54,M,01:38:54,03:24:49 +36,M,01:41:18,03:24:50 +43,W,01:38:22,03:24:51 +38,M,01:29:23,03:24:51 +62,M,01:37:41,03:24:51 +48,M,01:31:14,03:24:52 +58,M,01:38:41,03:24:52 +38,M,01:38:54,03:24:53 +54,M,01:38:05,03:24:54 +48,M,01:39:49,03:24:54 +53,M,01:36:54,03:24:54 +26,M,01:35:18,03:24:54 +59,M,01:40:04,03:24:55 +34,W,01:37:08,03:24:55 +37,M,01:37:05,03:24:55 +46,M,01:38:29,03:24:56 +45,M,01:37:04,03:24:56 +27,M,01:39:17,03:24:56 +50,M,01:38:45,03:24:57 +47,W,01:34:49,03:24:57 +52,M,01:40:02,03:24:57 +49,M,01:39:21,03:24:58 +32,W,01:39:18,03:24:58 +27,M,01:30:49,03:24:58 +49,W,01:36:17,03:24:58 +24,M,01:36:40,03:24:59 +48,M,01:40:04,03:25:00 +54,M,01:40:56,03:25:00 +52,M,01:39:41,03:25:01 +42,M,01:37:33,03:25:01 +30,W,01:35:26,03:25:01 +47,M,01:39:13,03:25:01 +36,M,01:42:27,03:25:02 +35,M,01:39:10,03:25:03 +36,M,01:39:26,03:25:03 +48,W,01:39:35,03:25:03 +56,M,01:40:14,03:25:04 +32,M,01:39:08,03:25:04 +33,M,01:23:38,03:25:05 +39,M,01:38:44,03:25:05 +38,M,01:29:06,03:25:05 +50,M,01:36:33,03:25:07 +40,W,01:40:20,03:25:07 +44,M,01:33:17,03:25:08 +60,M,01:39:26,03:25:08 +41,M,01:38:24,03:25:08 +26,W,01:37:38,03:25:08 +46,M,01:32:32,03:25:09 +38,M,01:42:08,03:25:09 +47,M,01:36:27,03:25:10 +34,M,01:30:12,03:25:10 +25,W,01:38:08,03:25:10 +22,M,01:35:09,03:25:10 +32,W,01:32:18,03:25:12 +44,W,01:37:55,03:25:12 +33,W,01:39:39,03:25:13 +38,M,01:33:35,03:25:14 +44,M,01:38:56,03:25:14 +34,W,01:38:22,03:25:14 +41,W,01:40:22,03:25:15 +42,M,01:40:44,03:25:15 +51,M,01:43:53,03:25:15 +37,M,01:33:29,03:25:15 +53,M,01:34:42,03:25:16 +39,M,01:37:41,03:25:16 +32,M,01:26:02,03:25:16 +22,M,01:39:35,03:25:16 +57,M,01:41:26,03:25:16 +36,M,01:39:31,03:25:17 +48,M,01:40:11,03:25:17 +45,M,01:30:33,03:25:17 +38,M,01:40:31,03:25:17 +29,M,01:36:51,03:25:18 +26,M,01:37:25,03:25:18 +60,M,01:34:33,03:25:18 +41,M,01:37:29,03:25:19 +42,W,01:34:46,03:25:19 +32,M,01:41:36,03:25:19 +46,M,01:31:24,03:25:19 +30,M,01:29:51,03:25:20 +45,M,01:29:14,03:25:20 +39,M,01:28:49,03:25:20 +42,M,01:35:50,03:25:21 +48,M,01:39:18,03:25:21 +51,W,01:39:43,03:25:22 +33,M,01:40:57,03:25:22 +40,M,01:35:04,03:25:22 +42,M,01:41:24,03:25:23 +37,M,01:38:43,03:25:24 +32,M,01:33:34,03:25:24 +59,M,01:35:39,03:25:24 +42,M,01:38:31,03:25:24 +36,M,01:38:45,03:25:24 +32,M,01:37:41,03:25:25 +31,M,01:32:09,03:25:25 +33,M,01:31:06,03:25:25 +67,M,01:34:49,03:25:26 +47,M,01:38:11,03:25:26 +37,M,01:32:56,03:25:26 +44,M,01:37:40,03:25:26 +40,M,01:35:51,03:25:27 +30,M,01:39:57,03:25:27 +33,M,01:40:30,03:25:28 +45,M,01:37:36,03:25:28 +34,M,01:43:22,03:25:28 +36,M,01:33:04,03:25:28 +38,M,01:37:07,03:25:28 +38,M,01:42:00,03:25:28 +27,M,01:40:22,03:25:29 +38,W,01:39:16,03:25:29 +39,M,01:41:36,03:25:29 +44,M,01:37:06,03:25:29 +32,M,01:40:11,03:25:30 +36,M,01:37:46,03:25:30 +36,M,01:40:23,03:25:30 +46,M,01:40:14,03:25:32 +41,M,01:32:54,03:25:32 +50,M,01:35:27,03:25:32 +41,M,01:34:12,03:25:33 +34,M,01:31:49,03:25:34 +37,M,01:38:54,03:25:34 +40,W,01:38:14,03:25:34 +43,M,01:36:19,03:25:34 +24,M,01:35:49,03:25:34 +38,M,01:40:10,03:25:35 +28,M,01:39:08,03:25:35 +34,M,01:27:22,03:25:36 +53,M,01:34:27,03:25:36 +38,M,01:38:44,03:25:37 +29,M,01:35:08,03:25:38 +55,M,01:35:07,03:25:38 +50,M,01:37:39,03:25:39 +46,M,01:31:56,03:25:39 +27,W,01:38:39,03:25:39 +53,M,01:38:25,03:25:40 +33,M,01:38:30,03:25:40 +35,M,01:30:55,03:25:40 +53,M,01:38:34,03:25:41 +45,W,01:39:21,03:25:42 +46,M,01:38:05,03:25:42 +37,W,01:40:38,03:25:42 +48,W,01:39:32,03:25:43 +39,W,01:40:26,03:25:43 +22,M,01:33:06,03:25:43 +30,M,01:35:14,03:25:43 +39,M,01:39:48,03:25:43 +39,W,01:39:01,03:25:44 +28,W,01:36:31,03:25:45 +54,W,01:41:19,03:25:45 +61,M,01:38:31,03:25:45 +30,W,01:38:00,03:25:45 +33,M,01:38:12,03:25:45 +29,M,01:31:44,03:25:46 +42,M,01:34:51,03:25:46 +48,M,01:35:07,03:25:46 +24,W,01:40:25,03:25:46 +35,M,01:35:21,03:25:46 +33,W,01:40:09,03:25:47 +42,M,01:35:39,03:25:47 +23,M,01:36:42,03:25:48 +35,W,01:37:16,03:25:48 +37,M,01:41:00,03:25:49 +65,M,01:39:33,03:25:49 +37,M,01:39:47,03:25:51 +37,M,01:36:24,03:25:52 +52,M,01:34:16,03:25:52 +27,W,01:35:28,03:25:52 +37,M,01:35:35,03:25:53 +44,M,01:37:56,03:25:54 +46,M,01:39:01,03:25:54 +24,W,01:29:22,03:25:55 +36,W,01:36:11,03:25:55 +32,M,01:41:55,03:25:55 +48,M,01:42:02,03:25:56 +36,M,01:33:00,03:25:57 +28,W,01:36:04,03:25:58 +34,M,01:38:59,03:25:58 +42,M,01:35:13,03:25:58 +32,M,01:34:56,03:25:58 +33,M,01:39:39,03:25:59 +61,W,01:36:55,03:25:59 +34,W,01:37:51,03:25:59 +54,M,01:30:32,03:25:59 +25,M,01:36:54,03:25:59 +37,M,01:34:29,03:25:59 +52,M,01:37:30,03:26:00 +45,M,01:41:03,03:26:01 +40,M,01:37:26,03:26:01 +33,M,01:33:12,03:26:02 +60,M,01:35:21,03:26:02 +61,M,01:34:05,03:26:02 +36,M,01:39:03,03:26:02 +48,M,01:28:48,03:26:02 +39,M,01:38:36,03:26:02 +26,M,01:24:17,03:26:02 +39,M,01:36:49,03:26:03 +53,M,01:35:28,03:26:03 +42,W,01:38:53,03:26:03 +44,W,01:33:06,03:26:03 +39,M,01:29:51,03:26:03 +26,W,01:40:23,03:26:05 +50,M,01:37:36,03:26:06 +39,M,01:41:16,03:26:07 +45,M,01:35:53,03:26:07 +39,W,01:42:18,03:26:07 +28,M,01:35:57,03:26:08 +50,M,01:39:12,03:26:08 +30,M,01:41:30,03:26:08 +30,M,01:38:08,03:26:09 +48,W,01:40:33,03:26:09 +44,M,01:38:27,03:26:09 +61,M,01:35:03,03:26:10 +31,M,01:41:57,03:26:10 +38,M,01:32:50,03:26:10 +49,M,01:37:03,03:26:10 +41,M,01:35:29,03:26:10 +39,M,01:37:52,03:26:10 +23,M,01:34:11,03:26:11 +51,M,01:36:43,03:26:11 +47,W,01:40:30,03:26:11 +42,M,01:35:14,03:26:11 +43,M,01:37:14,03:26:11 +27,M,01:33:43,03:26:11 +48,M,01:36:49,03:26:12 +37,M,01:28:02,03:26:12 +34,M,01:41:19,03:26:13 +46,M,01:44:31,03:26:13 +51,M,01:39:02,03:26:13 +40,M,01:35:21,03:26:13 +36,M,01:37:49,03:26:13 +35,M,01:33:50,03:26:13 +45,W,01:40:19,03:26:14 +41,W,01:39:41,03:26:14 +34,M,01:38:06,03:26:14 +39,W,01:40:10,03:26:15 +59,M,01:34:55,03:26:15 +43,M,01:31:31,03:26:15 +40,M,01:40:24,03:26:15 +28,M,01:34:54,03:26:15 +47,M,01:41:23,03:26:15 +38,M,01:27:00,03:26:15 +38,M,01:41:18,03:26:15 +45,M,01:32:18,03:26:17 +57,M,01:41:47,03:26:18 +30,W,01:44:19,03:26:19 +48,M,01:39:08,03:26:20 +45,M,01:41:52,03:26:20 +46,M,01:39:57,03:26:20 +23,W,01:36:16,03:26:21 +46,M,01:38:14,03:26:21 +33,M,01:39:48,03:26:22 +45,W,01:40:08,03:26:22 +45,M,01:27:25,03:26:22 +37,M,01:35:57,03:26:23 +48,W,01:37:18,03:26:23 +40,M,01:36:52,03:26:24 +48,M,01:30:54,03:26:24 +52,M,01:38:20,03:26:24 +56,M,01:31:59,03:26:26 +45,M,01:42:34,03:26:27 +46,M,01:37:28,03:26:27 +36,M,01:32:27,03:26:27 +40,W,01:40:11,03:26:28 +47,M,01:44:17,03:26:28 +47,M,01:36:01,03:26:28 +33,W,01:37:31,03:26:29 +46,W,01:39:34,03:26:30 +38,M,01:37:28,03:26:30 +28,M,01:41:30,03:26:31 +36,W,01:42:59,03:26:31 +58,M,01:40:32,03:26:31 +40,M,01:37:23,03:26:31 +53,M,01:35:54,03:26:33 +40,W,01:41:34,03:26:34 +52,M,01:37:31,03:26:35 +35,W,01:39:02,03:26:35 +35,M,01:26:38,03:26:35 +60,M,01:37:56,03:26:36 +33,M,01:44:45,03:26:37 +39,M,01:36:08,03:26:37 +39,M,01:35:40,03:26:39 +28,M,01:37:20,03:26:39 +32,M,01:26:05,03:26:39 +33,M,01:41:01,03:26:40 +30,M,01:25:55,03:26:41 +40,W,01:35:17,03:26:41 +33,M,01:37:26,03:26:42 +60,M,01:34:24,03:26:42 +46,M,01:41:21,03:26:42 +42,M,01:36:32,03:26:43 +28,M,01:43:37,03:26:43 +28,M,01:33:22,03:26:43 +52,M,01:37:07,03:26:43 +27,W,01:42:13,03:26:45 +50,M,01:37:10,03:26:45 +42,M,01:42:31,03:26:45 +43,M,01:39:26,03:26:45 +25,W,01:37:25,03:26:45 +35,M,01:37:35,03:26:45 +38,M,01:34:39,03:26:45 +45,M,01:37:31,03:26:46 +30,M,01:38:29,03:26:46 +51,M,01:38:18,03:26:46 +39,M,01:42:18,03:26:46 +36,W,01:41:38,03:26:46 +50,M,01:28:35,03:26:46 +40,M,01:38:42,03:26:47 +51,M,01:40:17,03:26:48 +33,M,01:39:26,03:26:48 +38,M,01:34:02,03:26:48 +53,M,01:38:34,03:26:49 +37,M,01:39:51,03:26:49 +48,M,01:41:07,03:26:50 +43,W,01:39:54,03:26:51 +39,M,01:41:01,03:26:51 +46,M,01:34:30,03:26:51 +41,M,01:40:39,03:26:52 +24,M,01:40:23,03:26:52 +31,W,01:36:26,03:26:52 +37,W,01:36:13,03:26:52 +24,M,01:35:18,03:26:53 +39,M,01:39:53,03:26:53 +38,M,01:39:50,03:26:54 +53,M,01:35:55,03:26:54 +41,M,01:37:31,03:26:54 +32,M,01:37:32,03:26:54 +39,M,01:31:08,03:26:55 +49,M,01:37:03,03:26:56 +51,M,01:39:53,03:26:57 +38,M,01:38:48,03:26:57 +34,W,01:40:50,03:26:57 +38,M,01:42:37,03:26:57 +38,M,01:41:06,03:26:57 +27,M,01:38:18,03:26:58 +39,M,01:34:20,03:26:59 +26,W,01:37:32,03:26:59 +23,W,01:38:43,03:27:00 +37,M,01:40:19,03:27:00 +34,M,01:30:09,03:27:00 +38,M,01:28:43,03:27:01 +38,M,01:37:31,03:27:01 +37,M,01:42:13,03:27:01 +38,M,01:42:13,03:27:02 +43,M,01:35:14,03:27:02 +41,M,01:41:41,03:27:02 +30,W,01:42:46,03:27:02 +59,M,01:40:11,03:27:03 +44,M,01:34:38,03:27:04 +42,M,01:41:29,03:27:04 +26,M,01:38:58,03:27:04 +54,M,01:39:52,03:27:07 +42,M,01:37:36,03:27:07 +35,M,01:40:41,03:27:08 +34,M,01:33:05,03:27:11 +41,M,01:39:16,03:27:11 +35,M,01:43:14,03:27:11 +33,M,01:31:13,03:27:11 +44,W,01:38:15,03:27:11 +53,M,01:37:24,03:27:12 +40,M,01:37:45,03:27:13 +49,W,01:40:33,03:27:13 +41,M,01:32:50,03:27:13 +55,M,01:39:15,03:27:13 +42,W,01:40:33,03:27:14 +50,M,01:36:33,03:27:14 +51,M,01:41:33,03:27:14 +44,M,01:40:03,03:27:14 +41,W,01:42:05,03:27:16 +32,M,01:42:01,03:27:16 +47,M,01:39:38,03:27:16 +51,W,01:38:17,03:27:17 +34,M,01:40:02,03:27:18 +30,M,01:30:27,03:27:20 +31,W,01:41:35,03:27:21 +39,M,01:38:31,03:27:22 +37,M,01:38:15,03:27:22 +46,M,01:36:29,03:27:23 +37,M,01:42:14,03:27:23 +51,M,01:33:31,03:27:23 +27,M,01:36:49,03:27:23 +44,M,01:43:35,03:27:24 +44,M,01:39:44,03:27:24 +56,M,01:36:26,03:27:25 +36,M,01:35:47,03:27:26 +29,W,01:40:57,03:27:26 +43,M,01:40:00,03:27:26 +55,M,01:37:14,03:27:26 +43,M,01:38:47,03:27:27 +50,M,01:41:51,03:27:28 +48,M,01:37:25,03:27:28 +43,M,01:34:50,03:27:28 +32,M,01:35:01,03:27:28 +32,M,01:34:06,03:27:29 +42,M,01:33:04,03:27:29 +27,W,01:43:25,03:27:29 +33,M,01:39:24,03:27:30 +28,M,01:40:53,03:27:31 +42,M,01:33:43,03:27:31 +33,M,01:40:50,03:27:31 +19,W,01:40:34,03:27:31 +48,M,01:39:17,03:27:32 +39,M,01:40:37,03:27:32 +36,M,01:35:53,03:27:33 +47,M,01:39:42,03:27:33 +45,M,01:39:20,03:27:33 +41,M,01:43:41,03:27:34 +46,M,01:35:44,03:27:34 +47,M,01:39:48,03:27:34 +42,W,01:36:34,03:27:34 +54,M,01:38:45,03:27:35 +51,M,01:40:46,03:27:35 +37,M,01:29:29,03:27:35 +43,M,01:41:05,03:27:35 +39,M,01:40:30,03:27:35 +38,M,01:37:04,03:27:36 +28,M,01:39:05,03:27:37 +44,W,01:40:18,03:27:38 +25,M,01:37:02,03:27:38 +28,M,01:37:52,03:27:38 +28,W,01:27:19,03:27:39 +62,M,01:39:21,03:27:40 +48,M,01:40:35,03:27:41 +43,M,01:41:28,03:27:42 +42,W,01:40:06,03:27:42 +52,M,01:40:47,03:27:42 +41,M,01:37:55,03:27:42 +59,M,01:41:19,03:27:42 +39,M,01:32:29,03:27:43 +38,M,01:35:28,03:27:43 +44,W,01:41:37,03:27:44 +52,M,01:38:46,03:27:45 +43,M,01:37:50,03:27:45 +36,M,01:35:17,03:27:45 +50,M,01:41:26,03:27:46 +47,M,01:40:11,03:27:46 +37,M,01:39:31,03:27:46 +28,W,01:41:44,03:27:46 +47,M,01:41:10,03:27:46 +32,M,01:42:00,03:27:47 +44,M,01:25:29,03:27:47 +29,M,01:35:21,03:27:48 +37,M,01:40:35,03:27:49 +28,W,01:35:23,03:27:49 +30,W,01:33:03,03:27:49 +26,M,01:36:31,03:27:49 +31,M,01:37:17,03:27:49 +54,M,01:32:22,03:27:50 +41,W,01:38:27,03:27:50 +25,W,01:41:06,03:27:50 +29,W,01:38:22,03:27:51 +40,M,01:41:16,03:27:51 +33,W,01:38:29,03:27:52 +40,M,01:39:29,03:27:52 +31,M,01:36:39,03:27:52 +57,M,01:35:32,03:27:53 +42,W,01:40:26,03:27:53 +26,W,01:41:03,03:27:53 +35,M,01:41:03,03:27:54 +26,M,01:36:43,03:27:55 +40,M,01:34:24,03:27:55 +27,M,01:41:22,03:27:56 +35,M,01:31:45,03:27:56 +49,M,01:39:00,03:27:56 +29,M,01:45:40,03:27:57 +51,M,01:35:00,03:27:57 +50,M,01:41:09,03:27:57 +36,M,01:39:04,03:27:58 +51,M,01:36:14,03:27:58 +23,W,01:43:17,03:27:59 +37,M,01:42:05,03:27:59 +36,M,01:38:25,03:27:59 +27,W,01:42:48,03:28:00 +42,M,01:37:29,03:28:00 +33,M,01:41:10,03:28:00 +42,M,01:40:48,03:28:00 +43,M,01:40:06,03:28:00 +50,M,01:41:59,03:28:01 +40,W,01:40:13,03:28:02 +35,M,01:42:51,03:28:02 +31,W,01:43:11,03:28:02 +50,M,01:40:48,03:28:02 +37,M,01:38:46,03:28:02 +40,M,01:40:21,03:28:02 +39,M,01:39:22,03:28:02 +36,M,01:37:13,03:28:02 +49,W,01:41:41,03:28:02 +58,M,01:31:47,03:28:02 +44,M,01:39:49,03:28:03 +38,M,01:39:26,03:28:05 +37,M,01:37:13,03:28:06 +49,M,01:40:58,03:28:06 +49,W,01:40:39,03:28:06 +61,M,01:35:44,03:28:07 +39,M,01:30:07,03:28:07 +44,M,01:44:52,03:28:07 +49,M,01:32:19,03:28:07 +37,M,01:40:19,03:28:09 +45,M,01:38:01,03:28:10 +48,W,01:42:01,03:28:10 +33,M,01:38:40,03:28:10 +55,M,01:42:06,03:28:10 +40,M,01:29:24,03:28:10 +63,M,01:37:29,03:28:11 +38,M,01:34:50,03:28:11 +53,M,01:41:12,03:28:11 +48,M,01:40:38,03:28:12 +61,M,01:39:07,03:28:12 +39,M,01:44:29,03:28:12 +39,M,01:33:24,03:28:12 +36,M,01:38:06,03:28:12 +43,M,01:39:04,03:28:14 +44,M,01:41:17,03:28:14 +38,W,01:40:22,03:28:14 +32,M,01:39:14,03:28:14 +40,M,01:29:55,03:28:15 +43,M,01:36:54,03:28:15 +57,M,01:36:00,03:28:15 +31,M,01:42:11,03:28:16 +48,M,01:39:20,03:28:16 +29,M,01:33:34,03:28:16 +26,M,01:39:01,03:28:16 +51,M,01:40:21,03:28:16 +49,M,01:42:20,03:28:17 +37,M,01:37:50,03:28:17 +42,W,01:41:25,03:28:17 +44,M,01:40:20,03:28:17 +43,M,01:35:09,03:28:18 +40,M,01:42:48,03:28:18 +33,W,01:35:57,03:28:19 +31,W,01:41:19,03:28:19 +35,M,01:32:11,03:28:20 +56,M,01:38:28,03:28:20 +39,M,01:40:12,03:28:20 +27,M,01:30:56,03:28:21 +52,M,01:36:16,03:28:21 +43,M,01:40:53,03:28:21 +38,M,01:38:25,03:28:22 +39,M,01:33:44,03:28:22 +31,W,01:38:53,03:28:23 +56,M,01:37:48,03:28:23 +38,W,01:43:48,03:28:23 +43,M,01:34:17,03:28:23 +56,M,01:39:57,03:28:24 +44,M,01:35:27,03:28:24 +47,M,01:39:24,03:28:25 +52,M,01:30:15,03:28:25 +48,M,01:31:26,03:28:25 +32,M,01:39:56,03:28:25 +47,M,01:35:38,03:28:26 +30,W,01:38:51,03:28:26 +28,M,01:33:15,03:28:26 +45,M,01:41:05,03:28:26 +49,M,01:41:31,03:28:26 +39,M,01:39:00,03:28:26 +46,M,01:42:58,03:28:26 +42,M,01:40:45,03:28:27 +48,M,01:36:31,03:28:27 +41,W,01:36:26,03:28:27 +56,W,01:39:26,03:28:28 +43,M,01:41:04,03:28:28 +45,M,01:38:42,03:28:28 +34,M,01:42:10,03:28:29 +36,W,01:42:01,03:28:30 +42,M,01:37:45,03:28:30 +57,M,01:42:31,03:28:31 +34,M,01:37:54,03:28:31 +36,M,01:41:13,03:28:31 +31,W,01:39:21,03:28:32 +49,W,01:40:14,03:28:32 +50,M,01:32:48,03:28:33 +34,W,01:41:32,03:28:33 +30,M,01:29:35,03:28:33 +28,W,01:38:43,03:28:35 +27,W,01:32:44,03:28:35 +35,M,01:42:45,03:28:35 +36,W,01:39:37,03:28:35 +36,M,01:39:48,03:28:35 +41,W,01:42:13,03:28:35 +40,M,01:39:02,03:28:36 +29,M,01:42:05,03:28:36 +43,W,01:41:18,03:28:36 +45,M,01:31:53,03:28:36 +55,M,01:39:57,03:28:36 +35,M,01:40:20,03:28:37 +40,M,01:42:58,03:28:37 +42,M,01:36:50,03:28:37 +26,W,01:40:29,03:28:38 +28,W,01:38:53,03:28:38 +40,W,01:41:17,03:28:38 +37,W,01:34:45,03:28:38 +27,W,01:38:06,03:28:39 +34,M,01:38:31,03:28:39 +42,W,01:39:24,03:28:40 +50,M,01:42:07,03:28:40 +45,W,01:36:23,03:28:41 +49,M,01:34:11,03:28:41 +37,M,01:37:40,03:28:41 +41,M,01:35:38,03:28:42 +42,M,01:42:39,03:28:42 +31,M,01:39:16,03:28:43 +54,M,01:41:13,03:28:43 +57,M,01:38:20,03:28:43 +30,M,01:37:43,03:28:44 +45,W,01:40:34,03:28:44 +47,M,01:42:09,03:28:44 +48,M,01:42:28,03:28:45 +41,M,01:37:29,03:28:45 +33,M,01:38:46,03:28:46 +36,W,01:37:01,03:28:46 +39,M,01:40:42,03:28:46 +49,M,01:37:35,03:28:48 +26,W,01:42:43,03:28:48 +51,M,01:35:28,03:28:48 +43,M,01:40:35,03:28:48 +36,M,01:37:56,03:28:48 +54,M,01:34:41,03:28:49 +41,W,01:42:19,03:28:49 +37,M,01:40:55,03:28:49 +42,W,01:42:51,03:28:49 +31,M,01:43:33,03:28:49 +27,W,01:42:01,03:28:49 +47,M,01:35:48,03:28:50 +25,M,01:37:03,03:28:50 +43,M,01:30:40,03:28:50 +28,W,01:44:41,03:28:50 +31,M,01:32:04,03:28:50 +44,W,01:43:45,03:28:50 +31,M,01:39:30,03:28:51 +29,M,01:44:09,03:28:51 +29,M,01:43:31,03:28:51 +43,M,01:40:59,03:28:52 +35,M,01:39:30,03:28:52 +30,M,01:44:29,03:28:52 +39,M,01:42:00,03:28:52 +44,M,01:42:38,03:28:53 +56,M,01:39:24,03:28:54 +35,W,01:42:33,03:28:54 +39,M,01:40:53,03:28:54 +53,M,01:41:38,03:28:55 +42,M,01:42:39,03:28:55 +44,W,01:38:45,03:28:55 +54,M,01:37:13,03:28:56 +30,M,01:36:49,03:28:57 +45,W,01:41:31,03:28:57 +34,M,01:39:24,03:28:57 +43,M,01:40:08,03:28:58 +37,M,01:35:12,03:28:59 +36,W,01:39:36,03:28:59 +49,M,01:39:42,03:29:00 +32,M,01:40:44,03:29:00 +31,M,01:38:53,03:29:00 +45,M,01:39:38,03:29:00 +41,M,01:40:54,03:29:01 +49,M,01:42:10,03:29:01 +41,M,01:44:09,03:29:02 +54,W,01:41:15,03:29:04 +48,M,01:42:43,03:29:04 +56,M,01:40:14,03:29:04 +40,W,01:43:20,03:29:04 +28,W,01:43:30,03:29:05 +27,M,01:42:04,03:29:06 +40,M,01:37:31,03:29:06 +36,M,01:35:13,03:29:06 +49,M,01:36:21,03:29:06 +57,M,01:37:51,03:29:07 +48,W,01:40:01,03:29:07 +33,M,01:37:57,03:29:08 +43,M,01:34:13,03:29:08 +32,M,01:40:57,03:29:08 +40,M,01:35:40,03:29:09 +34,M,01:36:34,03:29:09 +27,W,01:40:30,03:29:09 +38,M,01:44:24,03:29:10 +50,M,01:40:47,03:29:10 +34,M,01:32:48,03:29:11 +34,M,01:36:38,03:29:11 +50,M,01:39:42,03:29:12 +42,M,01:41:04,03:29:12 +39,M,01:40:15,03:29:12 +44,M,01:37:53,03:29:13 +62,M,01:35:30,03:29:13 +28,M,01:29:28,03:29:14 +32,M,01:37:38,03:29:14 +53,M,01:33:56,03:29:15 +46,M,01:38:08,03:29:15 +38,M,01:41:18,03:29:15 +38,M,01:39:50,03:29:16 +45,M,01:38:46,03:29:16 +40,M,01:33:32,03:29:16 +31,W,01:42:37,03:29:16 +58,M,01:43:37,03:29:16 +39,M,01:27:44,03:29:17 +35,M,01:40:12,03:29:17 +49,W,01:41:58,03:29:17 +43,W,01:40:24,03:29:18 +40,M,01:34:13,03:29:18 +26,M,01:40:58,03:29:18 +42,M,01:37:33,03:29:18 +30,M,01:30:36,03:29:19 +46,M,01:42:32,03:29:20 +31,M,01:41:22,03:29:20 +55,M,01:31:45,03:29:21 +29,W,01:32:08,03:29:21 +48,M,01:34:52,03:29:21 +19,M,01:40:05,03:29:21 +37,W,01:39:46,03:29:22 +40,M,01:42:45,03:29:22 +38,M,01:36:57,03:29:23 +42,M,01:38:44,03:29:23 +47,M,01:43:14,03:29:23 +30,M,01:41:31,03:29:25 +32,W,01:43:20,03:29:25 +30,M,01:41:49,03:29:26 +35,M,01:39:21,03:29:26 +25,M,01:41:59,03:29:26 +53,M,01:40:55,03:29:27 +37,M,01:38:23,03:29:27 +37,M,01:35:04,03:29:27 +38,M,01:41:32,03:29:29 +32,M,01:37:34,03:29:29 +31,M,01:40:15,03:29:29 +41,M,01:40:04,03:29:30 +38,M,01:43:44,03:29:30 +28,M,01:38:51,03:29:31 +28,M,01:41:45,03:29:31 +33,M,01:38:43,03:29:31 +48,W,01:40:30,03:29:31 +33,M,01:41:14,03:29:33 +32,W,01:38:41,03:29:33 +32,W,01:40:29,03:29:33 +42,W,01:40:58,03:29:34 +51,M,01:38:00,03:29:34 +24,M,01:37:48,03:29:34 +44,M,01:43:28,03:29:34 +38,W,01:43:32,03:29:34 +24,M,01:41:36,03:29:34 +40,M,01:44:49,03:29:34 +37,M,01:43:06,03:29:35 +26,M,01:44:03,03:29:35 +24,W,01:39:55,03:29:35 +36,W,01:41:56,03:29:36 +28,W,01:42:28,03:29:36 +53,M,01:36:42,03:29:36 +58,M,01:41:29,03:29:37 +46,M,01:32:34,03:29:38 +24,W,01:35:28,03:29:39 +54,M,01:40:06,03:29:39 +53,M,01:41:26,03:29:39 +45,W,01:43:03,03:29:41 +34,M,01:37:57,03:29:41 +53,M,01:41:00,03:29:41 +35,M,01:43:37,03:29:42 +45,M,01:40:17,03:29:42 +53,M,01:33:44,03:29:42 +33,M,01:28:58,03:29:42 +61,M,01:32:14,03:29:43 +41,M,01:38:27,03:29:43 +37,M,01:42:32,03:29:43 +40,M,01:39:17,03:29:43 +48,M,01:41:33,03:29:44 +47,M,01:39:42,03:29:44 +40,W,01:40:12,03:29:44 +36,M,01:38:30,03:29:45 +33,W,01:37:15,03:29:45 +44,M,01:38:34,03:29:46 +38,M,01:35:38,03:29:46 +34,M,01:37:51,03:29:46 +46,W,01:38:20,03:29:46 +28,W,01:41:49,03:29:47 +42,M,01:43:05,03:29:47 +56,M,01:41:25,03:29:47 +43,M,01:38:15,03:29:48 +36,M,01:43:41,03:29:48 +30,M,01:41:48,03:29:49 +40,M,01:44:45,03:29:49 +42,M,01:40:26,03:29:50 +30,W,01:41:23,03:29:50 +37,M,01:37:29,03:29:50 +48,M,01:36:02,03:29:51 +42,M,01:37:20,03:29:51 +44,M,01:41:48,03:29:51 +42,M,01:40:51,03:29:51 +33,M,01:38:22,03:29:52 +51,M,01:35:34,03:29:52 +42,M,01:38:36,03:29:52 +56,M,01:40:28,03:29:53 +35,M,01:38:37,03:29:53 +37,W,01:42:28,03:29:53 +50,M,01:41:52,03:29:54 +55,M,01:44:41,03:29:55 +39,M,01:38:19,03:29:56 +49,M,01:41:22,03:29:56 +46,M,01:42:55,03:29:56 +47,M,01:35:33,03:29:57 +31,M,01:36:03,03:29:57 +52,M,01:42:00,03:29:57 +38,W,01:40:20,03:29:57 +38,W,01:39:27,03:29:58 +36,M,01:39:50,03:29:58 +60,M,01:40:56,03:29:58 +56,M,01:40:04,03:29:58 +39,M,01:41:35,03:29:58 +45,M,01:38:16,03:29:59 +30,M,01:40:51,03:29:59 +58,M,01:43:05,03:29:59 +40,M,01:37:41,03:29:59 +39,M,01:41:56,03:29:59 +44,W,01:39:20,03:30:00 +59,M,01:37:27,03:30:00 +45,W,01:44:15,03:30:00 +38,M,01:44:43,03:30:00 +41,M,01:38:32,03:30:01 +42,M,01:43:25,03:30:01 +35,M,01:37:45,03:30:02 +36,W,01:41:44,03:30:03 +45,M,01:35:45,03:30:03 +32,M,01:45:40,03:30:03 +43,M,01:29:55,03:30:04 +31,M,01:39:29,03:30:04 +51,M,01:32:35,03:30:04 +48,M,01:39:08,03:30:04 +49,M,01:37:02,03:30:04 +34,M,01:40:26,03:30:05 +57,M,01:34:04,03:30:05 +44,M,01:38:53,03:30:05 +31,W,01:42:30,03:30:05 +45,M,01:42:49,03:30:06 +54,M,01:47:50,03:30:07 +42,M,01:42:18,03:30:07 +50,M,01:38:23,03:30:08 +48,M,01:38:18,03:30:08 +45,M,01:41:55,03:30:08 +41,W,01:40:31,03:30:08 +41,M,01:44:29,03:30:09 +29,M,01:41:14,03:30:10 +37,M,01:42:09,03:30:10 +51,M,01:42:30,03:30:11 +46,M,01:43:06,03:30:12 +58,M,01:42:31,03:30:12 +41,W,01:38:23,03:30:12 +55,M,01:44:23,03:30:12 +26,M,01:42:01,03:30:12 +30,M,01:43:43,03:30:13 +56,M,01:39:12,03:30:13 +43,W,01:41:51,03:30:14 +45,M,01:42:59,03:30:14 +42,M,01:35:19,03:30:14 +41,M,01:40:01,03:30:15 +48,M,01:46:07,03:30:15 +24,W,01:35:57,03:30:15 +42,W,01:39:31,03:30:16 +47,M,01:40:43,03:30:16 +57,M,01:37:41,03:30:16 +46,M,01:42:08,03:30:17 +30,W,01:42:15,03:30:17 +41,M,01:38:54,03:30:17 +45,M,01:39:37,03:30:17 +55,W,01:44:50,03:30:17 +37,M,01:33:40,03:30:17 +45,M,01:41:24,03:30:18 +44,M,01:41:42,03:30:18 +30,M,01:33:39,03:30:19 +29,W,01:40:08,03:30:19 +67,M,01:41:30,03:30:19 +29,M,01:45:25,03:30:19 +41,M,01:41:41,03:30:20 +41,M,01:43:00,03:30:20 +32,W,01:42:36,03:30:21 +47,M,01:41:39,03:30:21 +36,M,01:43:06,03:30:21 +33,M,01:39:25,03:30:21 +41,W,01:40:10,03:30:21 +34,M,01:38:30,03:30:21 +48,M,01:38:16,03:30:22 +32,M,01:43:19,03:30:22 +35,W,01:38:19,03:30:22 +57,M,01:41:46,03:30:22 +43,M,01:45:42,03:30:23 +42,M,01:36:35,03:30:23 +43,M,01:38:14,03:30:23 +50,M,01:33:05,03:30:24 +55,M,01:43:45,03:30:24 +22,M,01:44:29,03:30:25 +37,W,01:42:53,03:30:25 +27,M,01:30:51,03:30:25 +49,M,01:38:41,03:30:26 +27,W,01:44:49,03:30:26 +42,W,01:39:35,03:30:26 +39,W,01:42:58,03:30:27 +31,W,01:43:54,03:30:27 +38,W,01:38:08,03:30:27 +31,M,01:41:04,03:30:28 +37,M,01:35:53,03:30:28 +46,M,01:39:13,03:30:29 +33,M,01:34:32,03:30:29 +35,M,01:43:09,03:30:29 +51,M,01:36:28,03:30:29 +46,M,01:38:22,03:30:30 +51,M,01:42:31,03:30:30 +27,M,01:45:12,03:30:30 +38,W,01:39:20,03:30:30 +35,W,01:40:31,03:30:31 +42,M,01:42:11,03:30:31 +25,W,01:41:14,03:30:31 +38,W,01:41:32,03:30:32 +41,M,01:34:50,03:30:32 +41,M,01:37:49,03:30:32 +45,W,01:38:46,03:30:32 +24,M,01:40:13,03:30:32 +28,M,01:43:54,03:30:33 +43,M,01:38:07,03:30:33 +45,W,01:42:16,03:30:34 +31,M,01:41:49,03:30:34 +32,M,01:40:35,03:30:34 +44,M,01:39:27,03:30:35 +38,M,01:41:22,03:30:36 +46,W,01:43:21,03:30:37 +56,M,01:40:29,03:30:38 +58,M,01:39:32,03:30:38 +34,M,01:42:05,03:30:39 +49,W,01:44:52,03:30:40 +32,M,01:43:38,03:30:40 +42,W,01:41:38,03:30:40 +33,M,01:20:39,03:30:40 +54,M,01:33:39,03:30:40 +51,W,01:40:56,03:30:41 +46,M,01:43:45,03:30:41 +25,M,01:41:19,03:30:41 +25,W,01:40:38,03:30:41 +51,M,01:41:58,03:30:42 +41,M,01:43:16,03:30:43 +38,M,01:38:40,03:30:43 +42,M,01:45:09,03:30:43 +46,M,01:41:50,03:30:43 +56,M,01:37:21,03:30:44 +29,W,01:39:01,03:30:45 +40,M,01:37:19,03:30:45 +29,M,01:41:09,03:30:45 +47,W,01:40:02,03:30:45 +42,M,01:38:55,03:30:46 +50,M,01:41:42,03:30:46 +45,M,01:38:25,03:30:46 +46,M,01:44:53,03:30:46 +28,M,01:37:31,03:30:46 +45,M,01:35:59,03:30:47 +31,M,01:37:12,03:30:48 +47,W,01:44:03,03:30:48 +33,W,01:39:30,03:30:48 +66,M,01:44:32,03:30:48 +65,M,01:42:02,03:30:49 +40,M,01:43:00,03:30:49 +39,M,01:43:57,03:30:49 +46,M,01:43:58,03:30:49 +32,W,01:44:01,03:30:50 +40,M,01:38:04,03:30:50 +45,M,01:45:30,03:30:51 +31,M,01:40:45,03:30:51 +49,M,01:40:13,03:30:52 +50,M,01:36:01,03:30:52 +34,M,01:37:42,03:30:52 +53,W,01:43:19,03:30:53 +54,M,01:43:54,03:30:53 +30,M,01:35:49,03:30:54 +51,M,01:37:39,03:30:54 +25,W,01:40:43,03:30:55 +41,M,01:39:04,03:30:55 +41,M,01:43:19,03:30:56 +43,W,01:42:49,03:30:56 +43,W,01:39:00,03:30:57 +38,W,01:44:28,03:30:57 +48,M,01:40:58,03:30:57 +53,M,01:37:21,03:30:57 +50,M,01:25:41,03:30:57 +36,W,01:39:11,03:30:57 +26,M,01:37:29,03:30:57 +33,M,01:44:34,03:30:57 +30,M,01:28:05,03:30:58 +28,W,01:38:37,03:30:58 +46,M,01:38:46,03:30:58 +27,W,01:42:24,03:30:59 +39,W,01:43:25,03:30:59 +32,W,01:40:13,03:30:59 +30,M,01:35:12,03:31:00 +43,M,01:45:45,03:31:00 +20,M,01:38:40,03:31:00 +48,M,01:39:06,03:31:01 +55,M,01:41:33,03:31:01 +46,M,01:43:50,03:31:01 +33,M,01:43:29,03:31:01 +48,W,01:40:05,03:31:01 +26,W,01:43:51,03:31:02 +38,M,01:39:40,03:31:02 +29,M,01:39:11,03:31:03 +33,W,01:39:20,03:31:03 +35,M,01:40:28,03:31:03 +35,W,01:38:32,03:31:04 +46,M,01:40:16,03:31:05 +27,M,01:45:15,03:31:06 +56,M,01:40:16,03:31:06 +49,M,01:41:32,03:31:06 +45,M,01:35:56,03:31:07 +55,M,01:35:18,03:31:07 +43,M,01:42:05,03:31:07 +32,M,01:42:10,03:31:07 +44,M,01:41:15,03:31:08 +36,M,01:43:21,03:31:09 +53,M,01:43:45,03:31:09 +40,W,01:41:35,03:31:10 +44,M,01:43:49,03:31:11 +43,M,01:37:53,03:31:12 +45,M,01:43:13,03:31:12 +34,M,01:39:36,03:31:12 +48,M,01:40:19,03:31:12 +38,W,01:42:07,03:31:12 +40,W,01:43:16,03:31:13 +41,M,01:40:36,03:31:13 +33,M,01:43:42,03:31:13 +52,M,01:46:56,03:31:14 +42,M,01:41:08,03:31:14 +32,M,01:35:38,03:31:15 +48,M,01:42:27,03:31:15 +49,M,01:33:17,03:31:15 +44,M,01:38:38,03:31:15 +47,M,01:42:30,03:31:16 +39,M,01:39:15,03:31:16 +38,M,01:36:58,03:31:16 +33,M,01:45:34,03:31:16 +51,M,01:37:38,03:31:17 +37,M,01:43:20,03:31:17 +39,W,01:42:34,03:31:17 +46,M,01:38:20,03:31:18 +40,M,01:40:00,03:31:18 +48,M,01:35:16,03:31:18 +30,M,01:44:22,03:31:19 +27,W,01:41:27,03:31:19 +49,M,01:41:32,03:31:19 +32,M,01:35:31,03:31:19 +34,M,01:43:47,03:31:20 +32,M,01:42:24,03:31:20 +53,M,01:40:55,03:31:21 +26,W,01:45:50,03:31:21 +44,M,01:43:28,03:31:22 +43,M,01:43:36,03:31:22 +60,M,01:41:38,03:31:23 +52,M,01:41:40,03:31:24 +37,M,01:33:29,03:31:24 +47,M,01:42:18,03:31:25 +27,M,01:29:24,03:31:25 +43,M,01:38:02,03:31:25 +45,M,01:35:26,03:31:25 +26,M,01:32:04,03:31:26 +43,M,01:46:11,03:31:26 +48,M,01:38:42,03:31:27 +53,M,01:42:38,03:31:27 +39,W,01:40:50,03:31:28 +52,M,01:43:51,03:31:28 +42,M,01:41:24,03:31:28 +37,M,01:35:51,03:31:28 +47,M,01:35:51,03:31:28 +43,M,01:44:53,03:31:28 +42,M,01:41:09,03:31:29 +41,M,01:40:01,03:31:29 +26,M,01:41:29,03:31:29 +44,M,01:44:57,03:31:29 +51,M,01:31:54,03:31:29 +32,M,01:31:04,03:31:30 +46,M,01:41:32,03:31:30 +42,M,01:43:30,03:31:31 +43,M,01:44:15,03:31:32 +40,M,01:43:07,03:31:32 +39,W,01:39:30,03:31:32 +34,M,01:47:26,03:31:33 +29,W,01:41:43,03:31:34 +42,M,01:34:47,03:31:34 +20,M,01:44:31,03:31:35 +24,M,01:40:58,03:31:35 +25,M,01:29:24,03:31:35 +41,M,01:41:05,03:31:35 +30,W,01:38:32,03:31:35 +41,M,01:40:50,03:31:35 +49,M,01:36:48,03:31:36 +29,M,01:43:39,03:31:36 +33,M,01:37:22,03:31:37 +40,M,01:37:58,03:31:37 +42,W,01:43:57,03:31:37 +28,M,01:42:43,03:31:38 +58,M,01:40:44,03:31:38 +44,M,01:45:06,03:31:38 +38,M,01:45:00,03:31:38 +46,M,01:42:14,03:31:39 +41,W,01:45:16,03:31:39 +34,W,01:38:59,03:31:39 +25,M,01:40:32,03:31:40 +49,M,01:44:57,03:31:41 +39,M,01:45:47,03:31:41 +38,M,01:44:57,03:31:41 +30,M,01:42:26,03:31:41 +36,M,01:39:04,03:31:42 +39,M,01:39:09,03:31:42 +29,W,01:40:40,03:31:42 +41,W,01:41:41,03:31:42 +39,W,01:41:51,03:31:42 +52,M,01:38:08,03:31:43 +26,M,01:39:58,03:31:43 +21,W,01:32:08,03:31:43 +43,W,01:41:53,03:31:45 +44,M,01:44:33,03:31:46 +42,W,01:42:30,03:31:46 +37,M,01:44:06,03:31:46 +36,W,01:45:24,03:31:46 +52,M,01:42:02,03:31:47 +27,W,01:44:45,03:31:47 +51,M,01:40:15,03:31:48 +27,M,01:40:37,03:31:48 +45,M,01:41:47,03:31:48 +36,M,01:37:09,03:31:49 +46,M,01:39:25,03:31:49 +47,M,01:43:23,03:31:49 +39,M,01:35:42,03:31:49 +48,M,01:38:47,03:31:51 +42,W,01:41:29,03:31:52 +43,W,01:39:36,03:31:52 +44,M,01:44:20,03:31:52 +39,W,01:39:11,03:31:52 +45,M,01:40:10,03:31:53 +50,M,01:40:37,03:31:54 +60,M,01:33:44,03:31:55 +39,M,01:37:51,03:31:56 +37,M,01:41:11,03:31:56 +46,M,01:38:45,03:31:56 +38,M,01:37:03,03:31:57 +54,M,01:41:08,03:31:57 +31,W,01:38:03,03:31:57 +46,M,01:42:39,03:31:57 +34,M,01:28:59,03:31:57 +43,M,01:28:04,03:31:57 +42,M,01:44:14,03:31:59 +36,M,01:41:12,03:31:59 +23,M,01:40:08,03:31:59 +43,M,01:44:51,03:32:00 +33,M,01:38:55,03:32:00 +29,M,01:38:58,03:32:00 +30,M,01:43:25,03:32:00 +37,M,01:44:45,03:32:01 +48,M,01:44:45,03:32:01 +38,M,01:38:19,03:32:02 +29,M,01:49:56,03:32:02 +38,W,01:40:36,03:32:03 +36,M,01:35:34,03:32:03 +35,M,01:36:34,03:32:03 +35,W,01:44:13,03:32:04 +41,W,01:42:20,03:32:04 +42,M,01:41:40,03:32:05 +35,M,01:37:36,03:32:05 +29,W,01:37:39,03:32:06 +45,M,01:41:35,03:32:06 +51,W,01:42:15,03:32:07 +48,M,01:38:41,03:32:07 +46,M,01:39:05,03:32:07 +47,M,01:42:46,03:32:08 +45,M,01:36:31,03:32:09 +45,M,01:43:41,03:32:10 +39,M,01:39:22,03:32:10 +30,W,01:38:58,03:32:10 +46,M,01:36:03,03:32:11 +27,W,01:40:11,03:32:11 +45,M,01:36:18,03:32:12 +27,M,01:42:56,03:32:12 +23,W,01:37:59,03:32:12 +53,M,01:41:10,03:32:12 +46,M,01:45:00,03:32:12 +38,M,01:35:45,03:32:13 +38,M,01:45:17,03:32:13 +41,M,01:34:21,03:32:13 +42,M,01:44:40,03:32:13 +39,M,01:30:04,03:32:14 +26,W,01:35:33,03:32:14 +43,W,01:41:06,03:32:14 +27,M,01:45:36,03:32:15 +43,M,01:30:33,03:32:15 +34,M,01:46:41,03:32:15 +42,M,01:42:24,03:32:15 +33,M,01:34:07,03:32:15 +24,W,01:37:36,03:32:15 +51,M,01:43:06,03:32:16 +41,M,01:41:27,03:32:16 +27,M,01:31:57,03:32:17 +32,M,01:31:56,03:32:17 +44,M,01:46:47,03:32:17 +38,W,01:46:58,03:32:18 +57,M,01:41:35,03:32:18 +28,W,01:45:31,03:32:18 +36,W,01:38:27,03:32:18 +36,M,01:38:52,03:32:19 +32,M,01:40:03,03:32:19 +44,M,01:29:32,03:32:19 +34,M,01:45:13,03:32:19 +34,M,01:39:41,03:32:20 +35,M,01:43:31,03:32:20 +37,M,01:40:49,03:32:21 +35,M,01:41:15,03:32:21 +49,M,01:41:27,03:32:21 +38,M,01:40:50,03:32:22 +32,M,01:45:09,03:32:22 +47,M,01:39:23,03:32:22 +36,M,01:41:38,03:32:22 +26,W,01:39:02,03:32:23 +50,M,01:40:47,03:32:23 +42,M,01:43:06,03:32:24 +39,M,01:37:26,03:32:24 +42,M,01:38:14,03:32:24 +39,M,01:33:56,03:32:24 +41,M,01:46:37,03:32:24 +47,M,01:40:36,03:32:25 +45,M,01:40:00,03:32:25 +57,M,01:41:03,03:32:25 +38,M,01:40:46,03:32:25 +35,M,01:41:08,03:32:26 +38,W,01:43:32,03:32:26 +44,M,01:42:23,03:32:26 +45,M,01:41:51,03:32:26 +33,W,01:39:55,03:32:27 +36,M,01:31:54,03:32:27 +49,M,01:41:55,03:32:27 +41,M,01:43:21,03:32:28 +27,W,01:36:29,03:32:28 +42,M,01:41:24,03:32:28 +30,W,01:41:06,03:32:29 +35,W,01:38:49,03:32:29 +30,M,01:44:14,03:32:29 +25,M,01:32:53,03:32:29 +30,W,01:40:53,03:32:30 +49,M,01:43:00,03:32:30 +45,M,01:35:20,03:32:30 +30,M,01:39:48,03:32:31 +42,M,01:42:49,03:32:32 +36,W,01:41:55,03:32:32 +29,W,01:46:52,03:32:32 +39,M,01:48:57,03:32:33 +38,M,01:32:19,03:32:33 +57,M,01:43:04,03:32:33 +27,M,01:39:43,03:32:34 +45,M,01:43:44,03:32:35 +46,M,01:39:23,03:32:35 +43,M,01:36:19,03:32:35 +47,M,01:43:39,03:32:35 +42,M,01:45:26,03:32:36 +31,M,01:33:24,03:32:36 +24,M,01:41:03,03:32:37 +38,M,01:41:39,03:32:38 +38,M,01:40:42,03:32:38 +36,M,01:40:01,03:32:38 +44,W,01:38:35,03:32:38 +45,M,01:35:43,03:32:38 +43,W,01:48:00,03:32:39 +43,M,01:44:03,03:32:39 +47,M,01:43:26,03:32:39 +27,W,01:45:58,03:32:40 +38,M,01:35:19,03:32:40 +27,W,01:43:43,03:32:42 +42,W,01:44:51,03:32:42 +41,M,01:34:01,03:32:42 +56,M,01:39:13,03:32:43 +46,M,01:44:33,03:32:43 +46,M,01:29:31,03:32:43 +39,M,01:44:27,03:32:43 +45,M,01:35:59,03:32:44 +51,M,01:39:05,03:32:44 +47,M,01:34:07,03:32:45 +28,W,01:45:24,03:32:45 +36,W,01:39:37,03:32:45 +33,M,01:40:57,03:32:46 +22,W,01:43:56,03:32:46 +52,M,01:38:51,03:32:46 +49,M,01:44:13,03:32:47 +56,M,01:41:25,03:32:47 +32,W,01:46:50,03:32:47 +55,M,01:43:46,03:32:47 +49,M,01:31:54,03:32:48 +55,M,01:43:15,03:32:48 +41,W,01:39:03,03:32:48 +26,M,01:42:25,03:32:49 +45,M,01:41:42,03:32:50 +43,M,01:45:59,03:32:51 +46,W,01:43:02,03:32:51 +26,W,01:41:04,03:32:51 +38,M,01:35:19,03:32:51 +29,W,01:41:37,03:32:52 +20,M,01:39:53,03:32:52 +36,M,01:31:53,03:32:53 +46,M,01:40:36,03:32:54 +34,M,01:43:14,03:32:55 +36,M,01:35:20,03:32:55 +38,M,01:41:30,03:32:55 +42,M,01:40:45,03:32:55 +36,M,01:40:55,03:32:56 +50,W,01:40:31,03:32:56 +29,M,01:40:11,03:32:56 +38,M,01:34:12,03:32:56 +28,W,01:38:24,03:32:57 +49,M,01:44:49,03:32:57 +37,M,01:40:16,03:32:59 +47,M,01:40:38,03:32:59 +42,M,01:43:18,03:32:59 +29,M,01:42:00,03:32:59 +44,M,01:44:34,03:33:00 +28,W,01:46:42,03:33:00 +51,M,01:39:50,03:33:00 +30,M,01:43:15,03:33:01 +37,M,01:37:08,03:33:02 +30,M,01:36:45,03:33:02 +49,M,01:39:46,03:33:02 +38,M,01:39:17,03:33:02 +38,M,01:42:16,03:33:04 +40,M,01:36:14,03:33:05 +42,W,01:38:51,03:33:05 +31,M,01:43:32,03:33:06 +31,W,01:43:21,03:33:06 +54,M,01:42:42,03:33:07 +28,M,01:45:06,03:33:08 +52,W,01:43:19,03:33:08 +51,M,01:41:14,03:33:08 +26,M,01:32:14,03:33:09 +39,M,01:43:07,03:33:09 +55,M,01:43:16,03:33:09 +53,M,01:41:49,03:33:10 +38,M,01:40:49,03:33:10 +53,M,01:44:50,03:33:11 +31,M,01:43:58,03:33:11 +30,M,01:42:46,03:33:12 +31,M,01:37:03,03:33:12 +45,M,01:40:28,03:33:13 +36,M,01:40:17,03:33:13 +57,M,01:45:57,03:33:13 +26,M,01:42:36,03:33:13 +44,W,01:42:46,03:33:14 +37,M,01:37:47,03:33:14 +50,M,01:43:43,03:33:14 +41,M,01:38:45,03:33:14 +46,W,01:42:46,03:33:14 +47,M,01:44:26,03:33:14 +31,M,01:36:37,03:33:14 +42,M,01:36:40,03:33:15 +42,M,01:36:13,03:33:15 +53,M,01:38:29,03:33:16 +26,M,01:37:57,03:33:16 +49,M,01:34:31,03:33:16 +52,M,01:43:27,03:33:16 +48,M,01:40:40,03:33:17 +40,W,01:44:46,03:33:17 +44,M,01:46:21,03:33:17 +41,W,01:41:48,03:33:17 +42,M,01:38:39,03:33:18 +31,W,01:41:58,03:33:18 +44,W,01:41:22,03:33:19 +35,W,01:42:05,03:33:19 +31,W,01:44:37,03:33:20 +46,W,01:37:51,03:33:20 +55,M,01:43:58,03:33:21 +42,W,01:42:32,03:33:21 +47,M,01:43:07,03:33:22 +47,W,01:40:35,03:33:22 +36,W,01:38:06,03:33:23 +32,W,01:44:13,03:33:23 +39,M,01:42:29,03:33:23 +39,M,01:37:22,03:33:23 +36,M,01:39:23,03:33:24 +31,M,01:42:55,03:33:25 +32,M,01:44:51,03:33:25 +42,M,01:43:51,03:33:25 +48,M,01:43:45,03:33:25 +29,W,01:40:09,03:33:26 +33,M,01:42:54,03:33:26 +33,W,01:39:51,03:33:26 +54,W,01:41:54,03:33:26 +30,M,01:44:08,03:33:26 +53,M,01:40:51,03:33:27 +34,M,01:41:16,03:33:28 +43,M,01:30:16,03:33:28 +33,W,01:40:42,03:33:28 +49,M,01:37:53,03:33:28 +32,W,01:39:07,03:33:29 +50,M,01:44:40,03:33:29 +39,M,01:39:23,03:33:30 +32,M,01:37:38,03:33:30 +31,M,01:39:12,03:33:30 +46,W,01:39:53,03:33:31 +48,M,01:35:58,03:33:31 +41,M,01:40:59,03:33:31 +44,M,01:44:37,03:33:32 +43,M,01:42:51,03:33:33 +54,M,01:45:35,03:33:33 +36,M,01:45:06,03:33:34 +35,M,01:43:24,03:33:34 +43,M,01:38:35,03:33:34 +30,W,01:43:49,03:33:34 +53,M,01:42:51,03:33:35 +45,W,01:41:34,03:33:35 +40,M,01:44:34,03:33:36 +46,M,01:44:12,03:33:36 +52,M,01:40:51,03:33:36 +36,M,01:42:18,03:33:36 +48,M,01:42:17,03:33:36 +37,W,01:40:59,03:33:37 +36,M,01:42:52,03:33:37 +31,M,01:34:04,03:33:37 +37,M,01:37:40,03:33:38 +45,M,01:35:58,03:33:38 +51,M,01:45:07,03:33:38 +47,M,01:41:31,03:33:40 +31,M,01:44:51,03:33:40 +30,M,01:45:37,03:33:40 +43,M,01:46:12,03:33:41 +30,M,01:34:27,03:33:41 +26,W,01:43:00,03:33:41 +46,M,01:40:07,03:33:41 +29,W,01:40:28,03:33:41 +56,M,01:41:29,03:33:42 +37,M,01:37:38,03:33:42 +57,M,01:44:06,03:33:42 +41,M,01:40:32,03:33:42 +49,M,01:37:57,03:33:42 +45,W,01:44:03,03:33:43 +31,W,01:44:33,03:33:43 +37,M,01:43:12,03:33:43 +30,M,01:42:56,03:33:43 +30,M,01:38:01,03:33:43 +38,M,01:42:32,03:33:43 +51,M,01:43:55,03:33:43 +53,M,01:40:40,03:33:44 +25,W,01:42:46,03:33:44 +39,M,01:40:42,03:33:45 +29,W,01:40:55,03:33:45 +54,M,01:44:43,03:33:45 +36,M,01:46:46,03:33:45 +41,M,01:45:42,03:33:45 +40,M,01:41:00,03:33:45 +39,M,01:41:54,03:33:46 +42,M,01:39:40,03:33:46 +22,M,01:44:03,03:33:47 +35,M,01:34:38,03:33:48 +42,M,01:40:14,03:33:48 +47,M,01:40:45,03:33:49 +51,M,01:41:02,03:33:49 +38,W,01:44:29,03:33:50 +55,M,01:36:36,03:33:51 +33,M,01:37:28,03:33:52 +34,M,01:38:41,03:33:52 +34,W,01:43:52,03:33:52 +47,M,01:44:29,03:33:52 +39,M,01:28:13,03:33:53 +42,M,01:42:12,03:33:53 +43,M,01:40:18,03:33:54 +42,M,01:43:56,03:33:55 +42,M,01:42:46,03:33:55 +22,M,01:30:48,03:33:55 +25,M,01:36:45,03:33:56 +41,M,01:37:57,03:33:56 +39,M,01:30:44,03:33:56 +45,M,01:38:50,03:33:56 +40,M,01:40:55,03:33:57 +56,M,01:43:35,03:33:57 +40,M,01:44:52,03:33:57 +42,W,01:42:01,03:33:58 +32,W,01:45:38,03:33:58 +45,M,01:42:27,03:33:58 +40,W,01:42:16,03:33:58 +52,M,01:44:30,03:33:58 +28,W,01:38:36,03:33:58 +34,W,01:41:02,03:33:58 +36,M,01:44:34,03:33:59 +31,M,01:43:18,03:33:59 +26,M,01:48:07,03:34:00 +52,M,01:41:25,03:34:01 +35,M,01:39:25,03:34:01 +27,M,01:37:08,03:34:02 +40,M,01:39:01,03:34:02 +57,M,01:42:20,03:34:02 +30,W,01:40:31,03:34:03 +37,W,01:41:47,03:34:03 +29,M,01:43:42,03:34:03 +53,M,01:40:34,03:34:04 +41,M,01:43:19,03:34:05 +32,M,01:42:21,03:34:05 +37,M,01:46:51,03:34:06 +27,W,01:43:53,03:34:07 +41,M,01:44:50,03:34:07 +39,W,01:43:24,03:34:08 +29,W,01:39:04,03:34:09 +30,M,01:42:59,03:34:09 +60,M,01:38:20,03:34:09 +24,W,01:43:48,03:34:10 +33,M,01:43:58,03:34:10 +45,M,01:42:25,03:34:10 +45,W,01:45:23,03:34:10 +31,M,01:36:57,03:34:11 +62,M,01:45:01,03:34:11 +36,M,01:39:54,03:34:12 +37,W,01:44:29,03:34:12 +52,M,01:36:24,03:34:12 +40,M,01:42:33,03:34:12 +34,M,01:43:18,03:34:15 +54,M,01:43:11,03:34:15 +27,W,01:43:33,03:34:16 +32,W,01:31:29,03:34:16 +35,M,01:35:51,03:34:16 +54,M,01:41:44,03:34:16 +46,M,01:42:39,03:34:17 +37,M,01:45:01,03:34:17 +31,M,01:44:08,03:34:18 +46,M,01:41:23,03:34:18 +28,M,01:33:18,03:34:18 +33,M,01:37:12,03:34:18 +37,M,01:43:20,03:34:20 +45,M,01:41:20,03:34:20 +41,M,01:37:49,03:34:22 +36,M,01:45:19,03:34:22 +44,W,01:42:32,03:34:22 +47,M,01:41:01,03:34:22 +26,W,01:41:28,03:34:23 +43,M,01:38:59,03:34:23 +39,M,01:33:59,03:34:23 +30,W,01:39:01,03:34:24 +40,W,01:42:25,03:34:25 +45,W,01:45:58,03:34:25 +28,M,01:38:47,03:34:25 +44,M,01:42:06,03:34:25 +38,M,01:42:57,03:34:26 +36,M,01:44:45,03:34:26 +42,M,01:41:50,03:34:27 +33,M,01:39:31,03:34:27 +47,W,01:45:44,03:34:27 +33,M,01:40:49,03:34:27 +26,M,01:41:58,03:34:27 +44,M,01:45:03,03:34:28 +24,W,01:43:07,03:34:28 +42,M,01:42:05,03:34:29 +42,M,01:41:43,03:34:29 +42,M,01:42:18,03:34:29 +25,W,01:43:48,03:34:30 +36,M,01:43:43,03:34:31 +48,M,01:34:55,03:34:31 +43,M,01:39:23,03:34:31 +33,M,01:43:12,03:34:32 +54,M,01:44:29,03:34:32 +36,M,01:43:23,03:34:32 +43,M,01:42:36,03:34:32 +33,W,01:44:13,03:34:32 +47,M,01:44:17,03:34:33 +33,M,01:40:33,03:34:33 +42,W,01:44:38,03:34:34 +27,W,01:41:52,03:34:34 +25,M,01:39:36,03:34:34 +52,M,01:43:36,03:34:36 +59,M,01:39:57,03:34:36 +31,M,01:46:09,03:34:36 +50,M,01:37:37,03:34:37 +44,M,01:36:10,03:34:37 +54,M,01:43:39,03:34:37 +25,M,01:40:51,03:34:38 +42,W,01:44:39,03:34:38 +37,W,01:44:05,03:34:38 +28,M,01:35:05,03:34:38 +50,M,01:36:06,03:34:38 +47,M,01:44:01,03:34:39 +44,M,01:42:41,03:34:39 +52,W,01:45:27,03:34:39 +43,W,01:42:46,03:34:39 +25,M,01:43:26,03:34:40 +49,M,01:42:06,03:34:40 +53,M,01:38:05,03:34:40 +36,M,01:43:41,03:34:42 +45,M,01:38:54,03:34:42 +44,M,01:34:21,03:34:42 +31,M,01:35:07,03:34:44 +48,W,01:43:04,03:34:44 +41,M,01:33:13,03:34:45 +48,M,01:34:14,03:34:45 +50,M,01:43:34,03:34:45 +44,M,01:41:03,03:34:45 +45,M,01:38:35,03:34:45 +30,M,01:45:45,03:34:46 +29,W,01:44:19,03:34:46 +34,W,01:41:03,03:34:46 +38,M,01:38:48,03:34:46 +26,M,01:39:24,03:34:47 +29,M,01:45:30,03:34:47 +48,M,01:42:09,03:34:47 +42,W,01:40:48,03:34:47 +48,M,01:45:11,03:34:47 +38,M,01:41:37,03:34:47 +46,M,01:41:01,03:34:48 +59,M,01:37:45,03:34:48 +24,M,01:46:34,03:34:49 +28,M,01:40:50,03:34:49 +52,W,01:39:43,03:34:49 +40,M,01:46:30,03:34:49 +24,M,01:31:49,03:34:49 +54,M,01:40:36,03:34:50 +45,M,01:44:45,03:34:51 +37,M,01:46:04,03:34:51 +43,M,01:47:42,03:34:52 +27,M,01:40:23,03:34:52 +30,M,01:44:49,03:34:52 +27,M,01:34:27,03:34:53 +53,M,01:41:16,03:34:53 +35,W,01:45:29,03:34:53 +39,M,01:44:27,03:34:53 +33,W,01:35:33,03:34:54 +52,M,01:44:20,03:34:54 +49,M,01:43:19,03:34:54 +46,W,01:40:45,03:34:54 +29,M,01:40:55,03:34:55 +43,M,01:45:00,03:34:55 +51,M,01:41:15,03:34:56 +29,W,01:42:24,03:34:58 +55,M,01:33:47,03:34:58 +41,W,01:42:48,03:34:59 +30,M,01:39:22,03:34:59 +35,W,01:40:49,03:35:00 +30,M,01:34:46,03:35:01 +23,M,01:39:37,03:35:02 +44,M,01:45:06,03:35:02 +65,M,01:38:38,03:35:02 +32,W,01:44:41,03:35:04 +54,M,01:37:44,03:35:04 +44,M,01:46:51,03:35:04 +41,M,01:42:43,03:35:05 +26,M,01:44:31,03:35:05 +27,M,01:43:27,03:35:06 +42,M,01:41:57,03:35:06 +42,M,01:38:30,03:35:07 +28,M,01:45:50,03:35:07 +30,M,01:38:35,03:35:08 +34,M,01:38:39,03:35:09 +38,M,01:42:34,03:35:10 +52,M,01:46:52,03:35:10 +34,M,01:45:11,03:35:10 +32,M,01:43:04,03:35:10 +24,M,01:38:04,03:35:10 +48,M,01:46:16,03:35:11 +41,M,01:45:30,03:35:11 +43,M,01:44:14,03:35:12 +36,M,01:37:39,03:35:12 +36,W,01:47:58,03:35:12 +47,M,01:40:59,03:35:12 +34,M,01:39:29,03:35:13 +44,M,01:41:58,03:35:13 +49,M,01:43:36,03:35:13 +61,M,01:37:09,03:35:14 +34,M,01:34:52,03:35:14 +35,M,01:36:59,03:35:14 +34,M,01:33:11,03:35:15 +50,M,01:44:26,03:35:15 +41,W,01:44:41,03:35:16 +43,W,01:40:32,03:35:16 +48,W,01:42:00,03:35:16 +31,M,01:33:25,03:35:16 +40,M,01:43:00,03:35:16 +51,M,01:42:25,03:35:17 +35,M,01:41:08,03:35:17 +50,M,01:40:25,03:35:18 +40,W,01:43:58,03:35:18 +33,M,01:38:50,03:35:19 +47,M,01:43:25,03:35:19 +58,M,01:45:23,03:35:22 +41,M,01:45:22,03:35:22 +32,M,01:40:37,03:35:22 +40,M,01:43:53,03:35:23 +51,M,01:42:53,03:35:23 +49,M,01:41:57,03:35:23 +23,W,01:43:35,03:35:23 +42,M,01:42:31,03:35:23 +37,M,01:44:54,03:35:24 +36,W,01:33:33,03:35:24 +45,M,01:45:10,03:35:25 +37,M,01:39:48,03:35:25 +43,M,01:44:03,03:35:25 +31,M,01:39:58,03:35:25 +51,M,01:41:59,03:35:25 +23,M,01:46:13,03:35:26 +30,M,01:41:31,03:35:26 +27,M,01:34:33,03:35:26 +51,M,01:35:18,03:35:28 +30,M,01:40:29,03:35:28 +53,M,01:45:46,03:35:29 +37,W,01:42:41,03:35:30 +37,M,01:35:56,03:35:31 +56,M,01:40:37,03:35:32 +37,W,01:44:50,03:35:33 +35,M,01:42:39,03:35:33 +33,M,01:43:09,03:35:33 +31,M,01:41:38,03:35:35 +33,W,01:44:44,03:35:35 +35,M,01:41:26,03:35:35 +42,M,01:43:35,03:35:35 +43,W,01:43:06,03:35:36 +44,M,01:43:31,03:35:36 +42,W,01:41:58,03:35:36 +32,M,01:44:02,03:35:37 +50,W,01:44:50,03:35:37 +37,M,01:40:15,03:35:38 +33,W,01:44:26,03:35:38 +38,M,01:45:02,03:35:38 +28,M,01:44:33,03:35:39 +49,M,01:40:48,03:35:39 +45,M,01:38:47,03:35:40 +28,M,01:32:30,03:35:40 +32,M,01:40:39,03:35:41 +50,W,01:41:05,03:35:41 +44,M,01:38:47,03:35:41 +37,W,01:42:08,03:35:41 +51,M,01:42:41,03:35:42 +41,M,01:40:46,03:35:43 +51,M,01:41:42,03:35:43 +30,M,01:44:21,03:35:43 +38,W,01:38:47,03:35:43 +39,M,01:38:22,03:35:44 +46,M,01:46:14,03:35:44 +50,M,01:41:55,03:35:45 +45,M,01:46:11,03:35:45 +39,M,01:40:06,03:35:45 +51,W,01:37:36,03:35:46 +43,M,01:40:35,03:35:46 +38,M,01:39:17,03:35:46 +32,M,01:31:53,03:35:47 +28,M,01:37:45,03:35:48 +48,M,01:39:20,03:35:48 +46,M,01:43:01,03:35:48 +28,M,01:41:52,03:35:48 +28,M,01:43:26,03:35:48 +27,W,01:41:25,03:35:48 +34,M,01:40:02,03:35:49 +24,W,01:38:13,03:35:49 +40,M,01:48:24,03:35:49 +50,M,01:35:40,03:35:49 +40,M,01:45:31,03:35:50 +39,M,01:33:51,03:35:51 +37,M,01:36:38,03:35:51 +36,M,01:40:39,03:35:52 +42,M,01:41:28,03:35:52 +49,M,01:43:09,03:35:52 +33,W,01:41:40,03:35:52 +34,W,01:45:26,03:35:52 +44,M,01:46:21,03:35:53 +44,M,01:43:31,03:35:54 +30,W,01:40:33,03:35:54 +33,W,01:45:35,03:35:54 +35,M,01:42:03,03:35:55 +46,W,01:41:22,03:35:55 +27,M,01:43:47,03:35:55 +37,M,01:40:49,03:35:57 +37,M,01:39:41,03:35:57 +38,M,01:30:15,03:35:57 +38,W,01:44:18,03:35:58 +31,M,01:39:28,03:35:58 +37,M,01:41:45,03:35:59 +37,M,01:43:19,03:35:59 +32,M,01:44:13,03:36:00 +40,M,01:40:22,03:36:00 +39,W,01:42:49,03:36:00 +30,W,01:45:51,03:36:02 +49,M,01:31:45,03:36:02 +25,M,01:31:23,03:36:03 +43,M,01:41:00,03:36:03 +26,W,01:43:09,03:36:05 +49,M,01:38:09,03:36:05 +50,M,01:35:16,03:36:05 +43,M,01:41:01,03:36:05 +44,W,01:44:29,03:36:06 +29,M,01:38:26,03:36:06 +50,M,01:39:04,03:36:08 +37,M,01:47:56,03:36:08 +41,M,01:37:45,03:36:08 +43,M,01:39:16,03:36:09 +44,W,01:40:50,03:36:10 +35,W,01:43:00,03:36:10 +30,M,01:38:53,03:36:10 +36,M,01:47:10,03:36:10 +36,M,01:38:13,03:36:10 +44,M,01:39:53,03:36:11 +30,W,01:45:03,03:36:11 +40,M,01:40:07,03:36:12 +49,M,01:37:39,03:36:13 +34,W,01:40:06,03:36:14 +41,W,01:40:58,03:36:14 +58,M,01:48:24,03:36:15 +25,M,01:45:51,03:36:15 +46,W,01:43:25,03:36:15 +42,M,01:42:13,03:36:16 +43,M,01:47:48,03:36:16 +27,W,01:45:39,03:36:16 +41,M,01:33:59,03:36:16 +41,W,01:42:30,03:36:17 +27,W,01:41:36,03:36:17 +30,M,01:44:32,03:36:17 +30,M,01:37:34,03:36:18 +39,M,01:40:22,03:36:18 +39,W,01:41:51,03:36:19 +40,M,01:43:07,03:36:19 +46,M,01:42:32,03:36:20 +35,W,01:42:18,03:36:20 +37,M,01:41:04,03:36:20 +30,W,01:43:08,03:36:21 +30,M,01:49:08,03:36:22 +43,M,01:44:40,03:36:22 +46,M,01:37:34,03:36:22 +43,W,01:38:46,03:36:22 +42,M,01:44:33,03:36:22 +32,M,01:38:46,03:36:24 +58,M,01:45:34,03:36:24 +40,M,01:43:33,03:36:24 +64,M,01:48:28,03:36:24 +42,M,01:44:49,03:36:25 +51,M,01:36:57,03:36:25 +44,M,01:45:21,03:36:25 +38,M,01:36:07,03:36:25 +32,W,01:44:33,03:36:26 +46,M,01:43:56,03:36:26 +27,W,01:37:35,03:36:27 +47,M,01:40:23,03:36:28 +45,M,01:39:14,03:36:28 +38,W,01:46:03,03:36:28 +45,M,01:44:04,03:36:29 +41,M,01:44:05,03:36:29 +28,M,01:44:50,03:36:29 +55,M,01:44:55,03:36:29 +41,W,01:44:26,03:36:30 +46,M,01:41:42,03:36:30 +30,M,01:33:06,03:36:30 +44,W,01:44:26,03:36:30 +31,M,01:41:10,03:36:30 +44,M,01:40:35,03:36:30 +55,M,01:45:43,03:36:30 +41,W,01:42:13,03:36:31 +53,M,01:42:52,03:36:31 +35,W,01:45:43,03:36:31 +33,M,01:44:14,03:36:31 +32,W,01:42:36,03:36:31 +40,W,01:45:30,03:36:31 +40,M,01:45:36,03:36:32 +56,M,01:44:32,03:36:32 +43,M,01:33:10,03:36:33 +40,W,01:41:42,03:36:33 +43,M,01:44:02,03:36:34 +37,M,01:37:30,03:36:35 +52,M,01:41:39,03:36:35 +35,M,01:43:54,03:36:36 +35,M,01:40:38,03:36:37 +37,M,01:41:43,03:36:37 +41,M,01:41:15,03:36:37 +51,M,01:39:09,03:36:37 +25,M,01:39:16,03:36:37 +27,W,01:42:33,03:36:37 +37,M,01:41:13,03:36:37 +51,M,01:47:24,03:36:38 +39,M,01:41:42,03:36:38 +56,M,01:42:25,03:36:38 +31,W,01:36:01,03:36:38 +33,M,01:29:42,03:36:40 +36,M,01:37:46,03:36:40 +38,M,01:41:02,03:36:40 +46,M,01:46:09,03:36:40 +26,M,01:38:58,03:36:40 +40,M,01:46:08,03:36:40 +45,M,01:35:27,03:36:41 +37,M,01:36:22,03:36:42 +47,W,01:41:13,03:36:42 +29,M,01:45:12,03:36:42 +27,M,01:45:00,03:36:42 +49,M,01:41:32,03:36:42 +51,M,01:41:50,03:36:42 +27,M,01:41:02,03:36:43 +28,M,01:45:19,03:36:43 +36,M,01:41:12,03:36:44 +28,M,01:39:07,03:36:44 +41,M,01:37:23,03:36:44 +45,M,01:37:09,03:36:44 +39,M,01:38:04,03:36:45 +39,M,01:42:42,03:36:45 +47,M,01:31:41,03:36:45 +52,M,01:44:12,03:36:46 +42,M,01:42:36,03:36:46 +51,M,01:43:11,03:36:46 +40,W,01:42:45,03:36:46 +41,W,01:43:40,03:36:46 +42,M,01:45:56,03:36:48 +40,M,01:42:13,03:36:48 +45,M,01:40:27,03:36:48 +45,M,01:44:27,03:36:48 +43,W,01:42:39,03:36:49 +41,W,01:44:13,03:36:49 +42,M,01:41:56,03:36:49 +34,M,01:41:07,03:36:50 +24,M,01:36:56,03:36:50 +38,M,01:46:39,03:36:50 +40,M,01:43:50,03:36:50 +39,M,01:37:15,03:36:51 +38,W,01:37:50,03:36:51 +55,M,01:41:50,03:36:51 +42,W,01:44:26,03:36:51 +41,M,01:43:05,03:36:51 +54,M,01:43:29,03:36:54 +39,M,01:29:50,03:36:54 +31,W,01:43:31,03:36:54 +45,M,01:35:40,03:36:54 +48,M,01:36:43,03:36:55 +53,M,01:44:49,03:36:55 +46,M,01:41:03,03:36:55 +32,M,01:40:32,03:36:57 +45,M,01:45:25,03:36:57 +49,M,01:43:00,03:36:58 +43,M,01:44:18,03:36:58 +27,M,01:44:36,03:36:59 +49,M,01:41:00,03:36:59 +39,M,01:45:09,03:37:00 +23,W,01:41:32,03:37:00 +33,M,01:47:04,03:37:01 +42,W,01:44:10,03:37:01 +43,M,01:32:39,03:37:01 +44,M,01:45:13,03:37:02 +49,M,01:43:45,03:37:02 +37,M,01:44:31,03:37:02 +43,M,01:39:44,03:37:03 +38,M,01:36:40,03:37:03 +50,M,01:40:07,03:37:03 +37,W,01:45:07,03:37:04 +48,M,01:45:09,03:37:04 +40,M,01:42:02,03:37:04 +35,M,01:37:12,03:37:04 +41,W,01:45:51,03:37:04 +25,M,01:40:50,03:37:05 +28,M,01:41:07,03:37:06 +41,W,01:45:57,03:37:06 +32,M,01:35:04,03:37:06 +39,M,01:41:34,03:37:07 +39,W,01:42:27,03:37:08 +31,M,01:46:57,03:37:08 +53,W,01:44:42,03:37:08 +32,M,01:42:28,03:37:10 +42,M,01:41:29,03:37:10 +44,M,01:44:44,03:37:10 +28,M,01:47:00,03:37:10 +31,M,01:50:43,03:37:10 +50,M,01:38:31,03:37:11 +43,M,01:44:30,03:37:11 +43,M,01:43:01,03:37:12 +44,M,01:24:32,03:37:12 +26,W,01:43:05,03:37:12 +57,M,01:43:52,03:37:12 +36,M,01:43:31,03:37:13 +26,W,01:45:13,03:37:13 +47,M,01:46:26,03:37:14 +46,M,01:46:15,03:37:14 +45,M,01:44:54,03:37:14 +50,M,01:35:45,03:37:15 +32,M,01:42:25,03:37:15 +27,M,01:43:28,03:37:15 +35,M,01:42:36,03:37:16 +35,M,01:45:48,03:37:16 +41,M,01:40:10,03:37:16 +33,M,01:47:17,03:37:16 +32,M,01:33:29,03:37:17 +39,M,01:43:42,03:37:19 +41,W,01:43:29,03:37:19 +35,M,01:40:09,03:37:20 +26,M,01:38:42,03:37:20 +31,M,01:43:37,03:37:20 +59,M,01:44:39,03:37:20 +35,M,01:42:01,03:37:20 +51,M,01:41:52,03:37:21 +47,M,01:42:17,03:37:21 +36,M,01:37:04,03:37:21 +35,M,01:31:53,03:37:21 +36,M,01:44:59,03:37:22 +28,M,01:34:36,03:37:22 +37,M,01:41:43,03:37:23 +48,M,01:44:46,03:37:23 +29,W,01:40:27,03:37:24 +47,W,01:44:40,03:37:24 +44,M,01:39:44,03:37:25 +51,M,01:41:00,03:37:26 +35,M,01:38:30,03:37:27 +46,M,01:43:57,03:37:27 +41,M,01:43:17,03:37:28 +38,W,01:45:11,03:37:28 +50,W,01:43:32,03:37:28 +46,M,01:37:45,03:37:29 +40,M,01:34:14,03:37:29 +50,M,01:43:22,03:37:30 +41,M,01:49:23,03:37:32 +47,M,01:39:01,03:37:32 +36,M,01:35:29,03:37:32 +42,M,01:36:44,03:37:32 +33,M,01:36:40,03:37:32 +54,M,01:43:21,03:37:33 +43,W,01:38:12,03:37:33 +38,M,01:44:18,03:37:34 +23,M,01:40:18,03:37:34 +47,W,01:43:13,03:37:34 +21,M,01:45:12,03:37:35 +26,W,01:41:52,03:37:36 +54,M,01:42:03,03:37:36 +41,M,01:31:03,03:37:36 +45,M,01:38:06,03:37:36 +32,W,01:41:25,03:37:38 +46,W,01:46:19,03:37:38 +54,M,01:42:40,03:37:38 +30,M,01:42:19,03:37:39 +44,M,01:41:55,03:37:39 +31,M,01:43:15,03:37:39 +39,M,01:41:41,03:37:39 +29,W,01:43:39,03:37:40 +36,W,01:47:08,03:37:41 +38,M,01:36:07,03:37:41 +51,M,01:46:41,03:37:42 +36,M,01:41:11,03:37:42 +38,M,01:37:59,03:37:43 +30,W,01:45:00,03:37:43 +49,M,01:42:21,03:37:43 +44,M,01:43:37,03:37:43 +49,M,01:42:46,03:37:43 +29,W,01:45:08,03:37:44 +30,M,01:44:25,03:37:46 +45,W,01:46:31,03:37:46 +37,M,01:42:07,03:37:46 +31,M,01:32:07,03:37:46 +25,W,01:43:52,03:37:46 +45,M,01:39:41,03:37:47 +41,M,01:36:10,03:37:47 +30,W,01:43:20,03:37:47 +48,M,01:42:49,03:37:48 +40,M,01:39:22,03:37:48 +53,M,01:38:19,03:37:48 +48,M,01:42:52,03:37:50 +26,M,01:47:49,03:37:50 +41,M,01:35:57,03:37:50 +44,M,01:47:38,03:37:51 +56,M,01:44:16,03:37:51 +49,M,01:42:07,03:37:51 +19,M,01:32:42,03:37:52 +27,W,01:46:00,03:37:52 +45,W,01:46:03,03:37:52 +48,M,01:37:39,03:37:52 +28,W,01:48:02,03:37:52 +38,M,01:39:34,03:37:52 +58,M,01:45:25,03:37:53 +36,M,01:35:23,03:37:54 +54,M,01:43:32,03:37:54 +36,M,01:41:16,03:37:55 +35,W,01:41:32,03:37:55 +35,M,01:43:16,03:37:55 +38,M,01:35:45,03:37:55 +28,W,01:42:35,03:37:56 +44,W,01:42:55,03:37:56 +42,M,01:43:14,03:37:56 +33,M,01:44:42,03:37:57 +51,M,01:45:25,03:37:58 +49,M,01:39:32,03:37:59 +49,M,01:41:19,03:37:59 +46,W,01:42:46,03:37:59 +40,M,01:38:23,03:38:00 +31,M,01:45:03,03:38:00 +42,M,01:38:52,03:38:01 +48,M,01:48:50,03:38:01 +44,M,01:38:51,03:38:01 +52,M,01:41:23,03:38:02 +36,M,01:44:56,03:38:02 +37,W,01:39:41,03:38:02 +32,M,01:46:53,03:38:03 +25,W,01:45:36,03:38:03 +51,M,01:39:24,03:38:04 +33,M,01:37:39,03:38:04 +46,M,01:41:24,03:38:06 +39,M,01:29:00,03:38:06 +39,W,01:39:35,03:38:07 +35,M,01:37:06,03:38:07 +20,M,01:39:30,03:38:07 +47,M,01:33:56,03:38:07 +35,M,01:32:11,03:38:08 +30,W,01:43:45,03:38:08 +44,W,01:44:32,03:38:10 +38,M,01:43:04,03:38:10 +38,M,01:39:12,03:38:10 +33,M,01:35:29,03:38:11 +34,M,01:37:49,03:38:11 +43,M,01:44:44,03:38:12 +50,M,01:41:36,03:38:12 +39,M,01:38:57,03:38:12 +25,W,01:40:15,03:38:13 +36,M,01:40:33,03:38:13 +31,W,01:39:18,03:38:13 +34,M,01:32:44,03:38:13 +50,M,01:41:26,03:38:14 +28,W,01:55:54,03:38:14 +44,W,01:45:39,03:38:15 +46,W,01:44:15,03:38:15 +29,M,01:40:09,03:38:15 +45,W,01:41:52,03:38:16 +34,M,01:41:17,03:38:16 +59,M,01:41:56,03:38:16 +33,M,01:39:28,03:38:17 +43,W,01:41:52,03:38:17 +38,W,01:37:18,03:38:17 +47,M,01:45:47,03:38:17 +49,M,01:44:27,03:38:19 +34,M,01:42:24,03:38:19 +53,M,01:45:48,03:38:20 +46,M,01:39:46,03:38:21 +24,M,01:36:32,03:38:21 +27,M,01:37:35,03:38:21 +34,M,01:42:44,03:38:22 +40,M,01:38:54,03:38:22 +33,M,01:33:19,03:38:23 +34,M,01:43:53,03:38:23 +54,M,01:38:17,03:38:23 +41,M,01:38:44,03:38:24 +29,W,01:48:26,03:38:25 +58,M,01:35:44,03:38:25 +42,W,01:41:42,03:38:25 +27,M,01:46:42,03:38:25 +53,M,01:42:46,03:38:25 +36,M,01:45:42,03:38:26 +29,M,01:45:43,03:38:27 +40,M,01:39:32,03:38:27 +21,M,01:33:23,03:38:27 +26,M,01:42:05,03:38:28 +57,M,01:45:16,03:38:28 +34,M,01:36:47,03:38:29 +45,W,01:48:40,03:38:29 +41,M,01:45:58,03:38:29 +46,W,01:44:55,03:38:29 +28,M,01:33:37,03:38:29 +32,W,01:43:21,03:38:30 +26,W,01:40:40,03:38:30 +28,M,01:46:23,03:38:30 +41,M,01:40:26,03:38:30 +37,W,01:42:37,03:38:31 +51,M,01:44:15,03:38:31 +55,W,01:45:59,03:38:32 +31,M,01:44:02,03:38:32 +26,W,01:44:23,03:38:34 +33,M,01:37:58,03:38:34 +50,M,01:42:07,03:38:35 +31,M,01:47:25,03:38:35 +51,W,01:41:37,03:38:35 +45,M,01:47:30,03:38:35 +38,W,01:42:42,03:38:36 +54,M,01:38:33,03:38:36 +26,W,01:47:36,03:38:36 +39,M,01:38:30,03:38:37 +53,M,01:42:47,03:38:37 +66,M,01:42:24,03:38:37 +28,M,01:45:17,03:38:38 +24,W,01:45:33,03:38:38 +28,M,01:42:32,03:38:38 +33,W,01:46:54,03:38:39 +56,M,01:40:03,03:38:39 +36,M,01:41:38,03:38:39 +48,W,01:40:27,03:38:39 +55,M,01:46:12,03:38:40 +53,M,01:35:09,03:38:40 +56,M,01:48:04,03:38:41 +36,M,01:30:12,03:38:41 +48,W,01:41:29,03:38:42 +39,M,01:41:17,03:38:43 +30,M,01:36:17,03:38:43 +51,M,01:46:35,03:38:44 +35,M,01:44:47,03:38:44 +36,M,01:44:53,03:38:44 +34,W,01:38:53,03:38:45 +43,M,01:44:38,03:38:45 +41,M,01:45:03,03:38:45 +42,M,01:43:40,03:38:45 +55,M,01:44:36,03:38:46 +28,M,01:37:02,03:38:46 +30,W,01:43:40,03:38:46 +27,M,01:42:23,03:38:47 +43,M,01:43:21,03:38:48 +22,M,01:36:09,03:38:49 +39,W,01:41:04,03:38:49 +43,W,01:45:29,03:38:49 +40,M,01:45:26,03:38:50 +46,M,01:40:50,03:38:50 +44,W,01:48:00,03:38:50 +43,M,01:44:00,03:38:51 +64,M,01:44:08,03:38:51 +37,M,01:43:54,03:38:52 +33,W,01:48:10,03:38:53 +33,M,01:49:00,03:38:53 +43,M,01:34:57,03:38:53 +42,M,01:37:58,03:38:53 +27,W,01:40:08,03:38:53 +24,W,01:48:47,03:38:53 +36,W,01:43:12,03:38:54 +47,M,01:41:13,03:38:54 +49,M,01:44:01,03:38:55 +48,W,01:41:57,03:38:55 +43,W,01:40:50,03:38:56 +34,W,01:36:42,03:38:57 +48,M,01:45:36,03:38:58 +53,M,01:46:40,03:38:58 +43,M,01:32:15,03:38:59 +43,M,01:41:51,03:38:59 +40,W,01:44:36,03:39:00 +62,M,01:42:32,03:39:00 +28,W,01:40:46,03:39:01 +28,W,01:45:20,03:39:02 +55,M,01:44:45,03:39:03 +48,M,01:46:17,03:39:04 +50,W,01:46:35,03:39:04 +47,M,01:41:29,03:39:04 +28,W,01:40:43,03:39:04 +37,M,01:47:58,03:39:04 +54,M,01:45:20,03:39:05 +51,M,01:46:09,03:39:05 +32,M,01:39:33,03:39:05 +30,M,01:44:32,03:39:05 +40,M,01:44:58,03:39:06 +40,M,01:35:44,03:39:06 +29,M,01:44:41,03:39:07 +50,M,01:45:39,03:39:08 +52,M,01:51:05,03:39:08 +33,M,01:43:08,03:39:09 +43,M,01:46:56,03:39:09 +28,M,01:41:39,03:39:09 +24,M,01:45:38,03:39:09 +51,M,01:36:04,03:39:10 +34,M,01:41:02,03:39:10 +28,M,01:42:22,03:39:10 +52,W,01:46:28,03:39:11 +45,M,01:43:50,03:39:11 +49,M,01:48:54,03:39:11 +37,W,01:40:38,03:39:12 +32,M,01:36:10,03:39:12 +52,M,01:37:54,03:39:12 +29,M,01:36:49,03:39:12 +35,M,01:41:19,03:39:12 +44,M,01:38:51,03:39:13 +49,W,01:45:50,03:39:13 +36,W,01:40:22,03:39:13 +59,M,01:47:55,03:39:13 +44,M,01:39:14,03:39:13 +35,M,01:44:33,03:39:13 +38,M,01:44:11,03:39:13 +33,M,01:44:50,03:39:14 +42,M,01:38:05,03:39:15 +47,M,01:43:52,03:39:15 +36,M,01:33:07,03:39:15 +44,W,01:43:14,03:39:15 +55,M,01:48:23,03:39:16 +46,W,01:44:19,03:39:16 +45,M,01:46:37,03:39:16 +42,M,01:45:06,03:39:17 +27,W,01:45:54,03:39:17 +59,M,01:31:05,03:39:18 +47,M,01:45:24,03:39:18 +58,M,01:42:35,03:39:18 +35,M,01:48:32,03:39:18 +36,M,01:45:45,03:39:18 +27,W,01:39:53,03:39:19 +32,M,01:45:18,03:39:20 +47,M,01:41:53,03:39:21 +36,M,01:43:44,03:39:21 +42,M,01:47:11,03:39:22 +38,W,01:45:44,03:39:22 +44,M,01:48:43,03:39:22 +47,W,01:47:53,03:39:23 +42,W,01:45:04,03:39:23 +52,M,01:36:59,03:39:23 +28,W,01:45:49,03:39:23 +35,W,01:47:19,03:39:24 +23,M,01:32:02,03:39:24 +18,M,01:33:57,03:39:24 +52,M,01:48:04,03:39:24 +52,M,01:38:25,03:39:24 +38,W,01:48:44,03:39:24 +28,M,01:44:04,03:39:25 +48,M,01:45:07,03:39:25 +35,W,01:48:40,03:39:26 +35,M,01:26:40,03:39:26 +57,M,01:46:13,03:39:26 +32,W,01:42:56,03:39:27 +31,W,01:37:26,03:39:27 +47,M,01:43:42,03:39:28 +37,M,01:50:11,03:39:29 +43,W,01:42:22,03:39:30 +52,M,01:47:24,03:39:30 +47,M,01:43:29,03:39:30 +39,W,01:41:07,03:39:30 +35,W,01:46:24,03:39:30 +42,W,01:42:07,03:39:30 +34,M,01:47:33,03:39:31 +50,M,01:44:41,03:39:31 +37,M,01:42:41,03:39:32 +43,W,01:45:04,03:39:32 +32,M,01:47:10,03:39:32 +46,M,01:40:18,03:39:32 +28,M,01:39:00,03:39:33 +35,M,01:38:03,03:39:33 +31,W,01:48:38,03:39:33 +29,M,01:34:06,03:39:33 +44,M,01:46:27,03:39:33 +26,M,01:36:45,03:39:33 +39,M,01:44:01,03:39:33 +44,M,01:37:35,03:39:34 +38,M,01:43:36,03:39:35 +35,M,01:38:40,03:39:35 +29,M,01:39:59,03:39:37 +43,M,01:44:45,03:39:37 +46,M,01:41:39,03:39:38 +50,M,01:49:02,03:39:39 +40,M,01:39:08,03:39:39 +46,M,01:42:17,03:39:39 +50,M,01:34:56,03:39:39 +36,M,01:45:45,03:39:39 +32,W,01:45:08,03:39:39 +49,M,01:42:22,03:39:40 +35,M,01:42:26,03:39:40 +40,M,01:44:23,03:39:40 +28,W,01:45:18,03:39:40 +30,M,01:35:59,03:39:40 +45,M,01:38:18,03:39:41 +47,M,01:41:51,03:39:41 +37,M,01:44:48,03:39:41 +43,M,01:49:04,03:39:41 +40,M,01:45:37,03:39:41 +31,W,01:45:36,03:39:41 +49,W,01:43:11,03:39:43 +40,M,01:44:10,03:39:43 +33,M,01:42:36,03:39:44 +32,M,01:47:01,03:39:44 +47,M,01:46:01,03:39:44 +35,M,01:38:32,03:39:44 +31,M,01:40:08,03:39:45 +27,W,01:46:43,03:39:46 +36,M,01:44:48,03:39:46 +32,M,01:43:08,03:39:46 +33,M,01:44:29,03:39:48 +37,M,01:39:26,03:39:48 +53,M,01:41:14,03:39:49 +19,W,01:34:48,03:39:49 +37,W,01:38:00,03:39:49 +43,M,01:43:57,03:39:50 +42,M,01:44:10,03:39:50 +44,M,01:46:01,03:39:50 +34,M,01:36:09,03:39:51 +43,M,01:44:48,03:39:52 +52,M,01:42:46,03:39:52 +50,M,01:44:18,03:39:52 +46,M,01:41:21,03:39:52 +43,M,01:32:19,03:39:52 +39,M,01:47:32,03:39:53 +44,M,01:43:11,03:39:53 +30,M,01:43:01,03:39:53 +30,W,01:51:00,03:39:53 +37,M,01:38:46,03:39:54 +38,M,01:46:45,03:39:54 +23,W,01:44:22,03:39:55 +56,M,01:39:20,03:39:56 +42,M,01:44:10,03:39:56 +55,M,01:45:32,03:39:56 +37,M,01:39:39,03:39:56 +35,M,01:40:27,03:39:57 +24,M,01:41:08,03:39:57 +39,M,01:40:32,03:39:57 +31,W,01:45:41,03:39:57 +65,M,01:44:45,03:39:57 +49,M,01:45:12,03:39:57 +37,M,01:41:51,03:39:58 +38,M,01:32:46,03:39:58 +44,M,01:42:41,03:39:59 +45,W,01:44:16,03:39:59 +42,M,01:44:07,03:40:00 +24,M,01:38:40,03:40:00 +32,W,01:49:43,03:40:00 +32,M,01:43:48,03:40:00 +46,W,01:45:20,03:40:01 +48,W,01:43:52,03:40:02 +45,M,01:44:17,03:40:02 +40,W,01:44:52,03:40:02 +27,W,01:45:46,03:40:02 +45,M,01:38:51,03:40:02 +51,M,01:42:05,03:40:02 +53,M,01:37:50,03:40:03 +58,M,01:41:34,03:40:04 +37,M,01:45:01,03:40:04 +55,M,01:46:15,03:40:05 +44,M,01:45:00,03:40:05 +37,W,01:47:45,03:40:05 +51,M,01:35:45,03:40:05 +34,M,01:45:40,03:40:05 +28,M,01:36:43,03:40:05 +35,M,01:40:54,03:40:06 +41,M,01:38:27,03:40:06 +40,M,01:54:32,03:40:06 +24,W,01:39:10,03:40:06 +59,M,01:46:08,03:40:07 +50,M,01:40:58,03:40:07 +42,M,01:51:04,03:40:07 +46,M,01:41:51,03:40:08 +56,M,01:45:33,03:40:08 +56,M,01:41:58,03:40:09 +38,M,01:51:23,03:40:09 +35,M,01:43:44,03:40:10 +26,M,01:36:29,03:40:10 +44,M,01:32:59,03:40:10 +39,M,01:38:50,03:40:10 +36,M,01:45:35,03:40:10 +36,W,01:42:08,03:40:11 +40,M,01:45:52,03:40:11 +29,M,01:46:17,03:40:12 +57,M,01:42:03,03:40:12 +35,M,01:39:06,03:40:13 +44,M,01:44:22,03:40:13 +30,W,01:46:14,03:40:13 +41,M,01:42:05,03:40:13 +55,M,01:46:04,03:40:13 +64,M,01:40:24,03:40:14 +48,M,01:39:10,03:40:14 +44,M,01:44:10,03:40:14 +46,M,01:33:43,03:40:15 +31,M,01:37:24,03:40:15 +29,M,01:42:06,03:40:16 +45,M,01:39:09,03:40:16 +27,M,01:36:44,03:40:16 +44,W,01:42:28,03:40:17 +47,W,01:44:40,03:40:17 +46,M,01:36:37,03:40:18 +30,M,01:49:21,03:40:19 +31,M,01:38:18,03:40:20 +25,W,01:47:20,03:40:21 +39,M,01:44:05,03:40:21 +59,M,01:46:14,03:40:21 +34,M,01:42:10,03:40:21 +46,M,01:42:14,03:40:21 +45,W,01:43:04,03:40:22 +51,W,01:44:23,03:40:23 +43,M,01:44:46,03:40:23 +50,M,01:39:49,03:40:23 +57,M,01:45:16,03:40:23 +35,W,01:42:48,03:40:23 +26,M,01:40:18,03:40:24 +45,M,01:49:19,03:40:24 +48,M,01:45:48,03:40:24 +35,M,01:42:55,03:40:25 +27,M,01:39:20,03:40:25 +44,M,01:44:31,03:40:26 +24,M,01:34:33,03:40:26 +40,M,01:50:20,03:40:26 +30,M,01:42:16,03:40:28 +41,M,01:40:40,03:40:28 +40,M,01:45:19,03:40:28 +33,M,01:43:19,03:40:28 +34,M,01:40:23,03:40:29 +43,W,01:41:44,03:40:29 +35,M,01:38:14,03:40:30 +42,M,01:38:15,03:40:31 +51,M,01:45:45,03:40:32 +50,M,01:42:06,03:40:32 +43,W,01:44:59,03:40:32 +29,W,01:45:33,03:40:32 +32,W,01:45:27,03:40:32 +37,M,01:42:12,03:40:33 +30,M,01:37:10,03:40:34 +40,M,01:40:34,03:40:34 +41,M,01:39:03,03:40:35 +43,W,01:41:00,03:40:35 +36,M,01:41:09,03:40:35 +49,M,01:44:24,03:40:35 +42,M,01:46:59,03:40:36 +48,M,01:43:25,03:40:36 +41,W,01:44:08,03:40:36 +54,M,01:44:30,03:40:37 +40,M,01:45:47,03:40:37 +28,M,01:38:35,03:40:37 +56,M,01:45:35,03:40:37 +40,M,01:46:29,03:40:37 +27,M,01:41:49,03:40:38 +51,M,01:41:36,03:40:38 +24,W,01:45:05,03:40:38 +25,W,01:45:23,03:40:38 +48,W,01:47:24,03:40:39 +23,M,01:41:29,03:40:39 +51,M,01:45:22,03:40:39 +38,M,01:42:56,03:40:39 +49,M,01:42:21,03:40:39 +47,M,01:42:34,03:40:40 +28,M,01:42:42,03:40:40 +34,M,01:40:32,03:40:40 +31,M,01:42:25,03:40:41 +47,W,01:44:59,03:40:41 +49,M,01:41:24,03:40:42 +41,M,01:41:54,03:40:42 +48,M,01:42:50,03:40:43 +29,M,01:38:11,03:40:43 +49,M,01:40:42,03:40:43 +29,W,01:48:32,03:40:44 +32,M,01:40:17,03:40:44 +29,M,01:41:22,03:40:44 +45,M,01:40:24,03:40:45 +41,W,01:46:14,03:40:45 +45,M,01:39:51,03:40:46 +24,W,01:42:47,03:40:47 +46,M,01:35:14,03:40:47 +42,M,01:43:00,03:40:47 +43,W,01:41:51,03:40:48 +38,M,01:48:25,03:40:48 +28,M,01:46:56,03:40:48 +35,M,01:42:41,03:40:49 +34,M,01:32:24,03:40:49 +48,M,01:32:12,03:40:49 +49,M,01:36:02,03:40:50 +39,M,01:43:21,03:40:50 +42,M,01:45:31,03:40:50 +51,M,01:44:22,03:40:50 +35,W,01:45:54,03:40:51 +38,M,01:48:00,03:40:51 +33,M,01:29:49,03:40:52 +31,M,01:42:29,03:40:52 +56,M,01:45:20,03:40:53 +47,M,01:38:41,03:40:53 +38,M,01:46:08,03:40:53 +30,M,01:41:00,03:40:53 +17,M,01:34:04,03:40:53 +40,M,01:47:09,03:40:54 +50,M,01:45:45,03:40:55 +40,W,01:43:41,03:40:55 +40,M,01:38:53,03:40:56 +49,M,01:47:40,03:40:56 +42,M,01:42:06,03:40:56 +44,M,01:47:50,03:40:56 +35,W,01:48:58,03:40:56 +30,W,01:45:50,03:40:56 +39,W,01:41:18,03:40:57 +39,W,01:45:13,03:40:57 +35,M,01:41:16,03:40:58 +45,M,01:45:29,03:40:58 +33,M,01:43:25,03:40:59 +29,M,01:48:09,03:40:59 +41,M,01:47:55,03:40:59 +38,M,01:42:55,03:40:59 +42,M,01:41:12,03:40:59 +36,M,01:44:01,03:41:00 +44,M,01:45:58,03:41:01 +37,M,01:39:52,03:41:01 +34,M,01:44:11,03:41:01 +35,M,01:38:19,03:41:02 +50,M,01:44:27,03:41:02 +65,M,01:45:19,03:41:02 +34,W,01:43:43,03:41:03 +46,W,01:51:22,03:41:03 +54,M,01:40:20,03:41:03 +45,W,01:44:03,03:41:04 +24,W,01:47:49,03:41:04 +49,M,01:37:17,03:41:04 +32,M,01:42:00,03:41:04 +36,M,01:46:37,03:41:04 +49,M,01:42:39,03:41:05 +42,M,01:39:20,03:41:05 +29,W,01:49:49,03:41:05 +45,M,01:38:52,03:41:05 +34,W,01:41:46,03:41:06 +42,W,01:43:03,03:41:06 +51,M,01:48:09,03:41:06 +27,W,01:49:02,03:41:06 +36,M,01:46:49,03:41:07 +32,M,01:43:09,03:41:07 +41,M,01:45:20,03:41:07 +48,M,01:44:49,03:41:07 +48,W,01:46:15,03:41:08 +28,M,01:44:08,03:41:09 +33,M,01:43:33,03:41:10 +40,M,01:46:40,03:41:10 +31,M,01:43:21,03:41:10 +50,M,01:44:57,03:41:11 +32,M,01:43:44,03:41:11 +44,W,01:42:33,03:41:11 +30,M,01:44:48,03:41:11 +31,M,01:42:31,03:41:11 +33,M,01:45:09,03:41:12 +25,M,01:40:40,03:41:13 +43,M,01:39:44,03:41:13 +34,M,01:44:05,03:41:13 +41,M,01:44:16,03:41:13 +48,M,01:45:37,03:41:13 +43,M,01:40:12,03:41:14 +26,M,01:52:25,03:41:15 +38,W,01:46:58,03:41:19 +29,M,01:38:21,03:41:19 +38,W,01:41:42,03:41:19 +54,W,01:43:18,03:41:20 +28,M,01:46:58,03:41:20 +38,M,01:46:01,03:41:20 +35,W,01:41:37,03:41:20 +27,M,01:36:34,03:41:20 +33,M,01:42:00,03:41:21 +31,W,01:48:37,03:41:22 +42,M,01:47:39,03:41:22 +23,W,01:35:13,03:41:22 +58,M,01:41:27,03:41:22 +39,W,01:48:57,03:41:23 +33,W,01:38:09,03:41:23 +38,W,01:40:05,03:41:24 +53,M,01:40:52,03:41:24 +40,W,01:45:12,03:41:24 +43,W,01:43:59,03:41:25 +37,M,01:41:10,03:41:25 +34,M,01:44:40,03:41:25 +58,M,01:48:11,03:41:26 +30,M,01:42:07,03:41:26 +46,M,01:39:43,03:41:26 +39,M,01:41:53,03:41:27 +50,M,01:44:22,03:41:27 +46,M,01:48:16,03:41:27 +45,M,01:47:09,03:41:28 +45,M,01:33:55,03:41:28 +48,M,01:43:14,03:41:29 +38,M,01:45:46,03:41:30 +28,M,01:41:01,03:41:30 +37,M,01:42:39,03:41:30 +43,M,01:40:30,03:41:30 +45,M,01:37:27,03:41:31 +37,M,01:47:59,03:41:31 +50,M,01:46:32,03:41:32 +29,W,01:46:39,03:41:32 +32,M,01:36:32,03:41:32 +23,M,01:48:16,03:41:32 +42,M,01:48:41,03:41:33 +45,W,01:43:14,03:41:33 +37,M,01:39:03,03:41:33 +46,W,01:43:23,03:41:35 +49,M,01:43:58,03:41:35 +38,M,01:39:15,03:41:36 +40,M,01:36:37,03:41:36 +46,M,01:39:46,03:41:37 +43,W,01:37:29,03:41:39 +26,W,01:39:30,03:41:39 +25,W,01:48:56,03:41:40 +31,W,01:48:34,03:41:40 +39,M,01:46:29,03:41:40 +50,M,01:46:44,03:41:41 +32,M,01:44:26,03:41:41 +43,M,01:39:38,03:41:41 +36,W,01:49:17,03:41:41 +47,M,01:44:35,03:41:41 +62,M,01:34:08,03:41:41 +46,M,01:45:17,03:41:42 +54,M,01:38:15,03:41:43 +41,M,01:49:07,03:41:43 +27,W,01:48:18,03:41:43 +45,M,01:41:02,03:41:43 +29,M,01:40:44,03:41:44 +38,W,01:44:56,03:41:44 +33,M,01:39:49,03:41:45 +25,W,01:46:46,03:41:45 +50,M,01:46:13,03:41:45 +32,M,01:31:17,03:41:45 +36,M,01:45:36,03:41:47 +56,M,01:47:39,03:41:48 +55,M,01:47:31,03:41:48 +49,W,01:44:57,03:41:48 +53,M,01:43:51,03:41:48 +45,M,01:40:07,03:41:49 +49,M,01:44:54,03:41:50 +32,M,01:40:12,03:41:51 +24,M,01:44:11,03:41:51 +39,M,01:35:32,03:41:51 +34,M,01:44:59,03:41:52 +44,M,01:45:59,03:41:52 +36,M,01:38:32,03:41:52 +51,M,01:44:18,03:41:53 +42,M,01:40:21,03:41:53 +30,W,01:47:25,03:41:53 +38,M,01:40:29,03:41:54 +50,M,01:41:10,03:41:54 +35,M,01:48:03,03:41:54 +39,M,01:44:34,03:41:54 +32,M,01:35:33,03:41:54 +30,W,01:46:33,03:41:55 +53,M,01:42:37,03:41:55 +32,M,01:39:24,03:41:55 +27,W,01:44:37,03:41:56 +45,M,01:39:39,03:41:57 +46,M,01:46:26,03:41:57 +53,M,01:42:44,03:41:57 +37,M,01:32:32,03:41:57 +46,M,01:40:28,03:41:58 +32,M,01:52:16,03:41:58 +40,M,01:45:09,03:41:58 +23,W,01:47:02,03:41:58 +41,M,01:41:50,03:41:59 +53,W,01:46:59,03:41:59 +45,M,01:43:48,03:42:00 +20,M,01:44:36,03:42:00 +53,M,01:42:13,03:42:00 +33,M,01:39:09,03:42:00 +24,W,01:34:49,03:42:00 +44,W,01:42:48,03:42:01 +47,M,01:36:43,03:42:01 +54,W,01:47:32,03:42:01 +47,M,01:47:36,03:42:02 +55,M,01:53:18,03:42:02 +55,M,01:45:24,03:42:03 +38,M,01:46:32,03:42:03 +22,M,01:35:41,03:42:04 +59,M,01:47:37,03:42:04 +27,M,01:43:38,03:42:05 +36,W,01:48:20,03:42:05 +27,M,01:49:02,03:42:06 +23,M,01:43:31,03:42:06 +44,M,01:36:43,03:42:07 +42,W,01:45:25,03:42:07 +47,M,01:50:05,03:42:08 +40,W,01:43:33,03:42:08 +31,W,01:45:40,03:42:09 +40,W,01:42:06,03:42:09 +65,M,01:46:19,03:42:10 +41,M,01:43:27,03:42:10 +41,M,01:43:36,03:42:10 +44,M,01:46:50,03:42:11 +32,M,01:41:06,03:42:12 +42,M,01:37:31,03:42:12 +48,M,01:51:27,03:42:12 +47,M,01:44:59,03:42:12 +33,M,01:39:58,03:42:12 +30,W,01:40:47,03:42:12 +38,W,01:48:27,03:42:13 +34,M,01:42:22,03:42:13 +38,M,01:43:30,03:42:13 +39,M,01:45:18,03:42:13 +56,M,01:45:38,03:42:13 +60,W,01:42:43,03:42:13 +33,M,01:45:29,03:42:14 +37,W,01:45:15,03:42:14 +49,M,01:39:37,03:42:15 +24,M,01:45:21,03:42:15 +42,M,01:41:58,03:42:15 +28,W,01:43:46,03:42:15 +28,M,01:50:14,03:42:15 +48,W,01:41:25,03:42:17 +42,W,01:47:38,03:42:17 +41,M,01:36:56,03:42:17 +39,M,01:44:28,03:42:17 +34,M,01:37:13,03:42:17 +29,M,01:50:48,03:42:17 +35,M,01:44:20,03:42:18 +37,M,01:41:56,03:42:18 +42,M,01:40:07,03:42:18 +21,M,01:41:43,03:42:19 +39,W,01:47:09,03:42:20 +28,W,01:47:43,03:42:20 +59,M,01:45:26,03:42:20 +37,M,01:43:09,03:42:20 +40,M,01:41:24,03:42:21 +51,W,01:49:35,03:42:23 +41,W,01:45:20,03:42:23 +44,M,01:45:23,03:42:23 +53,M,01:37:55,03:42:23 +29,M,01:48:49,03:42:24 +34,M,01:45:31,03:42:24 +36,M,01:38:52,03:42:24 +43,M,01:42:46,03:42:24 +53,M,01:46:10,03:42:25 +48,M,01:47:49,03:42:25 +46,M,01:40:48,03:42:26 +29,M,01:40:14,03:42:26 +49,M,01:40:49,03:42:26 +30,W,01:49:07,03:42:26 +48,M,01:45:36,03:42:28 +39,M,01:21:57,03:42:28 +26,M,01:44:00,03:42:28 +34,M,01:42:39,03:42:28 +31,M,01:48:45,03:42:29 +50,M,01:43:36,03:42:29 +28,W,01:43:47,03:42:29 +39,M,01:41:28,03:42:29 +44,M,01:52:06,03:42:30 +48,M,01:35:24,03:42:30 +29,M,01:42:12,03:42:30 +29,M,01:37:43,03:42:30 +29,M,01:52:06,03:42:30 +57,M,01:46:21,03:42:30 +37,M,01:42:53,03:42:31 +40,M,01:39:11,03:42:31 +43,M,01:48:08,03:42:31 +29,M,01:36:49,03:42:31 +56,M,01:37:56,03:42:32 +33,W,01:41:45,03:42:32 +54,M,01:42:06,03:42:33 +49,M,01:44:44,03:42:33 +34,M,01:42:10,03:42:33 +37,M,01:43:47,03:42:33 +47,M,01:45:36,03:42:34 +31,M,01:44:01,03:42:34 +44,M,01:43:06,03:42:34 +35,M,01:43:59,03:42:34 +25,W,01:47:02,03:42:35 +44,M,01:45:06,03:42:35 +37,M,01:42:14,03:42:35 +38,W,01:48:29,03:42:36 +41,M,01:44:03,03:42:36 +51,M,01:40:47,03:42:36 +40,M,01:36:52,03:42:36 +47,M,01:45:03,03:42:37 +45,M,01:39:40,03:42:38 +48,M,01:45:53,03:42:38 +40,W,01:40:25,03:42:38 +37,M,01:47:09,03:42:38 +40,M,01:38:14,03:42:39 +31,M,01:41:15,03:42:39 +48,M,01:32:10,03:42:39 +49,W,01:46:34,03:42:40 +32,M,01:40:58,03:42:40 +43,M,01:44:38,03:42:40 +22,M,01:40:05,03:42:40 +37,M,01:45:46,03:42:40 +34,M,01:38:44,03:42:41 +39,W,01:48:01,03:42:41 +32,M,01:48:13,03:42:41 +39,W,01:44:06,03:42:41 +47,M,01:42:48,03:42:42 +56,M,01:44:54,03:42:42 +19,M,01:43:25,03:42:42 +28,M,01:48:20,03:42:43 +49,M,01:44:24,03:42:43 +53,M,01:44:13,03:42:43 +38,W,01:43:45,03:42:43 +44,M,01:43:22,03:42:43 +24,M,01:36:02,03:42:45 +26,M,01:39:27,03:42:45 +40,M,01:39:40,03:42:46 +46,M,01:42:37,03:42:46 +22,W,01:41:15,03:42:46 +31,M,01:40:53,03:42:46 +54,M,01:45:11,03:42:46 +34,M,01:37:10,03:42:47 +50,M,01:41:34,03:42:47 +39,M,01:42:54,03:42:49 +44,W,01:43:30,03:42:50 +37,M,01:46:46,03:42:50 +39,M,01:30:09,03:42:50 +35,M,01:42:09,03:42:51 +60,M,01:46:22,03:42:51 +38,M,01:38:50,03:42:52 +37,M,01:42:20,03:42:53 +26,W,01:45:12,03:42:53 +50,M,01:37:22,03:42:53 +53,M,01:43:54,03:42:53 +69,M,01:34:38,03:42:53 +31,M,01:47:05,03:42:54 +46,W,01:42:37,03:42:55 +45,M,01:47:13,03:42:55 +46,M,01:45:49,03:42:56 +49,M,01:43:29,03:42:56 +38,M,01:50:39,03:42:56 +26,M,01:46:48,03:42:56 +26,W,01:44:17,03:42:58 +57,W,01:41:57,03:42:59 +38,W,01:44:22,03:43:00 +27,W,01:39:03,03:43:01 +57,M,01:47:56,03:43:01 +46,M,01:49:08,03:43:01 +30,M,01:39:00,03:43:01 +33,M,01:31:19,03:43:02 +40,M,01:47:11,03:43:03 +54,M,01:39:11,03:43:03 +47,W,01:45:01,03:43:03 +33,M,01:42:43,03:43:03 +63,M,01:49:49,03:43:03 +42,M,01:41:47,03:43:03 +27,M,01:42:06,03:43:04 +45,M,01:50:38,03:43:04 +36,M,01:39:39,03:43:04 +34,W,01:47:53,03:43:04 +52,M,01:48:52,03:43:05 +57,M,01:45:02,03:43:05 +29,W,01:46:18,03:43:06 +38,M,01:44:18,03:43:06 +56,M,01:48:57,03:43:06 +42,M,01:49:04,03:43:06 +38,M,01:34:36,03:43:07 +39,M,01:47:11,03:43:08 +49,M,01:38:19,03:43:08 +36,M,01:44:58,03:43:08 +50,M,01:46:14,03:43:09 +36,M,01:44:51,03:43:09 +48,M,01:45:04,03:43:09 +39,M,01:48:42,03:43:10 +36,M,01:49:45,03:43:10 +49,M,01:37:47,03:43:11 +42,W,01:44:21,03:43:11 +24,M,01:37:57,03:43:11 +54,M,01:51:19,03:43:11 +32,M,01:45:59,03:43:12 +53,M,01:42:52,03:43:12 +50,M,01:41:15,03:43:12 +61,M,01:40:54,03:43:12 +40,M,01:48:45,03:43:12 +53,M,01:45:04,03:43:13 +25,M,01:49:03,03:43:14 +28,W,01:37:45,03:43:14 +37,M,01:47:09,03:43:14 +45,M,01:51:47,03:43:14 +27,M,01:46:48,03:43:16 +35,M,01:45:44,03:43:16 +49,M,01:33:42,03:43:16 +46,M,01:46:24,03:43:17 +43,M,01:38:42,03:43:17 +38,M,01:49:19,03:43:17 +39,M,01:42:19,03:43:17 +29,W,01:42:55,03:43:18 +41,W,01:47:40,03:43:19 +48,M,01:37:54,03:43:19 +37,M,01:41:17,03:43:20 +50,M,01:38:11,03:43:20 +42,M,01:43:23,03:43:21 +57,M,01:43:11,03:43:21 +36,W,01:45:47,03:43:22 +40,W,01:45:07,03:43:23 +33,M,01:44:39,03:43:23 +51,M,01:42:52,03:43:23 +62,M,01:46:05,03:43:24 +47,W,01:45:58,03:43:24 +26,M,01:38:32,03:43:24 +30,M,01:33:39,03:43:24 +39,M,01:35:21,03:43:24 +23,W,01:45:05,03:43:25 +41,M,01:39:05,03:43:25 +35,M,01:36:32,03:43:25 +49,M,01:38:20,03:43:26 +33,M,01:48:46,03:43:26 +59,M,01:42:05,03:43:27 +29,M,01:42:40,03:43:28 +49,M,01:36:31,03:43:28 +39,M,01:41:16,03:43:28 +44,M,01:37:26,03:43:29 +22,M,01:44:38,03:43:29 +29,W,01:42:33,03:43:30 +46,M,01:47:31,03:43:31 +46,M,01:41:42,03:43:32 +43,M,01:38:05,03:43:32 +43,M,01:44:56,03:43:33 +34,M,01:49:28,03:43:33 +42,W,01:48:00,03:43:34 +26,M,01:49:45,03:43:34 +29,M,01:47:45,03:43:35 +39,M,01:42:53,03:43:35 +41,M,01:41:42,03:43:35 +49,M,01:37:01,03:43:35 +36,M,01:43:30,03:43:36 +33,M,01:45:50,03:43:36 +49,M,01:45:16,03:43:36 +35,M,01:37:39,03:43:37 +50,M,01:41:26,03:43:37 +55,M,01:43:16,03:43:37 +29,M,01:50:18,03:43:38 +35,M,01:37:09,03:43:39 +30,W,01:46:02,03:43:40 +28,W,01:42:21,03:43:40 +30,M,01:45:11,03:43:40 +35,M,01:40:42,03:43:41 +28,M,01:42:14,03:43:41 +30,W,01:48:29,03:43:41 +43,M,01:43:36,03:43:41 +34,M,01:46:03,03:43:42 +34,M,01:37:17,03:43:42 +55,M,01:47:41,03:43:42 +35,M,01:35:21,03:43:42 +36,W,01:49:18,03:43:43 +30,M,01:50:16,03:43:43 +38,M,01:32:55,03:43:44 +31,M,01:42:00,03:43:44 +27,M,01:43:52,03:43:45 +41,M,01:38:35,03:43:45 +35,M,01:49:27,03:43:45 +29,W,01:40:30,03:43:45 +61,M,01:46:15,03:43:46 +43,M,01:44:44,03:43:46 +42,W,01:45:40,03:43:46 +37,M,01:40:57,03:43:48 +51,W,01:46:35,03:43:49 +32,M,01:51:04,03:43:49 +46,M,01:42:55,03:43:50 +40,M,01:50:57,03:43:51 +41,M,01:49:38,03:43:51 +52,M,01:38:49,03:43:51 +46,M,01:43:07,03:43:51 +54,M,01:45:41,03:43:52 +49,M,01:42:58,03:43:53 +54,M,01:40:42,03:43:53 +43,M,01:44:43,03:43:54 +38,W,01:43:19,03:43:54 +32,W,01:38:40,03:43:54 +35,M,01:46:27,03:43:55 +28,M,01:40:09,03:43:55 +46,W,01:46:15,03:43:55 +54,M,01:39:23,03:43:55 +32,W,01:38:49,03:43:56 +40,M,01:38:26,03:43:56 +66,M,01:43:07,03:43:59 +45,W,01:44:34,03:43:59 +37,M,01:39:58,03:43:59 +28,W,01:52:11,03:43:59 +42,M,01:44:07,03:43:59 +28,M,01:42:30,03:43:59 +39,W,01:44:34,03:44:00 +28,M,01:44:44,03:44:01 +47,M,01:51:18,03:44:02 +49,M,01:45:03,03:44:02 +37,W,01:44:03,03:44:02 +54,M,01:41:13,03:44:02 +54,M,01:43:46,03:44:03 +45,M,01:47:22,03:44:03 +27,M,01:49:39,03:44:03 +42,M,01:51:24,03:44:04 +26,M,01:42:28,03:44:04 +26,M,01:28:07,03:44:04 +28,W,01:46:30,03:44:04 +38,M,01:50:53,03:44:06 +32,M,01:41:30,03:44:06 +50,M,01:44:03,03:44:07 +29,M,01:44:26,03:44:07 +48,M,01:47:03,03:44:07 +43,M,01:49:35,03:44:08 +40,M,01:52:40,03:44:08 +34,M,01:43:47,03:44:09 +32,M,01:44:18,03:44:09 +27,M,01:44:42,03:44:09 +45,M,01:50:38,03:44:09 +33,M,01:44:17,03:44:10 +34,M,01:44:46,03:44:11 +41,M,01:39:19,03:44:11 +52,M,01:42:30,03:44:11 +43,M,01:51:45,03:44:12 +52,M,01:48:10,03:44:12 +42,M,01:40:28,03:44:13 +26,M,01:45:05,03:44:14 +54,M,01:45:14,03:44:14 +27,M,01:43:13,03:44:15 +26,W,01:48:46,03:44:15 +54,M,01:44:04,03:44:15 +47,M,01:43:13,03:44:16 +37,M,01:41:02,03:44:16 +25,M,01:49:40,03:44:16 +45,M,01:48:28,03:44:16 +35,M,01:50:04,03:44:18 +30,W,01:46:53,03:44:18 +26,M,01:39:00,03:44:20 +24,M,01:44:27,03:44:20 +31,M,01:46:36,03:44:20 +28,M,01:46:13,03:44:21 +34,M,01:48:50,03:44:21 +34,M,01:33:52,03:44:22 +37,M,01:50:38,03:44:24 +33,M,01:44:39,03:44:24 +44,M,01:40:02,03:44:24 +58,M,01:49:05,03:44:25 +38,M,01:47:54,03:44:25 +39,W,01:43:35,03:44:26 +43,M,01:37:48,03:44:26 +22,M,01:41:05,03:44:26 +36,W,01:46:01,03:44:26 +37,M,01:42:46,03:44:27 +32,M,01:38:14,03:44:29 +32,W,01:43:44,03:44:29 +34,W,01:44:34,03:44:29 +49,M,01:38:38,03:44:29 +23,M,01:39:23,03:44:31 +42,M,01:43:48,03:44:31 +52,M,01:35:46,03:44:32 +25,W,01:43:52,03:44:33 +38,M,01:36:40,03:44:33 +27,W,01:42:26,03:44:33 +52,M,01:50:36,03:44:33 +54,M,01:44:26,03:44:33 +53,M,01:20:59,03:44:34 +22,W,01:37:55,03:44:35 +42,M,01:46:24,03:44:35 +35,M,01:48:13,03:44:35 +37,W,01:50:05,03:44:38 +49,M,01:39:04,03:44:38 +51,M,01:44:38,03:44:39 +35,M,01:49:18,03:44:39 +49,M,01:44:07,03:44:40 +61,M,01:45:04,03:44:40 +38,M,01:44:11,03:44:40 +27,M,01:44:23,03:44:40 +40,W,01:45:05,03:44:40 +31,W,01:46:52,03:44:41 +48,M,01:49:59,03:44:41 +35,W,01:45:44,03:44:42 +22,M,01:41:25,03:44:42 +53,M,01:40:46,03:44:42 +49,M,01:48:27,03:44:43 +31,M,01:44:25,03:44:43 +30,M,01:47:00,03:44:43 +51,M,01:43:45,03:44:43 +50,M,01:47:31,03:44:43 +44,M,01:43:59,03:44:44 +29,M,01:46:29,03:44:44 +45,M,01:44:16,03:44:44 +51,M,01:38:32,03:44:45 +38,M,01:47:02,03:44:46 +37,W,01:45:53,03:44:46 +29,W,01:38:26,03:44:47 +38,M,01:45:48,03:44:47 +48,M,01:48:41,03:44:48 +41,M,01:49:15,03:44:48 +38,W,01:46:53,03:44:48 +33,W,01:49:42,03:44:49 +66,M,01:47:25,03:44:49 +38,W,01:43:54,03:44:49 +31,W,01:48:15,03:44:49 +31,M,01:44:21,03:44:49 +45,W,01:42:58,03:44:50 +37,M,01:46:31,03:44:50 +48,M,01:40:52,03:44:50 +37,M,01:48:51,03:44:51 +39,M,01:44:06,03:44:51 +41,M,01:50:20,03:44:51 +39,M,01:47:30,03:44:51 +27,W,01:47:33,03:44:52 +28,M,01:40:28,03:44:52 +33,M,01:39:24,03:44:52 +39,M,01:48:03,03:44:52 +27,M,01:29:04,03:44:53 +36,M,01:44:46,03:44:53 +29,M,01:50:57,03:44:53 +50,M,01:35:46,03:44:53 +52,M,01:42:02,03:44:53 +26,W,01:34:47,03:44:53 +37,M,01:45:58,03:44:54 +41,M,01:44:24,03:44:54 +56,M,01:42:56,03:44:54 +40,M,01:48:34,03:44:55 +35,M,01:46:19,03:44:55 +36,M,01:44:30,03:44:55 +40,M,01:43:17,03:44:56 +53,M,01:43:00,03:44:57 +32,M,01:47:37,03:44:57 +48,M,01:52:51,03:44:57 +38,W,01:49:34,03:44:57 +46,M,01:44:27,03:44:58 +51,M,01:44:10,03:44:58 +29,M,01:46:07,03:44:59 +25,M,01:42:48,03:44:59 +38,M,01:43:07,03:44:59 +49,M,01:40:02,03:44:59 +55,M,01:43:10,03:45:01 +36,M,01:42:56,03:45:02 +43,W,01:47:22,03:45:02 +45,W,01:41:28,03:45:02 +40,M,01:48:07,03:45:02 +55,M,01:43:36,03:45:02 +39,W,01:49:40,03:45:02 +30,W,01:47:06,03:45:03 +36,M,01:43:55,03:45:04 +41,M,01:46:27,03:45:04 +51,M,01:49:11,03:45:04 +41,W,01:46:03,03:45:04 +38,M,01:45:37,03:45:05 +48,M,01:39:44,03:45:07 +49,M,01:44:48,03:45:07 +42,M,01:49:53,03:45:07 +40,M,01:40:36,03:45:08 +51,M,01:44:15,03:45:08 +46,M,01:43:32,03:45:09 +26,W,01:46:42,03:45:09 +43,M,01:43:54,03:45:09 +57,M,01:41:39,03:45:09 +23,W,01:46:25,03:45:09 +33,M,01:47:05,03:45:09 +28,M,01:39:07,03:45:10 +40,M,01:45:44,03:45:10 +38,M,01:44:50,03:45:11 +36,M,01:47:16,03:45:11 +43,M,01:41:18,03:45:12 +57,M,01:50:21,03:45:13 +31,M,01:38:08,03:45:13 +43,M,01:46:23,03:45:15 +44,W,01:48:41,03:45:15 +28,W,01:49:52,03:45:16 +29,W,01:48:51,03:45:16 +30,M,01:40:16,03:45:16 +29,W,01:39:06,03:45:17 +44,M,01:48:48,03:45:17 +53,M,01:44:15,03:45:17 +27,M,01:41:49,03:45:18 +57,M,01:44:04,03:45:19 +44,M,01:40:22,03:45:19 +35,M,01:41:35,03:45:20 +30,M,01:37:30,03:45:20 +38,M,01:39:44,03:45:21 +54,M,01:37:30,03:45:21 +28,M,01:43:38,03:45:21 +42,M,01:45:16,03:45:22 +53,M,01:45:07,03:45:22 +54,M,01:49:36,03:45:23 +51,M,01:44:45,03:45:24 +41,M,01:45:59,03:45:24 +59,M,01:44:24,03:45:24 +28,W,01:46:17,03:45:24 +26,M,01:37:48,03:45:24 +26,M,01:48:24,03:45:24 +48,M,01:39:46,03:45:25 +18,M,01:35:05,03:45:26 +25,W,01:46:40,03:45:27 +27,M,01:32:11,03:45:27 +33,M,01:47:28,03:45:27 +50,M,01:43:34,03:45:28 +40,M,01:40:15,03:45:28 +54,M,01:43:05,03:45:28 +30,M,01:42:04,03:45:29 +41,M,01:36:54,03:45:29 +52,M,01:46:20,03:45:29 +40,M,01:46:55,03:45:30 +39,M,01:39:07,03:45:30 +41,M,01:38:20,03:45:30 +26,M,01:33:59,03:45:30 +44,M,01:45:25,03:45:31 +51,M,01:43:15,03:45:31 +35,M,01:34:01,03:45:31 +39,M,01:39:23,03:45:32 +27,W,01:51:54,03:45:32 +46,M,01:39:30,03:45:32 +27,M,01:41:04,03:45:32 +30,M,01:28:11,03:45:33 +19,M,01:39:45,03:45:34 +48,M,01:47:36,03:45:34 +39,W,01:47:17,03:45:34 +44,W,01:47:58,03:45:35 +37,M,01:41:08,03:45:35 +36,M,01:41:40,03:45:35 +47,W,01:44:08,03:45:35 +45,M,01:37:16,03:45:35 +53,M,01:47:43,03:45:35 +43,M,01:48:22,03:45:35 +35,W,01:45:53,03:45:36 +51,M,01:40:01,03:45:37 +37,M,01:40:13,03:45:37 +26,M,01:32:30,03:45:37 +41,M,01:48:14,03:45:38 +36,M,01:49:54,03:45:38 +30,M,01:48:16,03:45:38 +41,M,01:40:33,03:45:39 +36,M,01:46:11,03:45:39 +36,W,01:44:07,03:45:39 +44,W,01:39:26,03:45:40 +38,W,01:51:52,03:45:40 +40,W,01:51:19,03:45:40 +39,M,01:44:12,03:45:40 +41,W,01:48:20,03:45:42 +62,M,01:47:50,03:45:42 +24,W,01:45:02,03:45:43 +36,M,01:50:15,03:45:43 +34,W,01:34:37,03:45:43 +34,M,01:34:41,03:45:44 +40,M,01:46:14,03:45:45 +25,M,01:42:58,03:45:45 +41,M,01:47:28,03:45:46 +37,M,01:48:39,03:45:46 +26,W,01:43:40,03:45:47 +45,W,01:44:12,03:45:47 +47,M,01:36:27,03:45:47 +56,M,01:46:42,03:45:47 +52,M,01:49:40,03:45:48 +56,M,01:43:13,03:45:49 +41,M,01:43:53,03:45:49 +41,M,01:43:17,03:45:50 +39,M,01:44:26,03:45:50 +45,M,01:48:54,03:45:50 +24,M,01:47:07,03:45:51 +33,M,01:32:19,03:45:51 +53,M,01:39:34,03:45:51 +47,M,01:45:57,03:45:51 +41,M,01:37:28,03:45:52 +58,M,01:36:36,03:45:52 +65,M,01:37:36,03:45:52 +30,M,01:40:14,03:45:52 +34,M,01:53:44,03:45:53 +39,M,01:46:18,03:45:53 +55,M,01:47:35,03:45:53 +34,M,01:42:38,03:45:53 +39,W,01:49:03,03:45:54 +39,M,01:51:47,03:45:54 +39,M,01:48:03,03:45:54 +28,M,01:39:19,03:45:55 +24,M,01:34:24,03:45:55 +27,W,01:56:03,03:45:55 +41,M,01:49:32,03:45:55 +35,M,01:39:56,03:45:55 +37,M,01:43:15,03:45:55 +38,M,01:36:03,03:45:56 +40,M,01:46:42,03:45:56 +43,M,01:43:45,03:45:56 +42,W,01:45:26,03:45:56 +46,W,01:44:21,03:45:56 +45,M,01:43:36,03:45:56 +36,M,01:43:09,03:45:56 +41,M,01:40:21,03:45:57 +23,M,01:40:09,03:45:57 +42,M,01:49:36,03:45:58 +26,M,01:35:35,03:45:59 +49,M,01:38:55,03:45:59 +45,M,01:46:01,03:45:59 +39,W,01:39:47,03:45:59 +31,M,01:43:35,03:46:00 +41,M,01:42:24,03:46:00 +36,M,01:44:07,03:46:00 +33,M,01:47:34,03:46:00 +24,M,01:42:35,03:46:01 +31,M,01:43:57,03:46:01 +39,W,01:41:07,03:46:01 +39,M,01:50:09,03:46:02 +48,W,01:48:50,03:46:02 +50,W,01:50:22,03:46:02 +45,M,01:49:39,03:46:02 +52,M,01:40:58,03:46:02 +54,M,01:44:38,03:46:02 +34,M,01:46:01,03:46:03 +39,M,01:39:37,03:46:04 +58,M,01:42:31,03:46:04 +42,M,01:43:08,03:46:04 +57,M,01:52:53,03:46:04 +31,M,01:48:53,03:46:05 +48,M,01:37:13,03:46:05 +44,M,01:44:17,03:46:05 +38,M,01:45:03,03:46:05 +45,M,01:45:30,03:46:06 +45,M,01:44:39,03:46:06 +45,M,01:37:45,03:46:07 +41,M,01:46:26,03:46:07 +30,M,01:47:33,03:46:07 +30,W,01:49:54,03:46:07 +46,M,01:41:18,03:46:07 +32,W,01:49:07,03:46:07 +48,M,01:45:04,03:46:07 +51,M,01:39:33,03:46:08 +46,M,01:41:14,03:46:08 +38,M,01:42:32,03:46:09 +18,M,01:35:38,03:46:09 +27,M,01:41:01,03:46:09 +38,M,01:39:43,03:46:10 +26,M,01:45:19,03:46:10 +53,M,01:39:24,03:46:11 +29,M,01:50:18,03:46:11 +47,M,01:38:11,03:46:11 +27,M,01:48:59,03:46:13 +57,M,01:49:58,03:46:13 +31,M,01:41:50,03:46:13 +58,W,01:45:12,03:46:14 +42,M,01:48:18,03:46:14 +42,M,01:51:31,03:46:14 +42,W,01:51:18,03:46:15 +23,W,01:43:48,03:46:15 +44,M,01:47:23,03:46:15 +42,W,01:45:32,03:46:16 +52,M,01:43:51,03:46:16 +30,M,01:51:44,03:46:16 +56,M,01:45:33,03:46:16 +51,M,01:43:19,03:46:17 +43,W,01:45:18,03:46:17 +27,M,01:48:51,03:46:17 +44,W,01:49:46,03:46:18 +28,M,01:41:50,03:46:18 +44,M,01:38:25,03:46:18 +29,W,01:51:12,03:46:18 +41,W,01:47:43,03:46:21 +44,W,01:50:40,03:46:21 +39,M,01:43:09,03:46:21 +44,W,01:49:44,03:46:22 +44,M,01:46:42,03:46:24 +38,M,01:46:53,03:46:25 +42,M,01:48:59,03:46:25 +45,M,01:54:06,03:46:26 +27,W,01:48:18,03:46:26 +38,M,01:34:13,03:46:26 +43,M,01:36:49,03:46:27 +24,W,01:50:32,03:46:27 +48,W,01:45:16,03:46:28 +57,M,01:43:31,03:46:28 +44,M,01:47:53,03:46:29 +39,W,01:51:06,03:46:29 +52,M,01:40:14,03:46:29 +43,M,01:44:06,03:46:29 +44,M,01:44:17,03:46:30 +40,M,01:43:42,03:46:31 +42,M,01:48:58,03:46:33 +22,W,01:44:17,03:46:34 +40,M,01:38:50,03:46:34 +44,M,01:51:27,03:46:34 +43,M,01:46:25,03:46:34 +33,M,01:34:36,03:46:35 +40,M,01:46:41,03:46:35 +45,M,01:40:29,03:46:35 +40,M,01:41:21,03:46:35 +39,M,01:44:40,03:46:36 +41,M,01:48:25,03:46:37 +47,W,01:49:32,03:46:38 +42,M,01:40:18,03:46:39 +41,W,01:50:22,03:46:39 +40,W,01:48:17,03:46:39 +39,M,01:49:26,03:46:40 +39,W,01:46:26,03:46:40 +44,M,01:41:45,03:46:40 +22,M,01:39:51,03:46:40 +57,M,01:49:12,03:46:41 +27,W,01:53:47,03:46:41 +43,M,01:44:14,03:46:41 +48,M,01:44:19,03:46:42 +41,M,01:49:59,03:46:42 +58,M,01:33:18,03:46:44 +39,W,01:53:27,03:46:44 +38,W,01:52:26,03:46:45 +39,W,01:49:20,03:46:47 +44,W,01:45:42,03:46:47 +48,W,01:48:35,03:46:47 +30,M,01:35:58,03:46:48 +36,M,01:47:54,03:46:48 +46,M,01:39:20,03:46:48 +44,M,01:49:24,03:46:48 +26,W,01:49:24,03:46:48 +36,M,01:47:51,03:46:49 +58,W,01:39:24,03:46:49 +31,M,01:40:41,03:46:49 +50,M,01:44:59,03:46:49 +29,W,01:44:23,03:46:49 +46,M,01:47:35,03:46:49 +25,W,01:49:27,03:46:50 +38,M,01:45:03,03:46:50 +38,M,01:39:30,03:46:50 +30,M,01:42:30,03:46:51 +43,M,01:50:04,03:46:51 +46,M,01:47:00,03:46:52 +31,W,01:53:23,03:46:52 +47,M,01:38:39,03:46:52 +33,M,01:42:25,03:46:53 +28,M,01:44:29,03:46:53 +46,M,01:40:08,03:46:54 +50,M,01:39:48,03:46:54 +42,M,01:49:59,03:46:54 +44,M,01:43:08,03:46:54 +33,W,01:40:13,03:46:55 +54,M,01:42:35,03:46:55 +30,M,01:33:41,03:46:56 +31,M,01:41:21,03:46:56 +51,M,01:39:08,03:46:56 +50,M,01:47:20,03:46:57 +36,M,01:39:45,03:46:57 +47,M,01:47:01,03:46:57 +34,M,01:45:24,03:46:58 +42,M,01:41:42,03:46:58 +41,M,01:42:22,03:46:58 +37,M,01:48:58,03:46:58 +25,W,01:41:42,03:46:58 +29,M,01:40:35,03:46:58 +28,W,01:49:25,03:46:58 +36,M,01:39:53,03:46:59 +46,M,01:40:35,03:46:59 +54,M,01:48:03,03:46:59 +47,M,01:40:20,03:47:00 +28,M,01:42:45,03:47:00 +43,M,01:44:33,03:47:00 +50,M,01:44:56,03:47:00 +33,M,01:43:39,03:47:00 +39,M,01:44:53,03:47:00 +53,M,01:45:12,03:47:01 +31,M,01:45:42,03:47:01 +44,M,01:40:56,03:47:02 +23,M,01:41:43,03:47:02 +42,M,01:38:15,03:47:02 +30,M,01:47:40,03:47:03 +60,M,01:50:13,03:47:04 +27,W,01:44:52,03:47:04 +52,W,01:41:46,03:47:04 +43,M,01:48:57,03:47:06 +36,W,01:49:12,03:47:06 +49,M,01:42:21,03:47:06 +40,M,01:52:05,03:47:06 +30,M,01:50:48,03:47:06 +42,M,01:51:11,03:47:06 +61,M,01:45:50,03:47:06 +34,M,01:39:43,03:47:06 +52,M,01:45:07,03:47:07 +20,M,01:43:58,03:47:07 +49,M,01:44:40,03:47:07 +40,M,01:41:02,03:47:07 +41,M,01:47:37,03:47:07 +58,W,01:40:53,03:47:07 +40,M,01:36:38,03:47:07 +45,M,01:45:16,03:47:08 +46,M,01:49:31,03:47:08 +30,M,01:45:58,03:47:08 +38,M,01:44:26,03:47:08 +24,W,01:48:52,03:47:09 +33,M,01:50:54,03:47:10 +30,M,01:47:24,03:47:10 +66,M,01:39:30,03:47:10 +36,M,01:43:23,03:47:11 +40,W,01:46:16,03:47:12 +48,M,01:45:26,03:47:12 +39,M,01:44:05,03:47:12 +35,M,01:49:03,03:47:13 +26,M,01:43:42,03:47:13 +23,M,01:47:02,03:47:13 +41,W,01:51:28,03:47:13 +37,M,01:42:20,03:47:13 +42,M,01:43:33,03:47:14 +49,M,01:47:09,03:47:15 +45,M,01:47:34,03:47:15 +45,M,01:44:16,03:47:16 +47,W,01:48:05,03:47:16 +48,M,01:40:38,03:47:17 +44,W,01:50:01,03:47:17 +28,M,01:43:15,03:47:18 +36,W,01:49:55,03:47:18 +48,M,01:37:20,03:47:19 +43,M,01:43:05,03:47:19 +57,M,01:51:51,03:47:19 +42,M,01:39:33,03:47:19 +23,M,01:40:48,03:47:19 +47,W,01:45:33,03:47:20 +40,M,01:42:42,03:47:20 +42,M,01:40:52,03:47:21 +32,M,01:49:30,03:47:22 +35,M,01:42:10,03:47:22 +33,M,01:37:20,03:47:22 +42,M,01:45:26,03:47:22 +36,M,01:40:03,03:47:22 +51,W,01:49:44,03:47:23 +54,M,01:45:34,03:47:23 +34,M,01:37:38,03:47:23 +28,W,01:52:26,03:47:24 +38,M,01:40:49,03:47:24 +55,W,01:48:19,03:47:24 +45,M,01:47:53,03:47:25 +36,W,01:52:53,03:47:25 +53,M,01:49:48,03:47:26 +60,M,01:48:15,03:47:26 +32,M,01:44:07,03:47:27 +50,M,01:45:42,03:47:27 +52,M,01:49:10,03:47:27 +24,W,01:50:22,03:47:27 +52,M,01:38:33,03:47:28 +46,M,01:52:58,03:47:28 +29,M,01:48:21,03:47:28 +50,M,01:50:37,03:47:28 +63,M,01:49:33,03:47:28 +33,M,01:49:24,03:47:29 +31,W,01:46:50,03:47:30 +44,W,01:49:43,03:47:30 +45,M,01:42:06,03:47:30 +37,W,01:49:29,03:47:31 +33,M,01:48:16,03:47:32 +50,M,01:45:10,03:47:32 +36,W,01:48:36,03:47:32 +42,M,01:40:47,03:47:33 +44,M,01:43:12,03:47:33 +30,M,01:49:18,03:47:34 +35,M,01:50:21,03:47:34 +33,M,01:46:20,03:47:35 +25,M,01:46:03,03:47:35 +45,M,01:43:02,03:47:35 +44,M,01:46:13,03:47:36 +39,M,01:47:25,03:47:36 +44,M,01:50:22,03:47:37 +36,M,01:40:20,03:47:37 +39,M,01:49:10,03:47:37 +45,M,01:41:50,03:47:37 +31,W,01:50:14,03:47:38 +45,M,01:35:24,03:47:38 +29,M,01:44:42,03:47:38 +34,M,01:45:35,03:47:39 +24,W,01:47:59,03:47:39 +54,M,01:42:39,03:47:40 +39,M,01:39:02,03:47:40 +50,M,01:44:17,03:47:41 +49,W,01:51:02,03:47:42 +43,M,01:41:57,03:47:42 +58,M,01:39:59,03:47:42 +40,W,01:46:28,03:47:42 +51,M,01:49:43,03:47:42 +57,M,01:42:16,03:47:42 +58,M,01:42:25,03:47:43 +38,M,01:48:18,03:47:43 +30,M,01:36:47,03:47:44 +39,W,01:48:43,03:47:44 +27,W,01:47:03,03:47:45 +45,W,01:45:52,03:47:45 +42,M,01:46:46,03:47:45 +54,M,01:42:35,03:47:46 +43,M,01:41:15,03:47:46 +34,M,01:39:30,03:47:46 +31,M,01:37:03,03:47:47 +29,M,01:48:36,03:47:47 +31,M,01:40:54,03:47:49 +44,M,01:39:46,03:47:49 +44,M,01:48:00,03:47:50 +40,M,01:51:22,03:47:50 +42,W,01:47:16,03:47:51 +55,M,01:42:37,03:47:51 +19,M,01:40:06,03:47:51 +40,M,01:45:00,03:47:51 +30,M,01:47:45,03:47:52 +50,M,01:41:37,03:47:52 +34,M,01:37:48,03:47:52 +30,M,01:44:03,03:47:52 +32,W,01:48:28,03:47:52 +40,M,01:48:19,03:47:53 +39,M,01:46:50,03:47:53 +40,M,01:46:41,03:47:54 +33,M,01:43:28,03:47:54 +33,M,01:42:22,03:47:54 +43,M,01:49:28,03:47:54 +44,M,01:45:52,03:47:54 +35,W,01:52:03,03:47:55 +58,M,01:39:18,03:47:55 +34,M,01:42:52,03:47:56 +31,W,01:41:12,03:47:56 +60,M,01:48:48,03:47:56 +26,M,01:43:06,03:47:56 +32,M,01:41:09,03:47:57 +26,M,01:52:58,03:47:57 +44,M,01:40:25,03:47:57 +45,W,01:51:10,03:47:58 +51,M,01:42:05,03:47:58 +40,M,01:40:40,03:47:59 +40,M,01:37:36,03:48:01 +29,M,01:40:19,03:48:01 +33,M,01:46:10,03:48:01 +39,M,01:47:58,03:48:01 +33,M,01:41:26,03:48:02 +45,M,01:41:35,03:48:02 +28,M,01:39:32,03:48:03 +46,M,01:49:37,03:48:04 +39,M,01:43:01,03:48:04 +26,W,01:45:17,03:48:05 +59,W,01:47:29,03:48:06 +39,M,01:44:00,03:48:06 +50,M,01:45:30,03:48:06 +39,M,01:40:03,03:48:06 +31,M,01:48:33,03:48:07 +29,M,01:53:31,03:48:07 +47,M,01:48:35,03:48:07 +28,M,01:48:24,03:48:08 +33,W,01:46:47,03:48:08 +40,M,01:43:23,03:48:08 +27,M,01:41:29,03:48:08 +45,M,01:47:46,03:48:09 +31,M,01:33:02,03:48:09 +48,M,01:45:09,03:48:09 +43,W,01:47:21,03:48:09 +23,M,01:43:27,03:48:09 +41,M,01:43:30,03:48:10 +54,W,01:51:08,03:48:11 +62,M,01:39:43,03:48:11 +48,M,01:45:58,03:48:11 +52,M,01:45:07,03:48:11 +41,W,01:47:54,03:48:11 +47,M,01:51:53,03:48:12 +48,M,01:43:46,03:48:13 +60,M,01:50:20,03:48:13 +30,W,01:48:26,03:48:14 +50,M,01:46:41,03:48:14 +48,M,01:46:41,03:48:14 +53,M,01:50:46,03:48:15 +47,M,01:48:03,03:48:16 +39,M,01:42:13,03:48:16 +32,M,01:39:11,03:48:16 +42,M,01:42:49,03:48:16 +35,M,01:40:29,03:48:17 +31,M,01:42:19,03:48:17 +42,M,01:47:11,03:48:17 +65,M,01:50:12,03:48:18 +43,M,01:49:32,03:48:18 +35,W,01:48:28,03:48:18 +49,M,01:47:11,03:48:18 +43,M,01:49:16,03:48:19 +53,M,01:42:11,03:48:19 +30,M,01:47:00,03:48:19 +36,M,01:44:42,03:48:20 +45,M,01:46:39,03:48:20 +29,W,01:48:46,03:48:20 +21,M,01:44:41,03:48:20 +40,M,01:36:47,03:48:20 +35,M,01:39:55,03:48:22 +41,W,01:49:11,03:48:22 +44,M,01:50:00,03:48:23 +39,M,01:41:15,03:48:23 +45,M,01:50:46,03:48:24 +51,M,01:37:08,03:48:24 +36,M,01:37:20,03:48:25 +29,M,01:31:28,03:48:25 +52,M,01:31:07,03:48:25 +59,M,01:46:41,03:48:25 +47,M,01:47:44,03:48:26 +44,W,01:45:47,03:48:26 +41,M,01:36:35,03:48:27 +52,M,01:39:09,03:48:27 +56,M,01:30:39,03:48:28 +50,M,01:44:04,03:48:28 +25,W,01:41:22,03:48:28 +40,M,01:46:20,03:48:28 +42,M,01:37:12,03:48:29 +48,M,01:43:15,03:48:30 +48,W,01:51:33,03:48:31 +30,M,01:45:42,03:48:31 +28,M,01:50:36,03:48:32 +43,M,01:40:27,03:48:32 +43,M,01:44:28,03:48:33 +25,W,01:48:32,03:48:33 +39,M,01:48:37,03:48:33 +42,M,01:44:47,03:48:33 +57,W,01:47:38,03:48:33 +37,W,01:43:21,03:48:34 +40,W,01:48:32,03:48:34 +54,M,01:46:17,03:48:34 +48,W,01:42:53,03:48:34 +33,W,01:52:33,03:48:35 +54,M,01:49:49,03:48:35 +42,W,01:29:06,03:48:35 +52,M,01:49:24,03:48:35 +44,M,01:43:31,03:48:35 +44,M,01:42:11,03:48:36 +40,M,01:42:42,03:48:36 +35,M,01:37:44,03:48:37 +46,M,01:39:21,03:48:37 +39,M,01:51:20,03:48:37 +32,M,01:49:37,03:48:37 +38,M,01:49:18,03:48:38 +47,M,01:43:36,03:48:38 +32,M,01:41:06,03:48:38 +30,M,01:39:03,03:48:38 +42,M,01:46:22,03:48:39 +63,M,01:50:12,03:48:39 +49,M,01:45:41,03:48:39 +26,M,01:42:50,03:48:40 +32,M,01:41:23,03:48:40 +33,M,01:38:55,03:48:41 +36,W,01:41:32,03:48:41 +35,M,01:42:09,03:48:42 +50,M,01:44:52,03:48:43 +41,M,01:44:23,03:48:43 +39,M,01:48:28,03:48:43 +37,M,01:51:20,03:48:44 +47,M,01:56:08,03:48:44 +31,W,01:37:50,03:48:45 +36,M,01:39:48,03:48:45 +30,M,01:31:35,03:48:45 +40,M,01:45:47,03:48:46 +37,M,01:45:38,03:48:46 +36,M,01:42:49,03:48:47 +27,M,01:41:34,03:48:47 +30,M,01:47:35,03:48:47 +53,M,01:40:47,03:48:47 +56,M,01:46:15,03:48:48 +40,W,01:48:50,03:48:48 +52,M,01:41:51,03:48:49 +43,M,01:36:05,03:48:49 +44,M,01:48:02,03:48:49 +54,M,01:50:41,03:48:50 +38,M,01:45:47,03:48:50 +46,M,01:49:15,03:48:50 +42,M,01:39:52,03:48:50 +36,M,01:40:35,03:48:50 +34,M,01:47:22,03:48:51 +45,M,01:48:38,03:48:51 +28,M,01:36:18,03:48:51 +46,M,01:42:38,03:48:52 +47,M,01:52:06,03:48:52 +42,W,01:44:20,03:48:52 +26,W,01:46:50,03:48:53 +49,M,01:38:51,03:48:54 +27,W,01:48:12,03:48:54 +45,W,01:44:57,03:48:54 +45,M,01:42:12,03:48:54 +46,M,01:41:39,03:48:54 +34,M,01:45:54,03:48:54 +39,M,01:33:53,03:48:54 +44,W,01:47:35,03:48:55 +28,M,01:43:14,03:48:55 +52,M,01:52:08,03:48:57 +42,M,01:47:28,03:48:57 +45,M,01:42:59,03:48:57 +53,M,01:47:03,03:48:57 +26,M,01:48:04,03:48:57 +43,W,01:47:23,03:48:57 +30,M,01:45:25,03:48:58 +48,W,01:54:31,03:48:58 +46,W,01:47:42,03:48:58 +34,M,01:38:24,03:48:58 +45,M,01:46:18,03:48:59 +47,M,01:42:07,03:48:59 +31,M,01:42:52,03:48:59 +42,W,01:47:26,03:49:00 +24,M,01:38:01,03:49:00 +42,M,01:39:24,03:49:01 +43,M,01:40:46,03:49:01 +26,W,01:40:30,03:49:02 +33,M,01:48:31,03:49:02 +38,M,01:44:02,03:49:02 +35,M,01:42:57,03:49:02 +36,M,01:38:26,03:49:02 +43,M,01:45:21,03:49:02 +53,M,01:44:33,03:49:03 +50,M,01:51:49,03:49:03 +22,M,01:41:36,03:49:03 +42,M,01:40:57,03:49:04 +39,M,01:49:12,03:49:04 +51,M,01:41:19,03:49:04 +26,M,01:44:46,03:49:04 +41,M,01:49:31,03:49:05 +46,M,01:39:31,03:49:06 +43,M,01:41:24,03:49:06 +35,M,01:47:51,03:49:07 +36,W,01:51:09,03:49:07 +42,M,01:39:43,03:49:08 +48,M,01:48:02,03:49:08 +41,M,01:47:13,03:49:08 +32,W,01:38:32,03:49:08 +32,W,01:45:47,03:49:09 +54,M,01:44:37,03:49:10 +48,M,01:52:24,03:49:10 +56,M,01:48:02,03:49:10 +31,M,01:47:55,03:49:10 +55,M,01:49:41,03:49:10 +50,W,01:53:10,03:49:12 +23,M,01:47:06,03:49:12 +42,M,01:47:53,03:49:12 +45,M,01:40:09,03:49:13 +34,W,01:45:11,03:49:13 +43,M,01:45:50,03:49:14 +60,M,01:47:44,03:49:14 +60,M,01:47:30,03:49:14 +49,W,01:44:54,03:49:15 +37,M,01:44:21,03:49:15 +39,W,01:43:44,03:49:15 +36,M,01:45:29,03:49:15 +45,M,01:53:16,03:49:17 +26,M,01:47:56,03:49:18 +35,M,01:45:20,03:49:18 +40,M,01:49:41,03:49:18 +46,M,01:46:05,03:49:18 +43,M,01:48:06,03:49:19 +26,M,01:44:29,03:49:19 +32,M,01:41:45,03:49:19 +44,M,01:51:26,03:49:19 +43,M,01:46:48,03:49:20 +25,W,01:48:57,03:49:20 +37,M,01:39:26,03:49:20 +51,M,01:44:32,03:49:20 +41,M,01:37:37,03:49:20 +42,W,01:44:31,03:49:20 +44,M,01:42:42,03:49:20 +50,M,01:42:19,03:49:21 +71,M,01:52:46,03:49:21 +30,M,01:42:04,03:49:22 +34,M,01:38:45,03:49:22 +33,M,01:48:06,03:49:22 +44,M,01:48:28,03:49:23 +26,W,01:47:15,03:49:24 +38,M,01:44:25,03:49:24 +53,M,01:39:18,03:49:24 +29,M,01:43:12,03:49:24 +36,M,01:40:41,03:49:25 +42,W,01:51:31,03:49:25 +36,M,01:41:01,03:49:25 +40,M,01:45:08,03:49:25 +39,M,01:42:53,03:49:26 +36,W,01:44:53,03:49:27 +61,M,01:52:51,03:49:28 +60,M,01:51:19,03:49:28 +31,M,01:31:06,03:49:28 +34,M,01:56:34,03:49:28 +33,M,01:47:44,03:49:30 +37,M,01:49:05,03:49:30 +43,W,01:47:53,03:49:30 +33,W,01:44:08,03:49:31 +25,M,01:42:57,03:49:31 +39,W,01:51:58,03:49:33 +36,M,01:46:32,03:49:33 +29,M,01:42:43,03:49:33 +31,M,01:47:55,03:49:33 +30,M,01:33:55,03:49:33 +47,M,01:48:28,03:49:33 +33,M,01:39:01,03:49:33 +29,M,01:40:38,03:49:33 +46,M,01:44:04,03:49:34 +43,W,01:50:47,03:49:35 +41,M,01:50:19,03:49:35 +58,M,01:47:02,03:49:36 +26,W,01:52:38,03:49:37 +47,M,01:38:52,03:49:38 +36,M,01:43:00,03:49:38 +39,M,01:44:08,03:49:38 +49,W,01:48:00,03:49:38 +36,W,01:38:28,03:49:38 +40,M,01:44:07,03:49:38 +43,W,01:50:03,03:49:38 +58,M,01:45:34,03:49:39 +35,M,01:39:56,03:49:39 +43,M,01:44:10,03:49:39 +48,M,01:42:15,03:49:40 +30,W,01:44:25,03:49:40 +40,M,01:45:25,03:49:40 +36,M,01:50:59,03:49:41 +30,M,01:47:24,03:49:41 +44,M,01:50:25,03:49:41 +39,M,01:45:04,03:49:42 +35,M,01:42:27,03:49:43 +46,M,01:38:16,03:49:43 +27,M,01:49:01,03:49:43 +48,M,01:53:48,03:49:43 +27,M,01:44:22,03:49:43 +54,M,01:47:12,03:49:44 +48,M,01:54:23,03:49:44 +26,M,01:51:15,03:49:44 +50,M,01:43:32,03:49:45 +34,M,01:50:07,03:49:45 +36,M,01:45:15,03:49:45 +44,W,01:47:10,03:49:45 +38,M,01:51:21,03:49:46 +33,M,01:44:13,03:49:46 +27,M,01:50:10,03:49:47 +35,W,01:52:05,03:49:47 +45,M,01:43:28,03:49:47 +52,M,01:45:01,03:49:47 +24,W,01:44:50,03:49:48 +34,M,01:45:15,03:49:49 +39,M,01:38:23,03:49:49 +47,M,01:38:40,03:49:49 +46,W,01:44:42,03:49:50 +52,M,01:44:08,03:49:50 +35,W,01:42:08,03:49:50 +33,M,01:35:56,03:49:50 +40,W,01:42:33,03:49:51 +23,M,01:39:18,03:49:51 +38,M,01:41:02,03:49:51 +47,M,01:42:37,03:49:52 +39,M,01:42:41,03:49:53 +41,M,01:45:47,03:49:53 +36,M,01:45:06,03:49:53 +32,W,01:44:11,03:49:53 +47,M,01:40:50,03:49:54 +31,M,01:44:19,03:49:54 +41,W,01:53:46,03:49:54 +46,M,01:47:55,03:49:54 +65,M,01:47:13,03:49:55 +37,M,01:45:04,03:49:55 +51,M,01:55:57,03:49:57 +41,M,01:53:38,03:49:57 +48,M,01:49:15,03:49:57 +39,M,01:53:26,03:49:58 +44,W,01:50:08,03:49:58 +43,M,01:36:30,03:49:58 +26,W,01:50:05,03:49:59 +43,M,01:38:29,03:49:59 +39,M,01:52:40,03:49:59 +46,M,01:47:57,03:49:59 +31,M,01:39:23,03:49:59 +34,M,01:40:46,03:50:00 +36,W,01:51:22,03:50:00 +43,M,01:37:53,03:50:00 +25,M,01:40:40,03:50:02 +48,M,01:42:08,03:50:02 +38,M,01:44:06,03:50:02 +37,M,01:45:13,03:50:02 +42,M,01:43:31,03:50:02 +34,M,01:47:20,03:50:03 +44,M,01:54:30,03:50:03 +33,M,01:49:54,03:50:04 +31,M,01:40:12,03:50:04 +41,M,01:42:31,03:50:04 +30,M,01:44:21,03:50:05 +45,M,01:44:51,03:50:05 +45,M,01:41:30,03:50:06 +36,M,01:46:13,03:50:06 +51,M,01:41:46,03:50:07 +44,M,01:41:22,03:50:07 +26,M,01:36:03,03:50:07 +35,M,01:43:30,03:50:08 +55,M,01:44:07,03:50:08 +40,M,01:48:12,03:50:08 +38,M,01:51:48,03:50:08 +34,M,01:42:34,03:50:09 +27,M,01:42:24,03:50:10 +38,W,01:46:36,03:50:10 +62,M,01:41:32,03:50:10 +51,M,01:44:58,03:50:11 +58,M,01:48:41,03:50:11 +54,M,01:50:46,03:50:11 +26,M,01:50:18,03:50:12 +45,M,01:46:19,03:50:12 +47,M,01:44:18,03:50:12 +29,M,01:49:09,03:50:12 +24,W,01:41:15,03:50:13 +34,M,01:40:33,03:50:13 +35,W,01:41:53,03:50:14 +42,M,01:44:08,03:50:14 +44,M,01:44:04,03:50:14 +50,M,01:49:41,03:50:14 +56,M,01:39:42,03:50:14 +46,M,01:43:55,03:50:15 +30,M,01:47:18,03:50:15 +40,M,01:46:14,03:50:16 +42,M,01:40:32,03:50:16 +49,M,01:42:06,03:50:17 +42,M,01:52:47,03:50:18 +50,M,01:40:59,03:50:18 +43,M,01:48:17,03:50:18 +54,M,01:43:54,03:50:19 +48,M,01:46:34,03:50:20 +31,M,01:37:37,03:50:21 +32,M,01:47:48,03:50:21 +59,M,01:46:25,03:50:21 +55,M,01:48:02,03:50:21 +56,M,01:49:13,03:50:21 +31,M,01:44:00,03:50:22 +30,W,01:40:50,03:50:22 +28,W,01:49:02,03:50:23 +40,M,01:52:56,03:50:23 +30,M,01:39:52,03:50:23 +25,W,01:48:24,03:50:24 +43,M,01:44:41,03:50:24 +48,M,01:48:02,03:50:25 +44,W,01:44:58,03:50:25 +49,M,01:43:27,03:50:25 +38,W,01:38:52,03:50:26 +29,W,01:50:19,03:50:27 +55,M,01:46:12,03:50:27 +36,M,01:38:26,03:50:28 +30,M,01:53:16,03:50:28 +31,M,01:40:54,03:50:28 +41,M,01:45:49,03:50:28 +49,M,01:32:11,03:50:29 +25,M,01:37:46,03:50:29 +35,M,01:44:05,03:50:29 +22,M,01:39:43,03:50:30 +37,M,01:41:13,03:50:31 +48,M,01:49:09,03:50:31 +55,M,01:49:21,03:50:31 +29,M,01:49:42,03:50:32 +41,M,01:48:09,03:50:32 +41,M,01:45:27,03:50:32 +44,M,01:43:25,03:50:33 +43,M,01:48:28,03:50:33 +27,M,01:49:13,03:50:33 +29,M,01:46:38,03:50:34 +30,M,01:46:25,03:50:35 +49,M,01:43:16,03:50:35 +45,M,01:39:58,03:50:35 +23,M,01:36:26,03:50:36 +44,M,01:46:25,03:50:36 +62,M,01:43:08,03:50:36 +41,M,01:41:05,03:50:37 +39,W,01:47:26,03:50:37 +32,M,01:41:31,03:50:37 +36,W,01:53:27,03:50:37 +50,M,01:53:19,03:50:37 +49,M,01:51:12,03:50:38 +33,M,01:48:42,03:50:38 +36,M,01:42:04,03:50:38 +42,M,01:48:19,03:50:38 +53,M,01:47:10,03:50:38 +44,W,01:47:52,03:50:39 +32,M,01:48:24,03:50:39 +46,M,01:47:23,03:50:39 +32,M,01:37:02,03:50:40 +38,M,01:42:05,03:50:41 +30,M,01:44:12,03:50:41 +53,M,01:36:59,03:50:41 +28,M,01:39:56,03:50:42 +35,M,01:44:09,03:50:44 +45,M,01:49:52,03:50:44 +41,M,01:40:37,03:50:44 +48,M,01:42:54,03:50:44 +41,M,01:36:04,03:50:44 +45,M,01:44:38,03:50:45 +53,M,01:41:20,03:50:46 +42,M,01:45:41,03:50:46 +31,W,01:47:11,03:50:46 +30,M,01:35:46,03:50:46 +50,M,01:39:17,03:50:46 +42,M,01:44:58,03:50:46 +33,M,01:47:58,03:50:47 +36,M,01:42:36,03:50:47 +26,M,01:39:26,03:50:47 +30,M,01:39:21,03:50:48 +35,M,01:42:00,03:50:48 +38,W,01:48:18,03:50:49 +42,M,01:51:59,03:50:49 +25,W,01:47:58,03:50:50 +34,M,01:38:39,03:50:50 +39,M,01:39:10,03:50:51 +42,W,01:45:25,03:50:51 +35,W,01:47:45,03:50:51 +39,M,01:45:02,03:50:51 +20,W,01:44:31,03:50:52 +32,M,01:39:28,03:50:53 +26,M,01:44:41,03:50:53 +46,M,01:44:37,03:50:54 +39,M,01:42:28,03:50:54 +40,M,01:57:23,03:50:54 +25,M,01:52:20,03:50:55 +39,M,01:44:24,03:50:55 +26,W,01:42:02,03:50:55 +38,M,01:46:33,03:50:55 +34,W,01:44:56,03:50:55 +24,W,01:48:46,03:50:56 +49,M,01:50:59,03:50:56 +55,M,01:49:11,03:50:57 +37,M,01:41:21,03:50:57 +37,W,01:40:58,03:50:57 +42,M,01:46:16,03:50:57 +50,M,01:43:47,03:50:58 +26,W,01:39:46,03:50:58 +29,W,01:54:52,03:50:58 +41,M,01:42:37,03:50:59 +50,M,01:49:47,03:51:00 +35,W,01:47:43,03:51:01 +59,W,01:49:45,03:51:01 +35,M,01:36:21,03:51:01 +44,M,01:48:15,03:51:01 +29,W,01:46:56,03:51:02 +33,M,01:48:27,03:51:02 +35,M,01:47:37,03:51:02 +41,M,01:46:38,03:51:03 +31,M,01:39:52,03:51:03 +54,M,01:46:51,03:51:03 +50,M,01:42:32,03:51:04 +44,M,01:47:52,03:51:04 +38,W,01:52:02,03:51:04 +27,W,01:43:15,03:51:04 +25,M,01:45:04,03:51:05 +41,M,01:37:27,03:51:05 +39,M,01:43:11,03:51:05 +61,M,01:43:19,03:51:05 +35,W,01:53:11,03:51:05 +67,M,01:48:58,03:51:06 +29,M,01:41:16,03:51:06 +46,M,01:49:07,03:51:07 +43,M,01:51:39,03:51:07 +30,W,01:40:13,03:51:08 +40,M,01:49:40,03:51:09 +34,M,01:52:50,03:51:09 +39,W,01:57:44,03:51:10 +41,M,01:43:49,03:51:10 +60,M,01:57:53,03:51:10 +28,W,01:40:26,03:51:11 +48,M,01:47:10,03:51:11 +40,M,01:50:13,03:51:11 +53,M,01:50:03,03:51:11 +49,M,01:38:56,03:51:11 +45,M,01:49:14,03:51:11 +37,M,01:39:03,03:51:12 +30,M,01:55:42,03:51:12 +57,M,01:51:44,03:51:12 +38,M,01:42:55,03:51:13 +28,M,01:42:32,03:51:13 +40,M,01:46:01,03:51:14 +40,W,01:47:13,03:51:14 +31,M,01:34:44,03:51:14 +60,M,01:41:07,03:51:15 +27,M,01:47:27,03:51:15 +39,M,01:45:15,03:51:15 +28,M,01:45:35,03:51:15 +41,M,01:42:07,03:51:16 +34,M,01:48:31,03:51:16 +45,W,01:52:35,03:51:18 +34,M,01:54:42,03:51:18 +34,W,01:48:44,03:51:18 +26,M,01:50:13,03:51:18 +42,W,01:50:08,03:51:19 +33,W,01:50:10,03:51:19 +40,W,01:43:25,03:51:19 +35,M,01:52:26,03:51:19 +38,W,01:43:15,03:51:20 +36,W,01:47:46,03:51:20 +32,M,01:47:03,03:51:20 +23,M,01:35:26,03:51:20 +29,M,01:43:28,03:51:20 +29,M,01:30:15,03:51:20 +45,M,01:49:51,03:51:21 +46,W,01:53:23,03:51:21 +46,M,01:39:47,03:51:21 +39,M,01:45:54,03:51:21 +57,M,01:44:53,03:51:21 +55,W,01:48:06,03:51:23 +59,M,01:49:51,03:51:23 +33,M,01:45:00,03:51:24 +38,M,01:57:17,03:51:24 +39,M,01:43:08,03:51:25 +49,M,01:49:16,03:51:26 +35,M,01:45:41,03:51:26 +34,M,01:40:09,03:51:26 +37,M,01:51:32,03:51:26 +45,W,01:53:53,03:51:26 +55,M,01:49:20,03:51:26 +50,M,01:48:09,03:51:26 +41,W,01:49:23,03:51:27 +25,M,01:31:55,03:51:27 +45,M,01:36:35,03:51:27 +34,W,01:49:02,03:51:28 +49,W,01:45:59,03:51:28 +47,W,01:49:59,03:51:30 +45,M,01:48:27,03:51:30 +51,W,01:53:35,03:51:30 +46,M,01:42:52,03:51:30 +50,M,01:48:22,03:51:31 +27,M,01:52:03,03:51:31 +50,M,01:34:15,03:51:31 +54,M,01:50:39,03:51:31 +32,W,01:44:11,03:51:32 +34,M,01:48:22,03:51:32 +30,M,01:41:46,03:51:33 +46,M,01:45:30,03:51:33 +29,M,01:50:18,03:51:34 +53,W,01:54:09,03:51:34 +46,M,01:45:47,03:51:35 +47,M,01:43:53,03:51:35 +50,M,01:46:18,03:51:35 +50,M,01:45:40,03:51:36 +28,M,01:54:11,03:51:36 +28,W,01:45:55,03:51:36 +52,M,01:37:09,03:51:36 +44,M,01:47:57,03:51:37 +27,M,01:55:33,03:51:37 +40,M,01:41:14,03:51:37 +38,M,01:45:00,03:51:37 +26,M,01:36:12,03:51:38 +54,M,01:52:13,03:51:38 +39,M,01:54:14,03:51:38 +44,M,01:47:17,03:51:39 +50,M,01:49:59,03:51:40 +29,M,01:44:20,03:51:40 +32,M,01:45:36,03:51:41 +22,M,01:45:36,03:51:41 +28,M,01:49:16,03:51:41 +37,M,01:51:24,03:51:41 +27,M,01:43:43,03:51:42 +45,M,01:49:02,03:51:43 +48,W,01:47:23,03:51:43 +37,M,01:48:44,03:51:43 +61,M,01:42:57,03:51:43 +39,W,01:58:43,03:51:43 +39,M,01:46:34,03:51:43 +36,M,01:49:33,03:51:44 +42,M,01:47:20,03:51:44 +27,M,01:44:50,03:51:44 +28,M,01:37:20,03:51:45 +43,W,01:45:08,03:51:46 +37,M,01:38:35,03:51:46 +29,M,01:46:52,03:51:46 +37,M,01:49:53,03:51:46 +59,M,01:43:03,03:51:47 +47,M,01:43:12,03:51:47 +34,M,01:45:24,03:51:47 +27,W,01:44:53,03:51:47 +38,W,01:44:44,03:51:47 +46,W,01:48:15,03:51:47 +45,M,01:55:07,03:51:47 +59,M,01:51:20,03:51:48 +44,W,01:44:08,03:51:48 +35,M,01:47:31,03:51:49 +29,M,01:35:36,03:51:49 +26,W,01:49:00,03:51:50 +39,M,01:45:33,03:51:51 +35,M,01:47:38,03:51:51 +44,W,01:47:04,03:51:54 +25,W,01:52:51,03:51:54 +55,M,01:43:53,03:51:54 +34,M,01:44:27,03:51:55 +57,M,01:44:54,03:51:55 +66,M,01:46:08,03:51:55 +37,M,01:48:41,03:51:56 +53,W,01:50:36,03:51:56 +25,M,01:43:35,03:51:58 +45,W,01:34:18,03:51:58 +36,W,01:43:30,03:51:58 +30,W,01:40:26,03:51:59 +56,M,01:48:33,03:51:59 +29,M,01:52:42,03:52:00 +28,M,01:37:03,03:52:00 +45,M,01:38:42,03:52:00 +25,M,01:46:48,03:52:00 +37,M,01:41:37,03:52:00 +44,M,01:42:13,03:52:00 +45,M,01:43:29,03:52:00 +44,M,01:44:41,03:52:01 +26,M,01:54:04,03:52:02 +31,M,01:38:09,03:52:02 +37,M,01:44:25,03:52:02 +41,M,01:48:38,03:52:02 +57,M,01:45:09,03:52:02 +35,M,01:43:51,03:52:03 +57,W,01:51:25,03:52:03 +43,M,01:39:06,03:52:03 +59,M,01:42:47,03:52:04 +36,M,01:55:12,03:52:04 +37,M,01:48:27,03:52:05 +42,M,01:49:23,03:52:05 +32,M,01:42:51,03:52:06 +47,M,01:45:34,03:52:06 +26,M,01:43:22,03:52:06 +18,M,01:38:16,03:52:07 +43,W,01:51:06,03:52:07 +28,M,01:54:20,03:52:08 +27,M,01:44:19,03:52:08 +41,M,01:40:59,03:52:08 +28,M,01:40:06,03:52:09 +48,M,01:42:49,03:52:09 +39,M,01:50:55,03:52:09 +40,M,01:42:52,03:52:10 +49,M,01:47:03,03:52:10 +30,W,01:48:41,03:52:11 +41,M,01:42:35,03:52:11 +33,M,01:41:18,03:52:11 +47,M,01:52:11,03:52:12 +55,M,01:54:15,03:52:13 +40,W,01:51:38,03:52:13 +46,W,01:52:00,03:52:13 +39,W,01:49:45,03:52:13 +51,M,01:43:51,03:52:15 +45,M,01:53:42,03:52:15 +41,W,01:51:56,03:52:15 +32,M,01:43:54,03:52:15 +42,M,01:41:22,03:52:15 +37,W,01:54:59,03:52:16 +27,M,01:36:37,03:52:16 +46,M,01:41:38,03:52:17 +65,M,01:45:55,03:52:17 +42,M,01:49:19,03:52:17 +33,W,01:51:25,03:52:19 +45,M,01:41:48,03:52:19 +49,M,01:48:00,03:52:19 +51,W,01:43:52,03:52:19 +28,M,01:41:31,03:52:19 +51,M,01:54:28,03:52:19 +28,M,01:42:54,03:52:20 +51,M,01:43:30,03:52:20 +35,M,01:43:41,03:52:20 +45,W,01:49:52,03:52:21 +43,M,01:50:04,03:52:21 +54,M,01:51:15,03:52:22 +42,M,01:44:22,03:52:22 +43,M,01:47:37,03:52:22 +31,M,01:53:27,03:52:23 +26,M,01:47:42,03:52:23 +31,M,01:48:14,03:52:24 +59,M,01:45:15,03:52:24 +37,M,01:36:19,03:52:24 +58,M,01:41:07,03:52:25 +35,M,01:38:39,03:52:25 +45,M,01:42:21,03:52:26 +42,M,01:42:04,03:52:26 +37,M,01:52:41,03:52:26 +50,M,01:43:30,03:52:26 +35,M,01:47:47,03:52:27 +50,M,01:44:54,03:52:27 +53,M,01:50:10,03:52:28 +33,M,01:53:45,03:52:29 +46,M,01:44:29,03:52:29 +48,M,01:43:27,03:52:30 +41,M,01:52:31,03:52:30 +34,W,01:45:36,03:52:31 +41,M,01:48:05,03:52:32 +37,W,01:52:43,03:52:33 +43,M,01:43:01,03:52:33 +36,W,01:45:08,03:52:33 +38,M,01:48:27,03:52:34 +47,M,01:43:54,03:52:34 +37,M,01:41:52,03:52:34 +29,W,01:47:48,03:52:34 +39,M,01:47:42,03:52:34 +38,M,01:50:35,03:52:35 +39,M,01:49:50,03:52:35 +45,M,01:44:16,03:52:35 +36,M,01:51:40,03:52:35 +27,M,01:40:59,03:52:35 +45,M,01:48:08,03:52:36 +45,M,01:44:36,03:52:36 +34,M,01:43:55,03:52:36 +34,M,01:52:41,03:52:37 +37,M,01:45:11,03:52:37 +43,W,01:43:38,03:52:37 +41,M,01:47:39,03:52:38 +32,M,01:41:15,03:52:39 +28,M,01:51:08,03:52:40 +34,W,01:50:02,03:52:40 +31,M,01:42:00,03:52:41 +50,M,01:45:03,03:52:41 +35,M,01:40:58,03:52:41 +57,M,01:44:16,03:52:41 +37,M,01:41:03,03:52:42 +34,M,01:43:57,03:52:42 +34,M,01:47:16,03:52:42 +34,W,01:48:27,03:52:42 +42,W,01:47:50,03:52:42 +46,M,01:50:30,03:52:42 +33,M,01:43:46,03:52:43 +28,W,01:48:19,03:52:43 +39,M,01:49:35,03:52:43 +39,M,01:47:57,03:52:43 +55,M,01:53:32,03:52:43 +34,W,01:41:12,03:52:43 +50,M,01:38:02,03:52:44 +27,M,01:45:36,03:52:44 +52,W,01:48:58,03:52:44 +52,M,01:47:07,03:52:45 +38,M,01:43:43,03:52:45 +32,M,01:51:06,03:52:45 +54,M,01:52:08,03:52:45 +47,W,01:44:35,03:52:46 +32,M,01:40:10,03:52:46 +30,M,01:40:17,03:52:46 +35,W,01:46:55,03:52:46 +40,M,01:46:04,03:52:47 +43,M,01:43:05,03:52:48 +41,M,01:49:27,03:52:48 +60,M,01:52:40,03:52:49 +61,M,01:44:09,03:52:49 +39,M,01:45:59,03:52:50 +41,M,01:50:03,03:52:50 +28,W,01:50:00,03:52:50 +57,M,01:45:27,03:52:51 +37,M,01:44:52,03:52:51 +33,W,01:41:07,03:52:51 +45,W,01:46:54,03:52:52 +32,M,01:53:51,03:52:53 +60,M,01:48:47,03:52:53 +42,M,01:55:08,03:52:53 +39,M,01:35:58,03:52:54 +38,M,01:40:55,03:52:54 +38,M,01:46:53,03:52:54 +45,M,01:46:32,03:52:54 +43,M,01:50:04,03:52:54 +31,M,01:53:29,03:52:54 +27,W,01:51:00,03:52:55 +34,M,01:45:23,03:52:56 +24,M,01:49:09,03:52:56 +40,M,01:43:53,03:52:56 +43,M,01:41:26,03:52:56 +29,M,01:42:13,03:52:57 +48,M,01:43:22,03:52:58 +47,M,01:40:16,03:52:58 +44,M,01:43:32,03:52:59 +39,M,01:51:40,03:52:59 +53,M,01:45:04,03:53:00 +41,M,01:40:58,03:53:01 +47,W,01:50:35,03:53:01 +32,M,01:45:43,03:53:01 +27,W,01:49:32,03:53:01 +45,M,01:44:38,03:53:01 +42,M,01:50:17,03:53:01 +54,M,01:45:59,03:53:01 +40,M,01:36:27,03:53:02 +41,M,01:45:29,03:53:02 +46,W,01:52:21,03:53:02 +48,M,01:40:39,03:53:02 +39,W,01:51:23,03:53:02 +49,M,01:42:50,03:53:03 +28,M,01:34:03,03:53:03 +30,M,01:50:56,03:53:04 +42,M,01:50:59,03:53:04 +40,M,01:41:22,03:53:04 +51,W,01:45:43,03:53:04 +30,M,01:46:29,03:53:05 +47,W,01:45:03,03:53:05 +41,M,01:44:47,03:53:06 +36,M,01:54:33,03:53:06 +40,M,01:42:33,03:53:06 +30,M,01:39:06,03:53:07 +32,M,01:55:19,03:53:07 +53,W,01:44:47,03:53:07 +31,W,01:51:59,03:53:08 +45,M,01:44:15,03:53:09 +35,W,01:43:47,03:53:09 +42,M,01:41:52,03:53:09 +34,W,01:50:44,03:53:10 +34,M,01:45:31,03:53:10 +43,M,01:42:05,03:53:10 +38,M,01:48:20,03:53:10 +45,M,01:44:16,03:53:11 +28,W,01:50:12,03:53:11 +33,M,01:48:36,03:53:11 +27,M,01:42:00,03:53:11 +43,W,01:51:55,03:53:12 +50,M,01:42:30,03:53:12 +41,M,01:42:45,03:53:13 +38,M,01:47:44,03:53:13 +50,M,01:45:52,03:53:13 +53,M,01:44:15,03:53:13 +48,M,01:49:37,03:53:14 +33,M,01:40:11,03:53:14 +29,M,01:46:03,03:53:14 +52,M,01:50:55,03:53:16 +38,M,01:46:02,03:53:17 +38,M,01:49:10,03:53:18 +40,M,01:44:16,03:53:19 +51,M,01:49:09,03:53:20 +40,M,01:46:58,03:53:20 +52,M,01:49:17,03:53:21 +29,M,01:38:34,03:53:21 +41,W,01:40:30,03:53:21 +44,M,01:46:03,03:53:22 +55,M,01:54:11,03:53:22 +31,W,01:56:04,03:53:22 +37,M,01:52:01,03:53:22 +48,M,01:42:21,03:53:22 +51,M,01:44:34,03:53:22 +31,M,01:47:48,03:53:22 +36,M,01:41:56,03:53:23 +45,W,01:52:18,03:53:23 +39,M,01:40:25,03:53:23 +38,W,01:41:44,03:53:24 +29,W,01:53:17,03:53:24 +38,M,01:45:15,03:53:24 +32,M,01:55:37,03:53:24 +35,M,01:48:57,03:53:25 +47,M,01:42:20,03:53:25 +30,M,01:54:35,03:53:25 +40,M,01:42:21,03:53:27 +42,M,01:44:33,03:53:28 +53,M,01:39:51,03:53:29 +41,M,01:51:04,03:53:29 +51,M,01:44:36,03:53:30 +42,W,01:52:27,03:53:30 +36,M,01:49:27,03:53:30 +27,M,01:44:02,03:53:31 +39,M,01:42:43,03:53:31 +38,W,01:50:56,03:53:31 +30,W,01:43:25,03:53:32 +29,M,01:47:42,03:53:32 +27,W,01:49:43,03:53:32 +24,W,01:43:02,03:53:32 +34,M,01:44:11,03:53:33 +54,M,01:44:15,03:53:33 +48,M,01:49:59,03:53:33 +48,M,01:54:05,03:53:33 +50,M,01:51:46,03:53:34 +34,M,01:44:54,03:53:35 +23,M,01:48:09,03:53:35 +40,W,01:50:13,03:53:35 +25,M,01:50:32,03:53:36 +35,M,01:44:52,03:53:36 +34,M,01:42:51,03:53:36 +48,M,01:39:40,03:53:37 +60,M,01:47:36,03:53:38 +34,M,01:47:41,03:53:38 +51,M,01:50:32,03:53:38 +35,M,01:52:41,03:53:38 +27,M,01:39:01,03:53:38 +40,M,01:55:14,03:53:38 +39,M,01:42:44,03:53:39 +46,W,01:49:44,03:53:40 +44,M,01:47:52,03:53:41 +34,M,01:36:44,03:53:41 +41,M,01:46:46,03:53:41 +45,M,01:50:20,03:53:41 +27,W,01:48:32,03:53:41 +55,M,01:42:52,03:53:42 +41,M,01:39:36,03:53:42 +59,M,01:47:23,03:53:43 +56,M,01:53:55,03:53:43 +39,M,01:36:26,03:53:44 +48,M,01:43:01,03:53:44 +49,M,01:41:52,03:53:45 +38,M,01:42:46,03:53:45 +29,M,01:50:32,03:53:45 +40,M,01:41:13,03:53:46 +32,W,01:48:10,03:53:46 +27,W,01:52:25,03:53:46 +52,M,01:45:22,03:53:48 +50,M,01:42:39,03:53:48 +41,M,01:52:00,03:53:48 +24,M,01:46:47,03:53:48 +38,M,01:53:01,03:53:48 +24,M,01:44:58,03:53:48 +31,W,01:48:04,03:53:48 +48,M,01:41:31,03:53:49 +51,M,01:41:02,03:53:49 +49,M,01:44:39,03:53:49 +47,M,01:48:34,03:53:50 +43,W,01:42:38,03:53:50 +25,M,01:46:40,03:53:51 +48,M,01:48:09,03:53:51 +51,W,01:57:12,03:53:51 +31,M,01:38:28,03:53:51 +38,W,01:49:09,03:53:52 +40,M,01:32:47,03:53:52 +39,M,01:39:30,03:53:52 +37,M,01:43:22,03:53:52 +35,M,01:42:02,03:53:52 +35,M,01:44:06,03:53:52 +48,M,01:42:53,03:53:53 +49,M,01:50:48,03:53:53 +44,M,01:49:03,03:53:53 +39,M,01:48:27,03:53:53 +30,M,01:45:09,03:53:55 +47,M,01:48:24,03:53:55 +43,M,01:44:19,03:53:56 +38,W,01:43:19,03:53:56 +25,M,01:50:54,03:53:57 +54,M,01:49:54,03:53:57 +40,M,01:40:39,03:53:58 +41,M,01:49:21,03:53:59 +43,M,01:50:40,03:54:00 +41,M,01:40:21,03:54:00 +44,M,01:50:12,03:54:00 +44,W,01:51:25,03:54:00 +52,M,01:54:25,03:54:00 +41,M,01:37:39,03:54:00 +58,M,01:38:22,03:54:00 +44,W,01:50:32,03:54:01 +41,M,01:53:31,03:54:01 +41,M,01:44:24,03:54:01 +52,M,01:42:02,03:54:01 +30,M,01:47:13,03:54:02 +48,W,01:46:16,03:54:02 +37,M,01:53:41,03:54:03 +35,M,01:45:18,03:54:03 +51,M,01:54:39,03:54:04 +41,M,01:46:37,03:54:04 +25,W,01:46:12,03:54:04 +63,M,01:46:59,03:54:05 +36,M,01:48:41,03:54:05 +35,M,01:54:06,03:54:05 +47,M,01:50:30,03:54:05 +64,M,01:50:10,03:54:06 +25,M,01:50:30,03:54:06 +41,W,01:49:39,03:54:06 +38,M,01:47:52,03:54:06 +40,M,01:52:59,03:54:07 +29,M,01:43:56,03:54:07 +29,M,01:47:33,03:54:07 +23,M,01:48:55,03:54:07 +31,W,01:54:38,03:54:08 +29,M,01:43:47,03:54:09 +32,M,01:41:46,03:54:09 +42,M,01:44:46,03:54:09 +40,M,01:44:31,03:54:09 +36,M,01:44:29,03:54:10 +53,M,01:36:41,03:54:10 +35,M,01:43:57,03:54:10 +33,M,01:49:43,03:54:10 +60,M,01:52:04,03:54:10 +49,M,01:41:39,03:54:11 +51,W,01:49:13,03:54:11 +44,M,01:44:40,03:54:11 +42,M,01:55:10,03:54:11 +45,M,01:46:13,03:54:11 +38,M,01:44:31,03:54:11 +31,M,01:45:30,03:54:12 +30,M,01:54:16,03:54:13 +43,M,01:46:18,03:54:13 +38,M,01:42:35,03:54:15 +24,W,01:51:37,03:54:15 +32,W,01:49:03,03:54:16 +36,M,01:50:51,03:54:16 +49,M,01:54:56,03:54:16 +50,M,01:37:39,03:54:16 +34,W,01:45:31,03:54:16 +30,M,01:38:47,03:54:16 +55,M,01:51:32,03:54:17 +25,M,01:55:29,03:54:18 +33,M,01:48:12,03:54:18 +34,M,01:47:13,03:54:18 +38,M,01:50:02,03:54:19 +25,W,01:45:53,03:54:19 +28,M,01:41:15,03:54:19 +28,M,01:31:03,03:54:20 +42,M,01:39:50,03:54:20 +29,M,01:46:28,03:54:20 +46,M,01:43:08,03:54:21 +51,M,01:43:57,03:54:21 +26,M,01:46:15,03:54:21 +40,M,01:42:07,03:54:22 +40,W,01:45:47,03:54:22 +34,M,01:44:44,03:54:22 +41,M,01:44:12,03:54:22 +33,M,01:42:22,03:54:23 +55,M,01:50:23,03:54:23 +40,M,01:45:23,03:54:23 +41,M,01:46:33,03:54:24 +24,W,01:43:41,03:54:24 +42,M,01:45:53,03:54:25 +45,W,01:51:13,03:54:25 +30,M,01:35:04,03:54:25 +45,M,01:48:43,03:54:25 +49,M,01:47:04,03:54:25 +44,M,01:29:07,03:54:26 +36,W,01:50:14,03:54:27 +34,M,01:37:47,03:54:27 +43,W,01:57:10,03:54:28 +37,M,01:51:25,03:54:28 +37,M,01:45:44,03:54:28 +32,W,01:52:17,03:54:29 +49,M,01:46:08,03:54:30 +43,M,01:42:43,03:54:30 +36,M,01:49:58,03:54:31 +45,W,01:51:15,03:54:31 +42,M,01:44:16,03:54:31 +23,W,01:46:49,03:54:32 +21,M,01:47:35,03:54:32 +46,M,01:46:33,03:54:33 +26,M,01:53:53,03:54:33 +54,W,01:49:35,03:54:34 +49,M,01:50:36,03:54:34 +28,M,01:42:12,03:54:35 +31,W,01:47:00,03:54:35 +37,M,01:47:01,03:54:35 +48,M,01:42:42,03:54:35 +35,W,01:47:58,03:54:36 +39,M,01:46:02,03:54:37 +37,M,01:50:29,03:54:37 +26,M,01:43:18,03:54:38 +45,M,01:47:20,03:54:38 +36,M,01:40:41,03:54:38 +52,M,01:47:04,03:54:38 +50,M,01:50:26,03:54:39 +46,M,01:57:03,03:54:39 +33,M,01:42:09,03:54:39 +33,M,01:46:05,03:54:41 +24,W,01:48:51,03:54:41 +41,M,01:46:21,03:54:41 +29,W,01:57:19,03:54:42 +32,M,01:50:53,03:54:42 +37,M,01:54:00,03:54:43 +29,W,01:53:40,03:54:43 +43,W,01:57:43,03:54:43 +47,W,01:50:07,03:54:43 +37,M,01:46:38,03:54:44 +46,M,01:49:31,03:54:44 +59,M,01:45:28,03:54:45 +40,M,01:42:30,03:54:46 +40,M,01:47:40,03:54:46 +42,M,01:49:24,03:54:46 +33,M,01:54:34,03:54:47 +52,M,01:35:32,03:54:48 +44,M,01:56:00,03:54:48 +35,M,01:46:15,03:54:48 +44,M,01:49:06,03:54:49 +42,M,01:48:59,03:54:49 +42,W,01:47:06,03:54:49 +46,M,01:46:25,03:54:49 +41,M,01:44:56,03:54:49 +52,W,01:50:03,03:54:49 +25,M,01:43:34,03:54:50 +46,W,01:44:23,03:54:51 +32,M,01:55:26,03:54:51 +41,M,01:44:27,03:54:51 +28,W,01:49:54,03:54:51 +44,M,01:50:08,03:54:51 +42,M,01:46:41,03:54:52 +40,M,01:41:26,03:54:52 +52,W,01:49:17,03:54:52 +49,M,01:45:48,03:54:52 +51,W,01:48:48,03:54:52 +36,M,01:50:38,03:54:52 +56,M,01:44:20,03:54:53 +45,M,01:49:15,03:54:54 +34,M,01:40:03,03:54:54 +24,W,01:45:12,03:54:55 +38,M,01:44:36,03:54:55 +21,M,01:40:45,03:54:55 +55,M,01:46:47,03:54:55 +34,M,01:44:08,03:54:55 +22,M,01:37:16,03:54:56 +28,M,01:49:22,03:54:56 +53,M,01:53:04,03:54:56 +24,M,01:54:20,03:54:57 +42,M,01:53:05,03:54:58 +28,W,01:46:53,03:54:58 +40,M,01:53:09,03:55:00 +41,W,01:45:36,03:55:00 +53,M,01:55:23,03:55:00 +35,M,01:45:28,03:55:00 +44,M,01:46:46,03:55:00 +32,M,01:47:00,03:55:00 +58,W,01:51:28,03:55:01 +39,M,01:49:05,03:55:01 +52,M,01:43:17,03:55:01 +43,M,01:36:17,03:55:02 +46,M,01:41:39,03:55:02 +37,M,01:41:59,03:55:02 +46,M,01:47:55,03:55:02 +30,M,01:36:32,03:55:03 +40,M,01:43:20,03:55:03 +48,M,01:50:04,03:55:04 +32,W,01:50:13,03:55:04 +44,M,01:47:58,03:55:04 +43,M,01:54:48,03:55:05 +49,M,01:41:45,03:55:05 +50,M,01:54:42,03:55:05 +35,M,01:46:59,03:55:06 +40,M,01:37:15,03:55:06 +35,M,01:43:29,03:55:06 +56,M,01:48:49,03:55:06 +54,M,01:38:08,03:55:06 +29,W,01:44:55,03:55:07 +34,M,01:45:03,03:55:07 +47,W,01:46:32,03:55:08 +39,M,01:53:11,03:55:09 +38,M,01:48:21,03:55:09 +25,M,01:42:16,03:55:09 +50,M,01:52:00,03:55:09 +53,M,01:49:36,03:55:10 +45,M,01:44:35,03:55:10 +30,M,01:39:16,03:55:11 +38,W,01:53:31,03:55:11 +47,M,01:45:24,03:55:11 +32,M,01:38:44,03:55:11 +38,M,01:43:34,03:55:11 +37,M,01:40:30,03:55:12 +41,W,01:51:03,03:55:12 +42,W,01:50:08,03:55:12 +40,W,01:44:30,03:55:13 +51,W,01:42:29,03:55:13 +49,M,01:39:18,03:55:13 +37,M,01:43:37,03:55:13 +23,W,01:46:33,03:55:14 +40,M,01:39:00,03:55:14 +37,M,01:54:22,03:55:15 +35,M,01:43:01,03:55:15 +33,M,01:55:38,03:55:16 +30,M,01:53:04,03:55:16 +33,M,01:35:24,03:55:16 +42,M,01:50:40,03:55:16 +34,M,01:46:32,03:55:16 +39,M,01:42:15,03:55:17 +43,M,01:48:50,03:55:17 +27,M,01:47:12,03:55:17 +34,M,01:46:48,03:55:18 +22,M,01:43:08,03:55:18 +36,M,01:54:26,03:55:19 +30,M,01:50:07,03:55:20 +33,W,01:44:39,03:55:20 +49,M,01:43:09,03:55:21 +30,W,01:42:33,03:55:22 +31,M,01:38:33,03:55:22 +29,M,01:47:51,03:55:22 +35,M,01:48:11,03:55:23 +29,M,01:43:03,03:55:23 +56,M,01:50:21,03:55:23 +40,M,01:44:56,03:55:23 +46,W,01:44:18,03:55:23 +25,W,01:49:25,03:55:24 +44,M,01:46:17,03:55:24 +27,W,01:59:47,03:55:24 +45,M,01:44:22,03:55:24 +30,W,01:51:54,03:55:25 +61,M,01:57:10,03:55:25 +41,M,01:54:01,03:55:26 +50,M,01:30:47,03:55:26 +48,M,01:50:36,03:55:26 +22,M,01:40:14,03:55:26 +39,M,01:42:20,03:55:26 +55,M,01:50:33,03:55:28 +26,M,01:42:12,03:55:28 +48,M,01:52:20,03:55:28 +36,M,01:45:07,03:55:28 +26,M,01:46:26,03:55:29 +43,M,01:48:42,03:55:29 +36,M,01:45:48,03:55:30 +61,M,01:51:30,03:55:30 +29,M,01:48:01,03:55:31 +45,M,01:51:06,03:55:31 +34,M,01:44:29,03:55:31 +48,M,01:46:54,03:55:31 +31,M,01:43:09,03:55:32 +35,M,01:42:58,03:55:32 +37,M,01:53:29,03:55:32 +37,W,01:53:24,03:55:33 +21,W,01:47:37,03:55:33 +41,M,01:47:34,03:55:33 +46,M,01:49:24,03:55:34 +24,M,01:40:58,03:55:35 +23,W,01:53:24,03:55:35 +28,M,01:57:23,03:55:35 +38,W,01:47:04,03:55:36 +36,M,01:43:04,03:55:36 +51,M,01:42:51,03:55:37 +36,M,01:43:01,03:55:37 +27,M,01:37:48,03:55:37 +47,M,01:49:21,03:55:37 +33,M,01:49:05,03:55:38 +22,W,01:50:20,03:55:39 +40,W,01:42:16,03:55:39 +52,M,01:43:14,03:55:39 +24,W,01:46:03,03:55:41 +31,M,01:51:04,03:55:41 +52,M,01:43:38,03:55:41 +31,M,01:41:09,03:55:41 +58,M,01:44:41,03:55:42 +39,M,01:49:50,03:55:42 +35,W,01:49:12,03:55:42 +39,M,01:49:17,03:55:42 +47,M,01:36:49,03:55:42 +31,M,01:52:00,03:55:43 +32,W,01:44:45,03:55:43 +31,M,01:46:28,03:55:44 +36,M,01:39:11,03:55:44 +44,M,01:48:33,03:55:44 +38,W,01:48:42,03:55:45 +33,M,01:47:24,03:55:45 +36,M,01:54:40,03:55:45 +39,M,01:53:30,03:55:45 +33,M,01:55:25,03:55:45 +45,M,01:45:27,03:55:46 +49,M,01:45:35,03:55:47 +31,M,01:56:27,03:55:47 +42,M,01:43:36,03:55:47 +53,M,01:51:04,03:55:48 +26,W,01:46:13,03:55:48 +39,M,01:54:25,03:55:49 +33,M,01:49:31,03:55:50 +47,M,01:46:31,03:55:50 +33,W,01:45:43,03:55:50 +45,M,01:48:40,03:55:51 +36,M,01:48:58,03:55:51 +28,W,01:54:26,03:55:52 +25,W,01:47:48,03:55:52 +55,M,01:43:39,03:55:52 +31,M,01:44:04,03:55:52 +35,M,01:40:46,03:55:53 +52,W,01:42:55,03:55:53 +42,W,01:49:53,03:55:53 +40,M,01:43:13,03:55:55 +37,W,01:56:58,03:55:55 +28,M,01:49:54,03:55:55 +50,W,01:57:57,03:55:56 +39,W,01:50:17,03:55:56 +36,M,01:44:05,03:55:56 +30,M,01:45:20,03:55:58 +38,M,01:44:14,03:55:58 +43,W,01:47:06,03:55:59 +29,M,01:45:03,03:56:00 +35,M,01:46:45,03:56:00 +26,W,01:48:06,03:56:01 +33,M,01:54:49,03:56:01 +58,M,01:43:29,03:56:01 +36,M,01:48:18,03:56:02 +40,M,01:49:23,03:56:02 +64,W,01:52:58,03:56:02 +31,M,01:46:24,03:56:03 +35,M,01:46:23,03:56:03 +46,M,01:38:55,03:56:03 +36,W,01:46:04,03:56:03 +46,M,01:52:33,03:56:03 +19,M,01:52:43,03:56:04 +34,M,01:44:14,03:56:04 +30,M,01:43:35,03:56:04 +43,M,01:52:46,03:56:05 +47,M,01:52:09,03:56:06 +34,W,01:57:24,03:56:06 +45,W,01:44:51,03:56:06 +40,M,01:41:29,03:56:06 +44,W,01:51:44,03:56:07 +54,M,01:59:20,03:56:07 +47,M,01:54:05,03:56:07 +24,W,01:43:41,03:56:07 +35,M,01:44:18,03:56:08 +22,M,01:45:13,03:56:08 +34,W,01:51:51,03:56:08 +33,W,01:42:22,03:56:08 +47,M,01:46:57,03:56:09 +34,M,01:46:11,03:56:10 +31,W,01:50:40,03:56:10 +38,M,01:47:32,03:56:11 +42,M,01:51:31,03:56:11 +42,M,01:45:57,03:56:11 +45,W,01:45:40,03:56:11 +50,M,01:54:03,03:56:12 +25,W,01:42:17,03:56:12 +52,W,01:46:41,03:56:14 +40,M,01:45:31,03:56:14 +43,W,01:43:58,03:56:14 +38,W,01:53:48,03:56:14 +38,W,01:43:08,03:56:14 +30,M,01:52:23,03:56:15 +39,M,01:43:39,03:56:15 +47,M,01:45:41,03:56:16 +32,M,01:53:36,03:56:16 +59,W,01:53:14,03:56:16 +37,M,01:39:39,03:56:16 +29,M,01:51:51,03:56:17 +41,M,01:41:42,03:56:17 +38,M,01:51:28,03:56:17 +41,M,01:44:10,03:56:18 +26,M,01:38:44,03:56:18 +59,M,01:44:02,03:56:18 +34,M,01:55:03,03:56:18 +38,W,01:43:50,03:56:18 +62,M,01:43:16,03:56:19 +30,M,01:46:02,03:56:19 +37,M,01:35:47,03:56:20 +60,M,01:52:05,03:56:20 +60,M,01:44:52,03:56:20 +49,M,01:42:05,03:56:20 +42,M,01:42:21,03:56:20 +41,M,01:47:30,03:56:21 +47,M,01:51:02,03:56:21 +44,W,01:44:58,03:56:21 +46,M,01:47:44,03:56:21 +28,M,01:39:53,03:56:21 +62,M,01:53:41,03:56:21 +31,W,01:44:05,03:56:22 +36,M,01:44:37,03:56:22 +44,M,01:47:21,03:56:22 +35,M,01:40:25,03:56:23 +35,M,01:43:28,03:56:23 +42,M,01:54:02,03:56:23 +60,M,01:51:23,03:56:23 +34,M,01:49:22,03:56:23 +43,M,01:53:24,03:56:23 +43,M,01:57:01,03:56:25 +47,M,01:49:51,03:56:25 +45,M,01:46:51,03:56:26 +27,M,01:49:24,03:56:26 +32,M,01:51:07,03:56:26 +47,W,01:47:48,03:56:26 +47,M,01:40:16,03:56:26 +46,M,01:46:20,03:56:27 +27,M,01:46:40,03:56:27 +40,W,01:51:19,03:56:27 +34,W,01:44:58,03:56:28 +41,M,01:50:41,03:56:28 +33,M,01:42:50,03:56:29 +34,M,01:43:54,03:56:29 +43,M,01:46:51,03:56:29 +42,W,01:47:20,03:56:30 +38,W,01:53:39,03:56:30 +25,M,01:44:03,03:56:30 +45,M,01:47:56,03:56:30 +45,M,01:41:09,03:56:31 +33,W,01:53:27,03:56:31 +36,M,01:46:32,03:56:31 +37,M,01:47:11,03:56:31 +21,M,01:49:17,03:56:32 +43,M,01:34:15,03:56:32 +46,M,01:44:11,03:56:33 +41,W,01:42:23,03:56:34 +47,M,01:43:44,03:56:34 +31,W,01:47:03,03:56:34 +44,W,01:41:18,03:56:35 +41,M,01:48:30,03:56:37 +29,M,01:44:49,03:56:37 +34,M,01:33:28,03:56:37 +46,M,01:38:49,03:56:39 +30,M,01:43:31,03:56:39 +50,M,01:43:20,03:56:39 +50,M,01:48:00,03:56:39 +60,M,01:44:50,03:56:40 +46,M,01:47:33,03:56:40 +52,M,01:51:57,03:56:40 +28,M,01:55:44,03:56:41 +34,M,01:47:13,03:56:41 +44,M,01:45:31,03:56:43 +29,W,01:46:48,03:56:43 +32,M,01:51:35,03:56:44 +45,M,01:43:40,03:56:44 +29,M,01:42:48,03:56:44 +37,M,01:53:07,03:56:44 +23,M,01:48:47,03:56:44 +50,M,01:43:11,03:56:44 +50,M,01:44:54,03:56:44 +38,M,01:52:00,03:56:45 +35,M,01:46:43,03:56:45 +41,M,01:53:54,03:56:46 +30,W,01:43:16,03:56:46 +25,M,01:50:02,03:56:46 +37,M,01:42:57,03:56:46 +22,W,01:45:41,03:56:47 +48,M,01:45:25,03:56:47 +38,W,01:54:19,03:56:48 +34,M,01:52:05,03:56:48 +41,M,01:46:14,03:56:49 +64,M,01:49:25,03:56:50 +36,M,01:44:25,03:56:50 +35,M,01:43:59,03:56:50 +26,W,01:53:28,03:56:51 +43,M,01:48:00,03:56:51 +23,W,01:48:38,03:56:51 +37,M,01:55:29,03:56:51 +33,M,01:52:19,03:56:51 +37,M,01:40:29,03:56:52 +34,M,01:44:16,03:56:52 +35,M,01:52:45,03:56:52 +32,M,01:42:53,03:56:52 +22,M,01:44:55,03:56:52 +34,W,01:44:02,03:56:53 +33,M,01:51:27,03:56:53 +45,W,01:45:49,03:56:53 +36,M,01:44:38,03:56:53 +19,M,01:39:39,03:56:54 +62,M,01:45:14,03:56:54 +23,M,01:51:05,03:56:54 +38,M,01:44:08,03:56:55 +25,W,01:45:26,03:56:55 +29,M,01:48:00,03:56:55 +39,M,01:46:18,03:56:56 +37,M,01:46:33,03:56:56 +40,M,01:43:55,03:56:56 +35,M,01:40:19,03:56:56 +27,M,01:48:12,03:56:56 +43,M,01:51:01,03:56:57 +25,M,01:39:23,03:56:57 +43,W,01:49:31,03:56:58 +25,M,01:44:06,03:56:58 +56,M,01:50:02,03:56:58 +26,M,01:54:12,03:56:58 +55,M,01:49:23,03:56:58 +35,W,01:50:38,03:56:59 +29,M,01:50:50,03:56:59 +37,M,01:53:32,03:57:00 +30,M,01:42:37,03:57:00 +38,M,01:46:08,03:57:00 +61,W,01:44:33,03:57:00 +52,M,01:33:23,03:57:01 +33,M,01:44:37,03:57:01 +58,M,01:47:00,03:57:01 +28,M,01:41:46,03:57:02 +33,M,01:45:27,03:57:02 +46,W,01:53:20,03:57:02 +51,W,01:56:39,03:57:03 +25,W,01:47:30,03:57:03 +43,M,01:49:18,03:57:04 +38,M,01:43:49,03:57:04 +45,M,01:45:17,03:57:04 +31,M,01:53:45,03:57:05 +23,M,01:47:33,03:57:06 +45,M,01:41:43,03:57:06 +41,M,01:54:49,03:57:07 +30,W,01:44:50,03:57:07 +38,M,01:35:17,03:57:07 +30,W,01:53:34,03:57:07 +41,W,01:47:28,03:57:08 +44,M,01:48:21,03:57:08 +42,M,01:42:32,03:57:08 +46,M,01:51:25,03:57:08 +39,M,01:39:27,03:57:08 +42,M,01:44:07,03:57:09 +41,M,01:47:42,03:57:09 +31,W,01:52:21,03:57:09 +45,M,01:51:33,03:57:09 +31,M,01:50:26,03:57:10 +47,W,01:47:51,03:57:10 +38,M,01:52:27,03:57:10 +55,M,01:52:37,03:57:12 +40,M,01:57:21,03:57:12 +34,M,01:41:21,03:57:12 +45,M,01:39:07,03:57:13 +43,M,01:44:23,03:57:13 +34,M,01:45:14,03:57:13 +40,M,01:44:37,03:57:13 +48,M,01:42:08,03:57:14 +53,M,01:51:28,03:57:14 +34,M,01:43:38,03:57:15 +45,M,01:48:35,03:57:15 +25,W,01:51:12,03:57:15 +35,M,01:51:48,03:57:15 +35,M,01:44:39,03:57:15 +53,M,01:38:49,03:57:15 +38,M,01:53:51,03:57:16 +42,M,01:44:56,03:57:16 +31,M,01:37:21,03:57:16 +41,W,01:47:53,03:57:17 +36,M,01:50:44,03:57:17 +24,M,01:46:32,03:57:17 +49,M,01:45:21,03:57:17 +51,M,01:55:34,03:57:18 +39,M,01:44:54,03:57:18 +48,M,01:36:56,03:57:18 +46,M,01:45:42,03:57:18 +34,M,01:49:28,03:57:18 +31,W,01:48:48,03:57:18 +50,M,01:47:16,03:57:19 +42,M,01:46:21,03:57:20 +46,M,01:40:44,03:57:20 +29,W,01:48:33,03:57:20 +42,M,01:37:54,03:57:21 +32,M,01:54:48,03:57:21 +66,M,01:46:40,03:57:21 +36,W,01:49:33,03:57:21 +28,M,01:38:15,03:57:22 +49,M,01:43:11,03:57:22 +49,M,01:39:16,03:57:22 +38,M,01:39:10,03:57:23 +34,W,01:46:34,03:57:23 +46,M,01:50:41,03:57:23 +37,M,01:50:05,03:57:24 +39,W,01:47:13,03:57:24 +45,M,01:45:11,03:57:24 +30,W,01:44:32,03:57:25 +41,M,01:43:05,03:57:25 +39,M,01:49:53,03:57:25 +44,M,01:48:58,03:57:25 +44,M,01:48:33,03:57:25 +46,M,01:44:49,03:57:26 +34,M,01:49:18,03:57:26 +42,W,01:50:12,03:57:27 +49,M,01:45:56,03:57:27 +47,M,01:48:56,03:57:27 +29,M,01:42:21,03:57:27 +42,M,01:41:58,03:57:28 +42,M,01:49:00,03:57:28 +28,M,01:52:24,03:57:28 +50,M,01:47:52,03:57:28 +40,M,01:45:18,03:57:29 +39,W,01:50:23,03:57:29 +28,W,01:58:18,03:57:30 +33,W,01:44:08,03:57:30 +28,M,01:52:14,03:57:30 +31,M,01:42:09,03:57:32 +52,M,01:43:38,03:57:32 +27,W,01:51:14,03:57:32 +34,M,01:50:40,03:57:33 +44,M,01:43:46,03:57:34 +27,M,01:47:03,03:57:34 +34,W,01:52:00,03:57:34 +29,M,01:38:42,03:57:34 +30,W,01:56:04,03:57:34 +50,M,01:54:53,03:57:34 +38,W,01:56:27,03:57:35 +41,M,01:47:10,03:57:36 +46,M,01:50:12,03:57:36 +49,M,01:54:55,03:57:36 +26,W,01:45:04,03:57:37 +30,W,01:49:45,03:57:37 +47,M,01:47:38,03:57:37 +40,M,01:46:36,03:57:37 +32,M,01:52:16,03:57:37 +25,M,01:42:39,03:57:37 +39,M,01:43:59,03:57:37 +44,M,01:55:27,03:57:38 +52,M,01:45:25,03:57:38 +45,W,01:45:00,03:57:38 +44,M,01:46:36,03:57:38 +26,M,01:42:01,03:57:38 +50,M,01:40:07,03:57:39 +42,M,01:41:33,03:57:39 +39,M,01:44:53,03:57:39 +34,W,01:52:02,03:57:39 +38,M,01:54:36,03:57:40 +25,M,01:44:50,03:57:40 +42,M,01:48:26,03:57:40 +55,M,01:49:20,03:57:40 +33,W,01:49:17,03:57:40 +42,W,01:53:03,03:57:40 +41,W,01:54:55,03:57:41 +44,M,01:54:29,03:57:41 +44,W,01:46:36,03:57:41 +34,M,01:47:00,03:57:42 +46,M,01:43:41,03:57:43 +33,W,01:43:24,03:57:43 +27,M,01:43:01,03:57:43 +52,M,01:58:10,03:57:43 +27,W,01:42:30,03:57:43 +25,M,01:43:35,03:57:43 +41,M,01:43:42,03:57:44 +23,W,01:46:50,03:57:44 +29,M,01:49:47,03:57:44 +43,M,01:48:04,03:57:44 +32,W,01:43:48,03:57:44 +36,M,01:51:00,03:57:45 +43,M,01:46:01,03:57:45 +50,M,01:46:22,03:57:46 +30,M,01:46:59,03:57:46 +41,M,01:48:37,03:57:46 +36,M,01:48:13,03:57:46 +55,M,01:51:42,03:57:47 +35,M,01:56:11,03:57:47 +34,M,01:47:33,03:57:47 +33,M,01:46:06,03:57:48 +50,M,01:53:43,03:57:48 +36,W,01:49:57,03:57:49 +34,M,01:49:49,03:57:49 +46,M,01:43:25,03:57:49 +49,M,01:50:40,03:57:50 +26,M,01:51:11,03:57:50 +31,M,01:46:18,03:57:50 +40,W,01:47:30,03:57:51 +27,M,01:45:19,03:57:51 +26,M,01:41:27,03:57:51 +39,M,01:48:36,03:57:51 +35,M,01:52:23,03:57:51 +34,M,01:47:16,03:57:51 +26,W,01:51:00,03:57:53 +55,M,01:47:07,03:57:53 +49,W,01:47:46,03:57:53 +34,M,01:45:32,03:57:53 +46,M,01:46:10,03:57:53 +41,M,01:44:37,03:57:55 +43,M,01:59:26,03:57:55 +27,W,01:51:14,03:57:56 +31,M,01:41:16,03:57:56 +52,M,01:42:20,03:57:56 +33,W,01:46:20,03:57:56 +26,M,01:46:12,03:57:57 +55,M,01:43:01,03:57:57 +34,M,01:54:34,03:57:57 +29,W,01:48:29,03:57:58 +46,W,01:54:22,03:57:58 +27,W,01:42:43,03:57:58 +39,M,01:38:32,03:57:58 +44,M,01:42:06,03:57:59 +27,M,01:45:16,03:57:59 +38,M,01:50:19,03:58:00 +31,M,01:53:36,03:58:00 +43,M,01:42:46,03:58:00 +37,M,01:47:51,03:58:00 +34,M,01:48:45,03:58:00 +40,W,01:51:43,03:58:00 +39,M,01:48:22,03:58:00 +36,M,01:48:39,03:58:01 +45,M,01:45:14,03:58:01 +33,M,01:45:08,03:58:02 +53,M,01:44:45,03:58:02 +32,M,01:35:14,03:58:02 +37,M,01:49:03,03:58:02 +36,W,01:49:24,03:58:02 +52,M,01:54:40,03:58:02 +33,W,01:54:03,03:58:03 +36,W,01:53:23,03:58:03 +24,M,01:36:04,03:58:03 +30,M,01:54:13,03:58:03 +30,W,01:46:42,03:58:04 +29,M,01:57:32,03:58:04 +34,M,01:41:43,03:58:04 +27,M,01:54:27,03:58:04 +27,M,01:46:51,03:58:04 +43,M,01:44:19,03:58:05 +24,M,01:51:10,03:58:05 +38,M,01:39:42,03:58:05 +34,M,01:55:26,03:58:05 +38,M,01:37:54,03:58:05 +38,W,01:46:12,03:58:06 +30,W,01:45:42,03:58:06 +41,M,01:48:11,03:58:07 +30,W,01:56:30,03:58:07 +31,M,01:40:37,03:58:08 +58,M,01:57:06,03:58:08 +38,M,01:45:32,03:58:08 +43,M,01:46:26,03:58:08 +38,W,01:55:11,03:58:08 +45,M,01:43:47,03:58:09 +49,M,01:45:32,03:58:09 +37,M,01:56:08,03:58:09 +30,M,01:37:40,03:58:09 +39,M,01:38:18,03:58:09 +43,M,01:53:14,03:58:09 +40,M,01:45:32,03:58:10 +34,M,01:45:39,03:58:10 +52,M,01:44:47,03:58:10 +40,M,01:52:33,03:58:11 +38,M,01:50:04,03:58:11 +31,M,01:54:43,03:58:11 +25,M,01:44:57,03:58:12 +56,M,01:46:25,03:58:12 +40,M,01:49:19,03:58:13 +34,M,01:50:48,03:58:14 +31,M,01:50:30,03:58:14 +41,M,01:45:59,03:58:15 +47,M,01:50:05,03:58:15 +38,M,01:44:33,03:58:15 +31,M,01:43:35,03:58:15 +28,M,01:49:36,03:58:16 +27,M,01:46:57,03:58:16 +22,M,01:45:18,03:58:16 +41,W,01:55:09,03:58:16 +34,M,01:45:44,03:58:16 +37,M,01:48:39,03:58:16 +37,W,01:51:19,03:58:17 +25,M,01:34:10,03:58:17 +40,M,01:50:58,03:58:18 +21,M,01:39:10,03:58:18 +40,M,01:44:41,03:58:19 +37,M,01:35:57,03:58:19 +48,M,01:46:50,03:58:20 +28,M,01:43:56,03:58:20 +29,W,01:43:46,03:58:20 +42,W,01:55:26,03:58:20 +42,M,01:38:34,03:58:21 +51,M,01:55:01,03:58:21 +42,M,01:43:09,03:58:21 +24,W,01:43:36,03:58:22 +41,M,01:47:37,03:58:22 +51,M,01:55:38,03:58:23 +37,M,01:36:56,03:58:23 +44,M,01:46:44,03:58:23 +62,M,01:51:46,03:58:23 +41,M,01:47:10,03:58:24 +29,M,01:43:41,03:58:24 +42,M,01:41:15,03:58:24 +29,W,01:44:49,03:58:24 +23,M,01:41:46,03:58:24 +32,M,01:47:52,03:58:26 +37,M,01:44:40,03:58:27 +52,M,01:53:26,03:58:27 +35,M,01:44:08,03:58:28 +31,M,01:54:54,03:58:28 +51,M,01:46:02,03:58:28 +36,M,01:38:24,03:58:28 +31,M,01:46:25,03:58:29 +21,M,01:40:38,03:58:29 +38,M,01:44:07,03:58:29 +40,M,01:45:12,03:58:29 +28,M,01:41:40,03:58:29 +42,M,01:46:00,03:58:29 +39,M,01:56:18,03:58:30 +47,W,01:46:06,03:58:30 +36,W,01:47:25,03:58:31 +59,M,01:44:19,03:58:32 +44,M,01:59:36,03:58:32 +41,M,01:49:14,03:58:33 +36,M,01:50:38,03:58:33 +35,M,01:55:38,03:58:33 +40,W,01:42:43,03:58:34 +42,M,01:51:22,03:58:34 +37,M,01:39:16,03:58:34 +56,M,01:48:14,03:58:34 +46,M,01:49:55,03:58:35 +37,M,01:49:50,03:58:35 +46,M,01:52:56,03:58:35 +40,M,01:45:44,03:58:36 +43,M,01:42:56,03:58:36 +43,W,01:58:31,03:58:37 +31,M,01:49:21,03:58:37 +53,M,01:55:10,03:58:37 +44,M,01:41:50,03:58:38 +46,W,01:53:39,03:58:38 +47,W,01:48:05,03:58:39 +32,M,01:44:19,03:58:39 +22,W,01:50:48,03:58:39 +37,M,01:47:05,03:58:39 +28,M,01:44:15,03:58:39 +33,M,01:38:33,03:58:39 +35,M,01:53:56,03:58:40 +38,M,01:45:17,03:58:40 +47,M,01:37:53,03:58:41 +48,M,01:43:12,03:58:41 +43,M,01:56:11,03:58:41 +42,M,01:57:11,03:58:41 +33,M,01:57:41,03:58:42 +25,W,01:47:19,03:58:42 +37,M,01:51:20,03:58:42 +65,M,01:50:44,03:58:42 +35,W,01:51:28,03:58:43 +34,W,01:45:37,03:58:43 +43,M,01:43:53,03:58:43 +58,M,01:45:53,03:58:44 +48,M,01:48:00,03:58:44 +41,W,01:45:13,03:58:45 +32,M,01:42:44,03:58:45 +22,M,01:43:49,03:58:45 +42,M,01:48:55,03:58:45 +58,M,01:47:57,03:58:45 +29,W,01:46:27,03:58:45 +46,M,01:42:07,03:58:45 +50,M,01:50:19,03:58:46 +30,M,01:51:02,03:58:46 +59,M,01:48:06,03:58:46 +36,M,01:51:01,03:58:46 +26,M,01:39:42,03:58:46 +19,W,01:45:07,03:58:46 +55,M,01:48:52,03:58:46 +38,M,01:32:54,03:58:46 +59,M,01:41:14,03:58:46 +42,M,01:44:15,03:58:47 +46,M,01:47:04,03:58:48 +54,M,01:46:22,03:58:48 +38,M,01:42:47,03:58:49 +46,M,01:47:29,03:58:49 +32,M,01:45:40,03:58:49 +63,M,01:49:17,03:58:50 +48,M,01:57:12,03:58:50 +41,M,01:47:30,03:58:50 +44,M,01:49:27,03:58:50 +45,M,01:42:29,03:58:51 +42,M,01:42:56,03:58:51 +32,W,01:46:29,03:58:51 +34,W,01:58:24,03:58:51 +59,W,01:48:12,03:58:52 +25,W,01:51:17,03:58:52 +46,M,01:48:29,03:58:52 +36,M,01:47:18,03:58:52 +34,M,01:50:21,03:58:52 +27,M,01:57:06,03:58:52 +41,W,01:53:46,03:58:52 +55,W,01:51:43,03:58:53 +37,M,01:45:33,03:58:53 +27,W,01:50:15,03:58:53 +52,M,02:01:06,03:58:54 +42,M,02:06:06,03:58:54 +31,M,01:41:39,03:58:54 +42,W,01:51:22,03:58:54 +54,M,01:50:16,03:58:54 +44,M,01:51:07,03:58:54 +39,M,01:40:48,03:58:55 +39,M,01:43:06,03:58:55 +27,M,01:50:03,03:58:55 +42,M,01:56:58,03:58:55 +47,M,01:46:03,03:58:55 +39,W,01:50:44,03:58:55 +60,M,01:55:52,03:58:56 +25,M,01:40:48,03:58:56 +28,M,01:45:54,03:58:56 +42,M,01:40:18,03:58:56 +58,M,01:45:01,03:58:57 +49,M,01:52:17,03:58:57 +42,M,01:45:39,03:58:57 +49,M,01:57:54,03:58:58 +52,M,01:43:08,03:58:58 +47,W,01:54:27,03:58:58 +36,W,01:47:08,03:58:58 +26,W,01:49:54,03:58:58 +42,W,01:46:28,03:58:59 +65,M,01:46:03,03:58:59 +35,M,01:56:17,03:58:59 +47,M,01:54:01,03:59:00 +36,M,01:55:51,03:59:00 +51,M,01:48:19,03:59:00 +34,M,01:47:30,03:59:00 +28,M,01:48:20,03:59:01 +27,M,01:44:36,03:59:01 +38,M,01:47:36,03:59:01 +39,M,01:45:09,03:59:01 +27,W,01:41:57,03:59:02 +37,W,01:44:41,03:59:02 +40,M,01:45:21,03:59:03 +26,M,01:56:03,03:59:04 +45,M,01:50:45,03:59:04 +55,M,01:49:24,03:59:04 +44,W,01:47:35,03:59:04 +41,M,01:45:21,03:59:04 +42,M,01:51:23,03:59:04 +37,M,01:50:02,03:59:05 +60,M,01:46:15,03:59:05 +41,M,01:48:56,03:59:05 +39,M,01:45:00,03:59:06 +42,M,01:48:42,03:59:06 +47,M,01:55:43,03:59:06 +63,M,01:55:45,03:59:07 +39,M,01:56:20,03:59:08 +30,M,01:48:31,03:59:08 +54,M,01:51:26,03:59:09 +46,M,01:56:18,03:59:09 +41,M,01:50:44,03:59:09 +43,M,01:56:30,03:59:10 +37,M,01:47:18,03:59:10 +45,M,01:53:04,03:59:10 +44,M,01:59:37,03:59:10 +31,M,01:36:39,03:59:10 +44,M,01:47:02,03:59:11 +29,M,01:39:38,03:59:12 +28,M,01:58:50,03:59:12 +43,W,01:47:41,03:59:12 +41,M,01:46:08,03:59:13 +37,M,01:46:08,03:59:13 +38,M,01:46:30,03:59:13 +36,M,01:49:12,03:59:14 +37,W,01:49:29,03:59:15 +59,M,01:47:00,03:59:16 +47,M,01:52:22,03:59:16 +32,W,01:43:31,03:59:16 +41,M,01:54:13,03:59:17 +44,M,01:49:35,03:59:17 +28,M,01:50:00,03:59:18 +41,M,01:41:52,03:59:18 +44,M,01:50:01,03:59:18 +64,M,01:49:36,03:59:18 +57,M,01:53:09,03:59:18 +36,M,01:52:08,03:59:19 +29,W,01:53:41,03:59:20 +41,M,01:51:15,03:59:21 +50,W,01:48:02,03:59:21 +50,M,01:53:17,03:59:21 +30,M,01:52:46,03:59:22 +38,M,01:48:10,03:59:22 +47,M,01:56:43,03:59:22 +46,W,01:55:33,03:59:23 +72,M,01:52:07,03:59:23 +30,W,01:57:37,03:59:24 +23,M,01:47:18,03:59:24 +37,M,01:58:55,03:59:24 +45,W,01:56:59,03:59:24 +28,W,01:44:29,03:59:24 +41,M,01:53:42,03:59:25 +37,M,01:59:32,03:59:25 +35,W,01:45:02,03:59:25 +44,M,01:50:19,03:59:25 +53,M,01:48:43,03:59:26 +46,M,01:45:02,03:59:26 +40,M,01:51:21,03:59:26 +24,W,01:47:16,03:59:26 +30,M,01:46:41,03:59:27 +42,W,01:52:19,03:59:27 +48,W,01:48:48,03:59:27 +37,M,01:42:18,03:59:27 +58,M,01:45:00,03:59:27 +47,W,01:46:24,03:59:27 +70,M,01:45:55,03:59:28 +42,W,01:50:24,03:59:28 +26,W,01:54:10,03:59:28 +33,M,01:51:32,03:59:28 +20,M,01:43:59,03:59:28 +50,M,01:54:14,03:59:28 +40,M,01:47:25,03:59:29 +49,M,01:43:50,03:59:29 +38,W,01:54:14,03:59:29 +36,M,01:48:16,03:59:30 +37,M,01:42:05,03:59:30 +56,M,01:43:23,03:59:31 +40,W,01:41:14,03:59:32 +42,M,01:55:06,03:59:32 +29,M,01:44:29,03:59:33 +31,M,01:46:23,03:59:33 +42,M,01:48:54,03:59:33 +40,M,01:44:14,03:59:34 +25,M,01:55:10,03:59:34 +33,M,01:48:27,03:59:36 +59,M,01:47:54,03:59:37 +47,M,01:53:26,03:59:37 +55,M,01:50:05,03:59:37 +42,M,01:34:03,03:59:38 +55,M,01:46:49,03:59:38 +25,M,01:46:36,03:59:39 +25,W,01:48:03,03:59:39 +29,M,01:46:31,03:59:39 +52,M,01:45:30,03:59:39 +37,W,01:53:37,03:59:40 +43,W,01:43:57,03:59:40 +56,M,01:45:27,03:59:40 +41,M,01:44:10,03:59:40 +40,M,01:48:11,03:59:41 +31,W,01:51:11,03:59:41 +25,M,01:46:10,03:59:41 +43,M,01:47:33,03:59:41 +55,M,01:45:32,03:59:41 +43,M,01:48:49,03:59:42 +41,M,01:50:10,03:59:42 +47,M,01:42:37,03:59:42 +46,M,01:54:58,03:59:43 +30,M,01:54:03,03:59:44 +42,M,01:43:40,03:59:45 +40,M,01:54:58,03:59:45 +62,M,01:48:53,03:59:45 +49,M,01:41:43,03:59:45 +33,M,01:50:04,03:59:45 +38,W,01:48:29,03:59:45 +31,M,01:43:26,03:59:46 +49,M,01:47:11,03:59:46 +49,M,01:44:27,03:59:47 +48,M,01:51:50,03:59:47 +27,M,01:49:46,03:59:48 +44,M,01:56:52,03:59:49 +50,M,01:48:38,03:59:50 +57,M,01:37:31,03:59:50 +56,M,01:55:17,03:59:51 +51,M,01:49:46,03:59:51 +25,M,01:40:53,03:59:51 +36,M,01:43:27,03:59:51 +50,M,01:52:40,03:59:51 +33,M,01:50:51,03:59:52 +40,M,01:44:57,03:59:52 +44,M,01:39:27,03:59:52 +44,M,01:57:35,03:59:53 +60,M,01:46:39,03:59:53 +22,M,01:44:06,03:59:53 +52,M,01:55:44,03:59:55 +47,M,01:53:22,03:59:55 +37,M,01:55:10,03:59:56 +49,M,01:48:35,03:59:56 +35,M,01:48:56,03:59:56 +48,M,01:46:58,03:59:57 +41,W,01:50:16,03:59:57 +46,M,01:49:18,03:59:57 +43,M,01:44:50,03:59:57 +34,M,01:52:08,03:59:57 +31,M,01:47:10,03:59:58 +44,M,01:50:58,03:59:58 +26,M,01:44:34,03:59:58 +49,M,01:56:28,03:59:59 +28,W,01:47:19,03:59:59 +40,M,01:47:58,03:59:59 +38,M,01:52:22,04:00:00 +39,W,01:47:16,04:00:00 +44,M,01:55:58,04:00:00 +45,M,01:40:04,04:00:00 +46,M,01:40:25,04:00:01 +37,M,01:47:31,04:00:01 +63,M,01:56:16,04:00:01 +28,W,01:55:52,04:00:01 +40,W,01:43:56,04:00:01 +54,M,01:43:46,04:00:02 +49,M,01:52:49,04:00:03 +32,W,01:42:56,04:00:04 +51,M,01:48:21,04:00:04 +30,W,01:55:27,04:00:06 +44,M,01:47:46,04:00:07 +39,M,01:56:55,04:00:07 +36,M,01:47:36,04:00:07 +32,M,01:46:46,04:00:08 +50,W,01:44:45,04:00:08 +26,M,01:56:25,04:00:08 +51,M,01:49:43,04:00:09 +31,M,01:37:28,04:00:09 +23,W,01:52:17,04:00:09 +53,M,01:45:02,04:00:09 +43,W,01:45:17,04:00:09 +41,M,01:48:46,04:00:10 +31,M,01:41:50,04:00:10 +33,M,01:38:07,04:00:10 +53,M,01:55:32,04:00:10 +41,M,01:47:25,04:00:11 +37,M,01:55:00,04:00:11 +31,W,01:47:00,04:00:12 +39,M,01:48:22,04:00:13 +44,M,01:48:18,04:00:13 +41,M,01:42:03,04:00:14 +36,M,01:51:18,04:00:14 +25,M,01:40:08,04:00:14 +42,M,01:53:25,04:00:14 +32,M,01:41:14,04:00:15 +51,M,01:48:38,04:00:15 +29,M,01:50:01,04:00:16 +45,M,01:51:00,04:00:16 +34,M,01:53:19,04:00:16 +44,M,01:48:56,04:00:16 +44,M,01:48:22,04:00:16 +21,M,01:59:56,04:00:16 +37,M,01:48:09,04:00:16 +49,M,01:44:58,04:00:17 +27,M,01:47:57,04:00:17 +31,M,01:43:42,04:00:17 +28,M,01:44:56,04:00:18 +34,M,01:44:38,04:00:18 +30,M,01:47:03,04:00:18 +40,W,01:53:10,04:00:18 +56,M,01:44:55,04:00:18 +52,W,01:55:40,04:00:19 +31,M,01:47:43,04:00:19 +39,M,01:34:16,04:00:19 +43,M,02:06:32,04:00:20 +31,M,01:53:21,04:00:20 +36,M,02:01:26,04:00:21 +28,M,01:46:28,04:00:21 +33,W,01:47:53,04:00:22 +25,M,01:46:29,04:00:22 +45,M,01:45:44,04:00:23 +30,M,01:52:36,04:00:23 +33,M,01:49:00,04:00:23 +40,M,01:44:27,04:00:24 +52,M,01:48:46,04:00:24 +44,M,01:42:00,04:00:24 +40,W,01:55:11,04:00:24 +37,M,01:43:35,04:00:24 +39,M,01:51:36,04:00:25 +30,M,01:45:59,04:00:26 +47,M,01:54:48,04:00:26 +36,M,01:50:54,04:00:27 +60,M,01:49:58,04:00:28 +33,M,01:42:27,04:00:28 +44,M,01:45:51,04:00:29 +41,M,01:45:12,04:00:30 +39,W,01:50:19,04:00:30 +45,M,01:44:40,04:00:30 +26,W,01:47:01,04:00:31 +47,M,01:48:45,04:00:31 +32,M,01:43:25,04:00:32 +62,M,01:54:30,04:00:32 +48,M,01:59:56,04:00:33 +45,M,01:57:56,04:00:33 +47,M,01:47:19,04:00:33 +41,M,01:48:48,04:00:33 +39,M,01:44:34,04:00:34 +49,M,02:00:18,04:00:34 +56,M,01:50:07,04:00:35 +29,M,01:41:52,04:00:35 +30,M,01:43:45,04:00:36 +46,M,01:57:46,04:00:36 +48,M,01:47:11,04:00:36 +35,M,01:54:25,04:00:37 +27,W,01:56:40,04:00:37 +43,M,01:52:23,04:00:38 +62,M,01:55:10,04:00:38 +49,W,01:53:29,04:00:38 +42,W,01:54:35,04:00:40 +22,M,01:53:33,04:00:40 +46,W,01:52:26,04:00:40 +29,W,01:45:17,04:00:41 +35,M,01:42:46,04:00:41 +32,W,01:50:32,04:00:42 +30,M,01:55:03,04:00:42 +46,M,01:52:59,04:00:42 +48,W,01:52:13,04:00:42 +46,M,01:41:17,04:00:42 +36,M,01:55:44,04:00:42 +26,M,01:35:00,04:00:43 +56,M,01:49:46,04:00:43 +31,W,01:50:37,04:00:43 +26,M,01:56:45,04:00:43 +64,M,01:47:20,04:00:43 +41,M,01:52:56,04:00:44 +43,W,01:58:19,04:00:44 +43,M,01:47:49,04:00:44 +28,W,01:49:56,04:00:44 +39,M,01:40:44,04:00:44 +46,M,01:55:10,04:00:44 +52,W,01:44:40,04:00:46 +46,M,01:42:36,04:00:46 +29,W,01:41:56,04:00:47 +44,M,01:48:13,04:00:47 +45,W,01:51:44,04:00:48 +47,M,01:46:39,04:00:48 +43,W,01:48:08,04:00:48 +36,M,01:51:19,04:00:48 +49,M,01:52:43,04:00:48 +51,W,01:57:05,04:00:49 +47,M,01:47:53,04:00:49 +43,M,01:51:35,04:00:49 +35,M,01:46:17,04:00:50 +43,M,01:50:24,04:00:50 +26,W,01:49:57,04:00:50 +29,M,01:55:16,04:00:50 +53,M,01:41:27,04:00:51 +52,M,01:45:51,04:00:51 +31,M,01:54:05,04:00:51 +42,W,01:50:13,04:00:51 +47,M,01:55:11,04:00:52 +31,M,01:47:55,04:00:52 +33,M,01:49:13,04:00:52 +46,W,01:48:05,04:00:53 +57,M,01:49:03,04:00:53 +34,M,01:46:25,04:00:53 +45,M,01:42:10,04:00:54 +40,W,01:52:52,04:00:54 +26,M,01:55:12,04:00:54 +48,W,01:49:52,04:00:54 +30,M,01:37:54,04:00:54 +41,M,01:46:21,04:00:55 +27,W,01:51:11,04:00:55 +28,M,01:49:53,04:00:55 +49,M,01:50:37,04:00:55 +37,M,01:47:33,04:00:55 +39,W,01:51:01,04:00:55 +48,M,01:38:25,04:00:56 +28,M,01:44:03,04:00:56 +38,W,01:55:45,04:00:57 +46,M,01:43:12,04:00:58 +29,M,01:49:23,04:00:58 +27,M,01:51:15,04:00:58 +28,W,01:45:23,04:00:59 +40,M,01:44:21,04:01:00 +55,M,01:51:10,04:01:00 +44,M,01:46:22,04:01:00 +45,M,01:54:52,04:01:00 +49,M,01:56:21,04:01:01 +30,W,01:55:41,04:01:01 +45,M,01:39:17,04:01:01 +38,W,01:47:32,04:01:01 +37,M,01:55:56,04:01:02 +41,M,01:41:38,04:01:02 +46,M,01:58:35,04:01:02 +46,M,01:56:16,04:01:02 +40,M,01:58:13,04:01:03 +48,M,01:48:25,04:01:03 +30,M,01:44:00,04:01:03 +35,M,01:53:16,04:01:03 +28,M,01:46:39,04:01:03 +43,M,01:52:24,04:01:04 +58,M,01:48:15,04:01:04 +47,W,01:49:04,04:01:04 +36,W,01:58:04,04:01:05 +41,M,01:37:36,04:01:05 +36,M,01:43:40,04:01:05 +62,M,01:50:54,04:01:05 +48,W,01:51:11,04:01:06 +39,M,01:53:07,04:01:06 +34,M,01:48:28,04:01:06 +33,W,01:56:16,04:01:06 +43,M,01:48:00,04:01:06 +31,M,01:44:24,04:01:06 +44,W,01:48:48,04:01:07 +53,M,01:57:13,04:01:07 +41,M,01:49:38,04:01:07 +36,M,01:47:26,04:01:07 +34,M,01:49:32,04:01:07 +44,M,01:42:54,04:01:07 +42,M,01:51:55,04:01:08 +54,M,01:51:21,04:01:08 +35,M,01:57:27,04:01:09 +48,M,01:56:05,04:01:09 +41,M,01:48:39,04:01:09 +37,M,01:45:26,04:01:09 +53,M,01:52:32,04:01:10 +42,M,01:54:58,04:01:10 +47,M,01:50:24,04:01:10 +37,W,01:57:33,04:01:11 +38,M,01:38:19,04:01:11 +27,M,01:49:59,04:01:11 +46,M,01:49:59,04:01:12 +49,W,01:52:14,04:01:13 +46,M,01:44:03,04:01:13 +50,M,01:47:46,04:01:13 +26,M,01:43:42,04:01:14 +29,M,01:47:06,04:01:14 +33,M,01:47:18,04:01:14 +49,M,01:49:47,04:01:14 +49,M,01:50:58,04:01:14 +51,M,01:59:33,04:01:15 +54,M,01:40:41,04:01:15 +63,M,01:52:12,04:01:15 +42,M,01:55:53,04:01:15 +32,W,01:48:17,04:01:15 +39,W,01:56:30,04:01:15 +36,M,01:48:07,04:01:15 +44,M,01:47:50,04:01:16 +40,M,01:44:14,04:01:16 +55,M,01:47:51,04:01:16 +30,M,01:46:45,04:01:16 +38,M,01:44:43,04:01:17 +33,M,01:56:16,04:01:18 +33,W,01:56:43,04:01:19 +42,M,01:47:54,04:01:19 +30,M,01:49:15,04:01:19 +40,M,01:51:32,04:01:19 +39,W,01:49:03,04:01:20 +36,M,01:44:37,04:01:20 +49,M,01:44:04,04:01:20 +44,M,01:40:09,04:01:21 +39,M,01:57:53,04:01:21 +40,M,01:56:55,04:01:21 +32,M,01:44:28,04:01:21 +32,M,01:43:28,04:01:21 +46,M,01:48:51,04:01:22 +29,M,01:47:39,04:01:23 +31,W,01:45:54,04:01:23 +62,M,01:50:05,04:01:23 +33,M,01:36:55,04:01:24 +38,M,01:57:18,04:01:24 +31,M,01:45:29,04:01:24 +32,M,01:48:36,04:01:25 +50,M,01:49:39,04:01:25 +36,M,01:48:40,04:01:25 +47,M,01:46:03,04:01:25 +28,M,01:46:49,04:01:25 +26,W,01:51:43,04:01:25 +38,M,01:49:14,04:01:25 +32,W,01:44:38,04:01:27 +42,M,01:41:09,04:01:27 +54,M,01:50:03,04:01:27 +39,M,01:50:16,04:01:27 +39,M,01:48:23,04:01:28 +47,M,02:00:31,04:01:28 +34,M,01:45:10,04:01:28 +43,M,01:50:45,04:01:29 +35,M,02:00:25,04:01:29 +23,W,01:55:57,04:01:30 +37,M,01:50:26,04:01:30 +31,M,01:43:16,04:01:31 +39,M,01:56:44,04:01:32 +40,M,01:43:03,04:01:32 +38,W,01:53:05,04:01:33 +40,M,01:55:12,04:01:34 +45,M,01:57:18,04:01:34 +56,M,01:51:21,04:01:34 +43,M,01:49:13,04:01:34 +43,M,01:49:26,04:01:34 +45,M,01:53:30,04:01:34 +44,M,01:43:55,04:01:35 +36,M,02:00:26,04:01:35 +28,M,01:36:39,04:01:36 +47,M,01:55:01,04:01:36 +30,M,01:46:18,04:01:36 +40,M,01:51:30,04:01:36 +33,M,01:52:08,04:01:36 +36,M,01:51:34,04:01:37 +54,M,01:55:35,04:01:37 +54,M,01:48:13,04:01:37 +53,M,01:54:41,04:01:38 +38,M,01:48:20,04:01:38 +43,M,01:47:52,04:01:39 +55,M,01:51:48,04:01:39 +45,M,01:56:04,04:01:40 +49,M,01:40:56,04:01:41 +38,M,01:49:05,04:01:41 +37,M,01:40:10,04:01:41 +48,M,01:46:22,04:01:42 +38,W,01:46:40,04:01:42 +46,W,02:01:46,04:01:43 +46,M,01:49:47,04:01:43 +35,M,01:42:47,04:01:44 +36,M,01:45:42,04:01:44 +51,M,01:55:26,04:01:44 +40,M,01:49:18,04:01:45 +26,M,01:45:45,04:01:45 +34,W,01:48:10,04:01:45 +43,M,01:45:59,04:01:45 +28,M,01:50:30,04:01:46 +51,M,01:49:51,04:01:46 +29,M,01:50:04,04:01:46 +22,W,01:51:22,04:01:47 +28,M,01:44:34,04:01:47 +40,M,01:56:40,04:01:48 +36,W,01:46:28,04:01:48 +41,M,01:48:27,04:01:48 +50,M,01:43:11,04:01:48 +49,M,01:54:53,04:01:48 +34,W,01:49:30,04:01:49 +48,W,01:54:49,04:01:49 +29,M,01:48:31,04:01:49 +23,W,01:51:13,04:01:49 +33,M,01:52:25,04:01:49 +28,W,01:57:19,04:01:49 +33,M,01:43:12,04:01:50 +27,M,01:46:46,04:01:50 +32,M,01:44:15,04:01:50 +42,M,01:49:01,04:01:50 +58,M,01:51:11,04:01:52 +25,M,01:49:49,04:01:52 +35,M,01:55:30,04:01:53 +32,M,01:40:54,04:01:54 +52,M,01:56:06,04:01:54 +55,M,01:55:57,04:01:54 +28,M,01:48:08,04:01:54 +41,M,01:50:26,04:01:55 +28,M,01:42:08,04:01:55 +39,M,01:48:41,04:01:55 +36,W,01:49:06,04:01:55 +50,M,02:00:12,04:01:56 +37,W,01:52:02,04:01:56 +41,M,01:47:42,04:01:57 +48,M,01:52:23,04:01:57 +43,M,01:46:40,04:01:57 +32,M,01:37:44,04:01:57 +23,M,01:39:36,04:01:58 +45,M,01:45:11,04:01:58 +34,M,01:47:56,04:01:58 +43,M,02:00:42,04:01:59 +57,M,01:42:01,04:01:59 +34,W,01:51:54,04:01:59 +36,M,01:43:13,04:01:59 +45,M,01:45:30,04:01:59 +49,M,01:57:37,04:01:59 +56,M,01:47:21,04:02:00 +43,M,01:57:24,04:02:00 +42,M,01:39:44,04:02:00 +41,M,01:58:10,04:02:01 +27,M,01:48:55,04:02:01 +39,M,01:46:17,04:02:01 +29,M,01:45:35,04:02:02 +43,M,01:52:45,04:02:02 +40,W,01:48:31,04:02:03 +43,M,01:56:32,04:02:03 +50,M,01:52:07,04:02:03 +37,M,01:59:23,04:02:04 +33,M,01:47:05,04:02:04 +46,M,01:47:21,04:02:04 +33,M,01:46:42,04:02:05 +40,M,01:42:01,04:02:05 +37,M,01:46:57,04:02:05 +32,M,01:55:31,04:02:06 +37,M,01:49:15,04:02:06 +43,M,01:58:51,04:02:07 +33,W,01:50:53,04:02:07 +49,M,01:54:34,04:02:07 +36,M,01:45:36,04:02:08 +42,M,01:46:33,04:02:09 +61,M,01:54:43,04:02:10 +48,M,01:42:39,04:02:10 +33,M,01:41:37,04:02:10 +32,M,01:55:00,04:02:11 +44,M,01:48:23,04:02:11 +42,W,01:46:31,04:02:11 +29,W,01:49:39,04:02:13 +51,M,01:45:25,04:02:13 +24,W,01:47:24,04:02:13 +38,M,01:48:58,04:02:13 +38,M,01:47:32,04:02:13 +26,W,01:44:42,04:02:13 +45,M,01:45:07,04:02:13 +47,W,01:48:16,04:02:14 +45,M,01:50:25,04:02:14 +38,M,01:44:51,04:02:15 +46,M,01:51:06,04:02:15 +31,M,01:51:17,04:02:15 +36,M,01:52:01,04:02:15 +40,M,01:48:25,04:02:16 +48,M,01:47:17,04:02:16 +44,M,01:46:34,04:02:16 +47,M,01:48:27,04:02:17 +20,M,01:51:26,04:02:17 +35,M,01:48:31,04:02:17 +46,W,01:53:16,04:02:17 +41,W,01:53:08,04:02:17 +35,M,01:55:37,04:02:18 +37,M,01:47:12,04:02:18 +47,W,01:58:15,04:02:19 +49,M,01:47:57,04:02:20 +29,M,01:57:44,04:02:20 +35,M,01:48:36,04:02:20 +42,W,01:53:07,04:02:20 +37,M,01:53:20,04:02:21 +45,M,01:41:59,04:02:21 +51,M,01:44:49,04:02:21 +40,W,01:49:36,04:02:21 +43,M,01:45:10,04:02:22 +41,M,01:47:40,04:02:22 +36,M,01:37:08,04:02:22 +42,W,01:44:19,04:02:23 +24,M,01:42:46,04:02:23 +23,M,01:48:19,04:02:24 +38,M,01:47:26,04:02:24 +54,M,01:49:36,04:02:24 +59,M,01:46:35,04:02:24 +35,M,01:52:06,04:02:24 +26,W,01:49:08,04:02:24 +49,M,01:53:22,04:02:24 +41,M,01:46:55,04:02:25 +39,M,01:44:49,04:02:25 +29,M,01:46:14,04:02:25 +48,M,01:48:06,04:02:25 +43,M,01:41:28,04:02:26 +42,M,01:44:52,04:02:27 +29,W,01:52:10,04:02:27 +37,W,01:57:08,04:02:27 +38,M,01:46:51,04:02:27 +40,W,01:53:09,04:02:27 +36,M,01:44:29,04:02:27 +29,M,01:47:42,04:02:28 +46,M,01:52:37,04:02:29 +43,W,01:53:05,04:02:31 +50,M,01:52:23,04:02:31 +37,W,01:46:08,04:02:32 +47,M,01:50:20,04:02:32 +50,M,01:40:04,04:02:33 +31,M,01:51:06,04:02:33 +22,M,01:47:26,04:02:34 +28,M,01:47:08,04:02:34 +43,M,01:48:34,04:02:35 +24,M,01:49:04,04:02:35 +42,M,01:48:27,04:02:36 +35,M,01:45:26,04:02:36 +29,W,01:52:19,04:02:36 +37,M,01:53:49,04:02:36 +40,M,01:53:52,04:02:37 +33,M,01:45:13,04:02:37 +46,M,01:48:32,04:02:38 +37,M,01:43:19,04:02:39 +35,M,01:53:14,04:02:40 +41,W,01:51:00,04:02:40 +45,M,01:50:25,04:02:40 +55,M,01:52:29,04:02:41 +31,M,01:45:20,04:02:41 +51,W,01:45:52,04:02:41 +39,M,01:52:28,04:02:42 +40,M,01:56:10,04:02:42 +32,M,01:43:59,04:02:43 +49,M,01:56:34,04:02:43 +49,M,01:42:57,04:02:44 +42,M,01:53:56,04:02:44 +36,W,01:49:08,04:02:44 +44,W,01:52:46,04:02:45 +40,M,01:42:53,04:02:45 +61,M,01:39:21,04:02:45 +40,M,01:44:20,04:02:45 +52,M,01:51:06,04:02:45 +56,M,01:44:10,04:02:46 +42,M,01:42:56,04:02:47 +35,M,01:50:36,04:02:47 +48,M,01:51:20,04:02:48 +61,M,01:52:02,04:02:48 +29,M,01:34:54,04:02:49 +50,W,01:52:10,04:02:49 +47,M,01:44:22,04:02:49 +50,W,01:48:07,04:02:49 +46,M,01:57:14,04:02:50 +37,M,01:45:34,04:02:50 +39,M,01:37:57,04:02:51 +26,M,01:47:51,04:02:51 +30,W,01:41:33,04:02:51 +59,M,01:52:39,04:02:51 +42,M,01:56:57,04:02:51 +40,M,01:42:51,04:02:52 +51,M,01:44:30,04:02:52 +39,W,01:49:01,04:02:52 +28,W,01:53:02,04:02:52 +57,M,01:39:13,04:02:52 +38,M,01:38:31,04:02:52 +55,W,01:58:18,04:02:52 +32,M,01:42:13,04:02:52 +40,M,01:52:24,04:02:52 +32,M,01:51:45,04:02:53 +38,W,01:52:10,04:02:53 +36,M,01:49:00,04:02:53 +37,W,01:49:41,04:02:54 +44,M,01:51:39,04:02:54 +37,M,01:47:45,04:02:54 +42,M,01:45:15,04:02:54 +50,M,01:46:28,04:02:55 +44,M,02:00:30,04:02:55 +34,M,01:35:27,04:02:55 +55,M,01:55:52,04:02:56 +40,M,01:53:13,04:02:56 +30,M,02:02:06,04:02:57 +49,M,01:47:58,04:02:57 +30,W,01:52:00,04:02:58 +44,M,01:50:35,04:02:58 +42,W,01:58:20,04:02:58 +34,M,01:47:56,04:02:59 +40,M,01:49:15,04:02:59 +24,M,01:53:17,04:02:59 +27,M,01:45:38,04:02:59 +36,M,01:53:48,04:02:59 +56,M,01:43:17,04:03:00 +34,M,01:55:10,04:03:00 +30,W,01:53:43,04:03:01 +51,M,01:43:06,04:03:02 +35,W,01:50:15,04:03:02 +43,M,01:50:31,04:03:02 +46,M,01:52:19,04:03:03 +42,M,01:58:39,04:03:03 +38,M,01:45:34,04:03:04 +35,M,01:42:27,04:03:04 +34,M,01:41:43,04:03:04 +43,M,01:48:19,04:03:04 +33,W,01:50:35,04:03:05 +48,M,01:49:23,04:03:05 +37,M,01:40:42,04:03:06 +52,M,01:56:39,04:03:07 +38,W,01:48:25,04:03:07 +22,M,01:37:26,04:03:08 +39,M,01:49:56,04:03:08 +36,M,01:56:15,04:03:08 +58,M,01:53:11,04:03:08 +39,W,01:45:37,04:03:10 +33,M,01:48:07,04:03:11 +45,M,01:49:40,04:03:11 +33,M,01:57:46,04:03:11 +38,M,01:51:41,04:03:12 +39,M,01:44:36,04:03:12 +56,M,01:50:59,04:03:14 +35,W,01:50:52,04:03:14 +29,M,01:54:51,04:03:14 +46,W,01:43:47,04:03:15 +43,M,01:58:55,04:03:15 +20,M,01:41:27,04:03:15 +35,M,01:46:16,04:03:15 +34,W,01:59:30,04:03:16 +41,M,01:48:27,04:03:16 +26,W,01:49:17,04:03:16 +45,W,01:48:15,04:03:17 +43,M,01:57:38,04:03:17 +28,W,01:52:53,04:03:18 +29,M,01:46:37,04:03:18 +45,M,01:46:19,04:03:18 +33,M,01:55:02,04:03:18 +38,M,01:41:44,04:03:18 +39,W,01:53:52,04:03:19 +41,M,01:44:47,04:03:19 +49,M,01:48:21,04:03:19 +35,W,01:48:39,04:03:19 +39,W,01:48:42,04:03:19 +40,M,01:47:58,04:03:19 +40,W,01:59:32,04:03:20 +31,M,01:36:28,04:03:21 +56,M,01:57:18,04:03:21 +43,W,01:51:00,04:03:21 +27,M,01:55:59,04:03:21 +34,M,01:50:28,04:03:22 +55,M,01:44:49,04:03:22 +31,W,01:52:24,04:03:22 +29,W,01:47:43,04:03:22 +40,M,01:43:46,04:03:22 +63,M,01:47:10,04:03:23 +28,W,01:49:44,04:03:24 +51,M,01:49:27,04:03:24 +49,M,01:55:38,04:03:24 +32,M,01:51:39,04:03:24 +68,W,01:47:27,04:03:25 +42,M,01:55:36,04:03:25 +34,M,01:55:23,04:03:25 +25,W,01:49:51,04:03:26 +40,W,01:54:08,04:03:26 +31,M,01:48:26,04:03:27 +44,M,01:49:52,04:03:27 +28,M,01:58:15,04:03:27 +50,M,01:49:56,04:03:28 +45,M,01:48:17,04:03:28 +32,M,01:50:33,04:03:29 +52,M,01:51:43,04:03:29 +53,M,01:49:30,04:03:30 +41,M,01:44:37,04:03:30 +36,M,01:41:58,04:03:30 +39,M,01:48:50,04:03:30 +33,M,01:47:02,04:03:30 +50,M,01:55:54,04:03:30 +31,M,01:54:54,04:03:31 +27,W,01:50:52,04:03:31 +50,M,01:47:02,04:03:31 +42,M,01:52:20,04:03:31 +30,M,01:48:25,04:03:31 +42,W,01:51:49,04:03:31 +28,W,01:47:57,04:03:33 +33,M,01:47:56,04:03:33 +31,M,01:47:32,04:03:33 +66,M,01:48:03,04:03:33 +27,W,01:49:23,04:03:35 +47,W,01:52:14,04:03:35 +35,M,01:50:03,04:03:36 +45,M,01:46:08,04:03:36 +37,M,01:49:05,04:03:37 +32,M,01:51:50,04:03:37 +26,M,01:53:08,04:03:37 +38,M,01:50:51,04:03:37 +42,M,01:48:18,04:03:37 +41,M,01:54:51,04:03:37 +28,M,01:42:48,04:03:37 +24,M,01:56:39,04:03:37 +42,M,01:42:38,04:03:37 +26,W,01:54:06,04:03:38 +46,W,01:57:38,04:03:38 +43,M,01:49:28,04:03:38 +46,W,01:51:52,04:03:38 +64,M,01:55:19,04:03:39 +37,M,01:40:47,04:03:39 +42,M,01:48:43,04:03:40 +46,M,01:51:04,04:03:40 +39,M,01:58:39,04:03:41 +46,M,01:50:07,04:03:42 +35,M,01:49:24,04:03:42 +25,M,01:54:04,04:03:42 +41,M,01:52:22,04:03:43 +34,M,01:48:15,04:03:43 +28,M,01:44:40,04:03:43 +43,M,01:47:34,04:03:43 +33,M,01:50:09,04:03:43 +50,W,01:51:41,04:03:44 +32,M,01:44:33,04:03:44 +50,W,01:42:58,04:03:45 +35,M,01:57:18,04:03:45 +44,M,01:46:41,04:03:45 +40,M,01:47:51,04:03:45 +32,W,01:54:17,04:03:46 +46,M,01:53:38,04:03:46 +47,M,01:42:29,04:03:46 +36,M,01:46:22,04:03:46 +46,M,01:50:18,04:03:47 +50,M,01:49:00,04:03:47 +34,M,01:44:26,04:03:47 +41,M,01:43:50,04:03:47 +38,W,01:59:51,04:03:49 +33,M,01:51:56,04:03:49 +40,M,01:47:39,04:03:49 +38,M,01:58:14,04:03:49 +40,M,01:48:50,04:03:49 +25,W,01:51:29,04:03:50 +39,M,01:38:37,04:03:50 +37,M,01:48:01,04:03:50 +33,M,01:53:40,04:03:50 +58,M,01:57:22,04:03:50 +25,M,01:55:46,04:03:50 +47,M,01:45:56,04:03:50 +51,M,01:47:39,04:03:50 +35,M,01:49:33,04:03:51 +50,M,01:52:19,04:03:51 +40,W,01:48:23,04:03:52 +42,W,01:46:09,04:03:53 +44,M,01:50:38,04:03:53 +34,M,01:50:52,04:03:53 +27,W,01:52:54,04:03:53 +54,M,02:00:07,04:03:53 +42,M,01:47:05,04:03:54 +30,W,01:51:55,04:03:55 +36,M,01:46:16,04:03:55 +41,M,01:46:07,04:03:56 +35,M,01:47:25,04:03:56 +40,M,01:53:50,04:03:56 +38,M,01:55:43,04:03:56 +49,M,01:51:04,04:03:57 +45,W,01:49:43,04:03:57 +30,W,01:55:43,04:03:57 +57,M,01:50:33,04:03:57 +34,M,01:48:47,04:03:58 +25,W,01:48:43,04:03:58 +24,W,01:56:02,04:03:59 +42,M,01:47:43,04:03:59 +29,M,01:57:07,04:03:59 +27,W,01:49:27,04:03:59 +32,W,01:45:37,04:03:59 +34,M,01:48:31,04:03:59 +37,M,01:42:03,04:03:59 +18,M,01:57:35,04:04:00 +41,W,01:45:21,04:04:00 +43,M,01:43:52,04:04:00 +38,M,01:47:13,04:04:01 +40,W,01:53:53,04:04:01 +53,M,01:49:02,04:04:01 +40,M,01:53:11,04:04:01 +33,M,01:37:55,04:04:01 +47,M,01:51:00,04:04:02 +40,M,01:43:29,04:04:03 +45,M,01:50:24,04:04:03 +44,M,01:47:43,04:04:03 +44,M,01:42:21,04:04:03 +27,W,01:53:59,04:04:04 +50,W,01:49:36,04:04:04 +43,W,01:48:03,04:04:04 +31,W,01:50:53,04:04:05 +33,M,01:44:35,04:04:05 +34,M,01:45:54,04:04:05 +52,W,01:47:52,04:04:06 +40,M,01:52:52,04:04:06 +55,W,01:50:49,04:04:06 +36,M,01:45:43,04:04:06 +46,M,01:52:31,04:04:07 +50,M,01:49:04,04:04:07 +40,M,01:57:32,04:04:07 +30,M,01:43:20,04:04:07 +45,M,01:49:49,04:04:08 +46,W,01:53:38,04:04:09 +53,M,01:46:47,04:04:09 +49,M,01:47:33,04:04:09 +49,M,01:45:15,04:04:10 +40,W,01:45:28,04:04:11 +38,M,01:54:27,04:04:11 +35,M,01:58:48,04:04:11 +46,M,01:51:58,04:04:11 +38,M,01:47:26,04:04:12 +39,M,01:44:44,04:04:12 +47,M,01:50:58,04:04:12 +43,M,01:49:26,04:04:12 +34,M,01:51:29,04:04:12 +33,M,01:47:48,04:04:12 +43,M,01:32:25,04:04:13 +39,M,01:53:22,04:04:13 +35,M,01:51:20,04:04:13 +37,M,01:48:05,04:04:14 +52,M,01:58:47,04:04:14 +58,M,01:39:53,04:04:14 +37,M,01:50:05,04:04:15 +54,M,01:47:38,04:04:16 +41,M,01:44:50,04:04:16 +46,M,01:47:19,04:04:16 +41,M,01:47:03,04:04:17 +41,M,01:45:15,04:04:17 +36,M,01:45:31,04:04:17 +32,W,01:50:04,04:04:18 +32,M,01:55:24,04:04:18 +42,M,01:49:09,04:04:18 +28,M,01:45:12,04:04:18 +49,M,01:49:41,04:04:19 +50,W,01:51:47,04:04:19 +27,W,01:47:27,04:04:19 +41,M,01:46:57,04:04:21 +34,W,01:51:08,04:04:21 +20,M,01:51:35,04:04:21 +28,M,01:56:56,04:04:21 +56,M,01:54:15,04:04:22 +47,W,01:50:00,04:04:22 +34,M,01:52:46,04:04:23 +46,M,01:46:32,04:04:23 +59,M,01:45:08,04:04:24 +36,M,01:34:47,04:04:24 +58,M,01:50:32,04:04:24 +60,M,01:52:40,04:04:25 +36,W,01:59:07,04:04:25 +57,M,01:50:03,04:04:26 +40,M,01:59:58,04:04:27 +36,M,01:52:48,04:04:27 +28,W,01:54:07,04:04:28 +30,M,01:47:23,04:04:28 +37,M,01:54:05,04:04:30 +45,M,01:48:09,04:04:30 +51,M,01:49:49,04:04:30 +54,M,01:50:25,04:04:31 +33,M,01:55:00,04:04:31 +57,M,01:45:36,04:04:31 +40,M,01:50:11,04:04:31 +39,M,01:49:22,04:04:31 +24,M,01:35:26,04:04:31 +51,M,01:48:15,04:04:32 +26,M,01:46:42,04:04:32 +50,M,01:38:57,04:04:32 +67,M,01:50:37,04:04:32 +27,M,01:46:34,04:04:32 +40,W,01:50:21,04:04:32 +59,M,01:53:04,04:04:33 +37,W,01:47:07,04:04:33 +33,W,01:50:12,04:04:34 +37,M,01:57:43,04:04:34 +53,M,01:52:21,04:04:35 +51,M,01:50:33,04:04:35 +25,M,01:50:03,04:04:35 +48,M,01:48:43,04:04:35 +35,W,01:48:52,04:04:36 +46,M,01:58:15,04:04:36 +43,M,01:56:27,04:04:37 +38,M,01:54:00,04:04:37 +52,M,01:51:33,04:04:37 +27,M,01:44:55,04:04:37 +49,W,01:49:16,04:04:38 +29,M,01:45:38,04:04:38 +29,M,01:36:18,04:04:38 +49,M,01:48:24,04:04:38 +34,W,01:46:53,04:04:39 +25,W,01:51:01,04:04:39 +42,M,01:48:38,04:04:39 +39,W,01:48:29,04:04:39 +49,M,01:52:57,04:04:40 +39,M,01:46:13,04:04:41 +33,M,01:49:51,04:04:41 +46,M,01:47:41,04:04:41 +60,M,01:50:35,04:04:41 +43,M,01:46:13,04:04:41 +35,W,01:48:50,04:04:42 +43,M,01:45:21,04:04:42 +27,M,01:50:42,04:04:43 +39,M,01:52:47,04:04:43 +33,M,01:52:33,04:04:43 +35,M,01:39:06,04:04:44 +38,M,01:56:35,04:04:44 +24,M,01:49:36,04:04:44 +43,M,01:51:35,04:04:44 +31,M,01:53:04,04:04:45 +32,M,01:50:47,04:04:45 +31,M,01:45:11,04:04:45 +56,M,01:45:19,04:04:45 +41,W,01:50:28,04:04:45 +38,M,01:43:03,04:04:45 +32,W,01:53:38,04:04:46 +36,M,01:48:39,04:04:47 +33,M,01:51:10,04:04:47 +35,M,01:49:53,04:04:47 +42,M,01:47:42,04:04:47 +47,W,01:51:23,04:04:47 +50,W,01:49:39,04:04:48 +50,M,01:43:35,04:04:49 +49,W,01:51:40,04:04:49 +43,M,01:57:01,04:04:50 +33,W,01:49:41,04:04:51 +31,W,01:50:17,04:04:51 +30,W,01:50:00,04:04:52 +43,M,01:53:12,04:04:52 +29,M,01:48:49,04:04:52 +30,M,01:44:57,04:04:53 +44,M,01:50:32,04:04:53 +47,M,01:46:40,04:04:53 +62,M,01:53:56,04:04:53 +43,M,01:39:26,04:04:53 +38,M,01:54:45,04:04:53 +55,W,01:58:10,04:04:54 +56,M,01:53:58,04:04:54 +32,M,01:58:47,04:04:54 +42,M,01:47:34,04:04:54 +43,M,01:52:48,04:04:54 +43,W,01:50:54,04:04:55 +48,M,01:45:52,04:04:55 +33,M,01:49:19,04:04:55 +41,M,01:45:45,04:04:55 +31,M,01:49:17,04:04:55 +50,M,01:49:01,04:04:55 +42,M,01:49:10,04:04:55 +47,M,01:47:13,04:04:56 +50,M,01:42:49,04:04:56 +53,M,01:56:11,04:04:57 +47,M,01:44:09,04:04:57 +34,W,01:50:52,04:04:57 +35,M,01:52:44,04:04:57 +43,M,01:44:27,04:04:58 +21,M,01:44:52,04:04:58 +19,W,01:48:39,04:04:58 +25,W,01:45:29,04:04:59 +23,M,01:46:20,04:05:00 +24,W,01:49:34,04:05:00 +38,W,01:52:35,04:05:01 +57,M,01:49:51,04:05:01 +40,M,01:48:37,04:05:01 +22,M,01:50:16,04:05:02 +30,M,01:49:50,04:05:02 +39,M,01:52:46,04:05:02 +39,M,01:39:11,04:05:03 +31,W,01:48:03,04:05:03 +41,M,01:49:46,04:05:03 +41,M,01:52:00,04:05:03 +28,W,01:51:30,04:05:03 +57,W,01:58:27,04:05:03 +43,M,01:49:00,04:05:03 +38,M,01:43:43,04:05:04 +37,M,01:48:13,04:05:04 +45,M,01:49:55,04:05:04 +32,W,01:51:09,04:05:04 +30,M,01:40:08,04:05:04 +40,M,01:48:03,04:05:04 +48,M,01:43:14,04:05:04 +40,M,01:50:10,04:05:05 +37,M,01:51:09,04:05:05 +53,W,01:49:40,04:05:05 +35,W,01:49:17,04:05:05 +51,M,01:46:07,04:05:06 +56,M,01:46:14,04:05:06 +56,M,01:36:52,04:05:06 +46,M,01:58:16,04:05:06 +37,M,01:44:46,04:05:06 +41,M,01:42:53,04:05:07 +28,W,01:48:26,04:05:07 +45,W,01:47:45,04:05:07 +55,M,01:37:06,04:05:08 +40,W,01:55:43,04:05:08 +59,M,01:49:17,04:05:08 +57,M,01:46:55,04:05:08 +59,M,01:47:24,04:05:09 +30,W,01:48:29,04:05:09 +39,W,01:54:40,04:05:10 +37,W,01:58:30,04:05:10 +54,M,01:49:49,04:05:10 +37,M,01:53:59,04:05:10 +66,M,01:48:47,04:05:10 +40,M,01:48:46,04:05:11 +36,M,01:43:16,04:05:11 +51,M,01:53:17,04:05:11 +43,M,01:54:55,04:05:11 +32,W,01:49:24,04:05:13 +39,M,01:45:45,04:05:14 +34,M,01:51:48,04:05:14 +25,M,01:53:28,04:05:15 +43,M,01:46:59,04:05:15 +37,M,01:51:27,04:05:15 +39,M,01:40:21,04:05:16 +46,W,01:50:09,04:05:16 +47,M,01:47:36,04:05:16 +40,M,01:43:08,04:05:17 +55,M,01:47:52,04:05:17 +23,W,01:49:48,04:05:18 +43,W,01:53:25,04:05:18 +31,W,01:59:03,04:05:18 +36,M,01:45:48,04:05:18 +45,M,01:52:56,04:05:19 +51,W,01:56:33,04:05:19 +43,M,01:46:22,04:05:19 +36,M,01:45:16,04:05:19 +40,W,01:44:04,04:05:19 +39,W,01:53:04,04:05:20 +27,M,01:46:49,04:05:20 +44,M,01:52:32,04:05:21 +51,M,01:47:08,04:05:21 +52,M,01:48:38,04:05:21 +53,M,01:49:18,04:05:21 +30,M,01:48:03,04:05:22 +36,M,01:43:00,04:05:22 +51,M,01:52:11,04:05:22 +25,W,01:48:19,04:05:23 +29,M,01:48:53,04:05:23 +31,M,01:50:51,04:05:23 +31,M,01:50:41,04:05:23 +36,W,01:55:06,04:05:24 +39,W,01:59:19,04:05:24 +36,M,01:50:47,04:05:25 +35,M,01:44:47,04:05:25 +32,W,01:43:53,04:05:25 +43,M,01:47:36,04:05:26 +51,M,01:47:44,04:05:26 +43,W,01:56:04,04:05:26 +44,M,01:47:12,04:05:26 +53,M,01:49:16,04:05:27 +50,W,01:50:22,04:05:27 +47,M,01:39:38,04:05:27 +38,M,01:49:30,04:05:27 +33,M,01:53:12,04:05:28 +28,M,01:48:20,04:05:28 +41,M,01:43:59,04:05:29 +54,W,01:55:48,04:05:30 +39,M,01:51:39,04:05:30 +37,M,01:45:20,04:05:32 +61,M,01:48:38,04:05:32 +48,W,01:45:54,04:05:32 +42,M,01:42:32,04:05:33 +45,M,01:47:46,04:05:33 +31,M,01:51:22,04:05:33 +38,M,01:51:01,04:05:34 +28,M,01:55:56,04:05:34 +47,M,02:00:13,04:05:36 +38,M,01:54:26,04:05:36 +32,M,01:52:56,04:05:37 +40,M,01:50:15,04:05:37 +56,M,01:51:31,04:05:37 +34,M,01:48:09,04:05:37 +41,M,01:49:20,04:05:38 +46,M,01:45:24,04:05:38 +34,M,01:48:58,04:05:38 +53,M,01:48:56,04:05:38 +27,M,01:49:13,04:05:38 +30,M,01:44:56,04:05:38 +37,M,01:52:01,04:05:39 +39,M,01:47:47,04:05:39 +25,W,01:58:59,04:05:40 +46,M,01:50:53,04:05:40 +53,M,01:43:47,04:05:40 +26,M,01:50:46,04:05:41 +30,M,01:51:59,04:05:42 +44,M,01:50:02,04:05:42 +51,M,01:50:45,04:05:42 +33,M,01:46:04,04:05:42 +27,M,01:52:42,04:05:43 +53,M,01:49:27,04:05:44 +45,W,01:56:34,04:05:44 +48,M,01:50:10,04:05:45 +41,M,01:52:27,04:05:46 +40,M,01:54:53,04:05:46 +30,W,01:50:43,04:05:46 +26,W,01:52:57,04:05:47 +48,M,01:59:31,04:05:47 +48,M,02:00:47,04:05:48 +41,M,01:44:56,04:05:49 +47,M,01:51:16,04:05:49 +29,M,01:50:51,04:05:50 +30,W,01:58:25,04:05:50 +37,M,01:52:29,04:05:52 +51,M,01:48:09,04:05:52 +23,W,01:47:29,04:05:52 +52,W,01:59:08,04:05:52 +45,M,01:41:29,04:05:53 +26,M,01:49:39,04:05:53 +58,M,01:46:36,04:05:54 +39,W,01:50:58,04:05:54 +35,M,01:57:04,04:05:55 +47,W,01:52:53,04:05:55 +42,M,01:45:25,04:05:55 +28,W,01:47:32,04:05:55 +46,M,01:49:47,04:05:56 +29,M,01:49:10,04:05:56 +27,W,01:44:56,04:05:57 +26,M,01:43:00,04:05:58 +41,M,01:55:25,04:05:58 +40,M,01:50:34,04:05:58 +43,M,01:49:47,04:05:59 +52,M,01:46:54,04:05:59 +59,M,01:54:48,04:05:59 +36,M,01:55:25,04:06:00 +44,M,01:50:48,04:06:00 +39,M,01:50:09,04:06:00 +26,W,01:50:42,04:06:00 +44,M,01:49:21,04:06:00 +38,M,01:45:10,04:06:00 +61,M,01:44:56,04:06:01 +50,M,01:48:22,04:06:01 +32,M,01:56:53,04:06:02 +42,W,01:54:14,04:06:02 +37,M,01:49:48,04:06:02 +26,W,01:48:03,04:06:02 +37,W,01:44:03,04:06:03 +49,M,01:47:39,04:06:03 +38,M,01:41:47,04:06:03 +40,M,01:41:46,04:06:03 +47,M,01:49:17,04:06:04 +48,M,01:52:08,04:06:04 +51,M,01:46:15,04:06:04 +42,M,01:53:07,04:06:04 +49,M,01:53:08,04:06:04 +39,W,01:35:31,04:06:04 +42,W,01:47:54,04:06:05 +29,M,01:40:44,04:06:05 +50,M,01:44:06,04:06:06 +40,M,01:48:48,04:06:06 +31,W,01:54:31,04:06:06 +20,W,01:48:33,04:06:06 +25,M,01:48:51,04:06:07 +47,M,01:45:26,04:06:07 +33,M,01:50:50,04:06:07 +46,W,01:46:21,04:06:07 +32,M,01:50:27,04:06:07 +50,M,01:48:01,04:06:08 +40,W,01:50:25,04:06:09 +33,M,01:50:00,04:06:09 +36,M,01:54:08,04:06:10 +40,M,01:49:08,04:06:10 +34,M,01:50:32,04:06:10 +45,M,01:42:50,04:06:10 +57,M,01:48:17,04:06:10 +31,W,01:47:28,04:06:10 +51,M,01:49:17,04:06:11 +45,M,01:47:48,04:06:11 +25,M,01:53:27,04:06:12 +36,M,01:51:13,04:06:13 +36,W,01:59:01,04:06:13 +31,M,01:50:45,04:06:13 +56,M,01:41:57,04:06:14 +46,M,01:51:41,04:06:14 +41,M,01:49:50,04:06:15 +42,M,01:44:20,04:06:15 +24,M,01:47:18,04:06:15 +51,W,01:49:04,04:06:15 +23,M,01:50:07,04:06:15 +42,M,02:01:29,04:06:16 +43,M,01:46:45,04:06:16 +40,M,01:48:00,04:06:16 +35,M,01:42:18,04:06:16 +33,M,01:40:56,04:06:16 +46,M,01:52:56,04:06:17 +34,M,01:53:42,04:06:17 +34,M,02:01:04,04:06:17 +23,W,01:46:37,04:06:17 +46,M,01:46:36,04:06:18 +41,M,01:42:51,04:06:18 +44,M,01:51:21,04:06:18 +32,W,01:47:58,04:06:18 +29,W,01:47:57,04:06:18 +34,W,01:50:57,04:06:19 +38,M,01:49:17,04:06:19 +27,W,01:49:18,04:06:20 +46,M,01:43:24,04:06:20 +34,W,01:54:44,04:06:21 +43,M,01:55:24,04:06:21 +44,M,01:53:06,04:06:21 +63,M,02:00:19,04:06:21 +50,M,01:54:17,04:06:22 +47,M,01:55:38,04:06:22 +32,W,01:59:43,04:06:22 +26,M,01:48:31,04:06:22 +57,M,01:49:34,04:06:22 +36,M,01:40:06,04:06:22 +34,M,01:49:25,04:06:23 +63,M,01:48:10,04:06:24 +38,M,01:50:17,04:06:24 +51,W,01:58:05,04:06:24 +40,W,01:53:23,04:06:24 +21,M,01:51:04,04:06:24 +35,W,01:53:30,04:06:25 +30,M,01:50:33,04:06:25 +40,M,01:45:15,04:06:25 +29,M,01:47:00,04:06:25 +45,M,01:49:11,04:06:25 +22,M,01:44:40,04:06:25 +51,M,01:49:59,04:06:26 +33,W,01:48:29,04:06:26 +53,M,01:44:35,04:06:26 +39,M,01:48:04,04:06:27 +47,M,01:42:51,04:06:27 +31,M,01:52:37,04:06:28 +26,W,01:59:26,04:06:28 +27,M,01:51:36,04:06:29 +40,M,01:56:18,04:06:29 +50,M,01:53:08,04:06:29 +46,W,01:46:11,04:06:29 +52,M,01:49:15,04:06:29 +42,M,01:53:27,04:06:30 +42,M,01:51:39,04:06:30 +56,M,01:53:15,04:06:30 +40,M,01:51:31,04:06:30 +56,M,01:57:20,04:06:30 +25,M,02:00:18,04:06:30 +38,M,01:56:52,04:06:30 +32,M,01:50:38,04:06:31 +29,M,01:48:27,04:06:31 +37,M,01:49:36,04:06:31 +53,M,01:49:07,04:06:31 +28,W,01:54:40,04:06:32 +36,M,01:50:29,04:06:32 +37,M,01:47:48,04:06:32 +41,M,01:51:37,04:06:32 +29,M,01:47:08,04:06:32 +45,W,01:50:32,04:06:32 +39,W,01:49:11,04:06:32 +31,M,01:42:03,04:06:33 +37,M,01:44:11,04:06:33 +42,M,02:03:03,04:06:33 +42,M,01:54:03,04:06:33 +39,M,01:50:14,04:06:34 +49,M,01:50:27,04:06:34 +45,M,01:43:49,04:06:34 +58,M,01:51:16,04:06:34 +40,M,01:52:01,04:06:35 +41,W,01:55:54,04:06:35 +33,M,01:54:35,04:06:35 +51,M,01:47:23,04:06:35 +36,M,01:47:00,04:06:35 +46,M,01:44:26,04:06:35 +26,M,01:57:21,04:06:36 +24,W,01:47:44,04:06:36 +45,M,01:52:31,04:06:37 +42,M,01:32:55,04:06:37 +31,W,01:54:46,04:06:38 +34,M,01:53:20,04:06:38 +45,M,01:57:57,04:06:38 +62,M,01:49:08,04:06:38 +38,M,01:48:19,04:06:38 +42,M,01:42:09,04:06:39 +42,M,01:46:50,04:06:39 +32,W,01:52:10,04:06:39 +38,M,01:50:18,04:06:39 +37,M,01:46:53,04:06:39 +25,M,01:42:48,04:06:40 +42,M,01:45:06,04:06:41 +40,M,01:46:55,04:06:41 +51,M,01:52:45,04:06:42 +28,M,01:51:00,04:06:42 +37,M,01:47:59,04:06:42 +43,M,01:50:07,04:06:43 +51,M,01:44:37,04:06:43 +29,M,01:50:39,04:06:43 +41,M,01:45:39,04:06:44 +47,M,01:49:53,04:06:44 +41,W,01:49:27,04:06:45 +47,M,01:43:08,04:06:46 +45,W,02:00:38,04:06:46 +31,M,01:45:51,04:06:46 +42,M,01:52:34,04:06:47 +30,W,01:52:35,04:06:47 +41,M,01:47:07,04:06:47 +46,W,01:51:12,04:06:47 +58,M,01:45:31,04:06:48 +52,M,01:50:35,04:06:48 +39,M,01:45:36,04:06:48 +38,M,01:49:57,04:06:48 +24,M,01:46:41,04:06:48 +39,M,01:53:57,04:06:48 +23,M,01:51:00,04:06:48 +55,M,01:47:53,04:06:50 +27,M,01:47:44,04:06:50 +45,M,01:47:13,04:06:50 +28,M,01:41:54,04:06:50 +38,M,01:50:55,04:06:51 +32,M,01:41:21,04:06:52 +50,M,01:49:15,04:06:52 +32,M,01:43:19,04:06:53 +56,M,01:53:30,04:06:54 +29,W,01:46:43,04:06:54 +47,W,01:51:03,04:06:54 +37,M,01:49:00,04:06:55 +30,M,01:58:25,04:06:55 +53,M,01:57:53,04:06:55 +25,M,01:46:28,04:06:55 +40,M,01:50:02,04:06:56 +31,M,01:50:20,04:06:56 +28,M,01:44:37,04:06:57 +40,M,01:50:20,04:06:57 +32,M,01:51:47,04:06:57 +40,M,01:55:28,04:06:57 +46,M,01:43:48,04:06:57 +44,M,01:54:04,04:06:57 +48,M,01:51:43,04:06:58 +34,M,01:50:31,04:06:59 +45,M,01:43:42,04:06:59 +41,W,01:56:21,04:07:00 +29,M,01:48:47,04:07:00 +39,M,01:48:31,04:07:01 +47,W,01:46:38,04:07:01 +33,M,01:50:28,04:07:01 +37,M,01:50:41,04:07:01 +49,M,01:50:18,04:07:01 +50,M,01:48:47,04:07:01 +31,W,01:48:24,04:07:02 +32,W,01:58:35,04:07:02 +36,M,01:44:27,04:07:02 +47,M,01:47:21,04:07:02 +34,M,01:44:19,04:07:03 +30,M,01:52:48,04:07:03 +48,M,01:46:38,04:07:03 +39,M,01:51:01,04:07:03 +48,M,01:46:42,04:07:03 +30,W,01:53:19,04:07:03 +45,M,01:46:53,04:07:05 +26,M,01:46:15,04:07:05 +24,M,01:47:08,04:07:05 +36,M,01:38:43,04:07:06 +29,M,01:48:11,04:07:06 +21,M,01:50:35,04:07:06 +46,M,01:51:38,04:07:06 +30,M,01:54:39,04:07:06 +41,W,01:48:34,04:07:06 +51,M,01:50:02,04:07:07 +36,M,01:53:35,04:07:07 +52,M,01:42:52,04:07:08 +34,M,01:51:26,04:07:08 +28,M,01:48:53,04:07:08 +33,M,01:50:22,04:07:09 +35,M,01:50:48,04:07:09 +50,M,01:47:53,04:07:09 +42,M,01:51:36,04:07:09 +39,W,01:50:24,04:07:10 +39,M,01:48:27,04:07:10 +30,M,01:46:39,04:07:11 +32,W,01:50:29,04:07:12 +32,M,01:45:19,04:07:12 +51,M,01:46:36,04:07:12 +38,W,01:52:47,04:07:12 +32,W,01:49:23,04:07:13 +52,M,01:53:31,04:07:14 +37,M,01:46:51,04:07:14 +45,M,01:54:31,04:07:15 +57,M,01:56:09,04:07:15 +36,W,01:51:04,04:07:16 +38,M,01:51:51,04:07:16 +48,W,01:51:08,04:07:17 +41,M,01:52:46,04:07:17 +56,M,01:54:29,04:07:17 +29,M,01:52:33,04:07:18 +41,M,01:42:28,04:07:18 +51,W,01:48:32,04:07:18 +19,M,01:50:34,04:07:18 +44,M,01:50:06,04:07:19 +43,M,01:48:44,04:07:19 +49,M,01:57:29,04:07:20 +46,M,01:44:16,04:07:20 +27,M,01:46:43,04:07:20 +41,M,01:58:25,04:07:21 +41,M,01:46:13,04:07:21 +43,M,01:50:12,04:07:21 +55,M,01:46:21,04:07:22 +31,M,01:40:28,04:07:22 +42,W,01:48:14,04:07:22 +54,W,01:51:27,04:07:22 +45,M,01:58:11,04:07:23 +39,M,01:49:50,04:07:23 +31,M,01:48:01,04:07:23 +35,W,01:49:15,04:07:24 +34,M,01:42:37,04:07:24 +28,W,01:47:23,04:07:24 +53,M,01:47:18,04:07:25 +34,M,01:48:58,04:07:26 +29,M,01:51:17,04:07:27 +58,M,01:54:23,04:07:27 +28,M,01:50:53,04:07:28 +39,M,01:56:30,04:07:28 +29,W,01:48:56,04:07:29 +37,W,01:45:43,04:07:29 +32,M,01:46:02,04:07:29 +40,M,01:51:06,04:07:29 +28,M,01:39:40,04:07:30 +33,M,01:56:27,04:07:30 +27,W,01:50:51,04:07:30 +45,W,01:48:36,04:07:30 +54,W,01:53:42,04:07:31 +46,M,01:56:18,04:07:31 +31,M,01:44:28,04:07:31 +35,M,01:53:41,04:07:31 +28,M,01:50:18,04:07:31 +23,M,01:46:55,04:07:31 +40,M,01:51:23,04:07:32 +46,M,01:46:23,04:07:32 +56,M,01:52:40,04:07:33 +37,M,01:36:37,04:07:34 +53,M,01:54:33,04:07:35 +31,W,01:48:24,04:07:35 +29,M,01:51:03,04:07:37 +28,M,01:52:22,04:07:38 +50,M,01:45:12,04:07:38 +40,M,01:50:13,04:07:39 +28,M,01:37:09,04:07:39 +44,M,01:48:19,04:07:39 +47,M,01:47:07,04:07:39 +38,M,01:46:24,04:07:40 +47,W,01:47:21,04:07:41 +50,M,01:53:58,04:07:41 +58,M,01:52:04,04:07:43 +54,M,01:45:40,04:07:43 +30,M,01:54:56,04:07:43 +34,M,01:54:03,04:07:43 +40,M,01:50:17,04:07:44 +43,M,01:45:25,04:07:44 +43,M,01:51:44,04:07:45 +45,M,01:51:28,04:07:45 +54,W,01:56:54,04:07:45 +51,M,01:50:48,04:07:46 +54,W,01:51:31,04:07:46 +43,M,02:04:56,04:07:46 +58,M,01:52:11,04:07:46 +39,M,01:56:30,04:07:46 +46,W,01:46:30,04:07:46 +28,M,01:51:49,04:07:47 +53,M,01:48:53,04:07:47 +34,M,01:52:07,04:07:47 +32,M,01:49:28,04:07:47 +43,M,01:40:31,04:07:48 +45,M,01:56:48,04:07:48 +29,M,01:46:46,04:07:48 +49,W,01:53:43,04:07:49 +35,W,01:49:30,04:07:49 +36,M,01:41:04,04:07:49 +26,W,01:51:05,04:07:49 +36,M,01:58:16,04:07:49 +36,W,01:52:36,04:07:50 +47,M,01:49:19,04:07:50 +51,M,01:55:21,04:07:51 +35,W,01:50:57,04:07:51 +41,M,01:49:37,04:07:52 +28,M,01:59:34,04:07:52 +59,M,01:51:08,04:07:53 +38,W,01:39:29,04:07:53 +53,M,01:55:50,04:07:53 +63,M,01:45:49,04:07:53 +45,W,01:50:36,04:07:54 +33,W,01:53:11,04:07:54 +42,M,01:52:24,04:07:54 +37,M,01:38:45,04:07:55 +47,M,01:50:19,04:07:55 +55,M,01:40:39,04:07:56 +28,W,01:51:37,04:07:56 +37,M,01:54:08,04:07:56 +39,M,01:49:55,04:07:56 +40,M,01:53:41,04:07:56 +60,M,01:47:24,04:07:56 +43,M,01:50:49,04:07:56 +42,M,01:49:59,04:07:57 +45,W,01:51:16,04:07:57 +39,M,01:49:47,04:07:57 +35,W,01:48:51,04:07:58 +54,M,01:49:19,04:07:58 +49,M,01:47:30,04:07:58 +39,W,01:51:22,04:07:58 +35,M,01:51:10,04:07:58 +26,W,01:50:16,04:07:58 +42,M,01:49:55,04:07:58 +32,M,01:50:39,04:07:59 +31,M,01:39:42,04:07:59 +39,M,01:47:38,04:07:59 +39,M,01:57:21,04:07:59 +33,M,01:54:09,04:07:59 +27,M,01:48:12,04:08:00 +46,M,01:42:53,04:08:00 +27,W,01:52:19,04:08:00 +43,M,02:02:38,04:08:00 +33,W,01:52:39,04:08:00 +34,M,01:45:54,04:08:00 +34,M,01:48:44,04:08:01 +27,M,01:49:35,04:08:01 +38,M,01:53:44,04:08:02 +46,M,01:44:32,04:08:02 +38,M,01:35:57,04:08:03 +31,M,01:58:46,04:08:03 +37,M,01:51:40,04:08:04 +53,M,01:55:09,04:08:04 +40,M,01:59:24,04:08:04 +45,M,01:55:40,04:08:05 +49,M,01:48:46,04:08:05 +42,W,01:52:08,04:08:06 +22,W,01:40:41,04:08:06 +28,M,01:47:36,04:08:06 +46,W,01:53:40,04:08:06 +53,M,01:43:47,04:08:07 +41,W,01:45:12,04:08:07 +35,W,01:48:03,04:08:07 +22,M,01:51:49,04:08:07 +24,M,01:52:10,04:08:07 +32,W,02:00:47,04:08:07 +37,M,01:58:11,04:08:08 +43,W,01:43:28,04:08:08 +38,M,01:45:41,04:08:08 +42,M,01:44:43,04:08:08 +20,M,01:45:45,04:08:08 +30,M,01:52:17,04:08:08 +43,M,01:49:29,04:08:09 +42,M,01:42:16,04:08:09 +56,W,02:01:43,04:08:09 +31,W,01:56:05,04:08:09 +42,W,01:51:51,04:08:10 +39,M,01:51:42,04:08:10 +54,M,01:54:51,04:08:11 +39,W,01:53:47,04:08:11 +42,W,01:51:24,04:08:11 +53,M,01:49:28,04:08:11 +26,W,01:53:31,04:08:11 +28,M,01:48:56,04:08:11 +45,M,01:50:48,04:08:11 +38,M,01:47:27,04:08:12 +41,W,01:49:04,04:08:12 +47,M,01:56:41,04:08:12 +33,M,01:50:12,04:08:12 +36,M,01:36:11,04:08:12 +31,M,01:54:34,04:08:13 +36,M,01:51:07,04:08:13 +33,M,01:45:20,04:08:13 +47,M,01:55:43,04:08:14 +40,M,01:55:07,04:08:14 +48,M,01:48:55,04:08:14 +54,M,01:48:14,04:08:14 +46,M,01:49:08,04:08:15 +62,M,01:47:26,04:08:15 +28,W,01:51:35,04:08:16 +50,M,01:40:49,04:08:16 +26,M,01:40:48,04:08:16 +42,M,01:50:53,04:08:17 +25,M,01:43:07,04:08:17 +52,M,01:50:49,04:08:17 +41,M,01:52:28,04:08:17 +41,W,01:48:14,04:08:17 +41,M,01:51:27,04:08:18 +48,M,01:50:44,04:08:18 +25,W,01:50:28,04:08:19 +48,W,01:49:40,04:08:19 +43,M,01:54:31,04:08:19 +55,M,02:04:24,04:08:19 +31,M,01:53:36,04:08:20 +25,M,01:51:14,04:08:20 +31,W,01:51:09,04:08:20 +43,W,01:58:23,04:08:20 +27,W,01:55:30,04:08:21 +32,M,01:58:02,04:08:21 +39,W,01:57:50,04:08:21 +46,M,01:48:00,04:08:21 +44,W,01:55:04,04:08:21 +32,M,01:49:15,04:08:22 +53,M,01:51:08,04:08:22 +41,M,01:42:41,04:08:22 +34,M,01:53:59,04:08:22 +43,W,01:58:05,04:08:22 +30,M,01:45:47,04:08:22 +44,W,01:52:04,04:08:23 +38,W,01:51:14,04:08:23 +29,M,01:50:08,04:08:23 +45,W,01:48:27,04:08:23 +28,W,01:49:41,04:08:23 +46,M,01:51:59,04:08:23 +46,M,01:56:21,04:08:23 +38,W,01:50:44,04:08:24 +46,M,01:51:40,04:08:24 +31,M,01:51:05,04:08:24 +41,M,01:51:57,04:08:24 +54,M,01:45:23,04:08:24 +40,M,01:54:29,04:08:24 +36,M,01:51:35,04:08:25 +41,W,01:52:46,04:08:26 +67,M,01:48:14,04:08:26 +32,W,01:51:56,04:08:26 +52,M,01:46:17,04:08:26 +37,M,01:50:23,04:08:26 +27,W,01:49:30,04:08:26 +43,M,01:39:42,04:08:27 +45,M,01:50:44,04:08:27 +41,M,01:50:19,04:08:27 +48,M,01:49:02,04:08:27 +47,W,01:48:51,04:08:27 +25,W,01:52:15,04:08:28 +48,M,01:47:49,04:08:28 +35,M,01:47:05,04:08:28 +51,M,01:56:57,04:08:28 +40,M,01:52:07,04:08:28 +44,W,01:40:24,04:08:29 +46,M,01:46:17,04:08:29 +34,M,01:52:58,04:08:30 +45,W,01:51:03,04:08:30 +32,M,01:48:44,04:08:30 +41,M,01:43:16,04:08:30 +33,M,01:48:05,04:08:31 +28,M,01:58:04,04:08:32 +48,M,01:45:37,04:08:32 +59,M,01:49:39,04:08:32 +27,M,01:46:34,04:08:34 +39,M,01:48:55,04:08:34 +34,M,01:52:33,04:08:34 +41,M,01:51:39,04:08:34 +32,W,01:53:36,04:08:34 +35,W,01:52:24,04:08:35 +38,M,01:52:53,04:08:35 +56,M,01:50:27,04:08:36 +39,M,01:45:28,04:08:36 +43,M,01:44:10,04:08:36 +38,M,01:49:57,04:08:36 +34,M,01:51:13,04:08:36 +38,W,01:51:12,04:08:36 +42,M,01:47:57,04:08:37 +46,M,01:48:35,04:08:37 +26,W,01:49:01,04:08:38 +60,M,01:50:55,04:08:38 +34,M,01:53:15,04:08:38 +37,M,02:00:32,04:08:39 +38,M,01:50:55,04:08:39 +47,W,01:51:13,04:08:39 +32,M,01:51:58,04:08:40 +41,M,01:44:04,04:08:40 +38,M,01:54:07,04:08:40 +43,M,01:48:50,04:08:40 +36,M,01:54:11,04:08:41 +34,M,01:46:26,04:08:41 +33,M,01:40:40,04:08:41 +35,M,01:46:01,04:08:41 +54,M,01:52:03,04:08:41 +32,W,01:56:30,04:08:42 +55,M,01:45:40,04:08:42 +36,M,01:48:52,04:08:42 +30,W,01:50:28,04:08:43 +46,M,01:52:14,04:08:43 +37,M,01:46:24,04:08:44 +26,M,01:49:15,04:08:44 +35,M,01:46:31,04:08:44 +31,M,01:56:46,04:08:44 +47,M,01:53:33,04:08:44 +35,M,01:54:26,04:08:45 +37,W,01:54:04,04:08:45 +41,M,01:42:32,04:08:45 +33,W,01:48:11,04:08:46 +54,M,01:55:08,04:08:46 +35,W,01:54:14,04:08:46 +44,W,01:53:18,04:08:46 +27,M,01:44:41,04:08:46 +42,M,01:48:04,04:08:46 +27,W,01:52:58,04:08:47 +51,M,01:49:24,04:08:47 +45,M,01:45:43,04:08:47 +33,M,01:47:31,04:08:47 +28,M,01:51:16,04:08:47 +23,M,01:39:20,04:08:47 +43,M,01:48:45,04:08:47 +32,M,01:54:46,04:08:47 +55,M,01:56:53,04:08:47 +43,M,01:47:30,04:08:48 +64,M,01:48:54,04:08:48 +45,W,01:49:00,04:08:48 +28,M,01:49:02,04:08:48 +31,M,01:55:51,04:08:49 +28,M,01:52:04,04:08:49 +36,M,01:48:27,04:08:49 +28,M,01:55:21,04:08:49 +34,M,01:45:39,04:08:49 +62,M,01:47:05,04:08:49 +59,M,01:54:15,04:08:49 +34,W,01:47:45,04:08:49 +42,M,01:51:40,04:08:49 +23,W,01:52:15,04:08:49 +23,M,01:53:10,04:08:50 +31,M,02:03:24,04:08:50 +32,W,01:52:32,04:08:50 +39,M,01:49:48,04:08:51 +49,M,01:52:27,04:08:51 +34,M,01:47:18,04:08:51 +54,M,01:51:43,04:08:51 +49,M,01:49:56,04:08:51 +47,M,01:50:47,04:08:51 +39,M,01:44:23,04:08:51 +45,W,01:52:40,04:08:51 +37,M,01:47:26,04:08:52 +53,M,01:51:40,04:08:52 +37,M,01:49:05,04:08:52 +40,M,01:42:03,04:08:53 +40,M,01:48:03,04:08:53 +45,M,01:49:33,04:08:53 +34,M,01:47:19,04:08:53 +41,M,01:50:44,04:08:54 +47,W,01:51:00,04:08:54 +31,M,01:48:07,04:08:54 +31,W,02:00:49,04:08:55 +31,W,01:53:42,04:08:55 +28,M,01:53:13,04:08:55 +44,M,01:43:14,04:08:55 +43,M,01:53:52,04:08:55 +42,M,01:56:29,04:08:57 +25,W,01:44:04,04:08:57 +31,M,01:53:21,04:08:57 +49,M,01:53:49,04:08:57 +50,M,01:55:29,04:08:57 +31,M,01:49:09,04:08:58 +28,M,01:48:29,04:08:58 +47,W,01:49:42,04:08:58 +53,M,01:53:16,04:08:58 +48,M,01:55:59,04:08:58 +50,M,01:52:48,04:08:58 +30,M,01:48:01,04:08:58 +52,M,01:50:36,04:08:58 +35,M,01:50:12,04:08:59 +30,M,01:56:59,04:08:59 +36,M,01:47:04,04:08:59 +52,M,01:54:33,04:09:00 +50,M,01:47:53,04:09:00 +44,M,01:50:20,04:09:00 +38,M,01:38:44,04:09:01 +71,M,01:55:49,04:09:01 +32,W,01:48:48,04:09:01 +41,M,01:53:55,04:09:01 +29,M,01:55:34,04:09:01 +32,W,01:54:32,04:09:02 +44,M,01:51:22,04:09:02 +35,M,01:58:53,04:09:02 +45,M,01:54:20,04:09:02 +26,M,01:52:12,04:09:03 +26,W,01:56:13,04:09:03 +25,M,01:52:38,04:09:04 +32,W,02:00:23,04:09:04 +29,M,01:47:09,04:09:04 +43,M,01:43:54,04:09:04 +51,M,01:54:30,04:09:04 +33,W,01:48:34,04:09:05 +35,W,01:54:40,04:09:05 +45,M,01:51:01,04:09:05 +49,W,02:05:39,04:09:05 +36,W,02:02:50,04:09:05 +28,W,01:52:20,04:09:05 +49,M,01:53:07,04:09:05 +33,M,01:41:33,04:09:05 +42,M,01:50:53,04:09:06 +34,M,01:51:51,04:09:06 +40,M,01:53:28,04:09:06 +35,W,01:49:16,04:09:06 +28,M,01:50:54,04:09:06 +38,M,01:55:17,04:09:07 +56,M,01:53:10,04:09:08 +66,M,01:49:03,04:09:08 +45,M,01:49:38,04:09:09 +43,M,01:52:00,04:09:09 +48,M,01:40:07,04:09:09 +25,W,01:56:05,04:09:09 +26,M,01:33:54,04:09:09 +43,W,01:51:40,04:09:10 +55,M,01:55:12,04:09:10 +34,M,01:48:11,04:09:10 +52,W,02:01:42,04:09:11 +42,W,02:03:38,04:09:11 +39,M,01:45:41,04:09:11 +30,M,01:50:49,04:09:11 +38,M,01:42:53,04:09:11 +49,M,01:50:11,04:09:11 +35,M,01:51:57,04:09:11 +35,W,01:49:59,04:09:12 +63,M,01:48:00,04:09:12 +32,M,01:54:14,04:09:12 +32,M,01:52:39,04:09:12 +20,M,01:50:20,04:09:12 +43,W,01:55:24,04:09:13 +51,M,01:53:22,04:09:13 +46,W,01:48:43,04:09:13 +33,M,01:50:22,04:09:14 +39,M,01:52:42,04:09:15 +36,M,01:47:00,04:09:15 +55,M,01:50:38,04:09:15 +27,W,01:52:49,04:09:15 +48,M,01:50:45,04:09:15 +34,M,01:43:02,04:09:15 +57,M,01:49:59,04:09:16 +38,W,02:01:05,04:09:16 +47,M,01:49:45,04:09:16 +24,M,01:54:27,04:09:16 +32,M,01:49:51,04:09:16 +37,M,01:56:01,04:09:17 +49,W,01:52:22,04:09:18 +36,M,01:54:34,04:09:18 +54,M,01:57:28,04:09:19 +31,M,01:54:30,04:09:20 +56,M,01:52:25,04:09:20 +32,W,01:44:25,04:09:20 +52,M,01:49:46,04:09:21 +54,M,01:48:35,04:09:22 +37,M,01:55:33,04:09:22 +25,M,01:42:31,04:09:23 +50,M,01:49:18,04:09:23 +39,M,01:49:33,04:09:24 +41,M,01:50:28,04:09:24 +32,M,01:45:08,04:09:24 +38,W,01:49:32,04:09:26 +24,W,01:58:01,04:09:26 +46,M,01:50:01,04:09:27 +24,W,01:48:14,04:09:27 +36,M,01:55:57,04:09:27 +37,M,01:46:13,04:09:28 +48,M,01:46:48,04:09:28 +38,M,01:48:33,04:09:29 +52,M,01:55:47,04:09:29 +38,W,01:47:43,04:09:30 +54,M,01:51:48,04:09:30 +31,W,01:48:39,04:09:30 +42,M,01:51:18,04:09:30 +36,M,01:49:11,04:09:30 +56,M,01:53:24,04:09:30 +39,M,01:48:22,04:09:30 +47,M,01:49:18,04:09:31 +41,W,01:55:44,04:09:31 +48,M,01:46:16,04:09:31 +40,M,01:54:25,04:09:31 +38,M,01:53:21,04:09:31 +39,M,01:51:02,04:09:32 +26,M,01:50:18,04:09:32 +42,M,01:54:37,04:09:32 +50,M,01:50:11,04:09:32 +29,W,01:57:42,04:09:32 +58,M,01:54:20,04:09:32 +49,M,01:53:11,04:09:32 +47,W,01:46:16,04:09:32 +22,W,01:53:00,04:09:33 +40,M,01:52:54,04:09:33 +51,W,01:51:37,04:09:33 +54,M,01:53:19,04:09:33 +47,M,01:54:25,04:09:34 +40,W,01:57:00,04:09:34 +35,W,01:50:05,04:09:34 +45,M,01:39:31,04:09:34 +47,M,01:48:55,04:09:34 +42,W,01:48:58,04:09:35 +32,W,01:50:26,04:09:35 +45,M,01:54:20,04:09:35 +39,M,01:48:54,04:09:36 +28,M,01:28:47,04:09:36 +41,W,01:53:56,04:09:36 +47,W,02:07:58,04:09:36 +49,M,01:53:48,04:09:36 +25,W,01:39:22,04:09:37 +47,M,01:46:26,04:09:37 +41,M,01:54:01,04:09:37 +33,W,01:56:14,04:09:37 +45,M,02:01:07,04:09:37 +27,W,01:52:22,04:09:38 +50,M,01:52:17,04:09:38 +26,W,01:54:27,04:09:38 +37,M,02:01:40,04:09:38 +39,M,01:48:50,04:09:39 +30,M,01:53:49,04:09:39 +34,W,01:53:17,04:09:39 +47,W,01:54:54,04:09:39 +30,M,01:55:04,04:09:39 +39,M,02:00:56,04:09:40 +24,M,01:53:52,04:09:40 +50,M,01:52:06,04:09:40 +39,M,01:45:45,04:09:40 +27,M,01:40:23,04:09:40 +46,M,01:42:39,04:09:40 +49,M,01:51:06,04:09:40 +47,M,01:53:01,04:09:41 +28,M,01:54:53,04:09:41 +29,M,01:44:02,04:09:41 +38,M,01:52:39,04:09:41 +44,M,01:46:32,04:09:41 +35,M,01:43:00,04:09:41 +46,M,01:53:08,04:09:41 +43,M,02:00:36,04:09:42 +49,M,01:56:45,04:09:42 +57,M,01:49:09,04:09:42 +43,M,01:36:34,04:09:43 +32,M,01:57:38,04:09:43 +47,M,01:53:18,04:09:43 +29,W,02:00:46,04:09:44 +43,M,02:00:08,04:09:45 +48,W,01:50:04,04:09:45 +41,M,01:43:06,04:09:45 +29,M,01:54:15,04:09:45 +49,M,01:44:09,04:09:45 +54,M,01:48:46,04:09:45 +42,M,01:51:39,04:09:46 +38,M,01:43:37,04:09:46 +26,W,01:45:01,04:09:46 +42,M,01:54:52,04:09:47 +52,M,01:53:12,04:09:47 +43,W,01:48:19,04:09:47 +29,M,02:03:48,04:09:48 +40,M,01:52:28,04:09:48 +58,M,01:47:37,04:09:48 +40,M,01:54:17,04:09:49 +53,M,01:46:25,04:09:49 +37,M,01:54:05,04:09:49 +28,W,01:49:59,04:09:50 +53,M,01:50:15,04:09:50 +39,M,01:52:54,04:09:50 +68,M,01:50:05,04:09:50 +57,M,01:49:28,04:09:50 +34,M,01:51:17,04:09:51 +30,W,01:53:08,04:09:51 +32,W,01:50:05,04:09:51 +62,M,01:58:27,04:09:51 +50,M,01:49:19,04:09:53 +44,M,01:53:02,04:09:53 +28,M,01:41:53,04:09:54 +50,M,01:48:08,04:09:54 +43,W,01:57:00,04:09:54 +39,M,01:47:26,04:09:55 +33,W,01:52:42,04:09:55 +30,M,01:54:53,04:09:55 +28,W,01:52:39,04:09:55 +51,M,01:51:50,04:09:55 +43,M,01:48:11,04:09:56 +37,W,01:54:10,04:09:56 +44,M,01:55:11,04:09:57 +44,W,01:54:04,04:09:57 +40,M,01:53:17,04:09:57 +34,M,01:46:04,04:09:57 +55,M,01:54:25,04:09:57 +42,M,01:51:55,04:09:57 +24,M,01:50:32,04:09:57 +61,M,01:46:21,04:09:58 +34,W,01:52:02,04:09:58 +41,W,01:54:39,04:09:58 +24,W,01:52:59,04:09:59 +31,M,01:53:33,04:09:59 +43,W,01:45:24,04:09:59 +29,W,01:46:44,04:09:59 +32,W,01:49:58,04:09:59 +33,W,01:46:08,04:10:00 +40,M,02:00:19,04:10:01 +29,M,01:50:43,04:10:01 +38,M,01:52:50,04:10:02 +31,W,02:00:03,04:10:02 +58,M,01:57:55,04:10:03 +57,M,01:50:20,04:10:03 +21,M,01:40:17,04:10:03 +40,M,01:55:11,04:10:03 +39,W,01:52:16,04:10:03 +36,M,01:48:06,04:10:05 +21,M,01:44:43,04:10:05 +30,W,01:47:25,04:10:05 +29,M,01:51:18,04:10:05 +32,M,01:53:23,04:10:06 +47,W,01:51:09,04:10:06 +36,M,01:52:20,04:10:06 +26,M,01:48:36,04:10:06 +42,W,02:00:45,04:10:07 +38,W,01:56:27,04:10:07 +38,M,01:47:49,04:10:07 +40,W,01:54:10,04:10:07 +27,W,01:59:22,04:10:08 +32,M,01:45:13,04:10:08 +44,M,01:46:04,04:10:09 +49,M,01:57:07,04:10:09 +42,M,01:51:23,04:10:09 +56,M,01:55:22,04:10:09 +40,W,01:50:37,04:10:10 +47,M,01:52:14,04:10:10 +36,W,01:47:09,04:10:10 +50,M,01:49:03,04:10:10 +47,M,01:50:34,04:10:10 +45,M,01:50:40,04:10:10 +36,M,01:57:32,04:10:10 +59,M,01:47:53,04:10:11 +47,M,01:42:27,04:10:11 +40,M,01:50:14,04:10:11 +51,M,02:04:39,04:10:12 +35,W,01:53:26,04:10:12 +19,M,01:56:33,04:10:12 +50,W,01:56:39,04:10:12 +25,M,01:48:52,04:10:12 +45,M,01:43:05,04:10:12 +42,M,01:58:42,04:10:12 +53,M,01:50:25,04:10:12 +43,M,01:52:08,04:10:14 +36,W,01:53:41,04:10:14 +28,M,01:56:44,04:10:14 +35,W,01:57:27,04:10:14 +39,M,01:57:07,04:10:15 +63,M,01:51:15,04:10:15 +25,W,01:51:27,04:10:16 +59,M,01:49:12,04:10:16 +52,M,01:54:17,04:10:17 +49,M,01:53:57,04:10:17 +37,W,01:46:32,04:10:17 +57,M,01:49:24,04:10:17 +31,M,01:52:03,04:10:18 +44,W,01:52:41,04:10:18 +31,W,01:44:28,04:10:18 +46,W,01:54:48,04:10:18 +50,W,01:44:44,04:10:18 +33,W,01:53:58,04:10:18 +36,M,01:51:27,04:10:18 +46,M,01:48:50,04:10:19 +27,M,01:36:05,04:10:20 +44,M,01:54:10,04:10:20 +40,M,01:51:48,04:10:20 +45,M,01:40:03,04:10:20 +33,M,01:55:15,04:10:21 +35,W,01:54:11,04:10:22 +45,M,01:55:31,04:10:22 +53,M,01:48:41,04:10:22 +30,W,01:49:51,04:10:22 +26,W,01:50:12,04:10:22 +41,W,01:59:28,04:10:23 +61,M,01:51:01,04:10:23 +49,M,01:47:30,04:10:23 +51,M,01:53:13,04:10:23 +24,M,01:40:30,04:10:23 +36,M,01:54:22,04:10:23 +57,M,01:52:37,04:10:24 +49,M,01:47:49,04:10:24 +44,W,01:56:52,04:10:24 +37,W,01:46:28,04:10:24 +43,M,01:48:22,04:10:24 +36,M,01:50:09,04:10:24 +27,M,01:46:24,04:10:25 +47,M,01:52:43,04:10:26 +36,W,01:52:52,04:10:26 +42,M,01:59:20,04:10:26 +40,M,01:44:22,04:10:27 +22,W,01:45:15,04:10:27 +51,M,01:52:14,04:10:27 +34,W,01:57:45,04:10:27 +48,M,01:53:34,04:10:28 +36,M,01:45:15,04:10:28 +40,M,01:49:02,04:10:28 +34,M,01:52:41,04:10:28 +36,W,01:46:20,04:10:28 +44,M,01:54:09,04:10:29 +33,M,01:52:42,04:10:29 +35,M,01:54:55,04:10:29 +37,M,01:47:46,04:10:29 +24,W,01:47:45,04:10:29 +29,M,01:52:03,04:10:29 +52,W,01:53:08,04:10:29 +37,M,01:55:16,04:10:29 +33,W,01:53:15,04:10:30 +32,M,01:49:38,04:10:30 +46,W,01:48:09,04:10:30 +42,M,02:15:12,04:10:31 +50,W,01:54:09,04:10:31 +33,M,01:46:54,04:10:31 +43,M,01:48:14,04:10:31 +56,M,01:53:13,04:10:31 +52,W,01:54:34,04:10:31 +40,M,01:49:27,04:10:32 +37,M,01:53:22,04:10:32 +32,M,01:48:52,04:10:32 +58,M,01:52:53,04:10:32 +41,M,01:44:23,04:10:32 +58,M,01:54:54,04:10:33 +51,M,01:50:34,04:10:33 +45,M,01:53:08,04:10:33 +39,W,01:50:35,04:10:33 +37,M,01:48:44,04:10:33 +47,M,01:56:13,04:10:34 +47,M,01:49:58,04:10:34 +39,M,01:45:18,04:10:34 +41,M,01:54:44,04:10:34 +24,W,01:52:14,04:10:34 +33,M,01:50:23,04:10:35 +33,M,01:42:53,04:10:35 +44,M,01:58:58,04:10:35 +44,M,01:50:44,04:10:35 +19,M,01:58:39,04:10:36 +54,W,01:54:11,04:10:36 +28,M,01:42:51,04:10:37 +46,M,01:51:08,04:10:37 +46,M,01:58:59,04:10:37 +41,W,01:58:24,04:10:38 +50,M,01:49:13,04:10:38 +41,M,01:48:53,04:10:39 +24,M,01:51:13,04:10:39 +49,M,01:48:48,04:10:39 +38,M,01:58:24,04:10:39 +44,M,01:46:42,04:10:39 +33,M,01:53:19,04:10:40 +55,M,01:52:36,04:10:40 +49,M,01:46:19,04:10:41 +40,M,01:49:27,04:10:41 +31,W,01:46:56,04:10:41 +40,M,01:50:34,04:10:41 +28,W,01:49:13,04:10:41 +39,M,01:54:01,04:10:41 +29,M,01:53:26,04:10:41 +40,M,01:56:39,04:10:42 +39,M,01:53:11,04:10:42 +30,W,01:51:52,04:10:42 +69,M,01:50:04,04:10:43 +29,M,01:53:45,04:10:43 +44,M,01:54:48,04:10:43 +44,M,01:53:45,04:10:44 +56,M,01:59:07,04:10:44 +31,M,01:51:37,04:10:44 +42,M,01:48:15,04:10:44 +30,W,01:52:55,04:10:44 +31,M,01:49:40,04:10:45 +42,W,01:54:46,04:10:45 +29,M,01:57:21,04:10:45 +26,M,01:54:16,04:10:45 +60,M,01:46:11,04:10:46 +29,M,01:43:58,04:10:46 +52,M,01:49:32,04:10:46 +51,M,01:49:37,04:10:46 +58,M,01:52:30,04:10:46 +40,M,01:48:41,04:10:47 +44,M,01:51:08,04:10:47 +51,M,01:57:21,04:10:47 +28,W,01:54:43,04:10:47 +51,W,01:52:25,04:10:47 +45,M,01:49:37,04:10:47 +46,M,01:54:36,04:10:48 +45,W,01:51:37,04:10:48 +32,W,01:55:38,04:10:48 +39,M,01:51:24,04:10:48 +27,M,01:55:39,04:10:48 +23,M,01:42:15,04:10:48 +64,M,01:57:42,04:10:49 +40,W,01:45:04,04:10:49 +28,M,01:54:20,04:10:49 +33,M,01:53:59,04:10:50 +30,M,01:51:38,04:10:50 +37,W,01:53:10,04:10:50 +21,M,01:45:47,04:10:50 +23,M,01:46:32,04:10:51 +49,M,01:49:25,04:10:51 +55,W,01:51:06,04:10:51 +31,M,01:50:55,04:10:52 +36,M,01:49:44,04:10:52 +29,M,01:52:10,04:10:53 +36,M,01:46:54,04:10:53 +27,M,01:50:30,04:10:53 +35,M,01:53:29,04:10:53 +52,W,01:58:02,04:10:54 +52,M,01:51:13,04:10:54 +28,W,01:50:45,04:10:54 +41,M,01:49:29,04:10:56 +28,M,01:53:05,04:10:56 +41,M,01:44:06,04:10:57 +40,W,01:48:10,04:10:57 +48,M,01:52:26,04:10:57 +55,M,01:54:54,04:10:57 +56,M,01:55:07,04:10:57 +44,M,01:54:21,04:10:57 +40,M,01:54:23,04:10:57 +43,W,01:49:21,04:10:58 +29,W,01:53:49,04:10:59 +28,M,01:53:16,04:10:59 +30,W,01:53:34,04:10:59 +44,M,01:54:13,04:11:00 +28,W,01:55:00,04:11:00 +35,M,01:53:34,04:11:00 +34,M,01:59:52,04:11:00 +38,M,01:51:52,04:11:00 +44,M,01:59:41,04:11:00 +44,M,01:46:02,04:11:01 +42,M,01:52:13,04:11:01 +33,M,01:50:02,04:11:01 +33,W,01:57:11,04:11:01 +27,W,01:50:42,04:11:02 +35,M,01:53:31,04:11:02 +58,M,01:47:43,04:11:02 +37,M,01:47:19,04:11:03 +33,M,01:53:48,04:11:03 +46,M,01:53:08,04:11:03 +35,M,01:52:20,04:11:04 +35,M,01:43:00,04:11:04 +47,W,01:54:54,04:11:04 +51,M,01:53:35,04:11:04 +25,W,01:51:20,04:11:05 +51,W,01:49:46,04:11:06 +33,W,01:52:31,04:11:06 +43,M,01:47:53,04:11:06 +38,W,01:51:01,04:11:07 +40,M,01:51:06,04:11:07 +42,W,01:53:48,04:11:07 +30,W,01:55:04,04:11:07 +31,M,01:52:54,04:11:07 +28,M,01:47:45,04:11:07 +29,M,01:35:42,04:11:08 +44,W,01:51:04,04:11:08 +40,M,01:43:59,04:11:08 +38,M,01:52:36,04:11:09 +30,M,01:47:51,04:11:09 +39,M,01:56:29,04:11:10 +64,M,01:50:30,04:11:11 +30,M,01:51:47,04:11:12 +43,M,01:55:26,04:11:12 +33,M,01:56:27,04:11:12 +34,M,01:47:14,04:11:13 +33,M,01:43:15,04:11:13 +21,M,01:55:09,04:11:13 +47,M,01:50:44,04:11:14 +38,M,01:56:13,04:11:14 +39,M,01:49:06,04:11:14 +33,W,01:50:49,04:11:15 +53,M,01:46:09,04:11:15 +35,W,01:49:49,04:11:15 +39,M,01:53:09,04:11:15 +31,M,01:52:31,04:11:15 +24,W,01:54:47,04:11:16 +39,M,01:51:30,04:11:16 +33,M,01:52:46,04:11:17 +38,W,01:52:54,04:11:17 +45,M,01:52:06,04:11:17 +45,M,01:54:35,04:11:18 +31,M,01:55:01,04:11:18 +39,M,01:51:17,04:11:18 +24,W,01:41:41,04:11:18 +34,M,01:57:18,04:11:18 +37,M,01:37:43,04:11:18 +36,M,01:54:08,04:11:18 +26,M,01:49:51,04:11:19 +38,W,01:45:28,04:11:19 +28,W,01:54:14,04:11:20 +31,M,01:57:20,04:11:20 +38,M,01:59:45,04:11:20 +35,M,01:57:37,04:11:21 +47,M,01:45:34,04:11:21 +47,M,02:03:54,04:11:22 +40,W,01:54:54,04:11:22 +49,M,01:51:05,04:11:22 +57,M,01:53:16,04:11:22 +54,M,01:58:55,04:11:22 +37,M,01:52:03,04:11:23 +33,M,01:47:42,04:11:23 +51,M,01:57:35,04:11:23 +33,M,02:02:13,04:11:23 +50,M,01:53:17,04:11:24 +38,W,01:57:02,04:11:24 +27,M,01:50:30,04:11:25 +41,M,01:51:52,04:11:25 +33,W,01:49:53,04:11:26 +33,M,01:49:56,04:11:26 +44,M,01:51:47,04:11:26 +49,M,01:52:08,04:11:26 +42,M,01:54:55,04:11:29 +47,W,01:52:46,04:11:30 +47,M,01:55:19,04:11:31 +45,W,01:43:59,04:11:32 +45,M,01:51:20,04:11:32 +43,M,01:58:48,04:11:32 +24,W,01:55:18,04:11:32 +43,M,01:51:00,04:11:33 +36,M,01:54:29,04:11:33 +63,M,01:55:44,04:11:33 +25,W,01:54:20,04:11:33 +29,W,01:59:03,04:11:33 +38,M,01:42:38,04:11:33 +31,W,01:53:58,04:11:33 +27,M,01:49:00,04:11:33 +36,M,01:44:42,04:11:33 +25,W,01:51:51,04:11:35 +39,M,01:49:06,04:11:35 +31,M,01:53:29,04:11:35 +41,M,01:58:02,04:11:35 +42,M,01:54:39,04:11:35 +43,M,01:58:44,04:11:35 +45,M,01:42:33,04:11:35 +25,M,01:56:30,04:11:35 +47,M,01:53:07,04:11:36 +38,M,01:49:25,04:11:36 +35,M,01:58:04,04:11:37 +34,M,01:57:10,04:11:37 +47,W,01:59:55,04:11:38 +26,W,01:54:09,04:11:38 +46,M,01:50:33,04:11:39 +31,M,01:56:32,04:11:39 +28,W,01:51:18,04:11:39 +46,M,01:52:02,04:11:39 +40,W,01:50:34,04:11:40 +39,W,01:54:37,04:11:40 +35,W,01:53:00,04:11:40 +34,M,01:58:09,04:11:41 +26,W,01:52:49,04:11:42 +42,W,01:49:44,04:11:42 +44,W,01:51:12,04:11:42 +31,M,01:56:51,04:11:42 +39,M,01:56:12,04:11:42 +43,W,02:04:20,04:11:43 +41,M,01:54:10,04:11:44 +56,M,01:52:39,04:11:44 +41,M,01:47:52,04:11:44 +51,M,01:53:23,04:11:44 +42,M,01:48:27,04:11:44 +33,M,02:01:20,04:11:45 +45,M,01:51:47,04:11:45 +37,M,01:45:26,04:11:45 +56,M,02:00:23,04:11:46 +41,W,01:49:08,04:11:46 +31,M,01:54:19,04:11:46 +43,W,01:51:28,04:11:46 +44,M,01:53:35,04:11:46 +41,W,01:53:02,04:11:47 +36,M,01:47:47,04:11:47 +43,W,01:52:58,04:11:47 +28,M,01:40:26,04:11:48 +50,M,01:47:39,04:11:48 +32,W,01:53:43,04:11:48 +41,M,01:56:38,04:11:48 +39,W,01:48:14,04:11:49 +39,M,01:58:41,04:11:49 +31,M,01:51:24,04:11:49 +49,M,01:56:52,04:11:50 +30,M,01:45:07,04:11:50 +43,M,01:55:40,04:11:50 +42,M,01:45:27,04:11:51 +54,M,01:56:18,04:11:51 +44,W,01:53:35,04:11:52 +43,M,01:55:42,04:11:52 +49,M,01:53:33,04:11:52 +28,W,01:45:44,04:11:52 +43,M,01:53:16,04:11:53 +32,M,01:54:50,04:11:54 +37,W,01:58:54,04:11:54 +39,M,01:46:38,04:11:54 +51,M,01:49:34,04:11:54 +41,M,01:51:33,04:11:54 +29,W,01:54:24,04:11:55 +33,W,01:50:59,04:11:55 +47,M,01:43:14,04:11:55 +32,M,01:51:22,04:11:55 +35,M,01:51:27,04:11:55 +39,M,01:54:44,04:11:55 +32,M,01:54:40,04:11:56 +38,W,01:57:30,04:11:56 +50,M,01:57:24,04:11:56 +42,W,02:03:02,04:11:56 +38,M,01:49:24,04:11:56 +30,M,01:51:37,04:11:56 +25,M,01:44:23,04:11:56 +70,M,01:52:18,04:11:57 +35,M,01:57:38,04:11:57 +34,M,01:48:22,04:11:58 +39,M,01:51:06,04:11:58 +44,M,01:50:44,04:11:58 +37,M,01:44:31,04:11:58 +60,M,01:53:23,04:11:58 +39,M,01:49:45,04:11:59 +33,M,01:49:04,04:11:59 +35,M,01:53:47,04:11:59 +50,W,01:57:06,04:11:59 +37,W,01:51:43,04:12:00 +31,M,01:45:47,04:12:00 +30,M,01:51:51,04:12:00 +47,M,01:49:59,04:12:00 +49,W,01:53:54,04:12:00 +52,M,01:47:14,04:12:01 +45,M,02:04:03,04:12:01 +31,W,01:54:18,04:12:01 +34,M,01:49:48,04:12:01 +32,W,01:51:47,04:12:02 +48,M,01:43:44,04:12:02 +30,W,01:53:20,04:12:02 +53,M,01:52:59,04:12:03 +48,W,01:48:49,04:12:04 +31,M,01:44:26,04:12:04 +40,M,01:49:51,04:12:05 +31,M,01:54:05,04:12:05 +34,M,01:53:28,04:12:05 +30,W,01:47:53,04:12:06 +44,W,01:59:18,04:12:06 +45,M,01:44:45,04:12:07 +42,M,01:43:00,04:12:07 +26,W,01:54:51,04:12:08 +38,M,01:53:52,04:12:08 +37,M,01:55:16,04:12:08 +43,M,01:49:48,04:12:08 +31,M,01:40:28,04:12:08 +30,M,01:43:15,04:12:08 +29,W,01:55:16,04:12:09 +40,M,01:54:37,04:12:09 +61,M,02:00:01,04:12:09 +41,M,01:49:28,04:12:09 +49,M,01:53:18,04:12:09 +45,M,01:51:45,04:12:09 +55,M,01:51:46,04:12:10 +27,W,01:53:55,04:12:10 +62,M,01:51:45,04:12:10 +43,M,01:59:27,04:12:10 +43,M,01:55:26,04:12:10 +59,M,01:57:26,04:12:11 +42,M,01:53:27,04:12:12 +35,W,01:50:40,04:12:12 +33,W,02:02:44,04:12:12 +59,M,01:52:08,04:12:13 +43,W,01:49:19,04:12:13 +43,M,01:53:41,04:12:13 +28,M,01:52:57,04:12:13 +31,M,01:51:42,04:12:13 +48,M,01:55:51,04:12:14 +23,W,01:54:03,04:12:14 +38,M,02:02:35,04:12:14 +41,W,01:53:02,04:12:14 +42,M,02:00:36,04:12:15 +48,M,01:51:21,04:12:15 +33,W,01:54:30,04:12:16 +61,M,01:49:27,04:12:16 +55,M,01:42:55,04:12:17 +33,M,01:50:24,04:12:17 +30,M,01:45:53,04:12:17 +57,M,01:42:50,04:12:17 +31,M,01:57:27,04:12:17 +40,W,01:57:01,04:12:17 +44,M,01:55:56,04:12:17 +32,M,01:53:39,04:12:17 +31,W,01:51:25,04:12:18 +51,M,01:54:22,04:12:18 +40,M,01:48:38,04:12:18 +32,W,01:51:48,04:12:18 +37,M,01:46:24,04:12:18 +38,M,01:49:11,04:12:19 +38,M,01:47:40,04:12:20 +40,M,01:53:28,04:12:20 +44,M,01:56:58,04:12:20 +23,W,01:48:56,04:12:20 +42,M,01:51:24,04:12:20 +42,M,02:00:12,04:12:21 +25,M,01:48:56,04:12:21 +50,M,01:53:23,04:12:22 +36,M,01:54:09,04:12:22 +32,M,01:56:29,04:12:22 +58,M,01:50:39,04:12:23 +26,W,01:57:19,04:12:23 +30,W,01:54:46,04:12:24 +38,M,01:54:28,04:12:24 +57,M,01:50:35,04:12:24 +43,M,01:54:58,04:12:24 +48,M,01:51:27,04:12:25 +48,W,01:51:40,04:12:25 +30,M,01:56:42,04:12:26 +45,W,01:50:19,04:12:26 +53,M,02:02:15,04:12:26 +28,W,01:51:34,04:12:26 +31,W,01:48:54,04:12:27 +34,M,01:51:24,04:12:27 +53,M,01:49:46,04:12:27 +39,M,01:55:27,04:12:27 +34,W,01:57:34,04:12:27 +41,M,01:43:53,04:12:28 +41,M,01:47:57,04:12:28 +43,M,01:55:14,04:12:28 +36,M,01:52:17,04:12:29 +38,M,01:55:05,04:12:29 +41,M,01:51:45,04:12:29 +42,M,01:54:50,04:12:29 +32,M,01:52:25,04:12:30 +41,M,01:49:53,04:12:30 +35,M,01:50:20,04:12:30 +30,W,01:44:50,04:12:31 +40,W,01:54:40,04:12:31 +47,M,01:50:02,04:12:31 +30,W,01:56:01,04:12:31 +32,M,01:55:58,04:12:31 +37,W,01:54:59,04:12:32 +37,W,01:53:44,04:12:33 +23,M,01:50:12,04:12:33 +44,M,02:00:47,04:12:33 +59,M,01:45:41,04:12:33 +30,M,01:53:09,04:12:33 +44,M,01:55:11,04:12:33 +45,M,01:49:02,04:12:34 +49,W,01:54:51,04:12:34 +27,W,01:47:03,04:12:34 +34,M,01:55:01,04:12:34 +32,M,01:47:23,04:12:34 +59,M,01:49:50,04:12:34 +34,M,02:00:21,04:12:34 +41,M,01:46:37,04:12:35 +29,W,01:46:46,04:12:35 +29,M,01:55:45,04:12:35 +31,M,01:57:37,04:12:35 +35,W,01:54:14,04:12:36 +36,M,01:51:05,04:12:36 +49,M,01:51:19,04:12:36 +46,M,01:53:36,04:12:37 +41,M,01:41:48,04:12:38 +38,M,01:53:38,04:12:38 +34,W,01:54:57,04:12:38 +50,M,01:54:44,04:12:38 +38,W,01:54:52,04:12:38 +43,M,01:44:18,04:12:39 +25,M,02:02:03,04:12:39 +27,M,01:49:17,04:12:40 +31,W,01:51:45,04:12:40 +39,M,01:55:20,04:12:40 +41,M,01:53:29,04:12:41 +42,M,01:49:27,04:12:41 +33,W,01:57:33,04:12:41 +37,M,01:52:28,04:12:41 +41,M,01:56:47,04:12:41 +45,M,01:55:03,04:12:41 +37,M,01:52:25,04:12:41 +36,M,01:47:13,04:12:43 +53,M,01:49:59,04:12:43 +57,M,01:50:06,04:12:43 +36,M,01:57:04,04:12:43 +38,W,01:51:10,04:12:44 +53,M,01:49:43,04:12:44 +36,M,01:47:36,04:12:44 +23,M,01:53:30,04:12:45 +32,W,01:52:34,04:12:45 +41,W,01:50:19,04:12:45 +32,M,01:45:30,04:12:45 +37,M,01:57:22,04:12:46 +29,M,01:51:32,04:12:46 +45,M,01:47:58,04:12:46 +46,W,01:54:45,04:12:47 +35,M,01:53:34,04:12:47 +35,M,01:56:31,04:12:47 +35,M,01:48:06,04:12:47 +25,W,01:51:57,04:12:47 +51,M,01:57:04,04:12:47 +30,M,01:43:46,04:12:48 +39,M,01:57:50,04:12:48 +62,M,01:59:11,04:12:48 +38,W,01:58:57,04:12:48 +48,M,01:53:04,04:12:49 +38,W,01:48:29,04:12:49 +30,M,01:56:03,04:12:49 +35,M,01:48:54,04:12:49 +26,W,01:47:46,04:12:50 +39,M,01:54:44,04:12:50 +35,M,01:53:20,04:12:51 +28,W,01:45:37,04:12:51 +46,W,01:55:11,04:12:51 +44,M,01:50:28,04:12:51 +31,W,01:56:06,04:12:51 +30,M,01:57:23,04:12:52 +25,W,01:53:12,04:12:52 +35,W,01:59:05,04:12:52 +41,W,01:50:25,04:12:52 +46,M,01:57:52,04:12:53 +40,W,01:59:21,04:12:53 +57,M,02:01:15,04:12:53 +27,W,01:49:34,04:12:54 +46,M,01:47:49,04:12:54 +48,M,01:49:26,04:12:54 +49,M,01:50:38,04:12:54 +47,W,01:49:32,04:12:54 +24,M,01:46:26,04:12:55 +67,M,02:01:54,04:12:56 +52,M,01:52:40,04:12:56 +25,W,01:55:21,04:12:56 +37,M,01:55:10,04:12:56 +42,M,01:54:04,04:12:57 +32,M,01:50:51,04:12:57 +37,W,01:59:59,04:12:57 +35,M,01:48:50,04:12:57 +52,M,01:49:46,04:12:57 +31,M,01:57:32,04:12:57 +44,M,01:54:25,04:12:57 +56,M,01:47:34,04:12:57 +34,M,01:59:15,04:12:57 +41,W,01:52:27,04:12:58 +53,M,02:07:09,04:12:58 +45,M,01:56:29,04:12:59 +39,M,01:58:19,04:12:59 +32,M,01:56:02,04:13:00 +33,M,01:58:45,04:13:00 +46,M,01:46:33,04:13:01 +39,M,01:53:55,04:13:01 +38,M,01:56:24,04:13:02 +40,M,01:52:17,04:13:02 +30,W,01:53:18,04:13:02 +43,M,01:53:10,04:13:02 +40,M,02:02:10,04:13:02 +56,M,01:47:29,04:13:03 +34,M,01:46:47,04:13:03 +47,W,01:53:11,04:13:03 +35,M,01:50:44,04:13:04 +31,W,01:52:03,04:13:04 +33,M,02:00:34,04:13:04 +50,W,01:51:36,04:13:04 +50,M,01:48:40,04:13:04 +43,M,01:49:05,04:13:05 +43,M,01:50:02,04:13:05 +44,M,01:49:56,04:13:05 +37,M,01:58:18,04:13:05 +45,M,01:57:30,04:13:06 +25,W,01:52:15,04:13:06 +43,M,01:49:54,04:13:06 +42,M,01:53:47,04:13:06 +38,M,01:52:13,04:13:06 +32,M,01:50:18,04:13:06 +41,M,01:46:24,04:13:06 +44,M,01:54:43,04:13:07 +35,M,01:54:47,04:13:07 +36,M,01:46:10,04:13:07 +25,M,01:50:38,04:13:07 +30,W,01:53:38,04:13:07 +37,W,01:52:59,04:13:07 +56,M,01:45:45,04:13:08 +41,M,01:51:46,04:13:08 +55,M,01:55:29,04:13:09 +55,M,01:54:16,04:13:09 +24,M,01:43:09,04:13:09 +49,W,01:52:10,04:13:09 +52,M,01:45:57,04:13:09 +37,M,01:58:20,04:13:10 +32,M,01:55:52,04:13:10 +31,W,01:56:24,04:13:11 +32,M,01:53:19,04:13:12 +26,W,02:01:47,04:13:12 +27,M,01:51:03,04:13:12 +32,W,01:55:08,04:13:12 +32,W,01:57:43,04:13:12 +29,W,01:45:25,04:13:13 +42,M,01:51:30,04:13:13 +60,M,01:55:08,04:13:13 +41,M,01:46:18,04:13:14 +59,W,01:55:25,04:13:14 +40,M,01:54:44,04:13:14 +35,W,01:53:42,04:13:14 +27,W,01:52:50,04:13:14 +60,M,01:51:30,04:13:15 +38,M,01:49:26,04:13:16 +40,M,01:51:35,04:13:16 +44,M,01:45:02,04:13:16 +54,W,01:48:47,04:13:16 +41,M,01:51:16,04:13:16 +37,M,01:48:19,04:13:17 +42,M,01:53:51,04:13:17 +39,M,01:56:16,04:13:17 +45,M,01:52:40,04:13:17 +35,W,01:53:04,04:13:17 +26,W,01:52:11,04:13:17 +24,M,01:53:24,04:13:18 +38,M,01:55:38,04:13:18 +40,M,01:56:29,04:13:18 +33,W,01:51:38,04:13:18 +36,M,02:06:18,04:13:19 +37,M,01:31:38,04:13:19 +52,W,01:57:00,04:13:19 +26,M,01:49:09,04:13:19 +24,W,01:49:13,04:13:19 +44,M,01:53:03,04:13:19 +40,W,01:53:16,04:13:20 +32,M,01:50:54,04:13:20 +46,M,01:47:14,04:13:20 +39,M,01:55:08,04:13:20 +64,M,01:54:28,04:13:20 +37,M,01:48:57,04:13:20 +45,M,01:59:21,04:13:20 +57,M,01:52:10,04:13:20 +64,M,01:51:09,04:13:21 +33,M,01:48:03,04:13:21 +60,W,01:52:30,04:13:21 +50,M,01:55:41,04:13:22 +28,W,01:57:44,04:13:22 +30,M,01:45:42,04:13:22 +51,M,01:57:13,04:13:22 +27,W,01:50:37,04:13:23 +44,M,01:54:35,04:13:23 +40,M,01:53:32,04:13:23 +46,M,01:51:34,04:13:24 +36,M,01:50:18,04:13:24 +61,M,01:55:34,04:13:25 +53,M,01:58:56,04:13:25 +42,M,01:48:09,04:13:25 +39,M,01:56:43,04:13:25 +29,M,01:51:54,04:13:26 +45,M,01:54:44,04:13:26 +36,M,01:42:40,04:13:26 +37,M,01:53:18,04:13:26 +38,M,01:54:29,04:13:27 +30,M,01:54:27,04:13:27 +31,M,01:48:17,04:13:27 +45,M,01:51:35,04:13:28 +45,M,01:48:41,04:13:28 +27,M,01:57:58,04:13:28 +33,W,01:58:19,04:13:28 +35,W,01:51:10,04:13:28 +32,M,01:54:18,04:13:28 +42,M,02:01:25,04:13:29 +39,M,01:56:38,04:13:29 +19,M,01:57:19,04:13:29 +50,M,01:50:25,04:13:29 +51,M,02:04:19,04:13:29 +30,M,01:47:33,04:13:29 +40,M,01:57:23,04:13:30 +22,W,01:53:10,04:13:31 +57,M,02:00:57,04:13:31 +33,M,02:00:52,04:13:31 +36,M,01:56:15,04:13:31 +30,W,01:50:59,04:13:32 +40,M,01:46:19,04:13:32 +49,W,01:57:03,04:13:32 +36,M,01:48:33,04:13:33 +35,M,01:41:57,04:13:33 +41,M,01:46:11,04:13:33 +47,M,01:51:42,04:13:33 +35,M,01:46:13,04:13:33 +32,M,01:42:48,04:13:34 +53,M,01:54:48,04:13:34 +32,M,01:41:00,04:13:35 +30,M,01:53:04,04:13:35 +39,M,01:51:47,04:13:35 +44,W,01:50:56,04:13:35 +25,W,01:54:42,04:13:35 +28,W,01:51:46,04:13:36 +37,M,01:36:41,04:13:36 +40,M,01:49:46,04:13:36 +37,M,01:56:14,04:13:36 +56,M,01:53:12,04:13:36 +47,M,01:52:52,04:13:36 +41,M,01:52:28,04:13:37 +55,M,01:53:22,04:13:37 +61,M,01:54:17,04:13:37 +40,M,01:47:04,04:13:37 +32,M,01:55:35,04:13:38 +40,M,01:52:18,04:13:39 +54,M,02:01:18,04:13:39 +71,M,01:54:49,04:13:39 +47,M,01:53:50,04:13:39 +41,W,01:53:56,04:13:40 +30,M,01:52:22,04:13:40 +49,M,01:54:53,04:13:40 +48,M,01:50:07,04:13:40 +49,M,01:48:09,04:13:40 +37,M,01:51:05,04:13:40 +41,W,01:51:29,04:13:40 +52,W,01:52:14,04:13:40 +54,M,01:53:44,04:13:41 +44,W,01:54:23,04:13:41 +39,M,01:52:30,04:13:41 +40,M,01:46:17,04:13:41 +41,M,01:58:07,04:13:41 +41,W,01:54:41,04:13:41 +34,M,01:47:19,04:13:41 +27,M,01:58:09,04:13:42 +41,W,02:05:35,04:13:42 +49,M,01:49:40,04:13:43 +40,M,01:48:20,04:13:43 +33,M,01:52:42,04:13:43 +48,M,01:56:49,04:13:43 +43,W,01:54:36,04:13:44 +48,M,01:57:04,04:13:44 +29,W,02:01:32,04:13:44 +58,M,01:51:02,04:13:45 +49,M,01:53:19,04:13:45 +44,M,01:56:46,04:13:45 +43,M,01:53:35,04:13:46 +25,W,01:43:08,04:13:46 +43,M,01:49:31,04:13:47 +45,M,01:44:06,04:13:47 +31,W,01:50:06,04:13:47 +30,M,01:46:22,04:13:48 +28,M,01:51:59,04:13:49 +48,M,01:43:34,04:13:49 +39,M,01:54:27,04:13:49 +60,M,01:50:47,04:13:49 +33,M,01:54:50,04:13:50 +41,M,01:49:12,04:13:50 +34,M,01:51:39,04:13:50 +40,W,01:52:04,04:13:50 +49,M,01:57:43,04:13:51 +43,M,01:55:07,04:13:51 +34,M,01:52:54,04:13:51 +52,M,01:51:37,04:13:51 +52,M,02:07:06,04:13:51 +38,W,01:51:37,04:13:51 +55,M,01:54:09,04:13:52 +61,M,01:54:58,04:13:52 +26,W,01:48:25,04:13:52 +49,M,01:46:43,04:13:52 +50,W,01:57:56,04:13:52 +26,M,01:40:28,04:13:52 +47,M,01:52:44,04:13:52 +28,M,01:59:40,04:13:53 +44,W,01:55:42,04:13:53 +57,W,01:50:14,04:13:54 +42,M,01:56:04,04:13:54 +33,M,01:46:33,04:13:54 +46,M,01:47:13,04:13:54 +43,M,01:57:02,04:13:55 +31,M,01:53:03,04:13:55 +32,M,01:46:04,04:13:55 +43,M,01:59:43,04:13:55 +49,M,01:53:17,04:13:55 +47,W,01:56:24,04:13:56 +39,M,01:52:28,04:13:56 +28,W,01:56:39,04:13:56 +47,M,01:53:43,04:13:56 +40,M,01:52:12,04:13:56 +50,W,01:51:02,04:13:56 +63,M,01:49:21,04:13:56 +44,W,01:54:42,04:13:57 +35,W,01:59:46,04:13:57 +40,M,01:46:24,04:13:57 +31,M,01:51:13,04:13:57 +41,M,01:53:14,04:13:57 +31,M,01:58:20,04:13:58 +46,M,01:49:52,04:13:59 +36,M,01:55:51,04:13:59 +32,W,01:55:27,04:13:59 +29,M,01:40:57,04:14:00 +41,W,01:56:01,04:14:00 +53,W,01:49:39,04:14:00 +35,M,01:44:04,04:14:01 +26,W,01:47:35,04:14:01 +35,M,01:58:03,04:14:01 +35,M,01:56:18,04:14:01 +32,W,01:52:08,04:14:01 +40,M,02:02:01,04:14:01 +38,M,01:55:52,04:14:01 +46,M,01:54:17,04:14:04 +40,M,01:55:12,04:14:04 +55,M,01:54:02,04:14:04 +27,M,01:51:01,04:14:04 +41,W,01:42:35,04:14:04 +38,M,01:39:14,04:14:05 +29,W,01:55:05,04:14:05 +27,W,01:56:28,04:14:05 +56,M,01:50:27,04:14:05 +30,W,01:52:57,04:14:06 +29,M,01:44:20,04:14:06 +54,M,01:50:48,04:14:06 +41,M,01:53:39,04:14:06 +42,M,01:47:59,04:14:06 +41,M,01:44:03,04:14:07 +21,M,01:40:09,04:14:07 +46,M,02:03:07,04:14:07 +44,M,01:57:35,04:14:07 +32,M,01:54:43,04:14:07 +36,M,01:52:28,04:14:07 +48,M,01:54:36,04:14:07 +46,M,01:46:51,04:14:07 +46,M,01:59:57,04:14:08 +48,W,01:50:05,04:14:08 +38,M,01:44:49,04:14:08 +33,M,01:58:19,04:14:08 +39,M,01:51:51,04:14:08 +23,W,01:53:24,04:14:09 +37,M,01:46:13,04:14:09 +43,M,01:51:17,04:14:09 +44,M,01:52:53,04:14:09 +51,M,01:57:19,04:14:10 +35,W,01:47:26,04:14:10 +47,W,01:57:39,04:14:10 +30,W,01:50:48,04:14:11 +22,M,01:48:36,04:14:11 +28,W,01:54:30,04:14:11 +27,M,01:47:28,04:14:11 +32,W,01:56:26,04:14:11 +47,M,01:50:08,04:14:11 +26,W,01:50:46,04:14:12 +47,M,01:43:28,04:14:12 +48,W,01:55:30,04:14:12 +46,M,01:55:37,04:14:13 +46,M,01:55:45,04:14:13 +51,M,01:54:08,04:14:13 +36,W,01:49:11,04:14:14 +41,M,01:46:49,04:14:14 +43,W,01:48:41,04:14:14 +22,W,01:44:53,04:14:14 +24,M,01:45:29,04:14:15 +43,M,02:02:50,04:14:16 +48,W,01:59:43,04:14:16 +51,M,01:53:44,04:14:16 +29,M,01:54:20,04:14:16 +50,M,01:53:03,04:14:17 +50,W,01:54:45,04:14:18 +45,M,01:58:21,04:14:18 +36,M,01:53:03,04:14:18 +39,M,01:48:01,04:14:18 +50,M,01:45:17,04:14:18 +34,M,01:55:03,04:14:18 +39,M,01:53:43,04:14:18 +50,M,01:57:41,04:14:18 +48,M,01:48:24,04:14:19 +28,W,01:54:51,04:14:19 +47,W,01:59:29,04:14:19 +35,M,01:53:53,04:14:19 +57,M,01:57:34,04:14:19 +34,M,01:55:46,04:14:20 +44,M,01:40:45,04:14:20 +41,M,01:52:45,04:14:20 +48,M,01:49:45,04:14:20 +41,W,01:57:13,04:14:20 +33,W,01:51:04,04:14:20 +47,M,01:49:13,04:14:20 +32,W,01:50:46,04:14:20 +30,W,01:45:19,04:14:21 +46,W,01:57:31,04:14:22 +34,M,01:50:42,04:14:22 +48,W,01:56:16,04:14:22 +51,M,01:50:17,04:14:22 +41,M,01:54:42,04:14:23 +27,W,01:54:52,04:14:23 +25,W,01:55:37,04:14:23 +45,M,01:47:55,04:14:23 +48,M,01:54:07,04:14:23 +36,M,01:43:20,04:14:24 +46,W,01:56:03,04:14:24 +40,M,01:57:13,04:14:24 +29,W,01:53:34,04:14:24 +34,M,02:00:13,04:14:25 +40,M,01:52:56,04:14:25 +48,M,01:53:22,04:14:25 +46,M,01:54:43,04:14:25 +41,M,01:45:55,04:14:26 +40,W,01:55:46,04:14:28 +40,M,01:51:07,04:14:28 +45,M,01:46:55,04:14:28 +33,W,01:54:21,04:14:28 +36,M,01:34:14,04:14:29 +46,W,01:53:31,04:14:29 +44,W,01:51:38,04:14:29 +43,M,01:45:51,04:14:29 +40,M,01:53:44,04:14:29 +32,M,01:54:37,04:14:29 +44,M,01:52:10,04:14:29 +20,M,01:52:48,04:14:29 +28,W,01:56:51,04:14:30 +32,W,01:50:54,04:14:30 +43,M,01:55:01,04:14:30 +43,M,01:56:16,04:14:31 +28,W,01:53:53,04:14:31 +41,M,01:52:42,04:14:31 +47,M,01:48:39,04:14:32 +40,W,01:55:15,04:14:32 +28,M,01:56:17,04:14:33 +37,M,01:53:27,04:14:33 +53,M,01:49:13,04:14:33 +25,W,01:58:43,04:14:33 +49,M,01:52:53,04:14:33 +52,M,01:46:36,04:14:33 +48,M,01:57:03,04:14:34 +37,W,01:52:04,04:14:34 +36,M,01:55:20,04:14:35 +34,M,01:48:17,04:14:35 +27,M,01:50:19,04:14:35 +42,M,01:50:57,04:14:35 +32,M,01:51:06,04:14:35 +43,M,01:57:07,04:14:35 +27,W,01:54:55,04:14:35 +42,M,01:58:31,04:14:35 +52,M,01:58:13,04:14:36 +20,W,01:46:50,04:14:36 +42,M,01:44:28,04:14:36 +35,W,02:00:14,04:14:36 +43,M,01:52:15,04:14:37 +33,M,01:51:26,04:14:37 +64,W,01:52:03,04:14:38 +33,M,01:57:29,04:14:38 +36,M,01:53:23,04:14:38 +38,W,01:51:17,04:14:39 +48,W,01:55:15,04:14:39 +39,M,01:47:25,04:14:39 +34,W,01:42:25,04:14:40 +29,W,01:55:08,04:14:40 +41,M,01:56:16,04:14:42 +47,M,01:43:54,04:14:43 +43,M,01:56:04,04:14:43 +50,M,01:52:38,04:14:44 +36,W,01:52:16,04:14:44 +31,W,01:49:51,04:14:44 +25,W,01:56:32,04:14:44 +41,M,01:43:16,04:14:44 +40,M,01:54:31,04:14:45 +35,W,01:59:30,04:14:45 +42,M,01:49:03,04:14:45 +54,W,01:51:24,04:14:46 +35,M,01:53:50,04:14:46 +42,M,01:47:29,04:14:46 +47,W,01:56:34,04:14:46 +35,M,01:52:59,04:14:47 +40,M,01:55:40,04:14:47 +36,M,01:44:15,04:14:47 +30,W,01:59:37,04:14:47 +31,M,01:56:54,04:14:47 +33,M,01:58:01,04:14:47 +30,M,02:00:00,04:14:47 +30,W,01:58:30,04:14:47 +40,M,01:51:16,04:14:48 +43,W,01:58:31,04:14:48 +35,W,01:50:53,04:14:48 +37,W,02:01:38,04:14:48 +24,M,01:40:10,04:14:48 +38,M,01:56:24,04:14:49 +44,M,01:49:08,04:14:49 +46,W,01:51:25,04:14:49 +30,M,01:47:27,04:14:49 +38,M,01:47:18,04:14:49 +49,M,01:56:43,04:14:49 +34,M,01:44:08,04:14:50 +26,W,01:47:49,04:14:50 +44,M,01:56:42,04:14:50 +30,W,01:51:13,04:14:51 +42,M,01:53:11,04:14:51 +30,M,01:48:17,04:14:51 +36,W,01:53:44,04:14:51 +36,M,01:59:29,04:14:51 +39,M,01:52:53,04:14:51 +57,M,01:45:58,04:14:52 +38,M,01:53:18,04:14:52 +29,W,01:57:48,04:14:53 +29,M,01:42:25,04:14:53 +48,M,01:52:01,04:14:53 +33,M,01:50:37,04:14:53 +39,M,01:53:29,04:14:53 +31,M,01:46:53,04:14:53 +48,W,01:54:17,04:14:54 +48,M,01:42:37,04:14:54 +60,M,01:43:10,04:14:54 +31,M,01:53:30,04:14:54 +43,M,01:46:14,04:14:54 +35,M,01:55:32,04:14:55 +36,M,01:56:37,04:14:55 +30,W,01:50:21,04:14:55 +45,M,01:51:17,04:14:55 +34,M,01:48:11,04:14:55 +44,M,01:51:13,04:14:56 +52,M,01:56:01,04:14:56 +41,W,01:51:15,04:14:56 +35,M,01:51:49,04:14:56 +45,M,01:55:57,04:14:57 +31,W,01:57:02,04:14:57 +40,M,01:56:52,04:14:57 +26,W,01:47:16,04:14:57 +42,M,01:56:44,04:14:57 +37,M,01:59:57,04:14:58 +21,W,01:51:54,04:14:58 +49,M,01:45:20,04:14:58 +28,W,02:04:09,04:14:58 +49,M,01:53:52,04:14:58 +25,W,01:54:43,04:14:58 +34,M,01:47:32,04:14:58 +50,M,01:52:31,04:14:58 +30,M,01:54:08,04:14:59 +55,M,02:00:30,04:14:59 +45,M,01:41:21,04:14:59 +49,M,01:53:35,04:14:59 +39,M,01:55:36,04:14:59 +48,M,01:55:40,04:14:59 +25,M,01:44:44,04:14:59 +45,M,01:51:29,04:15:00 +28,W,01:57:24,04:15:00 +30,M,01:52:30,04:15:00 +43,M,01:54:40,04:15:00 +47,W,01:56:03,04:15:01 +50,W,02:01:40,04:15:01 +30,M,01:58:53,04:15:01 +31,M,01:55:44,04:15:01 +32,M,01:56:51,04:15:02 +40,M,01:49:23,04:15:02 +43,M,01:52:32,04:15:02 +26,W,01:53:58,04:15:03 +40,M,01:52:32,04:15:03 +32,W,01:55:07,04:15:03 +28,W,01:56:15,04:15:03 +45,M,01:56:40,04:15:03 +41,W,01:45:02,04:15:04 +44,M,01:54:01,04:15:04 +47,M,01:49:46,04:15:04 +38,M,01:56:53,04:15:04 +44,W,01:54:53,04:15:04 +41,W,01:53:48,04:15:05 +26,M,01:52:27,04:15:05 +53,M,01:50:16,04:15:05 +36,M,01:55:58,04:15:05 +49,M,01:52:32,04:15:05 +42,M,01:52:55,04:15:05 +34,W,01:57:57,04:15:06 +50,M,01:56:57,04:15:06 +32,W,01:52:53,04:15:06 +42,M,01:55:44,04:15:06 +22,W,01:52:46,04:15:06 +36,W,02:01:50,04:15:06 +45,M,01:51:44,04:15:06 +60,M,01:58:46,04:15:06 +28,W,01:49:35,04:15:06 +56,W,01:55:41,04:15:07 +36,W,01:52:10,04:15:07 +39,M,01:56:53,04:15:07 +40,W,01:53:53,04:15:07 +51,M,01:52:02,04:15:07 +28,M,01:45:42,04:15:08 +42,W,01:56:03,04:15:08 +45,W,01:55:17,04:15:08 +45,W,01:55:53,04:15:08 +32,M,01:57:25,04:15:08 +32,M,01:50:14,04:15:08 +54,M,01:55:14,04:15:09 +32,W,02:05:27,04:15:09 +43,M,01:45:58,04:15:09 +55,M,02:00:40,04:15:09 +37,M,01:58:31,04:15:10 +40,W,01:57:54,04:15:10 +35,M,01:59:42,04:15:11 +30,M,01:45:58,04:15:11 +29,M,01:58:27,04:15:11 +43,W,01:56:43,04:15:11 +27,M,01:43:34,04:15:11 +43,M,01:56:02,04:15:12 +35,W,01:56:31,04:15:13 +46,W,01:53:48,04:15:13 +42,M,01:47:50,04:15:13 +43,M,01:45:55,04:15:13 +28,M,01:47:49,04:15:13 +52,M,01:48:00,04:15:13 +47,W,01:59:11,04:15:14 +38,M,01:57:54,04:15:14 +31,M,01:39:39,04:15:14 +39,M,01:46:31,04:15:15 +42,M,01:56:03,04:15:15 +28,M,01:51:36,04:15:15 +31,W,02:05:35,04:15:15 +35,M,01:51:07,04:15:15 +49,M,01:56:00,04:15:16 +28,M,01:44:44,04:15:16 +31,W,01:29:08,04:15:17 +54,M,01:50:57,04:15:17 +62,M,01:53:06,04:15:17 +48,M,01:40:35,04:15:18 +51,M,01:55:43,04:15:19 +49,M,01:53:25,04:15:19 +27,W,01:54:58,04:15:20 +41,W,01:59:11,04:15:20 +33,M,01:44:20,04:15:20 +37,W,01:55:14,04:15:20 +30,M,01:53:49,04:15:20 +35,M,01:48:58,04:15:20 +40,M,01:46:29,04:15:21 +51,M,01:51:10,04:15:21 +26,M,01:57:55,04:15:21 +49,M,02:02:02,04:15:21 +39,W,01:50:38,04:15:22 +22,M,01:53:27,04:15:22 +54,M,01:57:55,04:15:23 +54,M,01:53:01,04:15:23 +39,W,01:56:26,04:15:23 +30,M,01:47:22,04:15:23 +18,M,01:55:02,04:15:24 +36,M,01:53:31,04:15:24 +46,W,01:50:41,04:15:25 +43,M,01:48:47,04:15:25 +48,W,01:45:06,04:15:26 +30,M,01:52:57,04:15:26 +46,M,01:49:46,04:15:26 +38,W,01:49:36,04:15:26 +34,W,01:53:47,04:15:27 +49,M,01:45:38,04:15:27 +25,M,01:56:59,04:15:27 +43,M,01:54:35,04:15:28 +37,M,01:43:52,04:15:28 +58,M,01:42:21,04:15:28 +51,M,01:50:00,04:15:28 +33,W,01:56:12,04:15:29 +36,W,01:49:35,04:15:29 +35,M,01:46:38,04:15:29 +40,M,02:00:17,04:15:29 +33,M,01:50:23,04:15:29 +25,M,01:47:53,04:15:30 +41,M,02:02:15,04:15:30 +47,W,01:58:43,04:15:30 +36,M,01:55:54,04:15:30 +40,W,01:56:54,04:15:30 +27,W,01:52:56,04:15:30 +42,M,01:53:47,04:15:31 +37,W,01:54:17,04:15:31 +43,W,02:00:17,04:15:31 +51,M,01:57:44,04:15:31 +38,M,01:54:04,04:15:31 +31,M,01:47:07,04:15:32 +49,W,01:53:40,04:15:33 +37,W,01:46:58,04:15:33 +38,M,01:57:20,04:15:33 +47,W,01:51:46,04:15:33 +28,M,01:46:41,04:15:33 +23,M,01:51:59,04:15:33 +46,M,01:42:57,04:15:33 +46,M,01:50:12,04:15:33 +39,M,01:58:34,04:15:34 +38,W,02:06:17,04:15:34 +46,W,01:51:51,04:15:34 +33,W,01:54:19,04:15:34 +60,M,01:55:15,04:15:34 +45,M,01:49:43,04:15:35 +52,M,01:53:39,04:15:35 +24,W,01:55:02,04:15:36 +44,M,01:56:57,04:15:36 +51,M,01:51:15,04:15:36 +51,M,01:50:33,04:15:36 +25,M,01:44:41,04:15:36 +34,M,01:54:23,04:15:36 +33,M,01:51:01,04:15:37 +30,M,01:49:34,04:15:37 +35,M,01:52:52,04:15:37 +49,M,01:57:16,04:15:37 +26,W,01:53:25,04:15:37 +52,M,01:48:21,04:15:37 +31,M,01:52:29,04:15:37 +43,M,01:50:42,04:15:37 +49,M,01:56:41,04:15:37 +42,M,02:03:09,04:15:38 +42,M,01:54:22,04:15:38 +38,M,01:57:49,04:15:38 +41,M,01:52:48,04:15:38 +31,M,01:49:34,04:15:39 +45,M,01:57:15,04:15:39 +41,M,01:47:22,04:15:39 +42,M,01:55:53,04:15:39 +34,W,01:55:46,04:15:40 +44,W,01:50:04,04:15:40 +58,W,01:53:05,04:15:40 +44,M,01:49:50,04:15:40 +41,M,01:56:32,04:15:40 +34,M,01:49:57,04:15:41 +51,M,01:56:19,04:15:41 +47,M,01:46:18,04:15:41 +36,M,01:53:20,04:15:42 +37,W,01:55:48,04:15:42 +25,M,01:50:55,04:15:42 +27,W,01:50:51,04:15:42 +43,W,01:57:42,04:15:42 +36,M,01:53:09,04:15:42 +41,W,01:58:34,04:15:43 +52,M,01:55:10,04:15:43 +39,M,01:52:15,04:15:43 +37,M,01:50:35,04:15:44 +24,W,01:50:56,04:15:44 +31,M,01:52:56,04:15:45 +41,M,02:01:03,04:15:45 +45,M,01:51:48,04:15:45 +33,M,01:44:54,04:15:45 +46,M,01:50:21,04:15:46 +48,M,01:46:56,04:15:46 +31,M,01:55:12,04:15:47 +33,M,01:43:51,04:15:47 +27,M,01:55:04,04:15:48 +45,M,02:00:42,04:15:48 +52,W,01:53:31,04:15:48 +31,M,01:57:46,04:15:48 +29,W,01:57:56,04:15:48 +44,M,01:57:39,04:15:48 +44,M,01:56:18,04:15:48 +40,M,01:47:14,04:15:49 +30,W,01:56:22,04:15:49 +25,M,01:53:11,04:15:50 +33,M,01:49:17,04:15:50 +30,W,01:49:46,04:15:51 +49,M,01:54:28,04:15:51 +26,M,01:46:45,04:15:51 +43,W,01:51:26,04:15:51 +34,M,01:49:25,04:15:52 +28,M,01:48:01,04:15:52 +45,W,01:53:13,04:15:53 +50,M,01:56:35,04:15:53 +31,M,01:45:09,04:15:53 +48,M,01:55:22,04:15:53 +44,M,01:50:49,04:15:53 +30,W,01:53:03,04:15:53 +47,M,01:45:17,04:15:53 +23,M,01:50:47,04:15:53 +41,W,01:52:41,04:15:53 +33,W,01:55:59,04:15:53 +26,M,01:51:35,04:15:54 +35,M,01:54:48,04:15:54 +42,M,01:48:53,04:15:54 +30,M,01:52:03,04:15:54 +27,W,01:53:12,04:15:55 +32,W,01:57:02,04:15:55 +23,W,01:59:54,04:15:55 +28,M,01:58:34,04:15:55 +45,M,01:54:09,04:15:55 +26,M,01:47:46,04:15:55 +58,M,01:54:02,04:15:55 +30,W,01:46:06,04:15:55 +54,M,01:57:21,04:15:55 +32,M,01:45:05,04:15:56 +44,M,01:55:11,04:15:56 +36,M,01:43:32,04:15:56 +34,M,01:49:10,04:15:56 +25,M,01:50:32,04:15:57 +45,M,01:46:17,04:15:57 +25,M,01:44:45,04:15:57 +30,M,01:43:51,04:15:58 +33,M,01:44:27,04:15:58 +33,W,01:56:09,04:15:58 +39,M,01:48:10,04:15:58 +39,M,01:51:01,04:15:58 +52,M,01:50:22,04:15:59 +37,W,01:58:50,04:15:59 +46,M,01:53:26,04:15:59 +34,M,01:52:01,04:15:59 +38,W,01:56:08,04:15:59 +26,M,01:51:11,04:16:00 +34,M,01:56:29,04:16:00 +42,W,01:51:49,04:16:00 +41,M,01:48:34,04:16:00 +61,M,01:51:50,04:16:00 +26,W,01:54:06,04:16:00 +32,M,01:55:49,04:16:00 +31,W,01:53:24,04:16:01 +65,M,01:57:29,04:16:01 +35,M,01:53:04,04:16:01 +48,W,01:56:48,04:16:01 +56,W,01:51:51,04:16:02 +32,M,01:47:15,04:16:02 +26,M,01:52:37,04:16:02 +35,M,01:44:03,04:16:02 +38,W,01:51:03,04:16:02 +45,M,01:51:31,04:16:02 +31,M,01:46:03,04:16:03 +32,M,01:55:17,04:16:03 +60,M,01:44:21,04:16:03 +44,M,01:47:57,04:16:03 +29,M,01:49:57,04:16:04 +55,M,01:55:50,04:16:04 +49,M,01:52:51,04:16:04 +52,W,01:52:37,04:16:04 +47,M,01:56:48,04:16:04 +46,M,01:49:07,04:16:04 +36,M,01:56:13,04:16:05 +45,M,01:50:31,04:16:05 +36,M,01:54:30,04:16:05 +29,W,01:52:00,04:16:05 +37,M,01:56:20,04:16:05 +42,W,01:58:53,04:16:05 +20,M,02:01:35,04:16:06 +46,W,01:54:08,04:16:06 +44,M,01:51:48,04:16:06 +57,M,01:54:40,04:16:07 +47,M,01:57:34,04:16:08 +51,W,02:00:39,04:16:08 +34,M,01:53:33,04:16:08 +36,W,01:58:21,04:16:08 +38,M,01:51:43,04:16:08 +54,M,01:59:25,04:16:08 +47,W,01:51:56,04:16:09 +40,M,01:43:13,04:16:09 +42,M,01:56:36,04:16:10 +31,M,01:52:37,04:16:10 +40,W,01:52:09,04:16:12 +53,M,01:59:52,04:16:12 +56,M,01:40:21,04:16:12 +46,W,01:55:06,04:16:13 +38,M,01:52:08,04:16:13 +42,M,01:48:58,04:16:13 +42,W,01:52:23,04:16:13 +38,M,01:45:47,04:16:13 +30,W,01:53:58,04:16:13 +25,M,01:53:38,04:16:13 +36,M,01:57:08,04:16:14 +55,M,01:58:26,04:16:14 +27,W,01:49:20,04:16:14 +42,M,01:50:16,04:16:15 +48,M,01:55:10,04:16:15 +45,M,01:47:18,04:16:15 +23,W,01:49:29,04:16:16 +38,M,01:55:55,04:16:16 +28,M,01:54:48,04:16:17 +40,M,01:44:55,04:16:17 +49,M,01:48:18,04:16:17 +47,W,02:00:24,04:16:17 +34,M,01:55:58,04:16:17 +22,M,01:53:35,04:16:17 +32,W,01:59:17,04:16:17 +44,M,02:00:07,04:16:18 +40,M,01:53:02,04:16:18 +38,M,01:52:11,04:16:18 +39,W,01:53:38,04:16:18 +33,W,01:54:21,04:16:18 +39,M,01:53:46,04:16:19 +50,M,01:47:27,04:16:19 +41,M,01:45:16,04:16:19 +55,M,01:48:11,04:16:19 +40,M,01:56:38,04:16:20 +37,M,01:51:02,04:16:20 +36,M,01:57:30,04:16:20 +61,M,02:00:19,04:16:21 +39,M,01:55:20,04:16:21 +30,W,01:52:10,04:16:21 +47,M,01:53:03,04:16:21 +25,M,01:58:11,04:16:21 +45,M,01:45:43,04:16:22 +47,W,01:58:24,04:16:22 +31,W,01:58:16,04:16:22 +34,M,01:47:11,04:16:22 +34,M,01:58:08,04:16:22 +42,M,01:47:27,04:16:22 +43,W,01:49:52,04:16:22 +32,W,01:50:36,04:16:22 +51,M,02:00:08,04:16:23 +39,W,01:56:17,04:16:23 +26,M,01:55:25,04:16:23 +42,M,01:55:20,04:16:23 +55,M,01:54:14,04:16:23 +55,M,01:51:45,04:16:23 +42,M,01:56:23,04:16:23 +41,M,02:04:30,04:16:23 +22,M,01:59:02,04:16:24 +34,M,01:54:14,04:16:24 +49,M,01:54:40,04:16:24 +60,M,01:52:27,04:16:24 +46,M,01:57:23,04:16:25 +49,W,01:57:15,04:16:25 +46,W,01:56:59,04:16:25 +31,W,01:51:14,04:16:25 +27,W,01:53:55,04:16:26 +34,M,01:55:51,04:16:26 +51,M,01:46:34,04:16:26 +38,W,01:55:28,04:16:27 +32,M,01:46:34,04:16:27 +67,M,01:48:44,04:16:27 +42,M,01:50:35,04:16:27 +26,W,01:55:03,04:16:27 +35,W,01:49:44,04:16:28 +57,M,01:51:06,04:16:28 +29,W,01:59:31,04:16:28 +30,M,01:47:57,04:16:28 +48,M,01:50:15,04:16:28 +25,M,01:58:17,04:16:28 +39,W,01:58:09,04:16:29 +43,M,01:52:37,04:16:29 +45,M,01:49:38,04:16:29 +49,M,01:49:56,04:16:29 +37,M,01:58:16,04:16:29 +44,W,01:49:59,04:16:30 +32,M,01:55:16,04:16:30 +40,M,01:52:55,04:16:30 +59,M,01:44:43,04:16:30 +30,M,01:51:53,04:16:30 +38,M,01:50:08,04:16:30 +31,M,01:48:48,04:16:30 +34,M,01:55:34,04:16:31 +45,M,01:55:17,04:16:31 +46,W,01:54:37,04:16:31 +35,M,01:54:04,04:16:31 +30,M,01:57:49,04:16:31 +27,W,01:57:21,04:16:32 +50,W,01:52:07,04:16:32 +41,M,01:45:11,04:16:32 +44,M,01:44:16,04:16:32 +38,M,01:55:14,04:16:33 +47,M,01:50:50,04:16:33 +47,M,02:03:19,04:16:33 +32,M,01:54:41,04:16:33 +22,M,01:47:03,04:16:33 +29,M,01:47:50,04:16:33 +34,M,01:59:53,04:16:34 +30,M,01:49:34,04:16:34 +40,M,01:59:15,04:16:34 +49,M,01:50:56,04:16:34 +50,M,01:50:49,04:16:34 +31,M,01:54:29,04:16:35 +33,M,01:53:50,04:16:35 +46,M,01:50:40,04:16:35 +44,M,01:48:24,04:16:35 +47,W,01:57:24,04:16:35 +51,M,01:52:00,04:16:35 +49,M,01:53:37,04:16:35 +39,M,01:48:15,04:16:36 +45,M,01:50:25,04:16:36 +25,W,01:51:01,04:16:36 +40,M,01:47:50,04:16:36 +43,W,01:59:07,04:16:36 +26,M,01:52:43,04:16:36 +31,W,01:57:41,04:16:36 +50,M,01:52:47,04:16:37 +35,M,01:45:49,04:16:37 +30,M,01:56:01,04:16:37 +56,W,01:55:07,04:16:37 +48,M,01:51:39,04:16:38 +35,M,01:57:06,04:16:38 +43,M,01:55:03,04:16:38 +34,M,01:46:35,04:16:38 +52,M,01:48:58,04:16:38 +42,M,01:53:38,04:16:38 +34,M,01:58:50,04:16:38 +45,W,01:53:29,04:16:39 +44,W,01:55:20,04:16:40 +60,M,01:53:31,04:16:40 +51,M,01:44:42,04:16:40 +49,W,01:49:11,04:16:40 +39,M,01:54:17,04:16:40 +32,M,01:42:29,04:16:40 +35,M,01:56:31,04:16:40 +48,M,01:55:53,04:16:41 +32,M,01:59:01,04:16:41 +33,M,01:50:32,04:16:41 +42,M,01:53:00,04:16:42 +51,M,01:57:32,04:16:42 +44,W,01:53:37,04:16:42 +34,M,01:50:27,04:16:42 +30,M,01:56:27,04:16:43 +40,M,01:55:48,04:16:43 +37,M,01:56:23,04:16:43 +38,M,01:58:01,04:16:44 +40,M,01:50:58,04:16:44 +31,M,01:58:20,04:16:44 +45,W,01:54:55,04:16:44 +43,M,01:54:41,04:16:44 +24,W,02:00:21,04:16:45 +26,W,01:57:22,04:16:45 +51,M,01:54:16,04:16:45 +32,W,01:54:42,04:16:46 +44,M,02:00:03,04:16:47 +47,M,01:54:56,04:16:47 +26,M,01:46:40,04:16:47 +45,M,01:56:08,04:16:47 +45,M,01:54:23,04:16:47 +41,M,01:56:34,04:16:47 +37,M,01:57:18,04:16:47 +27,M,01:49:31,04:16:47 +52,M,01:54:07,04:16:47 +35,W,01:52:52,04:16:48 +50,M,01:54:23,04:16:48 +36,M,01:48:10,04:16:48 +42,M,01:52:34,04:16:49 +38,M,01:53:26,04:16:49 +43,M,02:00:17,04:16:49 +49,M,02:03:28,04:16:50 +37,W,01:55:49,04:16:50 +43,M,01:41:56,04:16:50 +29,M,01:53:31,04:16:51 +46,M,01:57:32,04:16:51 +38,M,01:48:20,04:16:51 +31,W,01:51:47,04:16:51 +33,M,01:55:38,04:16:52 +44,M,01:55:32,04:16:52 +26,W,01:55:25,04:16:52 +30,W,01:50:41,04:16:52 +25,M,01:56:40,04:16:52 +35,W,01:54:41,04:16:52 +41,M,02:00:18,04:16:54 +26,M,01:58:03,04:16:54 +37,M,01:54:13,04:16:54 +40,W,01:57:42,04:16:54 +27,M,01:58:28,04:16:54 +47,W,01:57:20,04:16:54 +49,W,01:47:09,04:16:54 +43,M,01:57:04,04:16:55 +56,M,01:59:24,04:16:55 +38,M,01:56:31,04:16:55 +39,M,01:57:47,04:16:55 +49,M,02:03:26,04:16:55 +46,M,01:45:21,04:16:55 +33,W,01:53:20,04:16:55 +42,W,01:46:21,04:16:55 +25,M,01:57:55,04:16:55 +47,W,01:54:18,04:16:55 +39,M,01:55:00,04:16:56 +44,M,01:48:43,04:16:56 +30,M,01:45:06,04:16:56 +36,M,02:05:24,04:16:57 +29,M,01:51:19,04:16:57 +48,M,01:55:47,04:16:57 +53,M,01:55:38,04:16:57 +40,M,02:01:44,04:16:58 +50,M,01:51:46,04:16:58 +46,M,01:52:47,04:16:58 +41,M,01:55:53,04:16:58 +51,W,01:48:47,04:16:58 +42,W,02:00:54,04:16:59 +28,W,01:54:52,04:16:59 +57,M,01:48:46,04:16:59 +29,W,01:54:34,04:16:59 +46,M,01:57:58,04:17:00 +33,M,01:54:20,04:17:00 +30,M,01:52:59,04:17:00 +46,M,01:42:59,04:17:00 +32,M,01:57:40,04:17:01 +50,M,01:58:08,04:17:01 +30,M,01:54:55,04:17:01 +44,M,01:58:44,04:17:01 +47,M,01:53:58,04:17:01 +30,M,01:56:14,04:17:03 +34,M,01:48:19,04:17:03 +33,M,01:47:37,04:17:03 +39,M,02:02:42,04:17:03 +40,M,01:54:56,04:17:03 +37,M,01:53:25,04:17:03 +25,W,01:58:38,04:17:04 +36,W,01:58:49,04:17:04 +38,M,01:59:54,04:17:05 +44,M,01:55:04,04:17:05 +36,M,01:47:34,04:17:05 +60,M,01:57:02,04:17:05 +29,M,01:51:44,04:17:06 +43,M,01:53:24,04:17:06 +26,M,01:49:41,04:17:06 +54,M,01:53:19,04:17:06 +39,W,01:59:02,04:17:07 +60,M,01:52:24,04:17:07 +34,M,01:54:19,04:17:07 +60,M,01:45:28,04:17:08 +28,W,01:58:27,04:17:08 +30,M,02:06:06,04:17:08 +35,W,01:50:55,04:17:08 +47,M,01:56:01,04:17:08 +26,W,01:57:55,04:17:08 +57,M,01:58:02,04:17:09 +47,M,01:56:01,04:17:09 +44,M,01:57:25,04:17:09 +43,M,01:52:29,04:17:09 +49,M,01:51:16,04:17:09 +30,W,01:53:56,04:17:10 +34,M,01:51:57,04:17:10 +27,W,01:53:37,04:17:10 +32,M,01:48:29,04:17:10 +64,M,01:55:08,04:17:11 +38,W,01:45:50,04:17:11 +56,M,01:47:17,04:17:11 +32,M,01:42:03,04:17:11 +53,M,01:53:09,04:17:11 +44,M,01:57:42,04:17:11 +33,W,01:50:54,04:17:11 +48,M,01:53:30,04:17:11 +23,W,01:55:25,04:17:11 +58,M,01:50:26,04:17:12 +52,M,01:46:48,04:17:12 +38,W,01:52:57,04:17:12 +39,M,01:51:48,04:17:12 +46,M,01:56:37,04:17:13 +37,W,01:52:33,04:17:13 +42,M,01:54:08,04:17:13 +28,M,01:55:11,04:17:13 +45,M,02:04:52,04:17:13 +44,M,01:47:08,04:17:14 +25,W,01:52:14,04:17:14 +27,M,01:43:57,04:17:15 +29,W,01:58:01,04:17:15 +38,M,01:54:14,04:17:15 +30,M,01:56:00,04:17:15 +50,W,01:58:17,04:17:15 +42,M,01:46:28,04:17:16 +35,W,01:46:05,04:17:17 +38,M,01:49:47,04:17:17 +45,M,02:00:11,04:17:17 +48,M,01:55:56,04:17:17 +29,M,01:58:40,04:17:17 +41,M,01:56:35,04:17:17 +32,M,01:46:09,04:17:17 +61,M,02:01:56,04:17:18 +42,W,01:58:25,04:17:18 +44,M,02:02:53,04:17:18 +40,M,01:53:51,04:17:18 +50,M,01:55:01,04:17:19 +28,W,01:55:14,04:17:19 +35,M,01:43:49,04:17:19 +43,W,01:54:30,04:17:19 +39,M,01:56:27,04:17:20 +51,W,01:52:38,04:17:20 +41,M,01:54:42,04:17:21 +33,M,01:54:00,04:17:21 +53,M,01:55:04,04:17:21 +36,M,01:53:40,04:17:21 +36,M,01:57:19,04:17:21 +22,M,01:40:51,04:17:22 +48,M,01:50:13,04:17:22 +51,M,02:00:31,04:17:22 +59,M,01:54:24,04:17:22 +34,M,01:46:20,04:17:22 +51,M,01:54:01,04:17:22 +41,M,01:57:44,04:17:23 +61,M,01:52:42,04:17:23 +42,M,01:58:13,04:17:24 +27,M,01:49:34,04:17:24 +58,M,01:58:36,04:17:24 +24,M,01:41:22,04:17:25 +32,W,01:53:29,04:17:25 +43,W,01:58:26,04:17:25 +39,M,01:55:21,04:17:25 +35,W,01:55:24,04:17:25 +43,M,01:55:14,04:17:25 +35,M,01:56:07,04:17:26 +45,M,01:54:45,04:17:26 +48,M,01:57:11,04:17:26 +24,M,01:53:35,04:17:26 +44,M,01:54:47,04:17:26 +32,M,01:56:42,04:17:27 +47,M,01:55:27,04:17:27 +52,M,01:50:26,04:17:27 +51,M,01:53:31,04:17:27 +31,W,01:53:42,04:17:27 +55,M,01:53:10,04:17:27 +26,W,01:46:05,04:17:28 +37,M,01:53:05,04:17:28 +50,W,01:55:16,04:17:29 +41,M,01:52:27,04:17:29 +37,M,01:59:10,04:17:29 +61,M,01:50:32,04:17:29 +35,M,01:56:21,04:17:30 +39,W,01:45:25,04:17:31 +29,W,01:53:13,04:17:31 +61,M,01:57:45,04:17:32 +44,W,01:46:29,04:17:32 +45,M,01:56:24,04:17:33 +32,M,01:47:11,04:17:33 +39,M,01:56:09,04:17:33 +30,M,02:04:59,04:17:34 +34,M,01:50:16,04:17:34 +24,W,01:50:23,04:17:34 +28,W,01:55:17,04:17:34 +48,M,01:15:13,04:17:35 +51,W,01:57:49,04:17:35 +32,M,01:48:35,04:17:35 +45,W,01:55:57,04:17:35 +43,M,01:55:45,04:17:35 +38,W,01:51:05,04:17:36 +27,M,01:52:43,04:17:36 +40,W,01:55:36,04:17:36 +42,W,01:56:38,04:17:37 +48,M,01:57:26,04:17:37 +48,W,01:56:06,04:17:37 +43,W,01:55:27,04:17:37 +50,M,01:49:25,04:17:37 +48,M,01:58:45,04:17:38 +45,M,01:45:20,04:17:38 +39,W,01:54:00,04:17:38 +34,M,01:55:06,04:17:38 +41,M,01:56:17,04:17:39 +49,M,01:52:52,04:17:39 +50,M,01:51:44,04:17:39 +51,M,01:53:11,04:17:40 +37,W,01:52:50,04:17:40 +37,M,01:53:15,04:17:40 +63,M,01:53:38,04:17:41 +51,M,01:54:04,04:17:41 +43,M,01:57:20,04:17:42 +45,M,01:52:44,04:17:42 +27,W,01:49:05,04:17:42 +34,W,01:53:52,04:17:42 +30,M,01:50:55,04:17:43 +43,M,01:46:54,04:17:43 +28,M,01:56:22,04:17:44 +36,M,01:56:03,04:17:44 +39,W,01:55:43,04:17:45 +41,M,01:44:00,04:17:45 +48,M,01:58:55,04:17:45 +41,M,01:42:52,04:17:45 +36,M,01:44:53,04:17:45 +33,M,01:53:03,04:17:45 +45,M,01:42:25,04:17:45 +35,W,01:52:45,04:17:46 +46,M,01:50:41,04:17:46 +42,W,01:51:16,04:17:46 +48,M,01:54:06,04:17:47 +45,W,01:54:12,04:17:48 +33,M,01:55:55,04:17:49 +49,M,01:58:41,04:17:50 +27,W,01:52:41,04:17:50 +34,M,01:52:29,04:17:51 +33,M,01:58:34,04:17:51 +36,M,01:55:45,04:17:52 +39,M,01:48:21,04:17:52 +42,M,01:51:30,04:17:52 +41,W,01:59:17,04:17:53 +35,M,01:59:44,04:17:54 +36,M,01:54:31,04:17:55 +44,M,01:56:04,04:17:55 +25,W,01:56:49,04:17:55 +39,W,01:51:13,04:17:55 +29,M,01:48:57,04:17:55 +52,M,01:49:50,04:17:56 +41,M,01:43:32,04:17:56 +27,W,01:54:53,04:17:56 +57,M,01:59:18,04:17:56 +52,M,01:58:18,04:17:57 +56,M,01:58:36,04:17:57 +24,M,01:56:12,04:17:57 +29,W,01:54:57,04:17:57 +33,W,01:58:44,04:17:58 +40,W,01:50:16,04:17:58 +49,M,01:53:09,04:17:58 +27,M,01:40:41,04:17:58 +43,M,01:50:33,04:17:59 +29,W,01:58:58,04:17:59 +38,M,01:50:39,04:17:59 +26,W,01:55:00,04:17:59 +34,M,01:51:32,04:17:59 +36,W,01:54:33,04:18:00 +40,M,01:54:51,04:18:00 +51,W,01:56:10,04:18:00 +41,M,01:52:25,04:18:00 +48,M,01:59:29,04:18:00 +46,M,01:58:57,04:18:00 +58,M,01:52:55,04:18:00 +45,W,01:51:44,04:18:00 +48,W,02:00:43,04:18:01 +29,M,01:58:16,04:18:01 +29,M,02:01:43,04:18:01 +41,M,01:48:18,04:18:01 +51,M,01:51:24,04:18:01 +40,M,01:52:01,04:18:01 +51,M,01:55:56,04:18:02 +55,M,01:58:23,04:18:02 +50,W,02:03:41,04:18:02 +25,W,02:02:14,04:18:02 +51,M,01:56:01,04:18:03 +54,M,01:46:24,04:18:03 +25,M,01:50:00,04:18:03 +39,M,01:51:12,04:18:03 +32,M,01:45:17,04:18:03 +34,M,01:58:05,04:18:03 +54,M,01:58:04,04:18:03 +39,M,01:57:59,04:18:04 +30,W,01:59:50,04:18:04 +24,W,01:53:06,04:18:05 +49,M,01:55:46,04:18:05 +38,M,01:50:55,04:18:05 +42,M,01:53:31,04:18:05 +32,M,01:56:17,04:18:05 +28,W,01:49:59,04:18:05 +43,M,02:04:36,04:18:06 +58,W,01:56:01,04:18:06 +59,M,01:50:26,04:18:07 +32,W,02:01:59,04:18:07 +33,M,01:52:42,04:18:07 +44,M,01:48:44,04:18:08 +41,M,01:56:17,04:18:08 +35,W,01:59:17,04:18:08 +71,M,01:53:28,04:18:08 +58,M,01:58:34,04:18:08 +34,M,01:55:54,04:18:08 +52,M,01:53:27,04:18:09 +22,W,01:57:09,04:18:09 +47,W,01:57:46,04:18:09 +40,M,01:49:24,04:18:10 +50,M,01:58:08,04:18:10 +47,M,01:54:54,04:18:10 +43,W,01:52:29,04:18:11 +47,M,01:57:08,04:18:11 +60,M,01:55:59,04:18:11 +35,M,01:53:05,04:18:12 +34,M,01:57:13,04:18:12 +40,M,01:53:35,04:18:12 +46,W,01:46:34,04:18:12 +51,W,01:56:39,04:18:13 +50,M,01:58:25,04:18:13 +32,M,01:48:40,04:18:13 +44,M,01:52:52,04:18:14 +37,M,01:53:41,04:18:15 +49,M,01:55:09,04:18:16 +42,M,01:55:27,04:18:16 +39,M,01:55:12,04:18:16 +48,M,01:53:07,04:18:16 +56,W,01:53:36,04:18:16 +34,W,02:01:49,04:18:17 +45,M,01:55:28,04:18:17 +39,M,01:57:31,04:18:17 +38,M,01:48:14,04:18:17 +51,M,01:56:49,04:18:18 +24,W,01:54:05,04:18:18 +44,M,01:49:23,04:18:18 +48,M,01:57:18,04:18:18 +33,W,01:50:36,04:18:18 +52,M,01:54:57,04:18:19 +51,M,01:47:40,04:18:19 +39,W,01:59:26,04:18:19 +24,M,01:58:11,04:18:19 +24,W,01:47:20,04:18:20 +38,M,01:56:15,04:18:20 +52,M,01:56:19,04:18:21 +35,M,01:52:05,04:18:21 +46,M,01:50:59,04:18:21 +50,M,01:51:58,04:18:21 +36,M,01:53:07,04:18:22 +51,M,01:49:26,04:18:22 +28,M,01:53:32,04:18:22 +54,M,01:30:17,04:18:22 +44,W,02:00:30,04:18:23 +55,M,01:56:23,04:18:23 +40,M,01:54:03,04:18:23 +45,M,01:56:19,04:18:23 +28,M,01:41:00,04:18:24 +22,M,01:46:06,04:18:24 +42,M,01:59:26,04:18:24 +51,M,01:50:22,04:18:24 +51,W,02:01:07,04:18:24 +31,W,01:48:40,04:18:24 +37,M,01:55:55,04:18:26 +47,M,01:52:46,04:18:26 +46,W,01:56:42,04:18:26 +49,M,01:58:21,04:18:27 +51,M,01:50:43,04:18:27 +48,W,01:58:19,04:18:27 +47,M,01:52:22,04:18:28 +47,W,01:56:12,04:18:28 +41,M,01:51:51,04:18:28 +33,M,01:51:22,04:18:28 +27,W,02:02:42,04:18:29 +48,W,01:56:31,04:18:29 +42,M,01:44:31,04:18:29 +32,M,01:57:57,04:18:29 +33,W,01:56:41,04:18:29 +50,W,01:55:20,04:18:29 +48,M,01:47:04,04:18:30 +36,M,01:46:42,04:18:30 +33,M,01:51:14,04:18:30 +39,M,01:46:44,04:18:30 +57,M,01:53:21,04:18:30 +39,M,01:55:11,04:18:30 +28,M,01:49:54,04:18:31 +33,M,01:49:01,04:18:31 +28,W,01:51:53,04:18:31 +36,M,01:48:39,04:18:31 +42,M,01:54:51,04:18:31 +35,W,01:54:20,04:18:32 +42,M,01:56:43,04:18:32 +57,M,01:55:21,04:18:32 +39,M,01:57:21,04:18:33 +33,M,01:41:53,04:18:33 +33,M,01:56:07,04:18:33 +39,W,02:00:03,04:18:33 +47,W,01:54:23,04:18:34 +44,M,02:02:20,04:18:34 +42,M,01:56:46,04:18:35 +22,M,02:03:09,04:18:35 +30,W,01:58:29,04:18:35 +31,W,01:55:13,04:18:36 +27,M,01:48:26,04:18:36 +32,M,01:51:25,04:18:36 +31,W,01:55:14,04:18:36 +36,W,01:58:29,04:18:37 +38,M,01:53:45,04:18:37 +43,M,01:51:32,04:18:37 +42,W,02:07:58,04:18:37 +55,M,01:49:26,04:18:37 +43,M,01:54:09,04:18:38 +37,M,01:53:37,04:18:38 +43,M,01:59:29,04:18:38 +29,M,01:59:00,04:18:39 +26,W,01:57:15,04:18:39 +31,M,01:53:22,04:18:40 +40,W,01:57:01,04:18:40 +39,M,01:44:38,04:18:40 +32,M,01:58:12,04:18:40 +52,M,01:46:28,04:18:40 +44,W,02:00:23,04:18:40 +31,W,01:54:26,04:18:41 +49,M,02:03:57,04:18:41 +35,M,01:52:00,04:18:41 +35,M,01:53:06,04:18:41 +37,W,01:52:14,04:18:42 +50,W,02:06:11,04:18:42 +47,M,01:45:22,04:18:42 +38,M,01:58:42,04:18:43 +46,M,01:53:49,04:18:43 +37,W,02:02:18,04:18:43 +55,W,01:55:34,04:18:43 +53,M,01:51:11,04:18:43 +59,M,01:51:47,04:18:43 +42,M,01:52:38,04:18:44 +52,W,01:56:35,04:18:45 +35,M,01:52:05,04:18:45 +63,M,01:58:48,04:18:46 +33,M,01:52:43,04:18:46 +40,W,01:56:22,04:18:46 +55,M,01:54:17,04:18:46 +50,M,01:59:10,04:18:47 +26,M,01:42:17,04:18:47 +50,M,01:57:42,04:18:48 +50,M,01:58:06,04:18:48 +23,M,01:50:17,04:18:49 +49,M,01:54:58,04:18:49 +25,W,01:57:15,04:18:49 +51,M,01:52:19,04:18:49 +47,M,01:57:57,04:18:49 +38,M,01:57:48,04:18:49 +31,M,01:51:47,04:18:49 +26,M,01:40:45,04:18:49 +23,M,01:58:06,04:18:50 +35,M,01:58:44,04:18:50 +49,M,01:58:36,04:18:50 +39,M,01:51:26,04:18:51 +35,M,01:50:18,04:18:51 +27,W,01:56:48,04:18:51 +48,M,02:03:41,04:18:51 +41,M,02:03:29,04:18:51 +28,M,01:54:19,04:18:51 +42,M,01:59:10,04:18:52 +41,W,01:56:05,04:18:52 +44,M,01:56:56,04:18:52 +52,M,01:52:35,04:18:53 +39,M,01:53:55,04:18:55 +43,M,01:57:46,04:18:56 +30,W,01:52:29,04:18:56 +37,W,02:10:00,04:18:57 +38,M,01:57:33,04:18:57 +32,M,01:56:30,04:18:57 +38,M,01:54:36,04:18:57 +36,M,01:52:26,04:18:58 +25,W,01:58:04,04:18:58 +39,M,01:56:27,04:18:58 +54,M,01:52:49,04:18:58 +41,W,01:52:04,04:18:58 +37,W,01:56:25,04:18:59 +60,M,01:54:10,04:18:59 +36,M,01:43:48,04:19:00 +42,M,01:54:57,04:19:00 +41,W,01:57:46,04:19:00 +31,M,02:00:22,04:19:00 +31,M,01:43:24,04:19:00 +32,M,01:58:20,04:19:00 +31,W,01:55:42,04:19:01 +30,M,01:47:50,04:19:01 +42,M,01:53:56,04:19:02 +29,M,01:50:47,04:19:02 +55,M,01:51:08,04:19:02 +35,W,02:02:26,04:19:02 +48,M,01:55:05,04:19:02 +27,W,01:47:27,04:19:02 +24,M,01:50:49,04:19:02 +39,W,01:56:48,04:19:03 +24,M,01:52:01,04:19:03 +50,M,01:55:40,04:19:03 +27,W,01:46:29,04:19:03 +39,M,02:05:31,04:19:03 +25,W,01:58:45,04:19:03 +26,W,01:59:01,04:19:04 +39,M,01:43:46,04:19:04 +57,M,01:56:08,04:19:04 +44,M,01:56:13,04:19:05 +53,M,01:51:15,04:19:05 +30,M,01:46:15,04:19:05 +39,W,01:58:17,04:19:06 +44,M,01:57:33,04:19:06 +42,M,01:53:26,04:19:06 +60,M,02:04:53,04:19:06 +28,M,01:46:42,04:19:06 +59,M,01:57:55,04:19:07 +64,M,01:48:57,04:19:07 +36,W,01:55:46,04:19:07 +37,M,01:56:03,04:19:07 +32,M,01:49:59,04:19:08 +43,W,01:52:35,04:19:08 +39,M,01:55:43,04:19:08 +35,M,01:53:17,04:19:08 +37,M,01:57:10,04:19:10 +46,W,01:55:44,04:19:10 +43,M,01:56:55,04:19:10 +37,W,01:59:49,04:19:10 +36,M,01:41:41,04:19:10 +30,M,01:50:29,04:19:11 +32,W,01:53:26,04:19:11 +40,M,01:51:16,04:19:11 +53,M,01:56:29,04:19:11 +44,W,01:54:00,04:19:11 +64,M,01:58:06,04:19:11 +54,M,01:50:41,04:19:12 +52,M,01:50:35,04:19:12 +26,M,01:43:23,04:19:12 +48,M,01:55:45,04:19:13 +24,W,01:58:42,04:19:13 +46,M,01:55:03,04:19:13 +29,M,01:57:18,04:19:14 +47,M,01:53:19,04:19:14 +33,W,01:53:54,04:19:14 +43,M,01:58:39,04:19:14 +34,M,01:55:09,04:19:14 +47,M,01:57:29,04:19:14 +52,M,01:56:54,04:19:15 +33,W,01:59:15,04:19:15 +38,M,01:51:47,04:19:15 +25,M,01:52:06,04:19:15 +29,W,01:58:18,04:19:16 +50,W,01:58:36,04:19:16 +31,M,01:56:19,04:19:16 +47,M,01:53:10,04:19:16 +37,M,01:55:56,04:19:16 +44,M,01:45:35,04:19:16 +44,M,01:56:11,04:19:16 +46,W,01:53:34,04:19:17 +52,M,01:55:41,04:19:17 +44,M,01:51:20,04:19:17 +54,W,02:04:35,04:19:17 +22,W,01:52:37,04:19:17 +61,M,01:55:59,04:19:17 +36,M,01:58:27,04:19:17 +67,M,01:58:44,04:19:18 +52,M,01:53:35,04:19:18 +28,M,01:48:50,04:19:18 +30,M,01:57:36,04:19:19 +30,W,01:57:40,04:19:19 +27,M,02:02:37,04:19:19 +29,W,01:53:54,04:19:19 +40,M,01:48:04,04:19:19 +31,M,01:49:17,04:19:20 +27,M,01:47:42,04:19:20 +46,M,01:39:12,04:19:20 +46,M,01:56:40,04:19:20 +24,M,01:53:13,04:19:21 +44,M,01:49:56,04:19:21 +31,W,01:57:34,04:19:21 +60,M,02:03:39,04:19:22 +30,W,01:57:25,04:19:22 +39,M,01:59:08,04:19:23 +43,M,01:58:05,04:19:23 +50,M,01:54:21,04:19:23 +40,M,01:55:26,04:19:23 +23,M,01:54:22,04:19:23 +34,M,01:38:09,04:19:23 +30,M,01:51:25,04:19:23 +40,M,01:54:02,04:19:23 +41,M,01:49:28,04:19:24 +27,W,01:57:28,04:19:24 +26,M,01:58:44,04:19:25 +25,W,01:47:57,04:19:25 +36,M,01:50:37,04:19:25 +33,W,01:54:55,04:19:26 +50,M,02:01:51,04:19:26 +43,M,01:59:09,04:19:28 +34,M,01:59:40,04:19:29 +48,M,02:04:31,04:19:29 +22,W,01:53:54,04:19:30 +46,M,01:50:20,04:19:30 +35,M,01:56:38,04:19:30 +42,M,01:44:42,04:19:30 +46,M,01:55:12,04:19:30 +25,M,01:47:10,04:19:30 +40,W,01:56:45,04:19:31 +59,M,01:52:17,04:19:31 +39,W,02:00:38,04:19:32 +28,M,01:46:11,04:19:32 +54,M,01:54:16,04:19:33 +36,M,01:54:52,04:19:33 +53,W,01:59:43,04:19:33 +29,M,01:55:24,04:19:34 +30,W,01:45:39,04:19:34 +37,M,01:53:12,04:19:34 +30,M,01:51:23,04:19:34 +40,M,01:54:30,04:19:34 +21,M,01:57:09,04:19:35 +27,M,01:51:31,04:19:35 +43,M,01:51:44,04:19:35 +33,W,01:57:02,04:19:35 +52,M,01:56:19,04:19:35 +55,M,01:57:25,04:19:35 +32,W,01:58:49,04:19:36 +42,M,01:55:14,04:19:36 +35,M,01:48:12,04:19:37 +28,M,01:51:31,04:19:37 +51,M,01:51:32,04:19:37 +38,M,01:47:57,04:19:38 +54,M,02:04:08,04:19:38 +39,W,01:53:20,04:19:38 +51,M,01:50:49,04:19:39 +32,W,01:58:11,04:19:39 +41,M,01:57:41,04:19:39 +45,M,01:54:06,04:19:40 +29,W,01:52:22,04:19:40 +52,M,01:56:23,04:19:40 +26,W,02:00:03,04:19:40 +34,W,01:58:22,04:19:40 +32,M,01:57:30,04:19:40 +29,M,01:59:31,04:19:40 +38,M,01:29:54,04:19:41 +65,M,01:54:42,04:19:41 +38,M,01:54:17,04:19:41 +36,M,02:04:11,04:19:42 +31,M,01:59:33,04:19:42 +58,W,02:01:26,04:19:42 +36,M,01:51:28,04:19:42 +39,M,01:52:31,04:19:43 +45,M,01:54:51,04:19:43 +32,M,02:01:06,04:19:43 +54,W,02:00:07,04:19:43 +54,M,01:54:43,04:19:43 +49,M,01:58:08,04:19:44 +50,M,01:53:16,04:19:44 +26,M,01:49:58,04:19:44 +47,W,01:52:56,04:19:44 +26,W,01:56:48,04:19:45 +45,W,01:57:49,04:19:45 +52,M,01:54:10,04:19:45 +56,M,01:57:17,04:19:45 +21,M,01:51:31,04:19:46 +44,M,01:55:22,04:19:46 +41,M,01:58:54,04:19:46 +39,M,01:55:51,04:19:47 +40,M,02:00:07,04:19:47 +51,W,02:03:02,04:19:48 +46,M,01:55:38,04:19:48 +25,M,01:57:10,04:19:48 +27,W,01:50:02,04:19:48 +39,M,01:49:59,04:19:48 +54,W,01:58:58,04:19:48 +57,M,01:58:30,04:19:48 +50,M,01:54:55,04:19:49 +42,M,02:00:35,04:19:49 +27,M,02:00:45,04:19:49 +43,W,02:02:42,04:19:49 +46,M,01:56:49,04:19:49 +46,M,01:55:26,04:19:49 +46,W,01:55:45,04:19:50 +27,W,01:52:29,04:19:50 +53,M,01:59:06,04:19:50 +49,M,01:59:16,04:19:50 +36,M,01:57:25,04:19:51 +33,M,01:55:20,04:19:51 +39,M,01:55:32,04:19:51 +42,M,01:46:53,04:19:51 +50,M,01:51:57,04:19:51 +29,M,01:54:53,04:19:51 +27,M,01:49:31,04:19:51 +30,W,01:56:09,04:19:51 +29,W,02:00:06,04:19:51 +42,M,01:52:54,04:19:52 +33,M,01:58:20,04:19:52 +60,W,01:49:48,04:19:52 +27,M,01:58:58,04:19:52 +36,M,01:40:50,04:19:52 +44,M,01:56:10,04:19:52 +43,M,01:59:46,04:19:53 +31,M,01:53:55,04:19:53 +52,M,01:56:39,04:19:53 +26,W,01:54:11,04:19:54 +34,W,01:49:46,04:19:54 +39,M,01:57:40,04:19:54 +40,M,02:00:11,04:19:54 +32,W,01:54:38,04:19:54 +49,M,01:49:06,04:19:54 +44,M,01:53:39,04:19:54 +40,W,01:56:20,04:19:54 +40,M,01:57:28,04:19:55 +29,W,01:54:31,04:19:55 +50,M,01:52:26,04:19:55 +41,M,01:57:05,04:19:56 +51,M,02:05:15,04:19:56 +49,M,01:55:44,04:19:56 +40,M,01:48:08,04:19:56 +41,M,01:58:24,04:19:56 +46,M,02:05:55,04:19:56 +46,M,01:50:33,04:19:56 +38,M,01:53:15,04:19:56 +36,W,01:49:42,04:19:57 +31,W,01:59:57,04:19:57 +46,M,01:53:43,04:19:57 +31,M,01:48:39,04:19:57 +30,W,01:57:37,04:19:58 +47,M,01:54:32,04:19:58 +36,W,01:58:34,04:19:58 +47,M,01:58:48,04:19:58 +41,M,01:57:07,04:19:58 +41,M,01:52:11,04:19:58 +33,W,02:02:01,04:19:58 +49,M,01:58:46,04:19:58 +45,M,01:59:04,04:19:59 +53,M,02:01:07,04:19:59 +42,W,01:57:33,04:19:59 +33,W,02:00:16,04:19:59 +58,M,01:54:47,04:19:59 +45,M,01:51:39,04:19:59 +39,W,01:58:41,04:19:59 +32,M,01:57:22,04:20:00 +35,M,01:57:42,04:20:00 +26,W,01:55:27,04:20:00 +47,M,01:51:41,04:20:01 +36,W,01:48:05,04:20:01 +32,M,01:50:06,04:20:01 +41,M,01:53:35,04:20:01 +43,M,01:45:05,04:20:01 +48,M,01:52:20,04:20:01 +33,M,01:53:28,04:20:01 +47,M,01:54:19,04:20:02 +39,W,01:53:16,04:20:03 +47,M,02:01:21,04:20:03 +28,W,01:53:26,04:20:03 +65,M,01:55:20,04:20:04 +52,M,01:56:18,04:20:04 +41,M,01:58:19,04:20:04 +54,M,01:57:09,04:20:04 +41,M,02:01:41,04:20:04 +25,W,01:54:49,04:20:05 +41,M,02:00:12,04:20:05 +59,M,01:49:36,04:20:05 +39,M,01:54:50,04:20:05 +49,W,01:57:59,04:20:05 +50,M,01:51:48,04:20:05 +53,W,01:56:10,04:20:06 +51,M,01:53:17,04:20:06 +37,M,01:56:34,04:20:06 +35,W,01:56:44,04:20:06 +36,M,01:55:57,04:20:07 +65,M,02:00:51,04:20:07 +25,W,01:58:20,04:20:07 +29,M,01:49:42,04:20:07 +17,M,01:39:51,04:20:09 +20,M,01:49:59,04:20:10 +49,M,01:45:29,04:20:10 +35,M,02:07:40,04:20:11 +41,M,01:55:24,04:20:11 +42,M,01:54:03,04:20:11 +30,M,01:48:49,04:20:11 +37,M,01:54:31,04:20:12 +52,W,01:54:43,04:20:12 +38,M,01:59:30,04:20:12 +45,M,01:54:21,04:20:12 +55,M,02:00:49,04:20:13 +39,M,01:59:19,04:20:13 +64,M,01:53:23,04:20:13 +40,W,02:00:08,04:20:13 +33,W,01:58:49,04:20:14 +35,W,01:58:24,04:20:15 +46,M,01:53:29,04:20:16 +42,M,01:59:39,04:20:16 +45,M,01:41:30,04:20:17 +30,M,01:47:59,04:20:17 +58,M,01:57:10,04:20:17 +37,M,01:57:08,04:20:17 +36,M,01:56:48,04:20:17 +40,W,01:59:48,04:20:17 +44,M,01:56:48,04:20:18 +30,M,01:34:50,04:20:18 +35,M,02:04:09,04:20:19 +42,M,01:54:17,04:20:19 +39,M,01:53:34,04:20:19 +37,M,01:55:30,04:20:19 +32,W,01:59:08,04:20:19 +53,M,01:55:12,04:20:20 +37,M,01:50:52,04:20:20 +31,W,01:55:00,04:20:20 +54,M,01:46:27,04:20:21 +28,W,01:52:35,04:20:21 +41,M,01:56:12,04:20:21 +48,M,01:51:03,04:20:21 +34,M,01:54:29,04:20:21 +43,M,01:52:02,04:20:22 +42,M,02:03:42,04:20:22 +38,M,01:46:32,04:20:22 +31,W,02:01:31,04:20:22 +31,M,01:57:13,04:20:22 +29,W,01:57:55,04:20:23 +51,M,01:54:24,04:20:23 +38,M,01:52:27,04:20:23 +43,M,01:53:24,04:20:24 +31,W,01:58:08,04:20:24 +41,M,01:57:19,04:20:24 +47,M,02:01:04,04:20:24 +34,M,02:00:26,04:20:24 +41,M,01:56:22,04:20:24 +47,M,01:55:24,04:20:25 +42,W,01:56:37,04:20:26 +30,W,01:58:53,04:20:26 +32,M,02:00:45,04:20:26 +32,W,01:46:30,04:20:26 +41,W,02:00:04,04:20:27 +53,W,01:59:24,04:20:27 +41,M,01:52:52,04:20:28 +52,M,01:57:27,04:20:28 +44,M,01:58:27,04:20:28 +51,M,01:59:15,04:20:28 +41,M,01:56:55,04:20:29 +34,M,01:47:56,04:20:29 +36,M,01:49:47,04:20:29 +40,W,01:51:01,04:20:29 +53,M,01:52:20,04:20:29 +31,W,01:58:59,04:20:29 +26,W,01:56:44,04:20:30 +41,M,01:48:28,04:20:30 +44,W,01:56:50,04:20:30 +44,M,01:50:50,04:20:31 +32,M,01:57:54,04:20:31 +51,M,01:56:59,04:20:31 +51,M,01:59:47,04:20:31 +38,M,01:57:14,04:20:31 +33,W,01:52:11,04:20:32 +42,M,01:58:13,04:20:32 +49,M,02:02:11,04:20:32 +32,W,01:53:49,04:20:32 +30,W,01:57:47,04:20:32 +50,M,01:52:09,04:20:33 +26,W,01:53:00,04:20:33 +33,M,01:48:17,04:20:33 +28,W,01:55:19,04:20:33 +55,W,01:54:09,04:20:33 +30,W,01:49:07,04:20:33 +50,W,01:53:29,04:20:34 +25,M,02:00:26,04:20:34 +56,M,01:53:57,04:20:34 +44,M,01:56:51,04:20:34 +38,M,02:00:47,04:20:34 +56,M,01:58:30,04:20:34 +38,M,02:00:32,04:20:34 +34,W,01:52:37,04:20:35 +42,M,01:45:05,04:20:35 +51,M,01:57:26,04:20:35 +33,M,01:47:53,04:20:36 +30,M,01:51:34,04:20:36 +52,M,01:53:48,04:20:37 +48,M,01:57:50,04:20:37 +50,M,01:54:14,04:20:37 +45,M,01:48:36,04:20:38 +49,W,01:57:20,04:20:38 +60,M,01:58:36,04:20:38 +39,M,01:59:19,04:20:38 +37,M,01:54:15,04:20:39 +25,M,01:46:15,04:20:39 +41,M,01:48:08,04:20:39 +28,W,01:51:39,04:20:39 +37,M,01:57:24,04:20:39 +27,M,01:46:17,04:20:39 +31,W,01:54:31,04:20:40 +35,M,01:47:47,04:20:40 +42,M,02:02:00,04:20:40 +38,M,01:50:24,04:20:41 +36,M,01:52:54,04:20:41 +60,M,01:48:35,04:20:41 +31,M,01:56:50,04:20:41 +47,M,01:53:45,04:20:42 +56,M,01:49:31,04:20:42 +43,M,01:55:54,04:20:42 +49,W,01:56:43,04:20:42 +39,M,01:46:54,04:20:43 +39,W,01:57:05,04:20:43 +49,M,01:53:40,04:20:43 +50,M,01:49:06,04:20:44 +26,M,01:58:02,04:20:44 +35,W,02:00:42,04:20:44 +30,W,01:53:35,04:20:45 +40,M,01:46:51,04:20:45 +39,W,02:04:02,04:20:45 +49,M,02:03:12,04:20:45 +34,M,01:58:41,04:20:46 +45,M,02:04:06,04:20:46 +43,M,01:58:31,04:20:46 +44,M,01:58:28,04:20:46 +39,M,02:04:12,04:20:46 +54,M,02:04:34,04:20:46 +43,M,02:01:02,04:20:47 +37,M,01:51:16,04:20:47 +28,M,01:52:53,04:20:47 +57,W,02:01:08,04:20:49 +40,M,01:42:04,04:20:49 +66,M,01:47:24,04:20:49 +47,M,02:00:04,04:20:50 +35,M,01:54:58,04:20:50 +35,M,01:58:48,04:20:50 +38,M,01:56:12,04:20:50 +37,M,01:53:32,04:20:51 +38,M,01:50:36,04:20:51 +30,M,01:53:44,04:20:51 +59,M,01:58:17,04:20:51 +37,M,01:57:20,04:20:52 +26,W,01:57:03,04:20:52 +21,M,02:08:45,04:20:52 +30,W,02:00:18,04:20:53 +26,M,01:57:04,04:20:53 +48,M,01:56:21,04:20:53 +28,W,01:49:09,04:20:53 +42,M,01:53:15,04:20:53 +34,W,01:56:28,04:20:53 +43,M,01:57:54,04:20:53 +40,M,01:55:56,04:20:54 +33,M,01:54:43,04:20:54 +48,M,02:08:20,04:20:54 +44,M,01:49:38,04:20:54 +25,W,01:53:57,04:20:54 +37,W,02:04:29,04:20:54 +36,W,01:55:21,04:20:54 +29,M,01:49:40,04:20:55 +43,W,02:02:04,04:20:55 +36,M,01:58:38,04:20:55 +30,M,01:55:30,04:20:56 +44,W,01:56:58,04:20:57 +51,M,02:04:12,04:20:57 +48,M,01:52:34,04:20:57 +39,M,01:52:38,04:20:57 +47,M,01:49:27,04:20:57 +25,W,01:59:26,04:20:57 +27,M,01:57:43,04:20:57 +29,W,01:52:53,04:20:57 +60,M,01:48:41,04:20:58 +44,M,01:47:59,04:20:58 +65,M,01:55:36,04:20:58 +35,M,01:54:18,04:20:58 +34,W,01:53:32,04:20:58 +45,M,02:04:04,04:20:59 +32,M,01:52:29,04:20:59 +48,M,02:02:44,04:20:59 +34,M,01:50:28,04:20:59 +39,M,01:55:25,04:20:59 +40,M,02:01:37,04:20:59 +52,M,01:54:42,04:21:00 +39,M,01:53:01,04:21:00 +52,W,01:57:29,04:21:00 +33,M,01:45:38,04:21:00 +52,M,01:57:40,04:21:00 +34,W,01:56:14,04:21:00 +41,M,01:45:36,04:21:00 +49,M,01:48:13,04:21:01 +34,M,01:45:33,04:21:01 +42,M,01:53:31,04:21:02 +43,M,01:54:26,04:21:02 +54,M,01:56:51,04:21:02 +32,W,01:59:31,04:21:02 +65,M,01:59:21,04:21:03 +43,M,01:55:56,04:21:03 +44,M,01:51:37,04:21:03 +57,M,01:51:55,04:21:04 +34,M,01:59:15,04:21:04 +26,W,01:50:21,04:21:04 +52,M,01:57:35,04:21:04 +52,M,01:59:23,04:21:05 +38,M,02:00:05,04:21:05 +53,M,01:49:53,04:21:05 +28,W,01:55:53,04:21:05 +27,W,02:00:50,04:21:06 +41,W,01:58:54,04:21:06 +32,M,01:50:54,04:21:06 +53,M,01:56:04,04:21:06 +42,M,02:00:22,04:21:06 +43,W,01:52:18,04:21:07 +47,W,01:58:31,04:21:07 +37,M,02:10:13,04:21:07 +33,M,01:57:08,04:21:07 +39,W,01:57:10,04:21:07 +38,M,01:54:16,04:21:07 +55,M,01:58:48,04:21:07 +33,M,01:58:51,04:21:08 +43,W,01:58:59,04:21:08 +34,M,01:58:11,04:21:08 +40,M,02:02:37,04:21:08 +31,W,01:57:27,04:21:09 +27,M,01:55:50,04:21:10 +44,M,01:57:35,04:21:11 +53,M,01:57:48,04:21:11 +62,M,01:55:06,04:21:11 +48,M,01:54:53,04:21:11 +42,M,01:58:40,04:21:11 +44,M,01:58:15,04:21:11 +51,M,01:50:50,04:21:12 +25,M,01:50:19,04:21:13 +24,W,01:57:19,04:21:13 +37,W,01:58:03,04:21:13 +38,M,01:53:20,04:21:14 +45,M,01:59:23,04:21:14 +50,M,01:54:10,04:21:15 +28,W,02:03:51,04:21:15 +50,M,01:55:21,04:21:15 +24,M,01:57:15,04:21:15 +49,M,01:59:37,04:21:16 +30,M,01:46:58,04:21:16 +42,M,01:51:05,04:21:17 +25,M,01:59:49,04:21:17 +33,M,02:00:54,04:21:17 +54,M,02:01:46,04:21:17 +38,W,01:57:53,04:21:17 +39,M,01:58:03,04:21:17 +56,M,01:48:20,04:21:17 +32,M,01:50:10,04:21:18 +55,W,01:59:22,04:21:18 +31,W,01:56:49,04:21:18 +51,M,01:57:05,04:21:18 +42,M,01:58:37,04:21:19 +48,M,01:57:57,04:21:19 +40,M,01:55:56,04:21:19 +50,M,01:55:54,04:21:20 +36,M,01:59:23,04:21:20 +38,M,01:57:07,04:21:21 +46,M,01:56:58,04:21:21 +32,M,01:58:14,04:21:22 +47,W,01:57:14,04:21:22 +29,W,01:55:59,04:21:22 +50,W,01:58:52,04:21:23 +31,W,01:58:52,04:21:23 +38,M,01:52:05,04:21:23 +43,M,01:55:56,04:21:23 +43,M,02:01:21,04:21:23 +49,M,01:47:59,04:21:24 +41,M,01:51:52,04:21:24 +38,W,01:50:55,04:21:24 +46,M,01:53:39,04:21:24 +59,M,01:59:57,04:21:24 +35,M,01:55:03,04:21:26 +66,M,02:01:26,04:21:26 +49,M,01:44:06,04:21:26 +45,M,02:01:00,04:21:27 +33,M,01:51:04,04:21:27 +42,M,01:55:18,04:21:27 +49,M,01:54:43,04:21:27 +40,M,01:54:01,04:21:27 +49,W,01:58:54,04:21:28 +41,M,01:44:42,04:21:28 +41,M,01:44:07,04:21:28 +33,M,02:07:58,04:21:28 +37,W,01:55:18,04:21:28 +48,M,01:55:19,04:21:28 +48,M,01:54:18,04:21:28 +25,M,01:56:24,04:21:28 +50,M,01:55:39,04:21:29 +41,M,02:01:00,04:21:29 +56,M,01:52:11,04:21:29 +43,M,01:56:33,04:21:30 +36,M,01:57:18,04:21:31 +44,M,01:52:08,04:21:31 +35,M,02:01:22,04:21:31 +30,M,01:53:22,04:21:31 +44,M,02:01:49,04:21:31 +51,W,01:58:27,04:21:32 +27,W,01:57:53,04:21:32 +38,W,01:57:30,04:21:32 +32,M,01:50:29,04:21:33 +58,M,01:53:34,04:21:33 +39,M,01:45:52,04:21:33 +51,M,01:57:09,04:21:33 +47,M,01:49:02,04:21:33 +48,M,02:00:10,04:21:34 +28,W,01:58:57,04:21:34 +39,W,01:58:55,04:21:34 +39,M,01:53:35,04:21:34 +32,W,01:59:34,04:21:35 +55,W,01:58:32,04:21:36 +49,M,01:53:31,04:21:36 +30,M,01:59:07,04:21:36 +41,W,01:59:03,04:21:36 +31,W,01:57:40,04:21:36 +38,M,01:51:27,04:21:37 +25,M,01:47:16,04:21:37 +26,M,01:44:06,04:21:38 +51,W,01:55:34,04:21:38 +53,W,01:52:06,04:21:38 +38,M,01:52:42,04:21:38 +34,W,01:51:18,04:21:38 +29,W,02:00:21,04:21:38 +42,W,01:54:53,04:21:39 +28,W,01:52:42,04:21:39 +27,M,01:50:03,04:21:39 +53,M,01:56:59,04:21:39 +38,M,01:57:01,04:21:39 +27,W,01:49:53,04:21:40 +53,M,01:57:55,04:21:40 +51,W,02:05:40,04:21:40 +38,M,01:58:58,04:21:40 +30,M,01:54:17,04:21:40 +30,M,01:54:41,04:21:41 +25,W,02:02:18,04:21:41 +32,M,01:48:31,04:21:41 +45,M,01:58:03,04:21:41 +35,W,01:53:48,04:21:42 +63,M,01:56:57,04:21:42 +46,W,01:54:35,04:21:42 +39,W,02:01:40,04:21:43 +36,M,01:48:42,04:21:43 +37,M,01:44:34,04:21:43 +43,M,01:45:23,04:21:44 +40,M,01:49:13,04:21:44 +49,W,01:53:48,04:21:44 +34,M,01:50:45,04:21:45 +41,M,02:11:10,04:21:46 +25,M,01:55:54,04:21:46 +39,W,01:58:46,04:21:46 +33,W,02:00:44,04:21:47 +27,W,01:50:55,04:21:47 +42,M,01:53:38,04:21:47 +41,M,01:58:40,04:21:47 +43,M,02:02:24,04:21:47 +36,W,02:00:43,04:21:47 +41,W,01:56:57,04:21:48 +32,M,01:55:49,04:21:48 +51,M,01:45:29,04:21:48 +44,W,02:00:23,04:21:48 +50,M,01:48:49,04:21:48 +24,W,02:03:05,04:21:49 +29,W,01:57:12,04:21:49 +28,M,01:51:33,04:21:49 +52,M,01:50:22,04:21:50 +48,M,01:56:17,04:21:50 +30,W,01:56:21,04:21:50 +32,W,02:03:43,04:21:50 +51,W,01:54:16,04:21:51 +48,W,01:53:53,04:21:51 +41,M,01:48:51,04:21:51 +27,M,01:58:35,04:21:51 +34,W,01:54:47,04:21:51 +48,M,01:57:55,04:21:51 +31,W,02:01:41,04:21:51 +50,W,02:06:10,04:21:52 +35,M,01:55:56,04:21:52 +46,M,02:00:27,04:21:52 +41,W,01:59:36,04:21:52 +50,M,01:52:46,04:21:53 +42,M,01:51:26,04:21:53 +45,M,02:00:31,04:21:53 +40,M,01:52:02,04:21:53 +37,W,01:56:47,04:21:54 +32,M,01:58:35,04:21:54 +44,W,01:56:59,04:21:54 +62,M,01:56:24,04:21:55 +40,M,01:52:11,04:21:55 +32,M,01:57:27,04:21:55 +52,W,01:53:57,04:21:55 +28,W,01:57:54,04:21:55 +47,M,02:01:08,04:21:55 +33,M,01:51:12,04:21:56 +52,M,01:54:16,04:21:56 +29,M,01:55:29,04:21:56 +34,M,01:57:33,04:21:56 +28,W,01:56:41,04:21:56 +44,M,01:56:54,04:21:56 +31,M,01:59:01,04:21:56 +42,M,01:48:08,04:21:57 +33,M,01:48:25,04:21:57 +42,W,01:56:05,04:21:57 +44,M,01:57:46,04:21:57 +41,W,01:58:58,04:21:58 +32,W,01:52:29,04:21:58 +34,M,01:56:43,04:21:58 +30,M,01:59:48,04:21:58 +52,M,01:59:23,04:21:58 +38,W,01:58:44,04:21:58 +33,W,01:56:25,04:21:59 +46,M,01:47:43,04:21:59 +63,M,02:01:34,04:21:59 +37,W,01:50:42,04:21:59 +53,M,01:55:11,04:22:00 +30,M,01:46:45,04:22:00 +58,M,01:49:38,04:22:00 +32,W,01:58:49,04:22:00 +26,M,01:52:15,04:22:01 +36,W,01:56:50,04:22:01 +48,M,01:54:43,04:22:01 +25,M,01:47:12,04:22:01 +42,M,01:53:42,04:22:01 +44,M,02:02:23,04:22:02 +25,W,01:49:40,04:22:03 +33,W,02:02:08,04:22:03 +44,M,01:48:22,04:22:03 +38,W,02:03:46,04:22:03 +41,M,01:56:15,04:22:03 +32,W,01:58:14,04:22:03 +39,M,01:58:01,04:22:04 +46,W,01:54:30,04:22:04 +33,M,01:56:46,04:22:04 +50,M,02:01:15,04:22:05 +51,M,01:52:55,04:22:05 +27,W,01:56:07,04:22:06 +49,M,01:51:08,04:22:06 +24,W,01:53:56,04:22:07 +27,M,01:46:58,04:22:07 +51,M,01:52:35,04:22:08 +28,W,01:51:02,04:22:08 +52,M,01:52:58,04:22:08 +47,M,01:55:51,04:22:09 +28,M,02:04:16,04:22:09 +34,M,01:51:16,04:22:09 +49,W,01:56:47,04:22:09 +32,M,01:49:07,04:22:09 +29,W,01:54:43,04:22:10 +30,W,01:58:07,04:22:10 +34,W,01:54:04,04:22:11 +19,W,01:57:41,04:22:11 +51,M,01:54:59,04:22:11 +46,W,01:59:12,04:22:12 +26,M,01:51:37,04:22:13 +39,M,01:46:44,04:22:13 +23,W,01:56:03,04:22:13 +40,W,01:58:17,04:22:13 +34,M,01:47:58,04:22:14 +46,M,01:59:43,04:22:14 +43,M,01:51:09,04:22:14 +44,M,01:59:12,04:22:15 +52,M,01:59:36,04:22:15 +38,W,02:03:09,04:22:15 +28,M,01:56:44,04:22:16 +52,M,02:03:37,04:22:16 +46,M,01:49:46,04:22:17 +56,M,01:56:15,04:22:17 +30,M,01:54:35,04:22:17 +42,M,01:54:52,04:22:17 +30,W,01:48:47,04:22:17 +35,M,01:56:24,04:22:18 +54,W,02:03:55,04:22:18 +35,W,01:56:52,04:22:18 +36,M,01:56:34,04:22:19 +34,M,01:59:28,04:22:19 +37,M,01:51:20,04:22:19 +29,M,01:52:55,04:22:19 +25,M,01:45:57,04:22:19 +45,M,01:52:08,04:22:20 +39,M,01:56:55,04:22:20 +59,M,01:54:16,04:22:20 +57,M,01:52:21,04:22:20 +28,M,01:56:09,04:22:20 +51,M,01:56:11,04:22:22 +38,W,01:57:06,04:22:22 +41,M,01:57:41,04:22:22 +46,M,01:57:35,04:22:22 +62,M,01:53:54,04:22:23 +50,M,01:50:45,04:22:23 +41,M,01:58:22,04:22:23 +52,M,01:58:58,04:22:24 +19,M,01:48:02,04:22:24 +25,M,01:58:37,04:22:25 +66,M,01:51:16,04:22:25 +42,M,02:02:31,04:22:25 +31,M,01:57:49,04:22:26 +41,W,01:57:24,04:22:26 +55,M,01:56:12,04:22:26 +40,W,01:59:33,04:22:27 +39,M,01:49:36,04:22:27 +54,M,01:54:35,04:22:28 +30,W,01:59:59,04:22:28 +30,M,01:51:13,04:22:28 +32,M,01:53:19,04:22:28 +69,M,01:54:26,04:22:28 +48,M,01:57:58,04:22:29 +41,W,01:58:47,04:22:29 +43,M,01:50:54,04:22:29 +27,M,01:53:45,04:22:29 +35,M,01:57:04,04:22:29 +44,M,01:49:10,04:22:30 +34,M,01:58:59,04:22:30 +28,M,01:57:23,04:22:30 +47,M,01:55:53,04:22:31 +45,M,01:47:07,04:22:31 +29,M,01:54:02,04:22:31 +39,M,01:57:12,04:22:31 +43,W,01:56:44,04:22:32 +54,W,01:57:39,04:22:32 +52,M,01:58:57,04:22:33 +54,M,01:56:20,04:22:33 +27,W,01:59:09,04:22:33 +41,W,01:59:26,04:22:33 +37,M,01:55:55,04:22:34 +59,M,01:57:16,04:22:35 +59,M,01:57:26,04:22:35 +57,M,01:59:38,04:22:35 +32,M,01:54:00,04:22:35 +47,M,01:54:57,04:22:35 +41,W,01:56:51,04:22:36 +31,M,01:44:48,04:22:36 +45,M,01:54:48,04:22:37 +39,M,01:57:54,04:22:37 +32,M,01:47:59,04:22:37 +42,W,01:56:00,04:22:37 +51,M,01:56:13,04:22:38 +54,M,01:45:20,04:22:38 +48,M,02:02:34,04:22:38 +55,M,01:43:59,04:22:38 +25,W,01:54:59,04:22:39 +35,W,01:58:59,04:22:39 +37,M,01:43:15,04:22:39 +62,M,01:59:35,04:22:39 +46,M,01:44:56,04:22:40 +42,M,01:55:06,04:22:40 +40,M,01:49:07,04:22:40 +50,M,01:54:07,04:22:40 +26,M,01:58:03,04:22:40 +35,M,01:58:57,04:22:40 +36,M,01:43:25,04:22:41 +42,M,02:00:59,04:22:41 +51,M,01:55:08,04:22:41 +60,M,02:04:20,04:22:42 +39,M,01:57:05,04:22:42 +48,M,01:54:40,04:22:42 +43,W,01:56:58,04:22:42 +28,W,01:57:47,04:22:42 +29,M,01:56:13,04:22:42 +37,M,01:48:25,04:22:43 +35,W,01:59:05,04:22:43 +31,M,02:00:53,04:22:43 +36,M,01:44:10,04:22:43 +50,M,01:51:06,04:22:43 +56,M,01:50:57,04:22:44 +29,W,01:57:58,04:22:44 +51,W,01:56:11,04:22:44 +43,W,01:59:28,04:22:44 +22,W,01:55:27,04:22:44 +54,M,01:55:53,04:22:45 +39,M,02:01:27,04:22:45 +39,W,02:01:56,04:22:45 +43,M,01:58:23,04:22:46 +44,M,01:53:41,04:22:46 +38,M,01:55:27,04:22:46 +28,W,01:56:13,04:22:46 +46,M,01:59:07,04:22:47 +47,M,01:49:22,04:22:47 +41,M,01:56:07,04:22:48 +28,W,01:53:15,04:22:48 +28,W,01:57:30,04:22:49 +38,M,01:51:00,04:22:49 +28,W,01:53:51,04:22:49 +51,M,01:53:24,04:22:49 +40,M,02:04:11,04:22:49 +41,M,01:51:01,04:22:49 +38,M,01:55:53,04:22:50 +35,W,01:53:14,04:22:50 +45,W,01:58:26,04:22:50 +25,M,01:51:02,04:22:51 +28,W,02:00:03,04:22:51 +54,W,01:48:49,04:22:51 +37,W,02:01:03,04:22:52 +55,W,01:54:27,04:22:52 +21,M,01:44:02,04:22:53 +60,W,01:49:06,04:22:54 +32,M,01:56:44,04:22:54 +51,W,01:55:13,04:22:54 +27,W,01:58:50,04:22:55 +49,W,01:56:57,04:22:55 +32,W,02:02:27,04:22:55 +44,W,01:59:19,04:22:56 +35,W,01:52:20,04:22:56 +35,W,01:53:35,04:22:57 +36,M,01:52:01,04:22:57 +17,M,01:57:16,04:22:57 +36,M,01:57:26,04:22:58 +41,W,01:56:44,04:22:58 +43,M,01:52:07,04:22:58 +33,W,02:02:34,04:22:58 +45,M,02:05:20,04:22:59 +42,M,01:59:18,04:22:59 +38,M,01:52:01,04:22:59 +44,M,01:57:04,04:22:59 +29,W,01:50:49,04:23:00 +46,M,01:58:06,04:23:00 +51,M,01:57:13,04:23:01 +45,W,01:54:20,04:23:02 +24,M,01:54:38,04:23:02 +48,M,01:56:35,04:23:02 +26,M,01:41:02,04:23:03 +43,M,01:58:44,04:23:04 +42,M,01:56:31,04:23:04 +51,M,01:57:13,04:23:05 +46,M,01:45:23,04:23:05 +35,M,01:57:26,04:23:05 +37,W,02:03:00,04:23:05 +38,M,01:48:44,04:23:05 +31,M,01:49:48,04:23:06 +69,M,01:53:35,04:23:06 +44,M,01:56:39,04:23:06 +34,M,01:57:41,04:23:06 +26,M,01:52:08,04:23:06 +48,M,01:47:34,04:23:07 +22,W,01:56:25,04:23:07 +20,W,02:01:31,04:23:07 +45,M,01:45:57,04:23:07 +66,W,02:02:51,04:23:08 +33,M,01:59:26,04:23:08 +48,M,01:56:47,04:23:08 +55,W,01:56:01,04:23:08 +49,M,01:57:51,04:23:09 +36,W,01:57:05,04:23:09 +44,M,01:55:27,04:23:09 +44,M,01:45:27,04:23:10 +27,W,01:49:55,04:23:10 +35,M,02:03:05,04:23:10 +46,M,02:02:05,04:23:10 +38,W,01:58:45,04:23:11 +39,M,01:53:05,04:23:11 +36,M,01:46:18,04:23:11 +51,M,01:56:38,04:23:12 +39,W,01:58:55,04:23:12 +43,M,01:53:51,04:23:12 +55,W,01:56:17,04:23:13 +46,M,01:56:19,04:23:13 +43,M,01:59:21,04:23:13 +49,M,01:57:30,04:23:13 +40,W,01:49:35,04:23:14 +30,M,01:55:46,04:23:15 +33,M,01:58:41,04:23:15 +47,M,01:56:47,04:23:15 +33,M,01:52:33,04:23:15 +42,M,01:53:32,04:23:15 +25,M,02:10:02,04:23:16 +47,W,01:56:37,04:23:16 +43,M,01:58:43,04:23:16 +26,M,02:04:43,04:23:17 +50,M,01:52:34,04:23:17 +46,W,01:53:15,04:23:18 +31,W,01:54:04,04:23:18 +47,M,01:55:34,04:23:18 +44,M,01:48:23,04:23:18 +25,W,01:56:08,04:23:19 +36,M,01:57:04,04:23:19 +31,M,01:49:22,04:23:22 +42,W,02:04:22,04:23:22 +31,W,01:54:08,04:23:22 +27,W,01:53:54,04:23:23 +58,M,02:03:41,04:23:23 +38,M,01:45:26,04:23:23 +36,M,01:59:18,04:23:23 +33,W,01:51:02,04:23:24 +47,M,01:55:19,04:23:24 +45,M,01:54:40,04:23:24 +39,M,01:58:10,04:23:25 +30,M,01:57:06,04:23:25 +68,M,01:53:17,04:23:27 +49,M,01:57:49,04:23:27 +70,M,02:00:50,04:23:27 +50,M,01:56:25,04:23:27 +32,M,01:38:46,04:23:27 +35,M,01:53:39,04:23:28 +50,M,02:08:01,04:23:28 +33,W,02:02:19,04:23:28 +46,M,01:55:17,04:23:29 +56,M,01:56:38,04:23:29 +34,W,01:51:05,04:23:29 +56,M,02:05:32,04:23:30 +40,M,01:48:34,04:23:30 +22,W,01:57:19,04:23:30 +31,M,01:55:05,04:23:30 +29,M,01:59:36,04:23:30 +28,W,01:54:19,04:23:30 +26,W,02:10:18,04:23:30 +27,M,02:03:55,04:23:31 +40,W,02:01:01,04:23:31 +45,M,01:56:52,04:23:31 +36,W,01:54:43,04:23:31 +40,M,01:55:04,04:23:32 +34,M,01:47:02,04:23:32 +46,M,02:02:47,04:23:32 +46,M,01:49:38,04:23:32 +39,M,01:51:09,04:23:33 +34,W,01:57:39,04:23:33 +54,M,02:04:48,04:23:33 +37,M,01:57:45,04:23:34 +38,W,01:58:57,04:23:35 +34,M,02:09:48,04:23:35 +28,W,02:10:06,04:23:36 +37,W,01:59:28,04:23:36 +45,M,01:58:39,04:23:36 +42,M,02:04:35,04:23:36 +43,M,01:44:49,04:23:36 +40,M,02:03:11,04:23:37 +37,M,01:59:38,04:23:37 +51,M,01:51:41,04:23:37 +42,M,01:57:05,04:23:38 +41,M,01:50:29,04:23:38 +36,M,01:58:16,04:23:38 +50,M,01:46:30,04:23:38 +34,M,01:54:34,04:23:39 +41,M,01:56:35,04:23:39 +64,M,02:01:02,04:23:39 +23,W,01:54:59,04:23:39 +51,M,01:57:27,04:23:39 +47,M,01:59:10,04:23:39 +44,M,01:54:29,04:23:40 +54,W,02:00:08,04:23:41 +30,M,01:56:18,04:23:41 +44,M,01:47:16,04:23:41 +65,M,01:54:29,04:23:41 +41,M,01:56:06,04:23:41 +49,M,02:01:43,04:23:41 +49,M,02:00:20,04:23:42 +45,M,01:57:43,04:23:43 +42,W,02:01:42,04:23:43 +31,M,01:56:38,04:23:43 +54,M,02:05:38,04:23:43 +46,M,02:05:08,04:23:43 +30,M,01:59:24,04:23:44 +41,M,01:52:54,04:23:44 +25,M,01:51:55,04:23:44 +22,W,01:56:38,04:23:44 +26,W,01:50:47,04:23:45 +29,M,01:56:05,04:23:45 +46,M,01:50:17,04:23:45 +50,M,02:03:53,04:23:45 +27,M,01:58:02,04:23:45 +28,M,01:57:51,04:23:46 +38,M,01:45:24,04:23:46 +34,M,01:48:16,04:23:47 +29,M,01:55:27,04:23:47 +40,M,01:51:48,04:23:48 +37,M,02:01:13,04:23:48 +25,W,01:55:39,04:23:48 +38,W,01:59:36,04:23:48 +65,M,02:00:45,04:23:49 +40,M,01:50:44,04:23:49 +53,M,01:49:18,04:23:50 +52,M,02:00:22,04:23:50 +48,M,01:50:40,04:23:50 +44,M,01:50:59,04:23:50 +47,W,01:59:58,04:23:50 +45,M,02:02:49,04:23:51 +29,W,02:07:29,04:23:51 +61,W,01:50:24,04:23:51 +51,M,01:47:10,04:23:51 +43,M,01:59:13,04:23:51 +33,M,01:57:20,04:23:51 +24,W,01:52:21,04:23:52 +37,W,02:01:18,04:23:52 +44,M,01:48:54,04:23:53 +33,M,01:58:09,04:23:53 +61,M,01:58:35,04:23:53 +24,W,01:57:05,04:23:53 +41,M,01:57:12,04:23:53 +34,M,01:47:19,04:23:53 +33,M,01:52:52,04:23:53 +52,M,01:58:08,04:23:54 +31,M,01:50:26,04:23:54 +43,W,01:56:51,04:23:54 +36,M,01:55:43,04:23:54 +57,M,01:55:03,04:23:55 +44,W,01:53:32,04:23:55 +21,M,01:53:16,04:23:55 +50,M,01:59:30,04:23:55 +43,M,02:10:20,04:23:55 +57,M,02:01:18,04:23:56 +21,M,01:48:53,04:23:57 +55,M,01:58:30,04:23:57 +45,M,01:58:57,04:23:57 +35,W,01:55:11,04:23:58 +47,M,01:57:09,04:23:58 +40,W,01:48:53,04:23:58 +37,M,01:58:17,04:23:58 +51,M,01:47:54,04:23:58 +36,M,02:02:38,04:23:58 +60,M,01:58:43,04:23:59 +44,W,02:02:48,04:23:59 +49,W,02:00:03,04:24:00 +54,W,01:57:53,04:24:00 +25,M,01:50:04,04:24:01 +31,M,01:56:37,04:24:01 +56,M,01:54:15,04:24:01 +34,W,01:58:27,04:24:01 +56,M,02:00:10,04:24:02 +58,M,02:00:09,04:24:02 +29,M,01:56:57,04:24:02 +42,M,01:53:51,04:24:02 +54,M,01:46:52,04:24:03 +30,M,01:50:44,04:24:03 +47,M,01:52:33,04:24:03 +37,M,02:00:01,04:24:04 +36,M,01:58:07,04:24:04 +32,M,01:51:12,04:24:04 +25,M,02:02:58,04:24:04 +45,W,02:01:21,04:24:04 +43,M,01:55:46,04:24:05 +53,M,01:50:22,04:24:05 +65,M,01:56:41,04:24:05 +51,M,01:55:34,04:24:05 +47,M,01:56:05,04:24:05 +38,M,02:07:38,04:24:05 +29,W,01:57:16,04:24:06 +40,M,01:58:35,04:24:06 +37,M,01:55:23,04:24:06 +44,M,01:57:57,04:24:06 +33,W,01:56:26,04:24:07 +51,M,01:55:05,04:24:07 +25,M,01:57:24,04:24:07 +40,M,01:53:04,04:24:07 +34,M,01:51:24,04:24:07 +43,M,01:51:51,04:24:08 +59,M,01:59:18,04:24:08 +46,M,02:04:44,04:24:08 +45,M,01:58:11,04:24:08 +36,M,01:56:42,04:24:09 +71,M,01:55:56,04:24:09 +39,W,01:55:24,04:24:09 +30,M,01:48:48,04:24:09 +42,W,01:56:16,04:24:09 +40,M,01:51:12,04:24:10 +50,M,01:54:33,04:24:10 +39,M,01:57:50,04:24:10 +34,M,02:00:45,04:24:11 +54,M,01:48:18,04:24:11 +28,W,01:46:42,04:24:12 +31,M,01:59:58,04:24:12 +27,W,01:56:59,04:24:12 +56,M,01:49:05,04:24:12 +51,M,01:56:30,04:24:12 +40,M,01:50:48,04:24:12 +26,M,01:53:57,04:24:13 +37,M,01:50:09,04:24:13 +44,M,02:02:52,04:24:13 +45,W,01:59:23,04:24:13 +44,M,01:55:44,04:24:13 +43,W,02:00:50,04:24:13 +43,W,02:02:39,04:24:13 +28,W,01:53:13,04:24:14 +37,M,01:56:40,04:24:15 +61,M,02:01:06,04:24:15 +49,M,01:53:57,04:24:15 +31,M,01:54:34,04:24:15 +49,M,01:47:53,04:24:15 +27,W,01:57:51,04:24:16 +33,M,01:57:59,04:24:17 +41,M,01:50:34,04:24:17 +57,M,01:58:19,04:24:18 +42,M,01:44:34,04:24:19 +48,M,01:53:41,04:24:19 +25,M,01:55:33,04:24:19 +48,W,01:59:55,04:24:19 +30,M,02:02:57,04:24:19 +41,W,01:59:20,04:24:21 +45,M,01:56:31,04:24:21 +31,W,01:54:03,04:24:22 +32,M,01:59:34,04:24:22 +39,M,01:55:11,04:24:22 +32,M,01:58:28,04:24:22 +36,M,01:54:19,04:24:23 +31,M,01:55:46,04:24:23 +46,M,01:50:20,04:24:23 +53,W,01:57:41,04:24:24 +25,W,01:54:19,04:24:24 +31,W,01:56:58,04:24:24 +34,M,01:53:22,04:24:24 +38,M,01:51:25,04:24:24 +44,M,02:02:02,04:24:25 +44,M,01:58:16,04:24:25 +30,M,01:59:34,04:24:26 +43,M,01:55:39,04:24:26 +34,W,01:55:49,04:24:27 +36,W,02:07:20,04:24:27 +60,M,01:51:20,04:24:27 +42,M,01:54:54,04:24:27 +52,W,01:56:32,04:24:27 +46,M,01:56:03,04:24:27 +41,W,01:57:31,04:24:27 +53,M,01:47:03,04:24:27 +39,W,02:00:19,04:24:28 +26,M,02:00:32,04:24:28 +44,M,01:58:01,04:24:28 +40,M,01:52:10,04:24:28 +27,W,01:56:48,04:24:29 +35,W,01:59:34,04:24:29 +49,W,01:57:51,04:24:30 +46,M,01:42:50,04:24:30 +42,W,01:59:09,04:24:30 +34,M,01:57:54,04:24:32 +42,M,01:59:10,04:24:32 +37,M,01:59:43,04:24:33 +49,M,01:55:39,04:24:33 +61,M,01:50:14,04:24:33 +39,M,01:52:13,04:24:34 +45,M,01:54:31,04:24:34 +41,M,02:00:33,04:24:34 +36,M,01:56:51,04:24:34 +41,W,01:54:08,04:24:34 +50,M,01:58:09,04:24:35 +60,M,01:48:28,04:24:35 +32,M,01:58:30,04:24:35 +27,M,01:50:27,04:24:36 +27,M,01:32:16,04:24:36 +47,M,02:05:01,04:24:36 +47,M,01:55:40,04:24:36 +43,M,01:55:56,04:24:36 +40,M,01:55:15,04:24:36 +31,M,02:03:07,04:24:36 +27,M,02:01:40,04:24:37 +38,M,01:58:22,04:24:37 +45,M,01:50:33,04:24:37 +40,W,02:02:06,04:24:37 +35,M,01:48:22,04:24:37 +43,W,01:57:53,04:24:37 +47,M,02:08:24,04:24:37 +50,W,02:00:41,04:24:37 +37,M,02:01:55,04:24:38 +43,W,01:58:52,04:24:38 +52,M,01:57:17,04:24:38 +30,M,01:56:29,04:24:38 +39,W,02:03:24,04:24:38 +26,M,01:53:56,04:24:38 +30,W,01:53:59,04:24:39 +39,M,01:43:39,04:24:39 +38,M,01:46:19,04:24:40 +18,M,01:52:34,04:24:40 +32,W,01:53:52,04:24:40 +39,W,01:59:16,04:24:40 +47,M,02:06:41,04:24:40 +51,M,02:06:03,04:24:40 +38,M,01:58:58,04:24:40 +28,M,01:53:24,04:24:40 +45,W,01:58:24,04:24:41 +36,M,01:55:24,04:24:41 +29,W,01:59:14,04:24:41 +22,M,01:54:28,04:24:41 +29,W,01:55:09,04:24:42 +61,M,01:58:27,04:24:42 +56,M,01:55:40,04:24:42 +41,M,01:57:02,04:24:42 +33,M,01:51:44,04:24:42 +47,M,02:01:11,04:24:42 +38,M,01:56:45,04:24:42 +42,M,01:56:16,04:24:43 +53,M,01:57:02,04:24:43 +33,M,01:56:26,04:24:43 +42,W,01:44:42,04:24:43 +41,W,01:52:23,04:24:44 +44,M,01:57:30,04:24:44 +34,M,01:56:36,04:24:44 +47,W,01:57:49,04:24:45 +25,W,02:00:20,04:24:45 +50,W,01:54:57,04:24:45 +48,M,01:53:52,04:24:45 +35,W,01:52:17,04:24:46 +39,M,02:03:06,04:24:46 +22,M,01:55:57,04:24:46 +36,M,01:59:03,04:24:46 +50,M,01:55:53,04:24:47 +34,W,02:01:29,04:24:47 +48,M,01:59:03,04:24:47 +39,M,01:56:06,04:24:48 +40,W,01:52:57,04:24:48 +40,W,01:57:48,04:24:48 +37,M,01:57:19,04:24:49 +49,W,01:53:46,04:24:49 +37,W,01:55:08,04:24:49 +37,W,01:57:02,04:24:49 +51,M,01:57:13,04:24:49 +43,M,01:52:01,04:24:50 +31,W,01:56:52,04:24:50 +33,W,02:03:43,04:24:51 +44,M,01:53:24,04:24:51 +29,M,01:56:37,04:24:51 +26,W,01:51:57,04:24:51 +52,M,01:45:17,04:24:51 +36,W,02:01:45,04:24:51 +57,W,01:53:14,04:24:52 +26,M,01:58:27,04:24:52 +39,W,01:58:19,04:24:52 +51,M,01:47:38,04:24:53 +27,W,01:58:44,04:24:53 +34,W,01:56:04,04:24:53 +35,W,02:05:09,04:24:53 +45,W,01:57:44,04:24:54 +26,M,01:55:58,04:24:54 +31,M,01:55:32,04:24:54 +37,W,01:56:58,04:24:54 +39,M,01:49:45,04:24:55 +47,W,01:55:40,04:24:55 +34,M,01:55:45,04:24:56 +44,M,02:03:01,04:24:56 +40,W,01:53:34,04:24:57 +48,M,01:59:56,04:24:57 +42,M,01:53:41,04:24:57 +52,M,01:58:02,04:24:57 +46,M,01:58:44,04:24:57 +40,M,01:48:28,04:24:57 +36,M,01:57:03,04:24:58 +27,M,01:47:11,04:24:58 +50,M,02:02:55,04:24:58 +45,M,01:55:48,04:24:58 +43,M,01:41:36,04:24:59 +32,W,01:44:59,04:24:59 +36,M,02:03:42,04:25:00 +19,M,02:03:31,04:25:00 +49,M,01:57:23,04:25:00 +47,M,01:49:00,04:25:00 +33,W,02:04:38,04:25:00 +33,M,01:53:23,04:25:01 +37,W,02:00:45,04:25:01 +40,M,02:01:43,04:25:01 +51,W,01:57:26,04:25:01 +59,M,02:01:34,04:25:02 +50,M,02:00:04,04:25:02 +39,M,02:00:13,04:25:02 +35,M,02:00:10,04:25:02 +37,M,02:01:38,04:25:02 +63,M,01:56:31,04:25:03 +31,M,01:48:57,04:25:03 +46,M,02:11:49,04:25:03 +52,M,02:02:14,04:25:03 +40,M,02:02:58,04:25:03 +50,M,02:00:01,04:25:04 +34,M,01:59:35,04:25:04 +42,M,01:51:41,04:25:04 +42,M,01:53:15,04:25:05 +57,M,01:59:30,04:25:05 +32,M,01:54:05,04:25:05 +60,M,02:03:25,04:25:06 +33,W,01:58:03,04:25:06 +47,M,01:53:19,04:25:06 +36,M,02:04:12,04:25:06 +45,W,01:52:33,04:25:07 +36,M,01:58:06,04:25:07 +32,W,02:01:26,04:25:08 +29,M,01:56:05,04:25:08 +41,M,01:53:34,04:25:08 +38,W,02:01:38,04:25:09 +50,M,01:56:22,04:25:09 +39,M,01:54:56,04:25:09 +39,M,01:54:25,04:25:09 +29,M,01:57:52,04:25:09 +49,M,01:53:14,04:25:10 +49,W,01:58:54,04:25:10 +44,M,02:02:55,04:25:10 +37,M,01:54:38,04:25:10 +58,W,01:57:07,04:25:10 +33,M,01:50:29,04:25:10 +30,W,01:54:16,04:25:11 +31,W,02:00:34,04:25:11 +45,M,01:58:31,04:25:12 +37,W,01:56:42,04:25:12 +51,W,01:58:57,04:25:12 +58,M,02:02:43,04:25:12 +59,W,01:58:48,04:25:12 +41,M,01:52:44,04:25:12 +34,M,01:58:11,04:25:12 +38,W,01:57:50,04:25:13 +49,M,01:49:37,04:25:14 +34,M,01:53:35,04:25:14 +31,M,01:49:21,04:25:14 +51,M,02:04:11,04:25:14 +29,W,01:55:24,04:25:14 +42,M,01:58:06,04:25:15 +32,W,01:48:37,04:25:15 +41,M,01:56:41,04:25:16 +28,M,01:55:56,04:25:16 +44,M,01:54:10,04:25:16 +28,M,01:45:04,04:25:16 +45,M,01:47:41,04:25:16 +28,W,01:57:39,04:25:16 +39,M,01:56:40,04:25:16 +40,M,01:53:40,04:25:16 +34,M,01:58:28,04:25:16 +47,M,01:56:04,04:25:17 +25,M,02:02:54,04:25:17 +45,M,01:55:08,04:25:18 +31,W,01:52:23,04:25:19 +36,W,01:57:21,04:25:19 +43,W,02:04:05,04:25:19 +38,M,01:52:04,04:25:19 +36,W,01:58:04,04:25:19 +27,M,01:59:47,04:25:19 +44,W,02:02:03,04:25:20 +56,W,01:57:46,04:25:20 +43,M,01:56:19,04:25:20 +44,M,01:57:45,04:25:20 +37,W,02:01:43,04:25:21 +43,W,02:01:27,04:25:21 +32,W,01:54:51,04:25:21 +44,M,01:56:26,04:25:21 +32,M,01:56:28,04:25:21 +27,W,01:55:05,04:25:22 +29,M,01:50:26,04:25:22 +36,W,01:49:13,04:25:22 +23,W,01:57:34,04:25:23 +41,W,01:57:06,04:25:23 +58,M,02:06:03,04:25:23 +42,M,01:51:20,04:25:23 +27,M,01:54:35,04:25:24 +58,M,01:52:07,04:25:24 +31,W,02:00:39,04:25:25 +29,W,01:59:57,04:25:25 +35,M,01:57:35,04:25:25 +43,M,01:47:46,04:25:25 +28,M,01:58:28,04:25:25 +32,M,01:58:08,04:25:25 +27,W,01:59:13,04:25:26 +54,M,01:59:00,04:25:26 +23,M,01:58:51,04:25:27 +48,M,01:42:25,04:25:27 +36,W,01:58:03,04:25:27 +30,M,01:49:50,04:25:27 +44,M,01:52:01,04:25:28 +25,M,01:51:39,04:25:28 +27,W,02:15:14,04:25:28 +35,M,01:58:09,04:25:28 +27,M,01:58:03,04:25:29 +21,W,01:51:43,04:25:29 +40,W,01:58:33,04:25:30 +41,M,01:54:49,04:25:30 +39,M,01:50:37,04:25:31 +34,W,02:00:06,04:25:31 +31,W,01:50:44,04:25:31 +46,M,01:57:31,04:25:31 +34,W,02:02:26,04:25:31 +47,W,02:01:53,04:25:32 +46,W,02:01:07,04:25:32 +48,W,01:57:12,04:25:32 +35,W,02:00:51,04:25:33 +28,M,01:55:57,04:25:33 +26,W,01:59:34,04:25:33 +49,M,02:01:52,04:25:33 +31,M,01:59:17,04:25:33 +43,M,01:53:56,04:25:33 +49,M,01:50:01,04:25:33 +29,W,01:58:39,04:25:34 +44,M,01:58:15,04:25:34 +33,M,02:00:07,04:25:34 +44,M,02:01:20,04:25:34 +31,W,02:01:13,04:25:35 +60,W,01:55:17,04:25:35 +28,W,01:59:25,04:25:35 +58,M,01:58:10,04:25:35 +46,M,01:54:12,04:25:35 +36,M,01:55:42,04:25:35 +40,M,01:51:51,04:25:36 +39,W,01:55:16,04:25:36 +30,M,01:55:47,04:25:36 +40,M,01:56:02,04:25:36 +40,M,01:58:24,04:25:37 +45,M,01:56:36,04:25:38 +42,W,02:03:11,04:25:38 +50,M,01:58:25,04:25:39 +46,W,01:59:37,04:25:39 +39,M,01:59:37,04:25:39 +38,M,02:03:49,04:25:39 +35,M,01:59:39,04:25:39 +24,M,01:58:07,04:25:39 +25,W,02:00:56,04:25:40 +31,W,01:57:24,04:25:40 +39,W,02:00:22,04:25:40 +42,W,01:58:17,04:25:41 +49,M,01:59:00,04:25:41 +44,W,01:54:51,04:25:41 +46,W,02:04:03,04:25:41 +37,M,01:53:02,04:25:41 +37,M,01:59:38,04:25:42 +39,W,01:56:34,04:25:42 +23,M,02:02:13,04:25:43 +31,W,01:59:24,04:25:43 +26,W,01:59:25,04:25:43 +43,M,01:55:11,04:25:43 +31,M,01:54:36,04:25:43 +43,M,01:53:28,04:25:44 +31,M,01:43:29,04:25:44 +54,M,01:56:29,04:25:45 +44,M,01:57:40,04:25:46 +49,M,01:47:39,04:25:47 +50,W,01:59:04,04:25:47 +24,W,01:57:27,04:25:47 +48,W,02:03:54,04:25:48 +43,W,02:01:22,04:25:48 +27,W,02:02:00,04:25:48 +49,W,02:03:32,04:25:48 +41,M,02:06:51,04:25:48 +30,W,02:00:01,04:25:48 +49,W,01:54:23,04:25:48 +50,M,01:50:20,04:25:49 +51,M,02:04:32,04:25:51 +40,M,01:49:47,04:25:51 +38,M,02:01:56,04:25:52 +42,M,01:52:19,04:25:53 +33,M,01:38:21,04:25:53 +57,M,01:59:01,04:25:54 +45,M,02:01:59,04:25:54 +23,M,01:52:41,04:25:54 +34,W,01:57:26,04:25:55 +19,M,01:51:24,04:25:56 +33,M,01:56:39,04:25:57 +44,W,02:04:16,04:25:57 +24,M,02:01:30,04:25:57 +53,M,01:57:04,04:25:57 +35,M,02:03:47,04:25:58 +39,M,02:03:03,04:25:58 +34,M,01:54:45,04:25:59 +42,M,01:57:04,04:25:59 +48,W,01:56:13,04:25:59 +43,M,02:01:43,04:26:00 +39,M,02:00:19,04:26:00 +37,W,01:57:01,04:26:00 +29,W,01:46:48,04:26:00 +46,W,02:01:50,04:26:00 +47,M,01:51:48,04:26:01 +22,M,02:01:48,04:26:01 +44,M,01:59:27,04:26:01 +32,M,01:57:46,04:26:02 +45,M,02:03:23,04:26:02 +35,M,01:53:48,04:26:02 +40,M,02:00:22,04:26:02 +46,W,01:51:41,04:26:03 +34,M,01:44:13,04:26:03 +51,M,01:54:39,04:26:03 +47,W,01:57:29,04:26:04 +25,M,01:56:55,04:26:04 +57,M,02:01:07,04:26:05 +40,M,01:48:00,04:26:05 +55,W,01:56:28,04:26:05 +39,W,01:56:09,04:26:05 +30,W,02:04:13,04:26:05 +27,M,01:53:11,04:26:06 +31,M,01:57:00,04:26:06 +27,M,01:55:46,04:26:06 +49,M,01:59:44,04:26:06 +26,W,01:52:01,04:26:06 +60,M,01:57:54,04:26:06 +46,M,02:01:59,04:26:06 +28,M,01:56:08,04:26:06 +28,W,02:01:27,04:26:07 +41,M,01:47:27,04:26:08 +34,M,01:49:59,04:26:08 +31,M,01:57:45,04:26:08 +52,M,01:58:43,04:26:08 +30,M,02:07:36,04:26:08 +30,W,01:57:10,04:26:08 +41,M,02:04:07,04:26:11 +44,M,01:55:38,04:26:11 +41,W,01:51:01,04:26:11 +42,M,01:47:17,04:26:12 +39,M,01:54:55,04:26:12 +48,M,02:00:17,04:26:12 +33,M,01:58:19,04:26:12 +29,W,02:01:19,04:26:12 +33,M,01:54:44,04:26:13 +50,M,02:00:54,04:26:13 +54,M,01:53:13,04:26:13 +39,M,01:59:39,04:26:13 +44,M,01:53:30,04:26:13 +49,M,01:51:35,04:26:14 +42,W,01:58:48,04:26:14 +50,M,02:01:44,04:26:14 +47,M,01:55:40,04:26:14 +34,W,02:03:15,04:26:15 +44,M,01:49:46,04:26:15 +41,M,02:02:13,04:26:15 +50,M,01:55:12,04:26:15 +57,M,01:53:56,04:26:15 +39,M,02:02:49,04:26:16 +36,M,01:59:28,04:26:16 +38,W,01:40:07,04:26:17 +48,W,01:51:50,04:26:17 +39,M,01:59:54,04:26:18 +48,M,02:07:11,04:26:18 +55,W,01:58:38,04:26:19 +37,M,01:51:43,04:26:19 +29,W,01:56:33,04:26:19 +34,W,02:04:07,04:26:19 +36,W,01:55:12,04:26:19 +42,M,02:00:02,04:26:20 +41,M,01:58:26,04:26:20 +41,M,02:01:33,04:26:20 +31,M,01:59:33,04:26:20 +40,M,02:00:31,04:26:20 +28,W,01:50:07,04:26:21 +38,W,02:00:36,04:26:21 +59,M,01:51:09,04:26:21 +30,W,01:50:12,04:26:21 +43,M,02:04:08,04:26:21 +66,M,01:53:28,04:26:22 +40,W,01:56:03,04:26:22 +45,W,01:59:48,04:26:22 +36,M,01:58:30,04:26:23 +30,W,01:50:18,04:26:23 +41,M,01:48:12,04:26:24 +27,M,01:47:48,04:26:25 +46,M,01:58:45,04:26:25 +38,M,01:53:16,04:26:25 +40,W,02:00:15,04:26:26 +41,M,01:54:11,04:26:26 +52,M,01:53:37,04:26:26 +40,M,02:03:27,04:26:26 +40,W,01:56:37,04:26:27 +45,M,01:55:04,04:26:27 +41,M,01:57:18,04:26:27 +31,W,01:55:41,04:26:27 +41,M,01:51:20,04:26:28 +31,W,02:03:50,04:26:28 +44,W,02:04:38,04:26:28 +29,M,01:59:26,04:26:28 +49,M,02:02:00,04:26:29 +45,W,01:59:12,04:26:30 +37,M,01:58:30,04:26:30 +41,M,01:43:59,04:26:30 +40,M,01:58:17,04:26:30 +45,M,01:59:50,04:26:30 +29,W,01:50:30,04:26:30 +40,W,01:58:54,04:26:31 +44,M,01:52:48,04:26:31 +33,M,01:58:25,04:26:31 +46,M,02:00:10,04:26:31 +30,M,01:56:08,04:26:32 +41,M,02:03:34,04:26:32 +31,W,01:55:20,04:26:33 +43,M,01:51:37,04:26:33 +26,M,01:45:40,04:26:33 +49,M,01:59:01,04:26:33 +30,M,01:55:18,04:26:33 +43,M,01:53:20,04:26:33 +31,M,01:54:17,04:26:33 +32,W,02:02:35,04:26:33 +42,M,01:48:52,04:26:34 +35,M,02:04:18,04:26:34 +40,M,01:54:24,04:26:35 +50,M,02:00:13,04:26:35 +49,W,01:51:52,04:26:35 +31,M,01:59:14,04:26:36 +58,M,01:50:55,04:26:36 +35,M,01:41:07,04:26:37 +35,W,02:00:51,04:26:37 +57,M,01:52:21,04:26:37 +37,W,01:59:49,04:26:37 +43,M,02:06:23,04:26:37 +31,M,01:57:19,04:26:38 +25,W,01:57:13,04:26:38 +50,M,01:56:13,04:26:38 +29,W,01:58:01,04:26:38 +27,M,01:58:05,04:26:39 +49,M,01:57:55,04:26:39 +43,M,01:48:01,04:26:39 +27,M,01:57:47,04:26:39 +46,M,01:54:45,04:26:39 +46,M,01:58:10,04:26:39 +20,M,01:49:14,04:26:39 +40,M,01:45:51,04:26:39 +53,W,02:00:57,04:26:40 +40,M,02:02:37,04:26:40 +26,W,01:59:37,04:26:41 +72,M,01:58:27,04:26:41 +37,M,01:59:29,04:26:41 +48,M,01:58:15,04:26:41 +30,W,02:03:05,04:26:41 +53,M,01:57:22,04:26:42 +28,M,02:00:16,04:26:42 +49,M,02:08:30,04:26:42 +38,W,01:53:35,04:26:42 +30,M,01:58:23,04:26:43 +27,M,01:51:06,04:26:43 +36,M,01:56:15,04:26:43 +45,M,01:51:40,04:26:43 +57,W,02:04:38,04:26:43 +41,M,01:47:22,04:26:43 +38,W,02:01:58,04:26:43 +55,W,02:00:05,04:26:43 +44,M,01:56:14,04:26:43 +48,M,02:02:56,04:26:44 +27,M,01:51:47,04:26:44 +27,M,01:58:33,04:26:45 +41,M,01:51:05,04:26:45 +45,M,01:48:07,04:26:45 +52,M,01:59:43,04:26:45 +42,M,01:56:01,04:26:45 +47,M,01:55:38,04:26:46 +46,W,01:56:40,04:26:46 +61,M,01:55:10,04:26:46 +57,M,01:46:29,04:26:47 +48,M,02:02:57,04:26:47 +51,W,01:59:19,04:26:47 +38,M,01:54:34,04:26:47 +33,W,01:58:31,04:26:47 +51,W,01:56:55,04:26:47 +50,M,01:49:04,04:26:47 +39,M,01:56:04,04:26:48 +31,M,01:55:50,04:26:48 +40,W,01:56:36,04:26:49 +17,M,01:53:21,04:26:49 +43,M,01:56:28,04:26:49 +35,M,01:53:16,04:26:49 +47,W,01:53:34,04:26:49 +62,M,02:00:28,04:26:50 +28,W,02:01:46,04:26:50 +49,M,02:03:42,04:26:51 +34,M,01:55:06,04:26:51 +55,W,02:00:43,04:26:51 +35,M,01:51:19,04:26:51 +29,W,01:57:29,04:26:52 +48,M,01:55:33,04:26:52 +45,M,02:01:31,04:26:52 +39,W,01:59:33,04:26:52 +26,W,01:52:15,04:26:52 +33,W,02:00:47,04:26:52 +47,W,02:01:05,04:26:52 +31,W,01:55:41,04:26:52 +35,W,02:02:15,04:26:52 +28,M,01:47:09,04:26:52 +37,W,02:03:03,04:26:53 +51,M,01:57:44,04:26:53 +37,M,01:53:48,04:26:53 +46,M,01:54:10,04:26:53 +38,M,01:50:26,04:26:53 +29,W,01:56:29,04:26:54 +41,M,02:03:03,04:26:54 +40,W,01:55:32,04:26:54 +47,W,02:01:37,04:26:54 +34,W,01:59:06,04:26:54 +41,W,01:58:15,04:26:54 +35,M,01:54:27,04:26:55 +28,M,01:58:14,04:26:56 +39,W,01:53:47,04:26:56 +68,M,02:03:55,04:26:56 +50,M,01:48:18,04:26:57 +30,W,01:56:54,04:26:57 +49,M,01:56:04,04:26:57 +49,M,01:53:09,04:26:57 +33,M,01:42:19,04:26:57 +49,M,02:01:44,04:26:57 +32,W,01:54:46,04:26:58 +43,M,01:54:11,04:26:58 +30,W,01:56:40,04:26:59 +29,W,02:02:33,04:26:59 +41,M,01:52:43,04:26:59 +38,M,01:51:43,04:27:00 +42,W,01:57:26,04:27:00 +47,M,01:51:19,04:27:00 +47,M,01:54:58,04:27:01 +19,W,01:57:46,04:27:01 +36,W,02:01:28,04:27:02 +44,M,02:03:18,04:27:02 +49,M,01:58:21,04:27:02 +28,M,01:50:19,04:27:02 +37,M,01:59:37,04:27:02 +44,M,01:53:02,04:27:03 +30,W,01:59:11,04:27:03 +48,W,01:59:54,04:27:03 +47,M,01:48:04,04:27:03 +46,M,01:56:25,04:27:03 +28,W,01:54:40,04:27:03 +29,W,01:59:08,04:27:03 +39,M,01:56:01,04:27:03 +42,M,01:58:58,04:27:03 +57,M,01:57:34,04:27:04 +30,M,02:01:14,04:27:04 +36,M,02:01:03,04:27:05 +46,M,01:57:36,04:27:05 +38,M,01:56:20,04:27:05 +36,M,01:54:40,04:27:06 +36,M,01:40:50,04:27:06 +38,M,02:00:13,04:27:06 +47,M,01:54:37,04:27:07 +40,W,01:58:55,04:27:07 +42,M,01:53:08,04:27:07 +49,M,01:58:07,04:27:07 +42,M,02:03:04,04:27:07 +47,M,01:53:28,04:27:07 +28,M,01:53:12,04:27:07 +27,W,02:01:06,04:27:07 +32,W,01:59:44,04:27:08 +37,W,01:50:47,04:27:08 +47,M,01:51:29,04:27:08 +54,M,01:59:34,04:27:08 +46,W,02:00:34,04:27:08 +38,W,01:54:16,04:27:09 +45,M,01:48:16,04:27:09 +42,M,01:58:17,04:27:10 +36,W,02:03:25,04:27:10 +47,M,02:00:01,04:27:11 +33,M,01:58:15,04:27:12 +48,M,01:58:47,04:27:12 +36,W,02:02:43,04:27:12 +35,M,02:01:53,04:27:12 +51,M,01:57:00,04:27:13 +33,W,01:56:07,04:27:13 +50,M,01:57:06,04:27:13 +30,M,01:53:10,04:27:13 +26,M,02:03:47,04:27:14 +27,W,01:54:31,04:27:14 +36,W,01:56:55,04:27:14 +46,W,02:01:21,04:27:14 +37,M,02:00:05,04:27:16 +53,M,01:45:43,04:27:16 +28,W,01:59:11,04:27:16 +47,M,01:58:52,04:27:16 +37,M,01:57:55,04:27:17 +45,M,01:51:21,04:27:17 +53,M,01:59:58,04:27:18 +61,W,01:58:39,04:27:18 +60,M,01:57:04,04:27:18 +58,M,01:58:57,04:27:18 +38,M,01:54:57,04:27:18 +35,M,02:02:02,04:27:19 +39,M,01:53:48,04:27:19 +29,W,01:52:14,04:27:19 +50,M,01:55:44,04:27:19 +37,M,02:05:13,04:27:19 +45,M,01:54:33,04:27:19 +45,W,02:01:12,04:27:19 +40,M,01:51:34,04:27:20 +41,M,01:50:41,04:27:21 +46,W,01:50:47,04:27:21 +58,M,02:04:05,04:27:21 +59,M,01:55:42,04:27:22 +26,M,01:52:47,04:27:22 +25,W,01:58:00,04:27:22 +29,M,02:04:54,04:27:22 +42,W,02:02:59,04:27:22 +40,M,02:02:41,04:27:22 +50,W,02:00:44,04:27:22 +46,M,02:00:18,04:27:23 +46,M,01:51:56,04:27:23 +32,M,01:48:18,04:27:23 +44,W,01:55:07,04:27:23 +29,M,01:52:14,04:27:23 +30,M,01:54:39,04:27:23 +43,M,01:59:59,04:27:23 +44,M,02:05:38,04:27:23 +42,W,01:57:35,04:27:24 +40,M,01:51:15,04:27:24 +43,M,01:51:24,04:27:24 +25,M,01:49:03,04:27:24 +49,M,01:57:04,04:27:24 +57,W,02:04:10,04:27:25 +54,M,01:56:53,04:27:25 +45,M,02:04:10,04:27:26 +26,W,01:53:53,04:27:26 +50,M,01:55:40,04:27:26 +43,M,02:00:54,04:27:26 +26,W,01:59:34,04:27:26 +32,M,02:02:37,04:27:26 +51,M,01:57:47,04:27:26 +39,M,01:50:25,04:27:27 +33,W,01:56:48,04:27:27 +42,M,01:59:27,04:27:28 +58,M,01:45:44,04:27:28 +30,M,01:55:42,04:27:28 +30,W,01:58:59,04:27:29 +46,M,01:57:16,04:27:29 +36,M,01:54:43,04:27:29 +35,M,01:52:58,04:27:30 +40,M,01:58:03,04:27:30 +40,M,01:54:09,04:27:30 +45,M,01:49:52,04:27:30 +35,M,01:58:22,04:27:30 +33,M,01:55:00,04:27:31 +27,W,01:58:08,04:27:31 +42,M,01:56:50,04:27:31 +58,M,02:00:31,04:27:32 +62,M,01:57:03,04:27:32 +28,M,02:02:40,04:27:32 +31,M,01:49:46,04:27:32 +31,W,01:52:03,04:27:33 +26,M,01:57:40,04:27:34 +45,M,01:51:29,04:27:34 +38,W,02:00:05,04:27:34 +41,M,01:57:36,04:27:34 +40,M,01:54:26,04:27:34 +36,M,02:04:06,04:27:35 +29,M,01:55:18,04:27:35 +50,M,02:03:46,04:27:35 +43,M,01:56:18,04:27:36 +58,M,01:57:38,04:27:36 +46,M,02:00:48,04:27:36 +43,W,01:58:48,04:27:36 +42,W,01:53:57,04:27:36 +50,M,01:55:39,04:27:37 +42,W,01:59:31,04:27:37 +47,M,01:50:49,04:27:37 +27,M,01:54:26,04:27:37 +41,M,01:59:17,04:27:37 +45,W,01:57:07,04:27:37 +45,M,01:50:28,04:27:37 +30,W,01:55:04,04:27:37 +42,M,01:54:52,04:27:38 +67,M,01:57:41,04:27:38 +52,M,01:59:44,04:27:38 +45,M,01:34:07,04:27:39 +44,M,01:51:59,04:27:39 +49,M,01:50:33,04:27:39 +27,W,01:56:34,04:27:39 +52,W,01:59:50,04:27:39 +45,M,01:41:23,04:27:40 +62,M,01:55:45,04:27:40 +52,M,01:58:30,04:27:40 +34,W,01:53:28,04:27:41 +47,M,01:51:52,04:27:41 +49,M,01:59:16,04:27:41 +38,W,01:59:16,04:27:42 +54,M,02:01:38,04:27:43 +53,M,01:56:50,04:27:43 +49,M,02:01:27,04:27:43 +24,M,01:48:06,04:27:43 +39,W,02:00:46,04:27:44 +46,M,01:57:40,04:27:44 +38,M,01:55:30,04:27:44 +43,M,02:01:06,04:27:45 +46,M,01:56:02,04:27:45 +35,W,01:59:10,04:27:45 +26,W,01:57:54,04:27:45 +48,W,02:00:16,04:27:46 +35,M,01:55:29,04:27:46 +26,W,01:57:40,04:27:47 +32,M,02:01:08,04:27:47 +35,M,02:01:25,04:27:47 +31,M,01:57:06,04:27:47 +48,M,01:56:53,04:27:48 +44,M,01:52:51,04:27:48 +30,M,01:59:53,04:27:48 +31,M,01:50:18,04:27:48 +28,W,01:50:53,04:27:48 +28,W,02:03:21,04:27:48 +28,W,01:56:06,04:27:48 +49,M,01:57:48,04:27:49 +54,M,01:55:51,04:27:50 +43,M,01:58:46,04:27:50 +39,M,02:02:01,04:27:50 +40,M,02:03:04,04:27:50 +38,M,01:48:05,04:27:50 +40,W,02:00:44,04:27:50 +35,M,01:58:27,04:27:50 +27,M,01:45:01,04:27:51 +52,M,02:07:29,04:27:51 +38,M,01:50:18,04:27:51 +49,M,01:51:48,04:27:51 +27,W,01:52:50,04:27:52 +33,W,01:52:54,04:27:52 +32,M,01:51:31,04:27:52 +47,M,01:56:19,04:27:52 +59,M,02:14:07,04:27:53 +24,M,01:56:25,04:27:53 +31,M,02:01:43,04:27:54 +32,M,01:53:26,04:27:54 +33,W,01:57:45,04:27:54 +27,M,01:57:40,04:27:55 +46,M,01:49:35,04:27:55 +33,W,01:55:09,04:27:55 +42,M,01:58:11,04:27:55 +50,M,02:07:56,04:27:56 +51,M,01:42:48,04:27:56 +54,M,01:55:09,04:27:56 +25,M,01:47:14,04:27:56 +47,M,02:00:48,04:27:57 +47,W,02:00:35,04:27:57 +41,W,02:01:20,04:27:57 +53,M,02:04:32,04:27:57 +26,M,01:59:57,04:27:58 +52,M,01:49:03,04:27:58 +27,W,02:03:20,04:27:58 +48,M,01:58:21,04:27:58 +45,M,02:02:47,04:27:58 +36,M,01:59:27,04:27:59 +39,M,02:00:32,04:27:59 +40,M,02:02:16,04:27:59 +41,M,01:53:31,04:27:59 +40,M,02:02:28,04:27:59 +36,M,01:57:49,04:27:59 +23,M,01:59:41,04:28:00 +26,W,01:53:20,04:28:00 +60,M,02:00:35,04:28:00 +49,M,01:50:31,04:28:00 +18,M,01:44:56,04:28:00 +49,M,01:57:20,04:28:01 +40,M,01:55:24,04:28:01 +25,M,01:44:44,04:28:01 +46,M,01:55:34,04:28:01 +42,M,01:57:45,04:28:02 +35,W,01:59:13,04:28:02 +46,M,01:58:49,04:28:02 +45,W,02:02:22,04:28:02 +24,M,01:43:52,04:28:02 +43,M,01:49:38,04:28:03 +26,M,01:56:44,04:28:03 +54,M,01:58:41,04:28:03 +34,M,02:00:10,04:28:03 +36,M,01:52:22,04:28:04 +64,M,01:57:47,04:28:04 +43,M,01:44:57,04:28:04 +44,M,01:47:42,04:28:05 +42,W,01:50:43,04:28:05 +45,M,02:01:52,04:28:06 +32,M,01:49:15,04:28:06 +49,M,02:05:45,04:28:06 +44,W,01:57:45,04:28:06 +40,W,01:53:50,04:28:07 +33,M,01:46:53,04:28:07 +55,M,02:07:43,04:28:07 +33,W,01:55:17,04:28:07 +24,W,01:56:32,04:28:07 +43,M,01:56:08,04:28:08 +42,W,02:01:00,04:28:08 +31,M,01:50:19,04:28:08 +57,M,02:03:51,04:28:08 +46,W,01:59:55,04:28:08 +37,W,01:56:00,04:28:09 +56,M,01:56:22,04:28:10 +27,M,01:56:05,04:28:10 +35,M,02:05:46,04:28:10 +51,W,01:58:33,04:28:11 +48,W,01:54:59,04:28:11 +30,W,02:02:37,04:28:11 +38,M,01:55:57,04:28:12 +37,M,01:58:06,04:28:12 +56,M,02:04:33,04:28:13 +29,M,01:54:10,04:28:13 +41,W,01:59:07,04:28:13 +32,M,01:57:01,04:28:13 +38,M,01:50:54,04:28:14 +44,M,02:13:35,04:28:14 +39,W,01:59:17,04:28:14 +36,W,01:46:35,04:28:14 +42,M,01:47:53,04:28:15 +50,W,02:01:37,04:28:15 +23,M,01:54:05,04:28:15 +37,W,02:01:17,04:28:15 +31,W,02:01:26,04:28:16 +37,M,01:54:36,04:28:16 +48,M,01:54:52,04:28:17 +30,M,01:57:08,04:28:17 +31,M,01:59:17,04:28:17 +25,M,01:56:49,04:28:17 +45,W,01:59:04,04:28:17 +45,M,02:00:43,04:28:17 +31,W,01:56:56,04:28:18 +29,M,01:53:59,04:28:18 +29,W,01:55:15,04:28:18 +49,M,02:00:09,04:28:18 +44,W,02:02:35,04:28:18 +44,M,02:06:22,04:28:18 +22,M,01:59:23,04:28:19 +44,M,01:53:49,04:28:19 +39,W,01:59:51,04:28:19 +36,M,01:54:12,04:28:19 +40,W,02:01:51,04:28:19 +45,W,02:07:01,04:28:19 +31,M,01:46:23,04:28:19 +40,M,01:50:17,04:28:20 +38,W,02:04:18,04:28:20 +45,W,02:01:02,04:28:20 +53,M,01:55:28,04:28:20 +45,W,02:01:18,04:28:20 +24,M,01:47:05,04:28:20 +54,M,01:55:39,04:28:20 +36,M,02:04:01,04:28:21 +19,M,01:54:43,04:28:21 +38,M,01:59:44,04:28:21 +55,M,01:54:20,04:28:22 +41,M,01:57:17,04:28:22 +51,M,01:50:34,04:28:23 +44,M,01:53:51,04:28:23 +56,M,01:57:11,04:28:23 +46,W,01:59:11,04:28:23 +43,M,02:13:25,04:28:23 +49,M,01:54:15,04:28:24 +43,M,01:59:57,04:28:25 +46,M,01:46:21,04:28:26 +30,W,01:56:16,04:28:26 +35,M,01:57:59,04:28:26 +67,M,02:09:01,04:28:26 +45,W,02:00:56,04:28:26 +38,M,01:52:39,04:28:27 +41,M,02:07:22,04:28:28 +49,M,01:58:44,04:28:29 +48,M,01:59:27,04:28:29 +38,M,02:01:13,04:28:29 +48,M,01:58:00,04:28:29 +51,M,01:55:00,04:28:29 +56,M,02:06:09,04:28:30 +31,M,02:13:26,04:28:30 +52,W,02:00:21,04:28:30 +52,M,01:58:33,04:28:30 +50,W,01:58:11,04:28:31 +32,M,02:10:49,04:28:31 +54,M,01:57:51,04:28:32 +43,W,01:54:56,04:28:32 +38,M,02:07:53,04:28:32 +34,M,01:53:44,04:28:33 +34,M,01:50:19,04:28:33 +62,M,01:58:20,04:28:33 +32,M,01:53:43,04:28:33 +70,M,01:50:55,04:28:34 +57,M,02:04:41,04:28:34 +52,W,01:54:13,04:28:34 +28,M,01:52:52,04:28:34 +46,M,01:59:06,04:28:35 +39,M,02:06:00,04:28:35 +43,M,01:53:42,04:28:35 +57,M,01:53:56,04:28:35 +38,M,01:55:19,04:28:35 +31,W,01:53:58,04:28:35 +35,W,01:58:22,04:28:35 +41,M,01:55:40,04:28:36 +44,M,01:51:38,04:28:36 +40,M,01:53:08,04:28:36 +23,W,01:56:03,04:28:36 +49,M,02:00:38,04:28:37 +40,M,01:50:58,04:28:37 +33,M,01:49:12,04:28:37 +47,W,02:02:10,04:28:37 +50,M,01:58:49,04:28:38 +37,W,01:59:21,04:28:38 +50,M,01:51:27,04:28:38 +40,M,01:51:07,04:28:39 +53,M,02:03:16,04:28:39 +29,M,01:59:09,04:28:40 +51,M,02:04:32,04:28:40 +35,W,01:52:49,04:28:40 +38,M,02:08:15,04:28:40 +42,M,02:10:40,04:28:40 +49,M,01:56:49,04:28:41 +36,M,01:57:07,04:28:41 +47,M,02:02:15,04:28:41 +76,W,01:59:42,04:28:41 +52,M,02:01:50,04:28:41 +26,M,01:59:59,04:28:41 +43,M,01:58:06,04:28:42 +51,W,02:00:05,04:28:42 +23,M,01:59:17,04:28:42 +44,M,01:51:02,04:28:43 +41,W,02:02:38,04:28:43 +41,M,01:50:31,04:28:43 +45,M,01:56:45,04:28:43 +44,M,02:01:10,04:28:43 +50,M,01:53:15,04:28:43 +62,M,01:57:55,04:28:44 +40,M,02:00:12,04:28:44 +28,M,01:54:39,04:28:45 +39,W,01:56:48,04:28:46 +39,M,01:53:20,04:28:46 +52,M,01:57:17,04:28:46 +26,W,02:00:17,04:28:46 +28,W,01:54:47,04:28:46 +46,M,01:58:22,04:28:46 +34,W,01:59:59,04:28:46 +44,M,02:02:26,04:28:46 +30,M,01:57:41,04:28:47 +41,W,01:54:56,04:28:47 +23,W,01:52:27,04:28:48 +50,M,01:56:36,04:28:48 +43,M,02:01:47,04:28:48 +40,M,01:53:08,04:28:48 +27,M,01:56:14,04:28:49 +46,W,01:54:59,04:28:49 +39,M,01:57:40,04:28:50 +41,M,01:52:35,04:28:51 +29,W,01:56:30,04:28:51 +29,W,01:49:43,04:28:52 +33,W,01:56:34,04:28:52 +29,W,02:05:40,04:28:52 +52,M,01:55:51,04:28:53 +31,W,01:54:51,04:28:53 +36,M,01:57:03,04:28:54 +46,M,01:59:37,04:28:54 +46,M,02:03:41,04:28:54 +36,M,01:55:47,04:28:55 +27,W,01:51:06,04:28:55 +30,M,02:06:56,04:28:55 +48,M,02:00:33,04:28:55 +25,W,02:00:06,04:28:56 +42,M,01:55:58,04:28:56 +27,W,01:59:13,04:28:56 +49,M,01:57:42,04:28:56 +45,W,02:01:26,04:28:56 +34,M,01:52:28,04:28:57 +37,M,01:52:15,04:28:57 +49,M,02:02:32,04:28:57 +35,W,02:00:45,04:28:57 +33,W,02:01:29,04:28:57 +23,W,01:51:07,04:28:57 +32,M,02:00:03,04:28:57 +40,M,01:52:10,04:28:58 +46,W,01:56:46,04:28:58 +51,M,01:54:35,04:28:59 +58,M,02:02:52,04:28:59 +33,W,01:51:36,04:29:00 +32,M,01:52:40,04:29:00 +46,W,01:56:47,04:29:00 +63,M,02:02:54,04:29:00 +39,M,01:56:19,04:29:00 +41,M,01:47:01,04:29:00 +37,W,02:01:02,04:29:01 +28,W,01:59:14,04:29:01 +43,M,01:46:21,04:29:01 +31,M,01:58:41,04:29:01 +59,W,01:59:47,04:29:02 +27,W,02:02:54,04:29:02 +42,M,02:09:08,04:29:02 +22,W,01:57:20,04:29:03 +55,M,02:04:30,04:29:03 +41,W,02:04:01,04:29:03 +52,M,01:54:45,04:29:03 +45,W,01:51:35,04:29:04 +50,W,01:55:08,04:29:04 +41,M,01:44:49,04:29:04 +58,M,01:57:42,04:29:04 +17,M,01:43:53,04:29:05 +33,M,01:50:31,04:29:06 +23,M,01:49:59,04:29:06 +28,W,02:05:22,04:29:06 +56,W,01:57:20,04:29:06 +50,M,01:58:05,04:29:07 +40,M,01:45:34,04:29:08 +34,M,02:06:24,04:29:08 +32,W,01:43:42,04:29:08 +43,M,01:58:06,04:29:09 +45,W,01:57:06,04:29:09 +54,M,01:52:11,04:29:09 +49,M,02:12:58,04:29:09 +34,M,01:59:09,04:29:09 +52,M,01:56:53,04:29:10 +40,M,01:41:01,04:29:10 +25,M,01:51:12,04:29:10 +41,W,02:04:55,04:29:11 +50,M,02:04:46,04:29:12 +31,W,01:53:09,04:29:12 +29,M,01:54:39,04:29:12 +38,M,01:59:47,04:29:13 +38,M,02:00:30,04:29:13 +26,W,01:52:25,04:29:13 +46,M,02:00:53,04:29:13 +29,W,02:02:55,04:29:13 +27,W,01:55:20,04:29:13 +25,M,01:58:08,04:29:14 +25,M,02:00:32,04:29:14 +40,M,01:54:52,04:29:14 +32,M,01:51:44,04:29:14 +39,M,01:56:33,04:29:14 +56,M,02:00:38,04:29:14 +53,M,02:00:00,04:29:14 +36,M,02:00:41,04:29:15 +57,M,01:46:48,04:29:15 +30,M,01:58:10,04:29:15 +30,M,01:56:20,04:29:15 +42,M,01:59:53,04:29:16 +25,M,02:04:57,04:29:16 +29,W,01:58:02,04:29:17 +28,M,01:45:46,04:29:18 +32,M,02:04:35,04:29:18 +54,W,01:56:01,04:29:18 +27,M,01:53:58,04:29:18 +32,M,02:13:10,04:29:19 +35,W,01:51:07,04:29:19 +32,W,01:58:05,04:29:19 +33,W,02:01:13,04:29:20 +30,M,01:59:27,04:29:20 +45,M,01:55:53,04:29:20 +34,M,01:59:04,04:29:20 +31,M,01:48:58,04:29:20 +32,M,01:53:39,04:29:20 +39,M,01:56:35,04:29:20 +41,M,02:00:37,04:29:21 +39,M,02:06:54,04:29:22 +33,M,01:50:30,04:29:22 +49,M,02:01:05,04:29:22 +45,W,01:56:58,04:29:23 +53,M,02:00:12,04:29:23 +30,M,01:57:20,04:29:24 +25,M,01:44:39,04:29:24 +34,M,01:46:11,04:29:25 +54,M,01:56:40,04:29:25 +54,M,01:56:25,04:29:25 +43,W,01:58:07,04:29:25 +60,M,01:51:45,04:29:25 +52,M,02:01:40,04:29:25 +60,M,02:05:37,04:29:26 +36,W,01:57:09,04:29:26 +34,M,02:08:50,04:29:26 +38,M,02:03:00,04:29:26 +32,M,02:09:03,04:29:26 +43,W,01:58:10,04:29:27 +25,W,01:58:51,04:29:27 +29,M,01:53:35,04:29:28 +43,M,02:00:05,04:29:29 +30,W,01:57:22,04:29:29 +47,M,01:52:26,04:29:30 +48,M,01:59:48,04:29:30 +49,M,01:53:51,04:29:30 +50,M,02:05:35,04:29:30 +44,M,02:06:34,04:29:30 +42,M,01:55:55,04:29:30 +32,W,02:03:21,04:29:31 +28,W,01:58:28,04:29:31 +42,M,01:59:39,04:29:32 +21,W,02:00:21,04:29:32 +31,M,02:02:39,04:29:32 +44,W,01:58:22,04:29:34 +36,W,01:59:34,04:29:35 +33,M,01:46:51,04:29:35 +30,M,02:03:50,04:29:35 +42,M,01:46:09,04:29:35 +49,W,01:55:24,04:29:36 +28,M,01:55:40,04:29:36 +28,W,02:01:10,04:29:36 +22,M,01:48:24,04:29:36 +47,M,02:00:55,04:29:36 +32,M,01:58:34,04:29:37 +49,M,02:01:38,04:29:37 +47,M,01:51:20,04:29:37 +32,M,01:53:49,04:29:38 +39,M,01:53:48,04:29:38 +42,M,01:52:05,04:29:38 +46,M,01:53:51,04:29:39 +42,M,02:05:39,04:29:39 +28,M,01:49:52,04:29:39 +33,M,01:47:22,04:29:39 +38,M,02:00:19,04:29:39 +45,M,02:02:23,04:29:40 +29,W,02:00:22,04:29:40 +36,M,01:55:58,04:29:40 +45,M,01:51:07,04:29:41 +48,W,02:00:20,04:29:41 +23,M,01:53:10,04:29:41 +26,M,01:56:32,04:29:41 +41,M,01:57:21,04:29:42 +50,M,01:56:30,04:29:43 +33,M,01:48:08,04:29:44 +33,M,01:52:08,04:29:44 +30,M,01:47:56,04:29:44 +35,M,02:12:16,04:29:45 +49,W,01:58:56,04:29:45 +43,M,01:56:43,04:29:45 +27,W,01:53:54,04:29:45 +48,M,01:56:43,04:29:45 +28,W,01:56:41,04:29:46 +24,W,02:00:02,04:29:47 +35,M,02:05:57,04:29:47 +39,M,02:11:52,04:29:47 +28,M,01:52:18,04:29:49 +39,M,02:00:38,04:29:49 +30,W,01:53:01,04:29:49 +41,M,01:58:15,04:29:49 +37,M,01:57:21,04:29:50 +53,M,02:02:01,04:29:51 +26,W,01:59:06,04:29:51 +44,M,01:53:33,04:29:51 +47,M,01:48:23,04:29:51 +34,W,02:01:21,04:29:51 +44,M,01:51:10,04:29:52 +54,W,01:57:19,04:29:52 +40,M,01:51:25,04:29:53 +33,M,01:51:37,04:29:53 +54,M,01:54:05,04:29:53 +38,M,01:57:13,04:29:53 +36,M,01:55:00,04:29:54 +43,M,01:53:55,04:29:54 +39,M,01:57:10,04:29:55 +39,M,01:53:08,04:29:55 +32,M,01:56:47,04:29:55 +49,M,01:58:29,04:29:56 +57,M,01:51:11,04:29:56 +33,M,01:55:41,04:29:57 +53,M,01:52:49,04:29:58 +52,M,01:59:07,04:29:58 +35,M,01:57:24,04:29:58 +35,M,01:56:13,04:29:58 +40,W,01:55:59,04:29:59 +60,M,01:53:23,04:29:59 +32,M,01:54:14,04:29:59 +30,M,01:48:18,04:29:59 +43,M,01:53:18,04:29:59 +47,M,01:53:36,04:29:59 +44,M,01:58:59,04:30:00 +43,W,02:00:30,04:30:01 +42,W,02:00:08,04:30:01 +46,M,01:59:39,04:30:01 +24,W,02:03:10,04:30:02 +54,M,01:58:28,04:30:02 +36,W,01:52:35,04:30:02 +45,M,01:54:22,04:30:03 +30,W,02:00:38,04:30:03 +48,M,02:00:55,04:30:04 +43,M,01:54:12,04:30:04 +25,M,01:43:10,04:30:05 +49,M,01:59:55,04:30:05 +27,W,01:59:44,04:30:05 +26,M,01:58:25,04:30:05 +40,M,01:58:31,04:30:06 +24,M,02:03:02,04:30:06 +35,M,02:02:46,04:30:06 +63,M,01:58:42,04:30:06 +57,M,02:04:00,04:30:07 +49,M,01:53:31,04:30:07 +54,W,02:00:27,04:30:07 +39,M,02:07:55,04:30:07 +53,M,01:59:24,04:30:08 +53,W,01:58:02,04:30:08 +36,W,01:56:22,04:30:08 +41,M,01:56:30,04:30:08 +39,M,01:56:06,04:30:08 +30,M,01:41:14,04:30:08 +26,W,02:02:32,04:30:08 +50,M,01:58:51,04:30:08 +41,W,01:59:12,04:30:08 +47,W,02:07:56,04:30:09 +49,W,01:52:36,04:30:10 +40,W,01:54:58,04:30:10 +48,M,01:50:57,04:30:10 +48,W,02:00:07,04:30:10 +38,M,01:51:25,04:30:10 +46,W,01:56:26,04:30:10 +45,M,02:05:38,04:30:10 +37,M,02:02:59,04:30:11 +22,M,01:59:43,04:30:11 +38,W,02:02:33,04:30:11 +53,M,01:50:59,04:30:11 +40,M,01:59:51,04:30:11 +51,M,01:50:45,04:30:12 +46,M,02:02:42,04:30:12 +45,M,01:52:51,04:30:13 +34,M,02:00:51,04:30:13 +50,M,01:52:27,04:30:13 +26,W,02:01:07,04:30:13 +33,W,01:56:43,04:30:14 +48,M,02:01:28,04:30:14 +45,M,02:03:38,04:30:14 +48,M,01:57:16,04:30:14 +50,M,02:01:49,04:30:14 +32,M,02:00:27,04:30:14 +34,W,02:00:28,04:30:14 +33,M,02:04:22,04:30:15 +29,M,02:01:20,04:30:15 +52,W,02:05:14,04:30:15 +54,M,02:09:19,04:30:15 +36,M,01:53:18,04:30:16 +34,M,01:57:10,04:30:16 +44,M,01:54:47,04:30:16 +44,W,01:52:32,04:30:17 +36,W,01:57:46,04:30:17 +38,W,01:59:32,04:30:17 +42,W,01:58:47,04:30:17 +27,M,02:04:33,04:30:17 +40,M,01:56:32,04:30:18 +30,M,02:04:51,04:30:19 +53,W,02:06:38,04:30:19 +37,W,02:01:32,04:30:19 +34,W,02:04:23,04:30:19 +49,W,01:52:57,04:30:20 +51,M,01:57:55,04:30:20 +29,W,01:58:31,04:30:20 +36,M,01:55:58,04:30:21 +49,M,01:57:14,04:30:21 +24,M,01:59:16,04:30:21 +49,M,01:58:44,04:30:22 +31,M,01:50:31,04:30:22 +46,W,01:59:05,04:30:22 +33,M,01:56:24,04:30:23 +23,M,01:39:15,04:30:23 +50,W,01:58:16,04:30:23 +57,M,01:59:09,04:30:23 +49,M,01:54:07,04:30:23 +29,W,02:03:28,04:30:24 +55,M,01:57:33,04:30:24 +38,W,02:06:59,04:30:25 +34,M,01:54:10,04:30:25 +54,M,01:54:29,04:30:25 +29,M,02:14:25,04:30:25 +35,M,02:00:14,04:30:26 +49,M,01:54:41,04:30:27 +55,W,01:58:18,04:30:27 +49,M,01:44:19,04:30:27 +52,M,01:52:11,04:30:28 +36,M,01:58:10,04:30:28 +49,M,01:53:33,04:30:28 +40,W,01:59:47,04:30:29 +22,W,01:50:43,04:30:29 +25,M,01:48:28,04:30:30 +53,M,02:00:07,04:30:30 +44,W,01:58:06,04:30:31 +43,W,02:02:25,04:30:31 +39,M,02:01:12,04:30:31 +29,M,01:48:20,04:30:31 +43,M,01:53:39,04:30:31 +35,W,02:01:27,04:30:31 +32,M,01:55:22,04:30:32 +42,M,02:00:51,04:30:32 +23,M,01:55:26,04:30:32 +44,M,01:55:31,04:30:33 +34,M,02:07:46,04:30:34 +41,M,02:07:36,04:30:34 +46,M,01:58:46,04:30:34 +43,M,01:58:32,04:30:34 +38,M,01:50:53,04:30:35 +49,M,01:55:56,04:30:36 +53,M,02:00:23,04:30:36 +39,M,02:07:52,04:30:36 +32,M,01:57:38,04:30:37 +47,M,02:02:48,04:30:37 +34,W,01:59:12,04:30:37 +47,W,01:57:55,04:30:37 +36,W,01:59:06,04:30:37 +50,W,02:00:10,04:30:37 +22,W,02:00:56,04:30:38 +50,M,01:49:05,04:30:38 +42,M,02:01:20,04:30:38 +42,M,02:00:36,04:30:38 +52,W,01:58:30,04:30:39 +31,M,01:53:34,04:30:39 +39,W,02:00:36,04:30:39 +64,M,01:53:40,04:30:40 +35,M,02:00:24,04:30:41 +32,M,01:54:26,04:30:41 +26,W,02:02:49,04:30:41 +38,M,02:04:24,04:30:41 +43,W,01:59:17,04:30:41 +46,W,02:01:41,04:30:41 +22,M,02:06:38,04:30:42 +40,M,01:53:57,04:30:42 +54,M,01:54:06,04:30:42 +24,W,02:03:19,04:30:42 +33,M,01:56:50,04:30:43 +32,W,02:04:42,04:30:43 +54,M,01:55:56,04:30:43 +44,M,01:59:07,04:30:43 +34,M,01:44:00,04:30:43 +41,W,01:57:53,04:30:43 +29,W,02:02:23,04:30:44 +38,M,01:48:50,04:30:44 +27,W,01:56:02,04:30:45 +40,W,02:00:09,04:30:45 +28,W,02:06:32,04:30:45 +34,M,01:56:57,04:30:45 +33,M,01:54:02,04:30:45 +30,W,02:08:48,04:30:45 +45,W,02:02:18,04:30:45 +47,M,01:52:36,04:30:46 +30,W,01:57:59,04:30:47 +46,M,01:42:42,04:30:47 +34,M,01:58:54,04:30:47 +37,M,01:58:33,04:30:48 +27,W,02:09:40,04:30:49 +36,M,01:53:34,04:30:49 +55,M,01:59:21,04:30:50 +44,W,02:04:08,04:30:50 +20,M,01:49:59,04:30:52 +35,M,01:38:53,04:30:52 +35,M,02:13:57,04:30:52 +44,M,01:45:04,04:30:52 +31,M,01:57:34,04:30:52 +28,M,01:56:20,04:30:52 +33,W,01:52:18,04:30:52 +44,W,02:01:31,04:30:52 +41,M,01:58:17,04:30:53 +50,M,02:13:28,04:30:53 +39,W,02:00:25,04:30:53 +37,M,01:58:31,04:30:53 +51,M,01:53:35,04:30:55 +31,M,01:58:37,04:30:56 +32,M,01:50:47,04:30:56 +35,W,01:51:28,04:30:56 +54,M,01:51:07,04:30:57 +37,M,02:02:00,04:30:57 +39,M,02:00:53,04:30:57 +31,M,02:01:54,04:30:58 +28,W,01:59:07,04:30:58 +34,W,01:49:55,04:30:58 +33,W,01:53:40,04:30:59 +31,M,01:51:26,04:31:00 +44,M,02:00:01,04:31:00 +36,M,02:04:14,04:31:00 +29,M,01:42:10,04:31:00 +57,M,01:59:02,04:31:00 +39,M,02:00:09,04:31:01 +57,M,01:54:43,04:31:01 +41,M,01:54:56,04:31:02 +42,W,01:59:31,04:31:02 +33,M,02:01:59,04:31:02 +39,M,01:47:23,04:31:02 +43,M,01:47:47,04:31:02 +38,M,01:55:36,04:31:02 +52,M,01:59:42,04:31:02 +39,M,01:51:02,04:31:02 +26,M,02:02:40,04:31:03 +28,M,01:46:47,04:31:03 +27,W,02:10:27,04:31:03 +31,W,01:58:03,04:31:04 +30,W,02:05:29,04:31:04 +59,M,01:53:20,04:31:04 +19,W,01:57:03,04:31:04 +32,M,01:59:41,04:31:05 +31,M,02:00:09,04:31:05 +28,W,02:08:56,04:31:05 +45,M,02:03:12,04:31:05 +55,M,02:01:09,04:31:05 +47,M,01:52:23,04:31:05 +45,M,01:58:28,04:31:06 +28,M,01:56:04,04:31:06 +48,M,01:46:48,04:31:06 +34,M,01:47:02,04:31:07 +35,M,01:56:40,04:31:07 +30,W,01:59:50,04:31:07 +44,M,01:58:39,04:31:07 +55,W,02:08:50,04:31:07 +27,W,02:00:47,04:31:08 +53,W,01:59:14,04:31:08 +37,M,02:05:55,04:31:08 +34,M,01:54:13,04:31:08 +31,M,01:49:52,04:31:08 +62,M,02:01:20,04:31:08 +59,M,01:55:50,04:31:08 +52,M,01:57:36,04:31:09 +36,M,02:00:17,04:31:09 +34,M,01:41:42,04:31:10 +52,M,01:54:10,04:31:10 +37,W,01:56:50,04:31:10 +34,M,01:48:16,04:31:11 +26,M,01:57:55,04:31:11 +33,W,02:02:51,04:31:11 +28,M,02:12:34,04:31:11 +46,M,01:53:02,04:31:12 +55,M,01:54:07,04:31:12 +40,M,01:57:23,04:31:12 +59,W,02:03:00,04:31:13 +30,M,02:05:36,04:31:13 +26,M,02:00:00,04:31:14 +60,M,01:58:45,04:31:14 +42,W,02:01:49,04:31:14 +55,M,02:03:39,04:31:15 +38,W,01:54:42,04:31:15 +33,M,01:59:00,04:31:15 +54,M,01:58:01,04:31:15 +47,M,01:59:08,04:31:15 +30,M,01:49:47,04:31:15 +40,M,01:59:22,04:31:16 +44,M,01:55:31,04:31:16 +37,W,02:00:18,04:31:16 +23,W,02:04:45,04:31:16 +31,M,01:49:46,04:31:17 +55,M,01:50:17,04:31:17 +46,M,01:58:00,04:31:17 +49,M,01:52:46,04:31:17 +31,W,01:53:57,04:31:18 +47,M,01:58:12,04:31:18 +23,W,02:09:53,04:31:18 +26,W,01:57:50,04:31:18 +29,M,01:49:38,04:31:18 +43,W,01:53:28,04:31:18 +61,M,01:58:24,04:31:19 +49,M,01:59:19,04:31:20 +63,M,01:53:25,04:31:20 +28,W,02:03:49,04:31:20 +44,M,01:54:39,04:31:21 +32,M,01:48:29,04:31:21 +46,M,01:59:43,04:31:22 +42,M,01:54:14,04:31:22 +29,W,01:59:29,04:31:23 +42,M,01:43:08,04:31:24 +40,M,02:00:03,04:31:24 +31,W,01:57:12,04:31:24 +29,W,01:55:26,04:31:24 +43,W,01:54:32,04:31:25 +27,W,02:09:01,04:31:25 +33,M,01:53:53,04:31:26 +28,W,01:54:45,04:31:26 +58,M,01:55:30,04:31:26 +34,M,02:04:40,04:31:27 +34,M,01:59:03,04:31:27 +38,M,02:01:37,04:31:27 +28,M,01:55:01,04:31:27 +46,M,01:58:36,04:31:27 +53,W,02:03:42,04:31:28 +52,M,01:54:11,04:31:28 +26,M,01:57:14,04:31:28 +38,W,01:57:09,04:31:28 +36,M,01:54:23,04:31:28 +46,M,02:06:42,04:31:28 +37,M,01:54:47,04:31:29 +41,M,01:44:19,04:31:29 +46,M,02:03:01,04:31:30 +47,M,01:53:34,04:31:30 +48,W,02:06:13,04:31:30 +33,W,02:04:42,04:31:30 +31,W,01:55:33,04:31:30 +49,M,01:57:08,04:31:30 +30,M,01:59:05,04:31:30 +30,M,02:00:25,04:31:30 +26,W,01:56:57,04:31:31 +43,M,01:39:26,04:31:31 +32,M,02:00:04,04:31:32 +27,M,02:01:26,04:31:32 +30,W,01:57:01,04:31:32 +46,M,01:53:55,04:31:33 +52,M,01:58:15,04:31:33 +29,M,02:03:48,04:31:33 +45,W,02:01:24,04:31:34 +35,W,02:03:56,04:31:34 +37,W,02:05:15,04:31:34 +33,W,02:03:36,04:31:34 +38,W,02:04:40,04:31:34 +41,M,01:54:28,04:31:35 +35,M,01:56:56,04:31:35 +54,W,01:58:24,04:31:35 +48,M,02:00:51,04:31:35 +39,M,01:54:08,04:31:37 +30,W,01:50:23,04:31:37 +52,M,02:00:19,04:31:37 +43,M,02:08:45,04:31:37 +34,M,01:51:57,04:31:37 +58,W,01:59:34,04:31:38 +26,M,01:49:43,04:31:38 +32,M,02:06:30,04:31:39 +35,M,02:05:05,04:31:39 +50,W,02:00:32,04:31:40 +37,M,01:54:28,04:31:40 +36,W,02:06:01,04:31:41 +45,M,01:57:30,04:31:42 +48,M,01:52:00,04:31:42 +58,M,01:57:27,04:31:42 +43,W,02:03:06,04:31:42 +34,M,01:59:37,04:31:42 +39,W,02:04:32,04:31:42 +33,W,01:59:22,04:31:43 +46,M,01:56:36,04:31:44 +35,M,01:58:03,04:31:44 +33,M,01:59:12,04:31:44 +46,M,01:49:29,04:31:44 +34,M,01:54:16,04:31:44 +26,W,01:54:47,04:31:45 +20,M,01:57:57,04:31:45 +46,W,02:02:21,04:31:45 +31,M,02:03:11,04:31:45 +51,W,02:02:07,04:31:46 +44,M,02:01:02,04:31:46 +35,M,01:59:30,04:31:46 +52,M,01:59:41,04:31:46 +37,M,01:52:07,04:31:47 +29,M,01:55:55,04:31:47 +42,M,02:01:02,04:31:48 +44,W,01:58:29,04:31:48 +31,W,02:00:14,04:31:49 +56,M,01:47:01,04:31:49 +40,W,01:52:57,04:31:50 +49,M,01:53:31,04:31:50 +54,M,02:03:44,04:31:50 +30,M,01:55:16,04:31:50 +40,M,02:00:50,04:31:50 +47,W,01:59:30,04:31:50 +27,M,02:02:21,04:31:50 +51,M,02:02:02,04:31:51 +31,W,02:00:30,04:31:51 +29,M,01:48:10,04:31:51 +29,W,01:47:25,04:31:52 +28,M,01:54:36,04:31:52 +61,M,02:00:41,04:31:52 +52,M,01:57:26,04:31:52 +35,M,01:53:39,04:31:52 +31,W,02:04:19,04:31:52 +40,M,01:56:10,04:31:52 +39,W,02:07:05,04:31:52 +56,M,02:00:11,04:31:53 +30,W,01:55:31,04:31:53 +24,W,02:01:25,04:31:53 +33,W,01:57:40,04:31:54 +42,M,01:58:30,04:31:54 +54,M,01:57:15,04:31:54 +45,M,01:59:39,04:31:54 +43,W,01:53:09,04:31:54 +46,M,01:54:53,04:31:54 +33,M,02:02:58,04:31:54 +43,M,02:00:36,04:31:54 +44,M,01:59:10,04:31:54 +36,W,02:02:13,04:31:55 +51,M,02:01:30,04:31:55 +49,W,01:57:55,04:31:55 +52,M,01:56:48,04:31:55 +58,M,02:00:23,04:31:56 +51,W,02:00:48,04:31:56 +43,M,01:59:23,04:31:56 +39,M,01:50:51,04:31:56 +38,W,01:56:40,04:31:56 +61,M,01:56:47,04:31:56 +34,W,02:04:56,04:31:57 +38,W,02:04:01,04:31:57 +32,M,01:51:43,04:31:57 +25,W,01:52:39,04:31:57 +41,M,01:58:43,04:31:58 +32,M,01:57:00,04:31:58 +24,W,01:59:49,04:31:59 +32,W,02:04:20,04:32:00 +42,M,01:50:01,04:32:00 +56,W,01:59:14,04:32:00 +31,W,02:00:44,04:32:00 +24,M,02:05:33,04:32:00 +52,W,02:00:09,04:32:01 +30,M,02:00:07,04:32:01 +37,M,01:56:06,04:32:01 +47,M,01:49:39,04:32:02 +42,W,01:52:51,04:32:02 +33,M,01:54:10,04:32:02 +50,M,01:49:32,04:32:02 +67,M,01:59:16,04:32:03 +33,M,01:48:47,04:32:03 +31,W,01:54:37,04:32:03 +46,M,01:49:39,04:32:03 +54,M,01:55:04,04:32:04 +48,M,02:03:46,04:32:04 +39,W,01:59:17,04:32:05 +26,M,02:09:36,04:32:05 +46,M,01:49:13,04:32:05 +26,W,02:05:08,04:32:05 +59,M,02:01:08,04:32:05 +36,M,01:24:13,04:32:06 +43,W,01:59:20,04:32:06 +30,M,01:53:54,04:32:06 +30,W,02:03:45,04:32:06 +45,M,01:44:57,04:32:06 +31,M,01:47:28,04:32:07 +52,W,02:03:05,04:32:07 +35,M,02:04:10,04:32:07 +51,W,02:05:42,04:32:07 +33,M,01:56:03,04:32:07 +32,M,01:56:36,04:32:08 +41,M,01:51:07,04:32:08 +43,M,02:02:14,04:32:08 +44,M,02:00:36,04:32:08 +28,W,01:59:32,04:32:08 +28,W,02:12:12,04:32:09 +40,W,01:58:37,04:32:09 +39,M,01:57:40,04:32:09 +35,W,01:59:01,04:32:09 +36,W,01:53:49,04:32:09 +36,W,01:42:35,04:32:10 +26,W,01:56:54,04:32:10 +41,M,01:57:00,04:32:10 +30,M,01:54:25,04:32:11 +30,M,01:54:02,04:32:11 +59,M,02:04:19,04:32:11 +41,W,01:57:38,04:32:11 +40,M,01:53:01,04:32:11 +40,M,01:58:28,04:32:11 +33,W,01:59:08,04:32:11 +60,M,02:04:40,04:32:12 +44,M,02:00:30,04:32:12 +31,M,01:54:41,04:32:12 +49,M,01:56:44,04:32:12 +51,M,01:52:20,04:32:12 +42,M,01:40:31,04:32:12 +36,M,01:50:32,04:32:12 +34,W,02:03:41,04:32:13 +30,M,01:57:29,04:32:13 +49,M,01:57:07,04:32:13 +34,M,01:56:01,04:32:14 +41,M,01:59:51,04:32:16 +23,W,01:55:28,04:32:17 +25,M,01:50:28,04:32:17 +42,W,01:51:18,04:32:17 +40,M,01:55:26,04:32:17 +47,W,01:59:58,04:32:17 +56,M,02:00:59,04:32:17 +45,W,02:05:34,04:32:18 +37,M,01:56:06,04:32:18 +47,W,02:00:51,04:32:19 +34,M,02:04:49,04:32:20 +40,W,01:59:59,04:32:20 +22,M,01:58:50,04:32:20 +30,M,01:51:01,04:32:20 +34,W,01:47:58,04:32:21 +40,W,02:05:27,04:32:21 +52,M,01:50:20,04:32:21 +50,M,02:01:28,04:32:22 +42,M,01:54:42,04:32:22 +23,M,01:43:58,04:32:22 +45,M,02:01:44,04:32:22 +47,M,01:57:56,04:32:23 +31,M,02:01:07,04:32:23 +48,M,01:54:14,04:32:23 +64,M,01:59:59,04:32:23 +42,W,02:06:22,04:32:23 +41,M,01:52:14,04:32:24 +23,W,02:12:03,04:32:24 +27,W,02:00:42,04:32:24 +39,M,02:05:48,04:32:24 +24,M,01:46:35,04:32:24 +35,M,02:01:36,04:32:25 +28,W,01:56:12,04:32:25 +18,M,02:03:40,04:32:25 +43,W,02:01:41,04:32:25 +44,W,02:03:41,04:32:26 +29,W,02:05:08,04:32:26 +35,M,02:06:47,04:32:26 +33,M,01:54:18,04:32:26 +55,M,01:52:01,04:32:26 +54,M,01:52:32,04:32:26 +28,W,01:58:37,04:32:26 +42,M,01:56:45,04:32:27 +32,M,01:50:04,04:32:27 +53,M,01:51:30,04:32:27 +47,M,01:51:44,04:32:27 +46,M,01:49:48,04:32:28 +27,M,01:48:25,04:32:28 +49,M,01:54:34,04:32:29 +46,M,01:53:40,04:32:29 +37,W,02:08:35,04:32:30 +27,W,01:52:11,04:32:30 +35,M,01:52:12,04:32:30 +49,M,01:51:41,04:32:30 +36,M,01:48:58,04:32:31 +30,W,01:56:20,04:32:31 +37,M,01:59:55,04:32:31 +39,W,01:59:09,04:32:32 +39,W,01:54:54,04:32:32 +36,M,01:59:19,04:32:32 +46,M,01:53:21,04:32:32 +33,M,02:00:01,04:32:32 +48,M,01:47:21,04:32:32 +49,W,01:58:10,04:32:33 +27,M,01:58:35,04:32:33 +34,W,02:04:19,04:32:33 +36,W,02:07:17,04:32:33 +24,M,01:57:20,04:32:33 +33,W,02:02:16,04:32:33 +39,W,01:54:44,04:32:33 +37,W,02:02:39,04:32:35 +42,W,02:02:38,04:32:35 +51,M,01:57:30,04:32:36 +39,M,01:56:44,04:32:36 +47,W,01:58:12,04:32:36 +40,W,02:01:48,04:32:36 +34,W,01:59:36,04:32:37 +47,M,01:55:21,04:32:38 +39,W,02:04:44,04:32:38 +37,M,02:04:16,04:32:38 +54,M,01:49:33,04:32:38 +37,M,02:35:50,04:32:39 +37,M,02:05:39,04:32:39 +35,M,01:57:51,04:32:40 +47,M,02:01:57,04:32:40 +49,M,01:47:47,04:32:40 +27,W,01:58:39,04:32:40 +50,M,01:56:10,04:32:40 +42,W,01:58:15,04:32:40 +47,W,02:04:31,04:32:40 +44,M,02:03:27,04:32:40 +52,M,01:54:08,04:32:41 +48,M,02:10:09,04:32:41 +37,M,01:55:21,04:32:42 +29,W,02:00:41,04:32:42 +33,M,01:57:10,04:32:43 +56,M,01:55:11,04:32:43 +26,M,01:52:09,04:32:43 +26,M,01:58:13,04:32:43 +18,M,01:56:43,04:32:43 +30,W,01:54:46,04:32:44 +35,M,01:52:03,04:32:45 +51,M,01:59:20,04:32:45 +43,M,02:08:22,04:32:45 +32,M,01:58:10,04:32:45 +35,W,01:54:33,04:32:46 +37,M,01:57:12,04:32:46 +57,M,02:01:50,04:32:46 +29,M,02:00:42,04:32:46 +54,M,01:50:00,04:32:46 +26,W,02:02:31,04:32:46 +29,M,01:54:42,04:32:46 +31,M,01:58:28,04:32:46 +40,M,01:55:02,04:32:47 +50,W,01:56:23,04:32:47 +41,W,02:01:22,04:32:47 +25,W,02:00:47,04:32:47 +61,M,01:40:24,04:32:47 +42,M,01:47:20,04:32:48 +27,W,01:51:04,04:32:48 +34,W,02:00:29,04:32:48 +28,W,02:02:25,04:32:48 +19,M,01:58:19,04:32:48 +41,W,01:58:41,04:32:49 +47,M,01:53:22,04:32:49 +49,M,01:59:08,04:32:49 +24,W,02:01:58,04:32:49 +34,M,01:57:01,04:32:49 +64,M,01:56:47,04:32:50 +38,M,01:45:59,04:32:50 +34,W,01:58:00,04:32:50 +35,M,02:07:52,04:32:50 +39,W,01:50:26,04:32:50 +64,M,02:06:07,04:32:50 +29,W,02:01:22,04:32:50 +30,W,02:02:07,04:32:51 +46,M,02:01:42,04:32:51 +32,M,01:54:37,04:32:51 +58,M,01:55:54,04:32:52 +40,M,02:00:30,04:32:52 +36,M,01:47:50,04:32:52 +54,M,01:54:30,04:32:52 +32,M,01:54:03,04:32:53 +29,W,02:04:13,04:32:53 +42,W,01:56:06,04:32:54 +39,M,01:54:49,04:32:54 +39,M,01:53:47,04:32:54 +50,M,01:57:07,04:32:54 +33,M,02:04:03,04:32:55 +57,M,02:06:51,04:32:55 +43,M,01:46:35,04:32:56 +32,W,01:50:35,04:32:56 +45,M,02:05:52,04:32:57 +40,M,01:59:51,04:32:57 +28,W,02:09:50,04:32:57 +36,M,02:15:47,04:32:57 +53,M,02:00:59,04:32:57 +45,W,01:52:57,04:32:57 +45,M,01:49:38,04:32:58 +36,W,01:59:48,04:32:58 +61,M,02:05:10,04:32:58 +50,W,02:04:03,04:32:58 +32,W,02:12:23,04:32:59 +42,M,01:51:27,04:32:59 +43,M,02:00:11,04:32:59 +40,M,01:49:59,04:33:00 +54,W,02:01:23,04:33:00 +28,W,02:03:07,04:33:00 +45,M,02:06:28,04:33:00 +38,M,01:49:04,04:33:01 +44,M,01:55:56,04:33:01 +46,W,02:05:39,04:33:01 +33,M,01:53:31,04:33:01 +47,W,02:08:50,04:33:01 +47,M,01:59:33,04:33:01 +48,W,01:58:20,04:33:02 +33,M,01:58:59,04:33:02 +46,M,01:58:24,04:33:02 +61,M,01:59:31,04:33:02 +52,M,01:45:12,04:33:02 +28,M,02:05:34,04:33:03 +44,M,01:55:30,04:33:03 +50,M,01:57:58,04:33:03 +35,W,02:01:02,04:33:03 +37,M,01:54:52,04:33:03 +31,M,01:46:27,04:33:04 +31,M,01:46:35,04:33:04 +27,W,01:56:59,04:33:04 +43,M,01:55:26,04:33:04 +29,W,02:02:51,04:33:04 +30,M,01:56:06,04:33:04 +44,M,01:56:26,04:33:05 +42,M,01:58:13,04:33:05 +49,W,01:56:24,04:33:05 +42,M,02:04:17,04:33:05 +36,W,01:57:11,04:33:06 +37,M,02:00:29,04:33:06 +40,M,01:52:58,04:33:07 +29,M,01:56:34,04:33:07 +33,M,01:58:56,04:33:07 +55,W,02:00:21,04:33:07 +42,W,01:58:10,04:33:08 +23,W,01:55:43,04:33:08 +42,M,01:54:40,04:33:09 +36,M,01:59:20,04:33:09 +40,M,01:52:51,04:33:09 +37,W,02:03:01,04:33:09 +30,M,01:58:24,04:33:09 +47,W,02:04:02,04:33:09 +41,W,02:05:45,04:33:11 +38,M,02:00:07,04:33:12 +42,M,01:57:26,04:33:12 +62,M,01:56:14,04:33:12 +35,M,01:47:37,04:33:12 +30,M,02:00:11,04:33:12 +36,W,01:59:48,04:33:13 +31,W,02:01:16,04:33:13 +31,M,01:58:23,04:33:14 +51,M,01:59:40,04:33:14 +38,M,02:02:23,04:33:14 +40,W,02:01:54,04:33:14 +40,M,01:50:25,04:33:14 +44,W,02:01:50,04:33:15 +22,W,02:03:58,04:33:15 +62,M,01:57:57,04:33:15 +31,M,02:00:14,04:33:15 +54,M,01:55:53,04:33:15 +45,M,01:51:31,04:33:15 +38,W,01:53:23,04:33:15 +37,M,01:55:56,04:33:16 +56,M,02:08:24,04:33:16 +50,M,01:59:41,04:33:16 +49,M,01:56:23,04:33:17 +39,W,02:07:51,04:33:17 +31,M,02:00:50,04:33:17 +26,M,02:02:12,04:33:17 +39,M,02:01:58,04:33:17 +39,M,02:02:36,04:33:17 +48,M,02:02:56,04:33:18 +33,W,02:06:00,04:33:18 +55,M,01:47:10,04:33:18 +36,M,01:53:55,04:33:19 +50,W,02:06:00,04:33:19 +50,W,01:54:42,04:33:20 +51,M,01:55:26,04:33:20 +49,M,02:01:17,04:33:21 +36,W,02:01:05,04:33:21 +45,M,01:58:27,04:33:21 +61,M,02:01:19,04:33:21 +37,W,02:04:24,04:33:21 +65,M,01:59:07,04:33:22 +58,M,01:57:18,04:33:22 +28,W,01:52:51,04:33:22 +47,M,01:54:05,04:33:22 +51,M,02:06:40,04:33:22 +48,M,01:58:47,04:33:23 +40,W,01:48:37,04:33:23 +28,W,01:56:14,04:33:23 +24,M,01:56:16,04:33:24 +48,M,01:56:52,04:33:24 +31,M,01:55:56,04:33:24 +59,M,02:01:44,04:33:24 +31,M,01:54:49,04:33:25 +37,W,01:56:53,04:33:25 +34,M,02:01:10,04:33:25 +47,M,02:06:06,04:33:26 +34,M,02:05:15,04:33:26 +48,W,02:06:20,04:33:26 +34,M,02:04:28,04:33:26 +24,W,01:56:34,04:33:27 +55,M,01:53:08,04:33:27 +39,M,01:42:48,04:33:27 +25,M,02:05:44,04:33:27 +29,M,02:02:20,04:33:28 +39,M,02:05:55,04:33:28 +48,M,01:57:53,04:33:28 +40,M,01:53:08,04:33:29 +25,M,01:57:02,04:33:29 +39,M,02:02:09,04:33:29 +45,M,01:53:56,04:33:30 +42,M,02:09:23,04:33:30 +26,W,01:54:40,04:33:31 +34,M,01:51:29,04:33:31 +32,M,01:59:02,04:33:31 +32,M,01:57:37,04:33:31 +36,M,02:02:33,04:33:32 +47,M,02:00:04,04:33:32 +40,M,01:58:07,04:33:32 +49,M,01:55:46,04:33:32 +47,M,02:01:39,04:33:33 +45,M,01:54:35,04:33:34 +38,M,01:59:42,04:33:34 +24,M,01:47:21,04:33:34 +42,M,02:01:11,04:33:35 +56,M,01:52:54,04:33:35 +45,M,01:53:57,04:33:35 +27,W,02:00:18,04:33:36 +24,W,01:59:31,04:33:36 +35,M,01:50:24,04:33:36 +46,M,02:07:37,04:33:37 +46,M,01:52:29,04:33:38 +28,W,02:00:29,04:33:38 +30,M,01:55:03,04:33:40 +46,M,02:00:41,04:33:41 +45,M,01:58:55,04:33:42 +41,M,02:00:31,04:33:43 +36,M,01:57:03,04:33:43 +32,W,01:54:01,04:33:43 +58,M,02:07:31,04:33:43 +43,M,02:08:02,04:33:44 +56,M,02:06:03,04:33:44 +54,W,01:58:09,04:33:44 +37,M,01:58:48,04:33:45 +32,M,01:54:09,04:33:45 +26,M,01:59:52,04:33:45 +34,W,02:09:31,04:33:46 +31,M,01:59:42,04:33:46 +31,M,02:03:38,04:33:46 +33,W,01:53:58,04:33:46 +41,M,01:57:01,04:33:47 +36,M,01:55:55,04:33:47 +49,M,01:55:44,04:33:47 +32,M,01:55:25,04:33:47 +48,W,02:04:17,04:33:47 +37,M,02:07:09,04:33:48 +49,M,02:06:51,04:33:48 +47,M,02:02:46,04:33:48 +38,W,01:59:34,04:33:48 +46,M,02:01:56,04:33:49 +34,W,02:01:04,04:33:50 +40,M,02:08:39,04:33:50 +36,W,01:58:13,04:33:50 +64,M,01:54:27,04:33:51 +63,M,01:56:37,04:33:51 +28,M,02:10:07,04:33:52 +51,M,01:49:19,04:33:52 +43,W,02:00:15,04:33:52 +25,W,02:08:21,04:33:52 +44,M,02:03:15,04:33:53 +25,M,01:54:47,04:33:53 +38,W,02:04:51,04:33:53 +42,W,02:05:46,04:33:53 +42,M,01:56:16,04:33:53 +43,M,01:54:54,04:33:53 +34,W,02:08:15,04:33:53 +48,M,01:53:27,04:33:54 +32,M,01:57:47,04:33:54 +51,M,02:04:34,04:33:54 +43,M,02:00:01,04:33:54 +38,M,01:59:37,04:33:55 +32,M,01:52:20,04:33:55 +51,M,02:07:16,04:33:55 +42,M,01:47:28,04:33:55 +28,M,01:59:04,04:33:55 +38,M,02:03:05,04:33:55 +28,M,01:56:29,04:33:56 +31,M,01:55:30,04:33:56 +39,M,02:01:23,04:33:56 +34,W,02:01:25,04:33:56 +32,M,01:50:01,04:33:56 +44,M,02:08:51,04:33:57 +40,W,02:01:01,04:33:57 +25,M,02:04:00,04:33:58 +26,W,01:51:02,04:33:58 +41,W,01:58:47,04:33:59 +38,M,01:54:05,04:33:59 +29,W,01:58:21,04:33:59 +44,M,01:49:44,04:33:59 +34,W,02:05:28,04:33:59 +60,M,01:53:55,04:33:59 +43,W,02:00:53,04:34:00 +38,M,02:02:00,04:34:00 +56,M,01:56:26,04:34:00 +38,W,01:55:40,04:34:00 +43,M,02:01:31,04:34:01 +43,M,01:49:03,04:34:01 +29,M,01:46:15,04:34:01 +49,M,01:58:25,04:34:02 +23,W,02:07:48,04:34:02 +51,M,01:57:11,04:34:02 +36,W,01:59:02,04:34:02 +51,M,01:58:11,04:34:02 +29,W,01:59:39,04:34:02 +40,W,02:02:15,04:34:03 +44,W,02:00:13,04:34:03 +41,W,02:04:27,04:34:03 +46,M,01:50:38,04:34:03 +46,M,01:50:44,04:34:03 +42,W,01:52:03,04:34:04 +48,M,01:50:20,04:34:04 +26,W,02:03:05,04:34:05 +28,W,02:03:38,04:34:05 +49,M,02:02:56,04:34:05 +36,M,01:52:36,04:34:05 +25,M,01:51:14,04:34:05 +35,M,01:59:07,04:34:05 +26,M,01:51:27,04:34:05 +56,M,01:57:11,04:34:06 +37,M,01:58:03,04:34:06 +40,W,02:00:36,04:34:06 +45,M,01:56:53,04:34:07 +25,M,01:58:36,04:34:07 +48,M,01:58:04,04:34:07 +38,W,01:58:35,04:34:07 +26,W,01:57:34,04:34:07 +34,M,02:11:26,04:34:07 +40,W,02:00:07,04:34:07 +46,M,02:00:24,04:34:07 +54,W,01:57:00,04:34:08 +49,M,01:58:22,04:34:08 +42,M,02:00:45,04:34:09 +36,W,01:55:36,04:34:09 +35,M,01:52:56,04:34:09 +33,W,01:54:16,04:34:10 +49,W,02:07:40,04:34:10 +41,M,02:04:24,04:34:10 +37,M,01:58:58,04:34:10 +22,M,01:50:46,04:34:10 +51,M,01:55:40,04:34:10 +36,W,02:00:56,04:34:11 +43,M,01:55:02,04:34:11 +52,M,01:56:14,04:34:11 +27,M,01:59:03,04:34:11 +37,M,01:48:42,04:34:11 +41,M,02:02:59,04:34:11 +52,M,01:52:06,04:34:12 +30,W,02:00:53,04:34:12 +51,M,02:02:02,04:34:12 +30,M,02:03:51,04:34:13 +30,M,01:57:29,04:34:13 +39,M,01:52:52,04:34:13 +45,M,01:50:56,04:34:13 +30,W,01:57:19,04:34:14 +54,M,01:57:09,04:34:14 +41,W,02:02:48,04:34:14 +63,M,02:00:19,04:34:15 +53,M,01:53:48,04:34:16 +39,M,01:59:26,04:34:16 +27,M,02:06:55,04:34:16 +44,M,02:00:39,04:34:16 +40,W,02:02:49,04:34:17 +39,M,01:57:56,04:34:17 +43,M,01:53:13,04:34:18 +45,M,01:59:05,04:34:18 +42,W,01:58:18,04:34:19 +39,W,01:50:40,04:34:19 +38,M,02:08:50,04:34:20 +53,W,01:57:10,04:34:20 +44,W,01:55:31,04:34:20 +59,M,01:58:34,04:34:20 +30,W,01:56:44,04:34:20 +63,M,01:59:18,04:34:20 +46,W,02:00:51,04:34:21 +50,W,02:07:44,04:34:21 +29,M,01:46:39,04:34:21 +42,M,02:01:06,04:34:21 +39,M,01:49:04,04:34:22 +30,M,02:11:19,04:34:22 +33,W,02:01:54,04:34:22 +47,M,01:54:24,04:34:22 +46,W,02:03:08,04:34:22 +46,M,02:04:17,04:34:22 +34,W,02:08:26,04:34:22 +32,M,01:55:39,04:34:22 +41,W,02:01:16,04:34:22 +47,M,01:58:25,04:34:22 +31,M,02:00:33,04:34:23 +40,M,01:54:07,04:34:23 +41,M,02:00:35,04:34:23 +47,M,01:57:34,04:34:23 +41,M,01:57:01,04:34:23 +37,W,02:04:11,04:34:23 +42,M,02:00:42,04:34:23 +31,W,02:01:25,04:34:24 +50,M,01:59:06,04:34:24 +53,M,01:56:19,04:34:24 +42,M,01:44:56,04:34:25 +44,M,01:53:17,04:34:25 +55,W,02:04:02,04:34:25 +48,W,02:00:33,04:34:26 +47,M,02:05:11,04:34:26 +40,M,01:56:47,04:34:27 +30,W,01:58:01,04:34:27 +36,M,01:56:49,04:34:28 +47,M,01:56:51,04:34:28 +40,M,01:52:45,04:34:28 +33,M,02:00:46,04:34:28 +50,M,01:56:22,04:34:29 +40,M,01:44:19,04:34:29 +35,M,02:00:56,04:34:29 +50,M,01:52:29,04:34:29 +51,W,02:02:57,04:34:29 +41,W,01:58:23,04:34:30 +32,W,01:59:17,04:34:30 +60,M,02:05:53,04:34:30 +27,M,02:00:39,04:34:30 +38,M,01:58:32,04:34:30 +37,M,01:55:38,04:34:31 +28,M,01:53:02,04:34:31 +45,M,01:50:00,04:34:31 +44,M,01:49:47,04:34:31 +50,M,01:57:35,04:34:31 +44,M,01:53:56,04:34:31 +39,M,01:57:13,04:34:32 +43,M,01:59:43,04:34:32 +42,W,01:59:04,04:34:32 +51,M,01:57:54,04:34:33 +31,W,02:02:59,04:34:33 +32,M,01:49:51,04:34:33 +49,M,01:53:15,04:34:33 +48,M,01:58:38,04:34:33 +44,M,02:01:11,04:34:33 +40,W,02:04:00,04:34:33 +37,W,01:55:33,04:34:34 +36,W,02:03:26,04:34:34 +35,M,01:58:54,04:34:35 +42,M,01:56:04,04:34:35 +51,M,01:52:35,04:34:35 +28,W,02:03:27,04:34:35 +26,M,01:56:20,04:34:36 +44,M,01:56:05,04:34:36 +47,M,01:57:08,04:34:36 +46,M,01:57:28,04:34:36 +67,M,02:04:05,04:34:36 +39,W,02:00:12,04:34:37 +44,M,02:04:48,04:34:38 +49,M,01:51:59,04:34:38 +27,W,01:57:25,04:34:39 +39,M,01:46:49,04:34:39 +39,W,01:59:47,04:34:39 +51,M,02:00:05,04:34:39 +46,M,01:54:36,04:34:39 +35,M,01:47:45,04:34:39 +44,M,01:53:32,04:34:39 +32,W,01:59:22,04:34:40 +42,M,01:55:44,04:34:40 +41,M,02:00:48,04:34:40 +48,M,01:52:36,04:34:40 +35,W,02:01:20,04:34:40 +44,M,01:56:02,04:34:41 +27,M,01:53:12,04:34:41 +49,M,01:50:56,04:34:41 +47,M,01:57:05,04:34:41 +61,M,02:02:34,04:34:41 +33,W,01:55:51,04:34:41 +40,M,01:52:44,04:34:42 +28,W,01:51:57,04:34:42 +46,M,01:52:17,04:34:43 +40,M,01:48:36,04:34:43 +43,M,02:05:48,04:34:43 +51,W,02:06:47,04:34:43 +37,M,01:53:04,04:34:43 +47,M,02:06:32,04:34:43 +40,W,01:59:51,04:34:44 +37,W,02:04:43,04:34:44 +43,M,02:00:10,04:34:44 +49,M,01:58:06,04:34:44 +30,W,01:55:55,04:34:44 +30,M,02:04:47,04:34:45 +43,M,01:49:12,04:34:46 +37,M,01:59:15,04:34:46 +41,M,01:55:21,04:34:47 +38,W,01:52:11,04:34:47 +44,M,01:51:06,04:34:47 +40,M,01:57:58,04:34:47 +51,M,01:58:30,04:34:47 +42,M,02:02:08,04:34:47 +34,W,01:51:10,04:34:47 +27,W,02:08:05,04:34:47 +41,W,02:04:55,04:34:48 +38,M,01:47:04,04:34:48 +25,M,01:51:10,04:34:49 +48,M,01:55:23,04:34:49 +40,M,02:05:52,04:34:49 +27,M,02:00:28,04:34:49 +28,W,01:59:52,04:34:49 +32,W,02:01:21,04:34:50 +52,M,01:57:51,04:34:50 +40,M,01:58:25,04:34:50 +40,M,01:47:13,04:34:50 +45,W,02:00:02,04:34:50 +33,W,01:57:21,04:34:50 +39,W,01:57:30,04:34:50 +58,M,02:06:41,04:34:51 +36,W,01:57:03,04:34:51 +37,M,01:55:58,04:34:51 +36,M,01:53:43,04:34:51 +34,M,02:01:40,04:34:52 +42,M,01:59:04,04:34:52 +41,M,02:02:47,04:34:52 +37,W,01:56:58,04:34:52 +32,M,01:59:40,04:34:52 +43,W,01:51:23,04:34:53 +48,M,02:03:01,04:34:53 +58,M,01:53:28,04:34:53 +48,M,01:51:57,04:34:53 +48,M,01:59:22,04:34:53 +57,W,02:08:18,04:34:53 +47,M,01:55:22,04:34:54 +41,M,01:50:31,04:34:55 +40,M,01:56:37,04:34:55 +41,W,02:05:53,04:34:55 +40,M,01:52:28,04:34:56 +29,M,01:56:07,04:34:57 +39,W,01:57:27,04:34:57 +64,M,01:59:48,04:34:58 +32,M,01:58:35,04:34:58 +33,W,01:49:42,04:34:58 +42,M,01:47:58,04:34:58 +31,W,02:02:57,04:34:58 +26,W,01:58:51,04:34:59 +22,W,01:58:13,04:34:59 +38,M,01:57:52,04:35:00 +47,M,01:55:42,04:35:00 +39,M,02:01:18,04:35:00 +37,M,02:03:02,04:35:00 +37,M,01:51:52,04:35:00 +55,M,01:55:53,04:35:01 +29,M,01:59:58,04:35:01 +49,M,01:59:23,04:35:01 +39,W,01:59:09,04:35:01 +29,M,01:54:57,04:35:02 +37,M,01:43:44,04:35:02 +62,M,01:52:25,04:35:03 +39,M,01:58:56,04:35:03 +29,M,01:55:33,04:35:03 +27,W,02:04:12,04:35:03 +47,M,01:50:20,04:35:03 +39,M,01:48:20,04:35:04 +34,W,02:06:46,04:35:04 +24,W,01:54:33,04:35:04 +28,M,01:59:08,04:35:04 +33,W,02:01:53,04:35:04 +41,W,02:00:31,04:35:04 +53,M,01:55:19,04:35:05 +30,M,01:54:59,04:35:05 +44,W,02:00:07,04:35:06 +24,M,01:56:43,04:35:06 +48,M,01:56:02,04:35:06 +35,W,01:59:24,04:35:06 +22,M,01:59:33,04:35:07 +33,W,02:00:38,04:35:07 +47,M,02:08:22,04:35:07 +28,M,01:46:46,04:35:07 +31,M,01:49:11,04:35:07 +28,M,01:53:22,04:35:07 +41,W,02:00:23,04:35:08 +41,W,01:58:37,04:35:08 +50,M,02:00:07,04:35:08 +37,M,01:44:31,04:35:08 +40,W,02:00:07,04:35:09 +40,M,01:55:36,04:35:09 +56,M,01:58:32,04:35:09 +38,M,02:04:37,04:35:09 +28,M,01:57:12,04:35:09 +38,M,01:52:46,04:35:09 +53,M,01:52:11,04:35:09 +50,W,01:54:07,04:35:10 +37,M,01:54:44,04:35:10 +52,W,01:58:54,04:35:10 +50,W,01:53:35,04:35:10 +36,M,01:53:20,04:35:11 +60,M,02:00:08,04:35:11 +34,M,02:02:07,04:35:11 +43,W,01:55:34,04:35:12 +41,M,01:56:25,04:35:12 +41,M,01:50:03,04:35:12 +24,M,01:48:26,04:35:12 +38,M,01:58:18,04:35:13 +60,M,02:01:58,04:35:13 +35,M,01:59:57,04:35:13 +51,M,02:05:02,04:35:13 +57,M,01:56:02,04:35:13 +36,W,02:02:48,04:35:13 +47,M,01:58:36,04:35:13 +44,M,02:06:05,04:35:14 +28,W,01:57:00,04:35:14 +35,M,01:54:05,04:35:15 +33,W,02:05:12,04:35:15 +37,M,01:55:01,04:35:15 +32,M,01:52:09,04:35:15 +45,M,01:54:42,04:35:16 +37,M,01:54:10,04:35:16 +43,M,02:02:14,04:35:16 +38,M,01:56:22,04:35:16 +43,M,01:55:36,04:35:16 +36,M,02:00:18,04:35:17 +54,M,02:03:40,04:35:17 +47,W,01:57:53,04:35:18 +26,W,02:02:42,04:35:18 +39,M,01:58:57,04:35:18 +29,M,01:56:07,04:35:18 +35,W,02:03:12,04:35:18 +42,M,01:55:13,04:35:18 +36,W,01:49:34,04:35:18 +27,M,01:56:06,04:35:18 +39,M,01:58:41,04:35:18 +24,W,01:58:35,04:35:18 +57,W,02:01:53,04:35:19 +57,M,01:56:44,04:35:19 +46,W,02:01:31,04:35:19 +42,M,01:51:42,04:35:19 +40,M,01:53:06,04:35:19 +33,M,01:59:16,04:35:19 +21,M,01:55:07,04:35:20 +40,M,02:07:49,04:35:20 +41,M,01:50:46,04:35:21 +36,M,01:48:46,04:35:22 +39,M,01:55:26,04:35:22 +39,M,02:05:52,04:35:22 +50,M,01:52:38,04:35:22 +37,M,02:00:11,04:35:22 +38,W,02:09:53,04:35:22 +34,M,02:02:23,04:35:23 +74,M,02:03:49,04:35:24 +43,M,01:53:27,04:35:24 +32,M,01:59:56,04:35:24 +43,W,02:05:13,04:35:24 +31,W,01:57:13,04:35:24 +40,M,01:55:46,04:35:24 +57,W,01:53:51,04:35:24 +25,M,01:53:25,04:35:25 +56,W,01:49:00,04:35:25 +35,W,01:57:56,04:35:25 +49,M,02:01:55,04:35:25 +41,M,01:44:44,04:35:25 +59,M,01:47:18,04:35:26 +51,W,01:52:35,04:35:26 +30,M,02:01:01,04:35:26 +32,M,02:04:29,04:35:26 +39,M,02:02:36,04:35:27 +42,W,01:57:47,04:35:27 +56,M,01:57:59,04:35:27 +51,W,02:11:21,04:35:28 +68,M,01:58:49,04:35:28 +33,M,01:55:52,04:35:28 +48,M,01:55:28,04:35:29 +47,M,01:58:13,04:35:29 +45,W,02:03:05,04:35:29 +48,M,01:55:44,04:35:29 +39,W,02:03:43,04:35:29 +32,W,02:06:27,04:35:29 +39,M,01:59:18,04:35:29 +50,W,01:54:23,04:35:29 +26,M,01:53:09,04:35:30 +32,M,02:03:03,04:35:30 +43,M,01:44:21,04:35:30 +30,W,01:58:12,04:35:31 +59,M,02:00:14,04:35:31 +47,M,01:54:59,04:35:31 +39,M,02:05:18,04:35:31 +41,M,02:02:03,04:35:32 +32,W,02:04:09,04:35:32 +36,W,02:07:05,04:35:32 +38,M,01:57:43,04:35:32 +49,M,01:58:18,04:35:32 +53,M,01:54:12,04:35:32 +51,M,01:54:46,04:35:33 +53,M,02:07:28,04:35:33 +40,W,01:51:46,04:35:33 +44,M,01:51:28,04:35:33 +44,M,01:58:06,04:35:33 +61,M,01:52:00,04:35:34 +40,W,02:04:04,04:35:34 +27,W,02:02:33,04:35:34 +33,M,02:09:57,04:35:35 +26,W,02:12:03,04:35:36 +25,W,02:00:30,04:35:36 +39,W,01:59:18,04:35:36 +27,M,01:55:08,04:35:36 +37,M,02:10:59,04:35:37 +36,M,01:50:45,04:35:37 +58,M,01:54:24,04:35:37 +28,W,02:01:26,04:35:37 +49,M,01:54:08,04:35:37 +53,M,01:55:48,04:35:38 +61,M,02:04:18,04:35:38 +52,M,01:57:59,04:35:38 +65,M,02:04:30,04:35:38 +31,M,01:56:52,04:35:38 +24,M,02:00:23,04:35:39 +36,M,01:58:52,04:35:39 +35,M,01:47:33,04:35:40 +32,M,01:50:55,04:35:40 +49,M,02:04:47,04:35:40 +29,W,02:02:22,04:35:40 +27,M,01:57:14,04:35:41 +29,M,01:56:30,04:35:41 +43,M,01:58:47,04:35:41 +54,M,02:06:09,04:35:42 +27,W,02:08:37,04:35:42 +29,M,01:51:07,04:35:42 +42,W,02:00:15,04:35:42 +53,W,02:06:28,04:35:43 +25,W,01:59:48,04:35:43 +32,W,02:04:01,04:35:43 +38,M,01:56:04,04:35:44 +33,M,01:49:16,04:35:44 +37,W,02:01:46,04:35:44 +22,M,01:45:32,04:35:44 +29,W,01:54:24,04:35:45 +44,M,01:53:30,04:35:45 +41,M,01:54:41,04:35:46 +49,M,01:47:11,04:35:46 +23,W,02:04:50,04:35:46 +29,W,02:16:04,04:35:46 +48,W,01:57:14,04:35:46 +51,M,01:59:52,04:35:46 +25,M,02:04:55,04:35:47 +35,W,01:55:26,04:35:47 +29,W,02:02:57,04:35:47 +29,M,02:00:48,04:35:48 +45,M,01:57:52,04:35:48 +45,M,01:51:36,04:35:48 +37,M,02:05:10,04:35:48 +30,W,01:58:44,04:35:49 +50,W,01:58:36,04:35:49 +25,W,01:59:59,04:35:49 +58,M,02:08:28,04:35:50 +40,M,02:00:36,04:35:50 +17,W,01:53:18,04:35:50 +48,M,02:03:11,04:35:50 +54,W,02:11:13,04:35:50 +38,W,01:59:59,04:35:51 +37,M,01:48:17,04:35:51 +37,M,01:58:51,04:35:51 +40,M,01:50:22,04:35:52 +47,M,01:49:38,04:35:52 +19,M,01:54:17,04:35:52 +38,M,01:56:28,04:35:53 +62,M,02:00:05,04:35:53 +35,M,01:57:48,04:35:53 +61,M,02:01:01,04:35:53 +36,M,01:55:34,04:35:54 +38,M,01:52:39,04:35:54 +32,W,01:59:41,04:35:54 +35,W,01:51:09,04:35:54 +40,M,01:54:29,04:35:54 +49,M,01:59:08,04:35:54 +28,M,01:58:06,04:35:54 +51,M,02:03:16,04:35:55 +38,W,02:00:58,04:35:56 +25,W,02:10:39,04:35:56 +36,W,02:06:19,04:35:56 +60,M,01:53:25,04:35:56 +49,W,02:06:49,04:35:56 +49,W,02:04:06,04:35:57 +30,W,02:07:10,04:35:57 +50,W,01:52:27,04:35:57 +44,M,01:59:48,04:35:57 +47,M,02:04:35,04:35:58 +35,W,02:08:12,04:35:58 +41,M,01:58:08,04:35:59 +39,W,02:09:43,04:35:59 +43,M,02:00:26,04:35:59 +50,M,02:17:32,04:35:59 +41,M,01:50:04,04:36:00 +42,M,01:56:40,04:36:00 +50,M,02:02:26,04:36:00 +39,W,02:03:50,04:36:00 +40,M,01:52:06,04:36:01 +42,M,02:06:03,04:36:01 +31,M,02:00:56,04:36:02 +43,M,01:45:02,04:36:02 +27,W,02:02:41,04:36:02 +38,M,01:59:54,04:36:03 +51,M,01:56:34,04:36:03 +32,M,01:40:42,04:36:03 +55,M,01:59:48,04:36:03 +51,M,02:00:04,04:36:03 +30,W,02:06:37,04:36:03 +39,M,01:51:21,04:36:04 +51,M,01:57:49,04:36:04 +38,W,02:05:13,04:36:04 +44,W,02:01:44,04:36:04 +23,M,02:09:01,04:36:04 +40,M,01:57:22,04:36:04 +44,W,02:04:37,04:36:05 +54,W,02:00:37,04:36:05 +57,M,01:57:38,04:36:05 +41,M,01:45:46,04:36:05 +31,W,02:02:33,04:36:05 +51,M,01:58:32,04:36:06 +22,M,01:54:23,04:36:06 +32,M,02:03:47,04:36:06 +33,M,01:52:16,04:36:06 +51,M,02:00:01,04:36:07 +41,M,02:00:00,04:36:07 +28,M,02:04:59,04:36:07 +29,M,01:51:56,04:36:07 +35,M,01:57:10,04:36:08 +42,M,01:56:47,04:36:08 +28,W,02:02:15,04:36:09 +31,M,02:01:47,04:36:09 +30,M,02:02:01,04:36:09 +52,M,01:58:18,04:36:09 +33,W,02:01:07,04:36:10 +50,W,02:06:40,04:36:10 +61,M,02:01:23,04:36:10 +52,M,01:50:35,04:36:11 +41,W,01:59:40,04:36:11 +41,M,01:59:12,04:36:12 +29,M,01:53:55,04:36:12 +45,M,02:01:23,04:36:12 +48,M,01:57:25,04:36:12 +37,W,01:58:40,04:36:12 +50,W,01:59:48,04:36:13 +41,W,02:07:16,04:36:13 +35,M,02:02:12,04:36:13 +22,W,02:01:11,04:36:13 +44,W,02:08:03,04:36:13 +47,M,01:47:55,04:36:13 +60,M,01:58:21,04:36:14 +38,M,01:57:50,04:36:14 +34,W,01:54:13,04:36:14 +27,W,02:03:53,04:36:14 +47,M,01:57:26,04:36:14 +28,M,01:53:22,04:36:15 +50,M,01:56:34,04:36:15 +44,M,01:53:23,04:36:15 +29,M,02:06:05,04:36:15 +32,M,02:05:53,04:36:16 +48,W,01:59:32,04:36:16 +26,W,02:16:36,04:36:16 +50,W,01:53:33,04:36:17 +38,M,02:03:39,04:36:18 +42,W,02:04:06,04:36:18 +55,M,02:09:48,04:36:18 +40,W,02:01:23,04:36:18 +54,M,01:56:37,04:36:18 +30,M,01:56:45,04:36:19 +53,W,02:03:44,04:36:19 +40,W,02:04:24,04:36:20 +41,W,01:55:47,04:36:20 +47,W,02:03:59,04:36:20 +33,M,01:47:26,04:36:20 +23,M,01:46:21,04:36:20 +34,W,02:08:01,04:36:21 +42,M,01:52:14,04:36:22 +43,M,02:03:52,04:36:22 +35,W,02:04:13,04:36:22 +26,M,01:57:37,04:36:22 +33,W,02:02:21,04:36:22 +57,M,01:52:04,04:36:23 +30,M,02:01:13,04:36:23 +40,M,01:54:55,04:36:23 +38,M,02:09:01,04:36:23 +31,M,02:01:33,04:36:24 +39,M,01:55:30,04:36:24 +42,M,02:03:58,04:36:24 +28,W,02:02:43,04:36:25 +27,W,02:03:03,04:36:26 +48,M,02:02:21,04:36:26 +51,M,02:04:19,04:36:26 +32,M,02:05:02,04:36:27 +28,W,02:00:17,04:36:27 +41,M,01:55:51,04:36:27 +52,M,01:59:51,04:36:27 +36,M,01:59:54,04:36:27 +63,M,01:54:19,04:36:28 +40,M,01:54:45,04:36:28 +50,M,01:58:36,04:36:28 +33,M,01:57:50,04:36:28 +40,M,01:51:38,04:36:28 +44,M,01:56:39,04:36:29 +45,W,02:07:56,04:36:29 +46,W,02:03:35,04:36:30 +40,M,02:00:34,04:36:30 +28,M,01:57:40,04:36:30 +35,M,01:55:32,04:36:31 +39,W,01:56:29,04:36:31 +32,M,01:57:23,04:36:31 +40,M,02:03:24,04:36:31 +31,M,01:56:58,04:36:32 +35,M,01:51:31,04:36:32 +28,M,02:00:14,04:36:32 +42,M,01:49:00,04:36:32 +29,M,01:59:47,04:36:32 +35,M,01:53:33,04:36:33 +54,M,01:58:46,04:36:33 +26,W,02:04:38,04:36:33 +41,W,02:04:53,04:36:33 +34,W,02:08:17,04:36:33 +37,M,01:59:30,04:36:33 +47,M,01:50:29,04:36:34 +43,M,01:54:25,04:36:34 +32,M,01:43:17,04:36:35 +43,W,02:07:58,04:36:35 +30,W,01:56:31,04:36:35 +43,M,01:51:27,04:36:35 +31,M,01:55:38,04:36:35 +27,M,01:51:15,04:36:35 +49,M,01:54:26,04:36:36 +24,M,01:53:43,04:36:36 +39,M,01:58:09,04:36:36 +26,W,01:59:52,04:36:36 +38,M,01:57:32,04:36:36 +32,W,01:52:58,04:36:37 +42,M,02:03:22,04:36:37 +71,M,01:59:44,04:36:38 +36,M,01:54:41,04:36:38 +38,W,01:52:40,04:36:39 +50,W,01:56:03,04:36:39 +37,W,01:53:58,04:36:39 +53,M,02:01:23,04:36:39 +32,M,01:54:57,04:36:39 +56,M,01:59:28,04:36:40 +32,W,02:06:25,04:36:40 +58,M,01:59:10,04:36:40 +38,M,01:57:37,04:36:40 +59,M,02:04:49,04:36:41 +34,M,01:53:31,04:36:41 +28,M,02:06:02,04:36:41 +33,W,01:52:56,04:36:41 +41,M,02:01:10,04:36:42 +33,M,01:53:44,04:36:42 +40,M,01:49:17,04:36:42 +28,M,02:03:30,04:36:42 +46,M,01:57:27,04:36:43 +35,M,01:53:13,04:36:43 +37,M,02:09:04,04:36:44 +37,M,02:07:26,04:36:44 +33,M,01:58:50,04:36:44 +61,M,02:05:40,04:36:44 +42,W,01:57:59,04:36:44 +29,W,02:08:38,04:36:44 +36,M,01:51:38,04:36:45 +43,M,02:06:09,04:36:45 +48,W,02:01:18,04:36:45 +59,M,01:52:35,04:36:46 +42,W,02:09:00,04:36:46 +51,M,02:01:47,04:36:47 +35,W,02:04:27,04:36:47 +41,M,01:57:09,04:36:47 +44,M,02:07:04,04:36:47 +31,W,01:58:06,04:36:47 +61,M,01:50:59,04:36:47 +39,M,01:56:06,04:36:47 +30,M,02:06:09,04:36:48 +42,M,01:59:49,04:36:48 +40,W,01:52:56,04:36:48 +25,W,02:01:40,04:36:49 +29,M,02:04:33,04:36:49 +45,M,01:51:34,04:36:49 +55,M,01:59:26,04:36:50 +41,M,02:02:22,04:36:51 +34,M,02:01:30,04:36:51 +32,M,01:55:32,04:36:51 +32,M,01:52:36,04:36:52 +45,M,02:00:41,04:36:52 +51,W,02:00:29,04:36:52 +24,M,01:50:07,04:36:53 +34,W,01:54:38,04:36:53 +42,M,02:04:49,04:36:53 +23,W,01:47:40,04:36:53 +27,M,01:56:09,04:36:53 +27,W,02:03:52,04:36:54 +50,M,01:55:11,04:36:54 +24,M,02:02:25,04:36:54 +50,M,01:57:49,04:36:54 +37,W,02:05:43,04:36:54 +58,W,02:05:47,04:36:55 +33,M,01:56:49,04:36:55 +30,M,01:50:48,04:36:56 +25,M,02:00:26,04:36:56 +22,W,02:09:23,04:36:56 +47,M,01:57:30,04:36:56 +46,W,01:58:29,04:36:56 +43,W,02:07:15,04:36:57 +37,M,01:52:13,04:36:57 +42,M,02:03:22,04:36:57 +53,M,02:04:56,04:36:57 +50,M,02:02:13,04:36:57 +52,M,01:56:37,04:36:57 +39,M,01:56:39,04:36:57 +51,M,01:57:04,04:36:57 +27,W,01:55:09,04:36:57 +27,M,02:07:11,04:36:57 +40,M,02:06:39,04:36:57 +26,W,01:57:20,04:36:57 +31,M,01:52:16,04:36:58 +24,W,02:04:12,04:36:58 +49,M,01:57:25,04:36:58 +50,M,01:57:00,04:36:59 +41,M,02:00:48,04:36:59 +25,M,01:59:56,04:36:59 +28,M,02:03:49,04:36:59 +55,M,01:54:10,04:37:00 +36,M,01:50:29,04:37:00 +25,M,02:14:47,04:37:00 +56,W,02:02:37,04:37:00 +37,M,01:46:20,04:37:00 +50,M,01:58:41,04:37:00 +44,M,02:00:58,04:37:01 +26,W,02:01:14,04:37:01 +27,M,01:52:25,04:37:01 +28,W,01:57:17,04:37:01 +34,W,02:05:59,04:37:01 +50,M,01:53:28,04:37:01 +42,M,01:54:24,04:37:02 +62,M,01:58:07,04:37:02 +50,M,02:06:29,04:37:02 +54,M,01:55:42,04:37:02 +25,W,01:59:06,04:37:02 +32,M,01:49:51,04:37:03 +30,W,02:02:31,04:37:03 +43,M,02:03:48,04:37:03 +42,M,02:07:17,04:37:03 +35,W,01:56:19,04:37:03 +51,M,01:56:19,04:37:03 +37,M,02:00:42,04:37:04 +41,M,01:51:01,04:37:04 +28,M,01:52:00,04:37:04 +50,M,01:59:22,04:37:05 +40,M,01:53:09,04:37:05 +55,M,02:03:23,04:37:05 +61,M,01:56:18,04:37:05 +39,M,02:00:51,04:37:06 +49,M,01:53:23,04:37:06 +41,M,02:00:50,04:37:06 +40,W,02:00:12,04:37:06 +52,M,01:52:13,04:37:06 +40,W,02:07:18,04:37:06 +39,M,02:00:02,04:37:07 +55,M,01:57:36,04:37:07 +25,W,02:00:45,04:37:07 +31,W,02:01:20,04:37:07 +54,W,02:02:25,04:37:07 +37,M,01:59:45,04:37:07 +35,W,02:02:24,04:37:07 +40,M,02:02:44,04:37:07 +45,M,02:10:02,04:37:08 +50,M,01:55:42,04:37:08 +58,M,01:49:18,04:37:08 +33,M,01:52:56,04:37:08 +42,M,01:52:27,04:37:08 +37,M,01:51:38,04:37:09 +45,M,01:57:38,04:37:09 +48,M,02:02:42,04:37:09 +47,M,01:57:13,04:37:09 +49,W,02:00:10,04:37:09 +45,M,02:01:58,04:37:10 +48,M,02:03:22,04:37:10 +37,M,01:56:43,04:37:10 +50,M,01:56:28,04:37:11 +29,M,01:55:23,04:37:11 +29,M,01:56:36,04:37:11 +41,W,02:00:07,04:37:12 +36,M,01:59:10,04:37:12 +49,W,02:03:35,04:37:12 +42,M,01:56:14,04:37:12 +36,M,01:51:49,04:37:12 +28,W,01:56:30,04:37:13 +39,M,02:00:58,04:37:13 +30,M,01:56:56,04:37:13 +41,M,01:49:19,04:37:13 +50,M,01:58:15,04:37:13 +44,W,02:05:41,04:37:14 +27,W,02:00:43,04:37:15 +25,W,01:57:48,04:37:15 +40,M,01:52:04,04:37:15 +44,M,02:03:28,04:37:15 +52,M,01:59:15,04:37:16 +32,M,01:58:41,04:37:16 +29,W,02:01:10,04:37:16 +37,W,02:01:01,04:37:16 +50,M,01:56:29,04:37:16 +40,W,02:00:57,04:37:17 +44,W,02:03:32,04:37:17 +43,W,01:50:14,04:37:17 +29,W,02:03:49,04:37:17 +41,M,01:58:05,04:37:18 +26,M,02:01:23,04:37:18 +32,W,01:53:46,04:37:18 +45,W,02:06:16,04:37:19 +29,M,02:04:07,04:37:19 +45,M,01:47:11,04:37:19 +37,M,01:57:10,04:37:19 +35,M,02:03:21,04:37:19 +31,M,01:59:41,04:37:19 +49,M,01:57:11,04:37:20 +23,M,01:42:27,04:37:21 +49,M,01:59:16,04:37:21 +40,M,01:52:13,04:37:21 +39,M,02:01:41,04:37:21 +28,W,02:00:44,04:37:21 +42,M,01:55:09,04:37:21 +42,W,01:52:51,04:37:21 +46,M,01:45:26,04:37:21 +40,W,02:00:50,04:37:21 +53,M,02:02:13,04:37:21 +53,M,01:51:57,04:37:21 +41,M,01:58:10,04:37:22 +44,M,02:00:03,04:37:22 +51,M,02:03:11,04:37:22 +39,M,01:56:48,04:37:23 +46,W,02:11:52,04:37:23 +32,W,02:02:34,04:37:23 +61,W,02:01:56,04:37:23 +42,W,01:59:20,04:37:23 +53,M,02:04:55,04:37:23 +29,W,02:04:18,04:37:23 +28,M,02:00:15,04:37:23 +28,W,02:05:29,04:37:24 +40,M,01:57:09,04:37:24 +56,M,02:04:40,04:37:24 +59,M,01:50:06,04:37:24 +43,W,01:55:26,04:37:24 +34,M,01:58:22,04:37:24 +48,M,02:04:53,04:37:25 +43,M,02:01:18,04:37:25 +35,M,02:02:58,04:37:25 +44,M,02:05:23,04:37:25 +41,M,01:54:21,04:37:26 +29,W,01:58:24,04:37:26 +42,M,01:54:30,04:37:26 +46,M,01:41:10,04:37:26 +42,M,01:55:12,04:37:27 +28,W,01:53:06,04:37:27 +59,M,01:56:49,04:37:27 +40,M,02:03:40,04:37:28 +32,W,02:01:36,04:37:28 +52,W,02:08:12,04:37:28 +46,M,01:47:17,04:37:29 +43,M,01:53:57,04:37:29 +61,M,02:01:01,04:37:29 +53,M,02:14:17,04:37:30 +57,M,02:02:33,04:37:30 +33,W,02:06:33,04:37:30 +41,W,01:57:57,04:37:30 +28,W,02:01:20,04:37:30 +45,M,02:02:53,04:37:31 +39,W,01:55:43,04:37:31 +41,M,02:01:00,04:37:31 +41,M,02:00:06,04:37:31 +45,M,01:51:08,04:37:32 +45,M,01:57:47,04:37:33 +43,W,01:55:55,04:37:33 +47,M,01:46:48,04:37:33 +59,M,01:56:25,04:37:34 +52,M,02:02:44,04:37:34 +43,W,02:03:50,04:37:35 +30,M,02:03:52,04:37:35 +42,W,02:02:58,04:37:35 +31,M,01:56:13,04:37:35 +46,W,02:00:57,04:37:35 +46,M,02:13:40,04:37:35 +42,W,02:00:34,04:37:35 +45,M,01:51:31,04:37:35 +46,M,01:56:21,04:37:35 +46,M,02:00:23,04:37:36 +29,M,01:58:16,04:37:36 +21,M,01:58:47,04:37:36 +44,W,02:05:08,04:37:36 +46,W,02:02:30,04:37:36 +24,W,01:55:40,04:37:36 +54,M,01:59:10,04:37:37 +32,M,02:04:37,04:37:37 +30,W,01:55:09,04:37:37 +20,W,01:58:43,04:37:37 +37,M,02:02:09,04:37:38 +66,M,02:05:23,04:37:38 +45,M,01:58:28,04:37:39 +42,W,02:10:20,04:37:39 +41,W,02:05:42,04:37:39 +32,W,02:01:38,04:37:39 +58,W,02:06:22,04:37:39 +53,M,02:03:08,04:37:40 +50,M,01:58:25,04:37:40 +48,W,02:03:42,04:37:40 +42,W,02:09:48,04:37:40 +47,M,02:05:04,04:37:41 +41,M,02:07:08,04:37:41 +36,W,01:59:08,04:37:41 +28,W,01:59:00,04:37:42 +34,W,01:53:41,04:37:42 +34,M,02:05:01,04:37:43 +27,M,02:06:15,04:37:43 +59,M,01:49:49,04:37:43 +48,W,02:05:21,04:37:43 +59,M,01:59:19,04:37:43 +39,M,02:04:36,04:37:44 +42,W,01:57:05,04:37:44 +50,M,01:56:57,04:37:45 +46,W,01:58:28,04:37:45 +37,M,01:55:28,04:37:45 +41,W,02:07:29,04:37:45 +35,W,02:00:40,04:37:46 +24,M,02:05:40,04:37:46 +43,M,01:57:53,04:37:47 +32,M,01:52:59,04:37:47 +47,M,01:48:33,04:37:47 +36,M,02:00:11,04:37:47 +67,M,01:52:34,04:37:47 +30,W,01:55:00,04:37:48 +35,M,02:07:31,04:37:48 +27,M,01:57:57,04:37:48 +45,M,01:56:01,04:37:48 +33,M,02:00:18,04:37:48 +29,W,01:53:04,04:37:49 +29,M,01:54:51,04:37:49 +42,W,01:52:02,04:37:49 +38,M,02:00:06,04:37:50 +29,W,02:00:24,04:37:50 +39,M,01:55:44,04:37:50 +59,W,01:59:13,04:37:51 +36,M,02:12:43,04:37:51 +42,W,01:57:37,04:37:52 +47,M,02:00:11,04:37:52 +31,M,01:58:32,04:37:52 +35,W,01:56:23,04:37:52 +30,M,01:56:01,04:37:53 +46,M,02:04:35,04:37:53 +57,M,02:02:50,04:37:53 +36,W,02:05:03,04:37:53 +42,M,01:56:10,04:37:53 +27,M,01:49:52,04:37:53 +43,M,01:55:20,04:37:54 +69,M,02:04:14,04:37:54 +36,M,01:53:56,04:37:54 +50,W,01:58:53,04:37:54 +33,W,02:02:33,04:37:54 +49,M,02:01:57,04:37:54 +44,M,02:01:56,04:37:54 +52,M,01:46:27,04:37:54 +59,M,02:06:41,04:37:55 +51,M,02:07:32,04:37:55 +37,W,02:00:48,04:37:56 +34,M,02:01:36,04:37:56 +36,W,01:59:37,04:37:56 +36,M,01:57:31,04:37:56 +41,M,02:01:17,04:37:56 +32,M,01:54:00,04:37:56 +38,M,01:54:26,04:37:57 +50,M,02:02:28,04:37:57 +49,W,02:04:22,04:37:57 +35,W,01:59:11,04:37:57 +53,W,02:09:01,04:37:57 +43,W,02:09:14,04:37:58 +41,W,01:55:30,04:37:58 +30,M,02:10:10,04:37:58 +43,M,01:56:54,04:37:58 +41,M,02:00:01,04:37:59 +44,M,01:59:30,04:37:59 +38,M,02:01:39,04:37:59 +51,W,01:59:37,04:37:59 +38,W,02:03:47,04:37:59 +28,W,01:57:56,04:38:00 +36,W,02:04:13,04:38:00 +52,M,01:56:48,04:38:01 +50,M,02:07:34,04:38:01 +57,M,02:00:59,04:38:01 +36,W,01:57:10,04:38:02 +26,W,02:07:18,04:38:02 +26,M,01:52:44,04:38:03 +46,M,01:56:03,04:38:03 +23,W,02:06:55,04:38:03 +51,M,01:52:51,04:38:03 +58,M,02:01:28,04:38:03 +48,M,01:59:15,04:38:03 +33,W,02:02:46,04:38:04 +44,M,01:55:01,04:38:04 +32,M,02:06:38,04:38:04 +45,M,02:01:22,04:38:04 +36,M,01:51:19,04:38:04 +21,M,01:59:32,04:38:05 +52,M,02:01:26,04:38:05 +56,M,02:01:34,04:38:05 +26,M,02:07:21,04:38:05 +27,M,01:54:01,04:38:05 +36,W,02:04:57,04:38:06 +58,M,02:02:36,04:38:06 +38,M,02:02:50,04:38:06 +48,M,02:11:33,04:38:06 +45,W,01:56:11,04:38:06 +30,M,01:49:53,04:38:07 +44,M,01:58:58,04:38:07 +35,M,01:58:20,04:38:07 +34,M,01:56:03,04:38:08 +22,M,02:09:05,04:38:08 +41,M,02:03:13,04:38:08 +35,M,01:48:05,04:38:08 +31,W,02:01:10,04:38:08 +47,M,02:05:00,04:38:08 +34,W,01:54:50,04:38:08 +32,W,02:01:05,04:38:08 +38,M,01:58:41,04:38:09 +37,W,01:58:19,04:38:09 +45,W,02:04:32,04:38:09 +27,M,01:55:32,04:38:09 +28,M,01:54:20,04:38:09 +61,W,02:05:40,04:38:10 +19,M,02:02:59,04:38:10 +53,M,01:57:23,04:38:10 +23,W,01:59:17,04:38:10 +58,M,02:06:12,04:38:10 +30,W,02:04:19,04:38:11 +44,W,01:54:24,04:38:11 +30,W,01:54:38,04:38:12 +37,W,02:10:11,04:38:12 +45,M,01:57:30,04:38:12 +43,M,01:55:26,04:38:12 +51,M,02:05:29,04:38:12 +48,M,02:00:38,04:38:12 +39,M,01:54:20,04:38:12 +54,M,01:55:35,04:38:13 +43,M,01:57:27,04:38:13 +30,W,01:57:59,04:38:13 +28,W,01:56:03,04:38:13 +40,M,02:02:31,04:38:13 +30,W,01:53:35,04:38:13 +37,W,02:07:36,04:38:14 +70,M,02:06:42,04:38:14 +54,W,02:01:59,04:38:14 +34,W,01:57:40,04:38:15 +52,W,02:04:20,04:38:15 +38,M,02:01:33,04:38:15 +23,W,01:55:35,04:38:15 +27,M,01:56:56,04:38:15 +28,M,02:00:01,04:38:15 +39,M,02:02:42,04:38:15 +27,W,02:01:05,04:38:15 +39,W,02:05:58,04:38:16 +53,M,01:56:36,04:38:16 +54,M,02:01:04,04:38:16 +42,W,02:01:17,04:38:17 +52,W,02:02:47,04:38:18 +32,M,01:59:20,04:38:18 +39,W,02:04:17,04:38:18 +39,M,02:03:03,04:38:19 +41,M,01:54:38,04:38:19 +55,W,02:06:40,04:38:19 +52,M,01:53:39,04:38:19 +33,M,01:52:56,04:38:19 +49,M,01:59:47,04:38:19 +33,W,02:05:50,04:38:20 +45,M,02:02:56,04:38:20 +41,M,01:53:43,04:38:20 +25,M,01:55:41,04:38:20 +39,M,02:06:23,04:38:20 +46,M,01:46:51,04:38:20 +33,M,01:58:52,04:38:21 +45,M,02:05:31,04:38:21 +43,M,01:56:10,04:38:21 +34,M,02:03:29,04:38:21 +55,M,02:03:10,04:38:22 +34,M,02:00:39,04:38:22 +35,M,01:53:16,04:38:22 +35,W,01:51:39,04:38:22 +28,W,02:05:50,04:38:22 +52,M,01:57:07,04:38:23 +52,M,01:58:15,04:38:23 +28,M,01:55:36,04:38:23 +32,W,02:04:49,04:38:23 +51,W,02:06:31,04:38:24 +31,M,01:57:35,04:38:24 +54,M,01:56:07,04:38:24 +24,M,01:59:10,04:38:25 +48,M,02:00:06,04:38:25 +51,W,02:00:51,04:38:25 +40,M,01:59:59,04:38:25 +30,W,01:57:55,04:38:25 +36,W,01:54:38,04:38:26 +27,M,01:55:11,04:38:26 +37,M,02:05:05,04:38:26 +40,M,01:56:33,04:38:26 +53,M,01:58:04,04:38:27 +54,M,01:54:41,04:38:28 +45,M,01:59:17,04:38:28 +44,M,02:05:16,04:38:29 +47,M,01:48:29,04:38:29 +43,M,02:00:21,04:38:29 +34,M,02:06:09,04:38:29 +37,W,02:06:31,04:38:29 +40,M,01:57:01,04:38:30 +44,M,02:00:27,04:38:30 +33,M,01:56:13,04:38:30 +23,M,02:03:58,04:38:31 +32,M,01:52:13,04:38:31 +48,M,02:01:27,04:38:31 +37,M,01:49:18,04:38:32 +22,M,01:57:25,04:38:32 +45,M,01:52:22,04:38:32 +39,M,01:58:29,04:38:33 +35,M,02:03:54,04:38:33 +44,M,02:05:15,04:38:33 +27,W,02:02:32,04:38:33 +53,M,01:56:03,04:38:33 +24,W,02:00:15,04:38:34 +23,M,01:58:33,04:38:35 +40,W,01:56:04,04:38:35 +45,M,01:55:30,04:38:35 +20,M,01:52:54,04:38:36 +40,M,01:57:36,04:38:36 +30,W,02:00:26,04:38:36 +55,M,01:55:49,04:38:36 +37,M,01:47:57,04:38:36 +41,W,02:04:59,04:38:37 +26,M,02:14:01,04:38:37 +46,M,01:59:54,04:38:38 +44,M,02:03:04,04:38:38 +40,M,02:07:50,04:38:38 +36,M,02:01:49,04:38:39 +34,M,02:05:23,04:38:40 +43,M,01:55:14,04:38:40 +53,M,02:04:38,04:38:40 +43,M,01:53:59,04:38:40 +35,W,02:04:02,04:38:41 +36,M,02:01:46,04:38:41 +51,M,02:02:32,04:38:41 +28,M,01:50:55,04:38:41 +39,M,01:53:15,04:38:41 +36,M,01:48:52,04:38:41 +41,M,01:48:37,04:38:42 +52,W,01:57:33,04:38:42 +51,M,01:55:24,04:38:43 +37,M,02:03:27,04:38:43 +51,W,02:06:20,04:38:43 +36,M,01:56:12,04:38:43 +35,M,02:04:27,04:38:43 +56,M,01:56:24,04:38:44 +47,M,01:53:40,04:38:44 +45,W,02:10:08,04:38:44 +37,W,01:53:36,04:38:44 +50,M,02:05:08,04:38:44 +27,M,02:02:02,04:38:45 +50,M,01:58:58,04:38:45 +48,M,02:05:31,04:38:45 +33,M,01:58:58,04:38:45 +49,M,01:54:52,04:38:45 +40,W,01:57:47,04:38:46 +32,M,01:53:22,04:38:46 +32,M,01:59:25,04:38:47 +26,W,01:55:19,04:38:47 +34,W,02:04:25,04:38:47 +41,M,02:01:58,04:38:47 +53,W,01:55:38,04:38:48 +46,M,02:03:04,04:38:48 +39,M,02:06:41,04:38:48 +46,W,02:00:01,04:38:48 +44,M,02:04:56,04:38:48 +39,M,01:58:59,04:38:49 +26,W,02:07:01,04:38:49 +48,W,01:53:29,04:38:49 +20,W,02:02:41,04:38:49 +41,M,01:56:04,04:38:50 +34,M,02:03:28,04:38:50 +41,M,02:03:27,04:38:50 +52,M,01:47:57,04:38:50 +43,M,02:02:20,04:38:50 +49,M,01:57:43,04:38:51 +49,M,01:57:54,04:38:51 +35,W,02:09:56,04:38:51 +54,M,02:00:10,04:38:51 +38,M,01:55:32,04:38:51 +19,M,02:02:20,04:38:51 +45,M,02:01:57,04:38:52 +56,M,02:07:34,04:38:52 +45,W,01:59:01,04:38:53 +33,W,02:02:11,04:38:53 +47,M,01:56:01,04:38:53 +31,W,02:04:23,04:38:54 +51,M,02:05:40,04:38:54 +39,M,02:00:33,04:38:54 +62,W,02:01:12,04:38:54 +24,M,01:57:32,04:38:55 +27,M,02:03:26,04:38:55 +37,M,01:59:08,04:38:55 +38,M,01:51:28,04:38:55 +70,M,02:14:06,04:38:56 +46,M,02:06:38,04:38:56 +52,M,01:45:54,04:38:56 +33,M,01:48:53,04:38:56 +50,M,02:02:03,04:38:56 +51,M,02:07:48,04:38:56 +58,W,02:03:38,04:38:56 +43,W,01:55:06,04:38:56 +33,M,01:57:34,04:38:57 +31,M,01:49:37,04:38:57 +47,M,01:51:01,04:38:57 +36,W,01:57:39,04:38:57 +39,M,01:47:29,04:38:57 +39,M,01:56:54,04:38:57 +24,M,02:02:13,04:38:57 +53,M,01:50:40,04:38:57 +50,M,02:01:49,04:38:57 +40,W,01:58:14,04:38:57 +47,M,01:54:06,04:38:58 +41,M,01:55:47,04:38:58 +31,M,01:55:52,04:38:58 +43,M,02:00:48,04:38:58 +52,M,02:06:07,04:38:59 +53,M,01:55:05,04:38:59 +42,M,01:53:13,04:38:59 +45,M,02:06:48,04:38:59 +37,M,02:01:33,04:38:59 +36,M,02:01:58,04:38:59 +38,W,02:01:54,04:39:00 +40,W,02:10:26,04:39:00 +45,M,01:57:03,04:39:00 +41,W,02:06:07,04:39:00 +45,M,01:50:09,04:39:01 +57,M,01:53:44,04:39:01 +31,M,02:01:49,04:39:01 +25,W,02:08:21,04:39:01 +49,M,02:06:51,04:39:01 +41,M,01:56:30,04:39:01 +49,M,02:06:14,04:39:02 +57,M,01:58:47,04:39:02 +31,W,02:01:34,04:39:02 +33,M,02:06:52,04:39:02 +45,W,01:59:22,04:39:02 +57,M,02:06:20,04:39:03 +49,M,02:07:31,04:39:03 +50,M,01:53:07,04:39:03 +30,W,02:05:24,04:39:03 +41,W,02:02:57,04:39:04 +42,W,02:12:59,04:39:04 +35,M,02:09:18,04:39:04 +48,M,01:59:00,04:39:04 +34,M,02:01:09,04:39:04 +39,M,01:59:22,04:39:05 +33,M,01:59:43,04:39:05 +48,M,01:51:20,04:39:06 +36,M,01:54:32,04:39:06 +46,M,01:54:42,04:39:06 +35,M,02:03:23,04:39:07 +28,W,01:56:49,04:39:07 +56,W,02:05:52,04:39:07 +37,M,01:56:20,04:39:07 +42,M,02:00:45,04:39:07 +43,M,01:56:45,04:39:07 +46,M,02:04:21,04:39:08 +40,M,01:53:48,04:39:08 +31,W,02:02:41,04:39:08 +44,M,01:54:41,04:39:08 +41,M,02:05:44,04:39:08 +36,M,01:53:58,04:39:08 +43,M,02:07:02,04:39:08 +31,M,01:56:33,04:39:08 +39,M,01:58:44,04:39:08 +44,W,02:03:22,04:39:09 +43,W,02:06:44,04:39:09 +47,M,01:59:32,04:39:09 +47,W,02:06:34,04:39:10 +38,M,02:01:54,04:39:10 +30,M,01:56:12,04:39:10 +35,M,02:01:25,04:39:10 +44,M,01:54:06,04:39:10 +49,M,01:49:47,04:39:10 +40,M,02:00:11,04:39:10 +50,M,02:01:02,04:39:11 +27,M,01:54:07,04:39:11 +43,M,02:05:28,04:39:11 +24,W,02:00:45,04:39:12 +49,M,01:58:32,04:39:12 +47,W,02:10:25,04:39:12 +58,M,01:48:28,04:39:13 +33,M,01:53:12,04:39:14 +42,M,01:54:28,04:39:14 +58,M,01:57:16,04:39:14 +25,W,01:54:48,04:39:14 +26,M,01:53:36,04:39:14 +34,W,02:04:15,04:39:14 +35,M,01:59:43,04:39:14 +35,M,02:06:07,04:39:15 +47,M,01:55:56,04:39:15 +43,M,02:03:04,04:39:15 +44,M,02:10:06,04:39:15 +49,W,01:58:29,04:39:15 +43,W,02:08:14,04:39:15 +60,M,02:04:00,04:39:15 +36,M,02:06:58,04:39:16 +49,M,01:58:27,04:39:16 +40,M,02:01:31,04:39:16 +53,M,02:06:27,04:39:16 +32,W,02:07:54,04:39:16 +50,M,01:57:12,04:39:17 +36,M,02:04:47,04:39:17 +48,M,01:53:05,04:39:17 +49,M,02:03:20,04:39:17 +41,M,01:55:05,04:39:17 +33,M,02:05:25,04:39:17 +35,W,02:06:10,04:39:17 +44,M,01:56:54,04:39:17 +42,W,02:05:25,04:39:18 +27,W,02:08:24,04:39:18 +25,W,02:00:17,04:39:18 +20,M,01:50:51,04:39:19 +37,M,02:08:41,04:39:19 +44,W,02:09:34,04:39:19 +39,M,01:57:26,04:39:20 +43,M,01:57:19,04:39:20 +52,M,01:56:50,04:39:20 +40,M,01:51:19,04:39:20 +41,W,01:56:48,04:39:20 +55,M,01:55:20,04:39:21 +49,M,02:05:11,04:39:21 +33,M,02:03:14,04:39:21 +66,M,02:07:17,04:39:21 +33,M,01:56:53,04:39:22 +39,M,01:58:22,04:39:22 +48,M,02:01:22,04:39:22 +26,W,02:06:29,04:39:22 +36,W,02:02:44,04:39:23 +42,M,01:58:24,04:39:24 +60,M,02:03:05,04:39:24 +35,W,01:53:47,04:39:24 +25,W,02:01:06,04:39:24 +54,M,02:02:58,04:39:25 +35,M,01:48:00,04:39:25 +55,M,01:58:18,04:39:25 +38,W,02:13:36,04:39:26 +49,M,01:58:08,04:39:26 +43,W,01:55:06,04:39:26 +42,M,01:57:28,04:39:26 +32,W,01:57:42,04:39:26 +51,M,02:09:56,04:39:27 +26,W,01:58:09,04:39:28 +29,W,01:56:26,04:39:29 +25,W,02:02:01,04:39:29 +28,W,02:11:54,04:39:29 +46,M,01:58:43,04:39:29 +45,M,01:58:13,04:39:30 +36,W,02:00:55,04:39:30 +40,W,02:06:46,04:39:30 +40,W,02:00:34,04:39:30 +45,M,02:06:02,04:39:30 +38,W,01:57:00,04:39:30 +65,M,02:02:29,04:39:31 +58,M,02:15:29,04:39:31 +25,W,01:58:42,04:39:32 +29,M,02:01:24,04:39:32 +40,M,01:56:08,04:39:32 +42,M,01:57:35,04:39:32 +51,M,02:03:10,04:39:32 +38,W,01:55:31,04:39:33 +47,W,01:59:49,04:39:33 +32,M,01:48:52,04:39:33 +57,M,02:10:14,04:39:33 +43,M,02:02:28,04:39:33 +48,M,01:54:30,04:39:33 +18,M,02:04:33,04:39:34 +62,M,02:02:28,04:39:34 +39,W,02:06:02,04:39:34 +65,M,01:59:04,04:39:34 +30,M,01:54:23,04:39:35 +23,M,01:52:54,04:39:35 +49,W,02:02:39,04:39:35 +34,M,01:59:22,04:39:35 +46,W,02:06:21,04:39:35 +28,W,02:08:16,04:39:35 +43,M,01:52:13,04:39:35 +41,W,02:07:58,04:39:35 +47,M,02:04:56,04:39:36 +40,M,01:55:20,04:39:36 +48,M,02:01:12,04:39:36 +35,M,02:00:05,04:39:36 +31,W,02:04:38,04:39:36 +33,M,01:56:16,04:39:36 +37,W,01:59:20,04:39:36 +41,M,01:58:55,04:39:37 +35,M,01:56:39,04:39:37 +35,W,02:03:23,04:39:38 +48,M,01:58:39,04:39:38 +43,M,02:02:17,04:39:38 +33,M,01:46:35,04:39:39 +47,M,02:11:58,04:39:39 +63,W,02:04:27,04:39:39 +35,M,02:01:06,04:39:39 +39,M,01:56:41,04:39:39 +42,W,02:03:20,04:39:40 +57,M,01:59:35,04:39:41 +44,W,02:19:08,04:39:41 +30,W,01:58:05,04:39:41 +43,W,02:03:03,04:39:41 +29,W,01:58:25,04:39:41 +41,M,01:55:00,04:39:42 +43,W,02:06:10,04:39:42 +44,W,01:57:30,04:39:42 +29,M,01:58:57,04:39:42 +60,M,01:57:57,04:39:43 +51,M,02:06:04,04:39:43 +50,M,01:55:33,04:39:43 +38,M,01:59:05,04:39:44 +35,M,02:03:50,04:39:44 +27,W,02:08:40,04:39:44 +44,M,02:00:29,04:39:45 +50,M,01:58:18,04:39:45 +43,W,01:51:39,04:39:45 +38,W,01:57:47,04:39:46 +33,W,02:07:49,04:39:46 +51,M,01:55:05,04:39:46 +24,W,02:11:25,04:39:46 +37,W,02:01:16,04:39:46 +30,W,01:58:11,04:39:46 +52,M,02:07:27,04:39:46 +37,M,01:57:45,04:39:47 +33,M,02:02:54,04:39:47 +53,W,02:02:49,04:39:47 +51,M,02:00:19,04:39:47 +40,M,02:03:16,04:39:47 +29,W,01:55:48,04:39:47 +34,M,01:58:32,04:39:47 +31,W,01:58:44,04:39:47 +43,W,01:57:22,04:39:48 +33,W,02:03:52,04:39:48 +45,W,01:59:09,04:39:48 +46,M,01:54:19,04:39:48 +27,W,01:58:44,04:39:48 +47,M,01:58:50,04:39:48 +19,M,01:57:55,04:39:49 +45,M,01:57:33,04:39:49 +27,M,01:53:53,04:39:49 +32,M,02:04:35,04:39:50 +31,M,01:59:11,04:39:50 +28,M,01:56:16,04:39:50 +24,M,02:01:44,04:39:51 +43,M,02:08:31,04:39:51 +73,M,02:09:41,04:39:51 +44,M,02:06:59,04:39:51 +51,M,02:10:52,04:39:51 +32,M,01:59:02,04:39:51 +25,M,01:58:56,04:39:51 +58,M,01:59:09,04:39:51 +38,M,01:53:08,04:39:52 +39,M,01:59:16,04:39:53 +46,M,01:59:42,04:39:53 +28,M,01:59:32,04:39:54 +31,M,01:59:40,04:39:54 +26,M,01:47:26,04:39:54 +46,M,02:00:30,04:39:55 +25,W,02:07:03,04:39:55 +32,M,02:16:31,04:39:55 +28,M,02:01:08,04:39:55 +49,M,01:53:20,04:39:55 +37,M,01:56:23,04:39:56 +57,M,01:52:49,04:39:56 +39,M,02:05:42,04:39:56 +47,M,01:59:17,04:39:57 +46,M,01:58:18,04:39:57 +42,W,01:54:17,04:39:57 +37,W,02:03:10,04:39:57 +30,W,02:03:32,04:39:58 +44,W,02:03:36,04:39:58 +28,M,02:09:05,04:39:58 +44,W,02:01:33,04:39:58 +42,M,02:09:28,04:39:59 +52,M,01:57:22,04:39:59 +36,M,01:58:23,04:39:59 +42,W,02:10:08,04:39:59 +39,M,01:58:44,04:39:59 +33,M,01:55:35,04:40:00 +48,W,02:01:15,04:40:00 +33,M,01:53:21,04:40:01 +43,M,02:02:52,04:40:01 +32,M,01:58:07,04:40:01 +29,M,01:49:18,04:40:01 +31,W,01:59:14,04:40:02 +46,M,02:04:17,04:40:02 +26,W,02:11:03,04:40:02 +42,M,01:55:03,04:40:02 +37,M,02:00:40,04:40:02 +30,W,02:08:59,04:40:02 +36,M,02:00:06,04:40:02 +24,W,01:58:18,04:40:03 +51,M,02:06:54,04:40:04 +54,M,01:54:00,04:40:04 +37,W,02:03:33,04:40:04 +52,M,02:04:01,04:40:04 +31,M,02:03:57,04:40:04 +47,W,02:07:59,04:40:05 +27,W,01:58:01,04:40:05 +35,M,01:49:48,04:40:06 +37,M,01:59:22,04:40:07 +42,M,01:54:14,04:40:08 +42,M,01:55:58,04:40:08 +46,M,01:55:12,04:40:08 +47,W,02:03:51,04:40:08 +48,M,01:57:46,04:40:09 +48,M,01:58:16,04:40:09 +41,M,02:08:36,04:40:10 +46,M,01:47:53,04:40:10 +56,W,01:58:44,04:40:10 +40,M,02:01:54,04:40:11 +38,M,01:54:45,04:40:11 +34,M,02:03:18,04:40:11 +31,M,02:01:16,04:40:11 +46,M,01:57:43,04:40:12 +34,M,01:56:42,04:40:13 +52,W,01:57:05,04:40:13 +39,M,01:58:37,04:40:13 +47,M,02:08:53,04:40:14 +58,W,02:11:56,04:40:14 +46,W,02:05:16,04:40:14 +47,W,02:07:56,04:40:15 +32,M,01:57:23,04:40:15 +25,W,01:57:56,04:40:15 +32,M,02:10:02,04:40:15 +49,M,01:57:58,04:40:15 +43,M,02:04:03,04:40:16 +44,M,01:55:16,04:40:16 +46,W,02:01:42,04:40:16 +53,W,02:08:07,04:40:16 +51,M,01:59:06,04:40:17 +36,W,02:00:41,04:40:17 +28,M,01:55:32,04:40:17 +29,M,02:00:10,04:40:18 +44,M,02:02:47,04:40:18 +23,M,01:55:06,04:40:18 +55,M,01:51:52,04:40:18 +32,W,02:02:10,04:40:18 +30,W,01:51:08,04:40:18 +33,M,02:03:54,04:40:19 +55,M,01:59:54,04:40:19 +25,M,01:59:45,04:40:19 +43,M,01:53:06,04:40:19 +34,M,01:58:06,04:40:20 +49,W,02:06:39,04:40:20 +48,M,02:02:18,04:40:20 +45,M,01:55:24,04:40:20 +51,W,02:00:04,04:40:21 +43,M,02:08:07,04:40:21 +36,M,01:59:31,04:40:21 +51,M,01:55:46,04:40:21 +36,W,01:59:47,04:40:21 +27,M,01:57:40,04:40:21 +30,W,02:02:34,04:40:22 +28,M,02:03:58,04:40:22 +54,M,01:57:45,04:40:22 +32,M,01:59:39,04:40:23 +23,M,01:52:12,04:40:23 +39,W,02:08:10,04:40:23 +34,W,02:00:45,04:40:24 +44,M,01:53:41,04:40:24 +40,W,02:02:09,04:40:24 +37,W,01:58:18,04:40:25 +39,M,02:06:49,04:40:25 +48,M,01:57:10,04:40:25 +33,M,01:54:34,04:40:26 +39,M,01:55:58,04:40:26 +54,M,02:02:22,04:40:26 +32,W,02:00:37,04:40:26 +38,M,01:53:46,04:40:26 +44,M,02:00:43,04:40:26 +38,M,02:06:26,04:40:26 +30,W,02:00:58,04:40:26 +34,M,01:56:06,04:40:27 +40,W,02:02:50,04:40:27 +40,W,02:04:48,04:40:27 +26,W,02:06:07,04:40:28 +42,M,01:54:25,04:40:28 +32,M,02:05:34,04:40:28 +45,W,01:55:04,04:40:28 +54,M,02:11:04,04:40:29 +30,W,01:53:57,04:40:29 +26,W,01:57:54,04:40:29 +34,M,02:10:36,04:40:29 +26,M,01:59:30,04:40:30 +32,W,02:07:03,04:40:30 +26,M,01:52:39,04:40:30 +33,W,02:03:04,04:40:30 +51,M,02:08:37,04:40:30 +26,M,02:00:16,04:40:31 +30,W,02:11:39,04:40:31 +38,M,01:54:17,04:40:31 +45,W,01:54:24,04:40:31 +25,W,02:01:34,04:40:32 +43,M,02:01:52,04:40:33 +24,W,01:59:59,04:40:33 +51,W,01:58:18,04:40:34 +40,M,02:06:05,04:40:34 +36,M,01:55:58,04:40:34 +49,M,02:02:07,04:40:34 +46,M,02:00:52,04:40:34 +51,M,02:05:01,04:40:34 +37,M,02:04:57,04:40:34 +50,M,01:50:31,04:40:35 +63,M,02:02:31,04:40:35 +41,M,02:01:54,04:40:35 +37,W,02:01:32,04:40:35 +48,M,02:07:38,04:40:35 +46,M,02:01:34,04:40:36 +52,M,01:54:49,04:40:36 +38,W,02:15:34,04:40:36 +48,M,01:56:03,04:40:36 +49,M,02:05:20,04:40:36 +47,M,02:04:07,04:40:37 +38,M,01:56:43,04:40:38 +39,M,01:55:37,04:40:38 +41,M,01:57:54,04:40:38 +30,M,01:54:58,04:40:38 +41,M,01:54:33,04:40:38 +38,M,02:00:03,04:40:38 +30,W,01:58:32,04:40:39 +29,M,01:56:17,04:40:39 +51,M,01:58:31,04:40:39 +30,W,02:08:41,04:40:39 +28,W,02:01:03,04:40:39 +53,M,01:53:51,04:40:40 +54,M,02:12:40,04:40:40 +36,M,01:56:04,04:40:40 +35,M,02:05:41,04:40:40 +36,M,02:04:17,04:40:40 +53,M,01:44:24,04:40:40 +30,M,02:00:49,04:40:41 +26,W,01:57:52,04:40:41 +42,M,02:08:48,04:40:42 +54,M,02:00:35,04:40:42 +41,M,01:57:52,04:40:43 +46,M,02:03:49,04:40:43 +36,M,01:58:27,04:40:43 +45,M,01:52:37,04:40:43 +41,M,01:56:36,04:40:43 +42,M,02:04:52,04:40:43 +50,M,01:35:36,04:40:43 +46,W,02:04:00,04:40:43 +38,M,01:54:32,04:40:44 +30,M,02:07:49,04:40:44 +37,M,01:54:50,04:40:44 +41,W,01:55:13,04:40:44 +40,M,01:51:58,04:40:44 +41,M,01:44:52,04:40:45 +29,W,02:01:33,04:40:45 +61,W,02:05:13,04:40:46 +41,M,01:49:33,04:40:47 +21,W,01:59:46,04:40:48 +48,M,01:51:22,04:40:48 +46,M,02:03:40,04:40:48 +52,M,01:49:20,04:40:48 +46,M,02:02:35,04:40:49 +40,W,02:06:02,04:40:49 +27,M,01:50:23,04:40:49 +28,W,01:59:37,04:40:49 +25,W,01:57:14,04:40:49 +39,W,02:00:06,04:40:49 +45,W,01:55:18,04:40:49 +30,M,02:00:44,04:40:50 +27,M,02:04:36,04:40:50 +43,M,01:58:43,04:40:51 +48,M,02:02:25,04:40:51 +42,W,02:07:28,04:40:51 +35,W,02:01:09,04:40:51 +31,M,01:54:32,04:40:51 +25,W,01:59:38,04:40:52 +44,M,01:53:07,04:40:52 +40,M,01:54:03,04:40:52 +28,M,02:09:16,04:40:53 +38,W,02:06:44,04:40:53 +47,M,01:58:38,04:40:53 +46,M,02:02:08,04:40:53 +40,M,01:52:40,04:40:53 +46,M,02:01:00,04:40:53 +31,M,02:03:44,04:40:53 +39,W,01:52:48,04:40:54 +38,W,02:01:42,04:40:54 +47,M,02:00:57,04:40:54 +36,M,02:11:47,04:40:55 +46,M,02:04:29,04:40:55 +40,M,01:58:47,04:40:55 +67,M,02:05:29,04:40:55 +31,M,01:53:02,04:40:56 +48,M,02:13:04,04:40:56 +35,M,01:56:02,04:40:56 +71,M,02:04:43,04:40:56 +33,W,02:05:11,04:40:57 +40,M,02:00:40,04:40:58 +38,M,01:52:22,04:40:58 +26,W,01:50:48,04:40:58 +41,M,01:52:50,04:40:58 +28,M,02:09:58,04:40:59 +47,M,02:00:20,04:40:59 +47,W,02:02:15,04:40:59 +43,M,01:54:30,04:41:00 +41,M,02:01:13,04:41:00 +40,M,02:03:33,04:41:00 +47,W,02:07:47,04:41:00 +37,M,02:07:22,04:41:01 +32,M,02:05:18,04:41:01 +41,W,02:07:31,04:41:01 +60,M,01:57:13,04:41:01 +50,M,01:59:29,04:41:03 +32,M,02:05:33,04:41:03 +17,M,02:13:27,04:41:03 +42,W,02:06:36,04:41:03 +29,W,01:59:41,04:41:03 +49,M,01:57:28,04:41:03 +49,M,01:53:17,04:41:04 +63,M,01:59:06,04:41:04 +37,M,02:03:38,04:41:05 +63,M,02:05:33,04:41:05 +61,M,02:04:26,04:41:05 +41,M,01:54:25,04:41:05 +37,W,02:10:27,04:41:05 +53,M,02:03:08,04:41:06 +46,M,01:59:27,04:41:06 +24,M,02:13:18,04:41:06 +49,M,01:57:57,04:41:06 +38,M,02:01:56,04:41:06 +60,M,01:57:09,04:41:06 +43,M,01:59:06,04:41:06 +39,M,02:08:13,04:41:07 +49,M,02:00:32,04:41:07 +63,M,01:59:09,04:41:07 +41,M,01:54:20,04:41:07 +42,W,02:10:34,04:41:07 +32,M,01:45:52,04:41:08 +43,W,02:10:03,04:41:09 +47,M,01:42:54,04:41:10 +35,M,02:07:31,04:41:10 +44,M,02:01:58,04:41:10 +28,W,02:05:05,04:41:11 +33,W,01:56:21,04:41:11 +44,M,02:01:13,04:41:11 +47,M,02:00:15,04:41:11 +43,W,02:09:59,04:41:11 +27,W,02:07:10,04:41:12 +45,M,02:04:42,04:41:12 +41,W,02:00:16,04:41:12 +48,M,02:08:01,04:41:12 +51,W,02:01:17,04:41:12 +50,M,02:02:00,04:41:12 +42,W,02:01:54,04:41:12 +48,M,02:01:26,04:41:13 +51,M,01:54:20,04:41:13 +41,W,02:07:31,04:41:13 +27,M,02:04:12,04:41:14 +31,M,02:06:14,04:41:14 +40,M,01:57:27,04:41:14 +37,M,01:54:11,04:41:14 +31,W,02:08:22,04:41:15 +45,M,02:03:39,04:41:15 +45,M,01:54:48,04:41:15 +33,M,02:11:15,04:41:16 +47,M,01:48:49,04:41:16 +51,M,02:18:12,04:41:16 +55,W,01:56:19,04:41:16 +45,M,01:51:11,04:41:16 +52,M,01:57:17,04:41:17 +25,M,02:04:26,04:41:17 +52,M,01:52:22,04:41:18 +44,W,02:07:49,04:41:18 +45,M,02:00:33,04:41:18 +37,M,02:00:49,04:41:18 +42,M,01:58:55,04:41:19 +53,M,01:55:33,04:41:19 +37,M,02:02:42,04:41:19 +55,M,01:53:18,04:41:20 +39,M,01:57:22,04:41:20 +56,M,02:01:03,04:41:20 +48,W,02:12:49,04:41:20 +41,M,01:54:22,04:41:21 +59,M,01:59:06,04:41:21 +26,M,02:01:52,04:41:21 +58,W,02:02:20,04:41:21 +39,M,01:59:30,04:41:22 +40,W,02:03:40,04:41:22 +55,M,02:09:41,04:41:22 +33,M,02:05:48,04:41:22 +37,M,02:00:02,04:41:22 +42,W,02:02:38,04:41:22 +37,W,01:57:41,04:41:22 +34,M,01:58:03,04:41:22 +44,W,02:04:21,04:41:23 +39,M,02:01:32,04:41:24 +57,M,02:03:34,04:41:24 +46,M,01:52:31,04:41:24 +28,M,01:49:41,04:41:24 +52,M,02:00:53,04:41:25 +43,M,01:58:58,04:41:26 +29,M,01:51:05,04:41:26 +27,W,02:04:59,04:41:27 +33,W,01:53:04,04:41:27 +33,W,02:05:56,04:41:28 +45,M,01:53:11,04:41:28 +27,M,01:52:04,04:41:28 +46,W,01:57:54,04:41:28 +54,M,02:00:54,04:41:28 +43,W,02:07:37,04:41:28 +30,W,02:00:33,04:41:28 +48,M,01:55:15,04:41:28 +44,W,01:59:01,04:41:29 +47,M,01:56:06,04:41:29 +32,W,02:00:25,04:41:30 +41,M,02:01:10,04:41:30 +39,M,01:52:25,04:41:30 +32,W,01:57:12,04:41:31 +37,M,01:54:12,04:41:31 +46,M,02:02:39,04:41:31 +35,W,01:59:44,04:41:31 +50,M,02:03:58,04:41:31 +40,M,01:45:12,04:41:31 +49,M,01:42:55,04:41:31 +53,M,01:58:36,04:41:32 +40,W,02:12:36,04:41:32 +52,W,02:02:02,04:41:32 +58,W,01:51:56,04:41:32 +48,M,02:01:35,04:41:33 +28,M,02:01:15,04:41:33 +52,M,01:59:16,04:41:33 +37,W,02:10:43,04:41:33 +78,M,01:59:38,04:41:33 +61,M,01:57:55,04:41:34 +35,M,01:58:51,04:41:34 +30,W,02:05:40,04:41:34 +47,W,02:06:04,04:41:34 +33,W,02:00:37,04:41:34 +35,W,01:53:09,04:41:35 +36,M,02:06:01,04:41:35 +40,W,02:02:18,04:41:37 +49,W,01:56:48,04:41:37 +29,M,01:55:09,04:41:38 +37,M,01:58:07,04:41:38 +63,M,02:04:16,04:41:39 +37,M,02:00:59,04:41:39 +50,M,02:06:46,04:41:39 +36,M,01:55:33,04:41:39 +43,M,01:55:37,04:41:40 +36,W,01:56:44,04:41:40 +25,W,02:01:57,04:41:40 +43,M,01:57:33,04:41:40 +36,M,02:02:27,04:41:40 +25,W,02:00:56,04:41:41 +33,M,02:00:41,04:41:41 +37,W,01:57:32,04:41:41 +31,W,02:14:58,04:41:41 +38,M,02:01:04,04:41:41 +64,M,02:07:02,04:41:42 +45,M,01:55:48,04:41:42 +57,W,02:09:28,04:41:42 +49,M,01:58:51,04:41:42 +45,M,01:56:06,04:41:42 +39,M,02:02:20,04:41:42 +38,W,02:03:34,04:41:42 +37,M,01:53:38,04:41:42 +32,M,01:58:49,04:41:42 +41,M,01:56:14,04:41:43 +44,M,02:11:33,04:41:44 +55,M,02:02:18,04:41:44 +28,M,02:08:16,04:41:44 +32,M,01:53:34,04:41:45 +35,M,02:06:44,04:41:45 +46,M,01:54:38,04:41:46 +45,M,01:59:18,04:41:46 +41,W,02:05:14,04:41:46 +29,M,01:57:18,04:41:46 +54,W,02:07:43,04:41:47 +39,M,02:03:24,04:41:47 +23,W,02:06:35,04:41:47 +49,M,02:04:15,04:41:48 +56,M,02:10:33,04:41:48 +32,M,02:10:47,04:41:48 +48,M,02:07:42,04:41:48 +26,M,01:55:13,04:41:48 +39,M,02:03:13,04:41:49 +39,M,02:02:03,04:41:49 +49,M,01:59:31,04:41:49 +43,W,02:09:08,04:41:50 +39,M,02:03:31,04:41:50 +26,W,02:04:14,04:41:50 +51,W,01:53:31,04:41:50 +46,M,01:48:20,04:41:50 +58,W,01:58:19,04:41:50 +35,M,02:10:20,04:41:50 +25,W,01:59:22,04:41:51 +24,M,01:55:55,04:41:51 +40,W,02:02:26,04:41:51 +43,M,02:00:51,04:41:51 +51,M,02:03:05,04:41:51 +30,W,02:04:31,04:41:51 +39,W,02:00:35,04:41:52 +44,W,02:08:12,04:41:52 +31,W,02:00:43,04:41:53 +32,M,02:00:23,04:41:53 +44,M,02:11:56,04:41:53 +53,M,01:59:13,04:41:54 +43,M,01:53:00,04:41:54 +29,W,01:58:01,04:41:54 +43,W,02:00:15,04:41:54 +28,W,02:00:39,04:41:55 +51,W,02:08:57,04:41:55 +25,W,02:07:14,04:41:56 +51,W,02:03:22,04:41:56 +26,M,02:03:39,04:41:56 +37,M,02:10:04,04:41:56 +29,W,01:59:26,04:41:56 +47,M,02:01:53,04:41:56 +50,M,02:02:27,04:41:56 +41,M,02:01:34,04:41:56 +45,W,02:03:26,04:41:57 +44,M,02:10:30,04:41:57 +33,W,01:51:18,04:41:58 +30,W,02:07:08,04:41:58 +35,M,02:08:25,04:41:58 +43,W,01:59:42,04:41:59 +28,W,01:59:59,04:41:59 +41,M,01:58:40,04:41:59 +41,W,02:05:58,04:41:59 +49,M,01:52:46,04:42:00 +51,M,02:04:42,04:42:00 +47,M,01:58:48,04:42:00 +27,W,02:02:30,04:42:00 +35,M,01:48:51,04:42:01 +31,W,02:08:56,04:42:02 +48,M,02:03:45,04:42:02 +45,M,02:03:10,04:42:03 +35,M,01:53:51,04:42:03 +23,W,01:57:23,04:42:04 +51,M,01:55:30,04:42:04 +38,M,01:52:48,04:42:04 +50,M,01:57:47,04:42:04 +29,M,02:10:08,04:42:05 +52,W,02:03:33,04:42:05 +52,W,02:00:31,04:42:05 +37,M,01:58:42,04:42:05 +40,M,01:51:11,04:42:05 +61,M,02:00:04,04:42:05 +54,W,02:01:20,04:42:06 +26,W,01:57:22,04:42:06 +27,W,02:00:38,04:42:06 +44,W,02:05:58,04:42:06 +46,M,01:57:28,04:42:06 +37,M,01:54:38,04:42:06 +29,W,02:01:55,04:42:06 +35,M,01:57:10,04:42:06 +41,M,01:53:01,04:42:06 +43,W,01:57:35,04:42:07 +30,M,01:56:15,04:42:07 +44,W,01:56:39,04:42:07 +36,M,01:59:07,04:42:07 +52,M,01:59:13,04:42:07 +32,M,01:59:31,04:42:07 +48,M,01:54:01,04:42:08 +36,W,01:56:17,04:42:08 +41,W,02:01:30,04:42:08 +31,M,02:04:27,04:42:09 +47,M,02:03:19,04:42:09 +45,M,02:00:42,04:42:09 +41,M,02:04:06,04:42:10 +40,W,02:01:03,04:42:10 +35,W,01:57:26,04:42:10 +38,M,01:58:17,04:42:10 +32,M,01:47:11,04:42:10 +34,W,01:58:54,04:42:11 +32,W,02:02:05,04:42:11 +42,M,02:08:27,04:42:11 +51,M,02:04:40,04:42:12 +25,W,01:54:46,04:42:12 +52,M,01:54:56,04:42:13 +34,M,02:04:42,04:42:13 +49,M,01:55:05,04:42:14 +46,M,02:07:21,04:42:14 +28,M,02:08:09,04:42:15 +40,M,02:02:45,04:42:15 +40,M,02:03:58,04:42:16 +29,M,02:05:53,04:42:16 +40,W,02:07:25,04:42:16 +34,M,01:54:34,04:42:16 +41,M,01:59:17,04:42:16 +42,M,01:58:44,04:42:17 +56,W,02:08:24,04:42:17 +46,M,02:00:54,04:42:17 +30,M,01:49:21,04:42:18 +40,M,01:58:47,04:42:19 +44,W,02:08:17,04:42:19 +53,W,01:54:28,04:42:20 +46,W,02:07:52,04:42:20 +47,M,02:00:58,04:42:20 +21,M,01:57:59,04:42:20 +29,W,02:04:25,04:42:20 +30,M,02:04:04,04:42:20 +49,W,01:55:58,04:42:21 +25,M,02:07:41,04:42:21 +53,M,01:56:03,04:42:21 +35,W,02:05:02,04:42:22 +26,M,02:06:25,04:42:23 +52,M,01:59:36,04:42:23 +40,W,02:13:19,04:42:23 +33,M,01:58:13,04:42:23 +46,M,02:07:30,04:42:23 +32,M,01:59:01,04:42:23 +44,M,01:43:40,04:42:23 +32,M,01:59:47,04:42:24 +30,M,01:56:04,04:42:25 +23,M,02:09:32,04:42:25 +42,M,02:03:47,04:42:26 +42,M,01:54:42,04:42:26 +43,M,01:56:21,04:42:26 +19,W,01:58:16,04:42:27 +47,M,01:48:52,04:42:27 +40,M,01:56:49,04:42:27 +34,W,02:04:19,04:42:27 +50,M,02:02:47,04:42:27 +42,M,02:01:46,04:42:28 +58,M,01:59:17,04:42:29 +41,M,01:45:20,04:42:29 +49,M,01:55:28,04:42:29 +42,M,01:46:07,04:42:29 +53,W,01:58:23,04:42:29 +31,M,01:51:52,04:42:30 +39,W,01:59:40,04:42:31 +40,W,01:54:10,04:42:31 +33,M,02:01:09,04:42:31 +36,M,02:00:36,04:42:31 +40,M,01:51:34,04:42:32 +69,M,02:02:10,04:42:32 +24,M,01:52:29,04:42:32 +42,M,01:55:24,04:42:32 +31,M,01:57:12,04:42:32 +41,M,01:58:29,04:42:33 +30,M,02:01:52,04:42:33 +40,M,01:53:49,04:42:33 +44,M,01:51:43,04:42:34 +43,M,01:51:07,04:42:34 +46,M,02:07:14,04:42:34 +52,M,02:03:56,04:42:34 +38,M,01:58:22,04:42:35 +41,M,01:56:08,04:42:35 +32,W,02:04:03,04:42:35 +45,M,02:06:56,04:42:35 +44,M,01:58:28,04:42:36 +33,M,02:13:36,04:42:36 +35,W,01:56:43,04:42:37 +45,M,02:07:20,04:42:38 +42,M,01:55:43,04:42:38 +25,M,02:03:48,04:42:38 +29,W,02:06:59,04:42:39 +41,M,01:59:06,04:42:39 +28,W,02:07:41,04:42:39 +37,M,02:11:00,04:42:39 +26,M,02:03:27,04:42:39 +33,M,01:56:11,04:42:39 +34,M,01:59:11,04:42:40 +24,M,01:51:32,04:42:41 +33,W,02:01:20,04:42:41 +34,W,02:05:59,04:42:41 +46,M,01:58:51,04:42:41 +46,M,02:00:08,04:42:41 +54,M,02:03:16,04:42:41 +47,M,01:58:52,04:42:42 +31,W,02:00:32,04:42:42 +49,W,02:08:37,04:42:42 +58,M,01:58:53,04:42:42 +28,W,02:00:34,04:42:43 +47,M,02:04:41,04:42:43 +30,W,02:04:33,04:42:43 +36,M,02:02:10,04:42:43 +32,M,02:03:37,04:42:44 +29,M,02:04:19,04:42:44 +42,M,02:04:44,04:42:44 +28,M,02:02:11,04:42:44 +31,M,02:10:21,04:42:44 +45,M,01:59:07,04:42:44 +36,M,02:01:23,04:42:45 +39,W,02:10:03,04:42:45 +38,M,02:07:25,04:42:45 +43,M,01:43:42,04:42:46 +42,M,02:09:47,04:42:46 +45,M,01:56:46,04:42:46 +33,M,01:59:29,04:42:46 +43,W,01:57:39,04:42:46 +30,W,02:07:52,04:42:46 +44,M,01:55:21,04:42:47 +43,M,01:56:13,04:42:47 +33,M,02:03:22,04:42:47 +34,M,01:56:20,04:42:47 +25,W,02:07:56,04:42:47 +28,M,01:59:03,04:42:47 +45,M,02:00:34,04:42:48 +58,W,02:06:54,04:42:48 +56,M,02:03:31,04:42:48 +27,W,02:04:12,04:42:48 +35,W,02:04:34,04:42:49 +23,M,02:26:59,04:42:49 +45,M,02:02:04,04:42:49 +35,M,01:56:34,04:42:49 +42,M,02:15:00,04:42:49 +52,M,02:02:52,04:42:50 +52,W,02:01:57,04:42:50 +28,M,01:53:02,04:42:50 +39,M,02:05:11,04:42:51 +32,W,02:14:07,04:42:51 +41,M,01:59:01,04:42:51 +50,M,02:06:27,04:42:52 +45,W,02:02:22,04:42:52 +44,M,02:00:07,04:42:52 +42,W,02:07:49,04:42:52 +50,W,02:09:10,04:42:53 +62,M,01:59:23,04:42:53 +55,M,02:10:17,04:42:53 +52,W,02:06:31,04:42:53 +37,M,02:00:13,04:42:54 +51,M,02:02:03,04:42:54 +37,W,02:07:03,04:42:54 +54,W,01:56:11,04:42:54 +31,M,02:05:51,04:42:54 +52,M,01:55:23,04:42:54 +40,M,01:59:29,04:42:54 +27,W,01:56:09,04:42:54 +45,M,02:00:07,04:42:54 +52,W,02:00:55,04:42:55 +29,M,01:56:45,04:42:55 +42,W,01:58:28,04:42:56 +42,W,02:05:41,04:42:56 +38,W,02:04:15,04:42:56 +32,M,02:06:25,04:42:56 +53,M,01:55:17,04:42:57 +51,W,02:08:10,04:42:57 +40,W,02:01:39,04:42:57 +45,W,02:11:56,04:42:57 +24,M,01:57:26,04:42:58 +43,M,02:00:13,04:42:58 +37,W,02:09:40,04:42:58 +42,M,02:00:06,04:42:58 +40,M,02:00:18,04:42:58 +26,W,01:58:19,04:42:58 +21,W,02:03:36,04:42:59 +63,M,01:55:28,04:42:59 +45,W,02:04:29,04:42:59 +47,M,01:54:14,04:42:59 +44,M,02:03:03,04:42:59 +37,M,01:50:26,04:43:00 +29,M,02:08:40,04:43:00 +27,M,02:05:07,04:43:00 +35,M,01:56:16,04:43:00 +53,M,02:12:20,04:43:00 +28,M,01:56:29,04:43:00 +53,M,02:01:24,04:43:01 +32,W,02:04:03,04:43:01 +59,M,01:58:50,04:43:01 +45,M,01:56:55,04:43:01 +32,W,01:56:37,04:43:01 +39,M,02:02:08,04:43:01 +30,W,02:06:34,04:43:01 +43,M,02:00:01,04:43:02 +29,W,02:07:50,04:43:02 +36,M,02:05:57,04:43:02 +38,M,01:54:11,04:43:02 +41,M,01:51:18,04:43:02 +51,M,02:05:32,04:43:02 +41,W,02:05:53,04:43:02 +30,M,01:55:00,04:43:03 +63,M,01:54:30,04:43:03 +33,M,02:01:26,04:43:03 +33,M,01:59:32,04:43:04 +31,M,02:04:04,04:43:04 +49,M,02:07:22,04:43:04 +35,W,02:02:15,04:43:04 +47,M,02:02:48,04:43:04 +25,M,01:52:23,04:43:04 +43,M,02:02:12,04:43:04 +53,M,02:02:17,04:43:04 +36,M,01:54:06,04:43:05 +52,M,02:03:17,04:43:05 +33,W,02:04:09,04:43:05 +32,M,01:58:29,04:43:05 +26,M,01:55:27,04:43:06 +28,W,01:59:11,04:43:06 +26,W,02:01:52,04:43:06 +41,M,01:54:34,04:43:06 +41,M,01:46:27,04:43:06 +23,M,02:03:26,04:43:07 +38,W,02:03:48,04:43:07 +35,M,02:03:03,04:43:07 +42,M,01:58:22,04:43:07 +41,M,01:56:32,04:43:08 +22,W,01:57:41,04:43:08 +36,M,01:55:52,04:43:08 +49,W,01:58:41,04:43:08 +33,M,02:01:59,04:43:08 +39,W,02:09:41,04:43:08 +40,W,02:03:40,04:43:09 +44,M,02:01:45,04:43:09 +49,M,01:50:30,04:43:09 +43,M,02:03:59,04:43:09 +50,M,02:00:07,04:43:10 +57,M,02:04:33,04:43:10 +57,W,02:10:42,04:43:10 +30,M,01:59:13,04:43:10 +53,M,02:03:30,04:43:11 +24,W,02:03:51,04:43:11 +54,M,02:08:19,04:43:12 +42,M,01:49:56,04:43:12 +41,M,02:16:08,04:43:12 +36,M,01:53:57,04:43:12 +46,W,02:16:02,04:43:12 +52,M,01:53:31,04:43:13 +51,M,01:59:47,04:43:13 +35,M,02:02:01,04:43:13 +34,W,01:58:56,04:43:13 +18,W,02:11:11,04:43:14 +29,M,01:57:09,04:43:14 +38,M,02:18:00,04:43:14 +50,M,02:01:57,04:43:14 +33,W,02:03:58,04:43:15 +28,W,02:07:41,04:43:15 +38,M,01:56:34,04:43:15 +43,M,01:57:26,04:43:15 +33,W,02:08:20,04:43:16 +34,W,02:06:37,04:43:16 +35,M,02:01:27,04:43:16 +56,M,02:01:28,04:43:16 +40,M,02:03:49,04:43:17 +33,M,01:52:44,04:43:17 +28,M,02:04:50,04:43:17 +49,M,01:54:34,04:43:17 +30,M,02:07:48,04:43:17 +45,M,01:54:52,04:43:17 +43,M,02:12:34,04:43:17 +30,M,01:56:35,04:43:18 +47,M,02:04:47,04:43:18 +40,M,02:03:43,04:43:18 +45,W,02:03:30,04:43:19 +41,M,02:08:29,04:43:19 +36,M,01:59:13,04:43:20 +53,M,02:03:21,04:43:20 +46,M,01:59:26,04:43:20 +37,W,02:07:38,04:43:20 +31,W,02:03:12,04:43:20 +38,W,02:00:47,04:43:20 +40,M,02:06:10,04:43:20 +55,W,02:02:27,04:43:20 +40,M,02:09:35,04:43:20 +53,M,02:00:09,04:43:20 +28,W,01:57:33,04:43:22 +43,M,02:05:04,04:43:22 +44,W,01:51:34,04:43:22 +46,M,02:03:57,04:43:22 +38,W,02:06:12,04:43:22 +56,M,02:07:33,04:43:22 +33,W,02:05:40,04:43:23 +48,M,01:59:04,04:43:23 +44,M,02:05:47,04:43:23 +32,W,02:11:40,04:43:23 +39,M,01:53:20,04:43:23 +57,M,01:52:53,04:43:24 +53,M,02:06:32,04:43:24 +28,M,02:04:00,04:43:24 +21,M,01:54:51,04:43:24 +52,M,01:59:30,04:43:25 +29,W,02:00:56,04:43:25 +32,M,02:03:41,04:43:26 +60,M,02:10:44,04:43:26 +33,M,01:58:14,04:43:26 +49,M,01:54:53,04:43:26 +53,M,01:54:03,04:43:27 +45,M,01:59:55,04:43:27 +44,M,01:53:22,04:43:27 +41,M,01:56:51,04:43:27 +24,M,02:02:50,04:43:28 +25,W,02:05:41,04:43:28 +26,M,01:54:03,04:43:28 +38,W,02:00:40,04:43:29 +37,M,02:06:20,04:43:29 +42,M,01:54:42,04:43:29 +47,M,02:03:12,04:43:29 +43,M,01:59:27,04:43:29 +37,M,01:59:46,04:43:30 +33,M,01:54:02,04:43:30 +49,M,02:13:25,04:43:30 +34,M,02:01:22,04:43:30 +44,W,02:07:09,04:43:31 +43,M,01:56:28,04:43:31 +29,M,02:00:56,04:43:31 +37,M,02:05:44,04:43:31 +26,W,01:57:04,04:43:32 +26,M,01:56:48,04:43:32 +35,M,01:52:00,04:43:32 +40,M,02:00:53,04:43:32 +38,M,01:52:47,04:43:32 +37,W,02:03:20,04:43:32 +55,W,02:00:47,04:43:33 +26,W,02:07:54,04:43:33 +39,W,02:03:35,04:43:33 +44,M,01:54:20,04:43:33 +32,M,01:55:18,04:43:33 +45,M,02:02:44,04:43:34 +31,M,02:05:21,04:43:34 +46,M,02:12:34,04:43:35 +40,M,02:07:13,04:43:35 +29,M,01:46:41,04:43:35 +46,M,02:07:23,04:43:35 +32,W,02:08:47,04:43:36 +47,M,02:06:27,04:43:36 +42,W,02:06:29,04:43:36 +35,M,02:04:46,04:43:36 +30,M,02:06:57,04:43:36 +51,M,01:54:04,04:43:36 +37,M,02:04:32,04:43:36 +36,M,02:12:22,04:43:37 +47,M,02:07:53,04:43:37 +38,M,01:59:17,04:43:37 +49,M,02:01:12,04:43:37 +31,M,01:42:39,04:43:37 +40,M,02:13:34,04:43:38 +44,M,02:01:42,04:43:38 +32,W,01:54:58,04:43:38 +38,M,02:00:41,04:43:38 +41,W,01:59:30,04:43:39 +38,M,01:52:47,04:43:39 +50,M,01:50:13,04:43:39 +44,M,02:05:49,04:43:39 +42,M,02:03:57,04:43:40 +35,W,02:03:56,04:43:40 +41,M,01:45:34,04:43:40 +20,M,01:58:16,04:43:41 +54,M,02:07:36,04:43:41 +23,M,02:00:01,04:43:41 +46,W,02:08:17,04:43:41 +30,W,02:05:22,04:43:41 +42,M,01:57:55,04:43:41 +41,W,02:03:23,04:43:41 +34,W,02:01:49,04:43:42 +22,M,01:56:55,04:43:42 +32,W,02:08:03,04:43:42 +35,M,02:04:04,04:43:42 +31,W,02:04:51,04:43:42 +52,M,02:05:22,04:43:42 +43,W,02:01:41,04:43:43 +30,M,01:59:54,04:43:43 +28,W,02:08:02,04:43:43 +34,M,01:59:28,04:43:43 +27,M,01:58:50,04:43:43 +33,W,02:01:08,04:43:44 +31,M,01:59:06,04:43:44 +23,M,02:03:11,04:43:44 +47,M,01:49:13,04:43:44 +48,W,01:58:40,04:43:44 +25,W,02:02:50,04:43:44 +32,W,02:08:39,04:43:44 +34,W,02:10:49,04:43:45 +57,M,02:00:14,04:43:45 +58,M,01:56:37,04:43:45 +35,M,01:59:39,04:43:46 +38,M,02:03:50,04:43:46 +39,M,01:52:47,04:43:46 +45,W,02:09:31,04:43:46 +48,M,02:12:11,04:43:47 +36,M,02:12:19,04:43:47 +52,M,01:56:29,04:43:47 +66,M,02:08:25,04:43:48 +23,M,01:54:13,04:43:48 +47,W,02:02:59,04:43:48 +33,M,01:53:53,04:43:48 +62,M,01:58:16,04:43:49 +57,M,02:08:33,04:43:49 +58,M,02:03:15,04:43:49 +29,M,01:47:53,04:43:49 +56,M,01:56:29,04:43:50 +28,W,02:00:57,04:43:50 +26,M,01:51:21,04:43:50 +56,M,01:59:11,04:43:51 +51,M,02:00:15,04:43:52 +41,M,01:51:44,04:43:52 +29,W,02:07:16,04:43:52 +40,M,01:57:44,04:43:52 +57,M,02:01:40,04:43:52 +41,M,01:56:16,04:43:52 +30,W,02:10:23,04:43:52 +46,M,01:56:06,04:43:52 +28,M,01:57:19,04:43:53 +30,M,01:54:30,04:43:53 +42,M,02:02:23,04:43:53 +24,W,02:04:29,04:43:53 +37,W,02:04:47,04:43:53 +38,M,01:59:31,04:43:53 +34,M,02:05:31,04:43:53 +46,M,02:08:15,04:43:54 +33,M,02:05:10,04:43:54 +51,M,02:00:35,04:43:54 +69,M,02:04:32,04:43:55 +37,M,02:01:36,04:43:55 +56,M,02:15:00,04:43:55 +48,M,02:07:29,04:43:55 +57,M,02:03:46,04:43:55 +30,W,02:03:44,04:43:56 +60,M,01:55:45,04:43:56 +41,W,02:01:35,04:43:56 +40,W,02:07:37,04:43:57 +62,M,02:01:54,04:43:57 +40,M,01:58:46,04:43:57 +50,M,02:05:48,04:43:57 +34,M,02:04:41,04:43:58 +56,M,02:09:27,04:43:58 +26,M,01:48:21,04:43:58 +42,W,02:05:16,04:43:59 +45,W,02:04:27,04:43:59 +43,M,01:57:17,04:43:59 +46,M,01:56:56,04:43:59 +61,M,02:05:17,04:43:59 +37,M,01:54:39,04:43:59 +30,W,01:58:44,04:44:00 +36,W,02:02:30,04:44:00 +28,W,01:59:41,04:44:00 +28,M,02:03:07,04:44:00 +32,M,02:01:47,04:44:01 +38,W,02:05:17,04:44:01 +20,W,02:00:49,04:44:01 +30,M,01:57:18,04:44:01 +60,M,01:55:56,04:44:01 +44,W,02:00:23,04:44:02 +25,M,02:03:23,04:44:02 +39,W,02:03:36,04:44:02 +46,W,01:58:23,04:44:02 +39,W,01:58:03,04:44:02 +41,M,02:05:40,04:44:02 +39,W,02:03:12,04:44:02 +35,W,01:56:33,04:44:02 +64,M,02:05:24,04:44:02 +40,M,01:46:47,04:44:03 +27,M,01:54:42,04:44:03 +49,M,02:03:27,04:44:03 +32,M,02:02:00,04:44:04 +39,M,02:02:09,04:44:04 +23,W,02:10:37,04:44:04 +39,M,01:58:42,04:44:04 +46,W,02:07:05,04:44:04 +40,M,02:04:39,04:44:05 +35,M,02:01:27,04:44:05 +49,M,02:01:00,04:44:05 +35,W,02:13:04,04:44:05 +50,M,01:53:27,04:44:05 +48,W,02:15:48,04:44:06 +43,W,01:56:07,04:44:06 +33,W,01:50:45,04:44:06 +34,M,02:01:03,04:44:07 +18,W,02:04:31,04:44:08 +49,M,01:58:54,04:44:08 +29,M,01:55:10,04:44:08 +36,W,02:03:09,04:44:09 +40,M,02:08:03,04:44:09 +46,M,02:07:56,04:44:09 +26,M,02:01:43,04:44:10 +33,W,01:54:13,04:44:10 +34,W,02:04:00,04:44:10 +60,W,02:05:49,04:44:10 +46,M,02:04:24,04:44:10 +45,M,01:58:03,04:44:11 +36,M,02:05:55,04:44:11 +41,M,01:55:11,04:44:11 +43,M,01:50:59,04:44:12 +38,M,02:02:36,04:44:12 +36,W,02:04:33,04:44:12 +36,M,02:02:54,04:44:12 +39,M,02:02:00,04:44:12 +44,M,01:58:04,04:44:14 +27,M,01:50:50,04:44:14 +54,M,02:15:02,04:44:14 +59,M,02:04:14,04:44:14 +35,M,02:08:41,04:44:14 +54,M,01:56:16,04:44:15 +28,W,02:16:21,04:44:15 +25,M,01:55:03,04:44:15 +37,W,01:55:16,04:44:15 +41,M,02:06:19,04:44:15 +33,M,02:06:06,04:44:16 +33,M,02:06:25,04:44:16 +27,M,02:09:04,04:44:16 +50,M,02:14:58,04:44:16 +50,W,01:59:28,04:44:16 +61,M,02:03:54,04:44:16 +36,M,01:54:17,04:44:16 +44,M,02:06:23,04:44:17 +59,M,01:55:23,04:44:17 +38,M,02:08:16,04:44:17 +52,M,02:05:28,04:44:18 +42,M,02:02:24,04:44:18 +47,M,02:02:33,04:44:19 +63,M,01:55:53,04:44:19 +47,W,02:04:37,04:44:19 +44,W,02:08:46,04:44:19 +50,M,02:04:51,04:44:19 +29,W,02:10:53,04:44:19 +59,W,02:05:17,04:44:20 +36,M,01:53:55,04:44:20 +40,W,02:12:55,04:44:20 +27,W,02:15:01,04:44:20 +36,W,02:04:56,04:44:20 +25,W,01:50:51,04:44:20 +49,M,02:05:59,04:44:22 +53,M,02:07:54,04:44:22 +43,M,02:03:19,04:44:22 +31,M,02:16:59,04:44:23 +47,M,01:56:06,04:44:24 +41,M,02:01:57,04:44:24 +36,M,02:03:22,04:44:24 +28,M,02:05:22,04:44:24 +45,M,02:05:12,04:44:25 +39,W,02:00:31,04:44:25 +43,M,02:06:33,04:44:25 +65,M,02:07:11,04:44:26 +29,M,01:59:24,04:44:26 +33,W,02:07:38,04:44:27 +36,M,02:02:28,04:44:27 +42,W,02:04:10,04:44:27 +27,M,02:04:16,04:44:28 +40,W,02:00:18,04:44:28 +40,M,02:07:03,04:44:28 +44,M,02:18:37,04:44:28 +34,M,02:06:54,04:44:28 +61,M,02:09:28,04:44:28 +38,W,02:09:56,04:44:28 +35,W,01:58:29,04:44:29 +37,M,01:48:01,04:44:29 +43,M,02:04:52,04:44:29 +56,W,02:09:50,04:44:29 +41,M,02:03:00,04:44:29 +51,W,02:08:22,04:44:29 +49,M,01:56:06,04:44:30 +36,M,01:54:48,04:44:30 +34,M,02:02:23,04:44:30 +60,M,02:06:58,04:44:30 +30,W,01:59:51,04:44:30 +60,M,02:04:45,04:44:31 +33,W,02:00:28,04:44:31 +47,W,01:59:01,04:44:31 +41,M,01:53:33,04:44:31 +32,W,02:06:26,04:44:31 +38,M,01:56:30,04:44:31 +51,M,01:58:07,04:44:31 +41,M,01:56:40,04:44:31 +32,W,02:08:18,04:44:31 +37,M,02:03:43,04:44:32 +45,M,01:59:07,04:44:32 +37,M,01:57:43,04:44:32 +28,M,01:57:40,04:44:32 +46,W,02:03:58,04:44:32 +47,M,01:59:12,04:44:32 +57,M,02:01:57,04:44:33 +30,W,02:03:30,04:44:33 +38,M,02:01:28,04:44:33 +50,M,01:59:49,04:44:33 +36,W,01:57:18,04:44:34 +42,M,01:57:39,04:44:34 +56,M,01:56:57,04:44:34 +39,M,02:06:09,04:44:34 +29,M,02:10:37,04:44:34 +35,M,02:01:51,04:44:34 +40,M,01:59:36,04:44:35 +52,M,02:06:15,04:44:35 +39,M,02:05:30,04:44:35 +45,M,02:08:31,04:44:35 +29,M,01:49:02,04:44:36 +51,M,02:07:04,04:44:36 +52,M,01:59:46,04:44:37 +59,M,02:03:59,04:44:37 +27,M,02:09:10,04:44:37 +42,W,02:01:20,04:44:37 +24,W,02:04:55,04:44:38 +57,M,02:05:08,04:44:38 +37,M,02:05:36,04:44:39 +60,M,02:01:18,04:44:39 +40,M,01:52:56,04:44:39 +35,M,02:05:28,04:44:39 +51,M,02:09:07,04:44:40 +43,M,02:07:30,04:44:40 +30,M,02:09:55,04:44:40 +49,M,01:57:26,04:44:40 +28,W,02:07:21,04:44:40 +45,M,01:55:53,04:44:40 +47,W,02:02:53,04:44:40 +20,M,01:47:43,04:44:41 +19,M,02:01:31,04:44:41 +41,M,01:51:07,04:44:42 +63,M,02:04:07,04:44:42 +60,M,02:02:19,04:44:42 +29,W,02:03:10,04:44:42 +38,M,02:04:31,04:44:43 +35,M,01:59:01,04:44:43 +51,M,02:03:38,04:44:43 +55,M,02:02:04,04:44:44 +41,M,02:01:04,04:44:44 +44,M,01:44:13,04:44:44 +33,M,02:07:38,04:44:44 +59,W,02:00:59,04:44:44 +41,W,01:58:53,04:44:44 +47,M,01:56:49,04:44:44 +28,M,01:56:57,04:44:45 +34,W,02:01:40,04:44:45 +40,W,02:05:03,04:44:45 +23,W,02:06:42,04:44:45 +46,M,02:06:34,04:44:46 +48,M,01:56:45,04:44:46 +50,M,01:59:06,04:44:46 +54,M,01:54:19,04:44:46 +44,W,01:58:45,04:44:47 +31,M,02:05:05,04:44:47 +44,M,02:05:25,04:44:47 +39,W,02:07:45,04:44:47 +49,W,01:58:47,04:44:47 +50,W,02:01:01,04:44:48 +41,M,01:58:53,04:44:49 +35,M,02:01:29,04:44:49 +44,W,02:11:14,04:44:49 +53,M,01:58:37,04:44:50 +43,M,01:59:01,04:44:50 +21,W,01:59:10,04:44:50 +44,M,02:01:20,04:44:50 +56,M,02:10:08,04:44:51 +26,M,02:06:10,04:44:51 +54,M,02:06:21,04:44:51 +49,M,01:56:58,04:44:51 +34,W,02:09:08,04:44:52 +47,W,02:01:12,04:44:52 +30,M,01:59:06,04:44:52 +45,M,01:53:46,04:44:52 +47,W,02:03:48,04:44:53 +40,M,02:03:02,04:44:54 +32,W,02:07:26,04:44:54 +34,M,02:06:19,04:44:54 +49,M,01:54:43,04:44:54 +50,W,02:00:55,04:44:54 +41,M,02:01:03,04:44:54 +30,W,02:08:00,04:44:54 +50,W,02:01:51,04:44:54 +59,M,02:01:56,04:44:55 +47,W,02:06:48,04:44:55 +48,M,02:12:19,04:44:56 +49,M,02:09:26,04:44:57 +36,W,02:04:52,04:44:57 +41,M,02:02:11,04:44:58 +35,M,01:57:55,04:44:58 +45,M,01:51:36,04:44:58 +29,M,01:56:45,04:44:59 +41,M,01:58:17,04:44:59 +45,M,02:09:35,04:44:59 +43,M,02:10:30,04:44:59 +37,M,01:59:54,04:45:00 +50,M,01:58:35,04:45:00 +41,M,02:09:35,04:45:01 +25,W,02:07:15,04:45:01 +41,M,02:01:08,04:45:01 +32,M,02:05:51,04:45:01 +26,M,02:09:49,04:45:01 +38,M,02:06:44,04:45:01 +27,M,02:06:15,04:45:02 +43,M,02:05:17,04:45:02 +52,M,02:02:07,04:45:02 +46,M,02:02:35,04:45:02 +29,M,02:05:48,04:45:02 +61,M,02:06:27,04:45:03 +23,W,02:06:52,04:45:03 +42,M,02:01:55,04:45:03 +53,M,01:59:04,04:45:03 +39,M,02:05:24,04:45:04 +45,M,01:58:31,04:45:04 +45,W,02:09:29,04:45:04 +41,M,02:04:44,04:45:04 +47,M,02:25:35,04:45:04 +37,M,01:55:02,04:45:04 +37,M,01:57:12,04:45:04 +45,M,02:01:41,04:45:05 +36,W,01:58:28,04:45:05 +37,M,02:08:52,04:45:05 +28,M,02:00:42,04:45:05 +43,W,02:00:57,04:45:06 +47,W,02:08:25,04:45:06 +50,M,01:51:42,04:45:06 +48,M,02:02:26,04:45:07 +51,W,02:04:26,04:45:07 +47,M,02:11:15,04:45:07 +50,M,02:02:37,04:45:08 +37,M,01:58:38,04:45:08 +30,W,02:00:56,04:45:09 +45,M,01:51:06,04:45:09 +40,W,02:04:17,04:45:09 +37,W,01:55:20,04:45:09 +60,W,02:05:05,04:45:09 +38,W,02:05:59,04:45:09 +53,M,01:54:36,04:45:10 +31,M,01:52:03,04:45:10 +47,M,02:09:00,04:45:10 +42,W,01:54:41,04:45:11 +25,M,01:48:58,04:45:11 +43,M,01:56:52,04:45:11 +44,M,02:10:37,04:45:12 +40,M,02:02:57,04:45:12 +42,W,02:11:22,04:45:12 +37,M,02:11:29,04:45:13 +38,M,02:01:02,04:45:14 +37,M,02:00:44,04:45:14 +41,W,02:02:18,04:45:14 +52,W,02:14:07,04:45:14 +37,W,02:08:04,04:45:15 +24,W,01:59:43,04:45:15 +36,M,01:59:28,04:45:15 +58,M,02:10:54,04:45:15 +52,M,02:05:42,04:45:16 +19,M,01:45:47,04:45:16 +42,W,01:59:23,04:45:16 +32,W,02:04:37,04:45:16 +45,M,02:02:46,04:45:17 +25,M,01:56:03,04:45:17 +54,M,02:04:31,04:45:17 +50,M,02:06:31,04:45:17 +30,M,02:07:08,04:45:17 +45,W,02:07:04,04:45:18 +36,W,02:02:35,04:45:18 +26,W,01:58:55,04:45:18 +35,M,02:04:37,04:45:19 +37,W,02:01:23,04:45:19 +30,M,02:03:02,04:45:19 +34,M,01:59:33,04:45:20 +50,W,02:08:16,04:45:20 +32,M,02:06:50,04:45:20 +35,M,01:53:36,04:45:20 +33,M,02:05:18,04:45:20 +44,M,02:01:52,04:45:20 +30,W,02:05:49,04:45:21 +41,M,02:01:39,04:45:21 +62,M,01:59:17,04:45:21 +56,W,02:14:23,04:45:21 +50,M,02:01:36,04:45:21 +50,M,02:03:02,04:45:21 +44,M,01:57:38,04:45:22 +38,M,01:54:45,04:45:22 +33,W,02:04:21,04:45:22 +51,M,01:58:41,04:45:24 +43,M,01:58:47,04:45:24 +21,W,02:06:07,04:45:24 +45,M,01:58:33,04:45:24 +24,W,02:06:44,04:45:24 +59,W,01:53:45,04:45:25 +49,M,02:05:02,04:45:25 +27,W,02:12:03,04:45:26 +30,W,02:07:18,04:45:26 +44,W,02:09:00,04:45:27 +48,M,02:06:01,04:45:28 +39,W,02:02:21,04:45:28 +62,M,02:09:21,04:45:28 +42,W,02:00:35,04:45:28 +26,M,01:59:12,04:45:28 +37,M,01:57:06,04:45:28 +44,M,02:11:15,04:45:28 +58,W,01:55:09,04:45:29 +19,M,01:49:46,04:45:29 +49,M,01:56:26,04:45:29 +37,M,01:54:17,04:45:29 +46,W,02:00:21,04:45:29 +30,W,01:59:08,04:45:29 +48,M,02:00:36,04:45:30 +53,M,02:05:58,04:45:30 +42,W,01:57:35,04:45:30 +27,W,02:05:59,04:45:30 +36,W,02:02:43,04:45:30 +38,M,02:07:13,04:45:30 +50,M,02:11:30,04:45:30 +40,M,02:03:57,04:45:30 +41,M,02:06:12,04:45:30 +65,M,02:08:52,04:45:30 +22,W,02:07:20,04:45:31 +39,W,02:15:11,04:45:31 +33,M,01:59:03,04:45:31 +35,M,01:57:57,04:45:31 +30,W,02:07:42,04:45:32 +32,W,02:04:17,04:45:32 +33,M,02:06:12,04:45:33 +45,M,01:57:14,04:45:33 +48,W,02:06:16,04:45:33 +56,M,02:10:18,04:45:33 +37,M,02:00:25,04:45:33 +36,M,02:01:29,04:45:34 +49,M,02:07:12,04:45:34 +52,W,02:07:14,04:45:35 +27,W,02:03:21,04:45:35 +33,W,02:00:46,04:45:36 +38,W,01:58:39,04:45:36 +29,M,02:10:32,04:45:36 +40,W,02:00:45,04:45:37 +47,M,01:54:04,04:45:37 +42,M,02:05:41,04:45:37 +57,M,01:56:53,04:45:38 +56,M,01:59:30,04:45:38 +28,M,01:53:11,04:45:38 +33,W,01:54:50,04:45:38 +47,M,02:03:08,04:45:39 +28,M,02:03:16,04:45:39 +55,M,02:00:00,04:45:39 +39,M,01:57:24,04:45:39 +33,M,02:07:20,04:45:39 +47,M,01:54:09,04:45:39 +44,M,02:03:38,04:45:39 +43,M,01:59:04,04:45:40 +56,M,01:56:01,04:45:41 +54,M,02:08:28,04:45:41 +28,W,02:09:41,04:45:41 +26,W,02:07:36,04:45:41 +29,W,02:07:01,04:45:41 +60,M,01:56:01,04:45:42 +38,W,01:56:23,04:45:42 +46,M,02:01:00,04:45:42 +51,W,02:05:13,04:45:42 +34,M,01:59:46,04:45:43 +36,W,02:00:15,04:45:43 +31,M,02:06:05,04:45:43 +44,M,02:10:29,04:45:44 +35,W,02:08:07,04:45:44 +45,M,02:07:24,04:45:45 +38,M,01:57:18,04:45:45 +44,W,02:16:43,04:45:45 +61,M,02:05:25,04:45:46 +47,W,02:01:52,04:45:46 +40,W,02:00:30,04:45:46 +36,M,02:11:16,04:45:47 +39,W,01:59:44,04:45:47 +25,W,02:06:18,04:45:48 +31,W,02:11:39,04:45:49 +39,W,01:56:23,04:45:49 +27,W,01:55:06,04:45:49 +40,M,02:02:44,04:45:49 +26,M,01:58:35,04:45:49 +41,M,02:01:47,04:45:49 +55,W,02:05:06,04:45:49 +34,M,02:07:06,04:45:49 +35,M,02:00:23,04:45:50 +30,W,02:13:28,04:45:50 +47,M,01:57:59,04:45:50 +51,W,01:58:11,04:45:50 +42,M,02:07:26,04:45:50 +30,M,02:02:18,04:45:50 +47,M,02:05:14,04:45:51 +32,W,02:00:47,04:45:51 +27,W,02:07:43,04:45:51 +29,M,01:54:40,04:45:51 +43,M,02:00:05,04:45:51 +33,M,01:54:32,04:45:51 +38,M,01:58:54,04:45:52 +27,M,02:06:27,04:45:52 +23,W,02:01:00,04:45:52 +31,M,02:06:59,04:45:53 +47,M,01:59:46,04:45:53 +29,W,01:58:46,04:45:53 +53,M,02:11:55,04:45:53 +54,M,02:08:11,04:45:54 +32,W,01:49:56,04:45:55 +37,M,02:00:19,04:45:56 +42,W,02:00:28,04:45:57 +52,M,02:11:40,04:45:57 +39,W,02:00:08,04:45:57 +51,M,02:04:04,04:45:58 +32,W,02:09:57,04:45:58 +24,W,01:51:25,04:45:59 +41,W,01:59:07,04:45:59 +45,W,01:57:13,04:45:59 +46,M,02:00:55,04:45:59 +30,W,02:11:53,04:45:59 +49,W,01:51:48,04:45:59 +44,M,01:50:07,04:45:59 +35,M,02:00:55,04:46:00 +54,M,01:59:46,04:46:00 +19,M,01:59:45,04:46:01 +63,M,02:02:49,04:46:01 +45,M,02:03:58,04:46:01 +31,M,01:56:54,04:46:01 +41,M,02:10:41,04:46:01 +36,M,01:58:30,04:46:01 +39,M,02:12:13,04:46:02 +29,M,01:59:48,04:46:02 +48,M,02:01:54,04:46:02 +35,W,01:56:28,04:46:02 +57,M,01:54:25,04:46:03 +42,M,01:51:27,04:46:03 +41,M,02:03:09,04:46:03 +45,M,02:00:49,04:46:04 +44,M,01:57:13,04:46:04 +60,W,02:07:11,04:46:04 +43,M,02:08:56,04:46:04 +50,M,01:58:08,04:46:05 +37,W,02:06:53,04:46:05 +35,W,02:03:40,04:46:06 +24,W,01:53:03,04:46:06 +35,W,02:02:48,04:46:06 +42,M,02:07:11,04:46:06 +39,W,02:09:30,04:46:07 +40,W,02:06:19,04:46:07 +23,W,02:08:09,04:46:07 +42,M,02:01:42,04:46:07 +29,M,02:02:57,04:46:07 +36,M,01:59:46,04:46:08 +49,M,02:01:01,04:46:08 +26,W,02:10:13,04:46:09 +42,M,02:13:07,04:46:09 +21,W,02:06:19,04:46:09 +25,M,01:47:24,04:46:09 +43,M,01:53:22,04:46:10 +61,W,02:08:22,04:46:10 +42,M,01:55:03,04:46:11 +40,M,02:09:29,04:46:11 +54,M,02:05:00,04:46:11 +29,M,01:46:23,04:46:12 +44,M,02:05:48,04:46:12 +50,M,02:00:17,04:46:12 +40,M,02:00:13,04:46:12 +28,M,02:05:47,04:46:12 +53,M,01:57:01,04:46:12 +43,W,02:06:12,04:46:13 +35,W,02:07:37,04:46:13 +48,W,02:08:23,04:46:13 +29,M,02:02:01,04:46:14 +54,M,02:03:55,04:46:14 +23,W,02:03:48,04:46:15 +31,M,01:58:14,04:46:15 +43,M,02:02:27,04:46:15 +41,W,01:59:57,04:46:15 +37,M,02:07:22,04:46:16 +52,M,02:01:58,04:46:16 +33,W,01:59:18,04:46:16 +39,M,01:53:39,04:46:16 +47,W,01:59:20,04:46:16 +41,W,01:58:32,04:46:16 +27,M,02:07:10,04:46:16 +58,M,02:03:10,04:46:16 +39,M,02:11:20,04:46:17 +37,W,02:04:30,04:46:17 +62,W,02:14:22,04:46:17 +39,M,01:51:44,04:46:17 +26,M,01:59:44,04:46:18 +29,M,02:12:13,04:46:18 +44,M,01:55:04,04:46:18 +23,M,01:59:05,04:46:19 +37,M,02:06:02,04:46:19 +40,M,01:58:26,04:46:19 +65,M,01:56:08,04:46:19 +26,W,02:03:11,04:46:20 +51,W,02:08:36,04:46:21 +43,W,02:04:50,04:46:21 +21,W,01:54:59,04:46:22 +30,M,02:10:31,04:46:22 +43,M,02:02:36,04:46:22 +45,W,02:06:32,04:46:22 +30,M,02:06:37,04:46:22 +31,M,02:00:42,04:46:22 +39,W,02:02:01,04:46:23 +40,M,01:57:37,04:46:23 +35,M,02:04:46,04:46:23 +41,M,02:00:03,04:46:24 +32,M,01:59:35,04:46:24 +62,M,01:56:18,04:46:24 +41,M,01:45:26,04:46:24 +36,M,02:01:40,04:46:25 +36,M,02:01:45,04:46:25 +53,M,02:00:37,04:46:25 +45,M,02:01:38,04:46:25 +28,W,02:07:23,04:46:25 +35,W,02:01:37,04:46:25 +43,M,01:57:11,04:46:25 +42,M,02:03:55,04:46:26 +40,M,02:04:30,04:46:26 +40,M,02:07:56,04:46:26 +35,W,02:01:51,04:46:26 +30,W,02:07:12,04:46:26 +25,W,02:06:58,04:46:27 +32,W,02:06:55,04:46:27 +49,M,01:54:46,04:46:27 +49,W,01:59:19,04:46:28 +37,M,01:58:39,04:46:28 +47,W,02:03:22,04:46:28 +52,M,01:55:37,04:46:29 +48,M,02:04:31,04:46:29 +55,M,02:03:11,04:46:29 +45,W,01:57:38,04:46:29 +41,W,01:59:35,04:46:29 +47,M,02:05:34,04:46:30 +52,M,02:04:03,04:46:30 +40,M,02:08:44,04:46:30 +45,M,01:59:42,04:46:30 +67,M,02:07:37,04:46:31 +35,M,01:50:46,04:46:31 +35,W,02:05:00,04:46:32 +43,M,02:08:21,04:46:33 +37,M,01:56:53,04:46:34 +32,M,02:00:31,04:46:34 +37,M,01:57:37,04:46:35 +51,M,02:10:23,04:46:35 +32,M,01:54:43,04:46:35 +37,M,02:04:30,04:46:35 +34,W,02:07:21,04:46:36 +24,W,02:06:26,04:46:36 +32,M,02:01:26,04:46:36 +41,M,01:56:54,04:46:37 +39,M,02:00:14,04:46:37 +32,M,02:09:54,04:46:37 +41,W,02:09:29,04:46:37 +54,M,02:02:31,04:46:37 +30,M,01:55:33,04:46:37 +30,W,02:01:39,04:46:38 +39,W,02:06:09,04:46:38 +32,M,02:02:54,04:46:39 +54,M,01:57:02,04:46:39 +62,M,01:52:05,04:46:39 +66,M,02:07:05,04:46:39 +30,M,02:07:33,04:46:39 +52,M,02:08:36,04:46:40 +39,M,01:51:32,04:46:40 +46,M,02:07:39,04:46:40 +28,M,01:50:37,04:46:40 +30,M,01:58:24,04:46:40 +31,W,01:52:14,04:46:41 +31,M,01:58:00,04:46:41 +30,M,02:02:58,04:46:41 +61,M,02:13:57,04:46:41 +44,M,02:03:27,04:46:42 +49,M,02:10:46,04:46:42 +28,W,01:56:25,04:46:42 +31,M,01:58:58,04:46:42 +43,M,01:59:19,04:46:43 +48,M,02:01:32,04:46:44 +28,M,01:56:00,04:46:44 +52,M,02:07:33,04:46:44 +55,M,02:00:54,04:46:44 +51,M,02:09:06,04:46:44 +28,W,02:03:59,04:46:44 +36,M,02:10:32,04:46:44 +33,M,01:57:44,04:46:45 +47,M,02:00:14,04:46:45 +60,M,01:58:28,04:46:45 +37,M,02:02:29,04:46:46 +35,W,02:10:45,04:46:46 +28,W,02:04:16,04:46:46 +47,M,02:06:27,04:46:47 +59,M,01:55:30,04:46:47 +26,M,01:53:37,04:46:47 +20,M,01:46:30,04:46:47 +32,W,02:08:04,04:46:47 +37,M,02:06:45,04:46:48 +46,M,02:11:17,04:46:48 +38,M,02:16:37,04:46:48 +64,M,02:02:57,04:46:49 +55,M,02:11:32,04:46:49 +34,M,02:01:03,04:46:49 +42,W,02:05:12,04:46:50 +57,M,01:56:35,04:46:51 +58,M,01:59:50,04:46:51 +42,W,01:55:31,04:46:52 +59,W,01:56:40,04:46:52 +29,W,02:07:42,04:46:53 +31,M,02:04:13,04:46:53 +37,W,01:56:56,04:46:54 +30,M,01:50:04,04:46:54 +31,W,02:00:40,04:46:54 +46,M,02:03:01,04:46:55 +39,M,02:07:29,04:46:55 +43,M,01:54:07,04:46:55 +44,M,02:00:35,04:46:55 +34,W,01:57:20,04:46:56 +37,M,02:05:21,04:46:56 +59,M,02:10:53,04:46:56 +48,M,02:04:41,04:46:56 +31,M,01:51:04,04:46:56 +63,M,01:54:48,04:46:56 +32,M,02:06:08,04:46:56 +27,M,02:09:51,04:46:57 +46,M,01:55:19,04:46:57 +59,M,02:00:42,04:46:58 +44,M,01:59:57,04:46:58 +44,W,02:08:06,04:46:58 +49,M,02:09:24,04:46:59 +47,M,02:03:48,04:46:59 +46,W,02:10:37,04:46:59 +55,M,02:01:35,04:46:59 +38,M,02:06:46,04:46:59 +38,M,02:00:33,04:46:59 +24,M,01:54:16,04:47:00 +23,W,01:53:13,04:47:00 +39,M,01:58:01,04:47:00 +41,M,02:01:51,04:47:00 +39,M,02:20:57,04:47:00 +52,M,02:00:17,04:47:00 +35,W,02:10:10,04:47:01 +45,M,02:02:49,04:47:01 +44,W,02:08:00,04:47:02 +49,M,02:06:37,04:47:02 +42,W,02:07:52,04:47:03 +23,W,01:59:36,04:47:03 +45,M,02:00:39,04:47:04 +40,W,02:03:11,04:47:04 +59,W,02:13:04,04:47:04 +44,W,02:01:01,04:47:05 +30,W,02:05:11,04:47:05 +53,M,01:58:39,04:47:05 +43,W,02:02:02,04:47:06 +45,M,01:54:14,04:47:07 +32,M,01:53:48,04:47:07 +32,W,02:05:41,04:47:07 +41,M,02:05:52,04:47:07 +39,M,01:57:27,04:47:07 +32,W,01:54:30,04:47:08 +44,M,02:03:25,04:47:08 +31,M,01:58:28,04:47:08 +29,M,02:03:25,04:47:08 +29,M,02:02:51,04:47:08 +31,W,02:00:14,04:47:09 +57,M,02:07:56,04:47:09 +50,W,01:58:43,04:47:10 +39,W,02:04:08,04:47:10 +36,W,01:55:13,04:47:11 +32,M,01:55:05,04:47:11 +29,M,02:05:22,04:47:11 +41,M,01:58:59,04:47:11 +33,W,01:58:00,04:47:11 +38,M,02:10:28,04:47:11 +44,W,02:05:22,04:47:11 +38,M,02:03:04,04:47:12 +22,M,02:17:50,04:47:12 +43,M,02:01:34,04:47:13 +40,M,01:51:00,04:47:13 +38,W,02:17:09,04:47:13 +37,W,02:08:37,04:47:13 +40,W,02:10:39,04:47:13 +31,W,02:01:42,04:47:14 +30,W,02:07:43,04:47:14 +59,M,02:09:54,04:47:14 +29,W,01:54:55,04:47:14 +20,M,02:03:24,04:47:14 +33,W,01:59:32,04:47:14 +46,M,02:01:46,04:47:14 +28,M,02:05:45,04:47:14 +56,M,02:02:48,04:47:15 +38,M,01:56:58,04:47:15 +51,M,02:03:02,04:47:15 +47,W,02:06:07,04:47:16 +41,W,02:01:54,04:47:16 +50,M,02:08:39,04:47:17 +44,M,02:00:46,04:47:17 +41,M,01:52:30,04:47:17 +28,W,01:56:15,04:47:17 +34,M,02:02:20,04:47:17 +41,M,01:58:30,04:47:17 +55,M,01:58:32,04:47:17 +44,M,02:00:46,04:47:18 +36,W,02:11:48,04:47:18 +27,W,01:59:33,04:47:19 +32,M,02:02:29,04:47:19 +18,M,02:03:42,04:47:19 +43,W,02:06:13,04:47:19 +37,M,02:14:10,04:47:19 +31,M,02:08:37,04:47:19 +32,M,01:55:35,04:47:19 +56,M,01:57:35,04:47:20 +27,M,01:59:44,04:47:21 +41,M,01:57:17,04:47:21 +38,M,02:03:59,04:47:21 +34,M,01:57:28,04:47:22 +38,W,01:58:02,04:47:22 +36,M,02:02:13,04:47:22 +28,M,01:54:00,04:47:23 +31,M,02:11:22,04:47:23 +71,M,02:05:51,04:47:23 +47,W,02:09:26,04:47:23 +41,M,02:03:27,04:47:25 +33,W,01:55:57,04:47:25 +52,M,02:02:20,04:47:25 +53,W,02:10:01,04:47:25 +38,M,01:58:06,04:47:25 +40,M,02:06:11,04:47:26 +32,M,02:00:27,04:47:26 +43,W,01:57:14,04:47:26 +43,W,01:58:07,04:47:26 +58,M,02:00:13,04:47:27 +30,W,02:07:57,04:47:27 +39,W,02:13:50,04:47:27 +53,M,01:56:37,04:47:27 +56,M,01:54:37,04:47:28 +30,M,02:06:40,04:47:29 +47,M,01:59:39,04:47:29 +45,M,02:13:19,04:47:29 +37,M,01:59:44,04:47:30 +29,W,02:03:14,04:47:30 +50,M,02:05:43,04:47:30 +37,W,02:07:42,04:47:30 +63,M,02:06:32,04:47:31 +30,W,01:55:25,04:47:32 +33,M,02:06:33,04:47:32 +33,M,01:51:40,04:47:32 +27,M,01:55:35,04:47:33 +37,M,01:59:58,04:47:33 +36,M,01:56:57,04:47:33 +53,M,02:06:25,04:47:34 +56,M,02:07:35,04:47:34 +32,M,02:01:09,04:47:34 +46,M,01:57:57,04:47:34 +47,W,01:57:04,04:47:34 +34,W,02:10:36,04:47:34 +52,W,02:12:17,04:47:35 +53,M,02:01:13,04:47:36 +29,W,02:05:21,04:47:36 +45,W,02:06:16,04:47:36 +44,W,01:57:13,04:47:36 +36,M,02:00:19,04:47:36 +27,W,02:04:11,04:47:37 +56,M,01:58:49,04:47:37 +40,W,02:11:04,04:47:37 +38,M,02:08:15,04:47:37 +69,M,02:06:09,04:47:38 +36,M,01:51:39,04:47:38 +25,W,02:07:59,04:47:39 +26,M,01:53:48,04:47:39 +53,M,02:04:22,04:47:39 +39,W,01:56:54,04:47:39 +42,W,02:14:17,04:47:39 +40,M,01:59:50,04:47:40 +36,M,02:02:25,04:47:40 +33,W,02:08:14,04:47:40 +33,W,02:07:18,04:47:40 +37,M,01:54:29,04:47:41 +36,M,02:01:20,04:47:41 +51,W,02:11:27,04:47:41 +40,M,01:59:53,04:47:42 +37,M,01:59:02,04:47:42 +46,M,02:05:18,04:47:43 +26,M,02:02:10,04:47:44 +29,W,01:57:43,04:47:44 +30,W,02:06:41,04:47:45 +32,M,01:57:51,04:47:45 +26,W,01:59:05,04:47:45 +46,M,01:46:22,04:47:45 +50,M,02:02:00,04:47:45 +58,M,02:10:41,04:47:45 +30,M,01:58:44,04:47:45 +44,M,01:58:19,04:47:46 +25,W,02:04:19,04:47:46 +45,M,01:54:30,04:47:46 +46,M,02:00:18,04:47:46 +29,M,01:49:53,04:47:47 +33,M,01:57:44,04:47:48 +43,M,02:09:27,04:47:48 +38,M,02:01:07,04:47:48 +60,M,02:10:56,04:47:48 +42,M,02:02:54,04:47:48 +45,M,02:06:15,04:47:48 +32,M,02:01:26,04:47:49 +27,M,02:01:45,04:47:49 +35,W,01:56:34,04:47:49 +51,W,01:54:15,04:47:49 +45,M,01:56:43,04:47:49 +35,W,01:57:39,04:47:50 +50,M,02:07:43,04:47:50 +39,M,01:57:57,04:47:50 +52,W,01:59:21,04:47:50 +27,M,02:01:30,04:47:51 +41,M,02:06:56,04:47:51 +46,M,02:02:34,04:47:51 +59,M,02:00:26,04:47:51 +49,M,02:02:36,04:47:51 +35,M,02:03:11,04:47:52 +41,W,02:23:12,04:47:52 +31,W,02:10:44,04:47:53 +46,M,02:03:16,04:47:53 +50,W,02:04:53,04:47:53 +41,M,02:12:03,04:47:53 +22,W,02:14:10,04:47:53 +23,W,02:01:42,04:47:53 +38,M,01:58:55,04:47:54 +23,W,01:54:15,04:47:54 +44,M,01:53:51,04:47:55 +28,M,02:09:02,04:47:55 +45,M,02:06:19,04:47:55 +25,W,02:01:02,04:47:55 +49,M,01:57:04,04:47:56 +51,M,02:11:39,04:47:56 +41,M,02:08:50,04:47:56 +53,W,01:59:16,04:47:57 +47,M,01:59:30,04:47:57 +43,W,02:06:28,04:47:58 +44,M,01:48:55,04:47:58 +42,W,01:47:45,04:47:59 +30,M,02:08:23,04:48:00 +49,M,01:57:24,04:48:00 +30,M,01:56:43,04:48:00 +29,W,01:54:00,04:48:00 +49,W,02:02:13,04:48:01 +31,M,01:55:05,04:48:02 +53,W,02:04:57,04:48:02 +43,M,02:04:44,04:48:02 +30,M,01:56:46,04:48:02 +50,M,02:06:27,04:48:02 +55,M,01:58:10,04:48:02 +34,M,01:55:56,04:48:02 +40,M,01:58:23,04:48:04 +40,W,02:09:37,04:48:04 +38,W,02:06:23,04:48:04 +26,W,02:04:00,04:48:04 +40,W,02:06:29,04:48:04 +55,M,02:03:12,04:48:04 +34,W,01:57:10,04:48:05 +18,M,02:03:42,04:48:05 +34,W,01:55:45,04:48:05 +37,W,01:58:41,04:48:05 +62,M,02:02:42,04:48:06 +41,M,01:57:33,04:48:06 +41,M,01:59:41,04:48:07 +36,M,02:00:26,04:48:07 +38,M,02:06:32,04:48:07 +28,W,01:58:34,04:48:07 +35,M,02:00:51,04:48:07 +38,M,02:00:40,04:48:07 +43,M,01:58:59,04:48:08 +54,M,02:08:27,04:48:08 +30,M,02:05:23,04:48:08 +30,W,02:10:17,04:48:09 +32,W,01:59:49,04:48:09 +43,M,01:59:00,04:48:09 +46,M,02:09:19,04:48:09 +46,M,02:01:06,04:48:10 +29,M,01:59:01,04:48:10 +40,W,02:20:11,04:48:10 +51,M,02:11:31,04:48:11 +43,W,02:05:54,04:48:11 +38,M,02:02:46,04:48:11 +58,M,01:58:12,04:48:11 +31,W,02:08:47,04:48:11 +46,M,01:59:06,04:48:12 +42,M,01:58:59,04:48:12 +54,M,02:00:04,04:48:12 +28,W,02:12:52,04:48:14 +30,M,01:55:31,04:48:14 +54,M,02:04:09,04:48:15 +52,M,01:47:15,04:48:15 +30,M,02:12:18,04:48:16 +17,W,02:10:12,04:48:16 +30,W,02:07:59,04:48:16 +25,W,02:05:38,04:48:16 +47,M,02:07:38,04:48:16 +37,M,02:05:09,04:48:16 +37,M,02:12:02,04:48:17 +35,W,01:56:11,04:48:17 +44,W,02:01:54,04:48:17 +58,M,01:53:45,04:48:17 +34,W,02:05:34,04:48:17 +33,W,01:57:16,04:48:17 +36,W,02:02:18,04:48:17 +35,W,02:01:51,04:48:18 +36,M,02:07:43,04:48:18 +26,M,02:03:26,04:48:18 +32,M,01:58:44,04:48:19 +50,M,02:05:01,04:48:19 +43,M,01:58:37,04:48:19 +32,M,01:56:28,04:48:19 +52,M,02:03:42,04:48:19 +26,M,02:04:03,04:48:19 +50,M,01:56:30,04:48:20 +40,M,01:56:26,04:48:20 +29,W,02:06:10,04:48:20 +37,M,02:02:49,04:48:20 +36,W,01:56:52,04:48:20 +30,W,02:08:45,04:48:20 +48,M,02:05:01,04:48:21 +26,W,02:03:33,04:48:22 +25,M,01:53:53,04:48:22 +56,M,02:02:19,04:48:22 +51,M,02:08:17,04:48:22 +31,W,02:02:54,04:48:23 +51,M,01:57:59,04:48:23 +52,W,02:04:16,04:48:23 +41,M,02:08:19,04:48:24 +24,M,02:14:46,04:48:24 +21,M,01:58:11,04:48:24 +43,M,02:01:20,04:48:25 +29,W,01:49:57,04:48:26 +35,M,02:00:38,04:48:26 +46,W,02:05:37,04:48:26 +47,M,02:10:40,04:48:27 +50,M,02:05:27,04:48:27 +40,M,02:05:15,04:48:27 +19,M,01:55:24,04:48:27 +30,M,02:01:19,04:48:28 +54,W,02:13:39,04:48:28 +55,M,02:08:51,04:48:28 +41,M,02:04:03,04:48:30 +60,M,01:57:22,04:48:30 +67,M,02:12:05,04:48:30 +31,M,02:01:14,04:48:31 +21,M,01:58:03,04:48:31 +25,W,02:11:16,04:48:31 +55,M,01:57:41,04:48:32 +61,M,01:58:42,04:48:32 +56,M,02:03:34,04:48:32 +42,W,02:05:54,04:48:32 +30,W,02:13:12,04:48:32 +57,M,01:57:47,04:48:33 +24,W,01:59:08,04:48:33 +36,W,01:53:57,04:48:33 +33,M,01:59:59,04:48:33 +65,M,02:01:23,04:48:33 +27,M,01:56:12,04:48:34 +35,W,01:59:07,04:48:34 +43,W,02:05:07,04:48:35 +43,M,02:03:30,04:48:35 +27,M,02:03:17,04:48:35 +40,M,02:01:28,04:48:36 +54,M,01:57:27,04:48:37 +27,W,02:09:04,04:48:37 +38,M,01:52:49,04:48:37 +54,M,01:59:54,04:48:37 +40,W,02:09:34,04:48:38 +43,M,02:19:27,04:48:38 +24,W,02:07:59,04:48:38 +56,M,01:58:25,04:48:39 +29,W,02:11:30,04:48:39 +36,M,02:01:56,04:48:39 +48,M,02:06:42,04:48:39 +55,M,01:55:51,04:48:41 +38,W,02:04:31,04:48:41 +44,M,01:53:35,04:48:41 +35,M,02:02:46,04:48:41 +24,M,01:58:31,04:48:43 +42,M,02:01:21,04:48:43 +51,W,02:12:10,04:48:43 +29,W,02:05:06,04:48:44 +30,M,02:16:32,04:48:44 +22,M,02:03:16,04:48:44 +43,M,01:54:17,04:48:45 +41,M,02:01:25,04:48:45 +61,M,02:00:43,04:48:45 +41,M,01:59:00,04:48:45 +41,M,02:08:07,04:48:46 +49,W,02:12:24,04:48:46 +24,W,02:01:04,04:48:46 +49,M,02:10:14,04:48:46 +49,W,02:06:35,04:48:47 +31,M,01:49:38,04:48:47 +47,M,02:06:20,04:48:47 +30,W,02:07:22,04:48:48 +44,M,01:58:27,04:48:48 +59,M,02:13:10,04:48:49 +45,M,01:55:23,04:48:49 +33,M,02:09:40,04:48:49 +39,W,02:00:14,04:48:49 +42,M,01:55:02,04:48:49 +31,M,01:59:09,04:48:50 +47,M,01:57:47,04:48:50 +53,M,02:06:36,04:48:50 +36,M,02:04:25,04:48:51 +30,M,02:00:16,04:48:51 +64,M,02:01:52,04:48:51 +33,M,02:00:07,04:48:51 +31,M,01:58:58,04:48:52 +55,M,01:55:42,04:48:52 +35,W,02:03:17,04:48:53 +40,M,02:08:45,04:48:53 +38,W,02:08:08,04:48:53 +35,W,02:05:47,04:48:54 +36,M,01:52:16,04:48:54 +31,W,02:02:26,04:48:54 +51,W,01:53:31,04:48:55 +32,M,01:55:38,04:48:55 +59,M,01:55:29,04:48:55 +34,W,02:03:12,04:48:55 +42,W,02:00:39,04:48:55 +31,M,02:00:45,04:48:56 +37,W,02:13:16,04:48:56 +48,W,02:03:28,04:48:56 +28,W,01:57:19,04:48:56 +50,M,02:01:12,04:48:56 +40,W,02:04:51,04:48:56 +43,W,02:10:15,04:48:56 +43,M,01:59:05,04:48:56 +43,M,02:14:32,04:48:57 +33,W,02:07:20,04:48:57 +36,M,01:57:53,04:48:58 +36,M,02:08:38,04:48:58 +26,M,02:07:36,04:48:58 +33,M,01:55:59,04:48:59 +39,M,01:54:26,04:48:59 +66,M,01:56:57,04:48:59 +34,W,02:14:36,04:48:59 +56,M,01:54:19,04:49:00 +46,W,01:50:42,04:49:00 +61,M,02:14:58,04:49:01 +32,W,01:50:34,04:49:01 +47,W,02:22:55,04:49:01 +24,W,01:57:56,04:49:01 +51,M,02:01:58,04:49:01 +55,M,01:56:43,04:49:02 +58,M,02:05:45,04:49:02 +47,M,02:00:54,04:49:03 +47,M,01:56:05,04:49:03 +30,W,01:58:03,04:49:03 +33,M,02:00:42,04:49:03 +43,M,01:56:34,04:49:04 +43,M,02:02:14,04:49:04 +37,M,01:53:44,04:49:04 +34,W,02:05:36,04:49:04 +46,M,02:01:02,04:49:04 +24,M,01:47:12,04:49:04 +44,W,02:09:34,04:49:05 +48,W,02:03:37,04:49:05 +47,M,02:06:06,04:49:05 +54,W,02:08:21,04:49:05 +37,M,02:00:44,04:49:05 +47,M,02:12:49,04:49:06 +33,M,02:07:04,04:49:06 +35,W,02:08:40,04:49:06 +50,M,02:14:21,04:49:06 +34,M,01:59:05,04:49:06 +31,M,02:02:45,04:49:06 +43,M,01:55:27,04:49:07 +49,W,02:06:37,04:49:07 +50,M,02:12:10,04:49:07 +43,M,02:01:56,04:49:08 +61,W,01:56:38,04:49:08 +39,M,02:00:09,04:49:08 +63,M,02:02:40,04:49:08 +55,M,01:51:53,04:49:08 +34,W,02:07:14,04:49:08 +57,M,01:46:25,04:49:08 +51,M,01:58:42,04:49:09 +46,M,01:59:09,04:49:09 +41,W,02:05:50,04:49:09 +31,M,02:00:51,04:49:10 +38,M,02:01:20,04:49:10 +26,W,02:03:04,04:49:10 +43,W,02:08:08,04:49:10 +43,M,01:59:46,04:49:10 +45,M,01:58:48,04:49:11 +52,M,02:07:11,04:49:11 +54,M,02:01:22,04:49:11 +48,W,02:11:34,04:49:12 +48,M,01:57:22,04:49:12 +26,M,01:50:50,04:49:12 +29,M,01:49:41,04:49:13 +35,M,02:00:26,04:49:13 +46,W,02:02:01,04:49:13 +36,M,01:56:43,04:49:13 +32,M,02:11:10,04:49:14 +31,M,02:03:09,04:49:14 +42,M,01:57:28,04:49:14 +30,M,01:58:56,04:49:14 +27,W,02:05:48,04:49:15 +44,M,01:54:39,04:49:15 +27,M,01:57:32,04:49:15 +46,M,02:00:05,04:49:15 +26,W,02:13:49,04:49:15 +41,M,02:01:28,04:49:15 +52,M,01:59:16,04:49:15 +35,W,02:00:09,04:49:15 +34,M,01:57:44,04:49:16 +32,W,02:10:15,04:49:16 +36,M,02:11:44,04:49:16 +35,M,02:08:36,04:49:17 +36,W,02:09:52,04:49:18 +33,W,02:08:37,04:49:18 +38,W,02:05:07,04:49:18 +25,M,01:57:56,04:49:19 +47,M,02:01:52,04:49:19 +28,W,02:12:10,04:49:19 +52,M,01:56:35,04:49:19 +50,M,02:11:21,04:49:20 +39,M,02:09:02,04:49:20 +36,M,01:54:37,04:49:20 +53,M,02:00:39,04:49:20 +54,M,02:09:41,04:49:21 +43,M,01:49:05,04:49:21 +26,W,02:09:16,04:49:21 +31,M,02:03:08,04:49:22 +36,W,02:00:53,04:49:22 +50,M,02:05:37,04:49:23 +35,M,02:23:44,04:49:23 +38,M,01:56:43,04:49:23 +38,M,02:06:36,04:49:23 +38,W,02:00:34,04:49:23 +29,W,02:08:46,04:49:23 +18,M,01:57:55,04:49:25 +32,W,02:10:22,04:49:25 +41,M,02:03:34,04:49:25 +64,M,02:00:33,04:49:25 +40,W,02:03:14,04:49:25 +35,W,01:57:10,04:49:25 +36,M,02:02:30,04:49:26 +52,M,02:08:32,04:49:26 +26,M,01:57:43,04:49:26 +39,M,02:09:33,04:49:27 +51,W,02:10:46,04:49:27 +42,W,02:00:02,04:49:27 +37,M,02:01:12,04:49:27 +49,M,01:58:35,04:49:27 +49,M,02:00:40,04:49:27 +49,M,01:59:12,04:49:27 +51,M,02:08:48,04:49:29 +42,M,01:56:23,04:49:29 +60,W,02:02:04,04:49:30 +37,W,02:12:34,04:49:30 +54,M,02:09:15,04:49:31 +54,M,02:01:13,04:49:31 +45,W,01:54:31,04:49:31 +29,M,02:16:39,04:49:31 +49,M,01:57:07,04:49:32 +54,M,02:17:14,04:49:32 +45,W,02:11:35,04:49:32 +31,W,02:03:49,04:49:32 +23,W,02:14:28,04:49:33 +34,M,01:56:04,04:49:33 +47,M,01:59:42,04:49:33 +50,M,02:09:16,04:49:33 +35,W,01:56:33,04:49:33 +47,M,02:01:27,04:49:33 +40,M,02:05:00,04:49:34 +38,M,01:54:57,04:49:34 +60,M,01:56:44,04:49:34 +53,M,02:07:56,04:49:35 +51,M,02:06:15,04:49:35 +33,M,01:56:48,04:49:35 +50,M,02:05:33,04:49:35 +54,M,02:03:38,04:49:36 +27,W,02:17:01,04:49:37 +32,W,02:09:33,04:49:37 +52,M,02:00:28,04:49:37 +42,M,01:57:02,04:49:37 +39,M,01:49:23,04:49:38 +41,W,02:14:25,04:49:38 +35,W,02:07:24,04:49:38 +39,M,02:07:12,04:49:39 +37,M,02:00:19,04:49:39 +51,M,01:55:21,04:49:39 +33,W,02:13:28,04:49:39 +31,M,02:07:26,04:49:39 +53,M,02:00:19,04:49:39 +27,W,01:59:53,04:49:39 +26,W,01:59:38,04:49:40 +32,W,01:56:58,04:49:40 +44,M,02:01:47,04:49:41 +34,W,02:00:16,04:49:41 +44,W,02:14:25,04:49:41 +49,M,01:59:56,04:49:41 +34,M,01:57:46,04:49:41 +56,M,02:09:06,04:49:41 +38,M,02:06:32,04:49:41 +42,M,02:11:34,04:49:41 +28,W,02:03:32,04:49:41 +27,W,02:07:36,04:49:41 +43,M,02:13:19,04:49:42 +33,M,01:58:26,04:49:42 +36,M,02:03:08,04:49:42 +23,M,01:56:59,04:49:42 +35,W,01:52:34,04:49:44 +50,W,02:03:36,04:49:44 +43,M,01:59:43,04:49:45 +26,W,01:56:20,04:49:45 +47,M,01:55:25,04:49:45 +61,M,02:05:02,04:49:45 +26,M,02:04:58,04:49:45 +37,M,01:55:04,04:49:46 +47,M,01:56:40,04:49:46 +32,M,02:09:09,04:49:47 +22,M,02:11:53,04:49:47 +36,M,01:59:42,04:49:47 +48,M,02:01:08,04:49:47 +19,M,01:53:38,04:49:47 +67,M,02:04:19,04:49:47 +52,M,02:00:22,04:49:47 +44,M,01:56:18,04:49:48 +30,W,02:10:14,04:49:48 +42,M,01:48:24,04:49:49 +35,W,01:59:03,04:49:49 +33,W,02:02:31,04:49:49 +26,M,02:01:44,04:49:50 +31,W,02:05:45,04:49:50 +49,M,02:07:14,04:49:50 +30,M,02:02:29,04:49:50 +42,W,02:02:46,04:49:51 +27,M,02:05:03,04:49:51 +49,W,02:11:54,04:49:51 +34,M,02:04:25,04:49:51 +30,M,01:48:54,04:49:51 +27,M,01:59:53,04:49:52 +33,W,02:05:21,04:49:52 +30,W,01:56:58,04:49:52 +34,M,02:06:01,04:49:53 +59,M,02:02:56,04:49:53 +48,M,02:07:35,04:49:53 +44,M,02:04:50,04:49:54 +27,W,02:03:43,04:49:54 +51,M,01:56:12,04:49:54 +38,W,02:05:43,04:49:54 +45,M,02:06:31,04:49:55 +52,M,02:11:33,04:49:55 +56,M,02:02:12,04:49:55 +54,W,01:57:53,04:49:56 +47,W,02:11:34,04:49:56 +24,M,01:58:45,04:49:57 +44,M,02:00:46,04:49:57 +52,W,02:00:36,04:49:57 +43,M,02:02:04,04:49:57 +35,W,02:08:35,04:49:57 +23,M,02:08:46,04:49:58 +45,M,02:11:57,04:49:58 +40,M,02:34:57,04:49:58 +53,W,02:18:18,04:49:58 +48,M,01:55:57,04:49:58 +55,M,02:05:52,04:49:59 +33,M,01:58:16,04:49:59 +49,W,02:07:19,04:50:00 +30,M,01:55:26,04:50:00 +28,W,02:07:23,04:50:00 +32,M,02:05:29,04:50:00 +42,M,01:56:05,04:50:00 +37,W,01:59:20,04:50:01 +45,W,02:13:43,04:50:01 +22,M,01:50:24,04:50:01 +39,M,02:00:14,04:50:01 +44,W,02:03:41,04:50:02 +48,W,02:12:03,04:50:02 +32,M,01:58:58,04:50:02 +26,M,01:58:06,04:50:02 +37,M,02:06:38,04:50:03 +34,M,01:55:25,04:50:03 +32,W,02:05:23,04:50:03 +31,M,01:56:35,04:50:04 +33,M,01:57:02,04:50:04 +31,W,01:57:55,04:50:04 +60,M,02:01:17,04:50:04 +43,W,02:05:38,04:50:04 +30,W,02:16:41,04:50:05 +30,W,02:02:47,04:50:05 +26,W,02:05:09,04:50:05 +38,M,02:06:47,04:50:05 +42,M,02:02:11,04:50:07 +55,M,02:20:14,04:50:07 +35,M,02:07:15,04:50:07 +40,M,02:10:57,04:50:07 +39,M,02:11:46,04:50:07 +38,W,02:05:34,04:50:07 +41,M,01:58:05,04:50:07 +33,M,02:16:06,04:50:07 +43,W,02:08:41,04:50:08 +43,M,02:07:43,04:50:08 +50,M,02:01:55,04:50:10 +62,M,01:57:52,04:50:11 +38,W,02:02:07,04:50:11 +43,M,02:01:13,04:50:12 +25,M,01:49:50,04:50:12 +37,W,02:03:39,04:50:12 +50,W,02:01:54,04:50:12 +35,M,02:06:42,04:50:12 +59,M,01:56:41,04:50:12 +47,M,02:07:03,04:50:13 +36,M,01:52:48,04:50:13 +35,M,02:00:32,04:50:14 +39,M,02:12:29,04:50:14 +40,M,02:14:58,04:50:14 +24,W,02:09:23,04:50:14 +39,W,02:02:21,04:50:15 +45,M,01:58:58,04:50:15 +36,M,01:54:50,04:50:15 +22,W,02:01:14,04:50:15 +49,W,02:11:03,04:50:15 +39,M,02:00:00,04:50:16 +40,M,02:07:30,04:50:16 +36,W,02:01:29,04:50:16 +45,M,01:57:53,04:50:16 +42,M,02:05:50,04:50:16 +46,W,02:12:25,04:50:17 +30,M,01:50:42,04:50:17 +30,M,01:52:18,04:50:17 +33,M,01:55:39,04:50:17 +23,M,02:05:23,04:50:17 +37,W,02:02:23,04:50:17 +49,M,02:09:40,04:50:17 +27,W,02:08:45,04:50:17 +34,M,01:49:03,04:50:18 +19,W,02:09:07,04:50:18 +39,W,02:12:17,04:50:18 +28,M,02:05:33,04:50:19 +51,M,02:06:13,04:50:19 +38,W,02:07:42,04:50:19 +50,W,01:56:35,04:50:19 +48,M,01:57:46,04:50:20 +26,W,02:06:58,04:50:20 +28,M,02:05:24,04:50:20 +39,W,02:02:47,04:50:20 +28,W,01:49:40,04:50:20 +38,M,02:08:08,04:50:21 +29,W,02:05:13,04:50:21 +48,M,02:03:54,04:50:21 +29,M,01:58:43,04:50:21 +26,M,01:59:17,04:50:22 +34,M,01:56:41,04:50:23 +28,W,02:01:45,04:50:23 +29,M,02:01:07,04:50:23 +29,M,02:03:37,04:50:23 +55,M,01:57:05,04:50:23 +36,W,02:02:12,04:50:23 +55,W,01:57:34,04:50:24 +42,M,01:59:39,04:50:24 +36,M,01:59:14,04:50:24 +57,M,02:04:35,04:50:24 +32,M,01:57:47,04:50:24 +23,W,01:59:20,04:50:25 +30,M,01:55:45,04:50:26 +32,M,02:04:43,04:50:26 +32,W,02:01:27,04:50:26 +24,M,01:59:29,04:50:26 +28,W,02:01:37,04:50:27 +60,M,01:59:42,04:50:27 +46,M,01:55:18,04:50:27 +39,W,01:55:29,04:50:27 +30,W,02:04:34,04:50:28 +47,M,02:03:35,04:50:28 +34,M,02:05:23,04:50:28 +44,W,02:11:18,04:50:28 +52,M,01:53:12,04:50:28 +39,M,02:05:37,04:50:29 +37,M,02:03:01,04:50:29 +43,M,02:04:32,04:50:29 +38,M,01:54:12,04:50:30 +41,W,02:10:26,04:50:30 +49,M,01:56:45,04:50:30 +30,M,02:06:27,04:50:30 +54,M,02:14:54,04:50:30 +28,M,02:10:25,04:50:30 +49,M,02:08:25,04:50:31 +44,W,02:09:41,04:50:31 +44,M,02:03:11,04:50:31 +40,W,02:06:54,04:50:31 +53,M,01:56:38,04:50:31 +39,W,02:08:24,04:50:31 +40,M,02:01:13,04:50:31 +43,W,01:55:08,04:50:31 +36,W,02:02:06,04:50:31 +36,M,01:56:09,04:50:31 +50,W,02:04:17,04:50:32 +29,M,02:00:03,04:50:32 +49,M,02:11:25,04:50:33 +31,M,02:02:08,04:50:33 +38,W,02:02:31,04:50:33 +34,M,01:54:14,04:50:33 +43,M,02:07:44,04:50:33 +40,M,01:57:11,04:50:34 +43,W,01:59:20,04:50:34 +27,W,02:13:54,04:50:34 +44,W,02:05:00,04:50:35 +46,W,02:07:43,04:50:35 +44,W,02:10:21,04:50:35 +44,M,02:09:38,04:50:35 +39,M,02:11:57,04:50:36 +40,M,02:10:56,04:50:36 +33,M,02:01:33,04:50:36 +39,M,01:53:49,04:50:37 +31,W,02:16:08,04:50:37 +45,M,01:57:41,04:50:37 +34,M,01:53:52,04:50:37 +26,M,02:06:19,04:50:37 +44,M,02:08:32,04:50:38 +64,M,02:07:27,04:50:39 +54,W,01:57:29,04:50:39 +47,M,01:57:18,04:50:39 +36,M,02:00:52,04:50:40 +25,W,02:06:43,04:50:40 +60,M,02:06:16,04:50:40 +40,M,01:49:47,04:50:41 +36,W,01:54:19,04:50:41 +30,W,02:02:13,04:50:41 +30,M,02:00:41,04:50:42 +57,M,01:57:45,04:50:44 +26,W,02:00:07,04:50:44 +52,M,02:04:28,04:50:44 +50,M,02:02:56,04:50:44 +50,M,02:06:02,04:50:44 +27,M,02:09:58,04:50:44 +47,M,02:01:12,04:50:45 +49,M,02:08:24,04:50:45 +44,M,02:16:35,04:50:45 +55,M,01:56:30,04:50:45 +25,M,01:53:46,04:50:45 +44,M,02:08:16,04:50:46 +60,M,02:11:04,04:50:47 +36,W,02:01:51,04:50:47 +52,M,02:00:04,04:50:47 +43,M,01:58:36,04:50:47 +64,W,02:03:13,04:50:48 +50,M,02:11:51,04:50:48 +53,M,02:12:03,04:50:48 +69,M,02:08:38,04:50:49 +31,M,02:08:04,04:50:50 +30,W,02:02:44,04:50:50 +58,M,02:08:37,04:50:50 +59,M,01:58:20,04:50:51 +45,W,02:05:26,04:50:51 +35,W,02:00:59,04:50:51 +20,W,02:12:07,04:50:51 +57,M,01:59:32,04:50:51 +27,W,02:11:39,04:50:51 +44,M,02:01:01,04:50:51 +37,M,01:56:08,04:50:51 +54,M,02:03:29,04:50:52 +41,M,01:53:07,04:50:52 +32,M,01:55:53,04:50:52 +53,M,02:05:54,04:50:52 +54,M,02:01:07,04:50:52 +55,M,02:07:58,04:50:53 +48,W,02:12:04,04:50:53 +42,M,01:55:31,04:50:53 +56,M,02:03:35,04:50:54 +43,W,02:11:23,04:50:54 +39,M,02:07:11,04:50:54 +38,M,02:05:43,04:50:54 +41,W,02:04:22,04:50:54 +38,M,02:00:43,04:50:55 +51,W,02:04:24,04:50:55 +35,M,01:58:53,04:50:55 +19,W,02:15:09,04:50:56 +41,M,01:59:07,04:50:56 +22,W,01:55:29,04:50:56 +52,M,02:10:34,04:50:57 +37,W,02:02:30,04:50:57 +35,W,02:00:40,04:50:58 +33,M,02:12:33,04:50:58 +33,M,02:02:15,04:50:58 +41,M,01:59:12,04:50:59 +41,M,02:04:02,04:50:59 +46,M,02:00:21,04:51:00 +42,W,01:59:55,04:51:00 +30,W,02:05:09,04:51:00 +54,M,02:05:36,04:51:00 +42,W,02:13:58,04:51:01 +68,M,02:09:00,04:51:01 +34,M,02:01:32,04:51:02 +37,M,02:02:37,04:51:02 +28,W,02:08:31,04:51:03 +55,M,01:50:41,04:51:04 +39,M,02:09:01,04:51:04 +44,W,02:05:48,04:51:04 +35,M,02:16:32,04:51:04 +49,M,02:08:51,04:51:05 +60,M,02:04:37,04:51:05 +32,M,02:01:34,04:51:05 +31,M,01:57:34,04:51:06 +36,W,02:07:46,04:51:06 +48,W,02:12:09,04:51:06 +29,W,02:02:40,04:51:06 +37,M,01:58:29,04:51:06 +38,W,02:03:04,04:51:06 +53,W,02:05:43,04:51:06 +25,M,01:57:15,04:51:07 +34,M,02:09:39,04:51:07 +30,W,02:08:06,04:51:08 +42,W,02:00:02,04:51:08 +48,W,02:00:49,04:51:08 +54,M,02:09:36,04:51:08 +49,M,02:02:25,04:51:08 +33,M,02:11:30,04:51:08 +30,M,02:01:08,04:51:08 +57,W,01:57:29,04:51:08 +32,M,02:00:00,04:51:08 +42,M,02:00:44,04:51:09 +36,W,02:13:33,04:51:09 +30,M,02:03:54,04:51:09 +49,M,01:57:15,04:51:09 +36,M,02:07:26,04:51:09 +36,W,02:02:59,04:51:09 +45,M,01:57:29,04:51:10 +43,M,01:58:16,04:51:10 +45,W,02:08:10,04:51:10 +43,W,02:02:34,04:51:10 +49,W,02:13:45,04:51:10 +40,M,02:06:32,04:51:10 +59,M,02:00:06,04:51:11 +27,M,02:13:47,04:51:11 +49,M,02:10:50,04:51:11 +25,W,02:14:50,04:51:11 +26,M,01:59:39,04:51:11 +42,M,02:13:32,04:51:12 +46,W,02:02:25,04:51:12 +21,M,01:58:35,04:51:12 +34,M,01:55:52,04:51:13 +30,M,02:01:09,04:51:13 +51,M,02:21:54,04:51:13 +45,M,02:04:06,04:51:13 +37,M,01:52:42,04:51:14 +41,M,02:04:33,04:51:15 +27,W,02:09:44,04:51:15 +49,W,02:02:37,04:51:15 +38,M,02:04:40,04:51:15 +57,M,02:09:06,04:51:15 +47,M,01:56:21,04:51:15 +39,W,01:50:32,04:51:15 +49,M,02:12:09,04:51:16 +39,M,01:57:41,04:51:16 +31,W,02:04:19,04:51:17 +23,M,01:54:44,04:51:17 +53,M,02:01:13,04:51:17 +34,M,02:11:27,04:51:17 +26,W,02:02:04,04:51:17 +44,M,02:02:25,04:51:17 +44,M,01:56:31,04:51:17 +49,M,02:18:53,04:51:18 +48,W,02:07:50,04:51:18 +41,M,02:05:58,04:51:18 +44,M,02:08:01,04:51:18 +46,M,02:06:30,04:51:19 +24,W,02:13:12,04:51:19 +40,M,01:59:53,04:51:19 +34,M,02:05:56,04:51:19 +43,M,02:00:50,04:51:20 +45,M,02:06:29,04:51:20 +51,M,02:11:40,04:51:20 +45,W,02:06:28,04:51:21 +57,M,02:07:13,04:51:21 +28,W,02:08:40,04:51:21 +43,W,02:08:58,04:51:21 +34,W,02:04:20,04:51:21 +40,M,02:14:27,04:51:21 +32,M,02:02:31,04:51:22 +39,M,01:56:52,04:51:22 +39,M,02:04:55,04:51:22 +41,M,01:53:16,04:51:23 +46,M,01:52:59,04:51:23 +47,M,02:05:28,04:51:23 +58,W,02:08:20,04:51:24 +33,W,01:59:52,04:51:25 +45,M,01:59:16,04:51:25 +24,W,02:08:32,04:51:26 +55,M,02:03:47,04:51:26 +47,M,01:57:30,04:51:26 +26,W,02:15:02,04:51:26 +46,M,01:56:41,04:51:26 +48,M,01:59:30,04:51:27 +38,M,02:05:10,04:51:27 +34,M,01:44:26,04:51:27 +37,M,01:52:39,04:51:27 +47,M,01:51:16,04:51:27 +39,W,02:15:10,04:51:28 +42,M,02:06:28,04:51:28 +42,M,01:49:38,04:51:29 +40,M,02:06:11,04:51:29 +38,W,02:09:26,04:51:29 +52,M,02:17:16,04:51:29 +40,M,01:59:32,04:51:29 +30,M,02:04:38,04:51:29 +36,W,02:01:52,04:51:29 +29,M,02:06:33,04:51:30 +46,M,02:02:36,04:51:30 +35,M,02:14:26,04:51:31 +47,M,01:59:16,04:51:31 +46,M,02:10:50,04:51:31 +30,W,02:07:49,04:51:31 +61,M,02:12:36,04:51:31 +38,M,02:03:42,04:51:31 +26,W,02:09:40,04:51:31 +51,M,02:03:14,04:51:32 +35,M,01:59:32,04:51:32 +34,M,02:03:22,04:51:32 +36,M,02:08:56,04:51:32 +28,M,01:58:33,04:51:33 +36,M,01:58:42,04:51:34 +20,M,02:16:53,04:51:34 +32,M,02:03:11,04:51:34 +37,W,02:04:25,04:51:34 +40,M,02:08:03,04:51:35 +31,W,02:08:12,04:51:35 +49,M,02:05:32,04:51:36 +37,M,02:03:54,04:51:36 +37,M,02:02:05,04:51:36 +34,M,02:07:51,04:51:36 +41,M,01:50:32,04:51:37 +31,W,02:04:17,04:51:38 +44,M,01:59:22,04:51:38 +29,M,02:01:11,04:51:39 +23,W,02:05:18,04:51:39 +32,W,02:09:12,04:51:39 +39,M,02:06:36,04:51:39 +29,W,02:12:07,04:51:39 +59,M,02:14:52,04:51:39 +44,M,02:07:04,04:51:39 +63,M,02:06:47,04:51:40 +36,M,02:01:10,04:51:40 +38,M,01:53:20,04:51:40 +40,M,01:53:12,04:51:40 +55,M,01:56:58,04:51:41 +43,M,02:02:13,04:51:42 +41,M,01:57:10,04:51:42 +53,M,02:02:34,04:51:42 +24,M,01:57:29,04:51:42 +36,W,02:01:18,04:51:43 +48,M,02:03:21,04:51:43 +45,M,02:05:25,04:51:43 +29,M,01:54:49,04:51:43 +32,M,02:03:05,04:51:43 +48,M,01:59:32,04:51:43 +43,M,02:01:47,04:51:43 +31,W,02:08:59,04:51:44 +39,M,02:08:16,04:51:45 +37,M,01:58:40,04:51:45 +43,M,02:08:45,04:51:45 +27,M,01:57:17,04:51:45 +54,M,02:10:22,04:51:46 +47,W,02:07:30,04:51:46 +42,M,02:04:07,04:51:46 +43,M,02:04:59,04:51:47 +37,M,02:05:16,04:51:47 +39,M,02:02:17,04:51:47 +25,M,01:49:22,04:51:47 +25,W,02:01:58,04:51:47 +48,M,02:13:56,04:51:48 +39,M,02:02:58,04:51:48 +40,M,02:02:40,04:51:49 +32,M,02:04:55,04:51:50 +43,M,02:07:53,04:51:50 +36,W,02:21:19,04:51:50 +35,M,02:08:28,04:51:50 +29,W,02:04:59,04:51:51 +45,M,02:10:14,04:51:51 +49,M,01:58:32,04:51:51 +40,M,02:06:20,04:51:51 +31,W,02:09:49,04:51:51 +47,W,02:07:52,04:51:51 +35,M,02:12:13,04:51:52 +28,W,02:05:54,04:51:53 +38,W,02:00:25,04:51:53 +40,M,01:53:31,04:51:53 +34,M,02:11:30,04:51:53 +51,M,02:06:25,04:51:54 +48,W,02:06:34,04:51:54 +52,M,02:01:45,04:51:54 +28,M,02:02:34,04:51:54 +54,M,02:11:40,04:51:54 +39,M,01:53:34,04:51:54 +42,M,02:09:00,04:51:54 +61,M,02:04:44,04:51:55 +50,M,02:01:31,04:51:55 +36,M,02:06:05,04:51:55 +29,M,02:04:52,04:51:56 +35,W,02:02:32,04:51:56 +45,M,02:07:08,04:51:56 +22,M,01:56:21,04:51:56 +34,W,02:08:19,04:51:56 +35,W,01:57:55,04:51:57 +24,W,02:07:46,04:51:57 +42,M,02:03:14,04:51:57 +23,W,01:58:07,04:51:58 +52,M,01:57:08,04:51:58 +49,M,02:00:55,04:51:58 +39,M,02:04:40,04:51:59 +49,M,02:00:06,04:51:59 +35,W,02:08:30,04:52:00 +41,W,02:07:09,04:52:00 +41,M,02:08:37,04:52:01 +57,M,01:53:11,04:52:01 +44,W,02:09:29,04:52:02 +26,W,02:01:12,04:52:02 +55,W,02:10:51,04:52:02 +32,W,02:12:54,04:52:02 +38,W,02:05:48,04:52:03 +57,M,02:09:23,04:52:03 +56,M,01:55:36,04:52:03 +47,M,02:04:40,04:52:03 +31,M,01:59:11,04:52:03 +47,M,02:00:33,04:52:03 +30,W,02:07:00,04:52:04 +41,W,02:06:33,04:52:04 +44,W,02:05:21,04:52:04 +43,W,01:58:55,04:52:05 +45,M,01:57:11,04:52:05 +46,M,02:05:46,04:52:05 +50,W,02:01:08,04:52:05 +47,W,02:01:40,04:52:06 +33,W,02:02:33,04:52:06 +36,W,01:54:11,04:52:06 +53,M,02:01:02,04:52:07 +46,M,01:55:07,04:52:08 +47,M,01:53:51,04:52:08 +36,M,01:56:05,04:52:08 +18,M,02:11:19,04:52:08 +60,M,02:05:23,04:52:08 +28,W,01:56:29,04:52:09 +39,M,02:02:27,04:52:10 +25,W,01:59:15,04:52:10 +49,W,02:02:06,04:52:10 +41,M,02:09:33,04:52:11 +44,W,02:04:27,04:52:11 +49,M,02:04:56,04:52:11 +42,W,01:56:10,04:52:11 +52,M,02:00:40,04:52:11 +28,W,02:14:40,04:52:11 +42,M,02:10:07,04:52:12 +60,M,02:07:36,04:52:12 +47,M,02:03:31,04:52:13 +51,M,02:33:15,04:52:13 +34,M,02:08:49,04:52:13 +52,W,02:00:09,04:52:13 +50,W,02:11:00,04:52:13 +25,W,02:05:00,04:52:13 +50,M,02:00:06,04:52:13 +45,W,02:08:52,04:52:13 +35,W,02:12:35,04:52:13 +38,W,02:04:04,04:52:13 +45,M,02:04:07,04:52:13 +44,M,02:07:00,04:52:14 +40,M,01:49:27,04:52:14 +27,W,02:05:33,04:52:14 +25,M,02:04:34,04:52:14 +31,W,02:08:42,04:52:14 +38,M,02:08:08,04:52:14 +45,M,01:57:28,04:52:15 +49,M,02:01:49,04:52:15 +35,W,02:01:29,04:52:15 +25,W,02:08:48,04:52:15 +27,W,02:03:58,04:52:16 +49,M,02:00:55,04:52:16 +39,M,01:54:41,04:52:16 +40,W,01:57:48,04:52:16 +32,W,02:04:08,04:52:17 +45,M,02:01:52,04:52:17 +45,M,01:59:51,04:52:17 +45,M,01:59:49,04:52:17 +46,W,02:01:03,04:52:18 +40,M,02:10:07,04:52:18 +29,W,02:02:04,04:52:18 +53,W,01:59:40,04:52:18 +39,M,01:55:54,04:52:18 +45,M,01:59:28,04:52:18 +61,W,02:04:13,04:52:19 +26,M,02:10:13,04:52:19 +39,M,01:53:01,04:52:19 +47,M,02:06:46,04:52:19 +33,M,02:10:22,04:52:20 +37,W,02:05:49,04:52:20 +36,W,02:09:47,04:52:20 +54,M,02:13:23,04:52:21 +44,M,01:58:29,04:52:21 +48,M,02:02:01,04:52:21 +53,W,02:17:03,04:52:22 +48,M,01:59:23,04:52:22 +53,W,01:55:46,04:52:23 +45,W,01:59:18,04:52:23 +62,M,02:06:32,04:52:23 +58,W,02:06:31,04:52:24 +50,M,02:07:44,04:52:25 +45,M,02:01:28,04:52:25 +33,M,01:59:37,04:52:25 +40,W,02:01:42,04:52:25 +28,M,02:06:32,04:52:26 +18,W,02:11:14,04:52:26 +46,M,02:04:53,04:52:26 +29,W,02:02:50,04:52:27 +51,M,02:03:57,04:52:27 +44,W,02:12:15,04:52:27 +43,W,02:03:16,04:52:28 +59,M,02:02:42,04:52:28 +31,M,01:55:32,04:52:28 +39,M,02:03:15,04:52:29 +42,M,02:03:18,04:52:31 +31,W,02:00:15,04:52:31 +28,M,02:04:08,04:52:31 +50,M,02:07:17,04:52:31 +38,M,01:56:55,04:52:31 +38,M,02:01:02,04:52:31 +51,M,02:07:54,04:52:32 +40,M,02:00:59,04:52:32 +44,M,01:52:28,04:52:32 +43,M,01:57:27,04:52:33 +48,M,02:02:02,04:52:33 +44,W,02:14:18,04:52:34 +27,W,02:08:12,04:52:34 +36,W,02:03:58,04:52:34 +45,M,02:08:47,04:52:34 +25,M,02:15:51,04:52:35 +40,W,02:00:19,04:52:35 +53,W,02:11:56,04:52:35 +55,M,01:54:30,04:52:35 +61,M,02:09:07,04:52:35 +51,W,02:04:37,04:52:36 +60,M,02:08:45,04:52:36 +39,W,02:08:18,04:52:37 +45,M,02:05:35,04:52:37 +29,W,02:08:37,04:52:38 +38,W,02:14:21,04:52:38 +36,M,01:58:16,04:52:38 +32,W,02:11:39,04:52:38 +62,M,01:57:55,04:52:39 +48,M,02:01:59,04:52:39 +39,M,01:57:08,04:52:40 +29,M,02:05:16,04:52:40 +40,M,02:12:12,04:52:40 +31,M,01:52:11,04:52:41 +53,W,02:06:19,04:52:41 +42,M,02:05:58,04:52:42 +42,M,02:17:38,04:52:42 +43,M,02:07:10,04:52:42 +45,M,02:09:26,04:52:42 +65,M,01:56:40,04:52:43 +55,M,01:57:20,04:52:43 +38,M,02:08:20,04:52:43 +62,M,02:02:16,04:52:43 +48,M,02:08:45,04:52:44 +49,M,02:04:58,04:52:44 +24,M,01:51:49,04:52:44 +44,M,01:59:04,04:52:44 +27,W,02:07:32,04:52:45 +41,M,01:54:10,04:52:45 +33,M,02:07:05,04:52:45 +37,W,02:19:28,04:52:45 +32,M,02:09:18,04:52:45 +20,M,01:56:10,04:52:46 +60,M,02:05:05,04:52:46 +38,M,02:08:05,04:52:47 +23,M,02:18:31,04:52:47 +42,M,01:55:04,04:52:47 +25,M,01:58:59,04:52:48 +39,W,02:06:22,04:52:48 +59,W,02:07:27,04:52:48 +36,W,02:08:00,04:52:48 +27,M,01:58:49,04:52:49 +34,M,02:06:38,04:52:49 +40,M,02:17:03,04:52:49 +45,M,01:56:34,04:52:49 +51,M,02:11:26,04:52:49 +53,M,02:03:36,04:52:50 +50,W,02:07:39,04:52:51 +28,M,02:08:51,04:52:51 +21,W,02:13:42,04:52:53 +39,M,02:00:49,04:52:53 +28,W,02:00:31,04:52:54 +41,M,02:07:06,04:52:54 +53,M,01:52:00,04:52:54 +32,M,01:51:47,04:52:54 +51,W,02:06:38,04:52:54 +44,M,01:57:00,04:52:54 +33,W,02:08:26,04:52:54 +51,M,02:01:09,04:52:54 +50,W,02:01:54,04:52:55 +31,M,01:49:56,04:52:55 +32,M,02:03:44,04:52:55 +32,W,02:12:05,04:52:55 +47,M,02:00:57,04:52:56 +37,M,02:00:19,04:52:56 +27,M,01:59:37,04:52:56 +34,W,02:04:51,04:52:57 +30,W,02:12:06,04:52:58 +51,M,02:02:42,04:52:58 +38,M,02:07:53,04:52:58 +34,M,02:00:45,04:52:59 +43,M,02:00:52,04:52:59 +48,M,02:11:44,04:53:00 +51,W,02:06:11,04:53:01 +50,M,02:04:53,04:53:01 +37,M,02:12:00,04:53:01 +30,W,02:07:57,04:53:01 +46,W,01:58:51,04:53:01 +26,W,02:04:43,04:53:01 +43,M,02:11:06,04:53:01 +55,M,02:06:49,04:53:01 +30,M,01:53:16,04:53:02 +51,W,02:01:21,04:53:02 +34,W,02:05:23,04:53:02 +41,W,01:54:53,04:53:03 +30,W,02:12:37,04:53:03 +40,M,01:57:52,04:53:03 +42,M,02:04:38,04:53:03 +42,W,02:02:41,04:53:03 +57,W,01:53:32,04:53:03 +33,W,02:11:11,04:53:03 +30,M,01:52:29,04:53:04 +40,M,02:06:04,04:53:04 +28,M,01:52:30,04:53:04 +51,M,01:53:53,04:53:04 +26,W,02:04:46,04:53:05 +41,M,01:59:46,04:53:05 +37,W,01:58:31,04:53:05 +28,W,02:00:43,04:53:05 +52,M,02:08:06,04:53:05 +48,M,01:55:44,04:53:05 +35,W,02:10:44,04:53:06 +45,W,02:07:29,04:53:06 +39,W,02:11:04,04:53:06 +40,M,01:54:37,04:53:06 +39,M,02:00:58,04:53:07 +40,M,02:08:08,04:53:07 +40,W,01:57:17,04:53:07 +56,M,02:09:32,04:53:07 +47,M,02:00:56,04:53:07 +51,M,02:08:12,04:53:07 +35,W,02:06:56,04:53:07 +27,M,02:13:16,04:53:08 +39,M,02:09:06,04:53:08 +25,W,02:00:02,04:53:08 +41,M,01:59:23,04:53:09 +48,M,01:57:37,04:53:09 +28,W,02:14:09,04:53:09 +40,M,02:14:42,04:53:09 +41,W,01:58:09,04:53:09 +68,M,02:06:15,04:53:09 +40,W,02:19:00,04:53:09 +37,M,01:55:45,04:53:09 +53,M,02:06:29,04:53:10 +42,M,02:01:09,04:53:11 +47,M,01:56:41,04:53:11 +26,M,02:08:25,04:53:11 +39,M,02:04:13,04:53:12 +56,M,01:55:46,04:53:12 +65,M,01:55:30,04:53:12 +40,M,02:08:01,04:53:13 +28,M,01:56:21,04:53:13 +31,W,01:59:25,04:53:14 +50,W,01:56:48,04:53:14 +26,M,02:04:28,04:53:14 +58,M,02:00:56,04:53:14 +40,W,02:09:45,04:53:15 +56,M,02:09:02,04:53:15 +40,W,02:07:13,04:53:16 +54,M,01:59:09,04:53:16 +43,W,02:05:12,04:53:16 +57,M,02:07:01,04:53:16 +43,W,01:58:57,04:53:16 +25,W,02:07:27,04:53:17 +54,W,02:06:06,04:53:17 +22,W,02:04:28,04:53:17 +43,W,02:07:22,04:53:17 +49,M,02:00:42,04:53:17 +41,W,02:07:32,04:53:18 +52,W,02:11:48,04:53:19 +46,M,01:57:12,04:53:19 +32,M,02:03:33,04:53:19 +49,W,02:06:56,04:53:19 +42,M,02:07:32,04:53:19 +48,M,02:07:35,04:53:20 +37,M,01:56:06,04:53:20 +37,M,02:14:22,04:53:20 +49,M,02:08:03,04:53:20 +61,M,02:05:40,04:53:20 +49,M,02:10:44,04:53:20 +48,M,01:56:37,04:53:21 +46,W,01:51:17,04:53:22 +23,M,01:59:13,04:53:22 +56,M,02:13:25,04:53:23 +33,M,02:05:32,04:53:23 +48,W,02:14:46,04:53:23 +25,M,01:51:53,04:53:23 +62,M,02:15:51,04:53:24 +40,W,02:13:40,04:53:24 +35,M,02:14:21,04:53:24 +51,W,02:17:00,04:53:25 +33,W,01:55:21,04:53:25 +34,M,02:01:56,04:53:25 +44,M,02:14:26,04:53:26 +39,M,02:00:17,04:53:26 +41,M,02:01:31,04:53:27 +26,M,02:04:22,04:53:27 +44,M,02:06:48,04:53:27 +37,M,02:03:25,04:53:28 +44,W,02:08:37,04:53:28 +42,W,02:06:41,04:53:28 +39,M,02:05:01,04:53:28 +31,W,02:11:21,04:53:28 +36,M,02:01:44,04:53:29 +57,M,01:47:37,04:53:29 +43,W,02:02:31,04:53:29 +50,M,01:57:40,04:53:29 +58,M,02:08:38,04:53:30 +22,W,02:00:20,04:53:30 +41,W,02:06:26,04:53:31 +41,M,02:10:56,04:53:31 +41,W,02:00:55,04:53:31 +63,M,02:07:50,04:53:32 +43,M,01:59:10,04:53:32 +57,M,02:06:34,04:53:32 +46,M,01:58:54,04:53:32 +43,W,02:04:14,04:53:33 +43,M,02:12:40,04:53:33 +28,M,02:00:05,04:53:33 +32,W,01:57:15,04:53:33 +54,W,01:54:24,04:53:33 +33,M,01:58:13,04:53:33 +24,W,02:03:30,04:53:33 +46,M,01:54:16,04:53:33 +43,M,02:15:11,04:53:34 +54,M,02:05:10,04:53:34 +26,W,02:12:52,04:53:34 +51,M,02:13:27,04:53:34 +42,W,01:58:00,04:53:34 +61,M,02:02:00,04:53:34 +41,M,02:03:39,04:53:35 +36,M,01:58:18,04:53:35 +47,W,02:05:44,04:53:36 +42,M,01:46:13,04:53:37 +50,M,02:05:42,04:53:37 +49,W,02:07:59,04:53:37 +42,W,02:13:40,04:53:37 +57,M,01:59:33,04:53:38 +44,M,02:00:19,04:53:38 +32,W,02:06:34,04:53:39 +30,W,01:57:28,04:53:39 +40,W,02:10:44,04:53:39 +41,M,02:04:25,04:53:39 +51,M,01:55:28,04:53:39 +55,M,01:59:45,04:53:39 +49,M,02:04:50,04:53:40 +35,M,02:02:57,04:53:40 +33,W,02:02:20,04:53:40 +40,M,02:07:33,04:53:40 +48,W,02:09:14,04:53:40 +36,M,02:06:08,04:53:41 +24,W,02:14:51,04:53:41 +61,M,02:05:15,04:53:41 +43,W,01:57:04,04:53:42 +48,M,02:07:41,04:53:42 +38,M,01:58:34,04:53:42 +30,M,01:58:38,04:53:43 +56,M,02:05:12,04:53:43 +54,M,01:57:15,04:53:43 +68,M,02:12:49,04:53:43 +50,W,02:07:28,04:53:43 +24,W,01:57:58,04:53:44 +52,M,01:59:49,04:53:44 +31,W,02:10:48,04:53:44 +35,M,02:10:27,04:53:45 +31,M,02:07:36,04:53:45 +33,M,01:54:56,04:53:45 +26,W,02:06:19,04:53:45 +51,M,02:12:14,04:53:45 +53,M,02:08:24,04:53:46 +45,W,02:03:17,04:53:46 +53,W,02:01:00,04:53:46 +34,W,02:11:51,04:53:46 +50,W,02:07:39,04:53:46 +47,W,02:06:43,04:53:47 +50,M,02:01:19,04:53:47 +46,W,02:04:14,04:53:47 +53,M,02:02:12,04:53:47 +22,W,02:06:18,04:53:48 +65,M,01:54:27,04:53:48 +37,M,02:03:06,04:53:48 +41,M,02:09:50,04:53:49 +33,W,02:11:30,04:53:49 +44,M,01:57:17,04:53:49 +57,M,01:55:47,04:53:49 +50,M,02:08:35,04:53:50 +44,M,02:01:42,04:53:50 +46,M,02:03:55,04:53:50 +45,W,02:16:19,04:53:51 +39,M,02:01:23,04:53:51 +32,W,02:11:02,04:53:51 +36,M,01:54:19,04:53:51 +19,M,01:48:23,04:53:52 +46,M,01:56:57,04:53:52 +41,M,01:54:03,04:53:53 +51,M,02:14:53,04:53:53 +42,M,02:04:57,04:53:54 +26,W,01:55:42,04:53:54 +42,W,02:08:57,04:53:54 +32,W,02:01:29,04:53:55 +26,W,02:05:01,04:53:56 +40,W,02:09:00,04:53:56 +26,W,02:00:19,04:53:56 +67,W,02:09:29,04:53:56 +27,M,01:57:01,04:53:57 +43,M,02:06:20,04:53:58 +30,M,02:03:28,04:53:58 +41,M,02:14:19,04:53:58 +67,M,02:17:12,04:53:59 +36,W,02:06:46,04:53:59 +37,W,02:08:04,04:53:59 +34,W,01:59:10,04:53:59 +35,M,02:11:33,04:53:59 +36,W,02:09:38,04:53:59 +35,M,01:47:33,04:54:00 +51,M,02:10:27,04:54:00 +47,M,02:01:56,04:54:00 +42,W,02:07:09,04:54:00 +40,W,02:01:46,04:54:01 +33,W,02:07:33,04:54:01 +41,M,02:08:07,04:54:01 +43,M,02:10:39,04:54:01 +26,M,02:07:23,04:54:01 +39,W,02:01:07,04:54:02 +48,W,01:53:58,04:54:02 +31,W,02:02:48,04:54:03 +41,M,02:00:09,04:54:03 +40,W,02:10:41,04:54:03 +35,W,02:15:13,04:54:03 +33,M,02:10:15,04:54:03 +26,W,02:14:20,04:54:03 +34,W,02:04:39,04:54:03 +36,M,02:12:58,04:54:03 +47,M,01:57:44,04:54:04 +30,M,02:03:34,04:54:04 +51,W,02:11:25,04:54:04 +36,M,02:03:25,04:54:04 +54,W,02:14:05,04:54:04 +37,W,02:01:07,04:54:05 +31,W,02:13:09,04:54:05 +47,M,01:55:50,04:54:05 +21,M,02:13:20,04:54:05 +48,M,02:14:37,04:54:05 +69,M,02:10:53,04:54:06 +27,M,01:58:45,04:54:06 +46,M,01:56:24,04:54:06 +42,M,02:12:16,04:54:06 +35,M,02:11:11,04:54:06 +46,W,02:00:15,04:54:07 +52,M,02:13:14,04:54:07 +54,W,02:14:59,04:54:07 +46,M,02:05:03,04:54:09 +42,M,02:02:27,04:54:09 +49,M,01:55:34,04:54:09 +36,M,01:53:42,04:54:09 +37,M,02:02:24,04:54:10 +32,M,02:05:55,04:54:10 +33,M,02:06:14,04:54:10 +38,M,02:06:54,04:54:10 +46,W,02:07:15,04:54:11 +60,M,02:01:47,04:54:11 +34,M,02:04:50,04:54:11 +31,M,02:07:58,04:54:11 +39,M,02:02:44,04:54:13 +52,M,02:05:08,04:54:13 +29,W,02:07:50,04:54:13 +45,M,02:04:45,04:54:14 +35,M,02:08:03,04:54:14 +50,W,02:04:27,04:54:15 +39,W,02:00:24,04:54:15 +33,W,02:18:52,04:54:15 +29,M,02:00:04,04:54:15 +29,W,01:57:47,04:54:15 +29,W,01:47:08,04:54:16 +41,W,02:03:05,04:54:16 +32,W,02:16:14,04:54:16 +35,M,02:05:40,04:54:16 +54,M,02:04:05,04:54:16 +27,W,02:10:08,04:54:17 +46,M,02:11:49,04:54:17 +30,W,02:00:45,04:54:17 +25,W,02:09:04,04:54:17 +50,W,02:05:00,04:54:18 +54,M,02:06:11,04:54:18 +33,M,02:10:02,04:54:18 +56,W,02:10:36,04:54:18 +52,M,02:02:46,04:54:18 +49,M,02:02:35,04:54:19 +30,M,02:13:37,04:54:19 +38,M,01:59:55,04:54:19 +28,W,02:11:43,04:54:19 +38,W,02:04:28,04:54:19 +23,W,02:21:32,04:54:20 +35,M,02:14:39,04:54:20 +56,M,02:05:14,04:54:20 +22,W,02:14:37,04:54:20 +66,M,02:06:52,04:54:20 +41,M,02:11:31,04:54:21 +40,M,02:11:16,04:54:21 +38,M,02:08:53,04:54:21 +40,W,02:13:54,04:54:21 +52,M,02:02:40,04:54:21 +32,M,02:07:22,04:54:21 +43,M,01:56:59,04:54:21 +32,M,02:00:53,04:54:22 +44,W,02:07:44,04:54:22 +37,M,01:56:47,04:54:23 +40,M,02:07:53,04:54:23 +33,W,01:59:35,04:54:23 +38,M,01:56:00,04:54:23 +37,M,02:05:47,04:54:23 +41,W,02:06:25,04:54:23 +44,W,02:05:26,04:54:25 +23,M,02:08:49,04:54:25 +60,M,02:04:20,04:54:26 +49,M,02:13:47,04:54:26 +43,W,02:10:19,04:54:26 +50,W,02:08:47,04:54:26 +39,M,01:59:27,04:54:26 +32,W,02:03:44,04:54:27 +34,W,02:04:01,04:54:27 +30,M,02:01:15,04:54:27 +24,M,02:02:41,04:54:27 +59,M,01:56:23,04:54:27 +50,M,01:57:43,04:54:28 +36,W,01:59:48,04:54:28 +25,M,02:03:58,04:54:29 +44,M,01:58:17,04:54:29 +30,M,02:01:06,04:54:30 +58,M,02:00:29,04:54:30 +35,W,02:00:00,04:54:30 +27,W,02:00:52,04:54:30 +50,M,01:57:19,04:54:31 +61,M,02:09:54,04:54:31 +26,W,02:06:43,04:54:31 +39,M,02:16:20,04:54:31 +32,W,02:10:19,04:54:31 +44,M,02:06:58,04:54:31 +28,W,02:20:33,04:54:33 +47,M,02:06:21,04:54:33 +50,M,01:58:59,04:54:33 +34,M,01:58:23,04:54:34 +55,M,01:59:22,04:54:34 +51,M,01:59:55,04:54:34 +55,M,02:19:54,04:54:35 +58,M,01:59:19,04:54:36 +44,W,02:03:20,04:54:36 +26,M,01:51:07,04:54:36 +69,M,02:03:52,04:54:37 +39,M,02:10:46,04:54:37 +47,M,01:58:50,04:54:37 +30,W,02:09:19,04:54:38 +39,W,02:14:34,04:54:38 +24,W,02:14:54,04:54:38 +41,M,01:55:39,04:54:39 +40,W,02:11:45,04:54:39 +41,W,02:07:12,04:54:40 +58,M,01:58:19,04:54:40 +35,M,02:13:17,04:54:41 +51,M,02:09:39,04:54:41 +26,W,01:59:53,04:54:42 +27,M,01:59:18,04:54:42 +38,M,02:11:52,04:54:43 +53,M,02:12:20,04:54:43 +50,M,02:04:02,04:54:43 +42,M,02:06:20,04:54:44 +54,M,01:59:52,04:54:44 +17,W,02:07:59,04:54:44 +49,M,01:59:38,04:54:44 +23,W,02:13:11,04:54:45 +28,W,02:08:29,04:54:45 +47,M,02:02:25,04:54:46 +27,M,02:06:29,04:54:46 +61,M,02:13:43,04:54:46 +24,M,02:02:44,04:54:47 +50,W,01:52:28,04:54:47 +44,M,02:02:22,04:54:47 +40,M,02:12:19,04:54:47 +50,M,02:11:37,04:54:47 +28,W,02:01:11,04:54:47 +44,W,02:06:26,04:54:47 +39,W,02:10:04,04:54:47 +25,M,02:07:16,04:54:48 +31,W,02:05:22,04:54:48 +46,M,01:59:33,04:54:48 +40,M,02:12:25,04:54:49 +29,W,02:09:08,04:54:49 +44,W,02:04:25,04:54:49 +41,W,02:03:31,04:54:49 +23,W,02:08:00,04:54:50 +43,W,02:04:48,04:54:50 +42,M,02:05:16,04:54:50 +60,M,02:01:12,04:54:50 +24,W,02:12:21,04:54:50 +36,M,02:14:27,04:54:51 +50,M,02:04:27,04:54:51 +30,M,02:04:46,04:54:51 +45,M,02:00:50,04:54:51 +53,M,02:10:43,04:54:51 +28,W,02:20:31,04:54:52 +33,M,02:04:24,04:54:52 +51,M,01:56:29,04:54:52 +41,M,01:53:54,04:54:52 +38,W,02:11:43,04:54:53 +41,M,02:17:19,04:54:53 +36,M,02:02:59,04:54:54 +46,M,02:11:25,04:54:54 +38,W,02:13:44,04:54:55 +45,M,02:08:10,04:54:55 +42,W,02:08:29,04:54:55 +34,M,01:57:54,04:54:55 +49,W,02:00:09,04:54:55 +23,W,02:03:02,04:54:55 +42,M,02:09:39,04:54:55 +45,M,02:12:02,04:54:56 +64,M,02:08:26,04:54:56 +52,M,02:04:28,04:54:56 +40,M,02:02:10,04:54:57 +35,W,02:00:36,04:54:57 +32,W,02:09:54,04:54:57 +33,M,02:05:41,04:54:57 +27,M,02:09:55,04:54:57 +26,W,02:12:51,04:54:57 +53,W,02:05:16,04:54:57 +33,M,02:05:35,04:54:58 +58,W,02:12:46,04:54:58 +43,W,01:57:52,04:54:58 +39,M,02:05:31,04:54:58 +32,M,01:59:48,04:54:58 +31,M,01:58:00,04:54:58 +29,W,02:00:19,04:54:59 +26,M,01:58:31,04:55:00 +40,W,02:10:17,04:55:00 +29,W,02:16:57,04:55:00 +31,W,01:57:40,04:55:00 +33,M,02:10:15,04:55:01 +52,M,02:05:51,04:55:01 +47,M,02:11:03,04:55:01 +28,M,02:03:17,04:55:01 +27,M,02:11:53,04:55:01 +46,W,01:59:49,04:55:01 +30,M,02:03:06,04:55:02 +43,W,01:59:43,04:55:02 +52,W,02:11:50,04:55:02 +50,M,02:04:22,04:55:02 +45,M,02:08:07,04:55:03 +59,M,02:08:40,04:55:03 +40,W,02:11:57,04:55:03 +41,M,02:05:53,04:55:03 +42,M,02:01:50,04:55:03 +53,W,01:59:51,04:55:03 +36,M,02:05:34,04:55:04 +39,W,02:06:09,04:55:04 +54,M,02:03:14,04:55:04 +32,M,01:58:24,04:55:04 +52,M,02:06:52,04:55:05 +49,W,02:03:50,04:55:05 +33,W,02:10:18,04:55:05 +31,M,01:50:58,04:55:05 +49,W,02:20:13,04:55:05 +37,M,01:59:34,04:55:05 +44,M,01:58:44,04:55:05 +56,M,02:08:02,04:55:06 +45,W,02:02:21,04:55:06 +38,W,01:58:24,04:55:06 +24,W,02:10:02,04:55:06 +29,M,02:00:31,04:55:07 +36,W,02:02:48,04:55:07 +38,W,01:57:18,04:55:07 +26,W,02:09:34,04:55:08 +38,W,01:54:03,04:55:08 +23,W,02:06:33,04:55:09 +36,M,02:06:07,04:55:09 +44,M,02:00:19,04:55:09 +47,M,02:04:24,04:55:09 +53,W,02:00:46,04:55:09 +55,M,02:04:12,04:55:09 +39,W,02:07:54,04:55:10 +38,W,02:00:49,04:55:10 +33,M,02:09:41,04:55:10 +38,M,02:12:11,04:55:10 +32,W,01:55:26,04:55:10 +25,M,02:04:49,04:55:11 +28,M,02:18:33,04:55:11 +44,M,02:07:01,04:55:11 +37,M,01:57:00,04:55:11 +44,M,02:03:56,04:55:12 +36,W,02:03:33,04:55:13 +27,W,01:56:54,04:55:13 +27,M,01:57:23,04:55:13 +43,W,02:04:55,04:55:13 +55,W,02:00:10,04:55:13 +25,W,01:55:16,04:55:13 +39,W,01:59:18,04:55:14 +33,W,01:56:39,04:55:14 +35,M,02:05:22,04:55:14 +41,M,01:56:25,04:55:15 +36,M,02:03:46,04:55:15 +48,M,02:03:21,04:55:15 +25,W,02:08:03,04:55:15 +47,M,02:04:26,04:55:16 +39,W,02:10:45,04:55:16 +53,M,01:58:58,04:55:16 +40,M,01:58:28,04:55:16 +27,M,01:56:49,04:55:17 +31,W,02:18:45,04:55:17 +37,M,02:02:31,04:55:17 +39,W,02:09:05,04:55:17 +66,W,02:02:05,04:55:18 +45,M,01:51:02,04:55:18 +48,M,02:08:08,04:55:18 +33,W,02:02:02,04:55:18 +32,M,01:59:30,04:55:18 +57,M,02:05:47,04:55:18 +48,M,02:01:52,04:55:18 +47,M,02:10:46,04:55:18 +54,M,02:15:07,04:55:19 +49,M,02:04:48,04:55:19 +48,M,02:12:48,04:55:19 +41,M,02:05:26,04:55:19 +35,M,01:58:47,04:55:19 +52,W,02:10:45,04:55:19 +33,W,01:47:32,04:55:20 +43,W,02:02:03,04:55:20 +45,M,02:02:09,04:55:20 +29,M,01:58:56,04:55:20 +47,M,02:01:22,04:55:21 +59,M,02:06:45,04:55:22 +37,M,02:01:24,04:55:22 +26,W,02:07:38,04:55:23 +39,M,02:07:02,04:55:23 +39,M,02:04:40,04:55:23 +39,M,01:56:13,04:55:23 +47,M,02:03:29,04:55:24 +37,M,02:06:51,04:55:24 +29,W,02:02:01,04:55:24 +64,M,02:08:56,04:55:24 +61,M,02:06:53,04:55:24 +38,W,02:12:07,04:55:25 +26,M,02:05:07,04:55:25 +32,W,01:59:39,04:55:25 +21,M,01:57:49,04:55:25 +27,M,02:04:20,04:55:26 +59,M,02:07:29,04:55:26 +36,W,02:01:19,04:55:26 +26,W,02:04:42,04:55:26 +35,M,02:00:40,04:55:26 +27,W,02:05:46,04:55:26 +34,M,02:04:47,04:55:27 +59,M,01:58:52,04:55:27 +52,M,02:00:19,04:55:27 +45,M,02:00:08,04:55:28 +53,M,02:07:04,04:55:28 +27,W,02:13:54,04:55:28 +34,W,02:11:29,04:55:29 +59,M,02:04:30,04:55:29 +58,M,01:59:03,04:55:29 +57,M,02:07:49,04:55:29 +43,W,02:06:42,04:55:30 +30,W,02:11:11,04:55:30 +58,W,02:07:12,04:55:30 +53,M,02:00:12,04:55:30 +28,W,01:55:10,04:55:30 +31,W,02:02:04,04:55:31 +40,W,02:11:06,04:55:31 +38,W,02:05:38,04:55:31 +28,W,02:06:10,04:55:31 +46,W,02:04:10,04:55:31 +35,W,02:09:48,04:55:32 +37,W,02:11:44,04:55:32 +44,M,02:12:34,04:55:32 +42,M,02:08:22,04:55:33 +58,W,02:10:34,04:55:33 +41,M,02:03:16,04:55:33 +30,W,02:14:33,04:55:33 +56,M,02:04:39,04:55:33 +33,M,02:07:38,04:55:33 +36,M,02:04:13,04:55:33 +41,M,02:00:23,04:55:34 +35,M,02:11:55,04:55:34 +47,M,02:03:06,04:55:34 +27,M,01:52:26,04:55:34 +27,M,01:55:00,04:55:34 +41,M,02:02:22,04:55:35 +52,M,02:14:41,04:55:36 +32,M,01:59:48,04:55:37 +33,M,02:06:24,04:55:37 +36,M,02:09:37,04:55:37 +44,W,02:16:11,04:55:37 +42,M,02:01:08,04:55:38 +26,M,02:03:17,04:55:38 +38,M,01:59:33,04:55:39 +70,M,02:13:43,04:55:39 +24,M,02:12:14,04:55:39 +42,W,02:13:51,04:55:39 +51,M,01:59:52,04:55:39 +29,W,02:05:52,04:55:39 +64,M,02:16:39,04:55:40 +64,M,02:08:12,04:55:40 +33,W,02:01:16,04:55:40 +22,M,02:07:44,04:55:40 +40,M,02:10:21,04:55:41 +43,M,02:05:03,04:55:43 +40,W,02:09:31,04:55:43 +43,M,02:05:42,04:55:43 +27,M,01:52:03,04:55:44 +66,M,01:53:14,04:55:44 +51,M,02:00:20,04:55:44 +31,W,01:59:23,04:55:45 +54,M,02:05:18,04:55:45 +40,W,02:14:24,04:55:45 +40,W,02:13:30,04:55:45 +48,W,02:07:55,04:55:45 +33,M,02:06:33,04:55:46 +32,M,02:08:10,04:55:46 +43,M,02:12:38,04:55:46 +36,M,01:53:55,04:55:47 +49,M,01:59:04,04:55:47 +48,W,02:05:52,04:55:47 +58,W,02:07:46,04:55:47 +56,M,02:08:11,04:55:48 +40,M,02:14:21,04:55:48 +42,W,02:03:34,04:55:48 +36,M,02:09:09,04:55:49 +46,W,02:02:41,04:55:49 +40,M,02:09:12,04:55:49 +40,W,02:05:23,04:55:49 +47,W,02:06:14,04:55:49 +49,M,02:06:42,04:55:49 +43,W,01:57:50,04:55:49 +40,M,02:06:31,04:55:49 +25,W,02:07:22,04:55:50 +30,W,01:58:48,04:55:50 +55,W,02:00:55,04:55:50 +56,M,02:14:39,04:55:50 +38,M,02:00:53,04:55:50 +34,W,02:03:56,04:55:50 +52,W,02:05:13,04:55:51 +34,W,02:00:49,04:55:51 +38,M,02:00:02,04:55:51 +29,W,02:11:12,04:55:51 +40,W,02:04:03,04:55:53 +52,W,02:15:10,04:55:53 +32,M,02:00:51,04:55:54 +43,W,02:19:15,04:55:54 +43,M,02:03:49,04:55:55 +25,M,02:02:20,04:55:55 +45,M,02:09:35,04:55:55 +28,M,02:08:21,04:55:56 +40,M,02:14:36,04:55:56 +39,M,01:48:47,04:55:56 +52,M,02:07:31,04:55:58 +42,M,01:52:49,04:55:58 +40,M,02:05:03,04:55:58 +27,M,02:07:41,04:55:58 +60,M,02:01:44,04:55:59 +39,M,02:00:28,04:55:59 +39,M,02:08:42,04:55:59 +31,W,02:06:33,04:55:59 +45,M,01:57:15,04:55:59 +37,M,02:01:13,04:55:59 +36,M,01:57:59,04:55:59 +51,M,02:04:01,04:55:59 +34,W,02:08:17,04:56:00 +50,W,02:00:14,04:56:00 +51,W,01:58:15,04:56:00 +36,M,02:09:41,04:56:00 +25,W,02:02:00,04:56:00 +36,W,02:03:46,04:56:00 +35,M,01:58:49,04:56:01 +42,W,02:10:34,04:56:01 +29,W,02:10:28,04:56:01 +39,W,02:06:18,04:56:01 +39,W,01:57:22,04:56:01 +47,W,02:06:53,04:56:01 +36,M,02:14:54,04:56:02 +31,M,01:57:33,04:56:02 +25,M,02:05:16,04:56:02 +43,W,01:59:48,04:56:03 +64,M,02:05:01,04:56:03 +56,W,02:06:11,04:56:03 +45,M,02:12:57,04:56:04 +23,M,02:06:05,04:56:04 +31,M,01:57:44,04:56:04 +28,W,02:03:14,04:56:05 +40,M,02:21:27,04:56:05 +39,M,01:59:02,04:56:05 +28,M,02:07:47,04:56:05 +29,W,02:12:22,04:56:05 +30,W,02:05:22,04:56:06 +28,W,01:52:21,04:56:06 +40,W,01:59:53,04:56:06 +36,W,02:07:56,04:56:06 +31,M,02:06:46,04:56:06 +27,W,02:10:49,04:56:07 +41,M,02:01:45,04:56:07 +42,M,02:08:39,04:56:07 +53,M,01:46:36,04:56:07 +22,W,02:00:17,04:56:07 +46,W,02:10:04,04:56:07 +47,W,02:01:55,04:56:07 +58,M,02:00:06,04:56:08 +30,M,02:06:34,04:56:09 +25,W,02:07:17,04:56:09 +48,M,02:01:55,04:56:09 +37,M,02:21:05,04:56:09 +37,W,02:05:10,04:56:09 +42,M,02:01:48,04:56:09 +38,W,01:59:25,04:56:09 +39,M,02:05:27,04:56:10 +38,M,01:59:30,04:56:10 +28,W,02:03:10,04:56:10 +45,W,02:13:55,04:56:10 +62,M,02:02:25,04:56:10 +45,M,01:53:45,04:56:11 +39,M,02:07:34,04:56:11 +36,M,02:05:16,04:56:11 +43,M,02:03:21,04:56:11 +32,M,01:53:56,04:56:11 +39,M,01:59:15,04:56:11 +48,M,01:57:09,04:56:12 +37,M,02:23:20,04:56:12 +56,M,02:17:23,04:56:13 +40,M,02:16:56,04:56:14 +51,M,02:13:48,04:56:14 +43,M,02:04:58,04:56:14 +35,M,02:13:01,04:56:14 +27,W,02:03:08,04:56:15 +44,M,01:59:29,04:56:15 +43,W,02:14:29,04:56:15 +35,M,02:06:12,04:56:15 +35,M,02:07:09,04:56:15 +28,M,02:06:07,04:56:16 +56,M,02:03:18,04:56:16 +56,M,02:05:46,04:56:17 +41,M,02:04:27,04:56:17 +33,M,01:54:23,04:56:17 +30,W,02:12:02,04:56:17 +42,M,01:58:59,04:56:18 +39,M,02:11:40,04:56:18 +41,M,02:01:42,04:56:18 +29,M,02:05:35,04:56:18 +60,M,01:58:35,04:56:18 +24,W,02:10:56,04:56:19 +28,W,02:00:28,04:56:19 +37,M,02:15:04,04:56:20 +22,W,02:02:34,04:56:20 +30,M,02:02:13,04:56:20 +48,M,01:54:50,04:56:21 +29,M,01:49:29,04:56:21 +43,W,02:04:47,04:56:22 +40,M,02:01:59,04:56:22 +33,M,01:59:08,04:56:22 +26,W,02:00:00,04:56:22 +44,M,01:59:39,04:56:22 +48,W,02:05:23,04:56:22 +44,M,01:56:43,04:56:23 +34,W,02:07:16,04:56:23 +23,M,02:22:58,04:56:23 +26,M,02:08:00,04:56:24 +70,M,02:07:31,04:56:24 +46,W,02:00:18,04:56:24 +34,M,02:07:16,04:56:24 +30,W,01:59:47,04:56:24 +30,W,02:06:01,04:56:24 +34,W,02:01:14,04:56:24 +55,M,02:10:24,04:56:25 +40,M,02:02:12,04:56:25 +45,M,02:05:01,04:56:26 +53,M,02:05:45,04:56:26 +54,M,02:07:16,04:56:27 +35,W,02:08:44,04:56:27 +61,M,02:04:20,04:56:27 +46,M,02:05:27,04:56:27 +35,M,02:12:56,04:56:28 +67,M,02:06:51,04:56:28 +54,M,02:18:26,04:56:28 +33,M,02:03:48,04:56:29 +27,M,01:45:41,04:56:29 +33,M,02:14:11,04:56:29 +33,W,02:13:06,04:56:30 +43,M,02:10:47,04:56:30 +40,W,02:03:25,04:56:30 +45,W,02:12:06,04:56:31 +42,W,02:03:02,04:56:31 +43,W,02:14:12,04:56:31 +33,M,02:10:48,04:56:31 +56,M,02:04:21,04:56:31 +27,M,02:03:42,04:56:31 +29,M,01:48:57,04:56:32 +32,W,02:05:54,04:56:32 +53,W,02:03:58,04:56:33 +45,M,02:16:27,04:56:33 +49,M,02:09:14,04:56:33 +44,M,02:06:56,04:56:33 +60,M,02:08:43,04:56:34 +26,W,02:11:03,04:56:34 +60,M,02:12:32,04:56:34 +39,M,02:02:50,04:56:35 +28,M,02:12:44,04:56:35 +60,M,02:03:49,04:56:35 +20,W,02:13:54,04:56:36 +40,M,02:03:47,04:56:36 +46,M,02:05:47,04:56:36 +30,M,02:01:25,04:56:36 +49,W,02:06:01,04:56:36 +30,M,02:11:55,04:56:37 +47,W,02:22:20,04:56:37 +38,M,02:06:49,04:56:37 +34,W,02:15:59,04:56:38 +58,M,02:04:36,04:56:38 +43,W,02:02:12,04:56:38 +34,M,02:02:34,04:56:38 +28,W,02:18:54,04:56:38 +56,W,02:07:58,04:56:38 +35,M,01:59:28,04:56:38 +44,M,01:57:48,04:56:38 +45,M,02:12:36,04:56:39 +31,M,01:50:57,04:56:39 +47,M,02:05:48,04:56:39 +42,M,02:01:32,04:56:39 +58,M,02:10:00,04:56:39 +55,W,01:57:41,04:56:40 +26,M,02:10:07,04:56:40 +45,W,01:57:24,04:56:40 +49,M,02:05:00,04:56:40 +46,M,02:03:08,04:56:41 +53,M,02:00:53,04:56:41 +53,M,02:08:03,04:56:41 +32,M,01:59:42,04:56:41 +36,W,02:08:58,04:56:41 +40,M,01:56:57,04:56:42 +34,W,02:00:59,04:56:42 +46,W,02:02:11,04:56:43 +33,M,01:50:50,04:56:43 +56,M,02:09:54,04:56:43 +53,M,02:03:07,04:56:43 +50,M,02:04:19,04:56:43 +55,M,01:58:54,04:56:44 +43,W,02:03:05,04:56:44 +35,W,02:03:43,04:56:44 +53,M,02:00:02,04:56:44 +47,W,01:55:11,04:56:45 +51,M,01:57:24,04:56:45 +38,W,02:06:58,04:56:45 +51,W,02:02:46,04:56:46 +48,M,01:57:07,04:56:46 +47,M,02:05:01,04:56:46 +32,W,02:03:47,04:56:47 +46,M,02:03:29,04:56:47 +31,M,02:09:11,04:56:47 +51,M,01:56:52,04:56:48 +56,W,02:09:51,04:56:48 +46,W,01:59:41,04:56:48 +40,M,02:03:07,04:56:48 +51,W,02:03:43,04:56:49 +29,M,02:07:05,04:56:49 +50,M,01:58:04,04:56:49 +28,M,02:05:35,04:56:49 +46,M,02:00:08,04:56:50 +39,M,02:07:31,04:56:51 +47,M,02:08:43,04:56:51 +43,M,02:06:46,04:56:51 +30,W,02:07:18,04:56:52 +38,W,02:09:22,04:56:52 +50,M,02:08:39,04:56:53 +38,M,02:01:06,04:56:53 +53,M,01:51:58,04:56:54 +43,W,02:09:06,04:56:54 +51,M,02:02:59,04:56:54 +25,W,02:01:32,04:56:54 +28,W,02:10:00,04:56:56 +33,M,02:04:22,04:56:56 +41,W,01:59:54,04:56:56 +32,W,02:06:21,04:56:57 +50,M,02:07:42,04:56:57 +46,M,02:08:25,04:56:57 +33,W,02:01:41,04:56:57 +36,W,02:11:12,04:56:57 +26,M,02:04:30,04:56:57 +32,M,01:58:41,04:56:57 +35,W,02:04:10,04:56:58 +49,M,01:56:09,04:56:58 +29,M,01:55:19,04:56:59 +53,W,02:14:51,04:56:59 +30,W,02:05:10,04:56:59 +58,M,01:51:55,04:56:59 +50,W,02:00:54,04:57:00 +40,M,02:16:41,04:57:00 +51,W,02:02:29,04:57:00 +33,W,02:07:11,04:57:00 +42,M,02:11:34,04:57:00 +27,M,01:58:46,04:57:00 +41,W,02:07:21,04:57:01 +30,W,02:02:02,04:57:01 +52,M,02:10:08,04:57:01 +25,W,02:01:49,04:57:02 +44,M,02:05:27,04:57:02 +42,W,02:06:19,04:57:02 +60,M,01:58:22,04:57:03 +60,M,01:59:57,04:57:03 +28,M,01:59:15,04:57:03 +30,W,02:17:20,04:57:04 +44,W,02:17:15,04:57:04 +37,M,01:58:10,04:57:05 +33,M,02:11:43,04:57:05 +35,M,02:02:25,04:57:05 +68,W,02:15:45,04:57:05 +65,M,02:03:40,04:57:05 +31,M,02:02:19,04:57:05 +29,W,02:04:50,04:57:05 +46,W,02:01:03,04:57:05 +43,M,02:08:45,04:57:06 +27,W,02:04:38,04:57:06 +36,M,02:16:13,04:57:06 +23,M,02:11:12,04:57:06 +44,M,02:01:19,04:57:06 +49,M,02:04:23,04:57:07 +59,M,02:03:58,04:57:07 +58,M,02:01:24,04:57:08 +35,W,02:02:50,04:57:08 +36,W,02:13:32,04:57:08 +23,M,02:06:53,04:57:09 +27,M,01:58:31,04:57:11 +41,W,02:11:03,04:57:11 +36,M,02:11:17,04:57:11 +60,M,02:01:49,04:57:11 +51,M,02:01:39,04:57:12 +43,M,01:59:16,04:57:12 +55,M,02:11:35,04:57:12 +31,W,02:20:56,04:57:12 +41,W,02:01:58,04:57:12 +40,W,02:02:56,04:57:13 +34,W,02:11:44,04:57:13 +27,W,02:13:52,04:57:13 +46,M,02:01:35,04:57:13 +50,W,02:05:34,04:57:14 +31,M,02:06:09,04:57:14 +40,W,02:11:33,04:57:14 +30,W,02:14:23,04:57:14 +55,M,01:54:42,04:57:14 +33,M,02:12:39,04:57:14 +30,M,02:03:55,04:57:14 +29,W,02:05:07,04:57:14 +35,W,02:04:50,04:57:14 +34,W,02:06:38,04:57:14 +23,W,02:01:23,04:57:14 +42,M,02:01:23,04:57:15 +32,M,02:11:49,04:57:15 +58,M,02:18:16,04:57:15 +58,M,01:55:59,04:57:16 +44,M,02:08:04,04:57:16 +28,M,02:02:36,04:57:16 +47,M,01:59:23,04:57:16 +28,W,02:11:28,04:57:16 +39,M,02:07:21,04:57:17 +45,M,01:57:58,04:57:17 +51,M,01:56:02,04:57:17 +28,W,02:05:32,04:57:18 +34,M,02:16:34,04:57:19 +28,M,01:52:34,04:57:19 +37,W,02:03:49,04:57:19 +47,M,01:56:07,04:57:19 +41,M,02:00:20,04:57:20 +47,M,02:04:38,04:57:20 +47,M,02:02:22,04:57:21 +36,M,01:59:17,04:57:21 +30,M,02:08:54,04:57:22 +48,M,02:03:53,04:57:23 +53,M,02:03:00,04:57:23 +50,M,02:04:11,04:57:23 +36,W,02:12:57,04:57:23 +39,M,02:08:51,04:57:24 +46,M,02:04:10,04:57:24 +58,M,02:01:45,04:57:25 +26,M,01:53:12,04:57:25 +46,W,02:01:28,04:57:25 +29,M,02:10:04,04:57:26 +51,M,02:11:27,04:57:26 +34,W,02:18:22,04:57:26 +61,M,02:10:01,04:57:27 +45,W,02:08:04,04:57:28 +47,M,02:09:24,04:57:28 +35,W,02:09:21,04:57:29 +25,W,02:01:28,04:57:29 +32,M,02:08:31,04:57:30 +49,M,02:20:53,04:57:30 +29,W,02:09:07,04:57:30 +31,W,02:06:31,04:57:30 +35,M,02:14:21,04:57:31 +45,M,02:08:41,04:57:31 +39,M,02:00:14,04:57:31 +28,W,02:09:24,04:57:31 +49,M,01:58:51,04:57:31 +28,W,02:01:55,04:57:32 +52,M,01:56:31,04:57:32 +34,W,02:04:55,04:57:32 +45,W,02:14:19,04:57:32 +40,M,01:57:06,04:57:32 +29,W,02:05:32,04:57:32 +59,W,01:59:12,04:57:32 +26,M,01:55:48,04:57:32 +51,M,02:05:24,04:57:33 +54,M,02:14:52,04:57:33 +26,W,02:14:56,04:57:33 +60,M,02:02:33,04:57:33 +45,M,01:57:27,04:57:34 +42,M,02:14:44,04:57:34 +61,M,01:51:26,04:57:35 +37,W,01:57:29,04:57:35 +33,W,02:03:30,04:57:35 +47,M,02:04:47,04:57:35 +55,W,02:13:06,04:57:35 +44,M,02:02:30,04:57:36 +58,M,01:59:54,04:57:36 +40,M,02:12:14,04:57:36 +29,M,02:05:00,04:57:36 +57,M,02:00:55,04:57:36 +50,M,01:55:57,04:57:37 +56,M,02:10:08,04:57:37 +42,M,02:05:25,04:57:38 +38,M,02:03:49,04:57:38 +18,M,02:04:54,04:57:38 +48,M,01:54:02,04:57:38 +50,M,02:11:56,04:57:38 +41,M,01:54:23,04:57:39 +25,W,02:05:27,04:57:40 +41,M,01:59:02,04:57:40 +38,M,02:03:32,04:57:40 +50,M,01:58:59,04:57:40 +34,M,01:56:23,04:57:40 +56,M,01:52:31,04:57:40 +29,M,02:02:34,04:57:40 +39,M,02:02:15,04:57:41 +47,W,02:01:48,04:57:41 +46,W,02:09:04,04:57:41 +47,W,01:57:32,04:57:41 +65,M,02:05:33,04:57:41 +43,M,01:59:25,04:57:41 +46,M,02:07:58,04:57:42 +26,W,02:06:55,04:57:42 +43,W,02:15:54,04:57:42 +43,W,02:08:29,04:57:42 +29,M,01:56:52,04:57:43 +50,M,02:08:47,04:57:43 +48,M,02:13:28,04:57:43 +57,W,02:04:29,04:57:43 +31,M,01:53:39,04:57:44 +31,W,02:17:13,04:57:44 +37,M,02:03:24,04:57:44 +24,M,02:07:36,04:57:44 +24,W,02:03:33,04:57:44 +49,M,02:00:55,04:57:45 +35,M,01:58:57,04:57:45 +20,M,02:09:38,04:57:45 +48,M,02:08:42,04:57:45 +39,W,02:08:20,04:57:45 +48,M,02:05:48,04:57:45 +41,W,02:03:48,04:57:45 +33,M,02:04:09,04:57:46 +27,W,02:12:34,04:57:46 +42,M,02:08:15,04:57:46 +48,M,02:05:27,04:57:46 +48,M,02:05:48,04:57:46 +50,W,02:14:25,04:57:46 +22,W,02:09:44,04:57:47 +55,W,02:10:35,04:57:47 +41,W,02:06:55,04:57:48 +52,M,02:01:25,04:57:48 +21,M,02:06:46,04:57:48 +47,W,02:04:59,04:57:49 +57,M,02:02:56,04:57:49 +42,M,02:10:06,04:57:49 +28,M,01:57:58,04:57:50 +33,W,01:59:20,04:57:50 +40,W,02:15:02,04:57:50 +47,W,02:11:37,04:57:50 +57,M,02:01:04,04:57:51 +33,W,02:10:19,04:57:51 +51,M,02:14:10,04:57:51 +22,M,02:02:33,04:57:51 +39,W,02:08:20,04:57:52 +38,M,02:01:46,04:57:52 +42,M,02:05:04,04:57:52 +32,M,02:08:39,04:57:52 +44,M,02:07:02,04:57:52 +41,M,01:57:37,04:57:52 +38,W,02:13:34,04:57:53 +46,M,02:03:17,04:57:53 +49,M,02:10:18,04:57:53 +39,M,02:05:51,04:57:53 +19,M,01:54:28,04:57:54 +43,M,02:12:02,04:57:55 +31,M,01:59:49,04:57:55 +36,M,02:21:10,04:57:56 +43,M,02:05:55,04:57:56 +49,W,02:00:33,04:57:56 +52,W,02:01:05,04:57:56 +52,M,02:02:58,04:57:57 +32,M,02:15:04,04:57:57 +63,W,02:11:35,04:57:57 +42,M,02:19:01,04:57:57 +43,M,02:04:23,04:57:57 +41,M,02:01:20,04:57:58 +29,M,02:06:56,04:57:58 +34,M,02:00:41,04:57:59 +48,M,01:59:05,04:57:59 +31,W,02:01:15,04:57:59 +35,W,02:19:22,04:57:59 +35,W,02:05:58,04:57:59 +55,W,02:02:30,04:57:59 +49,W,02:06:26,04:58:00 +31,W,02:11:13,04:58:01 +27,W,02:09:41,04:58:01 +27,W,02:05:25,04:58:01 +50,M,02:11:03,04:58:01 +57,M,02:06:53,04:58:01 +27,M,01:56:50,04:58:01 +30,M,02:05:28,04:58:02 +32,M,02:03:01,04:58:02 +27,W,02:11:28,04:58:02 +41,M,01:52:15,04:58:03 +43,M,02:06:45,04:58:04 +48,W,02:15:02,04:58:04 +43,M,02:13:19,04:58:04 +37,W,02:03:35,04:58:04 +38,M,01:44:08,04:58:04 +23,W,02:07:22,04:58:04 +56,M,02:01:30,04:58:05 +45,M,01:50:49,04:58:05 +44,W,02:08:25,04:58:05 +40,M,02:02:23,04:58:05 +30,W,02:11:25,04:58:06 +54,M,01:54:29,04:58:06 +52,M,02:07:22,04:58:06 +46,M,01:59:04,04:58:06 +45,M,02:10:49,04:58:07 +28,W,02:04:35,04:58:07 +50,M,02:02:54,04:58:07 +44,M,02:00:15,04:58:07 +54,M,02:00:16,04:58:08 +49,M,02:02:34,04:58:08 +38,M,01:55:43,04:58:08 +33,W,02:04:00,04:58:09 +37,W,02:06:06,04:58:09 +37,M,02:02:30,04:58:10 +51,M,02:09:40,04:58:10 +66,M,02:05:19,04:58:10 +37,M,01:56:08,04:58:11 +26,M,01:59:38,04:58:11 +50,W,02:05:36,04:58:11 +39,W,02:00:15,04:58:11 +40,M,02:11:40,04:58:11 +43,M,02:21:10,04:58:11 +33,W,02:11:31,04:58:12 +43,W,02:06:36,04:58:12 +45,W,02:02:02,04:58:12 +34,M,02:02:12,04:58:13 +34,W,02:10:28,04:58:13 +53,M,01:59:11,04:58:13 +36,W,02:02:16,04:58:14 +28,M,02:08:18,04:58:15 +49,M,02:13:44,04:58:15 +46,W,01:59:56,04:58:15 +36,M,02:01:19,04:58:16 +26,W,02:14:32,04:58:16 +37,W,02:17:41,04:58:16 +53,M,01:58:11,04:58:16 +37,W,02:13:17,04:58:16 +43,M,02:01:30,04:58:16 +39,M,01:59:09,04:58:16 +46,W,02:54:23,04:58:17 +40,M,01:52:57,04:58:17 +34,W,02:18:56,04:58:17 +29,W,02:09:17,04:58:17 +47,M,02:07:00,04:58:17 +46,W,02:09:27,04:58:18 +39,M,02:06:59,04:58:18 +40,M,01:57:10,04:58:18 +44,M,02:00:28,04:58:19 +51,M,01:58:18,04:58:19 +31,M,01:54:44,04:58:19 +33,M,02:08:12,04:58:19 +48,M,02:16:35,04:58:19 +39,W,02:16:50,04:58:19 +46,W,02:08:08,04:58:20 +39,W,02:07:03,04:58:20 +38,W,02:05:16,04:58:21 +41,M,02:08:57,04:58:21 +49,W,02:00:49,04:58:21 +38,M,02:05:12,04:58:21 +46,W,02:13:27,04:58:22 +33,M,02:14:35,04:58:23 +61,M,02:09:44,04:58:23 +32,M,02:06:20,04:58:23 +50,W,02:03:30,04:58:23 +56,M,02:05:38,04:58:24 +37,M,02:09:53,04:58:25 +38,W,02:00:32,04:58:25 +36,M,02:09:43,04:58:26 +49,W,02:08:49,04:58:26 +32,W,02:08:24,04:58:26 +42,M,02:12:30,04:58:27 +45,M,01:58:58,04:58:28 +60,M,01:58:20,04:58:28 +65,M,02:05:15,04:58:28 +54,W,02:12:33,04:58:28 +42,W,02:05:18,04:58:28 +34,W,02:12:19,04:58:28 +33,W,02:09:20,04:58:29 +43,M,02:03:49,04:58:29 +34,M,02:08:50,04:58:29 +49,W,02:05:23,04:58:29 +48,M,02:00:21,04:58:29 +26,W,02:04:31,04:58:30 +66,W,02:14:20,04:58:30 +30,W,02:05:09,04:58:30 +20,M,01:59:28,04:58:31 +43,M,02:01:01,04:58:31 +27,W,02:10:41,04:58:31 +38,W,02:10:20,04:58:31 +55,M,02:04:14,04:58:32 +28,W,02:00:25,04:58:32 +50,M,02:03:24,04:58:32 +44,M,02:01:06,04:58:32 +52,M,02:03:35,04:58:33 +53,M,02:01:48,04:58:33 +52,M,02:02:30,04:58:33 +42,M,02:02:48,04:58:33 +40,M,02:01:09,04:58:34 +26,W,02:24:01,04:58:34 +34,W,02:00:19,04:58:35 +30,W,02:07:08,04:58:35 +27,M,02:04:11,04:58:35 +48,M,02:00:07,04:58:35 +50,M,02:04:36,04:58:36 +40,M,02:02:42,04:58:36 +33,M,01:52:18,04:58:36 +46,W,02:17:28,04:58:36 +30,W,02:04:08,04:58:36 +47,M,02:05:49,04:58:37 +64,M,02:14:09,04:58:37 +25,W,02:11:01,04:58:37 +43,M,02:03:04,04:58:37 +28,M,01:59:18,04:58:37 +46,M,02:02:48,04:58:38 +34,W,02:09:01,04:58:38 +46,M,01:59:58,04:58:38 +62,M,02:10:30,04:58:39 +24,W,01:59:52,04:58:39 +51,M,02:01:08,04:58:39 +41,M,02:12:19,04:58:39 +30,W,02:11:25,04:58:39 +58,M,02:12:40,04:58:40 +39,M,02:08:31,04:58:40 +40,M,02:03:58,04:58:40 +31,W,02:21:33,04:58:40 +24,W,02:06:50,04:58:40 +61,M,02:13:57,04:58:40 +41,M,02:04:54,04:58:40 +69,M,02:12:18,04:58:41 +39,M,02:07:33,04:58:41 +36,W,02:10:50,04:58:41 +57,M,02:04:23,04:58:41 +36,W,02:01:29,04:58:42 +36,M,02:04:11,04:58:43 +50,W,02:04:29,04:58:43 +34,W,02:11:56,04:58:44 +52,M,02:04:32,04:58:44 +39,M,02:06:00,04:58:44 +23,W,02:04:28,04:58:44 +23,M,02:02:49,04:58:44 +57,M,02:13:43,04:58:45 +47,W,02:13:15,04:58:45 +36,W,02:09:36,04:58:45 +34,W,02:09:25,04:58:46 +38,M,02:00:55,04:58:47 +57,M,02:09:31,04:58:47 +41,M,02:17:42,04:58:48 +61,M,02:01:03,04:58:48 +44,M,02:01:53,04:58:48 +30,W,02:08:48,04:58:48 +49,W,02:09:17,04:58:48 +46,W,02:04:54,04:58:49 +29,M,01:48:32,04:58:49 +33,M,02:10:23,04:58:49 +38,M,01:58:23,04:58:49 +48,M,02:09:01,04:58:49 +44,M,02:09:59,04:58:49 +58,M,02:05:28,04:58:49 +37,W,02:09:25,04:58:49 +41,M,02:03:33,04:58:49 +43,M,01:56:36,04:58:50 +27,M,02:26:24,04:58:50 +27,W,01:57:36,04:58:50 +37,M,01:57:24,04:58:50 +37,W,02:02:50,04:58:51 +43,M,02:08:03,04:58:51 +42,M,02:08:19,04:58:51 +51,M,02:07:07,04:58:51 +30,M,02:03:12,04:58:51 +28,M,02:09:57,04:58:51 +31,M,02:16:12,04:58:51 +39,M,02:00:21,04:58:52 +38,W,02:05:17,04:58:52 +39,M,02:11:12,04:58:52 +42,W,02:06:01,04:58:52 +45,M,01:56:15,04:58:53 +46,M,02:06:11,04:58:53 +40,W,02:12:28,04:58:53 +47,W,02:02:16,04:58:53 +32,M,01:59:03,04:58:53 +28,M,02:04:16,04:58:54 +34,W,02:07:42,04:58:54 +31,M,02:12:12,04:58:54 +57,M,02:06:12,04:58:54 +44,M,02:03:18,04:58:54 +48,W,02:10:25,04:58:55 +37,W,02:13:55,04:58:55 +32,M,02:17:22,04:58:56 +37,M,02:11:15,04:58:56 +35,W,02:15:03,04:58:56 +24,W,02:03:44,04:58:57 +44,M,02:02:21,04:58:58 +56,M,02:01:58,04:58:58 +49,M,02:02:19,04:58:58 +29,W,02:02:03,04:58:58 +36,W,02:00:44,04:58:59 +27,M,01:56:34,04:58:59 +53,M,02:16:27,04:58:59 +27,W,02:07:11,04:58:59 +23,M,02:06:17,04:59:00 +45,W,02:10:18,04:59:01 +39,M,01:57:15,04:59:01 +40,M,01:59:06,04:59:02 +42,M,02:01:16,04:59:02 +27,W,02:04:37,04:59:02 +43,M,02:00:58,04:59:02 +25,W,02:04:59,04:59:02 +58,M,02:12:52,04:59:02 +42,W,02:03:06,04:59:03 +41,M,02:07:52,04:59:04 +42,W,02:05:03,04:59:04 +49,W,02:09:26,04:59:05 +46,W,02:07:28,04:59:05 +61,M,02:04:06,04:59:05 +53,M,02:10:02,04:59:05 +49,M,02:01:44,04:59:05 +55,M,02:08:17,04:59:06 +35,M,02:16:58,04:59:06 +53,M,02:11:10,04:59:06 +53,M,02:12:02,04:59:06 +42,M,01:58:44,04:59:06 +34,W,02:01:10,04:59:07 +33,M,02:06:10,04:59:07 +51,M,02:01:32,04:59:07 +28,M,01:56:41,04:59:07 +39,W,02:14:35,04:59:08 +27,M,02:16:25,04:59:08 +26,W,02:09:51,04:59:08 +34,W,02:11:20,04:59:08 +28,M,01:55:28,04:59:09 +46,M,01:57:55,04:59:09 +55,M,02:06:03,04:59:09 +30,M,01:55:39,04:59:10 +36,W,02:11:07,04:59:10 +51,M,02:12:29,04:59:10 +40,M,02:09:29,04:59:10 +39,M,02:05:34,04:59:10 +41,M,02:06:48,04:59:11 +44,M,01:59:30,04:59:11 +37,M,02:12:37,04:59:11 +38,M,02:15:42,04:59:11 +35,W,02:01:55,04:59:11 +48,W,02:17:44,04:59:12 +47,M,02:16:23,04:59:12 +25,W,02:02:04,04:59:12 +39,W,01:58:20,04:59:12 +30,M,01:55:42,04:59:13 +42,M,02:00:47,04:59:13 +42,M,01:57:24,04:59:13 +28,M,02:02:56,04:59:14 +43,M,02:09:44,04:59:14 +38,M,02:09:59,04:59:15 +51,W,02:14:52,04:59:15 +49,M,01:57:22,04:59:15 +38,M,01:54:03,04:59:15 +51,W,02:12:09,04:59:15 +54,W,02:05:59,04:59:16 +35,M,01:47:36,04:59:17 +36,M,02:16:56,04:59:17 +43,M,02:07:36,04:59:17 +48,M,02:07:31,04:59:17 +40,M,01:56:11,04:59:17 +43,W,02:03:28,04:59:17 +42,M,02:01:24,04:59:17 +33,W,02:05:55,04:59:17 +42,M,02:07:22,04:59:18 +31,M,02:08:12,04:59:18 +21,M,02:23:57,04:59:18 +36,W,02:05:50,04:59:18 +19,M,01:49:54,04:59:19 +40,M,02:20:25,04:59:20 +58,M,02:04:20,04:59:20 +28,M,02:00:12,04:59:20 +39,M,02:03:01,04:59:20 +46,M,01:57:07,04:59:22 +43,M,02:06:33,04:59:22 +41,W,02:01:25,04:59:23 +55,W,02:10:24,04:59:23 +54,W,02:09:32,04:59:23 +32,W,02:14:45,04:59:23 +29,M,02:01:11,04:59:24 +42,W,02:01:16,04:59:24 +30,M,02:16:46,04:59:24 +38,M,02:02:01,04:59:24 +43,W,02:07:02,04:59:25 +49,M,02:02:04,04:59:25 +41,M,02:09:50,04:59:25 +36,W,02:05:21,04:59:25 +58,W,02:13:13,04:59:26 +53,W,02:02:33,04:59:26 +56,M,02:17:06,04:59:27 +34,W,02:06:46,04:59:27 +33,M,02:04:01,04:59:28 +52,M,02:08:06,04:59:28 +51,M,02:08:45,04:59:28 +53,M,02:03:32,04:59:28 +29,W,02:08:45,04:59:28 +41,M,02:01:56,04:59:28 +32,W,02:06:35,04:59:28 +33,W,02:18:58,04:59:29 +35,W,02:10:23,04:59:29 +20,M,02:04:24,04:59:30 +45,M,02:03:26,04:59:30 +46,M,02:01:56,04:59:30 +38,M,01:55:42,04:59:30 +23,M,02:04:26,04:59:31 +64,W,02:00:07,04:59:31 +59,M,02:17:06,04:59:32 +59,M,01:56:10,04:59:32 +49,M,02:03:59,04:59:32 +26,M,01:46:38,04:59:32 +51,M,02:12:44,04:59:33 +41,W,02:12:29,04:59:33 +60,M,01:58:37,04:59:34 +36,M,02:09:57,04:59:34 +25,W,02:07:13,04:59:34 +28,M,02:19:53,04:59:34 +27,W,02:01:03,04:59:34 +21,M,01:53:13,04:59:35 +51,M,02:03:44,04:59:35 +50,W,02:10:08,04:59:35 +38,W,02:02:28,04:59:35 +37,M,02:07:12,04:59:36 +58,M,02:11:02,04:59:36 +33,W,02:02:34,04:59:36 +44,W,02:11:21,04:59:36 +48,W,02:02:36,04:59:36 +45,M,02:22:10,04:59:36 +41,M,02:10:42,04:59:37 +34,W,02:06:18,04:59:37 +52,M,02:04:50,04:59:37 +35,M,02:07:45,04:59:37 +41,M,02:15:57,04:59:37 +28,M,02:01:13,04:59:38 +28,W,02:17:14,04:59:38 +46,M,02:04:01,04:59:38 +43,W,02:13:29,04:59:38 +41,M,02:07:12,04:59:38 +27,W,02:11:13,04:59:39 +50,M,02:02:53,04:59:39 +45,W,02:02:16,04:59:40 +30,M,02:09:07,04:59:40 +51,M,01:56:47,04:59:41 +29,W,02:06:52,04:59:41 +26,W,02:04:51,04:59:41 +48,W,02:09:26,04:59:41 +28,W,02:09:13,04:59:42 +58,M,02:12:16,04:59:42 +37,W,02:04:44,04:59:42 +49,M,02:09:09,04:59:43 +42,M,01:52:57,04:59:43 +34,W,02:05:33,04:59:43 +39,W,02:11:09,04:59:43 +57,M,02:10:55,04:59:43 +54,M,01:57:56,04:59:43 +39,M,01:54:03,04:59:44 +44,M,02:02:12,04:59:44 +50,M,02:10:32,04:59:45 +44,M,02:07:13,04:59:45 +29,M,02:05:25,04:59:45 +34,M,02:11:03,04:59:45 +37,W,02:03:14,04:59:45 +27,M,01:57:55,04:59:45 +37,W,02:13:04,04:59:46 +40,M,02:12:26,04:59:46 +42,M,02:09:30,04:59:46 +41,M,02:00:19,04:59:46 +21,M,02:06:40,04:59:47 +54,W,02:00:15,04:59:47 +45,W,02:02:20,04:59:47 +43,M,02:13:14,04:59:48 +49,W,02:08:53,04:59:48 +39,W,02:07:09,04:59:48 +37,M,02:08:37,04:59:48 +55,M,02:21:55,04:59:49 +54,M,02:09:29,04:59:49 +33,W,02:08:20,04:59:49 +31,W,02:06:47,04:59:49 +42,W,02:08:21,04:59:50 +45,M,02:02:10,04:59:50 +47,M,02:01:21,04:59:50 +52,M,02:13:00,04:59:51 +33,M,02:07:17,04:59:51 +44,M,02:13:23,04:59:51 +43,M,02:07:57,04:59:51 +41,M,02:08:26,04:59:51 +39,M,02:04:05,04:59:52 +28,M,01:57:05,04:59:52 +56,W,02:09:47,04:59:52 +41,M,02:18:01,04:59:52 +63,M,02:20:51,04:59:52 +34,M,02:04:07,04:59:53 +38,M,02:12:55,04:59:53 +33,W,01:59:34,04:59:53 +47,M,02:09:03,04:59:53 +24,M,02:01:58,04:59:54 +31,M,02:09:01,04:59:54 +29,M,02:02:55,04:59:54 +38,M,01:57:27,04:59:55 +45,M,02:04:17,04:59:55 +48,M,01:59:23,04:59:56 +30,M,02:11:23,04:59:57 +40,W,02:09:09,04:59:57 +49,M,02:06:41,04:59:57 +48,W,02:16:06,04:59:57 +40,W,02:05:34,04:59:57 +35,W,02:04:12,04:59:58 +50,M,02:12:57,04:59:58 +28,W,02:02:43,04:59:58 +33,M,01:55:14,04:59:58 +28,W,02:11:26,04:59:58 +45,M,02:15:46,04:59:59 +32,M,02:08:55,04:59:59 +41,M,01:59:32,05:00:00 +54,W,02:28:03,05:00:01 +38,M,02:02:39,05:00:01 +28,W,02:02:26,05:00:01 +40,W,02:05:44,05:00:01 +35,M,02:10:32,05:00:01 +29,M,02:08:22,05:00:01 +52,M,02:09:09,05:00:02 +29,M,02:09:06,05:00:02 +30,M,02:10:44,05:00:02 +23,M,02:14:12,05:00:02 +33,W,02:00:04,05:00:03 +31,M,02:08:16,05:00:03 +42,M,02:00:04,05:00:04 +39,W,02:12:40,05:00:04 +68,M,02:09:29,05:00:04 +33,M,02:03:11,05:00:04 +50,W,02:04:07,05:00:04 +40,W,02:11:58,05:00:05 +52,M,02:09:12,05:00:05 +41,M,02:06:19,05:00:05 +38,M,02:14:44,05:00:06 +34,M,02:02:01,05:00:06 +45,W,02:04:51,05:00:06 +27,W,02:03:28,05:00:07 +28,M,02:09:49,05:00:07 +43,M,02:15:42,05:00:07 +44,M,02:03:27,05:00:08 +61,M,02:00:57,05:00:08 +26,M,02:13:38,05:00:08 +33,W,02:04:23,05:00:08 +54,M,02:01:52,05:00:08 +28,W,02:12:51,05:00:08 +25,W,02:06:18,05:00:08 +37,M,01:55:40,05:00:08 +40,W,01:59:34,05:00:09 +56,W,02:15:52,05:00:09 +40,M,02:17:47,05:00:09 +53,M,02:10:43,05:00:09 +43,W,02:05:41,05:00:09 +51,M,02:08:53,05:00:09 +37,M,02:12:54,05:00:09 +42,W,02:05:32,05:00:10 +28,M,02:23:54,05:00:10 +52,W,02:08:25,05:00:10 +22,M,02:07:40,05:00:10 +60,W,02:10:21,05:00:10 +26,M,02:16:10,05:00:10 +40,W,02:00:52,05:00:11 +30,W,01:59:47,05:00:11 +40,M,02:14:14,05:00:11 +44,M,02:08:41,05:00:11 +45,M,02:03:01,05:00:12 +43,M,02:03:34,05:00:12 +39,W,02:07:32,05:00:12 +33,M,02:02:16,05:00:13 +52,M,02:08:18,05:00:13 +57,M,02:06:16,05:00:13 +40,M,02:18:21,05:00:13 +26,M,02:09:25,05:00:13 +50,W,02:10:59,05:00:15 +33,M,02:19:22,05:00:15 +35,M,02:06:47,05:00:15 +40,W,02:07:14,05:00:15 +40,M,01:53:26,05:00:16 +51,M,02:10:01,05:00:16 +43,M,02:08:19,05:00:16 +24,W,02:13:21,05:00:16 +34,M,01:54:55,05:00:16 +46,W,02:07:13,05:00:17 +55,M,02:09:06,05:00:17 +46,M,02:10:14,05:00:17 +49,M,02:09:33,05:00:17 +52,W,02:08:35,05:00:17 +37,W,02:05:13,05:00:17 +38,W,02:09:10,05:00:17 +46,M,02:05:47,05:00:17 +63,M,02:10:24,05:00:18 +42,W,02:04:07,05:00:18 +39,M,02:09:43,05:00:18 +46,W,02:04:41,05:00:19 +33,W,02:04:31,05:00:19 +50,M,02:10:02,05:00:19 +57,M,02:15:42,05:00:20 +48,M,02:00:47,05:00:20 +56,M,02:07:17,05:00:20 +49,M,02:05:24,05:00:20 +50,M,02:05:45,05:00:20 +50,M,02:05:59,05:00:20 +33,M,01:56:01,05:00:20 +38,W,02:05:02,05:00:21 +44,W,02:06:40,05:00:22 +45,M,01:57:23,05:00:22 +51,M,02:05:36,05:00:23 +36,M,02:05:28,05:00:24 +34,M,02:04:57,05:00:24 +36,M,01:59:28,05:00:24 +39,M,02:07:29,05:00:25 +42,W,02:12:00,05:00:25 +42,M,02:04:06,05:00:25 +32,M,02:02:05,05:00:25 +25,W,02:13:49,05:00:25 +59,M,01:54:05,05:00:25 +45,M,01:58:21,05:00:25 +37,M,02:00:01,05:00:26 +51,W,02:01:28,05:00:26 +70,M,02:08:54,05:00:26 +33,M,01:59:30,05:00:26 +38,M,02:05:14,05:00:26 +45,M,01:59:08,05:00:27 +37,M,01:52:22,05:00:28 +48,M,02:09:12,05:00:28 +36,M,02:07:20,05:00:28 +27,M,02:17:26,05:00:28 +26,W,02:04:53,05:00:28 +51,M,02:07:34,05:00:29 +25,M,02:06:56,05:00:29 +42,M,02:11:22,05:00:29 +43,W,02:01:39,05:00:29 +49,M,02:17:23,05:00:29 +43,M,01:52:45,05:00:29 +31,M,01:59:19,05:00:30 +47,M,02:08:50,05:00:30 +24,M,02:06:13,05:00:30 +42,W,02:07:02,05:00:30 +37,M,01:57:01,05:00:31 +36,W,02:07:59,05:00:31 +36,M,02:08:01,05:00:31 +46,M,02:05:43,05:00:32 +41,M,02:08:49,05:00:32 +51,M,02:06:49,05:00:32 +54,M,02:08:14,05:00:32 +41,W,02:01:43,05:00:32 +52,M,02:21:57,05:00:32 +39,M,01:50:41,05:00:32 +44,W,02:03:18,05:00:33 +51,M,02:05:08,05:00:33 +61,M,02:14:50,05:00:33 +49,M,02:03:41,05:00:33 +32,W,02:15:34,05:00:33 +41,M,02:03:07,05:00:34 +56,M,01:59:47,05:00:34 +75,M,02:05:13,05:00:34 +38,M,02:13:17,05:00:34 +37,M,02:00:10,05:00:34 +51,M,02:03:15,05:00:34 +25,M,02:00:27,05:00:34 +35,M,02:05:32,05:00:35 +49,M,02:10:12,05:00:35 +45,M,02:10:30,05:00:35 +49,M,02:04:20,05:00:36 +28,W,02:00:40,05:00:36 +29,M,02:16:02,05:00:36 +39,M,02:03:50,05:00:36 +50,M,01:59:41,05:00:36 +41,M,02:13:44,05:00:36 +60,W,02:04:03,05:00:37 +36,M,02:01:35,05:00:37 +38,W,01:59:37,05:00:37 +50,M,02:08:29,05:00:38 +30,M,02:03:40,05:00:38 +43,M,02:17:39,05:00:38 +30,W,02:14:46,05:00:38 +33,M,01:58:05,05:00:38 +36,W,02:03:35,05:00:38 +27,W,02:04:30,05:00:39 +43,M,02:02:00,05:00:40 +35,W,02:06:30,05:00:40 +45,M,02:01:28,05:00:40 +32,W,02:15:14,05:00:40 +60,M,02:10:48,05:00:40 +43,W,02:07:38,05:00:40 +40,W,02:13:09,05:00:41 +40,M,02:02:16,05:00:41 +32,M,02:02:36,05:00:41 +40,W,02:03:57,05:00:41 +41,M,01:59:01,05:00:42 +43,M,02:09:00,05:00:42 +45,W,02:02:28,05:00:43 +34,M,01:59:01,05:00:43 +49,W,02:15:30,05:00:43 +34,M,02:00:10,05:00:43 +55,M,02:07:03,05:00:43 +62,M,02:08:52,05:00:44 +39,W,02:03:52,05:00:44 +44,W,02:03:37,05:00:44 +63,W,02:03:39,05:00:45 +45,W,02:14:01,05:00:45 +30,W,02:09:55,05:00:45 +56,W,02:04:41,05:00:47 +58,M,02:04:29,05:00:47 +27,M,02:08:17,05:00:48 +49,M,02:13:36,05:00:48 +34,W,02:05:58,05:00:50 +29,M,02:01:57,05:00:50 +48,M,02:04:37,05:00:50 +48,M,02:12:02,05:00:51 +49,W,02:10:06,05:00:51 +60,M,02:15:52,05:00:51 +34,M,02:09:13,05:00:51 +28,W,02:08:08,05:00:52 +49,M,01:58:17,05:00:52 +45,M,02:05:08,05:00:53 +50,M,02:08:10,05:00:53 +47,M,02:03:48,05:00:53 +59,M,02:07:31,05:00:53 +42,M,02:08:09,05:00:54 +51,W,02:06:04,05:00:54 +25,M,02:11:23,05:00:54 +20,W,02:11:36,05:00:54 +43,M,02:00:08,05:00:54 +65,M,02:15:26,05:00:54 +29,W,02:09:43,05:00:54 +60,M,02:13:50,05:00:55 +28,M,02:12:50,05:00:55 +39,M,01:55:25,05:00:55 +54,M,02:03:59,05:00:55 +28,W,02:21:08,05:00:55 +28,M,02:12:37,05:00:56 +37,W,02:08:36,05:00:56 +49,M,02:10:03,05:00:57 +25,M,02:01:55,05:00:57 +54,M,02:17:29,05:00:58 +33,M,02:13:23,05:00:58 +55,M,02:04:03,05:00:58 +36,M,01:56:55,05:00:59 +27,W,02:03:51,05:00:59 +39,W,02:04:31,05:00:59 +63,M,02:02:24,05:00:59 +49,M,02:13:13,05:01:00 +42,M,02:15:27,05:01:00 +39,W,02:12:54,05:01:00 +36,M,02:14:52,05:01:00 +41,M,02:02:14,05:01:01 +38,M,02:02:20,05:01:01 +42,M,02:14:26,05:01:01 +29,W,02:06:31,05:01:01 +25,W,02:05:40,05:01:01 +42,M,01:56:22,05:01:01 +70,M,02:12:35,05:01:02 +55,M,02:00:02,05:01:02 +43,M,02:00:17,05:01:02 +33,M,02:03:06,05:01:02 +47,M,02:01:59,05:01:02 +62,W,02:04:16,05:01:03 +36,M,02:08:30,05:01:03 +54,M,02:04:15,05:01:03 +28,M,02:09:12,05:01:03 +51,M,02:10:36,05:01:03 +41,M,02:09:00,05:01:03 +40,M,02:11:26,05:01:04 +49,W,01:59:29,05:01:04 +40,M,02:07:23,05:01:04 +59,M,02:06:53,05:01:04 +26,W,02:12:50,05:01:04 +28,W,02:03:30,05:01:05 +55,M,02:06:20,05:01:05 +28,W,02:03:13,05:01:05 +38,M,02:03:16,05:01:05 +55,M,02:13:56,05:01:07 +40,W,02:05:31,05:01:07 +33,W,01:59:01,05:01:07 +39,W,02:14:36,05:01:07 +45,W,02:08:34,05:01:08 +39,M,02:02:40,05:01:08 +46,M,02:03:48,05:01:08 +51,W,02:09:13,05:01:08 +49,M,02:00:50,05:01:08 +38,W,02:08:49,05:01:08 +42,M,01:59:42,05:01:09 +37,W,02:01:45,05:01:09 +49,W,02:12:04,05:01:09 +40,W,02:02:44,05:01:09 +34,M,02:07:32,05:01:09 +60,M,02:06:18,05:01:10 +33,M,02:16:29,05:01:10 +41,M,02:14:11,05:01:10 +39,W,02:04:31,05:01:10 +34,M,02:07:25,05:01:10 +31,W,02:05:31,05:01:11 +42,M,02:00:58,05:01:11 +38,M,02:08:50,05:01:11 +50,M,01:55:53,05:01:11 +44,M,02:11:27,05:01:12 +41,M,02:07:10,05:01:12 +41,M,02:08:25,05:01:12 +28,M,02:12:30,05:01:13 +31,M,02:03:17,05:01:13 +39,M,02:05:07,05:01:14 +38,M,01:59:01,05:01:14 +41,M,02:10:29,05:01:14 +39,M,02:07:53,05:01:14 +36,M,02:07:36,05:01:15 +65,M,02:06:52,05:01:15 +32,M,02:09:42,05:01:15 +49,M,02:09:54,05:01:16 +45,M,02:01:56,05:01:16 +26,W,02:05:53,05:01:17 +52,M,01:55:35,05:01:17 +40,M,02:02:20,05:01:17 +31,M,02:00:25,05:01:18 +50,W,02:16:18,05:01:18 +37,M,02:00:49,05:01:18 +47,M,02:04:26,05:01:18 +43,M,02:00:59,05:01:19 +27,W,02:06:20,05:01:19 +25,W,02:07:36,05:01:19 +38,W,02:03:41,05:01:19 +43,W,02:10:45,05:01:19 +33,W,02:14:46,05:01:20 +58,M,02:06:02,05:01:20 +49,W,02:08:52,05:01:20 +27,W,01:59:04,05:01:20 +34,M,02:20:30,05:01:20 +32,W,02:03:49,05:01:21 +38,M,02:09:57,05:01:21 +28,W,01:56:33,05:01:22 +36,W,02:11:07,05:01:22 +39,M,02:03:07,05:01:22 +30,W,02:00:19,05:01:22 +49,W,02:12:58,05:01:22 +40,M,02:13:46,05:01:22 +51,M,02:07:56,05:01:22 +61,M,02:14:47,05:01:23 +35,M,02:08:14,05:01:23 +46,M,02:07:07,05:01:23 +37,M,02:14:17,05:01:23 +31,M,02:00:07,05:01:23 +33,W,02:13:06,05:01:24 +39,M,02:07:29,05:01:25 +39,M,02:01:03,05:01:25 +62,M,02:10:38,05:01:25 +42,M,02:14:20,05:01:25 +55,M,01:55:35,05:01:25 +61,M,02:03:39,05:01:25 +52,M,02:06:18,05:01:25 +50,W,01:58:12,05:01:26 +30,W,02:07:08,05:01:26 +41,M,02:00:29,05:01:26 +41,W,02:12:09,05:01:26 +53,W,02:02:09,05:01:27 +31,M,02:03:25,05:01:27 +35,W,01:59:16,05:01:28 +28,M,02:12:43,05:01:28 +43,M,02:01:46,05:01:28 +36,M,01:54:39,05:01:28 +51,M,02:01:12,05:01:29 +43,M,02:02:58,05:01:29 +42,W,02:09:30,05:01:29 +41,W,02:14:46,05:01:29 +30,M,02:05:36,05:01:29 +28,W,02:18:27,05:01:30 +38,W,02:13:43,05:01:30 +37,W,02:09:58,05:01:32 +40,M,02:03:38,05:01:32 +47,W,02:08:26,05:01:32 +48,M,01:58:42,05:01:32 +38,M,02:08:36,05:01:32 +57,W,02:09:49,05:01:33 +31,W,02:00:23,05:01:33 +42,M,01:59:47,05:01:33 +45,M,02:07:11,05:01:34 +50,M,02:09:26,05:01:34 +53,M,02:19:43,05:01:34 +52,M,02:07:05,05:01:34 +57,M,02:04:45,05:01:34 +43,M,02:11:45,05:01:35 +40,W,02:11:36,05:01:35 +42,W,01:58:08,05:01:35 +22,M,02:08:57,05:01:35 +27,W,02:27:29,05:01:35 +33,W,02:09:06,05:01:36 +50,W,02:13:41,05:01:36 +30,W,02:04:35,05:01:36 +45,M,02:07:01,05:01:36 +38,M,02:01:43,05:01:37 +55,M,02:06:02,05:01:37 +39,M,02:09:33,05:01:38 +37,M,02:02:39,05:01:38 +40,M,02:08:28,05:01:38 +25,M,02:04:10,05:01:38 +41,M,02:00:59,05:01:38 +42,M,02:01:27,05:01:38 +41,W,02:18:25,05:01:39 +58,M,02:15:24,05:01:39 +34,M,01:57:43,05:01:40 +57,M,02:00:15,05:01:40 +38,W,02:11:40,05:01:40 +35,W,02:22:43,05:01:40 +31,W,02:07:13,05:01:40 +37,W,02:03:37,05:01:40 +43,M,01:58:51,05:01:40 +41,W,01:59:55,05:01:41 +39,W,02:08:48,05:01:41 +37,M,02:11:51,05:01:41 +35,W,02:09:22,05:01:41 +45,W,02:03:57,05:01:41 +38,W,02:18:13,05:01:42 +34,M,02:10:06,05:01:42 +35,M,02:05:43,05:01:44 +27,M,02:10:36,05:01:44 +30,M,01:55:29,05:01:44 +38,M,02:09:12,05:01:45 +23,M,01:57:34,05:01:46 +49,W,02:15:05,05:01:46 +49,W,02:05:29,05:01:46 +24,M,02:07:58,05:01:46 +50,W,02:05:20,05:01:47 +36,M,02:01:39,05:01:47 +39,M,02:06:28,05:01:47 +31,M,01:56:24,05:01:47 +34,M,02:17:40,05:01:48 +67,M,02:15:49,05:01:48 +37,M,02:02:47,05:01:48 +41,W,02:13:22,05:01:49 +36,M,02:01:26,05:01:49 +28,W,02:02:37,05:01:49 +47,M,01:59:07,05:01:49 +55,M,02:10:11,05:01:49 +25,M,01:57:20,05:01:49 +65,M,02:02:09,05:01:50 +50,M,02:11:29,05:01:50 +65,M,02:09:23,05:01:50 +38,W,02:14:44,05:01:50 +43,W,02:04:39,05:01:50 +54,W,02:11:45,05:01:50 +28,W,02:15:08,05:01:50 +47,W,02:02:01,05:01:50 +41,M,02:02:10,05:01:51 +39,M,02:09:36,05:01:51 +28,M,02:04:33,05:01:51 +27,W,02:09:43,05:01:52 +49,M,02:12:25,05:01:52 +49,M,02:19:05,05:01:52 +34,W,01:59:40,05:01:52 +56,M,02:07:40,05:01:52 +35,M,02:01:10,05:01:52 +44,W,02:18:45,05:01:52 +41,W,02:19:06,05:01:53 +32,W,02:07:02,05:01:55 +44,M,02:04:26,05:01:55 +37,W,02:18:56,05:01:55 +27,M,02:05:20,05:01:55 +27,W,02:03:15,05:01:56 +50,M,02:07:31,05:01:56 +39,M,02:11:23,05:01:56 +39,M,02:02:57,05:01:56 +30,W,02:19:46,05:01:57 +46,M,02:05:23,05:01:57 +33,M,02:03:24,05:01:57 +59,M,01:59:59,05:01:58 +38,M,02:18:43,05:01:58 +55,M,01:56:16,05:01:58 +33,W,02:04:29,05:01:58 +46,M,02:03:54,05:01:58 +35,M,01:54:19,05:01:58 +41,M,02:08:16,05:01:59 +30,M,02:04:26,05:01:59 +50,M,02:06:30,05:01:59 +41,M,02:17:38,05:01:59 +30,M,02:03:54,05:02:00 +43,M,02:06:41,05:02:01 +45,M,01:59:34,05:02:01 +41,M,02:16:09,05:02:01 +52,M,02:16:23,05:02:01 +39,W,02:24:35,05:02:01 +49,M,02:06:12,05:02:01 +50,M,02:15:56,05:02:01 +52,M,02:07:15,05:02:01 +41,M,02:00:30,05:02:02 +43,W,02:08:29,05:02:02 +46,M,02:15:20,05:02:03 +24,W,02:07:43,05:02:03 +36,M,02:10:18,05:02:03 +30,M,01:56:22,05:02:04 +39,M,02:07:21,05:02:04 +33,W,02:17:32,05:02:04 +48,M,01:53:09,05:02:05 +35,W,02:13:49,05:02:05 +30,W,02:15:31,05:02:05 +32,W,02:16:04,05:02:05 +31,M,02:01:35,05:02:05 +38,M,02:06:19,05:02:05 +37,M,02:03:07,05:02:05 +37,M,02:03:50,05:02:05 +44,M,02:01:02,05:02:06 +24,M,02:02:55,05:02:06 +44,W,02:11:41,05:02:06 +46,M,01:52:55,05:02:06 +37,W,02:04:12,05:02:07 +55,M,02:22:03,05:02:07 +47,W,02:14:55,05:02:07 +57,M,02:04:23,05:02:07 +44,W,02:15:06,05:02:07 +28,M,02:03:08,05:02:07 +36,M,01:55:06,05:02:07 +42,M,02:09:48,05:02:07 +26,W,02:16:40,05:02:08 +42,M,01:59:31,05:02:08 +33,M,01:59:01,05:02:08 +48,M,02:11:14,05:02:09 +45,M,02:05:42,05:02:09 +56,M,02:15:14,05:02:09 +41,M,02:07:55,05:02:09 +50,W,02:07:13,05:02:10 +43,M,02:00:38,05:02:10 +27,W,02:13:09,05:02:11 +31,W,02:15:32,05:02:11 +43,W,02:09:08,05:02:11 +37,M,02:18:48,05:02:11 +29,W,02:04:22,05:02:11 +46,M,01:55:09,05:02:11 +32,W,02:09:22,05:02:12 +46,M,02:16:09,05:02:12 +37,M,02:10:30,05:02:13 +52,M,02:11:08,05:02:13 +25,W,02:13:34,05:02:13 +35,M,02:05:41,05:02:13 +29,W,02:01:41,05:02:13 +23,M,01:54:39,05:02:14 +44,M,02:02:41,05:02:14 +38,M,02:07:18,05:02:16 +29,M,02:01:33,05:02:16 +44,M,02:04:41,05:02:16 +32,M,02:03:44,05:02:16 +46,W,02:11:08,05:02:17 +27,M,01:58:58,05:02:17 +28,M,02:10:59,05:02:17 +53,M,02:02:39,05:02:18 +30,M,01:59:40,05:02:18 +35,M,02:05:58,05:02:18 +34,M,02:06:09,05:02:19 +24,M,02:14:57,05:02:19 +32,W,02:03:14,05:02:19 +38,M,02:06:15,05:02:20 +37,W,02:09:59,05:02:20 +50,W,02:04:10,05:02:20 +31,M,02:10:17,05:02:20 +58,M,02:03:17,05:02:21 +43,M,02:17:21,05:02:21 +35,M,02:12:34,05:02:21 +35,M,02:09:09,05:02:21 +58,W,02:13:44,05:02:21 +42,M,02:07:06,05:02:21 +27,W,02:05:29,05:02:21 +33,M,02:16:32,05:02:22 +51,M,02:07:15,05:02:22 +42,M,02:04:03,05:02:22 +43,M,02:07:11,05:02:22 +58,M,02:03:17,05:02:22 +51,W,02:03:34,05:02:23 +56,W,02:05:18,05:02:23 +48,M,02:04:21,05:02:23 +41,M,02:09:27,05:02:23 +28,W,02:05:11,05:02:23 +44,W,02:11:33,05:02:23 +30,W,02:04:45,05:02:23 +45,M,01:56:32,05:02:23 +54,M,02:01:41,05:02:23 +27,W,02:07:41,05:02:23 +51,M,02:08:13,05:02:24 +25,W,02:05:44,05:02:25 +33,M,02:04:40,05:02:25 +26,W,02:05:11,05:02:25 +45,M,02:05:38,05:02:25 +55,M,02:13:56,05:02:25 +46,W,02:26:09,05:02:25 +58,M,02:10:37,05:02:26 +62,M,02:04:43,05:02:26 +43,M,02:01:09,05:02:26 +48,M,02:07:26,05:02:26 +40,M,02:13:10,05:02:27 +57,M,02:14:47,05:02:28 +27,W,02:01:18,05:02:28 +34,M,02:05:22,05:02:28 +46,M,02:21:55,05:02:28 +32,W,02:05:41,05:02:28 +32,M,02:03:29,05:02:28 +44,M,02:08:40,05:02:28 +49,W,02:15:41,05:02:29 +45,W,02:08:10,05:02:29 +45,W,02:13:01,05:02:29 +31,M,02:28:42,05:02:29 +35,M,02:07:00,05:02:29 +41,W,02:09:21,05:02:29 +29,M,02:06:02,05:02:29 +36,M,02:00:36,05:02:30 +39,W,02:06:05,05:02:31 +61,M,02:08:51,05:02:31 +51,M,02:05:44,05:02:32 +49,M,02:07:18,05:02:32 +48,M,02:07:52,05:02:32 +59,M,02:02:15,05:02:32 +29,M,01:58:46,05:02:32 +46,W,02:08:12,05:02:33 +21,M,01:56:11,05:02:33 +47,W,02:08:57,05:02:33 +33,M,02:11:01,05:02:34 +40,M,01:56:40,05:02:34 +33,M,02:14:07,05:02:34 +52,M,02:09:15,05:02:34 +46,M,02:06:52,05:02:34 +45,M,02:11:31,05:02:35 +31,M,02:14:56,05:02:35 +32,W,02:01:39,05:02:35 +43,M,02:04:49,05:02:35 +34,M,02:03:51,05:02:35 +40,M,01:58:00,05:02:36 +51,M,02:05:30,05:02:36 +55,M,01:56:46,05:02:36 +19,M,02:07:45,05:02:36 +31,W,01:57:50,05:02:36 +29,M,02:13:12,05:02:37 +41,M,02:08:06,05:02:37 +38,W,02:09:30,05:02:37 +41,W,02:04:02,05:02:38 +63,M,02:11:50,05:02:38 +62,M,02:16:35,05:02:38 +62,M,01:51:46,05:02:38 +21,W,01:57:12,05:02:38 +65,M,02:04:45,05:02:38 +32,W,02:14:39,05:02:38 +36,M,02:02:53,05:02:38 +36,W,02:10:50,05:02:39 +28,W,02:01:57,05:02:39 +34,M,02:06:48,05:02:39 +35,M,01:57:50,05:02:40 +42,M,02:10:32,05:02:41 +43,M,02:10:42,05:02:42 +37,M,02:12:08,05:02:42 +38,M,02:00:59,05:02:42 +45,M,02:06:37,05:02:42 +39,M,02:01:15,05:02:42 +54,W,02:03:09,05:02:43 +48,M,02:02:56,05:02:43 +57,M,02:17:05,05:02:43 +33,W,02:10:15,05:02:44 +63,W,02:04:09,05:02:44 +33,W,02:06:36,05:02:44 +25,W,02:19:08,05:02:44 +31,W,02:09:31,05:02:44 +36,M,01:56:02,05:02:45 +40,M,02:07:43,05:02:45 +53,W,02:01:10,05:02:45 +26,M,02:08:34,05:02:45 +40,M,02:10:43,05:02:46 +44,M,02:10:45,05:02:46 +22,W,02:05:54,05:02:46 +50,W,02:01:38,05:02:46 +39,W,02:18:52,05:02:46 +44,M,02:08:33,05:02:47 +63,W,02:04:14,05:02:47 +45,W,02:09:45,05:02:47 +39,W,02:06:33,05:02:47 +42,W,02:10:03,05:02:47 +41,M,01:57:38,05:02:47 +53,M,02:03:32,05:02:48 +37,M,02:00:47,05:02:48 +37,W,02:09:51,05:02:48 +44,M,02:09:49,05:02:48 +32,M,02:07:09,05:02:48 +40,W,02:09:47,05:02:48 +41,M,02:06:37,05:02:49 +33,M,01:59:36,05:02:49 +45,M,02:06:10,05:02:49 +28,W,02:06:59,05:02:50 +47,W,02:10:55,05:02:50 +56,M,02:07:05,05:02:51 +22,W,02:08:01,05:02:51 +30,M,02:10:30,05:02:51 +29,W,02:13:22,05:02:51 +55,M,02:05:18,05:02:51 +44,W,02:12:33,05:02:51 +34,W,02:02:25,05:02:52 +50,M,02:07:35,05:02:53 +47,M,02:12:40,05:02:53 +30,W,02:09:24,05:02:53 +41,M,02:14:51,05:02:53 +43,W,02:09:49,05:02:54 +41,W,02:12:13,05:02:54 +52,M,02:07:31,05:02:54 +43,M,02:01:02,05:02:54 +40,M,02:04:39,05:02:54 +24,W,02:09:14,05:02:55 +38,M,02:07:21,05:02:55 +35,M,02:11:25,05:02:55 +31,W,02:09:33,05:02:55 +32,M,02:20:10,05:02:55 +49,W,02:05:10,05:02:56 +41,M,02:07:04,05:02:56 +28,W,02:04:38,05:02:56 +63,M,02:11:25,05:02:56 +52,M,02:05:45,05:02:56 +42,W,02:10:58,05:02:57 +29,W,02:10:39,05:02:58 +38,W,02:04:20,05:02:58 +62,W,02:19:28,05:02:58 +25,W,01:58:24,05:02:58 +26,W,02:06:14,05:02:59 +46,M,02:11:33,05:02:59 +51,W,02:07:06,05:02:59 +43,W,02:09:30,05:02:59 +47,M,02:00:55,05:02:59 +40,M,02:12:25,05:02:59 +39,M,02:12:14,05:02:59 +35,W,02:06:22,05:03:00 +57,M,02:15:30,05:03:00 +51,M,01:56:53,05:03:00 +63,M,02:04:52,05:03:01 +40,M,02:07:56,05:03:01 +41,M,02:12:45,05:03:01 +34,M,02:16:19,05:03:02 +40,M,02:07:24,05:03:02 +26,W,02:06:25,05:03:02 +19,M,02:08:55,05:03:02 +33,M,01:59:34,05:03:02 +42,M,02:16:56,05:03:03 +30,M,01:57:53,05:03:03 +39,M,02:10:37,05:03:03 +43,W,02:06:22,05:03:03 +38,W,02:08:11,05:03:03 +25,W,02:06:52,05:03:04 +34,M,02:07:33,05:03:04 +42,W,02:12:17,05:03:05 +35,W,02:05:51,05:03:05 +46,M,02:06:38,05:03:06 +35,M,02:07:42,05:03:06 +43,W,02:07:42,05:03:06 +27,W,02:05:28,05:03:07 +41,M,02:04:41,05:03:07 +30,W,02:07:11,05:03:07 +33,W,02:12:55,05:03:08 +48,M,02:10:33,05:03:09 +30,W,02:16:34,05:03:09 +37,M,02:23:01,05:03:09 +49,M,02:08:32,05:03:10 +43,M,02:06:38,05:03:10 +53,M,01:57:29,05:03:10 +42,W,02:11:08,05:03:10 +25,W,02:05:17,05:03:10 +36,W,02:03:25,05:03:10 +55,W,02:06:43,05:03:10 +46,M,02:03:52,05:03:11 +58,M,02:11:55,05:03:11 +46,W,02:17:15,05:03:11 +27,M,02:14:18,05:03:11 +43,W,02:04:43,05:03:11 +39,W,02:04:18,05:03:12 +44,M,01:56:46,05:03:12 +51,M,02:04:44,05:03:12 +37,M,02:05:37,05:03:13 +48,M,02:09:12,05:03:13 +72,M,02:18:22,05:03:13 +50,M,02:16:42,05:03:13 +32,M,02:03:58,05:03:14 +36,M,02:10:40,05:03:14 +40,M,01:57:07,05:03:14 +55,M,02:00:20,05:03:14 +47,W,02:09:58,05:03:14 +56,M,02:03:16,05:03:14 +40,W,02:09:35,05:03:15 +64,M,02:08:07,05:03:16 +48,M,02:08:58,05:03:16 +30,M,01:57:51,05:03:16 +23,W,02:08:54,05:03:16 +42,M,02:14:15,05:03:16 +31,W,02:11:05,05:03:17 +54,W,02:13:00,05:03:17 +41,W,02:15:57,05:03:17 +25,W,02:08:08,05:03:17 +28,W,02:04:27,05:03:17 +49,M,01:57:24,05:03:17 +34,W,02:13:46,05:03:17 +29,W,02:03:31,05:03:17 +45,W,02:08:25,05:03:18 +42,M,02:03:07,05:03:18 +35,M,02:11:58,05:03:18 +28,W,02:09:02,05:03:18 +25,M,02:11:18,05:03:18 +52,M,02:08:57,05:03:19 +55,M,02:14:43,05:03:19 +38,M,02:12:56,05:03:20 +22,M,02:08:46,05:03:20 +33,M,02:13:28,05:03:20 +27,W,02:12:10,05:03:21 +42,W,02:16:26,05:03:21 +58,M,02:05:25,05:03:21 +57,W,02:05:41,05:03:21 +33,M,02:11:33,05:03:21 +35,M,02:03:53,05:03:21 +42,M,02:16:13,05:03:22 +23,M,02:09:41,05:03:22 +60,M,02:20:14,05:03:22 +40,M,02:04:31,05:03:22 +26,W,02:10:49,05:03:23 +51,M,02:07:22,05:03:23 +31,W,02:15:53,05:03:24 +33,M,02:13:35,05:03:24 +39,M,02:09:54,05:03:24 +26,W,02:05:02,05:03:24 +37,M,02:00:47,05:03:24 +31,W,02:06:32,05:03:24 +57,M,02:03:28,05:03:24 +50,M,02:01:27,05:03:25 +41,W,02:06:00,05:03:25 +46,W,02:06:14,05:03:25 +29,M,02:17:00,05:03:26 +29,W,02:10:18,05:03:26 +32,W,02:12:32,05:03:26 +52,M,02:18:12,05:03:27 +39,W,02:01:40,05:03:27 +29,W,02:04:44,05:03:27 +35,W,02:06:04,05:03:28 +50,M,02:07:45,05:03:28 +38,W,02:07:24,05:03:28 +26,W,02:02:30,05:03:28 +49,M,02:00:55,05:03:29 +48,M,02:05:33,05:03:30 +55,W,02:11:07,05:03:30 +39,W,02:13:48,05:03:30 +55,W,02:14:57,05:03:31 +48,M,02:08:11,05:03:31 +36,M,02:05:02,05:03:31 +38,M,02:10:21,05:03:31 +45,W,02:05:23,05:03:32 +23,M,02:00:45,05:03:32 +41,W,02:06:09,05:03:32 +46,M,02:00:34,05:03:33 +42,M,02:25:10,05:03:33 +32,W,02:08:11,05:03:33 +46,M,02:03:05,05:03:33 +53,M,02:07:21,05:03:33 +40,M,02:09:40,05:03:34 +52,M,02:04:03,05:03:34 +39,M,01:58:56,05:03:34 +43,M,02:08:43,05:03:34 +41,M,02:06:33,05:03:34 +44,W,02:07:06,05:03:34 +24,W,02:06:31,05:03:35 +25,M,02:08:52,05:03:35 +35,W,02:03:38,05:03:35 +30,W,02:10:52,05:03:35 +23,M,02:11:53,05:03:36 +30,W,02:04:15,05:03:37 +30,W,02:09:59,05:03:37 +42,M,02:04:37,05:03:37 +61,M,01:58:40,05:03:37 +32,W,02:06:52,05:03:38 +49,M,02:03:52,05:03:39 +45,W,02:09:15,05:03:39 +27,W,02:11:23,05:03:39 +48,M,02:09:50,05:03:40 +45,M,01:50:58,05:03:40 +41,W,02:04:54,05:03:41 +36,M,02:06:32,05:03:41 +36,M,01:56:43,05:03:41 +20,M,02:10:06,05:03:41 +61,M,02:06:23,05:03:41 +33,M,02:07:33,05:03:42 +43,M,02:12:49,05:03:42 +37,M,02:05:22,05:03:42 +26,W,02:07:36,05:03:43 +35,M,02:05:59,05:03:43 +45,M,02:04:06,05:03:43 +28,M,01:59:11,05:03:43 +52,M,02:07:02,05:03:43 +40,M,02:18:02,05:03:43 +49,M,02:00:03,05:03:44 +63,M,02:00:27,05:03:44 +26,M,02:09:14,05:03:44 +27,M,01:57:18,05:03:45 +50,M,02:05:36,05:03:45 +44,M,02:08:11,05:03:45 +41,W,02:13:20,05:03:45 +51,M,02:02:33,05:03:45 +46,M,01:58:22,05:03:46 +54,M,02:07:57,05:03:46 +46,M,02:13:06,05:03:46 +54,M,02:00:57,05:03:47 +56,M,02:03:07,05:03:47 +55,M,02:08:22,05:03:47 +35,W,02:07:21,05:03:47 +32,M,02:14:16,05:03:47 +26,W,02:06:15,05:03:48 +64,M,02:08:10,05:03:48 +60,W,02:03:17,05:03:48 +50,M,02:10:05,05:03:48 +25,W,02:09:31,05:03:48 +40,M,02:05:40,05:03:49 +45,M,02:09:37,05:03:49 +49,M,02:12:23,05:03:50 +41,W,02:10:56,05:03:50 +42,M,02:14:20,05:03:51 +63,M,01:56:52,05:03:51 +38,W,02:11:47,05:03:51 +60,M,01:59:34,05:03:52 +31,M,02:03:14,05:03:52 +49,W,02:18:57,05:03:53 +41,M,02:01:49,05:03:53 +31,M,02:01:09,05:03:53 +30,M,02:14:37,05:03:53 +60,M,02:06:44,05:03:53 +38,M,02:05:00,05:03:54 +46,M,02:13:16,05:03:54 +29,W,02:03:59,05:03:54 +42,M,02:14:49,05:03:54 +24,M,02:05:21,05:03:54 +44,M,02:28:07,05:03:55 +39,W,02:09:16,05:03:55 +35,M,01:57:51,05:03:56 +28,W,02:01:20,05:03:56 +30,W,02:13:47,05:03:56 +47,M,02:06:24,05:03:56 +32,W,02:06:39,05:03:57 +42,W,02:09:18,05:03:57 +60,M,02:19:31,05:03:57 +54,W,02:15:04,05:03:58 +41,M,02:04:52,05:03:59 +41,W,02:23:55,05:03:59 +43,M,02:14:00,05:03:59 +43,M,02:06:38,05:03:59 +48,W,02:06:37,05:03:59 +40,M,01:57:20,05:04:00 +47,W,01:59:40,05:04:00 +34,M,02:11:24,05:04:02 +28,M,01:58:45,05:04:02 +30,W,02:02:34,05:04:02 +51,M,02:12:55,05:04:03 +25,W,02:08:07,05:04:03 +44,M,02:15:35,05:04:03 +39,M,02:07:34,05:04:03 +42,M,02:13:01,05:04:03 +42,W,02:19:22,05:04:04 +49,W,02:18:09,05:04:05 +40,M,02:01:16,05:04:05 +24,M,01:59:52,05:04:05 +45,W,01:59:58,05:04:06 +36,M,02:10:46,05:04:06 +52,M,02:14:43,05:04:06 +47,M,01:51:13,05:04:06 +52,M,02:06:27,05:04:06 +44,M,01:59:11,05:04:06 +44,W,01:59:38,05:04:06 +28,M,02:03:41,05:04:07 +40,M,02:06:52,05:04:07 +44,M,02:04:04,05:04:08 +49,M,02:06:24,05:04:08 +37,W,02:01:17,05:04:08 +47,M,01:50:46,05:04:08 +62,M,02:21:51,05:04:09 +65,M,02:04:56,05:04:09 +55,M,02:07:46,05:04:09 +26,W,02:02:09,05:04:09 +28,W,02:04:04,05:04:09 +50,M,01:58:53,05:04:09 +49,M,02:04:29,05:04:10 +47,W,02:05:00,05:04:10 +40,M,02:09:31,05:04:10 +28,W,02:12:50,05:04:10 +36,W,02:19:47,05:04:10 +31,W,02:10:51,05:04:11 +34,W,02:05:57,05:04:11 +61,M,02:09:14,05:04:12 +46,M,01:58:42,05:04:12 +46,M,02:07:14,05:04:12 +48,M,02:01:33,05:04:13 +39,W,02:24:29,05:04:13 +50,W,02:06:58,05:04:14 +48,M,02:06:38,05:04:14 +56,M,02:09:50,05:04:15 +43,M,02:02:35,05:04:15 +36,W,02:14:10,05:04:15 +38,W,02:12:24,05:04:15 +23,W,02:05:31,05:04:15 +59,M,02:09:30,05:04:15 +42,M,02:13:07,05:04:16 +37,M,02:10:43,05:04:16 +50,W,02:00:37,05:04:16 +27,W,02:09:51,05:04:16 +28,M,02:00:19,05:04:16 +49,M,02:11:17,05:04:17 +45,M,02:10:06,05:04:17 +56,M,02:09:23,05:04:17 +41,W,02:08:20,05:04:18 +36,M,01:59:43,05:04:18 +38,W,02:14:25,05:04:19 +33,W,01:57:47,05:04:19 +51,M,02:12:50,05:04:19 +42,M,02:14:11,05:04:19 +33,W,01:58:17,05:04:19 +49,M,02:10:50,05:04:19 +43,M,02:12:52,05:04:19 +29,W,02:10:27,05:04:19 +31,M,02:07:54,05:04:19 +44,M,02:00:14,05:04:19 +36,M,02:02:14,05:04:20 +41,W,02:21:56,05:04:21 +47,W,02:17:46,05:04:22 +50,W,02:13:05,05:04:22 +55,M,02:25:28,05:04:22 +28,W,02:07:46,05:04:22 +36,M,02:05:45,05:04:23 +42,W,02:09:49,05:04:23 +40,W,02:06:57,05:04:24 +46,M,01:59:19,05:04:24 +50,W,02:13:19,05:04:24 +27,M,02:05:59,05:04:24 +42,M,02:19:08,05:04:25 +33,M,01:56:11,05:04:25 +42,M,02:05:50,05:04:25 +33,M,01:57:05,05:04:25 +40,M,02:01:26,05:04:26 +22,M,02:01:45,05:04:26 +31,W,02:10:46,05:04:26 +28,W,02:19:32,05:04:26 +43,M,02:02:18,05:04:27 +21,M,02:10:54,05:04:27 +45,M,02:03:28,05:04:29 +38,M,01:59:31,05:04:29 +54,M,02:08:10,05:04:30 +38,M,02:13:25,05:04:30 +55,M,01:57:46,05:04:31 +51,M,02:02:03,05:04:31 +24,M,02:03:11,05:04:31 +32,W,02:09:29,05:04:31 +48,W,02:15:42,05:04:32 +52,M,02:09:18,05:04:32 +40,M,02:14:32,05:04:32 +24,M,01:56:48,05:04:32 +50,M,02:23:30,05:04:32 +54,M,02:03:51,05:04:32 +52,W,02:04:06,05:04:32 +34,M,02:22:12,05:04:33 +52,M,02:04:19,05:04:33 +30,W,02:05:28,05:04:33 +36,M,01:57:40,05:04:33 +48,W,02:06:44,05:04:33 +51,M,02:16:58,05:04:34 +52,W,02:17:21,05:04:34 +33,M,02:02:35,05:04:35 +49,M,02:09:07,05:04:35 +33,W,02:15:06,05:04:35 +38,M,02:02:35,05:04:35 +59,M,02:02:48,05:04:35 +37,M,02:03:02,05:04:35 +30,W,02:31:22,05:04:35 +25,M,01:53:20,05:04:35 +51,W,02:10:50,05:04:35 +48,W,02:02:02,05:04:36 +42,M,02:04:15,05:04:36 +44,W,02:11:51,05:04:37 +47,M,02:13:54,05:04:37 +48,W,02:19:15,05:04:37 +29,W,02:02:36,05:04:37 +31,W,02:11:23,05:04:37 +41,M,02:00:43,05:04:38 +54,M,02:13:41,05:04:38 +42,M,02:05:35,05:04:39 +66,W,02:08:49,05:04:39 +34,M,02:10:44,05:04:39 +57,M,02:13:03,05:04:40 +26,W,02:02:37,05:04:40 +51,M,02:07:27,05:04:40 +54,M,02:19:56,05:04:41 +33,M,02:04:35,05:04:41 +27,W,02:17:44,05:04:41 +64,M,01:56:07,05:04:42 +39,M,02:09:00,05:04:42 +29,M,02:05:10,05:04:42 +36,M,01:54:33,05:04:42 +42,W,02:16:38,05:04:43 +37,W,02:16:57,05:04:43 +36,M,02:04:22,05:04:43 +30,W,02:10:29,05:04:44 +47,M,01:57:21,05:04:44 +40,M,02:06:33,05:04:44 +25,W,02:15:30,05:04:44 +51,W,02:09:23,05:04:45 +42,W,02:05:58,05:04:45 +42,M,02:05:40,05:04:47 +23,M,01:59:17,05:04:47 +27,W,02:25:07,05:04:48 +41,M,01:57:19,05:04:48 +68,M,02:21:09,05:04:48 +50,M,02:11:01,05:04:49 +41,W,02:15:57,05:04:49 +54,W,02:08:06,05:04:49 +35,W,02:08:38,05:04:49 +28,M,01:57:39,05:04:49 +49,W,02:03:14,05:04:49 +58,M,02:16:38,05:04:49 +49,M,02:08:56,05:04:50 +34,M,02:07:15,05:04:50 +34,M,02:07:44,05:04:50 +51,M,02:15:53,05:04:51 +39,W,02:07:52,05:04:51 +39,W,02:17:08,05:04:51 +54,M,02:06:22,05:04:51 +45,M,02:00:04,05:04:52 +40,M,02:04:08,05:04:52 +39,W,02:03:55,05:04:53 +52,M,01:55:40,05:04:53 +26,M,02:13:35,05:04:53 +39,M,02:09:00,05:04:54 +39,M,02:08:59,05:04:54 +47,W,02:16:02,05:04:54 +49,M,02:06:58,05:04:55 +42,W,02:07:28,05:04:55 +47,M,02:12:46,05:04:55 +41,W,02:17:37,05:04:56 +35,M,02:01:06,05:04:56 +41,M,02:19:32,05:04:56 +43,M,02:08:38,05:04:56 +38,M,02:03:29,05:04:57 +27,M,02:11:39,05:04:57 +52,W,02:07:49,05:04:57 +53,M,02:07:18,05:04:57 +32,W,02:13:08,05:04:57 +26,M,02:06:54,05:04:57 +37,M,02:19:11,05:04:58 +26,W,02:22:00,05:04:58 +49,W,02:07:42,05:04:58 +52,M,02:02:59,05:04:58 +48,M,01:58:56,05:04:59 +38,M,02:03:14,05:04:59 +54,M,02:15:51,05:05:00 +36,W,02:21:34,05:05:00 +37,M,01:59:16,05:05:01 +38,M,02:08:32,05:05:02 +48,M,02:15:21,05:05:02 +41,M,02:03:27,05:05:03 +21,W,02:04:05,05:05:03 +25,W,02:07:11,05:05:04 +50,M,02:10:10,05:05:04 +31,W,02:14:36,05:05:05 +38,M,01:46:45,05:05:05 +43,M,02:10:34,05:05:05 +25,M,02:00:37,05:05:05 +40,M,02:09:07,05:05:05 +32,M,01:47:43,05:05:06 +37,W,02:01:21,05:05:06 +44,W,02:08:40,05:05:06 +39,M,01:52:41,05:05:07 +50,W,02:08:03,05:05:07 +33,M,01:55:27,05:05:07 +50,M,02:03:46,05:05:07 +61,M,02:07:10,05:05:07 +31,M,02:08:25,05:05:08 +33,W,02:09:54,05:05:08 +31,W,02:06:43,05:05:08 +43,W,02:09:35,05:05:08 +31,W,02:05:13,05:05:09 +33,M,02:07:07,05:05:10 +31,W,02:13:53,05:05:10 +24,M,02:07:07,05:05:11 +43,M,02:02:01,05:05:11 +38,M,02:11:11,05:05:11 +33,M,01:39:13,05:05:11 +31,M,01:59:19,05:05:12 +41,W,02:02:38,05:05:12 +40,M,02:34:32,05:05:12 +42,W,02:15:06,05:05:13 +47,W,02:09:56,05:05:13 +22,M,02:06:45,05:05:13 +52,M,02:17:02,05:05:13 +47,W,02:18:53,05:05:13 +40,M,01:59:44,05:05:14 +53,W,02:20:01,05:05:14 +57,M,02:06:56,05:05:15 +34,M,02:11:58,05:05:15 +39,W,02:07:41,05:05:15 +39,W,02:05:16,05:05:15 +25,W,01:53:17,05:05:15 +58,M,02:16:30,05:05:15 +40,M,02:10:19,05:05:15 +65,M,02:14:09,05:05:16 +40,W,02:10:24,05:05:16 +25,W,02:08:58,05:05:16 +43,M,02:20:20,05:05:16 +60,M,02:05:34,05:05:17 +29,W,02:10:43,05:05:17 +28,W,02:14:19,05:05:18 +31,M,02:09:31,05:05:19 +22,W,02:15:54,05:05:19 +40,M,01:56:14,05:05:19 +53,W,02:06:13,05:05:20 +23,W,02:10:49,05:05:20 +60,M,02:04:22,05:05:20 +36,M,01:58:49,05:05:20 +50,M,02:03:06,05:05:20 +50,M,02:05:25,05:05:21 +53,M,02:10:00,05:05:21 +60,M,02:04:38,05:05:21 +27,W,02:13:18,05:05:21 +35,M,02:14:34,05:05:21 +40,W,02:07:28,05:05:21 +47,M,01:56:18,05:05:21 +35,W,02:14:10,05:05:22 +32,W,01:59:20,05:05:22 +43,M,02:16:34,05:05:22 +34,W,02:05:49,05:05:22 +26,M,01:52:56,05:05:22 +28,M,01:57:31,05:05:23 +41,W,02:16:12,05:05:23 +48,W,02:01:53,05:05:23 +43,W,02:12:55,05:05:24 +43,M,02:06:00,05:05:24 +32,M,02:03:39,05:05:24 +34,M,02:10:21,05:05:24 +26,M,02:05:28,05:05:24 +68,M,02:10:55,05:05:24 +36,M,02:12:46,05:05:25 +44,W,02:13:16,05:05:25 +39,M,02:05:30,05:05:25 +42,M,02:02:53,05:05:26 +39,W,02:09:49,05:05:26 +29,W,02:05:38,05:05:26 +38,M,02:09:09,05:05:26 +55,M,02:17:08,05:05:27 +35,M,02:07:15,05:05:27 +38,M,02:04:55,05:05:27 +32,W,02:00:29,05:05:27 +27,W,02:09:36,05:05:28 +43,M,02:09:44,05:05:28 +51,W,02:10:54,05:05:28 +42,M,01:58:45,05:05:28 +26,M,02:00:29,05:05:29 +30,M,02:07:58,05:05:29 +40,W,02:01:56,05:05:29 +57,M,02:13:59,05:05:29 +42,W,02:01:48,05:05:29 +55,M,02:15:46,05:05:31 +48,M,01:49:28,05:05:31 +42,M,02:03:15,05:05:31 +43,W,02:16:06,05:05:32 +34,M,02:05:25,05:05:32 +57,W,02:12:53,05:05:32 +59,M,02:08:20,05:05:32 +50,M,02:07:29,05:05:32 +49,W,02:08:31,05:05:33 +53,M,02:09:53,05:05:33 +26,W,02:19:18,05:05:34 +38,M,02:22:05,05:05:34 +38,M,02:05:37,05:05:34 +49,W,02:13:38,05:05:34 +23,M,02:09:27,05:05:34 +51,M,02:11:29,05:05:35 +29,W,02:12:46,05:05:35 +32,W,02:07:00,05:05:35 +34,W,02:12:36,05:05:36 +26,M,02:08:09,05:05:36 +34,W,02:10:32,05:05:36 +59,W,02:12:12,05:05:36 +31,M,01:56:25,05:05:36 +38,W,02:00:45,05:05:37 +50,W,02:14:55,05:05:37 +41,M,02:07:22,05:05:37 +54,W,02:04:52,05:05:37 +48,W,02:07:21,05:05:39 +50,W,02:15:22,05:05:39 +43,M,02:18:11,05:05:39 +40,M,02:14:31,05:05:40 +25,M,02:00:04,05:05:40 +37,W,02:08:20,05:05:40 +49,M,01:52:58,05:05:40 +44,M,02:06:30,05:05:40 +25,W,02:16:19,05:05:40 +65,M,02:22:49,05:05:41 +51,M,02:04:42,05:05:41 +59,M,01:57:04,05:05:41 +48,M,02:07:37,05:05:42 +52,M,02:09:48,05:05:42 +58,M,02:06:17,05:05:43 +52,M,02:10:25,05:05:43 +33,M,02:05:50,05:05:44 +31,W,02:10:37,05:05:44 +38,W,02:05:41,05:05:45 +37,M,01:58:57,05:05:45 +30,W,02:04:06,05:05:45 +42,W,02:07:32,05:05:45 +40,W,02:02:54,05:05:45 +25,W,03:09:07,05:05:46 +35,M,01:59:54,05:05:46 +51,W,02:11:37,05:05:46 +41,W,02:05:49,05:05:46 +32,W,02:00:11,05:05:47 +56,M,01:57:45,05:05:47 +58,M,02:06:36,05:05:47 +30,M,02:02:15,05:05:47 +51,M,02:20:46,05:05:47 +64,M,02:10:03,05:05:47 +27,W,02:08:17,05:05:48 +34,M,02:00:34,05:05:49 +42,M,02:05:43,05:05:49 +35,M,02:05:34,05:05:49 +37,M,02:07:53,05:05:49 +43,M,02:12:22,05:05:49 +31,M,02:04:14,05:05:50 +46,M,02:24:25,05:05:50 +41,W,02:09:16,05:05:51 +53,M,02:02:48,05:05:51 +54,M,02:18:41,05:05:51 +45,W,02:05:13,05:05:51 +60,W,02:09:23,05:05:51 +26,W,02:07:16,05:05:52 +51,M,02:07:17,05:05:53 +45,W,02:09:24,05:05:54 +28,W,02:06:33,05:05:54 +31,W,02:07:18,05:05:54 +39,M,02:04:57,05:05:54 +26,W,02:14:52,05:05:54 +23,W,02:05:33,05:05:54 +57,M,02:16:04,05:05:55 +44,M,02:06:49,05:05:55 +29,M,02:08:34,05:05:55 +25,M,02:00:37,05:05:55 +48,M,02:07:18,05:05:56 +42,M,02:11:59,05:05:56 +41,M,02:01:37,05:05:56 +31,M,02:08:31,05:05:56 +54,M,02:09:18,05:05:57 +46,W,02:06:46,05:05:57 +45,M,02:04:24,05:05:57 +40,W,02:14:20,05:05:57 +45,M,01:59:38,05:05:58 +30,W,02:03:29,05:05:58 +51,M,02:11:29,05:05:58 +50,M,02:10:18,05:05:58 +41,M,02:17:36,05:05:59 +38,M,02:05:47,05:05:59 +24,M,02:08:47,05:05:59 +48,W,02:07:18,05:06:00 +51,W,02:15:44,05:06:00 +41,W,02:17:09,05:06:00 +35,M,01:54:22,05:06:00 +38,M,02:07:06,05:06:00 +48,M,01:59:41,05:06:01 +43,M,02:04:01,05:06:01 +26,W,02:04:05,05:06:01 +36,W,02:10:17,05:06:01 +30,W,02:04:08,05:06:02 +45,W,02:08:18,05:06:02 +46,M,02:03:52,05:06:02 +41,M,01:50:18,05:06:02 +28,W,02:06:47,05:06:03 +50,M,02:19:17,05:06:03 +41,W,02:22:46,05:06:03 +52,W,02:08:37,05:06:03 +28,W,02:13:19,05:06:04 +58,M,02:07:35,05:06:04 +39,W,02:04:58,05:06:04 +29,W,02:10:39,05:06:04 +37,M,02:08:58,05:06:05 +65,M,02:14:05,05:06:05 +46,M,01:52:32,05:06:05 +41,M,02:07:32,05:06:05 +37,W,02:03:24,05:06:05 +32,M,02:09:35,05:06:06 +35,W,02:17:05,05:06:06 +39,M,02:11:34,05:06:06 +56,M,02:08:20,05:06:07 +30,M,02:01:07,05:06:07 +31,M,01:55:59,05:06:07 +45,M,02:13:22,05:06:07 +40,M,02:17:58,05:06:07 +46,M,02:07:02,05:06:07 +52,M,02:10:19,05:06:07 +32,W,02:11:58,05:06:07 +29,M,02:13:07,05:06:08 +48,M,01:57:54,05:06:08 +55,W,02:11:20,05:06:08 +23,M,02:09:17,05:06:08 +45,M,02:07:48,05:06:09 +51,W,02:02:10,05:06:09 +54,M,01:55:33,05:06:09 +36,M,02:24:11,05:06:09 +45,M,01:58:44,05:06:09 +25,W,02:09:34,05:06:09 +40,M,02:17:40,05:06:10 +40,M,02:05:02,05:06:10 +57,M,02:04:03,05:06:10 +27,M,01:58:06,05:06:10 +27,M,02:03:38,05:06:11 +33,M,01:56:04,05:06:11 +53,W,02:14:20,05:06:12 +36,W,02:11:59,05:06:12 +26,M,02:10:52,05:06:12 +38,M,02:21:53,05:06:12 +25,M,01:50:30,05:06:13 +45,W,02:15:33,05:06:13 +45,M,02:04:54,05:06:13 +41,W,02:00:34,05:06:13 +41,M,01:59:42,05:06:14 +28,W,02:12:54,05:06:14 +29,M,02:10:53,05:06:14 +32,M,02:00:46,05:06:14 +60,W,02:12:31,05:06:15 +31,M,02:01:29,05:06:15 +44,W,02:00:32,05:06:16 +44,M,02:10:33,05:06:16 +45,M,02:00:39,05:06:16 +41,W,02:08:14,05:06:17 +34,W,02:10:23,05:06:17 +53,M,02:03:27,05:06:17 +54,M,02:06:50,05:06:17 +51,M,02:17:06,05:06:17 +22,W,02:15:27,05:06:17 +49,W,02:08:31,05:06:17 +20,M,02:12:02,05:06:17 +33,W,02:05:27,05:06:18 +29,W,02:10:09,05:06:18 +34,W,02:03:54,05:06:18 +46,M,01:56:59,05:06:18 +37,W,02:08:28,05:06:18 +51,M,02:03:52,05:06:19 +60,M,02:08:33,05:06:19 +42,W,02:14:29,05:06:19 +27,W,02:04:14,05:06:19 +61,W,02:04:15,05:06:19 +36,M,02:07:11,05:06:19 +24,W,01:56:08,05:06:20 +52,M,02:22:26,05:06:20 +36,M,02:13:46,05:06:20 +44,M,02:07:57,05:06:20 +37,W,02:04:31,05:06:20 +54,W,02:04:14,05:06:21 +62,M,02:02:26,05:06:21 +29,W,02:06:23,05:06:22 +46,M,02:14:34,05:06:22 +26,M,02:03:34,05:06:22 +37,W,02:10:05,05:06:22 +52,M,02:10:16,05:06:23 +31,W,02:12:00,05:06:23 +32,W,02:09:48,05:06:23 +38,M,02:09:15,05:06:24 +25,W,02:04:56,05:06:24 +29,M,02:01:45,05:06:25 +43,M,02:10:05,05:06:26 +36,W,02:12:07,05:06:26 +40,W,02:08:06,05:06:26 +51,W,02:05:11,05:06:26 +26,W,02:04:52,05:06:26 +40,W,02:20:50,05:06:26 +27,M,02:11:40,05:06:27 +50,M,02:08:22,05:06:27 +44,M,02:06:37,05:06:27 +33,M,02:22:20,05:06:28 +41,M,02:05:39,05:06:28 +58,W,02:04:29,05:06:28 +34,M,01:54:13,05:06:28 +30,W,02:09:58,05:06:28 +43,M,01:54:23,05:06:29 +49,W,02:17:29,05:06:29 +36,M,02:10:55,05:06:29 +42,M,02:08:15,05:06:30 +25,W,02:01:34,05:06:30 +21,W,02:10:36,05:06:30 +37,W,02:04:40,05:06:30 +39,W,02:16:36,05:06:30 +48,M,02:10:30,05:06:30 +40,M,01:59:57,05:06:30 +27,W,02:17:21,05:06:31 +42,W,02:10:50,05:06:31 +44,W,02:16:43,05:06:32 +35,M,02:09:42,05:06:32 +33,M,02:15:52,05:06:32 +36,M,02:00:29,05:06:33 +39,M,02:12:17,05:06:33 +24,W,02:10:55,05:06:34 +44,M,01:59:54,05:06:34 +26,M,02:05:20,05:06:35 +43,M,01:56:41,05:06:35 +29,W,02:03:33,05:06:35 +40,M,02:03:51,05:06:35 +28,M,02:02:49,05:06:35 +36,W,02:14:11,05:06:36 +55,M,02:08:34,05:06:37 +34,W,02:14:54,05:06:37 +48,W,02:04:55,05:06:38 +54,M,02:06:45,05:06:38 +71,M,02:06:32,05:06:39 +39,W,02:11:42,05:06:39 +31,W,02:11:58,05:06:39 +26,W,02:01:55,05:06:39 +40,M,02:17:10,05:06:39 +58,M,02:07:01,05:06:39 +56,M,02:09:32,05:06:40 +40,M,02:09:14,05:06:40 +29,M,02:00:01,05:06:40 +42,W,02:08:16,05:06:41 +45,M,02:05:45,05:06:41 +39,M,02:04:33,05:06:41 +42,W,02:16:49,05:06:41 +43,M,02:08:57,05:06:41 +27,W,02:09:44,05:06:41 +44,M,02:07:24,05:06:42 +23,M,02:05:47,05:06:42 +49,M,02:16:30,05:06:42 +37,M,02:04:13,05:06:42 +57,M,01:56:41,05:06:42 +65,W,02:09:51,05:06:43 +28,W,02:14:58,05:06:43 +56,W,02:11:50,05:06:43 +26,W,02:12:52,05:06:43 +44,M,02:20:10,05:06:43 +51,M,02:12:14,05:06:43 +50,W,02:07:01,05:06:43 +33,M,02:08:32,05:06:44 +26,W,02:09:08,05:06:44 +37,M,02:02:40,05:06:44 +27,W,02:07:05,05:06:44 +35,M,02:02:40,05:06:44 +25,M,02:04:11,05:06:44 +28,M,02:05:50,05:06:44 +42,M,02:12:11,05:06:45 +41,W,02:04:23,05:06:45 +29,W,02:06:16,05:06:45 +32,M,02:00:52,05:06:45 +41,M,02:08:53,05:06:45 +26,W,02:04:45,05:06:45 +49,M,02:17:10,05:06:46 +29,W,02:04:35,05:06:46 +45,W,02:05:19,05:06:46 +61,M,02:16:07,05:06:46 +41,W,02:08:46,05:06:46 +35,M,02:01:13,05:06:46 +39,M,02:01:11,05:06:47 +25,W,02:09:22,05:06:47 +27,W,02:14:46,05:06:47 +48,M,02:06:57,05:06:48 +36,W,02:04:24,05:06:48 +32,W,02:10:11,05:06:48 +42,M,02:02:49,05:06:49 +39,W,02:08:11,05:06:49 +28,W,02:07:03,05:06:49 +32,M,01:58:45,05:06:50 +50,W,02:13:14,05:06:50 +39,M,01:58:59,05:06:50 +48,M,02:08:49,05:06:50 +49,M,02:06:57,05:06:50 +40,W,02:07:00,05:06:50 +37,M,02:08:19,05:06:50 +40,M,02:02:21,05:06:51 +32,W,02:06:57,05:06:51 +34,M,02:09:17,05:06:51 +40,M,02:01:02,05:06:51 +47,M,02:10:29,05:06:52 +54,M,02:14:46,05:06:52 +54,M,02:09:14,05:06:52 +54,M,02:09:23,05:06:52 +25,W,02:13:38,05:06:52 +43,W,02:09:51,05:06:52 +39,M,02:10:17,05:06:53 +58,W,02:08:24,05:06:53 +32,W,02:12:45,05:06:53 +46,M,02:14:53,05:06:54 +35,W,02:08:14,05:06:54 +34,M,02:05:35,05:06:54 +51,M,02:00:09,05:06:54 +38,M,02:07:33,05:06:54 +25,M,02:09:07,05:06:54 +23,M,02:06:47,05:06:55 +26,M,02:06:45,05:06:55 +46,W,02:02:29,05:06:55 +27,W,02:14:46,05:06:55 +24,W,02:09:26,05:06:56 +47,M,02:26:45,05:06:56 +51,M,02:00:14,05:06:56 +28,M,02:08:10,05:06:57 +23,W,02:16:04,05:06:57 +28,M,02:02:53,05:06:57 +53,M,02:15:08,05:06:57 +25,W,02:03:50,05:06:58 +37,M,02:06:40,05:06:58 +42,M,02:00:46,05:06:58 +34,M,01:59:47,05:06:58 +24,M,02:07:56,05:06:58 +70,M,02:23:43,05:06:58 +47,M,02:04:36,05:06:59 +36,M,02:04:19,05:06:59 +46,W,02:20:30,05:06:59 +29,W,02:07:41,05:07:00 +31,W,02:02:28,05:07:00 +43,W,02:13:00,05:07:00 +45,W,02:26:23,05:07:01 +36,M,02:12:21,05:07:02 +28,W,02:18:10,05:07:02 +26,M,01:57:09,05:07:02 +23,W,02:05:48,05:07:02 +44,W,02:04:36,05:07:02 +41,W,02:10:47,05:07:03 +71,M,02:13:21,05:07:03 +32,M,02:06:04,05:07:03 +29,M,02:10:46,05:07:04 +47,M,02:08:12,05:07:04 +60,M,02:02:53,05:07:04 +42,M,02:12:18,05:07:04 +59,M,02:10:17,05:07:04 +45,M,01:58:09,05:07:04 +53,M,01:58:09,05:07:04 +46,M,02:06:51,05:07:05 +34,W,02:08:42,05:07:05 +25,M,02:03:37,05:07:05 +31,M,02:04:35,05:07:05 +58,M,02:07:17,05:07:05 +30,W,01:57:21,05:07:05 +21,W,02:04:13,05:07:05 +64,M,02:05:37,05:07:05 +48,W,02:04:22,05:07:07 +30,M,01:59:27,05:07:07 +59,M,02:17:23,05:07:07 +42,M,02:13:23,05:07:08 +43,M,02:09:34,05:07:08 +21,M,02:09:37,05:07:08 +33,W,02:09:03,05:07:09 +40,W,02:03:06,05:07:09 +35,W,02:15:02,05:07:09 +29,W,01:57:31,05:07:10 +51,M,02:01:43,05:07:10 +43,M,01:56:58,05:07:10 +51,W,02:11:24,05:07:10 +32,M,02:05:26,05:07:10 +57,M,02:03:52,05:07:10 +40,W,02:09:24,05:07:11 +37,M,02:13:22,05:07:12 +26,W,02:15:19,05:07:12 +29,W,02:15:33,05:07:12 +30,M,01:48:33,05:07:13 +35,W,02:12:17,05:07:14 +36,M,02:13:12,05:07:14 +43,W,02:16:19,05:07:14 +33,W,02:05:23,05:07:14 +32,M,02:16:40,05:07:15 +36,M,01:56:23,05:07:15 +44,M,02:10:51,05:07:15 +60,M,02:14:53,05:07:15 +38,W,02:09:44,05:07:16 +57,W,02:10:11,05:07:16 +39,M,02:07:56,05:07:16 +51,W,02:07:38,05:07:17 +25,M,02:12:22,05:07:17 +35,M,02:17:01,05:07:18 +45,W,02:08:39,05:07:18 +40,W,02:24:38,05:07:18 +55,W,02:05:55,05:07:19 +58,M,02:09:43,05:07:19 +45,M,02:04:47,05:07:19 +38,W,02:08:54,05:07:19 +30,M,02:11:19,05:07:19 +26,W,02:06:09,05:07:19 +50,W,02:09:29,05:07:20 +45,M,02:05:42,05:07:20 +54,M,02:07:41,05:07:20 +38,M,02:07:18,05:07:20 +50,M,02:04:15,05:07:21 +34,W,02:12:25,05:07:21 +27,M,01:56:42,05:07:21 +23,W,02:00:54,05:07:21 +61,M,02:24:54,05:07:22 +61,M,02:13:23,05:07:22 +32,W,02:09:12,05:07:22 +45,W,02:08:33,05:07:23 +49,M,02:14:15,05:07:23 +42,W,02:07:20,05:07:23 +46,M,02:05:33,05:07:23 +29,W,02:06:50,05:07:23 +44,M,02:02:42,05:07:23 +45,M,02:04:04,05:07:24 +40,W,02:11:23,05:07:24 +51,M,02:12:58,05:07:24 +43,W,02:11:27,05:07:24 +34,M,02:01:51,05:07:24 +29,M,02:06:47,05:07:25 +38,W,02:18:30,05:07:25 +53,M,02:17:07,05:07:25 +50,W,02:08:37,05:07:26 +29,W,02:05:37,05:07:26 +29,M,01:59:47,05:07:26 +43,M,02:01:31,05:07:26 +29,W,02:10:30,05:07:26 +55,W,02:15:02,05:07:26 +51,M,02:01:55,05:07:26 +40,M,01:57:20,05:07:26 +52,M,02:17:22,05:07:27 +38,M,02:00:16,05:07:27 +47,M,02:22:43,05:07:27 +52,M,02:04:20,05:07:27 +58,M,02:13:44,05:07:27 +26,W,02:14:17,05:07:28 +53,M,02:11:02,05:07:28 +58,M,02:07:44,05:07:28 +61,M,01:57:14,05:07:29 +31,M,02:04:37,05:07:29 +57,M,02:10:25,05:07:29 +46,M,02:14:44,05:07:29 +46,W,02:11:08,05:07:29 +52,M,02:01:12,05:07:30 +22,W,02:12:22,05:07:30 +43,M,01:55:28,05:07:30 +27,W,02:06:41,05:07:30 +43,W,02:09:00,05:07:31 +49,W,02:03:31,05:07:31 +37,M,02:05:55,05:07:31 +41,W,02:03:50,05:07:31 +22,W,02:19:29,05:07:31 +48,M,02:06:11,05:07:32 +40,M,01:56:13,05:07:32 +46,W,02:12:33,05:07:32 +38,M,02:10:16,05:07:32 +45,W,02:14:29,05:07:33 +47,M,02:06:44,05:07:33 +49,M,02:08:36,05:07:33 +33,W,02:13:13,05:07:33 +38,M,02:10:56,05:07:33 +31,W,02:14:42,05:07:34 +61,W,02:06:21,05:07:35 +41,M,02:04:40,05:07:35 +43,M,01:59:00,05:07:35 +43,W,02:07:51,05:07:35 +43,M,01:58:05,05:07:35 +44,M,01:47:58,05:07:36 +32,W,02:12:14,05:07:36 +54,W,02:12:20,05:07:37 +54,W,02:06:40,05:07:37 +42,M,02:10:28,05:07:37 +44,M,02:05:18,05:07:37 +29,M,02:07:25,05:07:38 +40,W,02:18:24,05:07:38 +42,W,02:08:05,05:07:38 +39,W,02:14:36,05:07:39 +17,M,02:14:12,05:07:39 +44,M,02:06:23,05:07:39 +49,M,02:17:47,05:07:39 +35,W,02:08:23,05:07:39 +40,W,02:19:17,05:07:40 +30,W,02:09:45,05:07:40 +25,W,02:08:58,05:07:41 +30,M,01:58:43,05:07:41 +53,M,02:03:44,05:07:41 +51,M,02:14:12,05:07:41 +58,M,02:19:01,05:07:42 +40,M,02:13:35,05:07:42 +51,M,02:15:07,05:07:43 +47,W,02:12:06,05:07:44 +49,W,02:08:11,05:07:44 +24,W,02:18:07,05:07:44 +47,M,02:11:51,05:07:44 +35,M,02:01:53,05:07:44 +28,W,02:15:15,05:07:44 +30,W,02:12:52,05:07:44 +24,W,02:05:14,05:07:44 +61,M,01:59:21,05:07:44 +39,W,02:14:32,05:07:44 +52,W,02:15:33,05:07:45 +64,M,02:14:14,05:07:45 +52,M,02:12:33,05:07:45 +40,W,02:06:16,05:07:45 +53,M,02:10:36,05:07:45 +35,W,02:11:50,05:07:46 +55,W,02:06:34,05:07:46 +19,M,02:23:47,05:07:46 +34,W,02:08:13,05:07:46 +76,M,02:15:19,05:07:46 +38,W,02:06:01,05:07:47 +62,M,02:08:21,05:07:47 +39,W,02:05:48,05:07:48 +43,M,01:59:34,05:07:48 +36,W,02:21:15,05:07:48 +38,W,02:07:25,05:07:49 +39,M,02:20:22,05:07:49 +47,M,02:11:08,05:07:49 +35,M,02:07:02,05:07:50 +49,M,02:07:30,05:07:50 +40,W,02:09:16,05:07:50 +41,W,02:15:05,05:07:50 +35,W,02:14:22,05:07:50 +53,W,02:12:27,05:07:50 +29,M,02:07:29,05:07:50 +31,M,02:03:27,05:07:51 +33,M,02:13:59,05:07:51 +52,M,02:09:11,05:07:51 +44,W,02:09:23,05:07:52 +50,M,02:12:19,05:07:52 +64,M,02:07:14,05:07:52 +41,M,02:03:12,05:07:52 +27,W,02:18:09,05:07:53 +53,M,02:13:05,05:07:53 +40,W,02:12:15,05:07:53 +35,M,01:54:25,05:07:54 +40,W,02:17:51,05:07:54 +35,M,02:14:09,05:07:55 +51,M,02:40:37,05:07:55 +38,M,02:18:29,05:07:55 +46,M,02:05:34,05:07:55 +38,M,02:14:55,05:07:55 +40,W,02:05:25,05:07:56 +61,M,02:06:50,05:07:56 +25,W,02:01:12,05:07:56 +30,W,02:18:09,05:07:56 +61,M,02:10:12,05:07:57 +30,M,02:04:29,05:07:57 +24,M,02:06:45,05:07:57 +39,W,02:21:39,05:07:58 +49,M,02:04:11,05:07:58 +35,M,01:57:00,05:07:58 +29,M,02:03:33,05:07:58 +26,M,02:09:55,05:08:00 +55,M,02:02:21,05:08:01 +40,M,02:20:20,05:08:02 +50,W,02:08:27,05:08:02 +48,M,02:12:51,05:08:03 +58,M,02:09:50,05:08:03 +24,M,02:07:30,05:08:04 +29,M,02:00:42,05:08:04 +43,W,02:10:49,05:08:04 +40,M,02:08:33,05:08:04 +42,W,02:11:28,05:08:05 +30,M,02:08:00,05:08:05 +39,W,02:12:44,05:08:06 +31,M,02:08:58,05:08:06 +49,W,02:08:13,05:08:06 +43,M,02:01:59,05:08:07 +44,M,02:04:45,05:08:08 +53,M,02:08:43,05:08:08 +26,W,02:09:07,05:08:08 +45,W,02:08:59,05:08:09 +60,M,02:14:25,05:08:09 +30,M,02:06:29,05:08:09 +33,M,02:10:23,05:08:09 +61,M,02:08:02,05:08:09 +40,W,02:02:32,05:08:09 +55,W,02:08:57,05:08:09 +46,W,02:13:38,05:08:10 +69,M,02:04:38,05:08:10 +40,M,02:14:27,05:08:10 +29,W,02:18:26,05:08:10 +44,M,02:07:03,05:08:10 +31,W,02:09:47,05:08:11 +31,M,02:14:46,05:08:11 +30,M,02:13:34,05:08:11 +34,M,02:12:39,05:08:12 +38,W,02:12:15,05:08:12 +34,W,02:14:57,05:08:13 +18,W,02:03:20,05:08:13 +64,W,02:21:11,05:08:13 +41,M,02:08:39,05:08:14 +43,M,01:58:36,05:08:14 +58,M,02:03:03,05:08:14 +42,M,02:11:10,05:08:15 +43,W,02:10:56,05:08:15 +32,W,02:14:48,05:08:15 +51,W,02:02:49,05:08:15 +32,W,02:10:20,05:08:15 +24,M,02:04:51,05:08:15 +44,M,02:13:08,05:08:16 +63,M,02:03:54,05:08:16 +27,M,02:11:49,05:08:16 +30,W,02:09:14,05:08:17 +46,M,02:11:57,05:08:17 +31,W,02:03:57,05:08:18 +52,W,02:10:52,05:08:18 +29,M,02:14:41,05:08:18 +47,W,02:13:19,05:08:18 +55,M,02:24:15,05:08:18 +47,M,01:54:24,05:08:19 +35,M,02:04:52,05:08:19 +38,M,02:13:58,05:08:19 +33,M,02:07:20,05:08:19 +47,M,02:10:42,05:08:19 +48,M,02:08:08,05:08:20 +39,M,02:11:59,05:08:20 +59,M,01:53:16,05:08:20 +38,M,02:04:11,05:08:20 +43,W,01:54:03,05:08:21 +37,M,02:09:25,05:08:21 +42,W,02:10:41,05:08:21 +61,W,02:15:46,05:08:22 +62,M,02:04:55,05:08:22 +63,M,02:08:57,05:08:22 +30,W,02:10:07,05:08:22 +29,W,02:06:29,05:08:23 +29,M,02:11:34,05:08:23 +38,W,02:17:36,05:08:23 +40,W,02:11:53,05:08:23 +41,M,02:00:19,05:08:23 +25,W,02:13:36,05:08:24 +33,M,02:13:08,05:08:24 +27,W,01:59:12,05:08:24 +74,M,02:14:21,05:08:24 +48,W,02:07:05,05:08:24 +43,M,01:54:11,05:08:24 +39,M,02:05:42,05:08:25 +53,W,01:58:35,05:08:25 +40,M,02:02:08,05:08:25 +49,M,02:08:02,05:08:25 +55,M,02:10:14,05:08:26 +53,M,02:07:34,05:08:26 +33,M,02:05:34,05:08:26 +38,M,02:14:02,05:08:27 +37,M,02:12:31,05:08:27 +45,W,02:15:41,05:08:27 +41,M,02:12:57,05:08:27 +60,W,02:00:15,05:08:27 +29,W,01:59:47,05:08:27 +33,W,02:04:14,05:08:28 +52,W,02:05:54,05:08:28 +47,M,02:06:03,05:08:28 +40,W,02:10:54,05:08:28 +30,M,02:04:37,05:08:28 +44,W,02:03:09,05:08:28 +33,W,02:12:15,05:08:29 +40,M,02:02:56,05:08:29 +39,M,02:07:02,05:08:29 +55,W,02:13:24,05:08:29 +32,M,02:07:49,05:08:30 +43,M,02:20:32,05:08:30 +45,M,02:07:08,05:08:30 +26,M,02:01:52,05:08:31 +39,W,02:13:47,05:08:31 +43,M,01:59:03,05:08:31 +28,W,02:07:08,05:08:31 +44,M,02:11:58,05:08:32 +49,W,02:14:23,05:08:32 +36,M,02:03:26,05:08:33 +58,M,02:12:12,05:08:33 +57,M,02:11:49,05:08:33 +24,W,02:12:08,05:08:33 +34,W,02:12:04,05:08:33 +44,M,02:06:52,05:08:34 +30,M,02:01:51,05:08:34 +30,M,01:52:04,05:08:34 +51,W,02:19:23,05:08:34 +46,M,02:09:38,05:08:35 +51,M,02:05:38,05:08:35 +52,M,02:12:07,05:08:35 +50,W,02:08:45,05:08:36 +39,M,01:59:06,05:08:36 +36,M,01:57:14,05:08:36 +48,M,02:12:16,05:08:36 +34,W,02:13:22,05:08:36 +48,M,02:05:57,05:08:36 +29,W,02:16:05,05:08:37 +34,W,02:16:42,05:08:37 +47,M,02:01:23,05:08:37 +27,M,02:18:31,05:08:37 +48,M,02:09:09,05:08:37 +66,M,02:09:25,05:08:38 +36,M,02:07:28,05:08:38 +31,M,02:07:12,05:08:38 +57,M,02:05:21,05:08:39 +45,W,02:07:57,05:08:39 +41,M,02:15:51,05:08:39 +39,W,02:10:36,05:08:40 +59,W,02:01:44,05:08:40 +31,W,02:10:39,05:08:41 +37,M,02:04:48,05:08:41 +37,W,02:05:14,05:08:41 +65,M,02:22:12,05:08:41 +33,W,02:07:34,05:08:42 +44,M,02:05:29,05:08:42 +33,W,02:11:05,05:08:42 +33,M,01:55:04,05:08:42 +37,M,02:02:33,05:08:42 +36,M,02:08:08,05:08:43 +40,M,02:09:06,05:08:43 +49,W,02:12:45,05:08:43 +55,W,02:14:34,05:08:43 +43,M,02:01:32,05:08:44 +32,W,02:10:56,05:08:44 +37,W,02:03:52,05:08:44 +60,M,02:07:37,05:08:45 +26,W,02:19:01,05:08:45 +40,M,02:00:51,05:08:45 +45,M,02:01:07,05:08:45 +45,W,02:08:40,05:08:45 +54,M,01:55:18,05:08:45 +42,W,02:10:33,05:08:46 +39,M,02:09:26,05:08:46 +30,W,02:09:13,05:08:46 +46,M,01:54:06,05:08:47 +51,M,02:18:37,05:08:47 +32,W,02:10:27,05:08:48 +25,W,02:16:29,05:08:48 +33,M,02:06:32,05:08:48 +49,M,02:00:27,05:08:48 +41,M,02:12:55,05:08:48 +39,W,02:13:12,05:08:48 +41,W,02:09:50,05:08:48 +63,M,02:09:53,05:08:49 +29,W,02:05:12,05:08:51 +43,W,02:15:03,05:08:52 +39,M,02:24:18,05:08:52 +50,W,02:16:14,05:08:52 +24,W,02:20:39,05:08:53 +37,M,02:20:37,05:08:53 +32,M,02:11:02,05:08:53 +64,M,02:16:02,05:08:53 +45,M,01:55:38,05:08:53 +32,M,02:05:09,05:08:53 +33,W,02:08:11,05:08:54 +49,M,02:16:15,05:08:54 +57,M,02:00:00,05:08:54 +40,W,02:12:07,05:08:54 +40,M,02:14:37,05:08:54 +53,M,02:12:13,05:08:55 +41,W,02:00:45,05:08:55 +25,W,02:08:32,05:08:56 +28,W,01:47:54,05:08:56 +48,M,02:01:14,05:08:57 +45,W,02:14:48,05:08:58 +46,M,02:11:22,05:08:58 +51,M,02:13:08,05:08:58 +52,M,02:04:01,05:08:58 +43,M,02:06:24,05:08:58 +52,M,02:15:05,05:08:58 +37,M,02:14:41,05:08:58 +34,M,01:57:01,05:08:59 +58,W,02:10:23,05:08:59 +39,W,01:57:34,05:08:59 +33,M,02:06:04,05:09:00 +58,M,02:10:03,05:09:00 +42,M,02:00:26,05:09:01 +29,W,02:06:53,05:09:01 +48,M,02:12:25,05:09:01 +27,M,02:11:10,05:09:01 +23,M,02:06:37,05:09:01 +53,M,02:16:11,05:09:01 +44,M,02:11:32,05:09:02 +55,M,02:12:44,05:09:03 +50,W,02:14:14,05:09:03 +42,W,02:11:44,05:09:03 +50,W,02:18:50,05:09:03 +44,W,02:10:37,05:09:04 +51,M,02:01:14,05:09:04 +27,W,02:12:03,05:09:04 +46,M,02:18:49,05:09:04 +44,W,02:08:05,05:09:04 +59,M,02:06:10,05:09:04 +40,W,02:08:49,05:09:05 +45,W,02:11:37,05:09:05 +51,M,02:05:59,05:09:05 +32,M,02:07:22,05:09:05 +45,M,02:07:28,05:09:06 +28,M,02:08:53,05:09:06 +32,M,02:06:19,05:09:06 +48,M,02:06:43,05:09:06 +55,M,02:14:13,05:09:06 +38,M,02:10:19,05:09:07 +22,M,02:10:14,05:09:07 +42,W,02:06:26,05:09:07 +31,M,02:05:30,05:09:07 +43,W,02:14:44,05:09:07 +44,W,02:09:57,05:09:08 +34,M,02:05:07,05:09:09 +43,M,02:24:48,05:09:09 +24,W,02:07:34,05:09:09 +31,W,02:07:32,05:09:09 +29,W,02:08:39,05:09:09 +45,M,01:59:33,05:09:09 +30,M,02:13:46,05:09:10 +34,M,02:09:21,05:09:10 +55,W,02:13:03,05:09:10 +48,M,02:01:49,05:09:11 +30,W,02:02:30,05:09:11 +41,M,02:20:40,05:09:12 +25,W,02:13:26,05:09:12 +64,M,02:15:29,05:09:12 +36,W,02:08:45,05:09:12 +21,W,02:11:00,05:09:13 +45,M,02:15:59,05:09:13 +28,M,02:09:06,05:09:14 +31,W,02:13:25,05:09:14 +28,W,02:12:09,05:09:14 +53,M,02:05:58,05:09:15 +41,W,02:12:43,05:09:15 +24,W,02:09:16,05:09:16 +44,W,02:09:37,05:09:16 +49,M,02:07:46,05:09:16 +41,W,02:02:29,05:09:16 +45,M,02:12:14,05:09:17 +36,M,02:10:42,05:09:17 +32,W,02:09:14,05:09:17 +36,M,02:05:08,05:09:18 +31,M,02:03:50,05:09:18 +30,W,02:11:59,05:09:18 +54,W,02:01:35,05:09:19 +31,W,02:05:29,05:09:19 +42,W,02:10:52,05:09:20 +45,M,02:08:48,05:09:20 +38,W,02:10:55,05:09:20 +32,W,02:15:33,05:09:20 +36,W,02:08:19,05:09:21 +39,M,02:07:47,05:09:21 +50,M,02:09:40,05:09:21 +40,W,02:12:11,05:09:22 +39,W,02:16:39,05:09:22 +46,M,02:19:33,05:09:23 +51,M,02:05:08,05:09:24 +45,M,02:01:53,05:09:24 +48,W,02:10:08,05:09:25 +50,W,02:08:24,05:09:26 +45,W,02:10:31,05:09:26 +26,M,02:08:02,05:09:26 +44,M,02:28:52,05:09:27 +29,W,02:08:32,05:09:27 +30,M,02:06:49,05:09:27 +43,M,02:21:38,05:09:27 +32,M,02:07:04,05:09:28 +54,M,01:59:09,05:09:28 +30,M,02:21:42,05:09:29 +41,W,02:08:10,05:09:29 +34,M,02:04:23,05:09:29 +46,M,02:07:44,05:09:30 +40,M,02:05:57,05:09:30 +31,W,02:11:24,05:09:30 +38,W,02:11:13,05:09:31 +27,W,02:06:57,05:09:31 +47,M,02:07:19,05:09:31 +36,M,02:16:19,05:09:32 +22,W,02:15:36,05:09:32 +39,M,02:19:54,05:09:32 +26,M,02:06:43,05:09:32 +38,M,02:10:16,05:09:32 +40,M,02:04:37,05:09:32 +48,M,01:58:10,05:09:32 +25,W,02:17:44,05:09:33 +31,M,02:04:50,05:09:33 +38,W,02:07:41,05:09:33 +36,M,01:50:51,05:09:33 +49,M,02:02:56,05:09:33 +22,M,02:09:42,05:09:34 +32,M,02:16:00,05:09:34 +42,M,02:04:46,05:09:34 +46,W,02:06:42,05:09:34 +45,M,02:27:11,05:09:34 +57,M,02:11:11,05:09:35 +29,M,02:19:27,05:09:35 +36,M,02:07:46,05:09:35 +45,M,02:12:28,05:09:36 +29,M,02:07:16,05:09:36 +41,W,02:12:07,05:09:36 +39,M,02:00:58,05:09:37 +38,M,02:11:53,05:09:37 +33,M,02:08:33,05:09:37 +42,M,01:57:14,05:09:37 +40,M,02:07:14,05:09:37 +47,M,02:09:39,05:09:38 +24,M,02:08:55,05:09:38 +32,M,01:59:31,05:09:39 +32,M,02:04:35,05:09:39 +38,M,02:11:07,05:09:40 +47,M,02:14:11,05:09:40 +28,W,02:22:17,05:09:40 +45,W,02:08:22,05:09:41 +46,M,02:26:46,05:09:41 +47,W,02:08:56,05:09:41 +49,M,02:05:36,05:09:41 +22,W,02:11:02,05:09:41 +59,M,02:10:11,05:09:42 +32,W,02:10:42,05:09:42 +41,W,02:08:56,05:09:42 +50,M,02:23:46,05:09:42 +56,W,02:08:05,05:09:42 +40,M,02:09:36,05:09:42 +40,W,02:10:01,05:09:43 +52,W,02:18:56,05:09:43 +27,W,02:12:34,05:09:43 +50,W,02:03:07,05:09:43 +43,M,02:08:50,05:09:43 +39,W,02:05:57,05:09:43 +31,W,02:05:34,05:09:44 +32,M,02:08:15,05:09:44 +54,M,02:08:27,05:09:44 +36,M,02:07:31,05:09:44 +53,M,02:20:15,05:09:45 +41,W,02:10:14,05:09:45 +44,M,02:09:37,05:09:46 +50,W,02:12:02,05:09:46 +26,W,02:07:35,05:09:47 +37,M,02:05:47,05:09:47 +39,M,02:10:35,05:09:48 +43,M,02:07:10,05:09:48 +29,W,02:08:56,05:09:48 +68,M,02:06:42,05:09:48 +28,W,02:12:28,05:09:48 +40,M,02:18:06,05:09:49 +25,M,02:11:07,05:09:49 +39,M,02:15:06,05:09:49 +49,W,02:14:33,05:09:49 +41,M,02:08:16,05:09:50 +39,M,02:13:18,05:09:51 +21,M,02:13:36,05:09:51 +32,W,02:07:54,05:09:52 +41,M,02:09:49,05:09:52 +48,W,02:10:51,05:09:52 +53,W,02:08:21,05:09:53 +26,W,02:10:46,05:09:53 +30,W,02:06:14,05:09:53 +43,W,02:16:02,05:09:53 +41,W,02:16:40,05:09:53 +39,M,02:05:57,05:09:53 +41,W,02:08:46,05:09:53 +27,M,02:08:40,05:09:53 +52,M,02:20:24,05:09:53 +44,M,02:10:00,05:09:53 +43,M,02:06:47,05:09:53 +46,W,02:16:53,05:09:54 +53,M,02:08:52,05:09:54 +39,M,02:08:43,05:09:54 +54,M,02:09:39,05:09:54 +22,W,02:10:48,05:09:55 +33,M,01:56:22,05:09:55 +42,M,02:18:23,05:09:55 +30,W,02:10:28,05:09:56 +42,M,02:04:15,05:09:56 +43,M,02:03:57,05:09:56 +26,W,02:16:53,05:09:56 +44,W,02:11:04,05:09:57 +20,M,02:06:29,05:09:57 +32,W,02:19:25,05:09:57 +42,W,02:12:30,05:09:57 +48,W,02:08:34,05:09:58 +42,M,02:13:39,05:09:58 +63,M,02:09:19,05:09:58 +39,W,02:14:21,05:09:58 +48,M,02:02:09,05:09:59 +30,M,02:08:09,05:09:59 +31,M,02:10:22,05:09:59 +43,M,02:07:48,05:09:59 +66,M,01:56:15,05:09:59 +49,W,02:15:05,05:10:00 +60,M,02:07:03,05:10:00 +39,M,02:07:02,05:10:00 +53,W,02:09:53,05:10:00 +56,M,02:04:19,05:10:00 +38,M,01:58:36,05:10:01 +29,W,02:12:02,05:10:01 +55,M,02:01:35,05:10:01 +34,M,02:09:21,05:10:02 +32,W,02:14:48,05:10:02 +51,M,02:13:35,05:10:02 +51,M,02:02:22,05:10:03 +30,W,02:10:49,05:10:03 +58,M,02:19:38,05:10:03 +33,M,02:02:32,05:10:03 +34,W,02:05:10,05:10:03 +45,M,02:07:56,05:10:04 +39,M,02:05:37,05:10:04 +28,M,02:08:21,05:10:04 +57,M,02:07:42,05:10:04 +27,W,02:07:10,05:10:05 +47,M,02:04:38,05:10:05 +54,W,02:09:44,05:10:05 +50,M,02:09:50,05:10:05 +61,M,02:19:29,05:10:06 +42,M,02:14:48,05:10:06 +26,W,02:09:03,05:10:06 +28,M,02:04:33,05:10:06 +35,W,02:24:19,05:10:06 +49,M,02:11:15,05:10:07 +33,M,02:03:41,05:10:07 +36,M,02:08:07,05:10:07 +33,W,02:15:11,05:10:08 +47,M,01:59:32,05:10:08 +50,W,02:28:09,05:10:08 +36,W,02:10:17,05:10:08 +49,M,02:09:41,05:10:08 +66,M,02:05:42,05:10:08 +58,M,02:09:56,05:10:09 +51,M,02:13:52,05:10:09 +31,W,02:05:43,05:10:09 +47,W,02:08:59,05:10:09 +52,M,02:06:43,05:10:10 +37,W,02:09:53,05:10:10 +34,M,02:05:21,05:10:10 +41,W,02:27:13,05:10:11 +55,M,02:05:29,05:10:11 +44,M,02:12:22,05:10:11 +51,W,02:11:11,05:10:11 +33,W,02:08:58,05:10:12 +67,M,02:09:56,05:10:12 +53,M,02:04:07,05:10:12 +72,M,02:04:04,05:10:12 +49,W,02:12:52,05:10:13 +28,M,01:59:29,05:10:13 +54,M,02:10:38,05:10:13 +38,M,02:06:08,05:10:13 +49,W,02:13:17,05:10:13 +28,W,01:59:20,05:10:14 +59,M,02:06:51,05:10:14 +45,M,02:04:37,05:10:14 +23,W,02:16:55,05:10:15 +37,M,02:11:47,05:10:15 +30,M,02:08:50,05:10:15 +40,M,02:13:18,05:10:15 +35,W,02:03:32,05:10:15 +51,M,02:05:06,05:10:15 +51,M,02:12:34,05:10:15 +58,M,02:17:24,05:10:16 +47,M,02:03:19,05:10:16 +28,W,02:15:41,05:10:16 +55,M,02:14:55,05:10:17 +35,M,02:05:34,05:10:17 +45,M,02:10:00,05:10:17 +65,M,02:02:04,05:10:18 +54,W,02:03:28,05:10:18 +27,M,02:12:49,05:10:19 +43,W,02:07:24,05:10:20 +41,W,02:03:21,05:10:20 +55,M,02:02:35,05:10:20 +30,M,02:05:25,05:10:20 +35,M,01:59:29,05:10:21 +27,W,02:13:37,05:10:21 +37,M,01:55:12,05:10:21 +51,M,02:11:59,05:10:22 +30,W,01:59:20,05:10:22 +31,W,02:16:23,05:10:23 +29,W,02:16:53,05:10:23 +46,W,02:22:03,05:10:23 +21,M,02:11:31,05:10:24 +36,W,02:11:01,05:10:24 +40,W,02:15:10,05:10:24 +46,W,02:14:45,05:10:24 +41,W,02:12:49,05:10:24 +28,W,02:11:26,05:10:25 +37,W,02:06:53,05:10:26 +35,M,02:17:06,05:10:26 +49,M,02:12:37,05:10:26 +31,M,02:11:18,05:10:26 +23,W,02:14:12,05:10:26 +47,M,02:04:50,05:10:26 +25,W,01:56:47,05:10:27 +20,W,02:01:58,05:10:27 +36,M,02:05:56,05:10:27 +54,W,02:05:36,05:10:27 +37,W,02:10:05,05:10:27 +54,M,02:16:25,05:10:28 +42,M,02:03:06,05:10:28 +58,M,02:21:48,05:10:29 +32,M,02:22:46,05:10:29 +37,M,02:12:55,05:10:30 +54,M,02:05:18,05:10:30 +43,M,02:00:55,05:10:30 +29,W,02:15:42,05:10:31 +36,M,02:12:42,05:10:31 +41,M,02:04:53,05:10:32 +45,M,02:07:24,05:10:32 +46,M,02:12:19,05:10:32 +59,W,02:21:50,05:10:33 +36,M,02:10:30,05:10:33 +32,W,02:07:44,05:10:33 +27,M,02:03:27,05:10:33 +49,M,02:08:52,05:10:33 +50,M,02:06:35,05:10:34 +46,M,01:51:00,05:10:34 +49,M,02:03:49,05:10:34 +46,W,02:16:45,05:10:35 +33,M,02:11:46,05:10:35 +39,W,02:11:40,05:10:36 +42,M,02:09:23,05:10:37 +29,W,02:11:02,05:10:37 +54,M,02:11:26,05:10:38 +62,M,02:08:52,05:10:38 +31,W,02:13:37,05:10:38 +32,W,02:10:52,05:10:38 +45,M,02:05:20,05:10:38 +38,W,02:09:01,05:10:38 +26,M,02:10:09,05:10:38 +50,M,02:07:47,05:10:39 +43,W,02:09:38,05:10:39 +40,M,02:21:08,05:10:40 +42,W,02:23:47,05:10:40 +61,M,02:13:50,05:10:40 +54,W,02:11:16,05:10:40 +23,W,02:02:59,05:10:40 +27,M,02:07:24,05:10:41 +38,M,02:01:04,05:10:41 +41,M,02:17:44,05:10:41 +44,W,02:10:57,05:10:42 +26,W,02:08:52,05:10:42 +32,M,02:14:48,05:10:42 +33,W,02:21:29,05:10:42 +29,M,02:16:56,05:10:43 +51,M,02:17:54,05:10:43 +48,M,02:24:04,05:10:43 +38,M,02:09:16,05:10:43 +42,W,02:08:13,05:10:44 +45,M,02:12:38,05:10:44 +60,M,02:12:58,05:10:44 +40,W,02:17:11,05:10:45 +30,M,02:10:28,05:10:45 +41,W,02:15:28,05:10:45 +38,M,02:04:27,05:10:45 +42,W,02:08:26,05:10:45 +37,M,01:59:01,05:10:45 +32,M,02:02:44,05:10:46 +35,M,02:12:43,05:10:46 +44,M,02:09:41,05:10:46 +29,M,02:11:00,05:10:46 +28,W,02:08:50,05:10:46 +59,M,02:08:40,05:10:47 +51,M,02:07:10,05:10:47 +23,M,02:12:53,05:10:47 +61,M,02:11:05,05:10:48 +41,M,02:03:52,05:10:48 +34,W,02:12:54,05:10:49 +34,M,02:14:09,05:10:49 +40,W,02:09:19,05:10:50 +45,W,02:13:22,05:10:50 +31,W,02:04:11,05:10:50 +26,W,02:15:18,05:10:51 +28,W,02:12:38,05:10:51 +59,M,01:56:06,05:10:51 +36,M,02:16:22,05:10:51 +32,M,02:09:34,05:10:51 +32,W,02:07:29,05:10:51 +49,M,02:10:39,05:10:51 +37,W,02:15:46,05:10:51 +42,W,02:13:10,05:10:52 +24,M,02:11:47,05:10:52 +37,M,02:13:21,05:10:53 +57,M,02:01:24,05:10:53 +60,M,02:06:23,05:10:53 +38,M,02:11:26,05:10:53 +61,M,02:14:00,05:10:53 +36,M,02:04:37,05:10:53 +46,W,01:55:06,05:10:54 +43,W,02:15:11,05:10:55 +51,W,02:20:19,05:10:55 +31,W,02:07:36,05:10:55 +39,M,02:03:49,05:10:55 +48,W,02:04:22,05:10:56 +26,W,02:12:41,05:10:56 +55,M,02:11:05,05:10:56 +34,W,02:07:14,05:10:57 +43,M,02:04:34,05:10:57 +20,M,02:10:55,05:10:57 +47,M,02:13:59,05:10:57 +54,M,02:07:19,05:10:58 +44,M,02:09:26,05:10:58 +36,M,01:51:12,05:10:58 +58,M,02:08:56,05:10:59 +31,W,02:24:25,05:10:59 +38,M,02:09:47,05:10:59 +29,M,02:12:38,05:10:59 +30,W,02:06:02,05:10:59 +34,M,02:23:20,05:11:00 +39,M,02:07:13,05:11:00 +42,W,02:06:24,05:11:01 +37,W,02:11:48,05:11:01 +32,M,02:19:06,05:11:01 +41,W,02:15:08,05:11:01 +50,M,02:04:25,05:11:01 +39,W,02:10:21,05:11:02 +48,W,02:15:03,05:11:02 +48,M,02:05:22,05:11:02 +25,W,02:03:45,05:11:03 +30,M,01:56:10,05:11:03 +40,M,02:10:51,05:11:04 +34,M,02:09:23,05:11:04 +44,M,02:02:04,05:11:04 +31,M,02:01:31,05:11:05 +38,M,02:15:14,05:11:05 +55,M,02:19:36,05:11:05 +43,M,02:11:50,05:11:05 +45,M,02:07:21,05:11:06 +26,M,02:09:49,05:11:06 +28,W,02:13:28,05:11:06 +21,M,02:10:26,05:11:07 +57,M,02:07:39,05:11:07 +39,W,02:13:24,05:11:07 +35,M,02:08:20,05:11:07 +59,W,02:07:42,05:11:07 +36,M,02:15:24,05:11:07 +53,M,02:07:56,05:11:07 +42,W,02:01:33,05:11:08 +41,W,02:23:32,05:11:08 +38,M,02:20:41,05:11:08 +49,M,02:15:06,05:11:08 +36,W,02:13:38,05:11:09 +47,M,02:14:03,05:11:10 +32,W,02:07:01,05:11:10 +53,M,02:09:34,05:11:10 +36,W,02:24:40,05:11:11 +35,M,02:01:58,05:11:11 +41,M,01:56:29,05:11:11 +24,W,02:12:22,05:11:11 +60,M,02:15:09,05:11:11 +30,M,02:12:50,05:11:11 +38,M,02:00:13,05:11:12 +56,M,02:07:32,05:11:12 +42,M,02:09:14,05:11:12 +28,M,02:09:36,05:11:12 +39,M,02:13:59,05:11:13 +53,W,02:08:37,05:11:14 +41,M,02:11:31,05:11:14 +37,W,02:11:14,05:11:14 +57,M,02:09:28,05:11:15 +30,W,02:16:20,05:11:15 +46,M,02:09:56,05:11:15 +41,M,02:07:32,05:11:15 +40,W,02:14:10,05:11:16 +52,M,02:20:50,05:11:16 +33,W,02:11:32,05:11:17 +38,W,02:09:53,05:11:18 +32,W,02:10:59,05:11:18 +33,M,02:10:20,05:11:18 +37,M,02:02:01,05:11:18 +42,W,02:10:32,05:11:19 +53,M,02:23:23,05:11:19 +51,M,02:18:28,05:11:19 +49,M,02:02:54,05:11:19 +39,M,02:07:52,05:11:19 +26,W,02:14:01,05:11:19 +31,M,01:58:16,05:11:19 +39,M,02:02:48,05:11:19 +27,W,01:59:27,05:11:19 +31,W,02:05:36,05:11:19 +33,M,02:11:35,05:11:20 +27,M,02:11:45,05:11:20 +40,M,02:04:04,05:11:21 +42,W,02:11:26,05:11:21 +54,M,02:16:08,05:11:21 +39,W,02:15:20,05:11:21 +47,M,02:04:47,05:11:22 +46,W,02:09:07,05:11:22 +46,M,02:05:04,05:11:22 +50,M,02:09:14,05:11:22 +41,M,02:11:39,05:11:23 +44,M,02:16:49,05:11:23 +49,M,02:09:49,05:11:23 +53,M,02:16:55,05:11:23 +25,W,02:06:28,05:11:23 +28,W,02:11:03,05:11:23 +31,W,02:14:14,05:11:24 +54,M,02:12:21,05:11:24 +52,W,02:12:03,05:11:25 +24,M,01:52:52,05:11:25 +29,M,02:00:15,05:11:25 +47,M,02:06:47,05:11:25 +36,W,02:12:50,05:11:26 +49,M,02:05:39,05:11:26 +49,M,02:10:14,05:11:26 +38,M,02:14:14,05:11:26 +59,W,02:21:21,05:11:26 +22,W,02:09:15,05:11:26 +33,W,02:10:31,05:11:27 +30,M,02:06:24,05:11:27 +34,M,02:08:25,05:11:28 +29,M,02:17:26,05:11:28 +42,M,02:10:25,05:11:29 +55,W,02:07:38,05:11:29 +54,M,02:03:42,05:11:30 +42,M,02:09:33,05:11:30 +31,W,01:54:31,05:11:30 +49,W,02:10:56,05:11:30 +33,M,02:12:27,05:11:31 +58,M,02:08:21,05:11:31 +44,M,01:54:32,05:11:31 +58,M,02:15:18,05:11:31 +27,W,02:07:25,05:11:31 +35,W,02:07:33,05:11:31 +35,W,02:14:00,05:11:31 +59,W,02:12:51,05:11:33 +43,W,02:06:45,05:11:33 +30,W,02:07:12,05:11:33 +51,M,02:09:06,05:11:33 +52,M,02:04:45,05:11:33 +36,M,02:10:54,05:11:34 +39,M,02:02:27,05:11:35 +61,M,02:13:15,05:11:35 +52,M,02:03:24,05:11:36 +28,W,02:13:12,05:11:36 +44,M,02:05:20,05:11:37 +61,M,02:22:39,05:11:37 +34,W,02:07:13,05:11:37 +47,M,02:09:58,05:11:37 +60,W,02:11:01,05:11:38 +37,W,02:11:07,05:11:38 +26,M,02:08:36,05:11:39 +30,M,02:17:03,05:11:39 +39,M,02:09:04,05:11:39 +31,W,02:11:36,05:11:39 +25,M,02:06:44,05:11:39 +50,W,02:10:44,05:11:40 +36,W,02:14:47,05:11:41 +51,M,02:00:15,05:11:41 +51,M,02:30:03,05:11:41 +53,W,02:06:01,05:11:41 +43,M,02:17:40,05:11:41 +27,W,02:10:59,05:11:41 +50,M,02:02:03,05:11:41 +60,W,02:13:45,05:11:42 +36,W,02:18:18,05:11:42 +22,W,02:04:41,05:11:42 +48,M,02:09:43,05:11:42 +48,M,02:07:50,05:11:42 +32,M,02:08:44,05:11:43 +38,M,02:12:01,05:11:43 +27,M,02:07:29,05:11:43 +40,M,02:10:09,05:11:44 +32,W,02:04:19,05:11:44 +37,M,02:10:00,05:11:44 +36,M,02:11:40,05:11:44 +44,W,02:13:20,05:11:45 +17,M,02:15:51,05:11:46 +43,M,02:11:10,05:11:46 +46,W,02:09:30,05:11:46 +45,W,02:15:00,05:11:46 +25,W,02:16:11,05:11:47 +44,M,02:08:27,05:11:47 +50,M,02:14:45,05:11:47 +43,W,02:16:50,05:11:47 +41,M,02:06:14,05:11:47 +31,M,02:14:03,05:11:47 +49,W,02:03:35,05:11:48 +55,M,02:09:37,05:11:48 +39,M,02:09:06,05:11:48 +19,W,02:10:19,05:11:48 +25,W,02:09:59,05:11:48 +52,M,02:05:12,05:11:48 +31,W,02:13:55,05:11:49 +55,M,02:05:27,05:11:49 +48,W,02:25:52,05:11:49 +52,M,02:19:37,05:11:49 +17,W,01:58:37,05:11:49 +46,M,02:08:00,05:11:49 +52,M,02:16:11,05:11:50 +49,M,02:05:44,05:11:50 +41,W,02:15:42,05:11:50 +40,M,02:04:02,05:11:50 +58,M,02:02:48,05:11:52 +28,W,02:05:17,05:11:52 +33,W,02:16:06,05:11:52 +38,W,02:02:44,05:11:53 +31,W,02:13:23,05:11:53 +28,W,02:06:30,05:11:53 +31,W,02:11:33,05:11:53 +35,W,02:15:16,05:11:54 +32,W,02:11:07,05:11:54 +47,M,02:10:33,05:11:54 +39,W,02:19:24,05:11:55 +56,M,02:02:36,05:11:56 +45,M,02:17:37,05:11:56 +32,W,02:12:39,05:11:56 +27,W,02:15:09,05:11:56 +33,W,02:12:09,05:11:57 +41,M,02:15:39,05:11:57 +54,W,02:13:14,05:11:58 +31,M,02:03:11,05:11:58 +42,W,02:04:20,05:11:58 +39,W,02:16:03,05:11:58 +23,M,01:55:58,05:11:59 +49,M,02:13:48,05:11:59 +37,M,01:56:30,05:11:59 +28,W,02:14:42,05:11:59 +41,W,02:22:00,05:11:59 +34,M,01:58:18,05:11:59 +22,W,02:13:35,05:12:00 +26,M,02:03:52,05:12:00 +26,M,02:12:32,05:12:01 +18,W,02:06:54,05:12:01 +57,W,02:08:10,05:12:01 +46,W,02:11:35,05:12:01 +46,M,02:07:59,05:12:01 +28,W,02:13:15,05:12:02 +37,W,02:08:31,05:12:03 +31,W,02:07:57,05:12:03 +50,W,02:13:12,05:12:04 +41,W,02:08:31,05:12:04 +55,M,02:08:05,05:12:05 +48,W,02:09:12,05:12:05 +27,W,02:13:09,05:12:05 +30,M,02:16:41,05:12:05 +43,M,02:00:09,05:12:06 +66,M,02:14:43,05:12:06 +63,M,02:07:41,05:12:06 +42,W,02:15:55,05:12:07 +34,W,02:10:24,05:12:08 +23,M,01:59:34,05:12:08 +27,W,02:13:46,05:12:08 +29,M,02:12:26,05:12:09 +29,W,02:09:12,05:12:09 +29,W,02:12:52,05:12:10 +65,M,02:17:38,05:12:10 +45,M,02:18:36,05:12:11 +32,W,02:16:11,05:12:11 +61,W,02:08:01,05:12:11 +31,M,02:15:01,05:12:11 +28,W,02:07:04,05:12:11 +49,W,02:11:34,05:12:11 +32,M,02:19:54,05:12:11 +26,W,02:29:22,05:12:12 +39,W,02:01:36,05:12:12 +42,M,02:09:44,05:12:12 +39,W,02:10:43,05:12:12 +40,W,02:19:20,05:12:12 +48,W,02:11:06,05:12:13 +49,W,02:14:15,05:12:13 +29,M,02:15:42,05:12:13 +34,W,02:14:19,05:12:13 +32,M,02:12:12,05:12:13 +45,W,02:19:17,05:12:13 +26,W,02:10:12,05:12:13 +45,M,02:04:42,05:12:13 +36,M,02:11:54,05:12:13 +37,M,02:14:32,05:12:14 +28,W,02:08:38,05:12:14 +57,M,02:14:21,05:12:15 +47,M,01:59:35,05:12:15 +41,M,02:00:26,05:12:15 +34,W,02:08:39,05:12:15 +45,M,02:18:50,05:12:15 +42,M,02:06:44,05:12:16 +43,W,02:11:23,05:12:17 +64,M,02:06:25,05:12:17 +51,W,02:04:15,05:12:17 +39,M,02:12:10,05:12:18 +35,M,02:03:34,05:12:18 +38,M,02:10:14,05:12:18 +30,M,02:07:29,05:12:18 +44,W,02:05:57,05:12:18 +45,M,02:15:05,05:12:18 +45,M,01:57:41,05:12:19 +26,M,02:10:41,05:12:19 +38,W,02:18:51,05:12:19 +55,M,02:11:47,05:12:19 +35,M,02:16:13,05:12:19 +57,M,02:11:08,05:12:19 +37,W,02:06:56,05:12:20 +27,W,02:10:44,05:12:20 +38,M,02:17:30,05:12:20 +36,W,02:07:47,05:12:20 +44,M,02:17:33,05:12:20 +30,W,02:09:40,05:12:20 +44,M,02:07:39,05:12:21 +29,M,02:10:11,05:12:21 +37,M,01:57:16,05:12:22 +40,W,02:06:29,05:12:22 +45,M,02:05:05,05:12:22 +54,M,02:06:35,05:12:22 +30,W,02:16:41,05:12:22 +37,M,02:08:47,05:12:22 +37,M,02:06:35,05:12:23 +33,W,02:14:32,05:12:23 +51,W,02:05:06,05:12:23 +47,W,02:15:10,05:12:23 +22,M,01:55:45,05:12:24 +57,M,02:30:55,05:12:24 +55,M,02:08:11,05:12:24 +26,M,02:15:22,05:12:24 +28,W,02:04:36,05:12:24 +41,M,02:13:17,05:12:24 +50,W,02:12:01,05:12:24 +43,M,02:10:43,05:12:24 +47,M,02:05:04,05:12:24 +35,M,02:13:30,05:12:24 +30,M,02:13:24,05:12:25 +34,W,02:08:43,05:12:25 +23,M,01:59:08,05:12:25 +39,W,02:14:54,05:12:26 +36,M,02:00:37,05:12:27 +57,M,02:11:11,05:12:27 +37,W,02:12:47,05:12:27 +38,M,01:57:52,05:12:27 +41,M,02:08:33,05:12:27 +52,W,02:14:49,05:12:27 +36,M,02:02:31,05:12:28 +41,M,02:31:18,05:12:28 +32,M,02:10:43,05:12:28 +30,W,02:08:10,05:12:28 +41,M,02:16:18,05:12:28 +27,M,02:13:57,05:12:29 +23,M,01:56:33,05:12:29 +26,M,02:17:26,05:12:29 +52,M,02:03:45,05:12:29 +60,M,02:05:40,05:12:30 +39,M,02:17:43,05:12:30 +38,M,02:13:26,05:12:31 +49,M,02:02:34,05:12:31 +46,M,02:08:44,05:12:31 +52,M,01:58:22,05:12:32 +34,M,02:03:17,05:12:32 +42,M,02:22:31,05:12:32 +39,M,02:16:20,05:12:32 +40,M,02:10:53,05:12:32 +44,M,02:07:30,05:12:32 +53,W,02:22:27,05:12:32 +38,W,02:12:06,05:12:33 +51,M,02:00:14,05:12:33 +57,W,02:15:13,05:12:33 +54,M,02:14:31,05:12:33 +50,W,02:07:32,05:12:33 +46,M,02:00:37,05:12:33 +29,M,02:13:58,05:12:33 +39,W,02:09:23,05:12:34 +31,M,01:56:30,05:12:34 +27,M,02:00:32,05:12:34 +45,M,01:57:17,05:12:35 +31,W,02:16:52,05:12:35 +44,M,02:10:01,05:12:36 +47,M,02:09:56,05:12:36 +35,M,02:04:19,05:12:36 +55,W,02:10:33,05:12:37 +42,W,02:15:33,05:12:37 +33,M,02:06:54,05:12:37 +42,M,02:17:45,05:12:38 +40,W,02:04:22,05:12:38 +36,M,02:04:56,05:12:39 +31,M,02:10:00,05:12:39 +46,M,02:21:23,05:12:39 +41,M,02:09:29,05:12:40 +64,M,02:15:32,05:12:40 +44,W,02:08:03,05:12:40 +40,M,02:24:13,05:12:40 +33,M,02:06:22,05:12:40 +33,W,02:14:06,05:12:41 +26,W,02:11:46,05:12:41 +46,W,02:18:31,05:12:41 +48,M,02:01:04,05:12:41 +40,M,01:58:00,05:12:41 +39,W,02:13:21,05:12:41 +33,M,02:00:39,05:12:41 +34,W,02:11:58,05:12:42 +28,W,02:16:46,05:12:43 +41,M,02:16:02,05:12:43 +31,W,02:03:27,05:12:43 +36,M,02:04:19,05:12:44 +21,M,02:04:35,05:12:44 +40,M,02:07:06,05:12:45 +26,M,02:03:03,05:12:45 +34,W,02:12:36,05:12:45 +43,M,02:05:16,05:12:45 +35,M,02:10:19,05:12:45 +50,W,02:21:54,05:12:45 +56,M,02:13:04,05:12:45 +41,M,02:17:07,05:12:46 +47,W,02:15:47,05:12:46 +53,W,02:08:00,05:12:46 +39,M,02:12:11,05:12:46 +42,M,02:11:26,05:12:47 +30,W,02:17:07,05:12:48 +69,M,01:58:22,05:12:48 +51,W,02:09:51,05:12:49 +45,W,02:04:03,05:12:49 +42,W,02:17:13,05:12:49 +30,M,02:11:41,05:12:49 +42,M,02:09:46,05:12:49 +35,W,02:09:23,05:12:49 +49,W,02:05:58,05:12:50 +40,M,02:06:14,05:12:50 +30,W,02:14:43,05:12:51 +48,W,02:21:44,05:12:51 +37,M,02:12:40,05:12:51 +50,M,02:05:54,05:12:51 +44,M,02:08:52,05:12:52 +37,W,02:14:48,05:12:52 +35,M,02:12:55,05:12:52 +48,W,02:13:16,05:12:52 +43,M,02:13:28,05:12:53 +41,M,02:02:18,05:12:53 +27,W,02:10:55,05:12:53 +50,M,02:13:59,05:12:53 +21,M,02:02:50,05:12:53 +47,M,02:09:57,05:12:53 +34,W,02:08:02,05:12:54 +39,W,02:05:11,05:12:54 +30,W,02:09:02,05:12:55 +36,W,02:20:17,05:12:56 +40,M,01:53:16,05:12:56 +45,M,02:15:18,05:12:56 +51,M,02:10:32,05:12:56 +34,M,02:06:05,05:12:57 +48,M,02:14:10,05:12:57 +28,M,02:05:31,05:12:57 +49,M,02:05:56,05:12:58 +46,M,02:19:44,05:12:58 +26,W,02:12:35,05:12:58 +40,M,02:09:07,05:12:58 +27,W,02:13:34,05:12:58 +38,M,02:15:47,05:12:58 +27,W,02:12:10,05:12:59 +37,M,01:55:38,05:13:00 +34,M,02:05:39,05:13:00 +29,W,02:18:15,05:13:01 +49,M,02:02:30,05:13:01 +41,M,02:18:39,05:13:01 +44,M,01:53:55,05:13:01 +50,M,02:06:18,05:13:01 +29,W,02:12:33,05:13:02 +45,M,02:16:34,05:13:02 +41,W,02:12:33,05:13:02 +55,W,02:08:41,05:13:02 +34,M,02:05:47,05:13:02 +38,W,02:14:48,05:13:03 +67,M,02:13:35,05:13:03 +33,W,02:18:36,05:13:03 +52,M,02:13:16,05:13:04 +37,W,02:06:27,05:13:04 +45,M,02:05:17,05:13:04 +30,W,02:11:35,05:13:05 +62,M,02:11:51,05:13:05 +37,W,02:12:52,05:13:05 +61,M,02:16:08,05:13:05 +44,M,02:15:36,05:13:05 +33,W,02:16:09,05:13:06 +36,W,02:10:12,05:13:06 +50,M,02:11:00,05:13:07 +25,W,02:12:32,05:13:07 +24,M,02:14:37,05:13:07 +45,W,02:08:26,05:13:07 +65,M,02:09:55,05:13:07 +61,M,02:10:06,05:13:07 +37,M,02:10:50,05:13:07 +31,M,01:59:47,05:13:07 +51,W,02:18:16,05:13:08 +29,W,02:12:19,05:13:08 +41,M,02:16:18,05:13:08 +37,W,02:08:34,05:13:08 +55,M,02:13:53,05:13:09 +25,W,02:09:04,05:13:09 +43,W,02:16:53,05:13:09 +25,W,02:07:50,05:13:10 +29,W,02:13:05,05:13:10 +41,W,01:59:35,05:13:10 +26,W,02:02:58,05:13:10 +44,W,02:07:28,05:13:10 +27,W,02:09:30,05:13:11 +30,M,02:14:09,05:13:11 +39,M,02:13:47,05:13:12 +32,M,02:08:50,05:13:12 +64,M,02:10:45,05:13:13 +34,M,02:20:25,05:13:13 +39,W,02:16:11,05:13:14 +27,W,02:10:15,05:13:14 +27,W,02:26:47,05:13:15 +56,M,02:02:05,05:13:15 +34,M,02:16:13,05:13:15 +25,W,02:07:04,05:13:15 +35,M,02:11:37,05:13:15 +28,M,02:13:03,05:13:15 +44,W,02:12:39,05:13:16 +29,W,02:11:14,05:13:16 +42,W,02:07:52,05:13:16 +35,W,02:11:59,05:13:16 +52,M,02:08:56,05:13:16 +37,M,02:16:30,05:13:16 +43,M,02:22:00,05:13:16 +42,M,02:13:55,05:13:17 +54,M,02:04:31,05:13:17 +46,W,02:06:21,05:13:17 +40,M,02:09:35,05:13:17 +34,M,02:03:22,05:13:18 +25,W,02:07:57,05:13:19 +55,M,02:05:58,05:13:19 +59,M,02:13:31,05:13:19 +35,W,02:10:16,05:13:19 +57,M,02:05:40,05:13:19 +30,W,02:12:57,05:13:20 +55,M,02:09:38,05:13:20 +33,W,02:15:55,05:13:20 +36,M,02:10:19,05:13:21 +46,W,02:14:17,05:13:21 +46,M,02:12:06,05:13:21 +39,M,02:11:08,05:13:21 +47,M,01:56:58,05:13:22 +28,M,02:13:43,05:13:22 +30,M,02:13:53,05:13:23 +35,W,02:08:28,05:13:23 +53,M,02:22:00,05:13:23 +18,M,02:13:52,05:13:24 +32,W,02:09:37,05:13:24 +34,W,02:12:37,05:13:24 +45,M,02:08:02,05:13:24 +43,W,02:25:21,05:13:24 +42,W,02:16:10,05:13:25 +52,M,02:10:08,05:13:25 +51,M,02:11:50,05:13:25 +41,M,02:12:46,05:13:25 +49,M,02:09:06,05:13:25 +35,M,02:15:00,05:13:25 +41,W,02:10:22,05:13:25 +46,M,02:21:43,05:13:26 +41,W,02:10:02,05:13:26 +47,M,02:03:58,05:13:26 +37,W,02:12:50,05:13:26 +37,M,02:13:58,05:13:26 +46,W,02:11:54,05:13:27 +42,M,02:08:14,05:13:27 +31,W,02:06:04,05:13:27 +53,M,02:11:43,05:13:27 +36,M,02:08:05,05:13:27 +29,W,02:12:33,05:13:28 +62,M,02:11:20,05:13:28 +38,M,02:12:39,05:13:28 +26,W,02:09:06,05:13:28 +50,M,02:09:44,05:13:28 +46,W,02:08:49,05:13:28 +43,M,02:12:31,05:13:28 +52,M,02:10:57,05:13:29 +37,W,02:19:51,05:13:29 +28,W,02:13:07,05:13:29 +41,M,02:14:31,05:13:29 +43,M,02:04:39,05:13:29 +41,M,02:00:02,05:13:30 +29,M,02:17:48,05:13:30 +32,M,02:14:25,05:13:31 +52,M,02:07:01,05:13:32 +22,M,02:02:59,05:13:32 +43,M,02:09:09,05:13:32 +29,M,02:05:03,05:13:32 +29,M,02:17:25,05:13:32 +61,W,02:08:50,05:13:32 +24,W,02:05:00,05:13:33 +50,M,02:06:55,05:13:34 +33,W,02:12:07,05:13:35 +57,W,02:06:20,05:13:35 +40,W,02:02:28,05:13:35 +38,M,02:07:07,05:13:35 +56,W,02:16:13,05:13:35 +32,M,02:22:06,05:13:36 +30,W,02:12:01,05:13:36 +40,M,02:05:52,05:13:36 +31,W,02:17:16,05:13:37 +28,M,02:01:39,05:13:37 +46,W,02:14:26,05:13:37 +50,W,02:08:25,05:13:38 +50,W,02:06:03,05:13:38 +41,M,02:10:45,05:13:38 +46,W,02:09:13,05:13:38 +27,W,02:06:48,05:13:38 +46,M,02:13:54,05:13:38 +45,M,02:10:14,05:13:38 +31,M,02:04:10,05:13:39 +50,M,02:15:21,05:13:39 +35,M,02:16:28,05:13:39 +26,M,02:13:13,05:13:39 +29,M,02:10:18,05:13:39 +34,W,02:13:24,05:13:39 +49,M,02:03:52,05:13:39 +51,M,01:58:50,05:13:40 +45,M,02:17:01,05:13:40 +31,M,02:12:49,05:13:40 +45,M,02:04:59,05:13:41 +38,M,02:11:47,05:13:41 +58,M,02:07:51,05:13:41 +48,M,02:06:23,05:13:41 +34,W,02:12:34,05:13:41 +39,M,02:04:23,05:13:41 +27,W,02:15:20,05:13:42 +36,M,02:06:02,05:13:43 +39,W,02:17:40,05:13:43 +43,M,02:05:02,05:13:43 +65,M,02:12:55,05:13:43 +56,M,02:20:40,05:13:43 +50,M,02:08:44,05:13:44 +49,M,02:06:37,05:13:44 +46,M,02:18:10,05:13:44 +20,W,02:15:45,05:13:44 +54,M,02:09:01,05:13:44 +39,W,02:15:36,05:13:45 +40,W,02:17:37,05:13:45 +39,M,02:16:03,05:13:45 +38,W,02:14:35,05:13:45 +31,M,02:13:45,05:13:46 +40,M,02:12:40,05:13:46 +30,W,02:18:01,05:13:46 +39,M,02:15:43,05:13:46 +31,M,02:12:53,05:13:47 +41,M,02:13:11,05:13:47 +45,M,02:09:25,05:13:47 +54,M,02:08:24,05:13:47 +43,M,02:06:17,05:13:47 +39,W,02:08:11,05:13:47 +37,W,01:49:35,05:13:47 +41,M,02:07:48,05:13:47 +48,M,01:56:39,05:13:48 +49,W,02:22:37,05:13:49 +45,W,02:10:30,05:13:49 +38,W,02:09:41,05:13:50 +26,W,02:14:08,05:13:50 +54,M,02:09:17,05:13:50 +49,M,02:08:19,05:13:50 +57,M,02:32:23,05:13:50 +51,W,02:11:37,05:13:51 +46,M,02:10:24,05:13:51 +42,W,02:21:53,05:13:51 +35,M,02:08:08,05:13:52 +50,M,02:09:09,05:13:52 +40,M,02:05:24,05:13:53 +42,M,02:13:40,05:13:53 +29,W,02:12:33,05:13:53 +32,W,02:12:40,05:13:53 +48,M,02:11:25,05:13:53 +53,M,02:01:28,05:13:54 +34,W,02:04:04,05:13:54 +45,M,01:55:39,05:13:54 +32,W,02:15:16,05:13:54 +47,M,02:06:08,05:13:55 +62,M,02:07:49,05:13:55 +26,M,02:02:45,05:13:56 +44,W,02:12:14,05:13:56 +29,W,02:23:01,05:13:56 +44,M,02:13:29,05:13:56 +37,W,02:12:44,05:13:56 +48,M,02:15:35,05:13:57 +36,M,02:12:26,05:13:57 +46,W,02:11:19,05:13:57 +30,W,02:07:38,05:13:57 +59,M,02:07:57,05:13:57 +27,W,02:10:37,05:13:57 +36,M,02:04:37,05:13:57 +40,W,02:06:59,05:13:57 +39,W,02:10:12,05:13:58 +30,M,02:09:33,05:13:58 +48,M,02:18:18,05:13:58 +22,M,01:56:14,05:13:59 +38,M,02:06:20,05:13:59 +59,W,02:13:29,05:13:59 +47,M,02:08:58,05:14:00 +51,M,01:59:56,05:14:00 +47,M,02:10:36,05:14:00 +28,W,01:58:05,05:14:00 +53,W,02:09:02,05:14:00 +48,M,02:12:03,05:14:01 +27,W,02:14:20,05:14:01 +39,M,02:00:25,05:14:01 +33,M,02:10:48,05:14:01 +23,W,02:14:27,05:14:01 +52,M,02:05:07,05:14:01 +41,M,02:11:18,05:14:01 +30,M,02:01:57,05:14:02 +35,M,02:14:38,05:14:02 +50,W,02:12:10,05:14:03 +48,M,01:58:20,05:14:03 +47,M,02:11:23,05:14:04 +25,W,02:14:33,05:14:04 +33,M,02:05:59,05:14:04 +44,M,02:14:56,05:14:04 +44,W,02:08:54,05:14:04 +36,W,02:06:44,05:14:04 +56,M,02:20:12,05:14:05 +57,W,02:12:51,05:14:05 +31,M,02:15:14,05:14:06 +36,W,02:21:22,05:14:06 +31,W,02:12:44,05:14:06 +45,M,02:17:33,05:14:07 +37,W,02:25:34,05:14:07 +43,M,02:13:14,05:14:07 +34,W,02:18:17,05:14:08 +42,M,02:19:33,05:14:08 +43,M,01:59:27,05:14:08 +33,W,02:16:27,05:14:08 +40,M,02:20:02,05:14:08 +29,M,02:08:08,05:14:09 +25,W,02:17:30,05:14:09 +51,M,02:14:04,05:14:09 +34,W,02:09:00,05:14:09 +40,M,02:06:12,05:14:09 +49,M,02:16:35,05:14:09 +35,M,02:10:44,05:14:10 +32,M,02:08:50,05:14:10 +42,W,02:16:26,05:14:10 +27,M,02:20:27,05:14:11 +65,W,02:15:53,05:14:12 +26,M,02:14:14,05:14:12 +33,W,02:07:09,05:14:12 +38,W,02:10:16,05:14:12 +30,W,02:14:57,05:14:13 +27,W,02:14:07,05:14:13 +54,W,02:08:59,05:14:13 +36,M,02:19:11,05:14:14 +28,W,02:10:55,05:14:14 +51,M,01:53:19,05:14:14 +29,W,02:27:33,05:14:14 +36,M,02:05:00,05:14:15 +46,M,02:09:37,05:14:15 +30,W,02:13:03,05:14:15 +51,M,02:02:13,05:14:15 +28,W,02:12:59,05:14:15 +43,M,02:13:05,05:14:16 +41,W,02:12:05,05:14:16 +35,M,02:07:55,05:14:16 +35,W,02:18:38,05:14:17 +68,W,02:09:41,05:14:17 +30,W,02:10:09,05:14:17 +28,M,02:03:42,05:14:17 +37,W,02:19:34,05:14:18 +28,M,02:11:46,05:14:18 +37,W,02:08:35,05:14:18 +23,W,02:21:24,05:14:18 +47,M,02:04:56,05:14:18 +29,W,02:16:03,05:14:19 +44,W,02:09:05,05:14:19 +51,M,02:06:15,05:14:19 +37,W,02:20:20,05:14:20 +61,W,02:22:31,05:14:20 +49,W,02:12:01,05:14:20 +29,M,02:12:02,05:14:20 +44,M,02:10:57,05:14:20 +42,M,02:13:21,05:14:20 +39,W,02:05:12,05:14:20 +24,M,02:08:11,05:14:21 +41,M,02:07:57,05:14:22 +20,W,02:23:41,05:14:22 +79,M,02:06:29,05:14:22 +43,M,02:20:04,05:14:22 +51,M,02:17:48,05:14:22 +18,M,02:24:13,05:14:22 +36,M,02:03:04,05:14:23 +42,M,02:06:23,05:14:23 +36,W,02:12:53,05:14:23 +47,M,02:12:26,05:14:23 +32,M,02:20:52,05:14:23 +30,W,02:12:47,05:14:24 +34,W,02:18:28,05:14:24 +32,M,02:20:07,05:14:24 +33,W,02:11:26,05:14:24 +46,M,02:06:36,05:14:25 +35,M,02:15:51,05:14:25 +29,M,02:16:21,05:14:25 +39,W,02:18:34,05:14:25 +43,W,02:05:29,05:14:25 +63,M,02:00:20,05:14:26 +42,W,02:15:37,05:14:26 +50,W,02:31:02,05:14:26 +53,M,02:18:06,05:14:27 +30,M,02:16:26,05:14:27 +24,M,02:06:39,05:14:27 +35,M,02:21:22,05:14:28 +55,M,02:08:37,05:14:28 +41,W,02:07:36,05:14:29 +49,M,02:20:28,05:14:29 +45,W,02:20:27,05:14:29 +51,M,02:10:10,05:14:29 +29,W,02:07:43,05:14:29 +52,M,02:08:45,05:14:29 +51,M,02:03:47,05:14:30 +30,M,02:11:31,05:14:30 +35,W,02:11:24,05:14:30 +30,W,02:20:08,05:14:30 +26,M,02:13:54,05:14:31 +35,M,02:16:44,05:14:31 +43,M,02:12:38,05:14:32 +31,M,02:05:38,05:14:32 +35,M,02:19:43,05:14:32 +28,M,02:08:42,05:14:32 +58,M,02:21:26,05:14:32 +36,W,02:15:01,05:14:32 +22,M,02:14:43,05:14:33 +62,M,02:16:49,05:14:33 +42,W,02:17:44,05:14:34 +55,M,02:22:19,05:14:34 +66,M,02:24:42,05:14:35 +44,W,02:27:47,05:14:35 +49,W,02:16:12,05:14:35 +53,W,02:13:24,05:14:37 +61,M,02:14:20,05:14:37 +23,W,02:13:05,05:14:37 +52,M,02:11:25,05:14:37 +25,W,02:06:17,05:14:38 +52,M,02:18:24,05:14:38 +26,W,02:11:05,05:14:39 +36,W,02:11:51,05:14:39 +40,M,02:15:38,05:14:39 +53,M,02:07:22,05:14:39 +26,M,02:09:07,05:14:39 +51,W,02:22:35,05:14:40 +51,M,02:12:36,05:14:40 +46,M,02:03:51,05:14:40 +31,M,02:06:19,05:14:41 +63,M,02:06:17,05:14:41 +20,M,02:01:19,05:14:42 +26,W,02:11:49,05:14:42 +45,W,02:15:51,05:14:42 +50,M,02:09:11,05:14:42 +33,W,02:16:04,05:14:42 +40,W,02:11:31,05:14:43 +33,W,02:08:04,05:14:43 +37,W,02:12:27,05:14:43 +41,M,02:04:31,05:14:43 +60,M,02:20:36,05:14:43 +40,W,02:11:09,05:14:43 +51,M,02:03:22,05:14:43 +34,W,02:07:54,05:14:44 +37,W,02:08:19,05:14:44 +38,M,02:06:35,05:14:44 +60,M,02:18:44,05:14:44 +33,W,02:11:57,05:14:45 +45,W,02:19:46,05:14:45 +59,M,02:14:26,05:14:47 +36,W,02:14:31,05:14:47 +37,M,01:55:37,05:14:47 +37,W,02:20:25,05:14:47 +56,W,02:02:26,05:14:47 +36,M,02:07:47,05:14:48 +46,M,02:13:38,05:14:48 +34,W,02:22:31,05:14:48 +64,M,02:07:56,05:14:49 +26,W,02:10:00,05:14:49 +41,M,02:10:35,05:14:49 +50,W,02:11:13,05:14:49 +60,M,02:11:19,05:14:50 +37,W,02:12:58,05:14:50 +42,M,02:11:08,05:14:50 +62,M,02:16:10,05:14:50 +56,M,02:23:46,05:14:50 +29,M,02:09:38,05:14:51 +29,W,02:09:55,05:14:52 +49,M,02:05:14,05:14:53 +50,M,02:01:11,05:14:54 +37,W,02:15:44,05:14:54 +62,M,02:11:10,05:14:54 +46,W,02:05:49,05:14:54 +37,M,02:05:10,05:14:54 +40,M,02:16:52,05:14:54 +44,W,02:13:05,05:14:55 +42,M,02:04:51,05:14:55 +50,W,02:10:33,05:14:56 +39,M,02:07:03,05:14:56 +39,M,02:15:30,05:14:57 +48,M,02:19:03,05:14:58 +43,M,02:01:20,05:14:58 +39,W,02:10:34,05:14:58 +30,W,02:13:45,05:14:58 +36,M,02:21:26,05:14:58 +44,M,02:11:01,05:14:59 +24,M,02:20:43,05:14:59 +30,W,02:14:31,05:14:59 +26,M,02:03:01,05:14:59 +46,M,02:04:36,05:14:59 +27,W,02:07:49,05:14:59 +61,W,02:04:26,05:15:00 +51,M,02:02:54,05:15:00 +41,M,02:15:24,05:15:00 +20,M,01:54:53,05:15:01 +35,W,02:01:00,05:15:01 +48,W,02:14:28,05:15:01 +53,M,02:02:51,05:15:01 +31,M,01:38:44,05:15:02 +61,W,02:16:23,05:15:02 +64,M,02:06:46,05:15:02 +42,W,02:12:50,05:15:02 +40,M,01:56:15,05:15:02 +61,M,02:07:09,05:15:03 +48,W,02:04:20,05:15:03 +25,W,02:07:56,05:15:03 +45,M,02:01:40,05:15:03 +26,M,02:12:20,05:15:04 +47,M,01:57:25,05:15:04 +46,M,02:10:07,05:15:04 +19,M,02:05:33,05:15:04 +45,W,02:11:09,05:15:04 +46,W,02:10:45,05:15:04 +27,M,02:01:00,05:15:04 +46,M,02:21:57,05:15:05 +38,W,02:08:40,05:15:05 +50,W,02:10:04,05:15:05 +28,M,02:10:00,05:15:05 +37,M,02:26:24,05:15:05 +41,M,02:15:33,05:15:06 +59,M,02:14:16,05:15:06 +32,W,02:16:43,05:15:06 +44,M,02:03:07,05:15:06 +27,W,02:08:54,05:15:06 +40,M,02:15:06,05:15:07 +36,W,02:13:29,05:15:07 +43,M,02:03:03,05:15:07 +36,M,02:08:18,05:15:07 +47,M,01:55:04,05:15:08 +53,M,02:16:36,05:15:08 +28,M,02:20:49,05:15:08 +34,M,02:13:01,05:15:08 +29,M,02:21:14,05:15:08 +26,M,02:11:39,05:15:08 +35,M,02:11:08,05:15:09 +48,M,02:08:39,05:15:09 +61,M,02:02:40,05:15:09 +60,M,02:08:25,05:15:09 +32,W,02:20:10,05:15:10 +49,W,02:17:54,05:15:10 +53,W,02:11:59,05:15:10 +30,M,02:13:55,05:15:10 +29,W,02:16:31,05:15:10 +34,M,02:13:03,05:15:10 +30,W,02:19:59,05:15:11 +48,M,02:08:54,05:15:11 +40,W,02:10:42,05:15:11 +62,M,02:22:01,05:15:12 +40,W,02:09:56,05:15:12 +39,W,02:07:20,05:15:12 +41,M,02:03:24,05:15:12 +54,M,02:22:42,05:15:13 +50,M,02:02:12,05:15:13 +27,W,02:11:52,05:15:13 +35,M,02:23:14,05:15:14 +53,M,02:10:26,05:15:14 +49,M,02:13:05,05:15:14 +63,M,02:09:20,05:15:14 +49,M,02:09:31,05:15:14 +45,M,02:03:35,05:15:14 +48,M,02:09:36,05:15:14 +47,M,02:15:01,05:15:14 +49,M,02:12:53,05:15:15 +25,W,02:13:40,05:15:15 +63,M,02:23:07,05:15:15 +53,M,02:14:27,05:15:15 +39,W,02:12:20,05:15:15 +41,W,02:18:00,05:15:15 +48,M,01:57:26,05:15:16 +29,W,02:22:12,05:15:16 +57,M,02:13:42,05:15:16 +70,M,02:09:36,05:15:16 +27,M,02:00:16,05:15:16 +23,W,02:06:35,05:15:16 +53,M,02:05:00,05:15:16 +45,W,02:10:38,05:15:17 +45,W,02:15:10,05:15:17 +37,M,02:12:57,05:15:18 +46,M,02:14:32,05:15:18 +48,M,01:56:40,05:15:18 +27,M,02:12:18,05:15:18 +31,W,02:08:50,05:15:19 +51,M,02:14:19,05:15:19 +44,W,02:08:03,05:15:20 +50,M,02:14:20,05:15:21 +45,M,02:07:52,05:15:21 +35,M,02:13:11,05:15:21 +50,M,02:03:10,05:15:21 +44,M,02:13:34,05:15:21 +19,M,01:56:26,05:15:21 +38,W,02:13:10,05:15:21 +51,M,02:05:53,05:15:22 +39,M,02:24:21,05:15:22 +42,M,02:03:25,05:15:22 +43,M,02:08:16,05:15:22 +40,W,02:11:37,05:15:22 +41,M,02:14:08,05:15:22 +24,M,02:23:50,05:15:22 +49,M,02:14:22,05:15:23 +36,M,02:16:57,05:15:23 +40,W,02:16:06,05:15:23 +33,M,02:14:04,05:15:23 +33,M,02:10:58,05:15:23 +43,M,02:05:42,05:15:24 +53,M,02:06:03,05:15:24 +46,W,02:07:11,05:15:25 +28,W,02:21:05,05:15:25 +36,M,02:14:13,05:15:25 +51,W,02:10:31,05:15:25 +27,M,02:10:26,05:15:25 +48,M,02:13:46,05:15:26 +32,W,02:14:51,05:15:26 +51,W,02:08:03,05:15:27 +51,M,02:15:22,05:15:27 +38,M,02:11:24,05:15:27 +49,M,02:09:57,05:15:27 +45,M,02:00:33,05:15:28 +52,M,02:03:08,05:15:28 +29,W,02:11:45,05:15:28 +34,M,02:11:08,05:15:28 +58,M,02:23:21,05:15:28 +34,M,02:16:48,05:15:29 +44,M,02:12:18,05:15:29 +51,W,02:20:36,05:15:29 +49,M,02:10:48,05:15:29 +31,W,02:02:46,05:15:29 +32,W,02:26:09,05:15:30 +28,M,02:13:03,05:15:30 +38,M,02:19:25,05:15:30 +24,W,02:15:14,05:15:30 +32,M,02:11:10,05:15:30 +41,W,02:13:39,05:15:30 +38,W,02:11:06,05:15:30 +46,W,02:08:20,05:15:30 +57,W,02:13:27,05:15:31 +26,M,01:59:13,05:15:31 +36,M,02:09:15,05:15:31 +53,M,02:10:36,05:15:31 +34,W,02:22:21,05:15:31 +36,W,02:25:22,05:15:32 +42,W,02:12:28,05:15:33 +25,W,02:11:26,05:15:33 +59,W,02:11:01,05:15:33 +41,M,02:05:47,05:15:33 +47,M,02:06:17,05:15:34 +27,M,02:14:25,05:15:34 +48,M,02:12:02,05:15:35 +38,W,02:25:48,05:15:35 +34,M,02:10:59,05:15:35 +47,W,02:09:41,05:15:35 +47,W,02:02:19,05:15:35 +26,M,02:01:01,05:15:35 +50,W,02:08:08,05:15:36 +36,M,02:03:50,05:15:37 +39,W,02:10:17,05:15:37 +52,M,02:07:15,05:15:37 +42,M,02:09:10,05:15:37 +39,W,02:09:34,05:15:38 +43,M,02:07:53,05:15:38 +52,W,02:22:54,05:15:38 +28,M,02:07:49,05:15:39 +45,M,02:11:24,05:15:39 +25,W,02:17:58,05:15:39 +35,M,02:04:07,05:15:40 +50,W,02:06:42,05:15:40 +33,M,02:24:28,05:15:41 +43,M,02:15:23,05:15:41 +42,M,02:10:49,05:15:41 +48,W,02:08:44,05:15:41 +39,M,02:13:04,05:15:42 +49,M,02:12:27,05:15:42 +39,W,02:21:08,05:15:42 +28,W,02:17:15,05:15:42 +48,W,02:23:03,05:15:43 +37,M,02:08:22,05:15:44 +52,M,02:08:40,05:15:44 +26,W,02:16:03,05:15:44 +40,M,02:02:11,05:15:45 +35,M,02:07:19,05:15:45 +43,M,02:14:16,05:15:45 +40,W,02:23:06,05:15:45 +35,M,02:19:41,05:15:46 +48,W,02:27:19,05:15:46 +29,W,02:13:41,05:15:46 +39,M,02:11:28,05:15:47 +49,W,02:08:31,05:15:47 +28,W,02:13:49,05:15:47 +31,M,02:13:58,05:15:47 +57,M,02:05:50,05:15:47 +34,M,02:06:34,05:15:48 +27,M,02:01:07,05:15:48 +37,W,02:11:17,05:15:49 +31,M,02:05:02,05:15:49 +40,W,02:17:59,05:15:49 +64,M,02:23:54,05:15:49 +41,M,02:12:15,05:15:50 +46,M,02:14:42,05:15:50 +46,W,02:07:12,05:15:50 +64,M,02:07:06,05:15:50 +24,W,02:15:26,05:15:50 +18,W,02:04:56,05:15:50 +50,W,02:20:12,05:15:51 +41,W,02:09:07,05:15:51 +36,M,02:13:56,05:15:51 +33,M,02:12:59,05:15:51 +38,W,02:08:23,05:15:51 +40,M,02:21:05,05:15:52 +55,W,02:09:47,05:15:52 +42,W,02:19:56,05:15:53 +42,M,02:12:27,05:15:53 +46,W,02:10:12,05:15:53 +39,M,02:10:35,05:15:53 +28,M,02:08:16,05:15:53 +33,M,01:59:26,05:15:53 +29,M,02:17:09,05:15:53 +38,M,02:16:35,05:15:54 +55,M,02:05:06,05:15:54 +35,W,02:09:17,05:15:54 +30,M,01:47:08,05:15:54 +53,M,02:07:00,05:15:54 +71,M,02:09:29,05:15:55 +36,M,02:07:14,05:15:55 +57,M,02:12:16,05:15:55 +67,M,02:10:59,05:15:55 +32,W,02:18:32,05:15:55 +33,W,02:12:46,05:15:56 +27,M,02:11:01,05:15:56 +46,M,02:14:18,05:15:56 +40,W,02:05:14,05:15:56 +41,W,02:10:12,05:15:56 +28,M,02:05:34,05:15:57 +43,W,02:12:26,05:15:57 +29,W,02:15:00,05:15:57 +30,W,02:14:26,05:15:57 +50,W,02:06:00,05:15:57 +57,M,02:17:08,05:15:57 +43,W,02:04:20,05:15:57 +51,M,02:05:58,05:15:58 +34,W,02:15:20,05:15:58 +45,M,02:16:13,05:15:58 +38,M,02:11:01,05:15:59 +50,M,02:15:01,05:15:59 +24,W,02:12:28,05:15:59 +53,M,02:18:21,05:16:00 +44,W,02:11:27,05:16:00 +62,W,02:12:20,05:16:00 +44,M,02:08:35,05:16:01 +41,W,02:08:18,05:16:01 +56,M,01:59:00,05:16:02 +61,M,02:07:48,05:16:02 +45,W,02:04:52,05:16:02 +24,M,01:58:36,05:16:03 +44,M,02:04:50,05:16:03 +22,M,02:09:47,05:16:03 +31,M,02:10:11,05:16:03 +34,W,02:11:57,05:16:03 +40,M,02:20:27,05:16:03 +46,W,02:05:29,05:16:03 +59,M,02:05:24,05:16:04 +54,W,02:37:34,05:16:04 +24,W,02:10:23,05:16:04 +51,M,02:02:27,05:16:04 +39,W,02:14:08,05:16:05 +36,M,02:17:02,05:16:05 +25,W,02:09:17,05:16:05 +44,M,02:03:43,05:16:05 +36,M,02:16:43,05:16:06 +36,M,02:09:06,05:16:06 +29,W,02:13:36,05:16:06 +46,M,02:08:37,05:16:07 +51,W,01:59:14,05:16:07 +53,W,02:11:00,05:16:07 +49,W,02:17:56,05:16:08 +43,W,02:08:30,05:16:08 +41,M,01:58:42,05:16:08 +45,W,02:14:54,05:16:09 +41,M,02:01:00,05:16:09 +31,M,02:22:45,05:16:09 +36,M,02:07:36,05:16:10 +38,M,02:29:14,05:16:12 +59,M,02:13:01,05:16:12 +28,M,01:55:23,05:16:12 +45,W,02:12:24,05:16:12 +26,W,02:10:04,05:16:12 +37,M,02:09:12,05:16:12 +29,M,02:07:08,05:16:12 +39,M,02:01:55,05:16:13 +47,W,02:21:35,05:16:13 +41,M,02:07:21,05:16:13 +33,M,02:07:23,05:16:13 +39,W,02:10:56,05:16:14 +49,M,02:11:47,05:16:14 +49,W,02:11:59,05:16:14 +37,M,02:03:24,05:16:15 +33,W,02:20:33,05:16:15 +47,M,02:00:45,05:16:15 +45,M,02:06:16,05:16:15 +44,W,02:19:43,05:16:16 +36,W,02:18:36,05:16:16 +46,W,02:21:39,05:16:16 +31,W,02:18:10,05:16:17 +45,M,02:47:41,05:16:17 +34,W,02:22:58,05:16:17 +46,M,02:08:31,05:16:17 +51,M,02:10:07,05:16:17 +27,W,02:21:31,05:16:18 +66,M,02:15:24,05:16:18 +24,M,02:04:42,05:16:18 +21,M,02:02:33,05:16:18 +41,W,01:59:55,05:16:18 +52,W,02:08:32,05:16:18 +44,M,02:02:21,05:16:18 +38,M,02:04:08,05:16:18 +55,M,02:08:08,05:16:18 +39,M,02:14:00,05:16:18 +52,M,02:06:14,05:16:19 +58,M,02:18:43,05:16:19 +30,W,02:13:45,05:16:20 +60,M,02:11:44,05:16:20 +39,M,02:15:23,05:16:20 +46,W,02:10:36,05:16:20 +26,M,02:07:55,05:16:20 +35,W,02:12:43,05:16:21 +26,W,02:22:12,05:16:21 +37,M,02:08:57,05:16:21 +56,M,02:22:23,05:16:21 +47,W,02:09:04,05:16:21 +41,M,02:19:41,05:16:21 +46,M,02:08:46,05:16:22 +45,M,02:06:40,05:16:22 +26,W,02:15:48,05:16:23 +54,W,02:19:05,05:16:23 +38,M,02:20:22,05:16:24 +40,M,02:14:47,05:16:24 +41,M,02:07:26,05:16:24 +59,M,02:10:05,05:16:24 +51,W,02:14:57,05:16:25 +27,M,02:15:44,05:16:25 +64,M,02:23:00,05:16:25 +55,M,02:15:05,05:16:25 +28,W,02:01:46,05:16:25 +28,W,02:12:17,05:16:26 +34,M,02:10:50,05:16:26 +42,W,02:13:24,05:16:26 +49,M,02:04:09,05:16:26 +55,M,02:01:31,05:16:26 +37,M,02:10:08,05:16:26 +51,M,02:22:56,05:16:27 +41,M,02:04:19,05:16:27 +46,W,02:14:51,05:16:27 +27,M,02:06:40,05:16:27 +35,W,02:15:44,05:16:27 +35,W,02:15:21,05:16:27 +24,W,02:12:00,05:16:28 +35,M,02:24:09,05:16:28 +47,W,02:19:06,05:16:28 +52,M,01:47:19,05:16:28 +41,M,02:15:09,05:16:29 +51,M,02:16:11,05:16:29 +47,W,02:08:40,05:16:29 +27,W,02:06:24,05:16:29 +37,M,02:06:21,05:16:30 +38,M,02:19:23,05:16:30 +38,W,02:15:32,05:16:30 +25,W,02:10:30,05:16:30 +54,M,02:15:48,05:16:30 +59,M,02:04:43,05:16:30 +29,M,02:08:32,05:16:31 +35,W,02:17:37,05:16:32 +36,W,02:13:30,05:16:32 +47,M,02:18:00,05:16:32 +55,M,02:08:32,05:16:33 +46,M,02:02:11,05:16:33 +54,M,02:04:32,05:16:33 +32,M,02:11:56,05:16:33 +43,M,02:16:14,05:16:34 +25,W,02:11:59,05:16:34 +38,M,02:05:43,05:16:34 +34,M,02:08:05,05:16:34 +50,M,01:57:53,05:16:34 +48,M,02:09:17,05:16:34 +27,W,02:09:18,05:16:35 +27,W,02:10:54,05:16:35 +51,W,02:11:59,05:16:35 +30,W,02:17:27,05:16:35 +48,M,02:20:49,05:16:35 +42,M,02:06:48,05:16:36 +36,M,02:18:48,05:16:36 +31,M,02:22:12,05:16:36 +46,W,02:17:50,05:16:36 +37,W,02:08:46,05:16:37 +34,M,02:01:03,05:16:37 +49,M,02:17:07,05:16:37 +37,W,02:12:46,05:16:37 +30,W,02:19:01,05:16:38 +48,W,02:20:08,05:16:39 +38,M,02:09:35,05:16:39 +46,M,02:09:24,05:16:39 +47,M,02:04:35,05:16:40 +41,M,02:09:01,05:16:40 +42,M,02:16:27,05:16:40 +35,W,02:15:44,05:16:40 +50,M,02:10:36,05:16:40 +32,W,02:08:45,05:16:40 +35,M,02:10:36,05:16:40 +49,M,02:11:00,05:16:41 +63,M,02:12:10,05:16:41 +25,W,02:16:03,05:16:41 +43,W,02:20:12,05:16:41 +48,M,02:08:39,05:16:41 +31,W,02:14:24,05:16:42 +28,M,02:13:29,05:16:42 +31,M,02:00:07,05:16:42 +41,M,02:12:11,05:16:42 +42,W,02:08:30,05:16:42 +46,W,02:11:46,05:16:42 +48,W,02:08:55,05:16:42 +46,M,02:01:22,05:16:43 +28,M,02:14:36,05:16:43 +46,M,02:17:57,05:16:44 +73,M,02:14:09,05:16:44 +56,M,02:02:55,05:16:44 +29,W,02:18:34,05:16:44 +24,W,02:12:17,05:16:44 +35,W,02:12:34,05:16:44 +48,M,02:18:13,05:16:44 +39,W,02:08:41,05:16:45 +50,W,01:59:56,05:16:45 +39,M,02:01:50,05:16:45 +56,M,02:13:49,05:16:46 +42,M,02:04:26,05:16:46 +25,M,02:14:09,05:16:46 +37,M,02:13:47,05:16:46 +43,W,02:09:00,05:16:46 +40,M,02:03:16,05:16:46 +43,M,01:49:55,05:16:47 +48,W,02:01:53,05:16:47 +29,W,02:22:50,05:16:48 +42,M,02:06:50,05:16:48 +30,M,02:11:56,05:16:48 +38,M,02:10:14,05:16:49 +37,W,02:13:03,05:16:50 +37,M,02:16:08,05:16:50 +27,M,02:15:10,05:16:50 +37,W,02:13:18,05:16:50 +39,M,02:19:05,05:16:51 +30,W,02:09:38,05:16:51 +33,M,02:10:33,05:16:52 +27,M,02:11:14,05:16:52 +38,W,02:22:22,05:16:52 +36,M,02:05:22,05:16:53 +59,W,02:16:14,05:16:53 +38,M,02:17:27,05:16:53 +41,M,02:18:17,05:16:53 +51,M,02:07:17,05:16:53 +39,M,02:08:53,05:16:54 +28,M,02:13:59,05:16:54 +73,W,02:03:17,05:16:55 +38,W,02:13:39,05:16:55 +36,W,02:14:00,05:16:55 +40,W,02:15:15,05:16:55 +21,M,02:01:50,05:16:56 +32,W,02:07:51,05:16:56 +44,M,02:11:24,05:16:57 +32,M,02:18:49,05:16:57 +29,M,02:09:12,05:16:57 +61,M,02:00:29,05:16:58 +47,M,02:05:01,05:16:58 +49,M,02:21:56,05:16:58 +40,M,02:05:42,05:16:58 +28,M,02:15:12,05:16:58 +38,M,02:10:34,05:16:59 +51,M,02:13:08,05:16:59 +37,M,02:07:38,05:16:59 +46,W,02:14:53,05:17:00 +48,W,02:13:27,05:17:00 +57,M,01:54:26,05:17:00 +54,M,02:10:47,05:17:00 +35,M,02:17:05,05:17:00 +37,W,02:13:17,05:17:00 +45,M,02:09:48,05:17:01 +33,W,02:15:04,05:17:01 +51,M,02:18:31,05:17:01 +42,M,02:12:21,05:17:01 +39,M,02:17:38,05:17:02 +34,M,01:58:10,05:17:02 +59,W,02:14:23,05:17:02 +33,W,02:12:19,05:17:02 +42,M,02:13:31,05:17:02 +30,M,02:17:03,05:17:03 +56,M,02:07:40,05:17:04 +39,M,02:01:36,05:17:04 +27,W,02:07:51,05:17:04 +58,M,02:18:54,05:17:05 +53,M,02:24:45,05:17:05 +48,W,02:12:23,05:17:05 +36,M,02:00:46,05:17:05 +33,M,02:14:46,05:17:06 +49,M,02:13:14,05:17:06 +36,M,02:12:04,05:17:06 +47,M,02:11:36,05:17:06 +32,W,02:05:30,05:17:07 +48,M,02:14:31,05:17:07 +56,M,02:12:48,05:17:07 +33,W,02:14:12,05:17:07 +28,W,02:12:34,05:17:08 +42,M,02:21:41,05:17:08 +64,M,02:11:04,05:17:08 +39,W,02:19:38,05:17:08 +38,W,02:10:38,05:17:09 +43,M,02:04:37,05:17:09 +49,M,02:13:38,05:17:09 +42,W,02:10:58,05:17:09 +27,W,02:15:23,05:17:09 +48,M,02:10:08,05:17:10 +52,M,02:12:11,05:17:10 +43,M,02:26:33,05:17:10 +56,W,02:20:19,05:17:10 +34,W,02:18:59,05:17:10 +29,M,02:07:45,05:17:10 +35,M,02:13:54,05:17:11 +38,M,01:55:35,05:17:11 +38,W,02:18:43,05:17:12 +34,M,02:11:53,05:17:12 +57,M,02:25:08,05:17:12 +53,M,01:59:34,05:17:13 +60,W,02:18:56,05:17:13 +39,M,02:10:46,05:17:13 +34,W,02:06:22,05:17:14 +26,W,02:10:32,05:17:14 +53,W,02:10:57,05:17:15 +50,W,02:07:44,05:17:15 +34,M,02:02:52,05:17:16 +48,W,02:22:15,05:17:16 +38,W,02:10:17,05:17:16 +45,M,02:07:10,05:17:16 +37,M,02:10:34,05:17:16 +30,W,02:20:37,05:17:16 +53,M,02:03:33,05:17:16 +36,W,02:14:02,05:17:16 +63,M,02:09:36,05:17:16 +43,W,02:09:02,05:17:17 +36,W,02:15:00,05:17:17 +35,W,02:07:53,05:17:17 +35,W,02:13:34,05:17:17 +29,W,02:12:53,05:17:17 +55,M,02:19:18,05:17:17 +31,W,02:15:58,05:17:17 +55,M,02:13:17,05:17:18 +61,W,02:13:28,05:17:18 +54,M,02:09:53,05:17:18 +69,M,02:09:55,05:17:18 +38,M,02:12:25,05:17:18 +37,M,02:08:08,05:17:18 +66,M,02:16:56,05:17:18 +26,M,02:06:24,05:17:19 +40,M,01:58:45,05:17:19 +29,M,02:04:46,05:17:19 +30,M,02:13:30,05:17:19 +44,M,02:13:53,05:17:19 +35,W,02:22:26,05:17:20 +30,W,02:09:53,05:17:20 +39,M,02:05:03,05:17:20 +50,M,02:15:56,05:17:20 +52,M,02:09:52,05:17:20 +43,W,02:20:41,05:17:20 +43,M,02:04:01,05:17:20 +43,M,02:08:12,05:17:20 +36,W,02:14:13,05:17:21 +38,M,02:02:24,05:17:21 +23,W,02:08:12,05:17:21 +52,M,02:04:00,05:17:21 +34,M,02:01:40,05:17:21 +27,W,02:16:01,05:17:21 +32,W,02:14:43,05:17:21 +36,M,02:14:44,05:17:22 +49,M,02:06:01,05:17:22 +51,W,02:18:55,05:17:22 +34,M,02:00:26,05:17:22 +39,M,01:53:55,05:17:22 +35,W,02:12:29,05:17:23 +40,M,02:13:15,05:17:23 +47,M,02:17:44,05:17:23 +42,W,02:21:30,05:17:23 +53,M,02:16:18,05:17:23 +34,M,02:10:35,05:17:24 +45,M,02:13:15,05:17:24 +42,M,02:10:02,05:17:24 +38,W,02:10:40,05:17:24 +44,M,02:08:51,05:17:24 +42,W,02:09:46,05:17:25 +41,M,02:05:20,05:17:25 +39,M,02:11:04,05:17:25 +48,W,02:12:22,05:17:25 +52,W,02:10:36,05:17:26 +48,W,02:10:36,05:17:26 +31,W,02:09:43,05:17:26 +24,W,02:16:09,05:17:27 +56,W,02:14:58,05:17:27 +47,M,02:07:13,05:17:27 +57,M,02:11:14,05:17:28 +38,W,02:15:05,05:17:28 +42,W,02:16:18,05:17:28 +48,M,02:14:00,05:17:29 +51,M,02:08:57,05:17:29 +40,M,02:07:43,05:17:29 +27,W,02:07:00,05:17:29 +41,M,02:12:39,05:17:30 +59,M,02:18:52,05:17:30 +24,W,02:13:32,05:17:30 +30,M,02:00:12,05:17:31 +46,W,02:16:33,05:17:31 +55,M,02:00:52,05:17:31 +37,M,02:00:59,05:17:31 +34,M,02:14:18,05:17:31 +28,W,02:04:21,05:17:31 +43,M,02:11:42,05:17:32 +48,M,02:13:21,05:17:33 +62,M,02:12:56,05:17:33 +51,M,02:04:16,05:17:33 +59,W,02:12:20,05:17:34 +45,M,02:07:17,05:17:35 +41,M,02:12:12,05:17:35 +42,M,02:12:21,05:17:35 +24,M,02:05:41,05:17:36 +42,M,02:11:45,05:17:37 +65,M,02:16:31,05:17:37 +45,M,02:09:09,05:17:37 +29,M,02:18:17,05:17:38 +45,M,02:04:47,05:17:38 +45,W,02:18:55,05:17:38 +31,M,02:16:53,05:17:38 +41,W,02:12:41,05:17:39 +46,M,02:17:05,05:17:39 +27,W,02:11:36,05:17:39 +37,M,02:11:44,05:17:40 +41,M,02:14:59,05:17:40 +50,W,02:12:58,05:17:40 +25,W,02:14:23,05:17:41 +48,M,02:15:28,05:17:41 +40,W,02:06:23,05:17:41 +39,M,02:13:26,05:17:41 +23,M,02:02:41,05:17:41 +27,W,02:22:27,05:17:42 +29,W,02:10:30,05:17:42 +47,M,02:18:30,05:17:42 +34,M,02:10:27,05:17:42 +56,M,02:09:33,05:17:42 +37,W,02:10:05,05:17:42 +43,M,02:09:56,05:17:42 +31,W,02:15:42,05:17:43 +32,W,02:06:16,05:17:43 +44,M,02:02:34,05:17:43 +38,M,02:12:05,05:17:44 +55,W,02:11:35,05:17:44 +40,M,02:11:22,05:17:44 +35,M,02:02:41,05:17:44 +24,W,02:12:28,05:17:45 +40,W,02:15:54,05:17:45 +51,M,02:08:55,05:17:45 +63,W,02:08:47,05:17:45 +41,M,02:10:17,05:17:45 +31,M,02:09:32,05:17:45 +54,M,02:06:01,05:17:46 +35,M,02:08:22,05:17:46 +49,W,02:19:07,05:17:46 +22,M,02:14:24,05:17:46 +42,W,02:04:15,05:17:46 +31,W,02:15:09,05:17:46 +49,W,02:07:33,05:17:47 +32,W,02:08:33,05:17:47 +29,W,02:22:20,05:17:47 +34,M,02:13:27,05:17:48 +44,M,02:01:47,05:17:49 +40,W,02:10:23,05:17:49 +31,W,02:14:32,05:17:50 +20,W,02:13:24,05:17:50 +65,M,02:07:30,05:17:50 +50,M,02:05:16,05:17:51 +34,M,02:06:55,05:17:51 +53,W,02:07:46,05:17:51 +45,W,02:13:25,05:17:51 +29,W,02:14:08,05:17:51 +32,M,02:12:23,05:17:51 +48,M,02:09:57,05:17:51 +55,M,02:19:41,05:17:52 +48,M,02:02:13,05:17:52 +29,W,02:17:33,05:17:52 +41,M,02:15:58,05:17:52 +38,W,02:15:03,05:17:52 +42,M,02:06:00,05:17:52 +53,W,02:23:17,05:17:52 +29,W,02:04:34,05:17:52 +33,M,02:11:49,05:17:52 +56,M,02:04:43,05:17:53 +50,M,02:09:35,05:17:53 +45,W,02:12:29,05:17:54 +52,W,02:08:07,05:17:54 +40,W,02:12:46,05:17:54 +30,W,02:11:25,05:17:54 +37,M,02:09:22,05:17:54 +42,M,02:14:35,05:17:55 +44,M,02:18:01,05:17:55 +48,M,02:11:35,05:17:55 +59,M,02:15:10,05:17:56 +38,W,02:17:05,05:17:56 +59,M,02:25:01,05:17:56 +34,W,02:11:24,05:17:56 +70,M,02:22:28,05:17:56 +28,M,02:03:41,05:17:56 +40,M,02:16:20,05:17:56 +57,M,02:13:22,05:17:58 +30,M,02:16:51,05:17:58 +57,M,02:01:09,05:17:58 +39,M,02:22:30,05:17:58 +55,M,02:06:40,05:17:59 +44,W,02:12:47,05:17:59 +31,W,02:14:56,05:18:00 +30,W,02:06:47,05:18:00 +46,M,02:21:23,05:18:00 +40,M,02:27:12,05:18:01 +44,M,02:18:17,05:18:01 +38,M,02:13:14,05:18:01 +26,W,02:21:33,05:18:02 +58,W,02:19:43,05:18:02 +20,W,02:05:14,05:18:02 +43,W,02:08:19,05:18:03 +42,W,02:11:49,05:18:03 +55,M,02:02:14,05:18:03 +52,M,02:11:00,05:18:04 +49,M,02:07:21,05:18:04 +60,M,02:14:13,05:18:04 +39,M,02:26:48,05:18:05 +54,M,02:07:23,05:18:05 +35,W,02:17:24,05:18:05 +53,M,02:03:02,05:18:05 +52,M,02:11:20,05:18:06 +39,M,02:12:19,05:18:06 +25,M,02:14:53,05:18:06 +60,M,02:07:51,05:18:07 +36,M,02:18:48,05:18:07 +42,W,02:12:25,05:18:07 +24,W,01:59:30,05:18:08 +43,M,02:11:32,05:18:08 +38,W,02:17:15,05:18:08 +34,M,01:59:38,05:18:09 +33,M,02:09:33,05:18:09 +30,W,02:12:55,05:18:10 +30,W,02:09:26,05:18:10 +46,M,02:18:14,05:18:11 +51,W,02:13:00,05:18:11 +34,W,02:19:43,05:18:11 +30,M,02:23:12,05:18:11 +30,M,02:03:28,05:18:12 +38,W,02:06:16,05:18:12 +49,M,02:13:09,05:18:13 +60,W,02:24:20,05:18:13 +42,W,02:09:43,05:18:13 +42,W,02:07:30,05:18:13 +25,M,02:14:10,05:18:13 +40,W,02:13:11,05:18:13 +31,W,02:22:29,05:18:14 +63,M,02:08:33,05:18:14 +45,M,02:13:17,05:18:14 +40,M,02:16:15,05:18:14 +26,W,02:14:49,05:18:14 +34,W,02:05:07,05:18:14 +42,M,02:03:33,05:18:14 +52,W,02:16:43,05:18:15 +34,W,02:19:49,05:18:16 +45,W,02:15:17,05:18:16 +30,W,02:23:39,05:18:17 +48,M,02:01:49,05:18:17 +33,M,02:18:27,05:18:17 +56,M,02:11:16,05:18:17 +34,M,02:19:35,05:18:17 +24,W,02:10:51,05:18:17 +44,M,02:14:39,05:18:17 +49,M,02:11:58,05:18:18 +40,W,02:10:05,05:18:18 +42,M,02:12:33,05:18:18 +27,W,02:07:05,05:18:18 +51,W,02:23:17,05:18:19 +27,M,02:13:12,05:18:19 +27,W,01:59:29,05:18:20 +42,M,02:08:18,05:18:20 +58,M,02:09:15,05:18:20 +49,M,02:18:44,05:18:21 +69,M,02:16:04,05:18:21 +39,M,02:17:10,05:18:21 +35,M,02:17:25,05:18:22 +28,W,02:27:36,05:18:22 +54,W,02:14:03,05:18:22 +48,M,02:16:03,05:18:22 +29,W,02:10:52,05:18:23 +28,W,02:16:32,05:18:23 +26,W,02:11:33,05:18:23 +41,M,02:10:29,05:18:23 +27,W,02:27:29,05:18:24 +32,W,02:12:00,05:18:24 +28,W,02:14:13,05:18:24 +51,M,02:17:54,05:18:25 +50,W,02:12:30,05:18:25 +46,M,02:17:24,05:18:26 +46,M,02:21:08,05:18:26 +27,W,02:11:30,05:18:26 +35,M,02:17:39,05:18:26 +40,M,02:03:23,05:18:27 +36,W,02:19:39,05:18:27 +26,W,02:17:46,05:18:27 +36,M,02:06:40,05:18:28 +42,M,02:13:59,05:18:28 +44,W,02:13:40,05:18:30 +46,M,02:14:25,05:18:30 +48,M,02:13:21,05:18:30 +26,W,02:16:34,05:18:31 +39,M,02:05:15,05:18:31 +56,M,02:15:31,05:18:32 +57,M,01:50:32,05:18:32 +26,W,02:19:24,05:18:32 +33,M,02:21:02,05:18:33 +32,M,02:15:08,05:18:33 +43,W,02:17:08,05:18:33 +44,W,02:18:11,05:18:33 +28,M,02:16:32,05:18:33 +59,M,02:29:58,05:18:33 +42,W,02:13:38,05:18:34 +39,M,02:16:50,05:18:34 +52,M,02:21:23,05:18:34 +26,M,02:10:02,05:18:34 +30,M,02:23:00,05:18:34 +23,W,02:04:16,05:18:34 +38,W,02:15:01,05:18:34 +36,W,02:10:06,05:18:35 +54,M,02:09:33,05:18:35 +39,M,02:17:53,05:18:35 +31,M,02:14:01,05:18:35 +53,M,02:03:08,05:18:36 +29,W,02:10:13,05:18:36 +39,W,02:13:51,05:18:36 +31,M,02:19:16,05:18:36 +51,W,02:11:13,05:18:36 +28,W,02:01:16,05:18:37 +33,M,01:58:45,05:18:37 +63,W,02:29:17,05:18:37 +52,M,02:16:41,05:18:37 +49,M,02:07:59,05:18:37 +55,M,02:06:36,05:18:38 +28,W,02:19:22,05:18:38 +56,M,02:09:39,05:18:38 +41,M,02:16:19,05:18:38 +35,W,02:19:14,05:18:38 +42,M,02:04:58,05:18:39 +36,W,02:09:45,05:18:39 +66,W,02:17:34,05:18:39 +25,M,02:09:48,05:18:40 +35,W,02:12:25,05:18:40 +39,W,02:15:43,05:18:41 +51,M,02:15:29,05:18:41 +61,M,02:10:11,05:18:42 +47,M,02:07:21,05:18:42 +56,W,02:24:19,05:18:42 +30,W,02:14:56,05:18:42 +45,W,02:18:29,05:18:43 +34,W,02:28:14,05:18:43 +39,W,02:14:35,05:18:43 +50,M,02:19:54,05:18:43 +27,W,02:16:07,05:18:44 +18,W,02:20:52,05:18:44 +47,M,02:12:40,05:18:44 +23,M,01:57:16,05:18:45 +30,W,02:18:06,05:18:45 +25,M,02:14:00,05:18:45 +33,W,02:18:47,05:18:46 +33,M,02:24:28,05:18:46 +51,W,02:19:00,05:18:46 +45,M,01:58:06,05:18:47 +60,M,02:03:21,05:18:47 +35,M,02:10:42,05:18:47 +51,W,02:24:21,05:18:47 +59,M,02:09:16,05:18:47 +41,M,02:04:35,05:18:48 +48,W,02:11:10,05:18:48 +32,M,02:21:55,05:18:49 +54,M,02:01:44,05:18:49 +58,M,02:22:19,05:18:49 +53,M,02:07:11,05:18:49 +31,W,02:18:26,05:18:50 +37,M,02:07:00,05:18:50 +44,M,02:02:16,05:18:50 +40,W,02:20:36,05:18:50 +48,W,02:15:34,05:18:50 +51,M,01:58:34,05:18:51 +53,M,02:08:14,05:18:52 +55,W,02:20:16,05:18:52 +33,M,02:09:28,05:18:52 +30,W,02:16:13,05:18:53 +45,W,02:07:02,05:18:53 +42,M,02:13:12,05:18:53 +58,W,02:18:27,05:18:54 +34,W,02:24:59,05:18:54 +48,M,02:02:30,05:18:54 +35,M,02:24:24,05:18:54 +54,M,02:13:21,05:18:55 +46,M,02:11:39,05:18:55 +48,W,02:24:41,05:18:55 +47,W,02:28:51,05:18:56 +45,W,02:21:02,05:18:56 +44,W,02:14:04,05:18:56 +28,W,02:11:09,05:18:56 +58,M,02:12:44,05:18:56 +19,M,02:09:06,05:18:56 +41,M,02:18:53,05:18:56 +41,W,02:17:51,05:18:56 +35,M,02:02:10,05:18:57 +27,W,02:22:36,05:18:58 +53,M,02:19:51,05:18:58 +25,W,02:19:00,05:18:58 +48,M,02:13:55,05:18:58 +40,M,02:08:51,05:18:59 +23,M,02:21:41,05:18:59 +27,W,02:06:37,05:18:59 +34,W,02:18:11,05:18:59 +40,W,02:16:11,05:18:59 +50,M,02:14:57,05:18:59 +40,W,02:14:06,05:18:59 +41,M,02:10:36,05:19:00 +50,M,02:16:52,05:19:00 +40,M,02:16:20,05:19:00 +62,M,02:15:42,05:19:00 +33,W,02:20:17,05:19:01 +51,M,02:06:50,05:19:01 +34,M,02:12:21,05:19:01 +26,M,02:12:25,05:19:02 +24,M,01:54:59,05:19:02 +45,M,02:13:52,05:19:02 +48,W,02:13:03,05:19:02 +29,W,02:17:09,05:19:03 +60,W,02:11:15,05:19:03 +56,M,02:09:12,05:19:04 +24,W,02:21:18,05:19:05 +51,M,02:10:03,05:19:05 +47,W,02:06:33,05:19:05 +38,M,02:07:09,05:19:05 +29,W,02:17:41,05:19:05 +30,W,02:23:02,05:19:06 +26,W,02:10:43,05:19:06 +50,M,02:12:17,05:19:06 +42,M,02:19:24,05:19:06 +37,M,02:16:09,05:19:07 +47,M,02:12:01,05:19:07 +38,M,02:12:16,05:19:07 +49,M,02:01:26,05:19:07 +39,W,02:07:20,05:19:07 +27,W,02:17:55,05:19:07 +52,M,02:12:28,05:19:07 +65,M,02:13:10,05:19:07 +29,W,02:19:29,05:19:08 +54,M,02:08:58,05:19:08 +48,M,02:20:39,05:19:08 +43,M,02:10:44,05:19:09 +38,M,02:10:31,05:19:09 +59,M,02:05:27,05:19:10 +35,M,02:10:39,05:19:10 +39,W,02:04:23,05:19:10 +51,W,02:16:19,05:19:10 +55,M,02:25:08,05:19:11 +47,W,02:08:35,05:19:11 +45,M,01:59:01,05:19:11 +37,M,02:09:44,05:19:11 +51,M,02:24:47,05:19:12 +31,W,02:07:08,05:19:12 +39,W,02:16:01,05:19:12 +43,M,02:16:32,05:19:12 +44,M,02:12:53,05:19:12 +56,M,02:12:14,05:19:12 +30,M,02:06:49,05:19:12 +56,M,02:04:18,05:19:13 +53,M,02:07:38,05:19:13 +50,W,02:13:18,05:19:14 +38,M,02:11:16,05:19:14 +49,W,02:12:00,05:19:14 +42,W,02:19:32,05:19:14 +26,W,02:12:45,05:19:15 +24,W,02:16:23,05:19:15 +26,W,02:14:26,05:19:15 +43,M,02:16:06,05:19:15 +48,W,02:15:33,05:19:16 +26,W,02:16:43,05:19:16 +55,M,02:11:33,05:19:16 +41,M,02:03:57,05:19:16 +44,W,02:16:48,05:19:16 +34,W,02:12:02,05:19:16 +27,W,02:06:03,05:19:17 +20,W,02:14:04,05:19:18 +48,M,02:10:23,05:19:18 +27,W,02:14:46,05:19:18 +25,M,02:05:00,05:19:19 +41,W,02:14:22,05:19:19 +60,W,02:29:10,05:19:19 +43,M,02:04:10,05:19:19 +43,W,02:17:24,05:19:20 +37,W,02:18:42,05:19:20 +34,M,02:10:09,05:19:21 +48,W,02:18:13,05:19:22 +33,M,02:05:59,05:19:22 +25,W,02:09:24,05:19:23 +33,W,02:12:30,05:19:23 +26,W,02:17:55,05:19:24 +57,W,02:09:40,05:19:24 +41,M,02:17:24,05:19:24 +43,M,01:51:29,05:19:24 +60,W,02:18:00,05:19:24 +28,M,02:05:24,05:19:25 +32,M,02:15:16,05:19:25 +42,W,02:08:29,05:19:25 +18,M,02:16:59,05:19:27 +45,M,02:07:02,05:19:28 +23,M,02:07:50,05:19:28 +43,M,02:26:45,05:19:28 +40,W,02:12:45,05:19:29 +42,W,02:18:18,05:19:29 +45,W,02:15:00,05:19:30 +46,W,02:09:49,05:19:30 +53,M,02:13:49,05:19:30 +38,M,01:57:35,05:19:30 +51,M,01:57:15,05:19:30 +45,W,01:58:48,05:19:30 +42,W,02:15:19,05:19:31 +35,W,02:22:36,05:19:31 +34,W,02:16:05,05:19:31 +42,M,02:05:10,05:19:31 +44,W,02:18:06,05:19:32 +42,M,02:20:44,05:19:32 +33,W,02:12:13,05:19:33 +39,W,02:14:41,05:19:33 +40,M,02:15:05,05:19:33 +43,W,02:11:23,05:19:33 +33,W,02:30:04,05:19:33 +40,W,02:15:03,05:19:34 +31,M,02:15:45,05:19:35 +29,W,02:14:00,05:19:35 +44,M,02:08:59,05:19:35 +33,M,02:11:49,05:19:35 +46,W,02:09:09,05:19:35 +35,M,02:04:51,05:19:35 +39,M,02:12:21,05:19:36 +50,W,02:22:05,05:19:36 +58,M,02:09:31,05:19:36 +26,W,02:09:24,05:19:36 +32,W,02:07:29,05:19:37 +50,M,02:05:44,05:19:37 +35,M,02:12:55,05:19:37 +39,W,02:14:21,05:19:38 +44,W,02:07:00,05:19:38 +48,M,02:03:08,05:19:38 +37,M,02:13:45,05:19:38 +42,W,02:03:58,05:19:39 +40,M,02:20:09,05:19:39 +28,W,02:07:07,05:19:39 +33,M,02:17:32,05:19:40 +48,W,02:09:02,05:19:40 +37,W,02:16:46,05:19:40 +64,W,02:12:50,05:19:41 +25,W,02:11:03,05:19:42 +35,W,02:11:20,05:19:42 +46,M,02:11:18,05:19:42 +51,M,02:25:47,05:19:42 +40,M,02:25:46,05:19:42 +29,W,02:15:51,05:19:43 +52,W,02:00:32,05:19:43 +49,M,02:04:44,05:19:43 +37,M,02:06:44,05:19:43 +33,W,02:08:09,05:19:43 +53,M,02:08:55,05:19:44 +51,M,02:15:45,05:19:44 +27,M,02:13:48,05:19:45 +40,M,02:10:12,05:19:46 +42,M,01:59:49,05:19:46 +31,M,02:13:13,05:19:46 +29,W,02:11:11,05:19:46 +38,W,02:16:44,05:19:47 +57,W,02:26:15,05:19:48 +37,W,02:16:09,05:19:48 +49,M,02:17:24,05:19:49 +52,M,02:12:50,05:19:49 +18,W,02:13:44,05:19:49 +53,W,02:15:42,05:19:50 +65,M,02:08:50,05:19:50 +47,W,02:13:05,05:19:51 +34,M,02:10:24,05:19:51 +25,M,02:09:55,05:19:52 +43,M,02:08:21,05:19:52 +54,M,02:22:55,05:19:52 +36,W,02:16:38,05:19:53 +41,W,02:18:33,05:19:53 +36,M,02:07:33,05:19:54 +52,M,02:10:42,05:19:54 +46,M,02:12:57,05:19:55 +37,M,02:08:06,05:19:55 +40,W,02:15:08,05:19:55 +35,M,02:25:22,05:19:55 +37,M,02:20:30,05:19:55 +26,W,02:13:13,05:19:55 +36,M,02:06:25,05:19:56 +48,M,02:00:51,05:19:56 +43,M,02:03:46,05:19:56 +49,W,02:19:37,05:19:56 +45,M,01:59:01,05:19:56 +37,M,02:11:42,05:19:57 +29,M,02:03:25,05:19:57 +28,W,02:08:39,05:19:57 +48,W,02:20:16,05:19:57 +35,M,02:16:09,05:19:57 +33,M,02:19:41,05:19:58 +40,W,02:04:18,05:19:58 +43,M,02:06:57,05:19:58 +37,W,02:09:24,05:19:58 +50,M,02:10:14,05:19:58 +54,W,02:16:34,05:19:59 +41,W,02:07:41,05:19:59 +24,M,02:07:06,05:20:01 +50,M,02:24:59,05:20:01 +31,W,02:16:40,05:20:01 +50,M,02:15:51,05:20:01 +39,W,02:07:03,05:20:02 +44,W,02:15:00,05:20:02 +38,W,02:12:40,05:20:02 +45,W,02:15:30,05:20:02 +34,M,02:18:46,05:20:03 +38,M,02:11:25,05:20:03 +41,W,02:17:14,05:20:03 +44,W,02:14:34,05:20:03 +49,M,02:16:03,05:20:03 +51,M,02:10:34,05:20:03 +39,M,02:11:38,05:20:04 +44,W,02:27:42,05:20:04 +27,W,02:20:14,05:20:05 +46,W,02:12:46,05:20:05 +31,W,02:14:58,05:20:05 +28,M,02:04:53,05:20:06 +24,W,02:17:56,05:20:06 +29,W,02:11:33,05:20:06 +30,W,02:12:29,05:20:06 +44,W,02:22:42,05:20:07 +37,M,02:07:34,05:20:07 +26,W,02:11:10,05:20:07 +33,W,02:07:13,05:20:07 +47,W,02:12:10,05:20:08 +42,M,02:12:47,05:20:08 +30,W,02:12:59,05:20:08 +30,M,02:09:16,05:20:08 +34,M,02:04:37,05:20:08 +36,W,02:13:52,05:20:08 +37,W,02:10:51,05:20:08 +46,M,02:15:27,05:20:09 +42,M,02:02:29,05:20:10 +50,W,02:11:09,05:20:10 +27,M,02:17:19,05:20:10 +30,M,02:21:50,05:20:10 +38,M,02:20:15,05:20:11 +57,W,02:09:44,05:20:11 +35,W,02:20:34,05:20:11 +53,M,02:08:16,05:20:12 +38,W,02:15:22,05:20:13 +36,M,02:12:05,05:20:13 +47,M,02:10:11,05:20:14 +52,W,02:16:56,05:20:14 +35,W,02:10:31,05:20:14 +28,W,02:18:26,05:20:14 +47,M,02:02:02,05:20:15 +49,W,02:02:36,05:20:15 +68,M,02:23:13,05:20:15 +26,W,02:15:33,05:20:16 +51,M,02:06:38,05:20:16 +35,M,02:14:20,05:20:16 +41,M,02:07:52,05:20:16 +41,W,02:07:24,05:20:16 +41,W,02:14:38,05:20:17 +39,M,02:15:55,05:20:17 +41,M,02:15:16,05:20:17 +34,W,02:20:25,05:20:17 +34,W,02:14:41,05:20:18 +46,W,02:16:08,05:20:18 +27,W,02:14:15,05:20:20 +38,M,02:16:06,05:20:20 +38,W,02:09:32,05:20:20 +46,M,02:20:56,05:20:20 +50,W,02:31:24,05:20:23 +27,W,02:17:14,05:20:23 +34,M,02:14:11,05:20:23 +50,W,02:14:02,05:20:24 +21,M,02:09:12,05:20:24 +46,W,02:12:32,05:20:24 +43,W,02:14:58,05:20:25 +24,W,02:22:03,05:20:25 +36,W,02:18:34,05:20:25 +50,M,02:12:07,05:20:25 +40,W,02:17:28,05:20:26 +64,M,02:13:00,05:20:26 +48,W,02:13:09,05:20:26 +41,W,02:10:02,05:20:26 +40,M,02:05:51,05:20:26 +38,M,02:15:14,05:20:27 +51,M,02:11:18,05:20:27 +62,M,02:29:20,05:20:28 +41,M,02:11:18,05:20:28 +52,M,02:05:00,05:20:29 +35,M,02:13:08,05:20:29 +29,M,02:07:08,05:20:29 +26,W,02:10:26,05:20:30 +37,M,02:13:50,05:20:30 +47,M,02:23:50,05:20:30 +41,M,02:04:25,05:20:30 +29,M,02:07:05,05:20:30 +45,W,02:17:32,05:20:30 +40,W,02:09:09,05:20:30 +31,W,02:20:53,05:20:31 +34,W,02:15:31,05:20:31 +44,M,02:19:54,05:20:31 +44,M,02:08:56,05:20:32 +56,M,02:21:28,05:20:32 +32,M,02:14:40,05:20:32 +25,W,02:14:43,05:20:34 +29,W,02:16:29,05:20:34 +44,M,02:07:25,05:20:34 +41,W,02:05:18,05:20:34 +43,M,02:21:17,05:20:34 +23,M,02:12:09,05:20:35 +45,W,02:18:08,05:20:35 +52,W,02:14:22,05:20:35 +38,W,02:10:19,05:20:35 +58,M,02:08:20,05:20:35 +27,M,02:10:19,05:20:35 +28,M,02:14:37,05:20:35 +30,W,02:16:35,05:20:36 +43,M,02:06:43,05:20:36 +24,W,02:14:56,05:20:37 +36,M,02:22:33,05:20:37 +31,W,02:23:05,05:20:37 +39,M,02:15:11,05:20:37 +44,M,02:15:10,05:20:37 +47,W,02:29:18,05:20:38 +52,M,02:06:37,05:20:38 +29,W,02:10:54,05:20:38 +53,M,02:08:50,05:20:38 +38,W,02:17:34,05:20:39 +31,W,02:24:59,05:20:39 +41,M,02:03:38,05:20:39 +43,M,02:12:33,05:20:39 +45,W,02:17:20,05:20:39 +39,W,02:18:32,05:20:40 +36,W,02:09:25,05:20:40 +49,W,02:15:39,05:20:40 +40,M,02:09:28,05:20:41 +41,W,02:05:01,05:20:42 +54,W,02:10:05,05:20:42 +33,W,02:13:03,05:20:42 +31,W,02:06:25,05:20:42 +47,M,02:09:53,05:20:43 +20,M,02:14:51,05:20:43 +29,M,02:18:46,05:20:44 +48,M,02:16:30,05:20:44 +49,W,02:11:13,05:20:44 +42,M,02:16:31,05:20:45 +41,M,02:11:30,05:20:45 +38,M,02:10:41,05:20:46 +42,M,02:02:26,05:20:46 +42,M,02:19:17,05:20:46 +44,M,02:04:34,05:20:47 +43,W,02:08:21,05:20:47 +46,M,02:13:39,05:20:47 +50,M,02:06:30,05:20:47 +68,M,02:15:14,05:20:47 +49,M,02:10:24,05:20:47 +31,W,02:21:49,05:20:47 +34,W,02:04:53,05:20:47 +30,W,02:17:29,05:20:48 +29,W,02:08:10,05:20:48 +59,M,02:05:03,05:20:48 +62,W,02:15:19,05:20:48 +56,M,02:17:00,05:20:49 +27,M,02:07:21,05:20:49 +47,W,02:13:45,05:20:49 +23,M,02:10:06,05:20:50 +53,M,02:17:30,05:20:50 +53,W,02:15:43,05:20:51 +52,M,02:01:05,05:20:51 +53,W,02:24:50,05:20:51 +35,M,02:15:28,05:20:51 +32,W,02:15:43,05:20:52 +43,W,02:13:00,05:20:52 +44,W,02:08:41,05:20:53 +54,M,02:13:56,05:20:53 +36,M,02:06:34,05:20:53 +27,M,02:09:33,05:20:54 +27,M,02:07:08,05:20:54 +56,M,02:10:51,05:20:54 +34,M,02:11:21,05:20:55 +44,W,02:05:25,05:20:55 +52,M,02:12:49,05:20:55 +40,W,02:12:29,05:20:55 +30,W,02:21:52,05:20:55 +27,M,02:13:46,05:20:56 +26,M,01:49:24,05:20:56 +41,W,02:04:43,05:20:56 +29,W,02:06:43,05:20:56 +44,M,02:17:09,05:20:57 +45,M,02:13:51,05:20:57 +36,M,02:15:56,05:20:57 +40,W,02:29:22,05:20:57 +37,W,02:17:46,05:20:57 +29,M,02:15:54,05:20:59 +43,M,02:16:02,05:20:59 +45,W,02:13:21,05:21:00 +46,M,02:19:48,05:21:00 +51,M,02:01:55,05:21:00 +27,W,02:12:40,05:21:01 +48,M,02:06:35,05:21:01 +34,M,02:15:21,05:21:01 +67,M,02:12:27,05:21:01 +42,M,02:05:47,05:21:02 +23,W,02:08:58,05:21:02 +48,W,02:15:37,05:21:03 +62,M,02:20:08,05:21:03 +39,W,02:14:24,05:21:04 +58,M,02:12:11,05:21:04 +40,W,02:10:30,05:21:04 +49,M,02:16:39,05:21:04 +36,M,02:06:27,05:21:04 +54,W,02:13:22,05:21:05 +22,M,02:16:39,05:21:05 +32,W,02:10:06,05:21:06 +39,W,02:20:33,05:21:06 +32,W,02:14:45,05:21:07 +48,W,02:10:16,05:21:07 +43,M,02:13:11,05:21:07 +41,M,02:15:16,05:21:07 +63,M,02:15:41,05:21:07 +34,M,02:06:26,05:21:07 +25,W,02:05:42,05:21:07 +30,W,02:16:58,05:21:08 +28,M,02:01:11,05:21:08 +45,M,02:21:40,05:21:08 +45,W,02:04:22,05:21:09 +28,W,02:08:56,05:21:09 +43,M,02:11:44,05:21:09 +45,W,02:08:25,05:21:09 +26,W,02:21:41,05:21:10 +57,M,02:10:38,05:21:10 +42,M,02:24:36,05:21:11 +51,W,02:18:28,05:21:11 +50,M,02:13:18,05:21:11 +35,M,02:12:57,05:21:11 +39,W,02:19:03,05:21:11 +49,M,02:10:50,05:21:12 +25,M,02:15:39,05:21:12 +39,W,02:11:33,05:21:13 +44,W,02:09:28,05:21:13 +32,W,02:12:40,05:21:13 +25,M,02:17:48,05:21:13 +37,W,02:15:47,05:21:13 +47,M,02:14:17,05:21:14 +58,M,02:09:37,05:21:14 +59,M,02:25:12,05:21:15 +32,M,02:10:11,05:21:15 +39,M,01:44:35,05:21:15 +47,W,02:24:18,05:21:15 +51,M,02:10:14,05:21:15 +28,W,02:15:11,05:21:16 +59,M,02:14:53,05:21:16 +48,M,02:00:10,05:21:16 +60,M,02:07:15,05:21:16 +57,M,02:15:16,05:21:16 +46,W,02:13:27,05:21:16 +27,W,02:09:35,05:21:17 +53,W,02:15:20,05:21:17 +44,W,02:09:42,05:21:17 +31,M,02:16:43,05:21:18 +39,W,02:12:51,05:21:18 +21,W,02:02:55,05:21:18 +40,W,02:12:04,05:21:18 +58,W,02:14:44,05:21:19 +24,W,02:10:35,05:21:21 +64,M,02:11:09,05:21:21 +42,W,02:18:54,05:21:21 +47,M,02:18:08,05:21:22 +44,M,02:13:01,05:21:22 +34,M,02:08:42,05:21:22 +38,W,02:18:39,05:21:23 +41,W,02:16:18,05:21:23 +52,W,02:21:33,05:21:23 +45,M,02:21:49,05:21:23 +52,M,02:04:28,05:21:24 +30,W,02:15:05,05:21:24 +50,M,02:15:19,05:21:24 +19,M,02:00:47,05:21:24 +48,M,02:02:42,05:21:24 +29,M,02:12:58,05:21:25 +38,M,02:27:22,05:21:25 +36,W,02:20:13,05:21:25 +37,M,02:20:37,05:21:26 +26,W,02:05:10,05:21:26 +40,W,02:15:12,05:21:26 +19,M,02:16:55,05:21:26 +24,M,02:19:32,05:21:27 +39,M,02:19:46,05:21:27 +47,W,02:17:31,05:21:27 +38,M,02:19:02,05:21:27 +49,W,02:17:17,05:21:28 +53,M,02:14:46,05:21:28 +38,W,02:11:39,05:21:28 +37,W,02:07:38,05:21:29 +27,W,02:17:43,05:21:29 +44,W,02:14:48,05:21:29 +55,M,02:09:42,05:21:30 +35,W,02:21:48,05:21:30 +27,W,02:19:04,05:21:30 +40,W,02:10:20,05:21:31 +55,W,02:04:31,05:21:32 +54,M,02:07:31,05:21:32 +37,M,02:00:59,05:21:32 +42,M,02:17:41,05:21:32 +31,M,02:13:18,05:21:34 +40,W,02:19:00,05:21:34 +36,M,02:16:20,05:21:34 +53,W,02:19:05,05:21:35 +30,W,02:16:23,05:21:35 +28,M,02:19:26,05:21:35 +45,W,02:14:33,05:21:36 +23,M,02:06:27,05:21:36 +69,M,02:23:32,05:21:36 +37,W,02:18:05,05:21:37 +39,W,02:14:59,05:21:37 +40,M,02:20:25,05:21:37 +27,M,02:07:09,05:21:37 +42,M,02:19:00,05:21:37 +38,W,02:10:42,05:21:38 +42,W,02:13:14,05:21:38 +37,W,02:17:38,05:21:38 +36,W,02:17:14,05:21:38 +37,M,02:05:50,05:21:38 +43,W,02:23:17,05:21:39 +46,M,02:14:06,05:21:39 +64,W,02:22:03,05:21:39 +58,M,02:23:38,05:21:40 +55,M,02:05:09,05:21:40 +40,M,01:53:08,05:21:40 +49,W,02:07:40,05:21:41 +46,M,02:10:03,05:21:41 +44,W,02:11:46,05:21:42 +31,W,02:22:25,05:21:42 +39,M,02:17:19,05:21:42 +35,W,02:25:01,05:21:43 +39,M,02:10:24,05:21:43 +48,W,02:19:45,05:21:44 +43,W,02:18:46,05:21:44 +32,M,02:11:30,05:21:45 +32,M,02:10:25,05:21:45 +45,M,02:16:13,05:21:45 +37,M,02:14:48,05:21:45 +33,W,02:12:36,05:21:46 +35,W,02:12:36,05:21:47 +28,M,02:18:43,05:21:47 +53,M,02:15:12,05:21:47 +48,W,02:11:58,05:21:47 +44,W,02:15:11,05:21:48 +42,W,02:25:06,05:21:48 +43,W,02:16:52,05:21:48 +44,M,02:22:12,05:21:48 +49,M,02:13:45,05:21:49 +39,M,02:06:44,05:21:49 +31,W,02:17:30,05:21:50 +63,M,02:19:48,05:21:50 +39,M,02:12:17,05:21:50 +48,W,02:19:15,05:21:50 +56,W,02:32:12,05:21:51 +45,M,02:04:47,05:21:51 +46,M,02:10:51,05:21:51 +29,W,02:33:13,05:21:52 +41,M,02:09:25,05:21:52 +56,M,02:14:24,05:21:52 +34,W,02:10:17,05:21:53 +27,W,02:11:49,05:21:53 +37,M,02:15:56,05:21:53 +32,M,02:11:48,05:21:53 +28,M,02:14:24,05:21:53 +46,M,02:05:34,05:21:54 +50,M,02:21:23,05:21:54 +36,W,02:25:32,05:21:54 +50,W,02:13:25,05:21:54 +30,W,02:21:07,05:21:54 +35,M,02:02:07,05:21:54 +42,W,02:16:54,05:21:55 +38,W,02:07:29,05:21:55 +24,M,02:07:20,05:21:56 +21,M,02:20:14,05:21:56 +58,M,01:55:37,05:21:56 +49,M,02:04:37,05:21:56 +51,M,02:15:31,05:21:56 +38,M,02:13:37,05:21:56 +33,W,02:17:34,05:21:57 +54,M,02:13:24,05:21:57 +45,M,02:19:13,05:21:57 +41,M,02:09:29,05:21:57 +44,M,02:14:10,05:21:57 +47,M,02:11:12,05:21:58 +32,W,02:15:27,05:21:58 +29,W,02:09:16,05:21:59 +31,M,02:23:55,05:21:59 +54,W,02:17:00,05:21:59 +26,W,02:03:24,05:21:59 +60,M,02:13:07,05:22:00 +43,M,02:20:37,05:22:01 +50,M,02:08:25,05:22:01 +43,M,02:13:41,05:22:02 +40,M,02:15:49,05:22:02 +45,W,02:07:46,05:22:02 +38,W,02:03:52,05:22:04 +43,M,02:10:24,05:22:04 +26,M,02:10:16,05:22:04 +61,M,02:12:17,05:22:06 +23,M,02:06:46,05:22:06 +32,W,02:15:00,05:22:06 +28,M,02:20:40,05:22:06 +52,W,02:20:22,05:22:06 +42,M,02:19:45,05:22:07 +46,W,02:10:05,05:22:07 +58,M,02:20:03,05:22:07 +51,M,02:08:30,05:22:08 +34,M,02:12:33,05:22:09 +45,M,02:08:18,05:22:09 +38,M,02:22:48,05:22:09 +42,W,02:13:28,05:22:10 +37,W,02:12:27,05:22:11 +50,M,02:21:45,05:22:11 +31,M,02:09:16,05:22:12 +36,M,02:11:11,05:22:12 +42,W,02:12:56,05:22:12 +25,W,02:15:20,05:22:13 +35,M,02:14:27,05:22:13 +26,W,02:05:54,05:22:13 +39,M,02:23:12,05:22:13 +45,W,02:20:28,05:22:13 +62,M,02:05:09,05:22:13 +34,M,02:05:15,05:22:14 +74,M,02:14:02,05:22:14 +59,M,02:11:19,05:22:15 +37,W,02:10:50,05:22:15 +39,M,02:08:06,05:22:15 +47,M,02:17:04,05:22:15 +44,M,02:09:07,05:22:16 +33,W,02:11:53,05:22:16 +43,W,02:13:09,05:22:16 +44,W,02:09:32,05:22:16 +27,W,02:15:04,05:22:16 +48,M,02:17:09,05:22:17 +20,M,02:01:51,05:22:18 +40,M,02:19:48,05:22:18 +27,M,02:20:57,05:22:18 +36,M,01:59:37,05:22:18 +53,M,02:18:07,05:22:18 +43,M,02:12:19,05:22:19 +31,W,02:21:23,05:22:19 +53,M,02:23:07,05:22:19 +56,M,02:16:37,05:22:19 +27,W,02:05:12,05:22:19 +37,M,02:20:20,05:22:19 +48,M,02:20:55,05:22:19 +47,W,02:18:28,05:22:20 +32,W,02:18:20,05:22:20 +56,M,02:10:45,05:22:20 +30,W,02:20:23,05:22:21 +35,M,02:15:18,05:22:21 +41,M,02:14:18,05:22:21 +41,W,02:12:30,05:22:21 +29,M,02:02:21,05:22:21 +28,W,02:13:36,05:22:21 +50,M,02:18:33,05:22:21 +50,M,02:11:47,05:22:22 +20,M,02:15:49,05:22:22 +42,M,02:23:28,05:22:22 +45,M,02:11:38,05:22:23 +44,M,02:11:51,05:22:23 +45,M,02:17:36,05:22:23 +38,M,02:15:22,05:22:23 +22,M,02:13:55,05:22:23 +53,W,02:13:07,05:22:24 +36,M,02:14:05,05:22:24 +49,M,02:19:13,05:22:24 +55,W,02:11:56,05:22:25 +36,M,02:12:14,05:22:25 +26,W,02:06:48,05:22:25 +53,M,02:15:16,05:22:25 +29,W,02:12:44,05:22:26 +55,M,02:10:53,05:22:26 +56,M,02:13:34,05:22:26 +44,M,02:02:22,05:22:27 +34,W,02:20:17,05:22:27 +22,W,01:58:54,05:22:28 +45,M,02:07:22,05:22:28 +38,M,02:15:41,05:22:28 +40,M,02:08:28,05:22:28 +46,M,02:09:54,05:22:28 +40,W,02:19:35,05:22:28 +38,M,02:09:12,05:22:29 +39,M,02:14:09,05:22:29 +23,W,02:10:59,05:22:29 +61,M,02:25:01,05:22:30 +43,M,02:14:52,05:22:30 +32,W,02:16:40,05:22:30 +47,W,02:12:01,05:22:30 +52,W,02:10:25,05:22:30 +43,M,02:12:48,05:22:31 +53,M,02:22:23,05:22:31 +39,W,02:14:50,05:22:32 +29,W,02:09:26,05:22:32 +43,M,02:17:51,05:22:32 +39,W,02:17:43,05:22:33 +35,M,02:16:24,05:22:33 +41,W,02:18:42,05:22:33 +40,M,02:11:47,05:22:33 +35,W,02:15:50,05:22:34 +51,W,02:22:04,05:22:34 +24,W,02:14:49,05:22:34 +38,W,02:15:40,05:22:34 +61,M,02:04:50,05:22:35 +39,M,02:18:31,05:22:35 +53,W,02:15:49,05:22:36 +57,W,02:19:02,05:22:36 +48,M,02:19:12,05:22:36 +51,M,02:11:33,05:22:36 +33,W,02:16:59,05:22:37 +26,W,02:13:09,05:22:37 +39,W,02:18:24,05:22:37 +62,W,02:21:03,05:22:37 +50,M,02:01:21,05:22:37 +31,M,01:59:13,05:22:37 +58,M,02:01:42,05:22:38 +36,M,02:10:01,05:22:38 +40,M,02:11:35,05:22:38 +49,W,02:15:44,05:22:38 +56,W,02:16:37,05:22:38 +31,W,02:10:51,05:22:38 +46,W,02:13:20,05:22:39 +21,M,02:14:29,05:22:39 +41,M,02:19:01,05:22:39 +35,M,02:15:58,05:22:39 +57,W,02:28:57,05:22:40 +56,M,02:12:37,05:22:40 +28,W,02:07:13,05:22:40 +37,M,02:14:58,05:22:40 +52,M,02:10:57,05:22:40 +28,W,02:20:28,05:22:40 +30,W,02:12:12,05:22:40 +33,M,02:11:28,05:22:41 +47,M,02:09:10,05:22:41 +26,W,02:19:31,05:22:41 +37,M,02:15:43,05:22:41 +51,W,02:06:07,05:22:42 +42,M,02:15:13,05:22:42 +32,W,02:13:31,05:22:42 +50,M,02:02:58,05:22:42 +33,M,02:04:59,05:22:42 +34,M,02:18:33,05:22:42 +26,M,02:16:48,05:22:42 +49,W,02:20:49,05:22:43 +61,M,02:17:37,05:22:43 +41,W,02:12:15,05:22:44 +39,W,02:19:25,05:22:44 +33,W,02:16:39,05:22:44 +36,M,02:15:19,05:22:44 +25,M,02:12:37,05:22:44 +47,M,02:10:06,05:22:44 +48,W,02:19:58,05:22:44 +29,W,02:17:59,05:22:45 +36,M,02:04:40,05:22:45 +37,M,02:21:23,05:22:46 +40,M,02:08:08,05:22:47 +38,W,02:08:07,05:22:47 +59,M,02:15:19,05:22:47 +40,W,02:19:28,05:22:48 +28,M,02:07:23,05:22:48 +43,M,02:16:39,05:22:48 +28,W,02:14:44,05:22:48 +49,M,02:11:59,05:22:49 +26,W,02:17:03,05:22:49 +58,M,02:14:40,05:22:50 +34,W,02:14:44,05:22:51 +32,W,02:11:50,05:22:51 +50,W,02:09:27,05:22:51 +53,M,02:09:22,05:22:51 +22,M,02:05:48,05:22:51 +45,W,02:12:12,05:22:51 +34,W,02:13:36,05:22:51 +36,W,02:16:32,05:22:51 +34,W,02:14:22,05:22:52 +38,W,02:09:47,05:22:52 +50,W,02:11:42,05:22:52 +31,W,02:12:46,05:22:52 +38,M,02:05:51,05:22:52 +36,M,02:17:17,05:22:53 +32,W,02:19:34,05:22:53 +42,W,02:17:10,05:22:54 +28,M,02:05:36,05:22:54 +39,W,02:22:05,05:22:54 +29,W,02:10:38,05:22:55 +42,M,02:08:55,05:22:55 +45,W,02:01:49,05:22:56 +43,M,02:17:19,05:22:56 +40,W,02:09:35,05:22:56 +27,M,02:05:41,05:22:57 +36,M,02:05:27,05:22:58 +61,M,02:07:36,05:22:59 +53,M,02:12:39,05:22:59 +49,W,02:21:07,05:23:00 +35,W,02:05:20,05:23:00 +33,W,02:10:31,05:23:01 +30,W,02:02:36,05:23:01 +51,M,02:12:44,05:23:01 +62,M,02:18:32,05:23:01 +28,W,02:13:11,05:23:02 +61,M,02:14:36,05:23:03 +37,W,02:15:01,05:23:03 +50,W,02:17:24,05:23:03 +44,W,02:15:45,05:23:03 +56,W,02:01:17,05:23:03 +52,M,02:10:39,05:23:03 +36,W,02:09:20,05:23:03 +47,M,02:22:30,05:23:04 +31,M,02:24:48,05:23:04 +46,M,01:49:13,05:23:04 +43,M,02:07:52,05:23:05 +53,M,02:16:22,05:23:05 +32,W,02:01:08,05:23:05 +38,M,02:07:00,05:23:05 +36,W,02:08:03,05:23:06 +55,M,02:15:29,05:23:07 +43,M,02:10:01,05:23:07 +52,M,02:06:51,05:23:07 +36,W,02:19:00,05:23:07 +40,M,02:19:36,05:23:08 +36,W,02:20:06,05:23:08 +25,M,02:17:13,05:23:08 +36,M,02:18:18,05:23:08 +58,W,02:18:07,05:23:09 +38,M,02:07:39,05:23:09 +44,M,02:10:01,05:23:09 +30,M,02:03:33,05:23:09 +32,W,02:15:26,05:23:10 +52,M,02:19:09,05:23:10 +34,M,01:53:57,05:23:10 +35,W,02:01:31,05:23:10 +31,W,02:19:49,05:23:10 +28,W,02:21:32,05:23:11 +36,M,02:12:03,05:23:11 +30,M,02:21:06,05:23:11 +54,W,02:12:20,05:23:12 +51,M,02:14:18,05:23:12 +46,W,02:14:06,05:23:12 +25,M,02:12:06,05:23:12 +37,M,02:04:35,05:23:12 +55,W,02:15:04,05:23:13 +54,M,02:19:08,05:23:13 +33,W,02:16:03,05:23:13 +26,W,02:12:02,05:23:13 +48,W,02:21:28,05:23:14 +35,W,02:19:24,05:23:14 +62,M,02:18:59,05:23:14 +21,W,02:13:52,05:23:15 +46,M,02:10:28,05:23:16 +52,M,02:16:39,05:23:16 +48,W,02:17:03,05:23:16 +28,M,02:10:28,05:23:17 +35,W,02:14:58,05:23:17 +53,M,02:19:26,05:23:17 +40,M,02:16:18,05:23:17 +31,W,02:13:32,05:23:18 +32,M,02:10:34,05:23:19 +44,M,02:16:20,05:23:19 +46,W,02:21:37,05:23:19 +46,W,02:18:56,05:23:19 +54,M,02:10:24,05:23:20 +48,W,02:23:04,05:23:20 +55,M,02:17:07,05:23:20 +30,M,02:09:21,05:23:21 +43,M,02:09:54,05:23:21 +31,M,02:11:33,05:23:22 +55,M,02:13:37,05:23:22 +38,M,02:19:23,05:23:22 +40,W,02:29:20,05:23:22 +38,M,02:13:35,05:23:22 +29,W,02:14:43,05:23:23 +25,W,02:00:36,05:23:23 +46,M,02:13:16,05:23:23 +30,M,02:12:00,05:23:23 +43,M,02:08:42,05:23:24 +51,M,02:21:35,05:23:24 +51,M,02:02:34,05:23:24 +28,M,02:06:01,05:23:24 +28,M,02:09:08,05:23:25 +35,M,02:09:36,05:23:25 +44,W,02:04:03,05:23:25 +49,M,02:14:15,05:23:25 +30,W,02:17:57,05:23:25 +38,M,02:16:29,05:23:26 +27,W,02:11:44,05:23:27 +65,M,02:15:26,05:23:27 +55,W,02:17:12,05:23:27 +44,W,02:16:36,05:23:27 +36,W,02:11:41,05:23:27 +59,M,02:28:12,05:23:28 +64,M,02:20:24,05:23:28 +59,M,02:14:41,05:23:28 +39,M,02:08:30,05:23:29 +32,M,02:17:24,05:23:29 +29,W,02:11:50,05:23:30 +30,W,02:12:39,05:23:30 +32,W,01:59:00,05:23:30 +35,M,02:13:19,05:23:31 +60,M,02:09:25,05:23:31 +43,M,02:18:19,05:23:31 +45,M,02:13:49,05:23:31 +51,M,02:04:06,05:23:31 +38,W,02:13:35,05:23:31 +30,M,02:01:25,05:23:31 +44,M,02:13:22,05:23:32 +43,W,02:08:48,05:23:32 +40,M,02:15:42,05:23:34 +52,M,02:09:53,05:23:34 +26,M,02:10:37,05:23:35 +25,W,02:06:32,05:23:35 +54,M,02:24:18,05:23:35 +34,M,02:19:35,05:23:35 +48,M,02:19:20,05:23:35 +47,W,02:13:13,05:23:36 +51,M,02:16:00,05:23:36 +37,M,02:18:15,05:23:36 +49,M,02:06:55,05:23:36 +24,M,01:57:25,05:23:37 +61,M,02:06:41,05:23:37 +44,M,02:14:23,05:23:37 +49,M,02:11:13,05:23:38 +29,M,02:29:14,05:23:39 +33,M,02:04:34,05:23:39 +31,M,02:08:48,05:23:40 +40,W,02:23:47,05:23:40 +24,M,02:19:25,05:23:41 +45,M,02:19:56,05:23:41 +32,M,02:02:19,05:23:41 +26,W,02:14:34,05:23:41 +38,M,02:21:32,05:23:42 +35,M,02:17:49,05:23:42 +39,M,02:05:52,05:23:43 +40,W,02:10:48,05:23:44 +50,M,02:23:22,05:23:44 +41,M,02:07:53,05:23:44 +54,M,02:19:53,05:23:44 +38,M,02:14:00,05:23:44 +33,W,02:16:16,05:23:45 +33,M,02:02:01,05:23:45 +48,M,01:59:16,05:23:46 +56,M,02:30:45,05:23:46 +50,W,02:17:51,05:23:46 +66,M,02:17:25,05:23:46 +31,W,02:15:53,05:23:46 +28,M,02:09:42,05:23:46 +44,M,02:28:33,05:23:47 +34,W,02:24:55,05:23:47 +44,M,02:07:45,05:23:48 +40,M,02:20:18,05:23:48 +31,M,02:07:44,05:23:48 +46,M,02:17:40,05:23:48 +46,M,02:20:54,05:23:49 +50,M,02:15:32,05:23:49 +49,M,02:00:19,05:23:50 +40,W,02:08:31,05:23:50 +43,M,02:09:48,05:23:50 +27,W,02:09:55,05:23:51 +52,M,02:22:22,05:23:52 +52,W,02:20:10,05:23:52 +44,W,02:39:00,05:23:52 +44,M,02:08:10,05:23:52 +44,M,01:58:55,05:23:52 +55,W,02:09:09,05:23:52 +43,M,02:14:27,05:23:52 +31,W,02:14:53,05:23:52 +43,M,02:22:53,05:23:53 +48,M,02:12:51,05:23:53 +42,W,02:19:31,05:23:53 +25,M,02:15:14,05:23:53 +29,W,02:18:59,05:23:53 +42,M,02:22:40,05:23:53 +37,M,02:15:06,05:23:53 +53,M,02:05:25,05:23:54 +49,M,02:18:32,05:23:54 +34,M,02:07:58,05:23:55 +42,M,02:20:52,05:23:55 +42,W,02:17:30,05:23:55 +73,M,02:53:06,05:23:55 +32,M,02:00:39,05:23:56 +45,W,02:08:36,05:23:56 +38,W,02:12:33,05:23:56 +30,W,02:08:36,05:23:56 +34,W,02:19:41,05:23:56 +51,M,02:17:59,05:23:57 +31,M,02:09:55,05:23:57 +48,M,02:21:14,05:23:57 +46,W,02:12:31,05:23:57 +37,M,02:11:44,05:23:57 +28,M,02:08:08,05:23:58 +40,M,02:12:43,05:23:58 +35,W,02:15:01,05:23:58 +41,W,02:16:07,05:23:59 +35,M,02:17:04,05:23:59 +34,M,02:14:42,05:23:59 +37,W,02:12:39,05:23:59 +53,M,02:13:08,05:23:59 +45,W,02:18:23,05:24:00 +45,M,02:18:12,05:24:00 +30,M,02:08:37,05:24:00 +45,M,02:03:16,05:24:00 +47,M,02:09:44,05:24:01 +21,M,02:11:32,05:24:02 +44,W,02:10:32,05:24:02 +34,W,02:19:54,05:24:02 +29,W,02:16:50,05:24:03 +44,M,02:17:15,05:24:03 +39,W,02:18:50,05:24:03 +46,M,02:10:26,05:24:04 +62,W,02:11:43,05:24:04 +43,W,02:13:39,05:24:04 +36,W,02:08:34,05:24:04 +26,W,02:15:48,05:24:04 +24,W,02:16:46,05:24:04 +42,M,02:11:45,05:24:04 +41,W,02:20:52,05:24:04 +39,W,02:12:42,05:24:04 +44,M,02:10:08,05:24:06 +50,M,02:13:54,05:24:06 +62,M,02:08:11,05:24:07 +50,M,02:08:41,05:24:07 +45,W,02:10:56,05:24:08 +54,M,02:11:15,05:24:08 +46,M,02:20:25,05:24:08 +48,W,02:10:35,05:24:08 +61,M,02:18:27,05:24:08 +53,W,02:21:03,05:24:08 +47,M,02:12:38,05:24:09 +46,M,02:13:00,05:24:09 +69,M,02:17:34,05:24:10 +31,M,02:14:54,05:24:10 +48,M,02:14:25,05:24:10 +38,M,02:16:59,05:24:10 +59,W,02:09:05,05:24:11 +58,M,02:22:26,05:24:11 +56,W,02:16:53,05:24:11 +45,W,02:07:09,05:24:12 +45,M,02:12:25,05:24:12 +34,M,02:09:58,05:24:12 +54,W,02:17:43,05:24:12 +30,M,02:17:06,05:24:12 +40,W,02:15:32,05:24:13 +33,W,02:15:15,05:24:13 +49,M,02:11:02,05:24:13 +40,M,02:12:28,05:24:14 +29,M,02:13:44,05:24:14 +34,M,02:06:02,05:24:14 +50,M,02:16:26,05:24:14 +33,W,02:18:28,05:24:16 +50,M,02:18:39,05:24:16 +57,W,02:17:56,05:24:16 +51,W,02:15:27,05:24:16 +44,W,02:17:16,05:24:17 +33,W,02:18:24,05:24:17 +48,W,02:10:01,05:24:17 +64,M,02:17:07,05:24:18 +36,M,02:11:06,05:24:18 +29,W,02:13:04,05:24:18 +44,W,02:16:51,05:24:19 +46,M,02:20:11,05:24:20 +49,M,02:09:57,05:24:20 +26,W,02:13:22,05:24:20 +31,W,02:24:07,05:24:21 +34,W,02:02:57,05:24:22 +36,W,02:11:26,05:24:22 +57,W,02:20:17,05:24:24 +44,M,02:11:04,05:24:24 +43,M,02:17:33,05:24:24 +38,M,02:20:18,05:24:24 +25,M,02:12:57,05:24:24 +48,M,02:12:31,05:24:24 +33,M,02:07:03,05:24:25 +31,W,02:16:12,05:24:26 +39,W,02:19:22,05:24:26 +55,M,02:19:17,05:24:27 +29,M,01:59:05,05:24:28 +46,M,02:12:09,05:24:28 +36,W,02:06:15,05:24:29 +38,M,02:19:40,05:24:29 +51,M,02:14:15,05:24:29 +27,M,02:13:21,05:24:29 +33,M,02:21:02,05:24:29 +40,M,02:24:22,05:24:30 +47,W,02:24:05,05:24:30 +30,M,02:08:43,05:24:30 +39,M,02:11:13,05:24:30 +48,M,02:14:46,05:24:31 +33,W,02:21:26,05:24:32 +46,W,02:20:06,05:24:32 +45,M,02:17:35,05:24:32 +46,M,02:13:41,05:24:32 +32,M,02:20:54,05:24:33 +21,M,02:05:04,05:24:33 +43,M,02:09:33,05:24:33 +41,M,01:58:03,05:24:33 +31,M,02:11:49,05:24:34 +57,W,02:28:09,05:24:34 +37,M,02:15:52,05:24:34 +53,W,02:11:29,05:24:35 +56,M,02:19:57,05:24:35 +40,M,02:13:51,05:24:36 +29,W,02:15:09,05:24:36 +74,M,02:13:31,05:24:36 +34,M,02:20:02,05:24:36 +61,M,02:02:00,05:24:36 +38,W,02:11:56,05:24:37 +35,M,02:14:27,05:24:38 +43,M,02:06:16,05:24:38 +28,W,02:18:06,05:24:38 +47,W,02:13:29,05:24:38 +37,W,02:17:28,05:24:38 +44,W,02:11:00,05:24:39 +29,W,02:14:50,05:24:39 +51,W,02:17:04,05:24:39 +50,M,02:07:26,05:24:40 +56,M,02:29:05,05:24:40 +46,W,02:17:05,05:24:42 +24,W,02:15:21,05:24:43 +47,W,02:06:08,05:24:44 +44,W,02:13:47,05:24:44 +55,M,02:14:08,05:24:44 +34,M,02:04:23,05:24:45 +49,M,02:16:07,05:24:45 +34,M,02:16:51,05:24:45 +48,M,02:16:47,05:24:46 +43,W,02:25:22,05:24:46 +39,W,02:15:34,05:24:46 +40,W,02:22:27,05:24:46 +54,M,02:07:00,05:24:47 +32,M,02:06:06,05:24:47 +45,W,02:15:32,05:24:47 +60,M,02:17:12,05:24:47 +59,M,02:10:26,05:24:48 +39,W,02:12:27,05:24:48 +30,W,02:11:37,05:24:48 +59,M,02:14:43,05:24:48 +29,W,02:23:21,05:24:49 +30,M,02:10:19,05:24:49 +28,W,02:09:26,05:24:49 +46,W,02:23:09,05:24:49 +30,M,02:13:50,05:24:49 +30,M,02:10:55,05:24:50 +51,M,02:12:46,05:24:50 +44,M,02:11:33,05:24:51 +35,W,02:13:15,05:24:51 +30,W,02:29:17,05:24:52 +47,M,02:00:55,05:24:53 +25,W,02:15:15,05:24:53 +49,M,02:28:32,05:24:53 +36,W,02:19:36,05:24:54 +65,M,02:14:55,05:24:55 +24,M,02:23:42,05:24:55 +63,M,02:03:26,05:24:55 +36,M,02:16:44,05:24:56 +32,W,02:15:29,05:24:56 +47,M,02:14:26,05:24:56 +52,M,02:08:38,05:24:56 +41,M,02:11:12,05:24:57 +50,M,02:02:57,05:24:57 +56,W,02:15:46,05:24:57 +25,W,02:15:42,05:24:57 +46,M,02:15:50,05:24:58 +48,M,02:13:28,05:24:58 +47,M,02:18:04,05:24:58 +30,M,02:05:11,05:24:59 +35,M,02:20:10,05:24:59 +53,M,02:30:57,05:24:59 +66,M,02:09:14,05:24:59 +53,W,02:24:33,05:25:00 +47,M,02:10:52,05:25:01 +44,W,02:13:43,05:25:01 +26,W,02:16:21,05:25:01 +62,W,02:13:28,05:25:01 +41,M,02:25:12,05:25:02 +52,M,02:19:20,05:25:02 +35,M,02:20:43,05:25:02 +29,W,02:10:45,05:25:02 +42,W,02:16:24,05:25:02 +37,M,02:14:23,05:25:02 +32,M,02:20:08,05:25:02 +50,W,02:13:09,05:25:03 +56,W,02:20:33,05:25:03 +36,W,02:20:50,05:25:04 +47,W,02:09:50,05:25:04 +37,W,02:11:19,05:25:05 +47,M,02:11:12,05:25:06 +38,W,02:18:35,05:25:06 +49,M,02:19:56,05:25:06 +46,M,02:13:08,05:25:07 +48,M,02:21:48,05:25:08 +56,W,02:23:00,05:25:09 +24,M,02:18:23,05:25:09 +33,W,02:12:23,05:25:09 +47,W,02:13:55,05:25:09 +41,W,02:11:51,05:25:10 +33,M,02:21:13,05:25:10 +39,M,02:06:14,05:25:10 +49,W,02:19:22,05:25:10 +39,M,02:22:39,05:25:11 +35,W,02:20:19,05:25:11 +45,W,02:21:35,05:25:11 +51,M,02:10:42,05:25:12 +51,M,02:13:30,05:25:12 +34,W,02:13:07,05:25:12 +29,W,02:17:28,05:25:14 +60,M,02:11:58,05:25:14 +27,W,02:22:14,05:25:15 +27,M,02:29:29,05:25:15 +26,W,02:13:29,05:25:15 +32,M,02:23:49,05:25:16 +33,W,02:22:13,05:25:17 +45,W,02:17:48,05:25:17 +48,M,02:19:34,05:25:17 +50,W,02:19:18,05:25:17 +40,M,02:29:32,05:25:18 +57,M,02:22:33,05:25:18 +44,W,02:15:33,05:25:18 +49,W,02:10:40,05:25:18 +61,W,02:15:00,05:25:19 +31,M,02:07:15,05:25:19 +26,W,02:19:18,05:25:20 +45,W,02:21:37,05:25:20 +59,M,02:05:03,05:25:20 +69,M,02:16:28,05:25:20 +49,M,02:09:49,05:25:20 +29,W,02:20:32,05:25:20 +44,W,02:12:29,05:25:20 +33,W,02:20:25,05:25:20 +52,M,02:16:21,05:25:21 +58,M,02:17:45,05:25:21 +49,W,02:08:37,05:25:21 +27,W,02:10:55,05:25:22 +50,M,02:10:25,05:25:22 +41,W,02:17:40,05:25:23 +49,M,02:18:56,05:25:23 +40,W,02:11:13,05:25:23 +46,M,02:07:59,05:25:23 +41,W,02:28:46,05:25:23 +26,W,02:15:37,05:25:24 +59,M,02:23:27,05:25:24 +27,W,02:12:43,05:25:26 +43,M,02:02:58,05:25:27 +37,W,02:09:46,05:25:27 +38,W,02:17:46,05:25:27 +40,M,02:06:08,05:25:27 +35,W,02:15:28,05:25:27 +34,M,02:06:46,05:25:28 +40,M,02:01:24,05:25:28 +45,M,02:24:46,05:25:28 +46,W,02:15:03,05:25:28 +33,W,02:15:58,05:25:29 +49,W,02:11:35,05:25:29 +44,M,02:05:54,05:25:30 +49,M,02:16:09,05:25:30 +40,M,02:11:30,05:25:30 +43,M,02:02:26,05:25:31 +29,W,02:19:56,05:25:31 +30,M,02:10:41,05:25:32 +65,W,02:23:58,05:25:32 +43,M,02:19:19,05:25:32 +55,M,02:10:15,05:25:33 +44,M,02:22:29,05:25:33 +24,M,02:10:37,05:25:33 +30,W,02:22:24,05:25:34 +30,M,02:10:43,05:25:35 +38,M,02:16:54,05:25:35 +66,M,02:17:50,05:25:36 +24,M,02:15:35,05:25:36 +39,M,02:14:13,05:25:36 +66,M,02:09:25,05:25:37 +78,W,02:25:05,05:25:37 +45,W,02:15:40,05:25:37 +42,W,02:16:11,05:25:37 +26,W,02:19:36,05:25:38 +41,M,02:07:21,05:25:38 +45,W,02:12:58,05:25:39 +48,W,02:16:37,05:25:39 +56,W,02:18:21,05:25:39 +40,M,02:11:43,05:25:40 +50,W,02:19:12,05:25:40 +43,M,02:13:50,05:25:40 +50,M,02:23:25,05:25:40 +53,W,02:27:15,05:25:40 +47,W,02:16:05,05:25:41 +40,M,02:06:43,05:25:41 +47,M,02:14:53,05:25:41 +45,W,02:19:13,05:25:42 +39,M,02:15:55,05:25:42 +55,W,02:13:35,05:25:42 +27,W,02:16:00,05:25:42 +53,M,02:11:39,05:25:42 +40,W,02:24:40,05:25:42 +37,M,02:18:09,05:25:43 +38,W,02:09:07,05:25:43 +43,W,02:14:56,05:25:43 +26,W,02:09:59,05:25:43 +25,W,02:21:48,05:25:44 +49,W,02:22:39,05:25:44 +35,W,02:11:05,05:25:45 +47,W,02:15:49,05:25:45 +49,M,02:07:35,05:25:45 +54,M,02:20:48,05:25:46 +47,W,02:16:22,05:25:46 +45,M,02:23:16,05:25:46 +28,W,02:19:50,05:25:46 +25,M,02:13:32,05:25:46 +40,M,02:08:27,05:25:47 +29,W,02:07:33,05:25:47 +66,M,02:06:20,05:25:47 +38,W,02:14:54,05:25:48 +45,M,02:25:32,05:25:48 +54,M,02:10:50,05:25:48 +33,M,02:12:31,05:25:48 +59,M,02:10:30,05:25:49 +48,M,02:20:39,05:25:49 +56,W,02:07:57,05:25:49 +27,W,02:11:05,05:25:50 +55,M,02:07:10,05:25:51 +39,M,02:17:33,05:25:51 +58,M,02:23:21,05:25:51 +22,W,02:26:54,05:25:51 +63,W,02:07:15,05:25:52 +34,M,02:10:24,05:25:52 +33,W,02:31:24,05:25:53 +46,M,02:27:02,05:25:53 +38,M,02:15:44,05:25:53 +31,W,02:16:48,05:25:53 +38,W,02:14:44,05:25:53 +43,M,02:14:51,05:25:54 +45,M,02:22:56,05:25:54 +24,W,02:16:46,05:25:54 +50,M,02:14:19,05:25:54 +40,M,02:14:14,05:25:55 +54,M,02:11:14,05:25:55 +44,M,02:05:43,05:25:55 +33,M,02:09:51,05:25:56 +40,M,02:29:19,05:25:56 +41,W,02:20:00,05:25:56 +56,M,02:12:25,05:25:56 +42,W,02:13:19,05:25:56 +39,M,02:17:09,05:25:56 +52,W,02:21:03,05:25:57 +44,W,02:22:45,05:25:57 +37,M,02:17:03,05:25:57 +35,W,02:12:22,05:25:57 +30,M,02:03:40,05:25:58 +56,M,02:16:12,05:25:58 +54,M,02:19:41,05:25:58 +54,M,02:23:40,05:25:58 +59,M,02:19:52,05:25:59 +27,W,02:18:48,05:25:59 +55,M,02:14:06,05:26:00 +40,M,02:04:11,05:26:01 +40,M,02:14:51,05:26:01 +47,M,02:10:44,05:26:01 +42,M,02:15:09,05:26:01 +50,W,02:13:08,05:26:01 +43,W,02:15:55,05:26:01 +39,M,02:16:48,05:26:01 +60,W,02:15:27,05:26:02 +36,W,02:20:48,05:26:02 +24,W,02:15:26,05:26:02 +24,W,02:24:42,05:26:02 +49,M,02:06:54,05:26:02 +55,M,02:21:33,05:26:02 +33,M,02:08:21,05:26:02 +50,M,02:15:00,05:26:02 +31,M,02:11:38,05:26:02 +57,M,02:08:13,05:26:03 +32,M,02:12:57,05:26:03 +38,M,02:16:57,05:26:03 +26,M,02:02:56,05:26:03 +37,M,02:24:31,05:26:04 +53,M,02:22:42,05:26:05 +37,M,02:03:14,05:26:06 +21,W,02:16:09,05:26:07 +37,M,02:16:40,05:26:07 +24,M,02:15:28,05:26:07 +34,W,02:13:56,05:26:08 +53,M,02:12:55,05:26:08 +47,M,02:15:56,05:26:08 +35,W,02:14:37,05:26:09 +45,M,02:11:30,05:26:09 +26,M,02:19:02,05:26:09 +52,M,02:24:20,05:26:09 +42,M,02:21:21,05:26:09 +37,W,02:15:59,05:26:10 +31,M,02:02:07,05:26:10 +37,M,02:22:23,05:26:10 +40,M,02:18:51,05:26:10 +50,M,02:18:08,05:26:11 +44,M,02:03:08,05:26:11 +65,M,02:07:06,05:26:11 +38,W,02:15:01,05:26:12 +54,W,02:13:42,05:26:12 +51,M,02:07:07,05:26:12 +25,W,02:17:29,05:26:12 +50,M,02:09:25,05:26:13 +37,M,02:22:40,05:26:13 +58,W,02:21:14,05:26:13 +34,M,02:16:19,05:26:13 +33,W,02:18:05,05:26:15 +25,W,02:20:18,05:26:15 +52,M,02:10:37,05:26:15 +30,W,02:09:36,05:26:16 +50,M,02:05:06,05:26:17 +50,W,02:17:41,05:26:17 +28,W,02:12:16,05:26:17 +48,W,02:16:31,05:26:17 +39,M,02:09:43,05:26:17 +54,W,02:15:18,05:26:17 +48,W,02:27:43,05:26:18 +66,M,02:24:21,05:26:18 +47,W,02:08:18,05:26:18 +46,W,02:11:40,05:26:18 +38,W,02:11:52,05:26:18 +65,M,02:15:40,05:26:19 +61,W,02:16:11,05:26:19 +29,M,02:19:04,05:26:19 +49,M,02:06:35,05:26:19 +35,M,02:08:53,05:26:19 +29,M,02:18:25,05:26:19 +39,M,02:04:58,05:26:20 +38,M,02:20:18,05:26:20 +32,W,02:10:18,05:26:20 +29,M,02:12:32,05:26:21 +37,W,02:16:19,05:26:21 +40,W,02:13:16,05:26:21 +43,M,02:18:31,05:26:21 +31,W,02:18:57,05:26:21 +45,M,02:21:43,05:26:21 +42,W,02:21:03,05:26:22 +46,M,02:22:37,05:26:22 +53,W,02:12:24,05:26:22 +32,M,02:02:36,05:26:23 +49,W,02:13:06,05:26:23 +43,W,02:11:43,05:26:23 +47,M,02:13:52,05:26:23 +40,M,02:15:50,05:26:23 +26,W,02:14:22,05:26:23 +40,W,02:21:07,05:26:24 +33,W,02:24:11,05:26:24 +43,W,02:16:16,05:26:25 +32,M,01:59:52,05:26:25 +47,M,02:06:19,05:26:25 +30,W,02:18:28,05:26:26 +45,W,02:18:14,05:26:26 +25,W,02:17:07,05:26:26 +36,W,02:15:18,05:26:27 +29,W,02:12:55,05:26:27 +34,W,02:20:55,05:26:27 +48,W,02:26:56,05:26:27 +43,M,02:13:13,05:26:27 +52,M,02:18:59,05:26:27 +39,M,02:16:12,05:26:27 +43,W,02:26:45,05:26:28 +27,M,02:04:49,05:26:28 +35,W,02:17:55,05:26:29 +43,M,02:13:41,05:26:29 +36,M,02:18:30,05:26:30 +44,W,02:18:02,05:26:30 +34,M,02:00:25,05:26:31 +55,M,02:18:13,05:26:31 +28,M,02:14:10,05:26:31 +39,M,02:22:36,05:26:31 +47,M,02:23:29,05:26:32 +38,W,02:10:49,05:26:33 +38,M,02:03:30,05:26:33 +44,W,02:16:20,05:26:34 +56,W,02:09:32,05:26:34 +28,W,02:19:18,05:26:35 +36,W,02:20:19,05:26:35 +58,M,02:12:55,05:26:35 +31,W,02:15:28,05:26:36 +42,M,02:19:42,05:26:36 +43,W,02:14:45,05:26:36 +46,M,02:17:37,05:26:36 +41,M,02:14:46,05:26:36 +45,W,02:07:36,05:26:37 +59,W,02:18:08,05:26:37 +40,W,02:25:09,05:26:38 +29,W,02:20:29,05:26:38 +34,M,02:23:10,05:26:40 +51,M,02:10:04,05:26:40 +40,W,02:10:30,05:26:41 +52,M,02:16:41,05:26:41 +24,M,02:17:11,05:26:41 +54,M,02:23:41,05:26:41 +35,M,02:13:53,05:26:41 +47,M,02:15:11,05:26:42 +29,M,02:20:53,05:26:42 +35,W,02:25:28,05:26:42 +42,M,02:33:05,05:26:43 +39,M,02:15:12,05:26:44 +25,W,02:13:35,05:26:44 +40,W,02:07:11,05:26:44 +38,W,02:18:14,05:26:44 +24,W,02:05:10,05:26:45 +36,M,02:13:42,05:26:46 +33,M,02:11:10,05:26:46 +47,M,02:23:18,05:26:46 +28,M,02:00:26,05:26:46 +29,M,02:19:34,05:26:47 +35,W,02:20:44,05:26:47 +45,W,02:12:03,05:26:48 +46,M,02:20:09,05:26:48 +22,M,01:49:59,05:26:48 +34,M,02:13:03,05:26:48 +43,M,02:18:04,05:26:48 +42,M,02:14:59,05:26:49 +35,M,02:01:59,05:26:49 +29,W,02:20:46,05:26:49 +45,W,02:17:00,05:26:49 +37,M,02:19:06,05:26:49 +31,M,02:13:19,05:26:50 +56,M,02:19:27,05:26:50 +42,M,02:08:33,05:26:50 +70,M,02:13:52,05:26:50 +35,M,02:20:04,05:26:51 +31,W,02:12:16,05:26:51 +42,W,02:16:09,05:26:51 +35,W,02:13:02,05:26:52 +46,M,02:11:54,05:26:52 +33,W,02:20:08,05:26:52 +31,M,02:16:00,05:26:52 +45,W,02:15:18,05:26:53 +57,M,02:17:22,05:26:53 +40,W,02:11:55,05:26:54 +29,W,02:13:01,05:26:54 +38,M,02:12:54,05:26:54 +36,W,02:22:56,05:26:54 +32,W,02:14:46,05:26:55 +50,W,02:14:24,05:26:55 +50,W,02:19:59,05:26:55 +33,W,02:22:35,05:26:55 +40,W,02:23:05,05:26:55 +50,W,02:21:08,05:26:55 +31,M,02:12:38,05:26:56 +52,M,02:05:22,05:26:56 +44,M,02:12:40,05:26:56 +56,M,02:00:40,05:26:56 +52,M,02:20:21,05:26:56 +32,W,02:18:34,05:26:57 +40,W,02:21:37,05:26:58 +42,W,02:28:38,05:26:58 +47,W,02:18:05,05:27:00 +30,W,02:20:05,05:27:00 +45,W,02:19:18,05:27:01 +37,M,02:13:35,05:27:01 +37,W,02:17:26,05:27:01 +36,M,02:19:46,05:27:03 +51,M,02:06:54,05:27:03 +38,M,01:58:25,05:27:03 +52,M,02:11:38,05:27:04 +51,W,02:12:30,05:27:04 +62,M,02:22:03,05:27:04 +41,W,02:23:03,05:27:04 +49,M,02:13:16,05:27:04 +36,M,02:06:12,05:27:05 +49,W,02:22:51,05:27:06 +32,W,02:20:36,05:27:06 +56,M,02:16:35,05:27:06 +31,M,02:11:32,05:27:07 +36,M,02:08:18,05:27:07 +36,W,02:26:16,05:27:07 +63,M,02:23:28,05:27:09 +39,M,02:10:40,05:27:09 +36,W,02:17:26,05:27:09 +59,M,02:18:29,05:27:10 +47,M,02:02:21,05:27:10 +39,W,02:15:34,05:27:10 +36,M,02:11:31,05:27:10 +35,W,02:23:47,05:27:10 +29,W,02:19:22,05:27:10 +56,M,02:07:07,05:27:11 +37,W,02:12:32,05:27:11 +40,W,02:20:54,05:27:11 +30,W,02:14:45,05:27:11 +36,M,01:53:54,05:27:11 +47,W,02:11:15,05:27:12 +56,M,02:00:42,05:27:12 +44,M,02:11:16,05:27:13 +46,W,02:09:02,05:27:13 +30,M,02:26:52,05:27:13 +50,W,02:12:41,05:27:14 +33,M,02:10:05,05:27:14 +44,W,02:14:40,05:27:14 +45,M,01:59:00,05:27:15 +40,W,02:02:42,05:27:15 +27,W,02:18:49,05:27:16 +29,M,02:25:41,05:27:16 +38,M,02:11:00,05:27:17 +43,W,02:04:40,05:27:17 +39,W,02:12:18,05:27:17 +52,M,02:24:20,05:27:17 +43,M,02:19:47,05:27:17 +32,M,02:11:17,05:27:18 +40,W,02:19:09,05:27:18 +30,W,02:15:56,05:27:19 +35,W,02:19:47,05:27:19 +37,M,02:20:13,05:27:19 +31,W,02:14:11,05:27:19 +28,W,02:15:49,05:27:20 +42,M,02:02:00,05:27:20 +23,M,02:04:44,05:27:21 +29,M,02:20:02,05:27:21 +32,W,02:18:18,05:27:21 +20,M,02:16:29,05:27:21 +43,M,01:58:38,05:27:22 +49,M,02:16:26,05:27:23 +29,W,02:17:19,05:27:23 +45,M,02:17:46,05:27:24 +49,W,02:21:59,05:27:24 +29,W,02:26:55,05:27:24 +48,W,02:23:48,05:27:25 +43,M,02:09:44,05:27:25 +48,W,02:14:23,05:27:25 +38,W,02:25:17,05:27:25 +66,M,02:20:30,05:27:25 +41,W,02:27:02,05:27:25 +59,M,02:23:15,05:27:26 +29,M,02:13:36,05:27:27 +38,W,02:19:16,05:27:27 +43,M,02:12:35,05:27:28 +34,W,02:13:26,05:27:28 +40,W,02:24:53,05:27:29 +41,M,02:16:18,05:27:29 +51,M,02:17:00,05:27:30 +23,M,02:17:47,05:27:30 +30,M,02:10:59,05:27:30 +45,W,02:13:33,05:27:31 +31,W,02:18:52,05:27:31 +27,W,02:19:29,05:27:31 +37,M,02:04:47,05:27:32 +25,M,02:14:36,05:27:32 +39,M,02:10:49,05:27:32 +24,W,02:25:03,05:27:32 +24,M,02:22:27,05:27:32 +51,M,02:19:14,05:27:33 +31,W,02:15:33,05:27:33 +55,W,02:15:10,05:27:33 +30,W,02:21:19,05:27:33 +39,M,02:10:37,05:27:33 +26,M,02:19:38,05:27:34 +36,W,02:27:24,05:27:34 +44,M,02:23:06,05:27:35 +40,W,02:23:20,05:27:35 +58,M,02:07:03,05:27:35 +53,M,02:21:49,05:27:35 +44,W,02:14:44,05:27:35 +29,M,02:16:23,05:27:35 +38,M,02:10:36,05:27:35 +46,M,02:18:58,05:27:36 +55,M,02:07:04,05:27:36 +36,M,02:13:02,05:27:36 +45,W,02:19:15,05:27:36 +27,M,02:19:37,05:27:36 +50,W,02:20:42,05:27:36 +30,W,02:18:49,05:27:37 +45,M,02:10:18,05:27:37 +51,M,02:18:58,05:27:39 +59,W,02:20:06,05:27:39 +38,M,02:09:43,05:27:39 +45,M,02:07:56,05:27:40 +64,M,02:14:51,05:27:40 +34,M,02:20:01,05:27:40 +57,M,02:12:25,05:27:41 +66,M,02:15:27,05:27:41 +50,M,02:14:04,05:27:41 +32,M,02:15:20,05:27:41 +41,M,02:05:04,05:27:42 +54,M,02:17:22,05:27:42 +47,W,02:15:57,05:27:43 +30,M,02:08:17,05:27:44 +43,M,02:10:34,05:27:44 +25,W,02:19:20,05:27:44 +27,W,02:24:23,05:27:45 +28,W,02:18:22,05:27:45 +36,M,02:11:00,05:27:45 +41,M,02:06:51,05:27:46 +52,W,02:16:33,05:27:46 +45,M,02:07:28,05:27:47 +35,M,02:08:45,05:27:47 +33,W,02:16:39,05:27:47 +31,M,02:10:31,05:27:48 +38,M,02:15:48,05:27:48 +63,M,02:10:36,05:27:48 +25,W,02:22:56,05:27:49 +55,M,02:11:57,05:27:49 +36,W,02:10:04,05:27:50 +57,M,02:11:50,05:27:50 +52,M,02:16:58,05:27:50 +48,W,02:28:48,05:27:50 +22,M,02:22:25,05:27:50 +28,W,02:23:49,05:27:51 +34,M,02:07:21,05:27:51 +29,W,02:19:30,05:27:51 +45,W,02:10:54,05:27:51 +40,W,02:15:15,05:27:51 +28,W,02:18:29,05:27:51 +45,M,02:10:58,05:27:51 +32,M,02:09:50,05:27:52 +43,M,02:14:36,05:27:52 +28,W,02:14:16,05:27:52 +46,M,02:23:47,05:27:53 +37,M,02:18:11,05:27:53 +26,W,02:20:44,05:27:53 +50,W,02:23:27,05:27:53 +33,W,02:34:18,05:27:53 +31,M,02:10:21,05:27:54 +20,M,02:09:44,05:27:55 +44,M,02:05:04,05:27:55 +41,W,02:16:35,05:27:56 +42,M,02:18:27,05:27:56 +38,W,02:09:47,05:27:56 +59,W,02:20:25,05:27:56 +62,M,02:11:49,05:27:57 +36,M,02:21:34,05:27:57 +30,W,02:24:04,05:27:57 +50,M,02:15:27,05:27:57 +30,W,02:15:15,05:27:57 +33,M,02:13:09,05:27:59 +24,W,02:25:24,05:27:59 +24,W,02:13:37,05:28:00 +32,W,02:22:43,05:28:00 +25,W,02:16:08,05:28:01 +64,M,02:18:18,05:28:01 +27,W,02:20:27,05:28:01 +34,M,02:18:33,05:28:02 +48,M,02:20:53,05:28:02 +29,W,02:24:45,05:28:03 +46,M,02:13:13,05:28:03 +26,W,02:08:59,05:28:03 +61,M,02:16:28,05:28:04 +34,W,02:15:36,05:28:04 +63,M,02:10:07,05:28:04 +44,M,02:12:00,05:28:04 +38,M,02:15:50,05:28:05 +39,M,02:10:38,05:28:05 +50,M,02:09:59,05:28:05 +47,M,02:11:30,05:28:05 +54,M,02:16:04,05:28:05 +32,W,02:18:11,05:28:05 +50,M,02:14:26,05:28:05 +44,M,02:14:49,05:28:06 +50,M,02:11:25,05:28:06 +33,M,02:27:25,05:28:06 +38,W,02:16:24,05:28:07 +45,M,02:10:07,05:28:07 +39,W,02:16:41,05:28:08 +41,W,02:18:22,05:28:08 +32,W,02:24:06,05:28:08 +30,M,02:07:35,05:28:09 +40,M,02:22:01,05:28:10 +41,M,02:18:45,05:28:10 +44,M,02:20:13,05:28:10 +32,W,02:00:20,05:28:10 +64,M,02:22:00,05:28:11 +51,W,02:17:35,05:28:11 +41,M,02:15:35,05:28:11 +27,W,02:10:31,05:28:11 +27,W,02:12:12,05:28:11 +42,W,02:19:02,05:28:11 +50,M,02:14:02,05:28:12 +37,M,02:20:17,05:28:12 +30,W,02:23:07,05:28:14 +63,M,02:14:01,05:28:14 +45,M,02:07:26,05:28:14 +34,M,02:21:55,05:28:14 +32,M,02:12:27,05:28:14 +24,W,02:27:32,05:28:15 +24,M,02:25:27,05:28:15 +39,W,02:19:50,05:28:15 +24,W,02:09:05,05:28:16 +30,W,02:20:30,05:28:16 +52,W,02:20:43,05:28:17 +33,M,02:11:21,05:28:18 +42,M,02:17:10,05:28:19 +37,M,02:15:54,05:28:19 +33,W,02:20:21,05:28:19 +38,W,02:16:54,05:28:19 +41,W,02:15:29,05:28:20 +31,W,02:12:16,05:28:20 +29,M,02:20:22,05:28:21 +34,M,02:06:36,05:28:22 +52,W,02:16:30,05:28:22 +47,W,02:11:13,05:28:22 +27,W,02:19:50,05:28:22 +54,M,02:26:50,05:28:22 +57,M,02:18:24,05:28:23 +43,W,02:14:13,05:28:24 +35,M,02:08:05,05:28:24 +71,M,02:20:31,05:28:24 +32,M,02:09:20,05:28:25 +42,M,02:18:46,05:28:25 +40,W,02:18:41,05:28:25 +55,M,02:17:58,05:28:25 +36,M,02:07:31,05:28:25 +27,W,02:19:19,05:28:26 +54,M,02:02:21,05:28:26 +39,M,02:17:17,05:28:27 +23,W,02:21:14,05:28:27 +56,W,02:16:06,05:28:27 +44,M,02:10:28,05:28:27 +62,M,02:19:23,05:28:28 +30,W,02:19:55,05:28:28 +38,W,02:12:39,05:28:28 +36,W,02:17:52,05:28:29 +47,W,02:23:07,05:28:29 +35,M,02:10:45,05:28:30 +29,W,02:20:29,05:28:31 +53,M,02:15:21,05:28:31 +49,M,02:21:00,05:28:31 +44,W,02:15:26,05:28:32 +55,M,02:14:05,05:28:32 +47,M,02:35:25,05:28:32 +45,M,02:08:57,05:28:32 +56,M,02:21:13,05:28:32 +29,W,02:23:15,05:28:32 +34,M,02:08:42,05:28:33 +23,W,02:18:24,05:28:33 +43,W,02:24:50,05:28:34 +40,M,02:19:35,05:28:34 +65,M,02:16:20,05:28:34 +43,M,02:18:27,05:28:34 +63,M,02:21:55,05:28:34 +30,M,02:15:52,05:28:35 +43,W,02:07:33,05:28:35 +44,M,02:13:04,05:28:36 +45,W,02:17:52,05:28:36 +27,W,02:23:29,05:28:36 +60,M,02:08:25,05:28:37 +64,W,02:12:11,05:28:37 +33,W,02:19:33,05:28:38 +41,M,02:21:51,05:28:38 +40,M,02:11:42,05:28:38 +48,M,02:07:29,05:28:38 +40,M,02:08:04,05:28:38 +41,W,02:14:26,05:28:38 +51,W,02:16:27,05:28:38 +29,M,02:04:40,05:28:39 +64,M,02:24:03,05:28:39 +41,W,02:21:24,05:28:39 +39,W,02:07:39,05:28:39 +47,M,02:06:15,05:28:40 +46,W,02:22:53,05:28:40 +49,M,02:09:50,05:28:40 +34,W,02:22:36,05:28:40 +43,W,02:19:00,05:28:40 +53,W,02:11:39,05:28:40 +43,M,02:50:20,05:28:41 +46,M,02:17:24,05:28:41 +42,M,02:16:39,05:28:43 +69,M,02:16:47,05:28:43 +41,M,02:09:08,05:28:43 +50,W,02:23:01,05:28:43 +35,W,02:14:33,05:28:44 +54,M,02:27:09,05:28:44 +30,M,02:11:32,05:28:44 +26,M,02:15:44,05:28:45 +45,W,02:18:49,05:28:46 +28,W,02:18:50,05:28:46 +45,M,02:10:51,05:28:47 +37,W,02:30:57,05:28:47 +34,M,02:19:39,05:28:47 +38,W,02:22:20,05:28:48 +47,M,02:22:39,05:28:49 +54,M,02:16:37,05:28:49 +46,W,02:21:18,05:28:49 +28,M,02:11:48,05:28:50 +48,M,02:17:12,05:28:50 +50,M,02:50:29,05:28:50 +42,M,02:14:10,05:28:50 +53,M,02:12:02,05:28:50 +41,W,02:21:49,05:28:51 +36,W,02:17:29,05:28:51 +49,M,02:06:58,05:28:53 +52,W,02:21:50,05:28:53 +29,W,01:59:00,05:28:54 +52,W,02:17:05,05:28:54 +42,M,02:02:32,05:28:54 +38,W,02:14:33,05:28:55 +30,M,02:08:48,05:28:56 +33,W,02:20:18,05:28:56 +50,M,02:08:01,05:28:57 +41,M,02:11:16,05:28:58 +46,M,02:18:41,05:28:59 +48,M,02:07:33,05:28:59 +39,M,02:13:57,05:28:59 +40,M,02:05:45,05:28:59 +24,W,02:17:30,05:29:00 +65,M,02:21:02,05:29:00 +38,M,02:16:29,05:29:00 +46,W,02:22:54,05:29:02 +27,M,02:15:40,05:29:02 +35,M,02:17:24,05:29:03 +25,M,02:08:52,05:29:03 +28,W,02:13:28,05:29:03 +57,M,02:09:42,05:29:04 +33,W,02:11:14,05:29:04 +64,M,02:06:06,05:29:04 +25,W,02:14:31,05:29:05 +27,W,02:18:01,05:29:05 +24,M,02:20:01,05:29:05 +46,M,02:14:38,05:29:05 +42,M,02:11:59,05:29:06 +42,M,02:22:10,05:29:06 +60,W,02:20:30,05:29:06 +31,W,02:16:40,05:29:06 +29,M,02:05:30,05:29:06 +55,M,02:18:34,05:29:07 +37,M,02:16:01,05:29:07 +19,W,02:22:58,05:29:07 +35,M,02:07:10,05:29:07 +48,W,02:19:25,05:29:07 +33,M,02:03:00,05:29:07 +52,W,02:22:42,05:29:08 +51,W,02:19:25,05:29:08 +27,M,02:11:15,05:29:08 +45,M,02:18:08,05:29:09 +26,M,02:17:45,05:29:09 +42,W,02:20:45,05:29:09 +44,M,02:11:34,05:29:10 +45,M,02:13:51,05:29:10 +50,M,02:17:11,05:29:10 +36,M,02:16:11,05:29:11 +61,M,02:07:24,05:29:11 +34,W,02:23:34,05:29:12 +30,W,02:23:16,05:29:12 +53,M,02:06:37,05:29:13 +38,W,02:11:36,05:29:13 +49,M,02:02:44,05:29:13 +44,W,02:25:21,05:29:13 +53,M,02:17:38,05:29:13 +33,M,02:12:16,05:29:14 +42,W,02:15:05,05:29:14 +54,W,02:22:17,05:29:14 +46,W,02:36:50,05:29:15 +24,M,02:01:38,05:29:15 +36,W,02:17:24,05:29:15 +42,M,02:13:30,05:29:15 +37,W,02:20:23,05:29:15 +46,W,02:24:17,05:29:15 +50,M,02:13:21,05:29:16 +33,M,02:13:56,05:29:17 +29,M,02:09:49,05:29:17 +32,M,02:08:07,05:29:17 +49,W,02:18:42,05:29:18 +40,W,02:22:57,05:29:18 +46,W,02:13:11,05:29:18 +62,M,02:18:11,05:29:18 +50,W,02:18:52,05:29:19 +27,M,02:27:35,05:29:19 +52,M,02:17:57,05:29:19 +47,W,02:13:05,05:29:19 +31,W,02:14:24,05:29:19 +42,W,02:13:52,05:29:19 +29,W,02:25:07,05:29:20 +45,W,02:22:13,05:29:21 +28,W,02:20:33,05:29:21 +42,W,02:09:56,05:29:21 +33,W,02:15:16,05:29:21 +42,M,01:59:24,05:29:21 +21,M,02:22:29,05:29:22 +46,W,02:21:49,05:29:22 +28,W,02:12:05,05:29:22 +53,W,02:14:07,05:29:23 +38,M,02:02:13,05:29:23 +30,M,02:13:32,05:29:23 +50,M,02:08:21,05:29:23 +56,M,02:14:18,05:29:23 +37,W,02:14:04,05:29:23 +47,W,02:21:09,05:29:24 +40,W,02:12:27,05:29:24 +56,M,02:08:02,05:29:24 +55,W,02:28:58,05:29:24 +48,W,02:24:15,05:29:25 +25,W,02:17:42,05:29:25 +50,M,02:08:31,05:29:26 +48,W,02:13:01,05:29:26 +29,M,02:18:48,05:29:26 +51,W,02:19:29,05:29:26 +44,M,02:09:16,05:29:27 +25,M,02:25:47,05:29:27 +30,W,02:22:51,05:29:27 +61,M,01:59:56,05:29:28 +46,W,02:18:06,05:29:29 +33,W,02:13:22,05:29:31 +25,M,02:14:37,05:29:31 +61,M,02:10:39,05:29:31 +34,W,02:14:09,05:29:32 +49,W,02:26:49,05:29:32 +39,W,02:17:07,05:29:32 +47,W,02:18:42,05:29:32 +39,W,02:14:18,05:29:32 +39,M,02:02:45,05:29:33 +44,M,02:20:23,05:29:33 +61,M,02:14:43,05:29:33 +43,M,02:19:45,05:29:34 +26,M,02:18:31,05:29:34 +31,W,02:12:59,05:29:35 +29,W,02:20:13,05:29:36 +49,M,02:20:42,05:29:36 +47,M,02:22:18,05:29:37 +45,W,02:13:18,05:29:37 +46,M,02:08:48,05:29:37 +52,M,02:20:13,05:29:39 +27,W,02:23:31,05:29:39 +57,M,02:10:01,05:29:40 +34,W,02:18:44,05:29:41 +32,W,02:10:53,05:29:41 +50,M,02:22:22,05:29:41 +46,M,02:17:42,05:29:41 +45,M,02:18:16,05:29:41 +26,W,02:16:02,05:29:41 +30,M,02:10:00,05:29:42 +21,W,02:22:26,05:29:42 +52,W,02:15:03,05:29:42 +36,W,02:27:34,05:29:43 +38,W,02:23:19,05:29:43 +28,W,02:22:10,05:29:43 +46,M,02:04:04,05:29:43 +23,W,02:17:45,05:29:44 +40,M,02:22:25,05:29:44 +62,M,02:07:30,05:29:45 +26,M,02:03:01,05:29:45 +52,M,02:09:18,05:29:45 +57,M,02:06:56,05:29:46 +34,M,02:20:53,05:29:46 +36,M,02:15:42,05:29:46 +65,M,02:14:54,05:29:46 +41,W,02:21:26,05:29:47 +31,M,02:16:08,05:29:47 +57,M,02:11:47,05:29:47 +51,W,02:17:11,05:29:47 +44,M,02:18:49,05:29:48 +39,M,02:15:01,05:29:48 +50,W,02:10:03,05:29:48 +36,W,02:19:28,05:29:48 +53,M,02:17:22,05:29:49 +24,M,01:56:07,05:29:49 +42,W,02:09:46,05:29:49 +55,W,02:23:12,05:29:49 +37,W,02:14:55,05:29:49 +30,W,02:15:44,05:29:50 +23,W,02:10:29,05:29:50 +18,M,02:21:43,05:29:50 +40,W,02:16:40,05:29:51 +28,M,02:13:21,05:29:52 +57,M,02:27:59,05:29:52 +32,M,02:21:43,05:29:52 +56,W,02:16:26,05:29:53 +62,M,02:13:48,05:29:53 +34,M,02:18:40,05:29:54 +43,M,02:06:31,05:29:54 +35,W,02:13:02,05:29:54 +27,M,02:26:55,05:29:55 +51,W,02:18:48,05:29:56 +36,W,02:18:41,05:29:56 +42,M,02:15:46,05:29:57 +43,M,02:22:52,05:29:57 +37,W,02:14:53,05:29:57 +40,M,02:05:20,05:29:58 +37,M,02:15:52,05:29:59 +41,W,02:25:38,05:30:00 +26,M,02:09:11,05:30:00 +38,M,02:20:28,05:30:00 +50,M,02:21:56,05:30:01 +49,W,02:16:16,05:30:01 +31,M,02:10:43,05:30:01 +47,W,02:12:58,05:30:01 +46,W,02:21:57,05:30:01 +41,M,02:15:12,05:30:02 +37,W,02:12:38,05:30:03 +26,M,02:16:55,05:30:04 +30,W,02:21:53,05:30:04 +40,M,02:03:02,05:30:04 +37,W,02:15:46,05:30:04 +57,M,02:13:13,05:30:05 +55,M,02:15:29,05:30:05 +57,M,02:16:24,05:30:05 +30,W,02:14:53,05:30:06 +66,M,02:15:55,05:30:06 +46,W,02:26:59,05:30:07 +28,M,02:05:13,05:30:07 +32,W,02:19:50,05:30:08 +42,M,02:16:21,05:30:08 +30,W,02:44:46,05:30:08 +45,M,02:07:57,05:30:08 +41,W,02:06:26,05:30:09 +31,W,02:16:40,05:30:09 +43,W,02:10:59,05:30:10 +37,M,02:15:08,05:30:10 +57,M,02:10:41,05:30:10 +49,W,02:22:52,05:30:11 +42,M,02:07:01,05:30:11 +47,M,02:16:34,05:30:12 +31,W,02:24:15,05:30:12 +31,W,02:16:32,05:30:12 +22,M,02:18:02,05:30:13 +51,M,02:09:25,05:30:13 +21,W,02:23:30,05:30:13 +51,M,02:18:08,05:30:14 +35,W,02:18:43,05:30:14 +52,W,02:14:05,05:30:14 +45,M,02:27:08,05:30:14 +47,W,02:08:33,05:30:15 +46,M,02:11:20,05:30:15 +55,M,02:12:18,05:30:16 +30,W,02:22:59,05:30:16 +35,W,02:16:54,05:30:17 +28,M,02:16:02,05:30:17 +25,W,02:18:17,05:30:18 +53,W,02:16:54,05:30:18 +31,W,02:18:23,05:30:18 +51,W,02:17:14,05:30:18 +37,W,02:19:22,05:30:19 +36,M,02:16:37,05:30:19 +52,M,02:12:03,05:30:19 +31,W,02:20:13,05:30:19 +32,W,02:14:08,05:30:19 +38,M,02:14:25,05:30:19 +45,M,02:11:49,05:30:19 +71,W,02:16:15,05:30:19 +28,M,02:01:16,05:30:20 +31,W,02:13:35,05:30:20 +24,M,02:12:56,05:30:21 +49,W,02:08:25,05:30:21 +62,M,02:11:31,05:30:22 +37,W,02:22:30,05:30:22 +31,M,02:05:51,05:30:22 +33,W,02:23:16,05:30:23 +48,W,02:17:24,05:30:24 +38,W,02:15:36,05:30:25 +39,M,02:17:16,05:30:25 +57,M,02:11:58,05:30:26 +46,M,02:19:06,05:30:26 +20,W,02:17:23,05:30:27 +33,W,02:15:39,05:30:27 +23,M,02:19:50,05:30:27 +42,W,02:22:31,05:30:28 +44,W,02:15:35,05:30:29 +31,W,02:22:08,05:30:29 +37,M,01:58:05,05:30:29 +23,W,02:16:43,05:30:29 +52,M,02:08:57,05:30:30 +45,M,02:17:27,05:30:30 +50,M,02:12:59,05:30:30 +50,W,02:27:25,05:30:30 +29,M,02:20:22,05:30:31 +56,M,02:13:14,05:30:31 +59,M,02:12:21,05:30:31 +37,M,02:17:09,05:30:32 +42,M,02:26:19,05:30:32 +56,M,02:21:43,05:30:33 +36,W,02:19:48,05:30:33 +47,M,02:23:47,05:30:34 +43,M,02:13:37,05:30:35 +39,M,02:05:39,05:30:35 +40,M,02:19:40,05:30:35 +33,M,02:16:20,05:30:35 +45,M,02:11:08,05:30:35 +34,W,02:10:39,05:30:35 +38,M,02:22:53,05:30:35 +54,W,02:16:24,05:30:36 +56,M,02:06:42,05:30:36 +60,M,02:14:20,05:30:37 +44,M,02:09:46,05:30:37 +62,M,02:02:14,05:30:37 +47,W,02:15:14,05:30:38 +25,W,02:21:02,05:30:39 +38,M,02:16:56,05:30:40 +40,W,02:22:55,05:30:40 +40,M,02:19:24,05:30:40 +40,W,02:26:35,05:30:41 +41,W,02:19:12,05:30:41 +39,M,02:18:40,05:30:41 +52,W,02:23:00,05:30:43 +40,W,02:16:01,05:30:43 +33,W,02:12:00,05:30:44 +39,M,02:14:04,05:30:44 +39,M,02:24:32,05:30:44 +39,M,02:08:38,05:30:45 +44,M,02:07:15,05:30:46 +42,M,02:18:18,05:30:46 +35,W,02:18:43,05:30:46 +48,W,02:16:31,05:30:46 +36,M,02:21:37,05:30:47 +48,M,02:08:19,05:30:47 +24,M,02:17:18,05:30:47 +48,W,02:19:03,05:30:48 +44,M,02:17:23,05:30:48 +50,M,02:11:59,05:30:48 +41,W,02:20:13,05:30:48 +35,W,02:17:37,05:30:48 +51,M,02:17:41,05:30:49 +46,M,02:19:09,05:30:49 +23,W,02:20:04,05:30:50 +51,W,02:10:53,05:30:50 +48,M,02:21:04,05:30:51 +51,M,02:16:01,05:30:51 +27,M,02:10:05,05:30:51 +31,M,02:18:30,05:30:51 +32,W,02:12:15,05:30:52 +60,M,02:08:01,05:30:52 +48,W,02:05:59,05:30:52 +48,M,02:14:51,05:30:52 +34,M,02:09:52,05:30:53 +53,M,02:13:59,05:30:53 +61,W,02:14:09,05:30:54 +60,M,02:14:11,05:30:55 +40,W,02:11:56,05:30:55 +37,M,01:57:27,05:30:55 +40,M,02:18:02,05:30:55 +39,M,02:06:26,05:30:55 +37,M,02:13:47,05:30:55 +52,M,02:29:59,05:30:56 +48,W,02:23:20,05:30:56 +40,W,02:10:56,05:30:56 +32,W,02:23:08,05:30:56 +37,W,02:11:31,05:30:57 +42,W,02:20:41,05:30:57 +59,M,02:21:26,05:30:57 +60,W,02:16:08,05:30:58 +40,M,02:23:02,05:30:58 +27,M,02:26:42,05:30:58 +37,M,02:13:51,05:30:58 +41,W,02:16:54,05:30:59 +49,M,02:24:56,05:31:00 +55,W,02:13:19,05:31:01 +37,W,02:16:27,05:31:01 +44,W,02:17:51,05:31:01 +39,W,02:06:18,05:31:01 +31,W,02:19:25,05:31:01 +30,W,02:21:18,05:31:02 +44,W,02:39:07,05:31:02 +30,M,02:13:16,05:31:02 +27,W,02:23:35,05:31:03 +29,M,02:16:42,05:31:03 +48,M,02:13:07,05:31:03 +19,M,02:32:18,05:31:04 +57,M,02:05:29,05:31:04 +33,M,02:08:31,05:31:04 +57,W,02:26:23,05:31:04 +56,M,02:14:43,05:31:04 +63,M,02:12:31,05:31:04 +45,M,02:13:41,05:31:04 +42,M,02:13:12,05:31:05 +39,W,02:18:04,05:31:05 +50,M,02:22:26,05:31:05 +41,W,02:12:51,05:31:06 +43,M,02:24:04,05:31:06 +30,M,02:17:46,05:31:06 +62,M,02:12:14,05:31:07 +32,W,02:19:54,05:31:07 +54,W,02:13:37,05:31:07 +24,W,02:13:40,05:31:07 +37,M,02:09:51,05:31:08 +25,W,02:04:36,05:31:08 +31,W,02:14:03,05:31:08 +71,M,02:12:40,05:31:08 +28,W,02:09:54,05:31:08 +28,W,02:36:08,05:31:09 +39,W,02:14:15,05:31:09 +27,M,02:10:30,05:31:09 +66,M,02:28:03,05:31:09 +49,M,02:14:33,05:31:09 +36,M,02:23:13,05:31:10 +36,M,02:03:40,05:31:10 +31,M,02:21:57,05:31:10 +37,W,02:14:19,05:31:10 +58,M,02:26:57,05:31:11 +45,M,02:27:57,05:31:12 +42,M,02:16:52,05:31:12 +35,W,02:21:20,05:31:12 +30,W,02:14:58,05:31:12 +46,M,02:02:50,05:31:12 +61,M,02:08:21,05:31:13 +34,M,02:17:07,05:31:14 +58,M,02:04:28,05:31:15 +45,M,02:04:21,05:31:15 +47,M,02:17:15,05:31:15 +40,W,02:13:24,05:31:16 +55,M,02:22:32,05:31:16 +43,M,02:14:54,05:31:16 +27,W,02:27:00,05:31:17 +44,W,02:21:38,05:31:17 +45,W,02:16:29,05:31:17 +55,M,02:27:38,05:31:17 +36,W,02:19:00,05:31:18 +32,M,02:09:20,05:31:18 +44,M,02:24:30,05:31:18 +60,W,02:17:40,05:31:18 +59,M,02:19:18,05:31:18 +36,W,02:23:47,05:31:19 +63,M,02:16:50,05:31:19 +29,W,02:22:30,05:31:19 +46,M,02:01:21,05:31:19 +50,M,02:19:58,05:31:20 +65,W,02:28:18,05:31:20 +51,M,02:11:13,05:31:20 +21,M,02:00:32,05:31:20 +42,M,02:23:19,05:31:20 +51,M,02:20:21,05:31:21 +35,W,02:14:54,05:31:21 +30,W,02:19:32,05:31:21 +30,W,02:16:53,05:31:21 +44,M,02:19:52,05:31:21 +26,W,02:17:44,05:31:22 +38,M,02:22:14,05:31:23 +48,W,02:21:43,05:31:24 +34,M,02:13:57,05:31:24 +43,W,02:13:53,05:31:24 +40,W,02:15:12,05:31:24 +47,M,02:21:21,05:31:24 +38,W,02:27:54,05:31:25 +56,M,02:02:03,05:31:25 +37,M,02:13:36,05:31:25 +51,M,02:19:53,05:31:26 +37,M,02:24:46,05:31:26 +58,M,02:05:54,05:31:26 +32,M,02:03:21,05:31:26 +51,W,02:19:27,05:31:27 +32,M,02:15:27,05:31:27 +66,M,02:08:02,05:31:28 +63,M,02:11:38,05:31:28 +33,M,02:14:51,05:31:28 +35,M,02:17:13,05:31:28 +61,M,02:11:57,05:31:28 +31,W,02:21:09,05:31:28 +34,W,02:20:42,05:31:29 +39,W,02:12:53,05:31:29 +68,M,02:14:29,05:31:30 +42,W,02:21:29,05:31:30 +39,W,02:11:12,05:31:30 +39,W,02:12:05,05:31:30 +29,M,02:13:43,05:31:30 +45,M,02:12:18,05:31:30 +24,W,02:20:27,05:31:31 +31,W,02:20:08,05:31:31 +31,M,02:34:35,05:31:31 +60,W,02:14:41,05:31:33 +46,W,02:20:31,05:31:33 +34,W,02:12:06,05:31:33 +54,M,02:13:46,05:31:33 +49,M,02:18:22,05:31:33 +35,M,02:19:49,05:31:34 +31,W,02:24:26,05:31:34 +44,W,02:15:24,05:31:35 +41,M,02:06:27,05:31:35 +32,M,02:12:25,05:31:35 +27,W,02:16:36,05:31:36 +33,M,02:06:25,05:31:37 +42,W,02:24:06,05:31:37 +34,W,02:07:00,05:31:38 +27,M,02:15:54,05:31:38 +51,W,02:11:00,05:31:38 +52,M,02:12:45,05:31:39 +32,W,02:16:06,05:31:39 +37,W,02:18:15,05:31:40 +55,M,02:05:09,05:31:41 +39,W,02:21:53,05:31:41 +49,W,02:24:58,05:31:41 +51,M,02:19:48,05:31:42 +29,W,02:17:00,05:31:42 +52,M,02:10:22,05:31:42 +31,M,02:16:04,05:31:43 +55,W,02:18:25,05:31:43 +58,M,02:11:52,05:31:43 +39,M,02:23:33,05:31:43 +28,M,02:18:00,05:31:44 +35,W,02:22:26,05:31:45 +48,W,02:18:25,05:31:46 +50,M,02:09:28,05:31:47 +57,M,02:08:24,05:31:47 +47,W,02:22:05,05:31:48 +36,W,02:18:43,05:31:49 +44,M,02:23:15,05:31:49 +30,M,02:05:03,05:31:49 +56,M,02:09:12,05:31:50 +45,M,02:21:11,05:31:50 +56,W,02:16:40,05:31:50 +27,W,02:10:29,05:31:50 +46,M,02:15:01,05:31:51 +56,W,02:27:35,05:31:52 +49,W,02:11:07,05:31:52 +49,W,02:21:16,05:31:52 +35,M,02:17:58,05:31:53 +25,W,02:07:51,05:31:53 +53,M,02:24:16,05:31:54 +44,M,02:21:05,05:31:55 +32,W,02:30:36,05:31:55 +26,M,02:10:27,05:31:55 +33,W,02:14:09,05:31:56 +43,W,02:15:40,05:31:57 +28,W,02:16:55,05:31:57 +51,M,02:15:58,05:31:57 +31,M,02:32:15,05:31:58 +41,W,02:14:07,05:31:58 +40,W,02:12:33,05:31:58 +27,W,02:15:39,05:31:58 +37,M,02:28:49,05:31:58 +38,M,02:11:53,05:31:59 +44,W,02:28:43,05:31:59 +27,W,02:25:30,05:32:00 +55,W,02:29:20,05:32:00 +39,W,02:23:28,05:32:00 +34,M,02:18:04,05:32:01 +29,W,02:19:22,05:32:01 +36,M,02:13:36,05:32:01 +44,M,02:17:34,05:32:02 +42,M,02:08:55,05:32:02 +53,W,02:22:39,05:32:02 +32,M,01:55:44,05:32:03 +27,M,02:00:17,05:32:03 +37,W,02:19:00,05:32:03 +44,M,02:13:54,05:32:03 +26,W,02:20:16,05:32:04 +21,M,02:23:51,05:32:05 +32,W,02:16:58,05:32:05 +37,M,02:25:23,05:32:05 +54,M,01:56:10,05:32:05 +44,M,02:12:10,05:32:06 +56,M,02:09:53,05:32:06 +43,M,02:09:57,05:32:06 +54,M,01:59:18,05:32:07 +37,M,02:15:00,05:32:07 +26,M,02:17:45,05:32:08 +24,W,02:13:34,05:32:08 +51,W,02:26:15,05:32:08 +41,W,02:21:24,05:32:09 +34,M,02:14:27,05:32:09 +42,M,02:13:51,05:32:09 +39,M,02:14:17,05:32:10 +71,M,02:36:00,05:32:10 +46,W,02:23:58,05:32:10 +26,W,02:19:03,05:32:11 +38,W,02:15:00,05:32:13 +40,M,02:09:06,05:32:13 +46,M,02:16:40,05:32:13 +45,M,02:20:28,05:32:14 +43,M,02:13:08,05:32:14 +43,W,02:23:43,05:32:14 +29,W,02:18:05,05:32:14 +47,M,02:05:44,05:32:14 +32,M,02:13:25,05:32:14 +51,W,02:19:41,05:32:14 +50,M,02:07:53,05:32:14 +35,M,02:20:03,05:32:14 +24,W,02:13:07,05:32:16 +25,W,02:21:06,05:32:16 +31,W,02:19:29,05:32:16 +46,W,02:15:49,05:32:16 +37,M,02:18:20,05:32:16 +45,M,02:08:58,05:32:17 +66,M,02:02:18,05:32:17 +47,M,02:19:02,05:32:17 +28,M,02:19:03,05:32:17 +31,M,02:20:28,05:32:17 +32,W,02:21:12,05:32:17 +30,W,02:30:10,05:32:17 +42,W,02:28:36,05:32:20 +35,M,02:22:57,05:32:21 +42,M,02:20:07,05:32:21 +35,M,02:16:47,05:32:21 +33,M,02:20:43,05:32:21 +39,W,02:07:32,05:32:21 +53,W,02:23:53,05:32:22 +29,M,02:09:28,05:32:23 +57,M,02:12:03,05:32:23 +29,M,02:12:51,05:32:24 +29,W,02:15:07,05:32:25 +49,W,02:20:34,05:32:25 +49,M,02:11:02,05:32:25 +49,W,02:19:20,05:32:25 +46,W,02:23:43,05:32:26 +37,M,02:18:11,05:32:26 +41,W,02:27:08,05:32:26 +35,M,02:13:47,05:32:26 +52,W,02:36:05,05:32:26 +25,W,02:12:48,05:32:26 +50,M,02:16:39,05:32:27 +44,M,02:14:04,05:32:28 +54,M,02:08:47,05:32:28 +53,M,02:21:07,05:32:28 +30,W,02:19:09,05:32:28 +38,M,02:01:52,05:32:28 +35,W,02:15:02,05:32:29 +38,M,02:21:10,05:32:29 +45,W,02:20:09,05:32:29 +42,M,02:13:05,05:32:30 +44,M,02:30:34,05:32:30 +36,M,02:10:07,05:32:30 +42,W,02:13:32,05:32:30 +25,M,02:26:24,05:32:30 +35,M,02:23:13,05:32:30 +25,W,02:16:11,05:32:31 +53,M,02:20:18,05:32:31 +23,W,02:16:25,05:32:31 +39,W,02:20:33,05:32:31 +60,M,02:17:07,05:32:32 +41,M,02:10:24,05:32:32 +32,W,02:16:46,05:32:32 +47,W,02:26:21,05:32:32 +44,M,02:16:26,05:32:32 +70,M,02:10:37,05:32:32 +34,W,02:21:51,05:32:32 +50,W,02:24:22,05:32:32 +33,M,02:14:25,05:32:33 +30,M,02:16:13,05:32:33 +59,W,02:23:59,05:32:34 +39,W,02:19:31,05:32:34 +52,W,02:17:33,05:32:34 +59,M,02:18:17,05:32:34 +30,W,02:23:47,05:32:35 +33,M,02:11:45,05:32:36 +51,M,02:20:42,05:32:36 +41,M,02:02:23,05:32:37 +60,M,02:08:18,05:32:37 +66,M,02:15:03,05:32:37 +37,W,02:13:01,05:32:37 +48,M,02:29:24,05:32:38 +30,W,02:24:45,05:32:38 +23,M,02:07:16,05:32:38 +62,M,02:05:32,05:32:39 +36,W,02:12:36,05:32:39 +50,W,02:16:47,05:32:39 +38,M,02:10:27,05:32:41 +52,M,02:18:07,05:32:41 +43,M,02:15:19,05:32:42 +36,M,02:16:06,05:32:43 +46,M,02:13:37,05:32:44 +30,W,02:25:37,05:32:44 +31,M,02:16:53,05:32:44 +43,M,02:27:06,05:32:44 +52,M,02:18:33,05:32:44 +51,M,02:15:05,05:32:44 +57,M,02:09:02,05:32:45 +47,M,02:15:51,05:32:45 +30,W,02:12:39,05:32:45 +34,W,02:22:56,05:32:46 +28,W,02:24:32,05:32:47 +62,W,02:27:01,05:32:47 +42,M,02:22:07,05:32:47 +61,M,01:54:30,05:32:47 +28,M,02:04:35,05:32:47 +34,M,02:15:00,05:32:48 +57,M,02:18:14,05:32:49 +54,M,02:08:30,05:32:49 +59,M,02:14:50,05:32:49 +34,W,02:18:09,05:32:50 +45,W,02:16:01,05:32:50 +51,W,02:13:35,05:32:51 +26,W,02:31:33,05:32:51 +40,M,02:21:26,05:32:51 +50,W,02:19:14,05:32:51 +57,M,02:12:46,05:32:51 +48,W,02:20:45,05:32:51 +30,W,02:14:55,05:32:51 +41,W,02:16:55,05:32:52 +43,M,02:14:30,05:32:54 +57,W,02:19:48,05:32:54 +32,W,02:15:27,05:32:54 +51,W,02:23:09,05:32:54 +30,W,02:15:38,05:32:54 +55,W,02:17:01,05:32:55 +48,M,02:17:56,05:32:56 +34,W,02:11:27,05:32:56 +29,M,02:16:36,05:32:57 +65,M,02:12:24,05:32:57 +33,W,02:25:47,05:32:57 +42,W,02:23:18,05:32:57 +45,W,02:23:35,05:32:58 +28,W,02:08:55,05:32:58 +51,M,02:09:33,05:32:58 +39,W,02:19:47,05:32:59 +33,M,02:16:55,05:32:59 +39,W,02:17:09,05:33:00 +53,W,02:26:27,05:33:00 +34,W,02:17:01,05:33:00 +19,W,02:16:50,05:33:00 +34,M,02:18:04,05:33:01 +30,M,02:10:34,05:33:01 +18,W,02:23:48,05:33:01 +28,M,02:11:52,05:33:01 +36,W,02:22:28,05:33:01 +45,M,02:07:02,05:33:01 +29,W,02:18:10,05:33:02 +32,W,02:18:29,05:33:02 +40,M,02:15:11,05:33:03 +49,W,02:13:10,05:33:03 +61,M,02:16:52,05:33:03 +39,W,02:24:20,05:33:03 +44,W,02:18:07,05:33:03 +32,W,02:21:29,05:33:03 +30,W,02:30:59,05:33:04 +32,M,02:22:46,05:33:04 +54,M,02:19:37,05:33:04 +50,W,02:16:27,05:33:04 +36,M,02:09:28,05:33:05 +32,M,02:15:52,05:33:05 +56,M,02:08:51,05:33:05 +37,M,02:17:05,05:33:06 +29,M,02:17:15,05:33:06 +37,W,02:21:07,05:33:06 +59,M,02:16:47,05:33:06 +39,M,02:20:35,05:33:07 +38,M,02:05:38,05:33:07 +49,M,02:08:53,05:33:07 +55,M,02:18:56,05:33:07 +64,M,02:11:50,05:33:08 +34,W,02:16:34,05:33:09 +33,W,02:24:35,05:33:10 +43,M,02:22:49,05:33:10 +57,M,02:17:47,05:33:11 +50,M,02:13:36,05:33:11 +28,W,02:13:01,05:33:11 +54,W,02:24:46,05:33:12 +33,W,02:28:27,05:33:12 +52,M,02:27:27,05:33:12 +55,W,02:32:12,05:33:13 +48,W,02:16:41,05:33:13 +48,W,02:26:31,05:33:14 +27,W,02:23:33,05:33:14 +39,W,02:15:17,05:33:14 +55,M,02:17:56,05:33:14 +44,M,02:22:47,05:33:15 +51,W,02:15:07,05:33:15 +49,M,02:15:54,05:33:15 +59,M,02:20:02,05:33:15 +40,W,02:18:10,05:33:15 +39,W,02:21:38,05:33:15 +36,M,02:13:06,05:33:16 +49,W,02:24:43,05:33:16 +47,M,02:14:20,05:33:16 +45,W,02:22:30,05:33:17 +36,M,02:13:49,05:33:17 +52,M,02:22:12,05:33:17 +41,M,02:15:47,05:33:17 +41,W,02:31:43,05:33:18 +56,M,02:12:03,05:33:18 +32,W,02:18:35,05:33:19 +30,M,02:21:26,05:33:19 +34,W,02:07:34,05:33:19 +43,M,02:01:10,05:33:20 +45,M,02:14:26,05:33:20 +55,M,02:20:59,05:33:20 +49,W,02:15:35,05:33:20 +48,M,02:14:55,05:33:20 +38,W,02:20:35,05:33:21 +36,W,02:28:00,05:33:21 +46,W,02:19:16,05:33:21 +50,W,02:29:46,05:33:21 +45,W,01:56:57,05:33:22 +32,M,02:15:33,05:33:22 +51,M,02:00:29,05:33:22 +35,M,02:28:17,05:33:22 +49,M,02:18:49,05:33:22 +41,W,02:07:18,05:33:22 +51,W,02:05:33,05:33:22 +36,W,02:19:36,05:33:22 +41,W,02:20:55,05:33:22 +48,M,02:12:17,05:33:22 +44,M,02:16:04,05:33:23 +42,M,02:06:19,05:33:23 +29,W,02:15:40,05:33:25 +71,M,02:03:25,05:33:25 +33,M,02:15:11,05:33:25 +31,W,02:18:41,05:33:26 +44,M,02:15:29,05:33:26 +35,M,02:14:15,05:33:26 +46,W,02:22:34,05:33:26 +48,M,02:23:07,05:33:26 +30,M,02:15:15,05:33:27 +31,W,02:13:34,05:33:27 +50,M,02:23:45,05:33:27 +39,W,02:19:46,05:33:27 +28,W,02:15:28,05:33:28 +45,M,02:16:47,05:33:28 +44,W,02:16:29,05:33:28 +40,W,02:24:23,05:33:29 +40,M,02:14:20,05:33:31 +62,M,02:27:26,05:33:31 +63,W,02:14:46,05:33:32 +38,M,02:20:03,05:33:32 +57,M,02:09:47,05:33:33 +46,M,02:27:38,05:33:33 +33,W,02:17:23,05:33:34 +39,M,02:08:05,05:33:34 +47,M,02:28:20,05:33:35 +27,M,02:18:17,05:33:35 +32,W,02:21:06,05:33:36 +28,M,02:25:55,05:33:36 +46,W,02:19:11,05:33:36 +53,W,02:19:11,05:33:36 +38,M,02:17:03,05:33:37 +44,M,02:00:25,05:33:37 +36,W,02:14:50,05:33:37 +37,W,02:14:57,05:33:37 +42,M,02:09:44,05:33:38 +39,M,02:21:42,05:33:38 +26,M,02:14:32,05:33:38 +36,W,02:27:33,05:33:38 +45,M,02:06:55,05:33:39 +41,W,02:20:16,05:33:39 +26,W,02:14:00,05:33:39 +31,W,02:26:28,05:33:41 +37,M,02:12:23,05:33:41 +49,M,02:18:27,05:33:41 +33,W,02:13:54,05:33:42 +31,M,02:19:57,05:33:42 +40,W,02:13:17,05:33:43 +60,M,02:17:46,05:33:43 +50,W,02:18:08,05:33:43 +41,M,02:16:57,05:33:44 +42,W,02:22:02,05:33:44 +44,M,02:19:24,05:33:44 +30,W,02:17:38,05:33:44 +30,M,02:20:30,05:33:45 +36,M,02:12:27,05:33:45 +31,W,02:19:32,05:33:45 +40,M,02:12:22,05:33:45 +47,M,02:20:20,05:33:46 +50,W,02:26:06,05:33:46 +30,M,01:59:40,05:33:47 +60,M,02:18:47,05:33:47 +46,W,02:07:24,05:33:47 +35,M,02:11:52,05:33:47 +61,M,02:15:13,05:33:47 +56,M,02:17:38,05:33:48 +30,W,02:25:21,05:33:48 +56,M,02:10:08,05:33:48 +46,W,02:21:19,05:33:49 +28,W,02:19:43,05:33:49 +40,M,01:59:14,05:33:49 +47,M,02:13:52,05:33:49 +23,M,02:21:53,05:33:49 +46,W,02:11:07,05:33:50 +44,M,02:20:44,05:33:51 +46,M,02:17:49,05:33:51 +43,M,02:00:33,05:33:51 +52,M,02:12:44,05:33:51 +45,W,02:26:46,05:33:51 +48,M,02:05:37,05:33:52 +40,W,02:20:46,05:33:52 +48,M,02:18:29,05:33:52 +65,M,02:18:40,05:33:52 +30,W,02:24:17,05:33:52 +22,W,02:11:35,05:33:52 +32,M,02:39:42,05:33:52 +50,W,02:15:24,05:33:52 +39,M,02:13:51,05:33:53 +24,W,02:15:47,05:33:53 +37,W,02:15:25,05:33:53 +37,M,02:22:19,05:33:53 +41,M,02:05:45,05:33:54 +32,W,02:04:47,05:33:54 +42,W,02:06:56,05:33:54 +42,M,02:17:25,05:33:55 +38,M,02:16:09,05:33:56 +52,M,02:26:05,05:33:56 +59,M,02:18:46,05:33:56 +36,W,02:19:17,05:33:58 +51,M,02:14:34,05:33:58 +37,W,02:21:00,05:33:58 +50,M,02:22:41,05:33:58 +45,M,02:13:14,05:34:01 +48,W,02:27:15,05:34:01 +34,W,02:14:43,05:34:01 +31,M,02:19:39,05:34:01 +35,W,02:13:00,05:34:02 +52,W,02:14:12,05:34:02 +55,M,02:17:01,05:34:02 +53,M,02:16:20,05:34:02 +38,W,02:19:18,05:34:02 +37,W,02:17:14,05:34:03 +59,M,02:27:00,05:34:03 +43,M,02:17:07,05:34:03 +47,M,02:11:25,05:34:03 +19,W,02:30:40,05:34:04 +42,W,02:25:54,05:34:04 +44,M,02:15:50,05:34:04 +66,W,02:17:36,05:34:04 +44,M,02:09:28,05:34:04 +37,W,02:21:53,05:34:05 +48,W,02:19:48,05:34:05 +51,W,02:24:07,05:34:05 +55,M,02:23:08,05:34:05 +30,M,02:12:30,05:34:05 +51,M,02:21:05,05:34:06 +34,W,02:15:31,05:34:06 +54,W,02:23:12,05:34:07 +56,M,02:29:01,05:34:07 +17,W,02:26:43,05:34:07 +36,W,02:26:45,05:34:07 +54,M,02:07:19,05:34:07 +33,W,02:15:14,05:34:08 +33,W,02:16:10,05:34:08 +54,M,02:13:13,05:34:08 +42,W,02:17:05,05:34:08 +34,W,02:30:58,05:34:08 +59,M,02:14:50,05:34:09 +30,M,02:15:43,05:34:09 +29,W,02:23:12,05:34:10 +54,W,02:40:59,05:34:10 +62,W,02:15:29,05:34:11 +34,W,02:17:44,05:34:11 +30,W,02:24:22,05:34:11 +31,W,02:19:51,05:34:11 +50,M,02:16:56,05:34:11 +53,M,02:09:11,05:34:12 +53,M,02:30:20,05:34:12 +35,W,02:16:28,05:34:12 +65,M,02:31:19,05:34:12 +23,M,02:06:25,05:34:12 +45,W,02:20:55,05:34:13 +44,M,02:23:14,05:34:13 +41,M,02:22:53,05:34:13 +47,W,02:14:41,05:34:14 +45,M,02:15:09,05:34:14 +24,W,02:13:43,05:34:14 +45,M,02:25:06,05:34:14 +26,M,02:19:57,05:34:14 +38,W,02:12:39,05:34:14 +62,M,02:24:26,05:34:14 +59,M,02:12:27,05:34:15 +56,M,02:17:41,05:34:15 +31,W,02:24:53,05:34:15 +40,W,02:20:23,05:34:16 +44,M,02:17:15,05:34:16 +46,M,02:27:18,05:34:17 +44,M,02:22:48,05:34:17 +25,W,02:16:19,05:34:18 +51,M,02:24:52,05:34:18 +52,M,02:24:02,05:34:18 +31,M,02:16:58,05:34:18 +32,M,02:14:10,05:34:18 +19,M,02:38:52,05:34:19 +35,M,02:22:11,05:34:19 +53,W,02:24:13,05:34:19 +34,M,02:20:11,05:34:19 +48,W,02:12:37,05:34:20 +26,W,02:28:47,05:34:20 +55,M,02:11:33,05:34:20 +29,W,02:22:32,05:34:20 +45,W,02:15:35,05:34:21 +40,M,02:24:10,05:34:21 +27,M,02:04:34,05:34:21 +38,M,02:18:37,05:34:22 +40,W,02:14:28,05:34:22 +27,W,02:17:02,05:34:22 +38,W,02:21:52,05:34:23 +25,W,02:24:10,05:34:23 +42,M,02:09:40,05:34:23 +45,W,02:24:02,05:34:23 +29,W,02:18:36,05:34:23 +42,M,02:07:58,05:34:23 +32,M,02:10:07,05:34:23 +53,M,02:29:28,05:34:24 +54,M,02:27:05,05:34:24 +24,W,02:17:14,05:34:24 +28,W,02:15:30,05:34:25 +44,M,02:23:55,05:34:25 +52,W,02:31:34,05:34:26 +45,W,02:19:57,05:34:26 +29,W,02:18:22,05:34:26 +35,W,02:24:25,05:34:26 +47,M,02:15:51,05:34:26 +59,M,02:29:24,05:34:27 +49,W,02:23:44,05:34:27 +42,M,01:59:45,05:34:27 +49,M,02:09:09,05:34:27 +29,W,02:26:14,05:34:28 +32,M,02:25:08,05:34:29 +26,W,02:26:57,05:34:29 +42,M,02:14:13,05:34:29 +52,W,02:27:03,05:34:29 +35,W,02:15:48,05:34:30 +55,M,02:18:09,05:34:31 +32,W,02:20:56,05:34:31 +36,W,02:10:56,05:34:31 +45,M,02:18:37,05:34:32 +56,M,02:18:41,05:34:32 +35,W,02:26:17,05:34:32 +38,M,02:05:40,05:34:32 +35,W,02:17:04,05:34:33 +42,W,02:14:53,05:34:33 +43,M,02:15:18,05:34:33 +53,W,02:11:55,05:34:33 +28,W,02:13:01,05:34:35 +48,M,02:25:57,05:34:35 +30,W,02:22:10,05:34:35 +45,W,02:25:56,05:34:36 +49,W,02:20:44,05:34:36 +59,W,02:16:41,05:34:37 +60,M,02:30:27,05:34:37 +47,W,02:20:21,05:34:37 +41,W,02:16:39,05:34:38 +64,M,02:14:36,05:34:38 +48,W,02:16:39,05:34:38 +56,W,02:18:48,05:34:38 +42,M,02:21:02,05:34:39 +52,M,02:19:49,05:34:39 +54,W,02:20:59,05:34:40 +27,W,02:20:22,05:34:40 +36,W,02:26:55,05:34:40 +32,W,02:26:06,05:34:41 +31,M,02:03:30,05:34:41 +53,M,02:18:52,05:34:41 +48,W,02:14:39,05:34:41 +29,W,02:21:29,05:34:41 +51,M,02:15:18,05:34:42 +32,M,01:54:49,05:34:42 +42,W,02:20:26,05:34:42 +23,M,02:14:00,05:34:43 +46,M,02:27:12,05:34:43 +43,M,02:21:56,05:34:43 +45,W,02:18:53,05:34:43 +40,M,02:17:16,05:34:44 +37,M,02:21:33,05:34:44 +39,M,02:17:53,05:34:44 +49,W,02:17:11,05:34:45 +58,M,02:23:38,05:34:45 +56,W,02:26:13,05:34:45 +41,M,02:15:54,05:34:45 +47,M,02:16:17,05:34:46 +54,M,02:20:52,05:34:46 +31,M,02:18:34,05:34:46 +59,M,02:17:39,05:34:47 +59,M,02:01:29,05:34:47 +33,M,02:17:51,05:34:48 +52,W,02:16:33,05:34:48 +53,M,02:30:46,05:34:48 +42,W,02:22:39,05:34:48 +55,M,02:29:28,05:34:48 +37,M,02:06:56,05:34:48 +37,W,02:10:27,05:34:49 +45,W,02:16:58,05:34:49 +57,M,02:19:40,05:34:49 +43,M,02:15:47,05:34:50 +38,M,02:29:18,05:34:50 +37,W,02:21:36,05:34:50 +39,M,02:20:22,05:34:50 +42,W,02:16:34,05:34:50 +32,M,02:15:23,05:34:50 +36,M,02:23:27,05:34:50 +62,M,02:14:39,05:34:51 +37,M,02:27:38,05:34:51 +43,W,02:23:18,05:34:51 +50,M,02:20:55,05:34:52 +46,M,02:12:39,05:34:52 +35,W,02:13:40,05:34:52 +26,W,02:03:22,05:34:52 +44,M,02:06:40,05:34:53 +24,W,02:18:02,05:34:53 +57,M,02:24:21,05:34:53 +27,W,02:15:51,05:34:53 +45,M,02:22:52,05:34:53 +48,M,02:14:59,05:34:54 +37,W,02:16:42,05:34:54 +26,W,02:22:15,05:34:54 +23,W,02:08:29,05:34:54 +46,M,02:25:52,05:34:54 +37,W,02:21:24,05:34:55 +44,M,02:04:51,05:34:55 +38,W,02:17:28,05:34:55 +24,W,02:26:57,05:34:55 +56,M,02:16:29,05:34:55 +33,W,02:22:14,05:34:56 +41,M,02:22:22,05:34:56 +34,W,02:23:16,05:34:56 +41,W,02:13:21,05:34:56 +42,W,02:19:25,05:34:56 +23,W,02:17:11,05:34:56 +28,M,02:21:32,05:34:57 +45,M,02:04:12,05:34:57 +34,M,02:06:17,05:34:57 +45,M,02:14:45,05:34:57 +55,W,02:18:32,05:34:57 +23,W,02:16:16,05:34:58 +46,M,02:27:23,05:34:58 +45,W,02:22:06,05:34:59 +52,W,02:23:21,05:34:59 +51,M,02:18:14,05:34:59 +46,M,02:17:30,05:34:59 +59,M,02:14:03,05:34:59 +47,W,02:18:46,05:35:00 +42,W,02:27:23,05:35:00 +44,W,02:19:47,05:35:00 +46,W,02:21:07,05:35:00 +26,M,02:19:11,05:35:00 +31,W,02:18:19,05:35:01 +52,M,02:18:52,05:35:01 +41,W,02:25:39,05:35:01 +48,M,02:20:38,05:35:02 +45,M,02:08:17,05:35:02 +49,M,02:23:31,05:35:02 +49,W,02:17:53,05:35:02 +31,W,02:22:40,05:35:02 +37,W,02:16:33,05:35:03 +23,W,02:18:03,05:35:03 +54,M,02:10:40,05:35:03 +36,W,02:15:12,05:35:04 +46,M,02:17:02,05:35:04 +24,M,02:11:37,05:35:05 +20,M,02:16:49,05:35:05 +37,W,02:20:46,05:35:05 +59,M,02:23:28,05:35:05 +68,M,02:21:42,05:35:06 +28,W,02:15:24,05:35:06 +34,M,02:21:56,05:35:06 +41,W,02:31:23,05:35:07 +37,W,02:17:25,05:35:07 +59,M,02:11:43,05:35:07 +26,M,02:14:27,05:35:07 +51,M,02:12:47,05:35:07 +43,M,02:19:46,05:35:08 +42,M,02:19:21,05:35:08 +48,M,02:11:53,05:35:08 +40,M,02:26:20,05:35:08 +23,M,02:16:56,05:35:09 +67,M,02:14:40,05:35:09 +59,M,02:29:36,05:35:11 +30,M,02:18:40,05:35:11 +34,M,02:06:42,05:35:11 +23,M,02:14:34,05:35:11 +53,M,02:08:46,05:35:12 +45,W,02:21:58,05:35:12 +49,W,02:18:01,05:35:13 +34,W,02:26:10,05:35:13 +44,W,02:23:03,05:35:13 +61,M,02:14:27,05:35:13 +42,W,02:17:22,05:35:14 +42,M,02:15:38,05:35:14 +37,M,02:15:17,05:35:14 +28,M,02:05:07,05:35:14 +27,M,02:17:18,05:35:15 +49,W,02:24:36,05:35:15 +28,W,02:16:20,05:35:16 +45,W,02:25:49,05:35:16 +43,W,02:19:36,05:35:17 +25,W,02:24:06,05:35:17 +57,M,02:05:44,05:35:17 +44,M,02:11:10,05:35:17 +65,M,02:28:53,05:35:18 +29,W,02:16:22,05:35:18 +41,M,02:25:50,05:35:19 +35,W,02:20:02,05:35:19 +34,M,02:10:43,05:35:20 +73,W,02:15:36,05:35:20 +49,M,02:18:18,05:35:20 +57,M,02:17:22,05:35:20 +50,M,02:23:08,05:35:20 +44,M,02:12:14,05:35:21 +49,M,02:27:46,05:35:21 +39,W,02:24:35,05:35:21 +34,M,02:16:26,05:35:21 +49,M,02:08:38,05:35:22 +42,M,02:07:36,05:35:22 +50,W,02:07:47,05:35:22 +30,M,02:18:56,05:35:23 +48,W,02:20:57,05:35:24 +34,W,02:22:55,05:35:24 +40,W,02:17:12,05:35:24 +35,M,02:13:17,05:35:26 +27,W,02:21:18,05:35:27 +28,W,02:18:40,05:35:27 +47,W,02:17:14,05:35:28 +18,W,02:12:53,05:35:28 +39,M,02:16:56,05:35:28 +26,M,02:30:05,05:35:29 +40,W,02:13:53,05:35:29 +41,W,02:32:25,05:35:29 +51,M,02:20:50,05:35:29 +42,W,02:15:55,05:35:30 +57,M,02:21:59,05:35:30 +51,W,02:20:38,05:35:30 +50,W,02:28:04,05:35:30 +46,W,02:23:12,05:35:32 +40,M,02:14:11,05:35:32 +42,W,02:16:35,05:35:32 +53,M,02:18:41,05:35:34 +46,M,02:10:59,05:35:34 +30,M,02:11:44,05:35:34 +58,M,02:15:34,05:35:35 +37,W,02:28:26,05:35:35 +44,W,02:08:05,05:35:36 +24,M,02:02:54,05:35:36 +35,W,02:28:35,05:35:36 +18,M,02:25:00,05:35:36 +49,W,02:22:56,05:35:36 +27,W,02:27:07,05:35:36 +53,M,02:20:54,05:35:36 +52,W,02:15:30,05:35:36 +47,W,02:19:07,05:35:37 +40,W,02:22:03,05:35:37 +43,W,02:21:14,05:35:37 +35,W,02:26:00,05:35:38 +53,M,02:47:27,05:35:38 +56,M,02:22:36,05:35:38 +33,M,02:20:35,05:35:39 +33,W,02:27:58,05:35:39 +25,W,02:32:14,05:35:39 +32,M,02:05:58,05:35:40 +39,M,02:32:07,05:35:40 +27,M,02:13:37,05:35:41 +35,M,02:10:38,05:35:41 +50,M,02:20:55,05:35:42 +55,M,02:13:46,05:35:42 +38,M,02:01:45,05:35:42 +24,W,02:09:25,05:35:44 +37,M,02:13:59,05:35:44 +28,M,02:12:47,05:35:44 +28,W,02:23:04,05:35:45 +53,M,02:15:12,05:35:45 +45,M,02:19:58,05:35:45 +42,W,02:15:07,05:35:45 +59,M,02:10:56,05:35:45 +37,W,02:16:36,05:35:45 +53,M,02:19:24,05:35:46 +40,W,02:25:33,05:35:46 +51,W,02:17:11,05:35:46 +36,W,02:21:18,05:35:46 +50,W,02:20:10,05:35:47 +30,W,02:23:20,05:35:47 +44,M,02:09:56,05:35:47 +51,M,02:11:40,05:35:47 +36,M,02:21:24,05:35:47 +23,W,02:12:39,05:35:48 +29,W,02:24:17,05:35:48 +25,W,02:23:55,05:35:48 +39,M,02:15:11,05:35:48 +34,M,02:14:51,05:35:48 +40,W,02:22:27,05:35:48 +34,W,02:24:34,05:35:48 +63,W,02:22:59,05:35:49 +36,M,02:19:03,05:35:49 +41,W,02:20:15,05:35:49 +34,M,02:10:58,05:35:49 +50,M,02:15:06,05:35:50 +51,M,02:15:28,05:35:50 +28,W,02:16:56,05:35:50 +41,W,02:17:19,05:35:50 +43,W,02:13:07,05:35:51 +47,W,02:24:36,05:35:51 +57,M,02:21:15,05:35:51 +37,M,02:17:33,05:35:52 +33,W,02:21:38,05:35:52 +48,W,02:13:42,05:35:53 +22,W,02:17:02,05:35:53 +48,M,02:14:10,05:35:53 +48,W,02:31:07,05:35:53 +25,W,02:08:59,05:35:53 +36,M,02:18:44,05:35:54 +47,M,02:20:42,05:35:54 +24,M,01:52:16,05:35:54 +58,M,02:17:56,05:35:55 +31,W,02:19:41,05:35:55 +49,M,02:14:09,05:35:56 +28,W,02:28:22,05:35:56 +25,W,02:16:58,05:35:56 +49,M,02:16:27,05:35:56 +32,W,02:23:27,05:35:56 +34,M,02:24:14,05:35:56 +37,W,02:17:18,05:35:56 +25,W,02:25:28,05:35:56 +49,M,02:16:48,05:35:56 +55,W,02:16:40,05:35:56 +40,W,02:23:14,05:35:57 +55,M,02:19:14,05:35:57 +34,M,02:24:29,05:35:58 +52,M,02:17:47,05:35:58 +60,M,02:18:54,05:35:59 +22,M,02:25:30,05:35:59 +42,W,02:14:24,05:36:00 +39,M,02:14:35,05:36:00 +49,M,02:13:54,05:36:00 +29,W,02:11:46,05:36:01 +33,M,02:11:58,05:36:01 +26,W,02:18:12,05:36:02 +36,W,02:22:23,05:36:02 +49,M,02:28:36,05:36:02 +41,M,02:17:07,05:36:02 +29,M,02:31:30,05:36:02 +47,W,02:17:33,05:36:03 +33,W,02:14:20,05:36:03 +58,M,02:20:24,05:36:04 +60,W,02:16:49,05:36:04 +45,M,02:17:38,05:36:04 +35,W,02:28:54,05:36:04 +48,M,02:15:13,05:36:04 +52,W,02:21:13,05:36:04 +35,M,02:32:00,05:36:05 +30,M,02:23:05,05:36:05 +36,M,02:23:15,05:36:05 +43,W,02:22:34,05:36:05 +38,W,02:20:40,05:36:06 +51,M,02:09:45,05:36:06 +35,M,02:27:47,05:36:06 +33,W,02:27:22,05:36:06 +37,M,02:15:46,05:36:06 +46,M,02:21:31,05:36:07 +27,M,02:31:15,05:36:07 +49,M,02:16:19,05:36:07 +36,M,02:10:31,05:36:07 +56,M,02:00:31,05:36:07 +42,M,02:20:19,05:36:08 +33,M,02:16:11,05:36:08 +37,W,02:26:01,05:36:08 +41,M,02:16:12,05:36:08 +51,M,02:17:05,05:36:09 +52,M,02:17:50,05:36:09 +44,M,02:13:40,05:36:09 +48,M,02:23:04,05:36:09 +37,W,02:18:18,05:36:09 +40,M,02:19:20,05:36:09 +41,W,02:04:37,05:36:09 +56,M,02:24:11,05:36:09 +26,M,02:19:36,05:36:09 +29,W,02:20:12,05:36:10 +30,M,02:29:59,05:36:11 +37,M,02:24:12,05:36:11 +51,W,02:21:04,05:36:12 +43,M,02:13:37,05:36:12 +27,W,02:27:42,05:36:12 +39,M,02:15:50,05:36:12 +50,M,02:28:22,05:36:13 +50,M,02:08:53,05:36:13 +41,W,02:21:35,05:36:13 +30,W,02:19:44,05:36:13 +46,M,02:17:26,05:36:14 +55,M,02:13:58,05:36:14 +58,M,02:18:49,05:36:14 +58,M,02:16:08,05:36:15 +50,M,02:18:37,05:36:15 +32,W,02:22:03,05:36:16 +51,W,02:30:21,05:36:16 +38,W,02:20:03,05:36:17 +45,M,02:07:28,05:36:17 +38,M,02:21:14,05:36:17 +43,M,02:04:39,05:36:17 +35,W,02:18:21,05:36:17 +27,M,02:17:52,05:36:17 +32,M,02:14:48,05:36:18 +40,W,02:22:05,05:36:18 +59,M,02:17:04,05:36:18 +45,W,02:23:57,05:36:18 +33,W,02:15:37,05:36:19 +50,M,02:17:50,05:36:20 +29,M,02:22:26,05:36:20 +24,W,02:06:34,05:36:20 +39,W,02:07:09,05:36:20 +28,M,02:22:09,05:36:20 +57,M,02:18:23,05:36:21 +46,M,02:16:34,05:36:21 +49,W,02:23:47,05:36:22 +44,W,02:17:09,05:36:22 +42,M,02:21:13,05:36:22 +41,M,02:13:22,05:36:22 +50,M,02:12:26,05:36:22 +36,M,02:17:37,05:36:22 +41,M,02:20:43,05:36:22 +30,M,02:15:21,05:36:23 +41,M,02:16:14,05:36:24 +44,W,02:17:51,05:36:24 +42,W,02:19:19,05:36:24 +45,W,02:17:50,05:36:24 +54,M,02:21:03,05:36:24 +43,W,02:15:49,05:36:25 +51,W,02:10:50,05:36:25 +25,M,02:00:01,05:36:25 +43,M,02:21:53,05:36:26 +31,M,02:20:41,05:36:26 +42,M,02:18:57,05:36:26 +23,W,02:17:16,05:36:27 +40,M,02:16:43,05:36:28 +41,M,02:14:51,05:36:28 +50,M,02:22:52,05:36:28 +67,M,02:25:27,05:36:28 +49,M,02:22:14,05:36:28 +44,M,02:17:47,05:36:29 +55,M,02:21:28,05:36:29 +40,M,02:11:03,05:36:29 +32,W,02:17:10,05:36:29 +38,W,02:18:48,05:36:29 +32,M,02:14:59,05:36:30 +44,W,02:20:42,05:36:30 +48,M,02:12:17,05:36:30 +32,W,02:20:33,05:36:30 +35,W,02:15:43,05:36:31 +26,W,02:22:27,05:36:31 +49,W,02:16:37,05:36:31 +52,M,02:08:15,05:36:32 +37,W,02:27:56,05:36:32 +53,W,02:14:26,05:36:32 +61,M,02:16:53,05:36:32 +58,M,02:23:40,05:36:33 +57,W,02:17:41,05:36:33 +77,M,02:26:29,05:36:33 +31,W,02:26:30,05:36:34 +56,M,02:13:01,05:36:34 +31,W,02:18:20,05:36:35 +28,W,02:24:47,05:36:35 +51,W,02:28:21,05:36:35 +56,M,02:15:54,05:36:35 +39,W,02:14:27,05:36:35 +32,W,02:25:50,05:36:35 +37,W,02:12:48,05:36:35 +23,M,02:18:58,05:36:36 +39,W,02:21:52,05:36:36 +22,M,02:16:28,05:36:36 +27,W,02:19:38,05:36:36 +60,M,02:34:42,05:36:36 +49,M,02:17:32,05:36:37 +48,M,02:21:23,05:36:37 +43,M,02:25:31,05:36:38 +49,W,02:13:29,05:36:38 +18,M,02:15:24,05:36:38 +23,M,02:25:39,05:36:38 +41,M,02:14:43,05:36:38 +45,M,02:02:11,05:36:39 +41,M,02:20:43,05:36:39 +42,M,02:21:19,05:36:39 +31,W,02:14:19,05:36:40 +32,M,02:19:06,05:36:40 +25,W,02:20:04,05:36:41 +33,M,02:13:11,05:36:41 +31,W,02:28:18,05:36:41 +27,M,02:17:59,05:36:42 +34,W,02:28:28,05:36:42 +52,M,02:29:29,05:36:44 +42,W,02:31:13,05:36:44 +40,M,02:10:48,05:36:44 +65,M,02:23:22,05:36:45 +38,W,02:15:45,05:36:45 +51,W,02:17:38,05:36:45 +47,W,02:22:14,05:36:45 +24,W,02:22:37,05:36:45 +41,W,02:29:05,05:36:46 +28,W,02:13:26,05:36:46 +52,M,02:14:53,05:36:46 +25,W,02:11:53,05:36:46 +66,M,02:18:19,05:36:46 +33,W,02:16:32,05:36:46 +39,M,02:21:44,05:36:47 +43,M,02:26:05,05:36:47 +40,M,02:15:59,05:36:47 +45,M,02:15:06,05:36:47 +50,M,02:19:45,05:36:48 +40,M,02:14:13,05:36:48 +41,M,02:17:18,05:36:48 +65,M,02:07:07,05:36:48 +42,W,02:16:25,05:36:48 +45,W,02:19:45,05:36:48 +24,W,02:26:40,05:36:49 +25,W,02:21:01,05:36:49 +35,M,02:24:28,05:36:49 +38,M,02:16:56,05:36:50 +49,W,02:22:05,05:36:50 +36,W,02:13:38,05:36:51 +40,M,02:09:24,05:36:51 +54,W,02:27:18,05:36:51 +55,M,02:25:38,05:36:51 +37,M,02:16:55,05:36:51 +40,M,02:28:15,05:36:52 +28,W,02:24:42,05:36:52 +65,M,02:23:51,05:36:53 +49,M,02:20:50,05:36:53 +34,W,02:15:59,05:36:53 +50,M,02:17:44,05:36:54 +45,W,02:15:14,05:36:54 +27,W,02:18:30,05:36:54 +56,M,02:11:10,05:36:54 +56,M,02:18:58,05:36:54 +44,W,02:19:37,05:36:54 +33,M,02:04:56,05:36:55 +28,M,02:10:29,05:36:55 +45,M,02:15:20,05:36:56 +35,M,02:13:03,05:36:56 +26,W,02:24:41,05:36:56 +35,W,02:14:43,05:36:56 +63,M,02:24:59,05:36:56 +59,W,02:28:12,05:36:57 +51,W,02:35:23,05:36:57 +42,W,02:17:00,05:36:58 +31,W,02:28:03,05:36:58 +31,W,02:18:00,05:36:58 +67,M,02:14:23,05:36:58 +67,M,02:12:52,05:36:59 +42,M,02:19:21,05:36:59 +41,M,02:10:13,05:36:59 +69,W,02:25:18,05:37:00 +25,W,02:16:19,05:37:00 +38,W,02:21:42,05:37:00 +38,W,02:18:01,05:37:00 +33,W,02:29:47,05:37:00 +36,W,02:17:40,05:37:01 +35,M,02:26:42,05:37:01 +28,W,02:13:51,05:37:02 +27,W,02:13:49,05:37:02 +53,W,02:15:10,05:37:02 +44,M,02:19:31,05:37:02 +46,M,02:23:59,05:37:02 +42,M,02:20:48,05:37:02 +33,W,02:23:15,05:37:02 +35,W,02:17:23,05:37:03 +39,M,02:20:27,05:37:03 +27,W,02:30:31,05:37:03 +41,W,02:25:36,05:37:03 +26,W,02:19:19,05:37:04 +30,W,02:17:12,05:37:04 +30,W,02:13:13,05:37:05 +51,M,02:12:38,05:37:05 +42,M,02:20:02,05:37:06 +51,M,02:11:22,05:37:06 +35,M,02:12:30,05:37:07 +38,W,02:18:44,05:37:07 +30,W,02:25:19,05:37:07 +32,M,02:05:07,05:37:07 +38,M,02:07:46,05:37:08 +46,W,02:21:13,05:37:09 +41,M,02:10:15,05:37:09 +35,M,02:14:56,05:37:09 +30,W,02:23:08,05:37:10 +50,W,02:21:47,05:37:11 +48,M,02:30:46,05:37:12 +50,M,02:04:00,05:37:12 +63,W,02:20:21,05:37:12 +36,M,02:19:14,05:37:12 +47,W,02:23:38,05:37:13 +35,W,02:20:07,05:37:13 +33,M,02:19:04,05:37:13 +44,W,02:12:15,05:37:14 +72,M,02:14:30,05:37:14 +38,M,02:20:27,05:37:15 +43,M,02:27:50,05:37:15 +36,W,02:17:03,05:37:15 +50,M,02:30:24,05:37:16 +60,M,02:15:33,05:37:16 +36,W,02:36:51,05:37:17 +52,W,02:15:18,05:37:18 +30,W,02:06:29,05:37:18 +32,W,02:24:36,05:37:19 +31,M,02:26:59,05:37:19 +35,W,02:16:00,05:37:19 +33,W,02:25:13,05:37:19 +46,M,02:25:53,05:37:19 +48,M,02:17:43,05:37:19 +49,M,02:28:24,05:37:20 +33,M,02:15:18,05:37:20 +48,W,02:25:26,05:37:20 +31,M,02:16:39,05:37:20 +38,M,02:05:12,05:37:20 +64,M,02:21:19,05:37:20 +44,W,02:19:12,05:37:21 +18,M,02:20:05,05:37:21 +46,W,02:17:13,05:37:21 +41,W,02:14:33,05:37:21 +47,M,02:20:24,05:37:21 +56,W,02:24:48,05:37:21 +39,W,02:25:00,05:37:21 +51,M,02:21:20,05:37:23 +26,W,02:11:44,05:37:24 +55,M,02:22:27,05:37:24 +61,M,02:36:38,05:37:24 +36,W,02:21:58,05:37:25 +37,W,02:20:13,05:37:25 +37,W,02:21:51,05:37:25 +41,M,02:19:33,05:37:26 +51,M,02:09:57,05:37:26 +55,M,02:24:23,05:37:26 +38,W,02:25:50,05:37:26 +44,M,02:10:25,05:37:27 +32,W,02:26:57,05:37:27 +54,M,02:37:11,05:37:27 +57,M,02:20:20,05:37:27 +49,M,02:21:52,05:37:27 +61,M,02:10:49,05:37:27 +41,M,02:11:44,05:37:28 +44,W,02:14:13,05:37:28 +46,M,02:10:35,05:37:30 +39,W,02:20:30,05:37:31 +40,W,02:09:52,05:37:31 +29,M,02:17:24,05:37:31 +37,W,02:22:42,05:37:31 +50,M,02:16:08,05:37:32 +65,M,02:14:40,05:37:33 +42,M,02:19:16,05:37:33 +55,M,02:11:35,05:37:34 +66,W,02:23:36,05:37:34 +26,M,02:07:10,05:37:34 +39,M,02:18:39,05:37:34 +30,M,02:11:27,05:37:35 +33,M,02:09:18,05:37:35 +27,M,02:14:13,05:37:36 +50,W,02:21:50,05:37:36 +44,M,02:21:25,05:37:36 +43,M,02:07:28,05:37:36 +41,W,02:27:37,05:37:37 +45,W,02:24:43,05:37:37 +32,M,02:15:53,05:37:38 +52,M,02:14:50,05:37:38 +64,M,02:16:19,05:37:38 +42,M,02:20:28,05:37:39 +44,M,02:22:38,05:37:39 +31,M,02:25:08,05:37:39 +29,M,02:08:51,05:37:40 +39,W,02:16:01,05:37:40 +44,W,02:22:48,05:37:40 +39,W,02:26:07,05:37:41 +38,M,02:25:41,05:37:41 +26,W,02:18:30,05:37:41 +27,W,02:24:10,05:37:41 +40,W,02:28:21,05:37:41 +50,W,02:19:05,05:37:42 +39,W,02:18:02,05:37:42 +41,W,02:21:06,05:37:42 +30,W,02:21:30,05:37:43 +53,M,02:16:05,05:37:43 +30,W,02:22:27,05:37:43 +40,M,02:14:18,05:37:44 +28,M,02:17:00,05:37:44 +55,W,02:23:04,05:37:44 +58,W,02:27:11,05:37:44 +49,M,02:15:57,05:37:45 +33,W,02:16:32,05:37:45 +67,M,02:24:03,05:37:45 +30,W,02:18:58,05:37:46 +30,M,02:16:55,05:37:46 +26,M,02:39:48,05:37:47 +40,M,02:13:30,05:37:47 +29,W,02:17:53,05:37:47 +41,M,02:16:32,05:37:47 +40,W,02:19:55,05:37:47 +31,W,02:15:20,05:37:48 +25,M,02:14:56,05:37:49 +62,M,02:23:53,05:37:49 +17,W,02:13:28,05:37:50 +49,M,02:16:50,05:37:50 +44,W,02:16:14,05:37:50 +36,W,02:23:14,05:37:50 +51,M,02:20:30,05:37:51 +32,W,02:17:58,05:37:52 +32,W,02:24:01,05:37:53 +25,W,02:10:54,05:37:53 +45,W,02:31:33,05:37:53 +51,W,02:15:27,05:37:53 +44,M,02:19:36,05:37:54 +33,W,02:23:17,05:37:54 +48,M,02:02:42,05:37:54 +33,M,02:21:08,05:37:54 +23,W,02:23:24,05:37:54 +26,W,02:25:02,05:37:55 +57,M,02:22:09,05:37:56 +49,M,02:20:43,05:37:56 +36,M,02:19:11,05:37:58 +29,W,02:17:46,05:37:58 +44,M,02:18:33,05:37:58 +45,W,02:11:59,05:37:59 +34,W,02:22:58,05:37:59 +31,W,02:26:19,05:37:59 +44,M,02:22:25,05:37:59 +39,W,02:28:58,05:38:00 +53,W,02:09:38,05:38:00 +32,W,02:25:35,05:38:00 +28,W,02:24:45,05:38:01 +42,M,02:09:43,05:38:01 +46,W,02:25:25,05:38:01 +59,M,02:21:14,05:38:01 +30,M,02:27:11,05:38:02 +44,W,02:20:41,05:38:02 +55,M,02:13:07,05:38:03 +48,M,02:23:26,05:38:03 +60,M,02:21:27,05:38:06 +37,M,02:22:56,05:38:06 +32,W,02:22:14,05:38:07 +55,M,02:08:45,05:38:08 +38,M,02:16:39,05:38:08 +52,M,02:13:41,05:38:08 +33,W,02:12:29,05:38:09 +54,M,02:18:12,05:38:09 +33,W,02:08:38,05:38:10 +57,M,02:43:13,05:38:10 +40,W,02:27:08,05:38:11 +41,M,02:28:50,05:38:11 +40,M,02:21:29,05:38:12 +50,M,02:24:15,05:38:12 +24,W,02:28:51,05:38:12 +71,M,02:18:34,05:38:13 +48,M,02:23:18,05:38:13 +38,M,02:20:59,05:38:14 +59,M,02:20:22,05:38:15 +48,W,02:18:58,05:38:16 +49,M,02:12:10,05:38:16 +25,M,02:20:35,05:38:16 +37,W,02:22:29,05:38:16 +64,W,02:21:15,05:38:17 +40,M,02:14:29,05:38:18 +37,W,02:27:28,05:38:18 +46,M,02:13:07,05:38:18 +29,W,02:14:27,05:38:19 +38,W,02:22:38,05:38:19 +53,M,02:27:54,05:38:19 +49,W,02:27:56,05:38:20 +45,M,02:11:35,05:38:21 +31,W,02:16:56,05:38:21 +58,M,02:10:34,05:38:21 +31,W,02:23:49,05:38:21 +43,W,02:20:55,05:38:22 +49,W,02:19:29,05:38:22 +64,M,02:17:06,05:38:22 +38,W,02:17:42,05:38:23 +32,W,02:33:06,05:38:23 +46,W,02:15:36,05:38:23 +25,M,02:13:10,05:38:24 +47,M,02:23:38,05:38:24 +51,M,02:16:36,05:38:24 +41,M,02:01:58,05:38:25 +41,M,02:14:28,05:38:26 +31,M,02:15:34,05:38:26 +53,M,02:24:08,05:38:26 +47,W,02:19:33,05:38:27 +33,W,02:11:09,05:38:27 +48,W,02:17:57,05:38:28 +55,M,02:32:03,05:38:28 +27,W,02:28:37,05:38:29 +47,W,02:30:04,05:38:30 +45,W,02:25:23,05:38:30 +27,W,02:28:20,05:38:31 +35,M,02:21:55,05:38:31 +45,M,02:21:42,05:38:31 +53,W,02:17:41,05:38:32 +51,W,02:22:25,05:38:32 +29,W,02:20:15,05:38:33 +35,W,02:39:24,05:38:33 +22,W,02:09:13,05:38:34 +55,M,02:12:16,05:38:34 +27,W,02:13:52,05:38:34 +30,W,02:28:17,05:38:34 +46,M,02:29:13,05:38:35 +44,W,02:19:05,05:38:35 +27,W,02:19:52,05:38:36 +49,W,02:27:34,05:38:36 +35,M,02:06:14,05:38:36 +42,M,02:27:41,05:38:36 +35,M,02:13:17,05:38:37 +47,W,02:22:32,05:38:37 +44,M,02:11:29,05:38:37 +46,M,02:21:31,05:38:38 +24,M,02:27:22,05:38:38 +69,M,02:13:34,05:38:38 +55,M,02:19:12,05:38:39 +38,W,02:27:44,05:38:39 +40,W,02:14:58,05:38:41 +44,M,02:19:18,05:38:41 +49,W,02:23:47,05:38:42 +48,W,02:24:20,05:38:42 +41,W,02:18:21,05:38:42 +24,M,02:13:45,05:38:42 +44,M,02:21:37,05:38:43 +50,W,02:28:16,05:38:43 +43,M,02:21:07,05:38:43 +51,M,02:27:00,05:38:44 +33,W,02:16:24,05:38:44 +46,M,02:15:29,05:38:45 +57,M,02:25:58,05:38:45 +26,W,02:25:36,05:38:45 +27,M,02:29:07,05:38:46 +37,M,02:22:44,05:38:46 +47,W,02:20:03,05:38:46 +31,W,02:33:59,05:38:47 +52,W,02:18:05,05:38:47 +44,W,02:21:47,05:38:48 +27,M,02:07:49,05:38:48 +37,M,02:30:35,05:38:48 +58,M,02:15:14,05:38:50 +35,M,02:19:32,05:38:50 +54,M,02:23:30,05:38:50 +38,W,02:15:20,05:38:51 +38,W,02:34:20,05:38:51 +28,W,02:25:52,05:38:51 +25,M,02:05:03,05:38:52 +51,M,02:29:11,05:38:52 +30,W,02:28:18,05:38:52 +47,M,02:20:48,05:38:53 +33,M,02:22:10,05:38:54 +44,W,02:28:04,05:38:54 +44,M,02:13:35,05:38:54 +24,M,02:19:40,05:38:55 +46,W,02:14:45,05:38:55 +43,W,02:16:27,05:38:55 +52,M,02:18:38,05:38:55 +33,W,02:25:50,05:38:56 +54,W,02:21:19,05:38:56 +44,W,02:21:48,05:38:57 +31,W,02:21:32,05:38:57 +34,W,02:18:42,05:38:57 +34,W,02:21:35,05:38:57 +50,M,02:26:43,05:38:57 +38,M,02:12:28,05:38:57 +44,M,02:32:59,05:38:57 +56,M,02:18:42,05:38:59 +24,M,02:10:30,05:39:00 +35,M,02:10:27,05:39:00 +57,W,02:16:51,05:39:00 +52,M,02:08:14,05:39:01 +49,M,02:24:11,05:39:01 +38,W,02:08:01,05:39:02 +40,W,02:21:31,05:39:02 +59,M,02:29:25,05:39:02 +41,W,02:16:47,05:39:02 +46,M,02:11:49,05:39:02 +50,M,02:29:02,05:39:02 +61,M,02:20:04,05:39:04 +23,M,02:11:42,05:39:04 +41,M,02:27:08,05:39:04 +45,W,02:23:25,05:39:05 +42,W,02:12:56,05:39:06 +51,M,02:06:37,05:39:06 +44,M,02:20:15,05:39:07 +40,M,02:21:58,05:39:07 +64,M,02:29:58,05:39:07 +39,W,02:20:21,05:39:07 +69,M,02:27:42,05:39:08 +26,M,01:50:58,05:39:10 +70,M,02:15:20,05:39:10 +35,W,02:20:19,05:39:10 +41,M,02:23:44,05:39:11 +45,W,02:20:48,05:39:11 +60,W,02:20:11,05:39:12 +46,W,02:14:35,05:39:13 +50,M,02:31:28,05:39:13 +49,W,02:18:16,05:39:13 +66,M,02:21:54,05:39:15 +42,M,02:22:41,05:39:16 +31,M,02:19:12,05:39:16 +40,M,02:14:35,05:39:17 +41,M,02:18:43,05:39:18 +28,W,02:35:26,05:39:18 +54,M,02:24:41,05:39:18 +53,M,02:22:38,05:39:18 +20,W,02:20:23,05:39:18 +62,M,02:11:10,05:39:18 +24,W,02:35:25,05:39:19 +55,M,01:55:45,05:39:19 +46,M,02:29:53,05:39:19 +25,M,02:15:35,05:39:19 +40,W,02:13:40,05:39:20 +35,W,02:20:52,05:39:20 +48,M,02:17:06,05:39:20 +49,W,02:22:59,05:39:20 +23,M,02:27:42,05:39:20 +47,M,02:27:11,05:39:20 +29,M,02:22:16,05:39:20 +45,W,02:31:10,05:39:20 +34,M,02:25:17,05:39:21 +45,W,02:13:35,05:39:21 +44,M,02:21:37,05:39:21 +41,W,02:12:53,05:39:22 +38,M,02:06:05,05:39:22 +30,W,02:20:22,05:39:23 +29,M,02:22:49,05:39:24 +49,M,02:15:06,05:39:24 +42,W,02:08:23,05:39:24 +30,W,02:19:13,05:39:26 +45,W,02:21:34,05:39:26 +36,M,02:18:52,05:39:26 +41,W,02:14:10,05:39:26 +32,M,02:23:55,05:39:28 +23,W,02:11:14,05:39:29 +45,M,02:11:56,05:39:29 +25,M,02:16:29,05:39:29 +44,W,02:20:05,05:39:30 +27,W,02:27:33,05:39:30 +51,M,02:21:54,05:39:30 +47,M,02:26:03,05:39:30 +35,W,02:18:25,05:39:30 +33,M,02:10:58,05:39:31 +43,W,02:29:08,05:39:32 +38,W,02:32:23,05:39:32 +24,M,02:19:52,05:39:32 +38,W,02:22:27,05:39:33 +60,M,02:03:43,05:39:33 +30,M,02:13:49,05:39:33 +33,M,02:09:10,05:39:33 +32,W,02:22:02,05:39:33 +42,M,02:21:42,05:39:35 +38,M,02:30:13,05:39:35 +47,M,02:09:26,05:39:35 +66,M,02:18:06,05:39:35 +43,W,02:24:47,05:39:36 +43,M,02:22:03,05:39:36 +53,M,02:17:30,05:39:36 +27,M,02:14:54,05:39:36 +43,M,02:19:25,05:39:37 +34,W,02:19:29,05:39:37 +24,W,02:33:56,05:39:37 +40,M,02:30:54,05:39:38 +41,M,02:26:51,05:39:39 +38,W,02:23:09,05:39:39 +31,M,02:17:31,05:39:39 +30,M,02:29:01,05:39:39 +65,W,02:15:30,05:39:40 +29,M,02:09:51,05:39:40 +44,W,02:19:41,05:39:40 +63,W,02:21:29,05:39:40 +46,M,02:24:05,05:39:41 +45,M,02:28:15,05:39:41 +31,M,02:22:01,05:39:41 +43,M,02:30:15,05:39:42 +52,W,02:19:29,05:39:42 +44,M,02:12:57,05:39:42 +47,W,02:27:32,05:39:42 +49,M,02:19:29,05:39:43 +47,M,02:16:28,05:39:43 +57,M,02:14:07,05:39:44 +40,W,02:19:08,05:39:44 +31,W,02:23:14,05:39:44 +40,M,02:18:16,05:39:45 +21,W,02:27:13,05:39:45 +47,W,02:17:59,05:39:46 +33,M,02:09:34,05:39:47 +38,M,02:16:18,05:39:47 +46,M,02:18:47,05:39:47 +46,M,02:26:41,05:39:47 +35,W,02:21:25,05:39:48 +41,M,02:17:12,05:39:48 +42,W,02:19:41,05:39:48 +63,W,02:18:44,05:39:50 +28,M,02:17:35,05:39:50 +48,M,02:21:30,05:39:52 +29,W,02:23:20,05:39:54 +37,M,02:12:16,05:39:54 +51,W,02:21:18,05:39:54 +20,W,02:34:50,05:39:55 +38,M,02:11:03,05:39:55 +41,W,02:22:19,05:39:55 +55,W,02:21:11,05:39:55 +28,W,02:16:37,05:39:56 +36,M,02:26:49,05:39:56 +54,M,02:10:03,05:39:56 +26,W,02:26:45,05:39:56 +49,W,02:06:06,05:39:57 +42,M,02:11:24,05:39:57 +52,M,02:13:49,05:39:57 +32,M,02:20:24,05:39:59 +40,M,02:18:10,05:39:59 +48,W,02:33:02,05:39:59 +57,M,02:08:48,05:40:00 +60,M,02:27:27,05:40:00 +46,M,02:13:42,05:40:01 +38,M,02:16:38,05:40:01 +21,M,02:03:28,05:40:02 +42,W,02:25:43,05:40:03 +31,W,02:12:07,05:40:03 +37,M,02:17:36,05:40:03 +40,M,02:05:43,05:40:04 +38,M,02:16:16,05:40:04 +26,M,02:13:17,05:40:05 +50,M,02:08:07,05:40:05 +23,M,02:14:50,05:40:06 +53,W,02:16:41,05:40:06 +35,M,02:21:23,05:40:06 +38,M,02:25:17,05:40:06 +41,W,02:21:35,05:40:06 +26,M,02:19:03,05:40:06 +41,W,02:23:13,05:40:07 +51,W,02:24:05,05:40:07 +53,M,02:27:33,05:40:08 +28,W,02:14:52,05:40:08 +35,W,02:26:06,05:40:08 +39,W,02:33:05,05:40:08 +40,M,02:28:57,05:40:08 +39,W,02:17:58,05:40:09 +32,M,02:22:45,05:40:10 +43,W,02:13:30,05:40:10 +37,M,02:09:19,05:40:10 +46,M,02:14:15,05:40:10 +24,W,02:23:17,05:40:10 +47,W,02:36:45,05:40:10 +28,M,02:36:49,05:40:10 +33,W,02:21:34,05:40:10 +26,M,02:14:33,05:40:11 +40,W,02:16:35,05:40:11 +33,M,02:26:41,05:40:11 +31,W,02:06:55,05:40:11 +34,M,02:18:33,05:40:11 +44,M,02:08:43,05:40:12 +57,M,02:15:58,05:40:12 +51,W,02:16:18,05:40:13 +43,M,02:26:58,05:40:14 +31,W,02:14:08,05:40:15 +39,W,02:29:08,05:40:15 +26,W,02:25:39,05:40:15 +55,M,02:32:12,05:40:15 +46,W,02:27:36,05:40:16 +35,M,02:09:29,05:40:16 +30,M,02:13:13,05:40:16 +47,W,02:14:55,05:40:16 +43,W,02:21:09,05:40:17 +55,M,02:17:47,05:40:17 +26,M,02:12:42,05:40:18 +39,M,02:21:03,05:40:18 +38,M,02:23:11,05:40:19 +37,M,02:17:34,05:40:19 +31,M,02:26:41,05:40:19 +48,M,02:20:39,05:40:20 +38,M,02:16:22,05:40:20 +28,W,02:25:23,05:40:21 +46,W,02:13:53,05:40:21 +28,W,02:24:18,05:40:22 +27,M,02:14:35,05:40:22 +62,M,02:12:09,05:40:24 +46,M,02:17:01,05:40:25 +58,M,02:27:29,05:40:27 +46,M,02:10:14,05:40:27 +39,M,02:32:46,05:40:27 +56,W,02:21:43,05:40:27 +48,M,02:04:05,05:40:28 +62,M,02:17:38,05:40:28 +24,W,02:13:00,05:40:29 +44,W,02:23:21,05:40:30 +31,W,02:20:50,05:40:30 +27,W,02:23:15,05:40:30 +50,M,02:20:57,05:40:31 +33,W,02:30:42,05:40:31 +42,W,02:23:42,05:40:31 +46,M,02:19:34,05:40:32 +49,M,02:14:12,05:40:33 +59,M,02:24:43,05:40:34 +34,W,02:24:04,05:40:35 +32,W,02:22:20,05:40:35 +29,M,02:11:02,05:40:35 +43,W,02:25:43,05:40:36 +25,W,02:30:09,05:40:37 +50,M,02:16:40,05:40:37 +52,W,02:23:45,05:40:37 +28,W,02:21:41,05:40:38 +37,M,02:21:30,05:40:39 +36,W,02:26:46,05:40:40 +69,M,02:20:33,05:40:40 +56,M,02:23:48,05:40:40 +34,W,02:23:39,05:40:40 +33,M,02:24:28,05:40:41 +28,W,02:22:28,05:40:41 +38,W,02:26:30,05:40:41 +45,M,02:08:32,05:40:42 +45,M,02:27:42,05:40:43 +52,M,02:37:01,05:40:43 +35,W,02:25:29,05:40:44 +54,M,02:18:15,05:40:44 +43,M,02:21:59,05:40:45 +33,W,02:33:23,05:40:45 +28,M,02:36:24,05:40:46 +28,W,02:13:45,05:40:46 +47,M,02:23:14,05:40:47 +44,M,02:07:41,05:40:47 +43,W,02:21:09,05:40:47 +22,W,02:20:02,05:40:47 +28,W,02:22:53,05:40:47 +57,M,02:12:18,05:40:48 +28,W,02:21:28,05:40:48 +64,M,02:18:12,05:40:48 +40,W,02:28:10,05:40:48 +33,W,02:25:53,05:40:48 +47,W,02:11:25,05:40:48 +58,M,02:05:11,05:40:49 +36,M,02:34:12,05:40:49 +31,M,02:29:59,05:40:50 +56,W,02:24:36,05:40:50 +36,M,02:08:03,05:40:51 +50,W,02:21:11,05:40:51 +30,W,02:25:41,05:40:52 +31,W,02:24:39,05:40:52 +44,M,02:17:59,05:40:52 +50,W,02:18:39,05:40:52 +67,W,02:22:04,05:40:52 +47,W,02:13:41,05:40:53 +43,W,02:17:30,05:40:53 +50,M,02:33:54,05:40:54 +26,W,02:11:55,05:40:54 +52,M,02:29:38,05:40:54 +32,W,02:21:16,05:40:56 +42,W,02:18:19,05:40:56 +31,M,02:19:00,05:40:56 +31,W,02:21:55,05:40:57 +32,W,02:35:12,05:40:57 +41,M,02:34:59,05:40:58 +29,W,02:24:33,05:40:58 +30,W,02:19:18,05:40:59 +53,M,02:32:26,05:40:59 +40,M,02:18:52,05:40:59 +42,W,02:22:15,05:40:59 +43,M,02:17:25,05:41:00 +39,W,02:14:22,05:41:00 +34,W,02:23:52,05:41:00 +40,M,02:21:04,05:41:01 +38,W,02:13:39,05:41:02 +37,M,02:30:17,05:41:02 +19,W,02:11:48,05:41:02 +36,W,02:22:44,05:41:02 +22,W,02:18:53,05:41:03 +36,M,02:20:35,05:41:03 +38,W,02:24:39,05:41:03 +27,M,02:08:35,05:41:04 +59,W,02:27:28,05:41:04 +39,M,02:14:33,05:41:04 +28,W,02:26:07,05:41:04 +23,M,02:28:38,05:41:05 +36,W,02:25:50,05:41:06 +32,M,02:27:13,05:41:07 +39,W,02:25:40,05:41:07 +54,M,02:15:52,05:41:07 +42,M,01:58:03,05:41:07 +39,W,02:22:01,05:41:08 +37,W,02:17:40,05:41:09 +27,W,02:24:13,05:41:10 +40,M,02:16:44,05:41:10 +45,M,02:21:58,05:41:10 +26,M,02:05:34,05:41:10 +28,M,02:06:48,05:41:11 +25,W,02:09:26,05:41:12 +36,M,02:23:00,05:41:12 +46,W,02:12:16,05:41:12 +31,W,02:21:16,05:41:13 +33,W,02:22:43,05:41:13 +32,M,02:09:58,05:41:13 +41,M,02:15:17,05:41:14 +32,M,02:24:47,05:41:14 +49,W,02:18:59,05:41:15 +63,M,02:15:08,05:41:15 +57,W,02:13:16,05:41:15 +31,W,02:15:56,05:41:16 +25,W,02:13:04,05:41:16 +43,M,02:17:25,05:41:16 +48,W,02:20:48,05:41:17 +45,M,02:21:28,05:41:17 +41,W,02:35:35,05:41:17 +45,W,02:17:40,05:41:18 +59,M,02:17:55,05:41:18 +42,M,02:22:20,05:41:18 +31,W,02:26:58,05:41:20 +46,W,02:33:38,05:41:20 +62,M,02:25:48,05:41:21 +29,W,02:07:25,05:41:21 +71,M,02:15:37,05:41:21 +65,M,02:15:23,05:41:21 +27,M,02:23:54,05:41:22 +36,W,02:27:50,05:41:23 +43,W,02:34:24,05:41:23 +37,M,02:17:34,05:41:24 +41,M,02:17:21,05:41:25 +54,M,02:21:48,05:41:27 +39,M,02:23:12,05:41:27 +37,W,02:22:00,05:41:27 +48,M,02:16:43,05:41:27 +56,M,02:24:29,05:41:27 +56,W,02:24:07,05:41:28 +46,W,02:13:50,05:41:28 +42,W,02:16:24,05:41:28 +42,M,02:19:02,05:41:29 +23,W,02:24:27,05:41:29 +56,M,02:22:35,05:41:29 +38,M,02:10:11,05:41:30 +50,M,02:14:47,05:41:30 +30,M,02:34:31,05:41:30 +66,M,02:16:48,05:41:31 +31,M,02:17:30,05:41:31 +25,W,02:37:22,05:41:32 +23,M,02:14:47,05:41:32 +56,M,02:29:20,05:41:32 +41,M,02:10:52,05:41:33 +39,W,02:20:33,05:41:33 +45,M,02:08:58,05:41:33 +51,M,02:17:35,05:41:34 +75,M,02:28:55,05:41:34 +51,M,02:29:01,05:41:34 +46,W,02:20:18,05:41:34 +45,W,02:29:18,05:41:34 +38,W,02:25:54,05:41:34 +32,M,02:31:52,05:41:35 +38,M,02:25:17,05:41:36 +41,M,02:18:01,05:41:36 +33,M,02:23:21,05:41:36 +50,M,02:24:41,05:41:36 +34,W,02:17:35,05:41:37 +48,M,02:22:25,05:41:38 +43,M,02:12:37,05:41:38 +33,M,02:33:06,05:41:39 +42,M,02:10:40,05:41:39 +39,M,02:18:04,05:41:40 +36,M,02:23:16,05:41:40 +25,M,02:22:05,05:41:40 +32,W,02:27:36,05:41:41 +46,W,02:21:56,05:41:41 +30,W,02:16:30,05:41:41 +66,M,02:24:43,05:41:42 +34,W,02:31:30,05:41:43 +32,W,02:30:43,05:41:43 +45,M,02:23:24,05:41:43 +39,W,02:28:34,05:41:44 +58,M,02:17:37,05:41:44 +55,M,02:18:58,05:41:44 +29,W,02:27:48,05:41:44 +25,W,02:26:50,05:41:46 +38,W,02:24:18,05:41:47 +44,M,02:23:38,05:41:48 +33,W,02:15:15,05:41:48 +25,W,02:19:15,05:41:48 +49,W,02:16:18,05:41:48 +46,M,02:19:58,05:41:49 +35,M,02:16:12,05:41:50 +25,W,02:23:51,05:41:50 +57,M,02:26:56,05:41:50 +65,M,02:24:16,05:41:51 +51,W,02:20:45,05:41:51 +59,M,02:17:24,05:41:51 +57,M,02:22:38,05:41:52 +22,M,02:16:27,05:41:52 +62,M,02:21:27,05:41:53 +53,M,02:21:18,05:41:53 +63,W,02:22:51,05:41:54 +45,M,02:16:05,05:41:55 +26,W,02:20:14,05:41:55 +37,M,02:16:00,05:41:55 +27,W,02:21:33,05:41:55 +67,M,02:37:04,05:41:56 +43,W,02:13:40,05:41:57 +43,W,02:22:42,05:41:57 +47,M,02:22:32,05:41:57 +39,M,02:28:02,05:41:57 +58,M,02:20:42,05:41:58 +72,M,02:13:25,05:41:59 +43,M,02:13:03,05:42:01 +44,M,02:38:26,05:42:02 +49,W,02:15:23,05:42:02 +54,W,02:14:14,05:42:03 +35,W,02:21:54,05:42:03 +59,W,02:22:16,05:42:04 +29,W,02:22:28,05:42:04 +60,M,02:25:05,05:42:05 +28,W,02:31:33,05:42:05 +43,M,02:18:14,05:42:06 +28,M,02:18:40,05:42:06 +29,W,02:21:15,05:42:06 +23,W,02:27:24,05:42:08 +52,W,02:26:29,05:42:08 +51,M,02:27:09,05:42:08 +39,W,02:14:51,05:42:08 +46,W,02:24:28,05:42:09 +66,M,02:27:43,05:42:09 +28,M,02:20:30,05:42:10 +36,W,02:31:24,05:42:10 +24,W,02:17:45,05:42:10 +45,W,02:27:45,05:42:10 +62,M,02:14:04,05:42:10 +26,M,02:14:15,05:42:10 +60,M,02:24:19,05:42:11 +41,W,02:23:16,05:42:11 +29,W,02:20:05,05:42:12 +49,M,02:29:28,05:42:12 +46,M,02:10:55,05:42:13 +53,W,02:29:30,05:42:13 +38,M,02:24:48,05:42:13 +41,W,02:31:26,05:42:14 +25,W,02:03:34,05:42:14 +49,M,02:23:30,05:42:14 +27,M,02:20:23,05:42:14 +28,M,02:02:47,05:42:14 +25,M,02:26:25,05:42:15 +44,M,02:24:09,05:42:15 +36,M,02:26:07,05:42:15 +49,W,02:28:47,05:42:16 +59,M,02:26:14,05:42:16 +38,M,02:18:41,05:42:16 +58,M,02:21:32,05:42:17 +37,W,02:21:01,05:42:17 +57,M,02:20:07,05:42:19 +48,W,02:19:12,05:42:20 +69,M,02:18:45,05:42:20 +40,M,02:29:41,05:42:20 +34,W,02:18:41,05:42:20 +66,M,02:35:30,05:42:20 +40,W,02:18:28,05:42:21 +67,M,02:24:05,05:42:22 +61,M,02:13:37,05:42:22 +37,W,02:26:49,05:42:22 +31,M,02:19:03,05:42:22 +50,W,02:22:11,05:42:22 +35,W,02:23:00,05:42:24 +33,M,02:30:27,05:42:24 +29,W,02:23:02,05:42:25 +53,M,02:17:07,05:42:25 +50,M,02:34:26,05:42:25 +69,M,02:23:06,05:42:26 +48,M,02:19:12,05:42:26 +30,M,02:11:37,05:42:26 +29,W,02:11:03,05:42:26 +29,W,02:22:55,05:42:26 +44,W,02:18:26,05:42:27 +30,M,02:23:55,05:42:27 +49,W,02:20:14,05:42:28 +36,M,02:23:57,05:42:29 +38,W,02:17:47,05:42:30 +42,M,02:23:03,05:42:30 +41,W,02:22:53,05:42:30 +47,W,02:27:38,05:42:30 +55,M,02:03:11,05:42:31 +25,W,02:10:57,05:42:31 +41,M,01:57:50,05:42:32 +29,W,02:25:19,05:42:33 +50,M,02:05:58,05:42:33 +29,M,02:20:25,05:42:34 +25,W,02:21:25,05:42:34 +26,W,02:29:52,05:42:34 +34,W,02:20:24,05:42:34 +59,M,02:17:54,05:42:35 +60,M,02:13:37,05:42:36 +37,M,02:17:10,05:42:37 +53,M,02:22:52,05:42:38 +27,W,02:15:11,05:42:39 +52,M,02:20:59,05:42:40 +36,M,02:22:09,05:42:40 +31,M,02:21:38,05:42:40 +36,W,02:20:40,05:42:40 +45,W,02:27:37,05:42:41 +48,W,02:30:29,05:42:41 +38,W,02:35:12,05:42:41 +23,W,02:10:55,05:42:41 +46,M,02:18:14,05:42:42 +46,M,02:13:47,05:42:42 +47,M,02:18:13,05:42:43 +53,M,02:27:06,05:42:43 +38,W,02:24:19,05:42:43 +36,W,02:22:32,05:42:43 +49,M,02:14:26,05:42:44 +27,W,02:29:50,05:42:45 +31,W,02:13:27,05:42:45 +26,W,02:20:16,05:42:46 +32,M,02:20:05,05:42:46 +43,W,02:26:13,05:42:46 +29,W,02:32:14,05:42:46 +30,W,02:24:35,05:42:47 +29,M,02:39:08,05:42:48 +36,M,02:12:07,05:42:48 +45,W,02:14:21,05:42:48 +53,M,02:26:35,05:42:48 +51,M,02:29:17,05:42:48 +27,W,02:18:24,05:42:49 +35,M,02:15:42,05:42:49 +45,M,02:09:48,05:42:49 +38,W,02:21:30,05:42:50 +67,M,02:21:48,05:42:50 +49,W,02:32:58,05:42:51 +46,W,02:25:02,05:42:51 +39,W,02:22:01,05:42:52 +57,W,02:24:04,05:42:52 +52,W,02:28:29,05:42:53 +38,W,02:21:13,05:42:54 +31,M,02:28:33,05:42:54 +51,M,02:28:58,05:42:54 +47,W,02:18:13,05:42:55 +30,W,02:25:52,05:42:55 +38,M,02:18:25,05:42:55 +27,M,02:07:57,05:42:55 +50,M,02:25:16,05:42:56 +40,W,02:21:00,05:42:57 +47,M,02:19:59,05:42:58 +53,M,02:18:10,05:42:58 +63,M,02:27:25,05:42:58 +47,W,02:25:01,05:42:58 +47,W,02:35:29,05:42:58 +26,M,02:32:24,05:42:59 +61,M,02:15:07,05:43:00 +62,W,02:23:44,05:43:01 +25,W,02:24:04,05:43:02 +43,W,02:20:33,05:43:02 +35,W,02:17:29,05:43:03 +30,W,02:27:27,05:43:03 +44,W,02:17:33,05:43:03 +35,W,02:20:05,05:43:03 +59,M,02:22:05,05:43:03 +54,M,02:17:36,05:43:04 +31,W,02:26:29,05:43:04 +28,W,02:15:56,05:43:05 +53,M,02:19:56,05:43:06 +33,M,02:18:09,05:43:06 +61,M,02:22:35,05:43:06 +51,W,02:30:55,05:43:06 +35,W,01:59:36,05:43:07 +30,W,02:25:54,05:43:08 +30,W,02:15:29,05:43:08 +34,M,02:30:32,05:43:09 +31,W,02:29:49,05:43:09 +77,M,02:27:32,05:43:09 +22,M,02:22:46,05:43:09 +29,W,02:21:31,05:43:10 +48,M,02:32:36,05:43:10 +45,W,02:27:59,05:43:10 +32,M,02:19:23,05:43:10 +40,M,02:24:22,05:43:11 +49,M,02:21:02,05:43:11 +43,M,02:08:12,05:43:12 +45,W,02:20:02,05:43:12 +43,M,02:28:41,05:43:13 +47,M,02:22:51,05:43:13 +49,M,02:14:34,05:43:13 +37,M,02:26:36,05:43:13 +31,M,02:20:56,05:43:13 +59,W,02:20:44,05:43:13 +65,W,02:18:56,05:43:14 +34,M,02:12:53,05:43:14 +51,M,02:16:40,05:43:15 +65,M,02:23:20,05:43:16 +59,M,02:14:10,05:43:16 +28,M,02:28:01,05:43:17 +33,M,02:08:17,05:43:17 +48,W,02:22:19,05:43:17 +40,W,02:26:25,05:43:18 +27,W,02:18:31,05:43:19 +48,W,02:26:41,05:43:19 +33,W,02:20:05,05:43:20 +27,W,02:19:07,05:43:20 +47,M,02:12:28,05:43:21 +39,W,02:30:29,05:43:21 +43,M,02:18:48,05:43:21 +49,W,02:30:40,05:43:21 +24,M,02:18:02,05:43:22 +58,M,02:46:11,05:43:24 +59,M,02:21:26,05:43:24 +60,M,02:32:42,05:43:25 +27,W,02:19:15,05:43:25 +48,M,02:16:06,05:43:26 +29,W,02:14:41,05:43:27 +39,M,02:28:57,05:43:27 +36,M,02:21:07,05:43:27 +51,M,02:37:31,05:43:27 +30,W,02:15:44,05:43:27 +43,W,02:16:34,05:43:29 +49,W,02:12:58,05:43:29 +41,W,02:25:29,05:43:29 +43,M,02:21:10,05:43:31 +35,W,02:19:09,05:43:31 +33,M,02:16:27,05:43:31 +39,M,02:11:18,05:43:31 +50,W,02:24:17,05:43:31 +34,W,02:27:18,05:43:31 +54,M,02:27:10,05:43:32 +23,M,02:20:31,05:43:32 +26,W,02:24:05,05:43:33 +45,M,02:22:20,05:43:33 +43,W,02:23:55,05:43:34 +57,M,02:17:08,05:43:34 +33,W,02:18:14,05:43:35 +40,W,02:27:36,05:43:35 +37,W,02:22:20,05:43:35 +32,W,02:25:46,05:43:36 +46,W,02:22:24,05:43:36 +37,M,02:10:48,05:43:37 +44,M,02:16:50,05:43:37 +46,W,02:20:48,05:43:38 +40,M,02:27:10,05:43:38 +24,W,02:24:40,05:43:39 +48,M,02:24:16,05:43:39 +41,M,02:26:35,05:43:39 +27,W,02:17:50,05:43:39 +28,M,02:19:46,05:43:39 +42,W,02:08:30,05:43:40 +38,W,02:16:36,05:43:42 +46,M,02:17:21,05:43:42 +31,W,02:21:49,05:43:43 +37,M,02:18:55,05:43:43 +37,M,02:31:10,05:43:44 +49,W,02:17:42,05:43:44 +40,W,02:31:34,05:43:45 +51,M,02:30:51,05:43:45 +50,M,02:12:35,05:43:46 +48,W,02:26:03,05:43:47 +39,W,02:21:01,05:43:47 +39,M,02:18:35,05:43:47 +41,M,02:24:19,05:43:47 +46,W,02:25:36,05:43:47 +25,W,02:16:45,05:43:48 +30,M,02:09:10,05:43:48 +60,M,02:16:19,05:43:49 +46,M,02:19:23,05:43:50 +31,M,02:10:23,05:43:50 +58,W,02:38:11,05:43:50 +39,M,02:22:18,05:43:50 +41,W,02:20:46,05:43:51 +27,W,02:17:11,05:43:51 +35,M,02:06:35,05:43:53 +60,M,02:20:23,05:43:53 +43,W,02:25:31,05:43:53 +46,W,02:24:24,05:43:54 +39,M,02:22:12,05:43:54 +32,W,02:32:49,05:43:55 +26,W,02:24:25,05:43:55 +48,M,02:20:03,05:43:55 +52,M,02:22:03,05:43:56 +59,M,02:21:12,05:43:58 +28,M,02:26:39,05:43:59 +34,W,02:21:06,05:43:59 +52,M,02:25:28,05:44:01 +48,W,02:26:34,05:44:02 +49,M,02:22:07,05:44:03 +31,M,02:23:10,05:44:03 +52,M,02:24:11,05:44:03 +38,M,02:13:52,05:44:04 +68,M,02:32:06,05:44:05 +43,W,02:27:45,05:44:06 +35,W,02:17:54,05:44:06 +31,W,02:32:06,05:44:06 +61,M,02:26:07,05:44:07 +39,M,02:06:34,05:44:07 +49,W,02:39:01,05:44:07 +35,M,02:17:19,05:44:07 +25,M,02:20:20,05:44:07 +43,W,02:22:59,05:44:08 +48,M,02:20:40,05:44:08 +30,M,02:20:40,05:44:09 +56,M,02:25:31,05:44:09 +33,W,02:18:46,05:44:10 +42,M,02:27:44,05:44:10 +54,M,02:23:04,05:44:10 +42,W,02:17:14,05:44:10 +30,M,02:27:09,05:44:10 +52,W,02:20:35,05:44:10 +57,W,02:31:20,05:44:10 +60,M,02:16:45,05:44:11 +30,W,02:26:58,05:44:11 +47,M,02:15:01,05:44:11 +50,M,02:06:05,05:44:12 +39,W,02:14:38,05:44:15 +26,W,02:18:37,05:44:16 +23,M,02:11:38,05:44:16 +53,M,02:19:19,05:44:16 +51,M,02:20:22,05:44:16 +56,M,02:18:39,05:44:16 +38,M,02:08:54,05:44:16 +28,M,02:04:01,05:44:16 +21,M,02:19:00,05:44:16 +33,M,02:24:17,05:44:16 +22,M,02:13:29,05:44:17 +34,W,02:31:08,05:44:17 +23,W,02:23:36,05:44:17 +34,M,02:15:20,05:44:18 +42,W,02:16:55,05:44:18 +36,M,02:15:13,05:44:19 +54,W,02:29:14,05:44:19 +38,M,02:28:50,05:44:20 +32,W,02:25:49,05:44:20 +74,M,02:35:06,05:44:20 +27,W,02:28:16,05:44:21 +25,W,02:23:58,05:44:21 +51,W,02:31:15,05:44:21 +46,W,02:21:26,05:44:21 +56,M,02:21:09,05:44:22 +38,W,02:45:22,05:44:22 +43,M,02:33:21,05:44:22 +40,M,02:30:35,05:44:22 +31,W,02:27:46,05:44:22 +36,M,02:36:58,05:44:23 +50,M,02:17:03,05:44:23 +25,M,02:21:02,05:44:23 +47,M,02:18:37,05:44:24 +43,W,02:20:58,05:44:24 +50,W,02:26:55,05:44:24 +41,M,02:16:40,05:44:25 +31,W,02:26:12,05:44:25 +31,M,02:19:56,05:44:25 +55,M,02:10:22,05:44:25 +27,M,02:14:25,05:44:25 +52,W,02:22:04,05:44:26 +48,W,02:32:45,05:44:26 +49,M,02:06:10,05:44:26 +38,W,02:13:41,05:44:26 +37,W,02:22:47,05:44:28 +48,M,02:15:06,05:44:28 +44,M,02:21:27,05:44:29 +63,W,02:28:23,05:44:29 +26,W,02:23:29,05:44:29 +54,M,02:20:44,05:44:29 +36,W,02:27:11,05:44:30 +46,M,02:11:28,05:44:30 +40,M,01:59:37,05:44:31 +29,M,02:13:09,05:44:31 +34,M,02:23:07,05:44:31 +54,M,02:12:15,05:44:31 +55,M,02:53:41,05:44:31 +30,W,02:30:07,05:44:32 +33,W,02:10:06,05:44:33 +35,M,02:14:39,05:44:33 +51,M,02:12:40,05:44:34 +43,M,02:29:52,05:44:34 +34,W,02:16:58,05:44:34 +21,W,02:25:13,05:44:34 +28,M,02:20:42,05:44:35 +41,W,02:24:22,05:44:35 +41,W,02:24:26,05:44:36 +44,W,02:27:33,05:44:36 +35,W,02:17:24,05:44:36 +27,W,02:17:25,05:44:36 +26,W,02:19:15,05:44:37 +30,M,02:19:58,05:44:37 +52,M,02:23:47,05:44:38 +23,M,02:19:54,05:44:38 +45,M,02:21:14,05:44:38 +51,M,02:12:52,05:44:39 +54,M,02:05:48,05:44:40 +40,W,02:25:09,05:44:40 +42,W,02:21:34,05:44:40 +41,M,02:27:54,05:44:40 +39,W,02:24:15,05:44:42 +35,W,02:24:04,05:44:42 +34,W,02:28:01,05:44:43 +50,W,02:25:50,05:44:43 +33,M,02:19:52,05:44:43 +33,W,02:23:41,05:44:43 +32,W,02:28:53,05:44:44 +64,M,02:14:26,05:44:44 +37,W,02:31:09,05:44:44 +37,M,02:24:52,05:44:44 +35,W,02:28:39,05:44:45 +47,M,02:35:04,05:44:46 +56,M,02:23:51,05:44:46 +34,W,02:23:32,05:44:46 +46,W,02:27:42,05:44:47 +62,M,02:24:19,05:44:48 +41,M,02:18:53,05:44:48 +31,W,02:16:35,05:44:48 +48,M,02:19:53,05:44:48 +43,W,02:18:54,05:44:48 +26,M,02:23:23,05:44:49 +38,M,02:41:37,05:44:49 +42,M,02:15:28,05:44:50 +40,M,02:32:35,05:44:50 +38,M,02:32:06,05:44:50 +28,W,02:26:31,05:44:52 +37,W,02:20:12,05:44:52 +60,W,02:26:21,05:44:54 +50,W,02:20:37,05:44:54 +31,M,02:24:44,05:44:54 +40,M,02:18:28,05:44:54 +32,W,02:17:48,05:44:54 +56,M,02:21:49,05:44:55 +35,M,02:18:23,05:44:55 +44,M,02:18:09,05:44:55 +44,W,02:25:58,05:44:55 +26,M,02:08:03,05:44:56 +53,W,02:20:39,05:44:57 +49,M,02:17:16,05:44:57 +22,M,02:17:51,05:44:58 +62,M,02:23:27,05:44:58 +62,W,02:44:51,05:44:58 +56,M,02:22:55,05:44:59 +39,M,02:12:46,05:44:59 +26,W,02:24:48,05:45:00 +44,M,02:19:05,05:45:00 +50,M,02:14:22,05:45:01 +38,M,02:16:04,05:45:01 +52,M,02:13:46,05:45:01 +37,M,02:10:43,05:45:02 +28,W,02:29:27,05:45:02 +47,W,02:13:36,05:45:02 +53,M,02:22:36,05:45:03 +24,W,02:27:53,05:45:03 +28,M,02:23:25,05:45:04 +36,W,02:24:41,05:45:04 +30,W,02:23:42,05:45:05 +57,M,02:21:11,05:45:05 +40,W,02:14:34,05:45:05 +38,M,02:35:52,05:45:05 +39,W,02:20:19,05:45:06 +29,M,02:27:35,05:45:06 +38,M,02:17:09,05:45:07 +44,M,02:13:19,05:45:08 +53,W,02:23:19,05:45:08 +27,M,02:09:03,05:45:09 +31,W,02:25:44,05:45:10 +36,M,02:27:42,05:45:10 +28,W,02:23:57,05:45:11 +38,M,02:16:56,05:45:11 +37,M,02:22:03,05:45:12 +52,M,02:28:13,05:45:12 +48,M,02:16:12,05:45:13 +38,M,02:40:43,05:45:14 +54,M,02:38:03,05:45:14 +58,M,02:13:58,05:45:14 +27,W,02:23:56,05:45:14 +54,M,02:22:43,05:45:15 +45,W,02:22:12,05:45:15 +40,M,02:45:25,05:45:16 +59,M,02:32:33,05:45:16 +48,M,02:19:23,05:45:16 +56,M,02:05:26,05:45:16 +54,W,02:09:54,05:45:16 +28,W,02:23:16,05:45:16 +25,W,02:18:27,05:45:17 +40,M,02:21:11,05:45:17 +37,M,02:27:06,05:45:18 +29,W,02:25:53,05:45:19 +65,M,02:21:49,05:45:19 +41,M,02:21:05,05:45:19 +59,M,02:20:54,05:45:19 +33,M,02:21:00,05:45:22 +33,W,02:27:03,05:45:22 +39,W,02:18:28,05:45:23 +47,M,02:20:19,05:45:23 +40,M,02:16:15,05:45:23 +32,W,02:23:31,05:45:23 +63,M,02:29:32,05:45:23 +36,M,02:08:33,05:45:24 +34,M,02:13:13,05:45:24 +44,M,02:26:13,05:45:25 +56,W,02:22:32,05:45:25 +45,M,02:10:39,05:45:25 +53,M,02:26:09,05:45:25 +37,W,02:20:45,05:45:26 +40,M,02:21:00,05:45:26 +35,W,02:23:43,05:45:27 +30,M,02:23:20,05:45:27 +54,W,02:14:11,05:45:27 +61,M,02:25:54,05:45:27 +55,W,02:26:25,05:45:27 +57,W,02:25:43,05:45:28 +53,M,02:01:01,05:45:29 +50,W,02:28:19,05:45:29 +62,M,02:20:52,05:45:30 +58,M,02:17:31,05:45:31 +57,M,02:22:43,05:45:31 +38,M,02:27:01,05:45:32 +56,M,02:16:25,05:45:33 +37,M,02:23:24,05:45:33 +49,M,02:03:20,05:45:33 +31,W,02:18:33,05:45:33 +53,W,02:09:36,05:45:34 +45,M,02:15:43,05:45:34 +43,W,02:26:55,05:45:34 +37,M,02:16:37,05:45:35 +50,M,02:20:09,05:45:35 +54,W,02:34:39,05:45:36 +53,M,02:19:48,05:45:36 +26,M,02:16:37,05:45:37 +33,M,02:29:21,05:45:37 +23,M,02:17:19,05:45:38 +63,M,02:20:39,05:45:38 +36,M,02:17:34,05:45:38 +39,W,02:20:42,05:45:39 +52,M,02:16:37,05:45:39 +38,W,02:26:24,05:45:40 +41,W,02:03:23,05:45:40 +42,M,02:21:44,05:45:41 +44,M,02:16:22,05:45:43 +49,W,02:23:18,05:45:43 +44,W,02:30:58,05:45:43 +55,M,02:26:33,05:45:43 +43,M,02:30:01,05:45:43 +43,W,02:26:00,05:45:44 +46,M,02:15:40,05:45:44 +41,M,02:17:34,05:45:44 +35,W,02:25:35,05:45:44 +45,M,02:28:30,05:45:44 +28,M,02:35:29,05:45:45 +29,W,02:25:10,05:45:45 +50,M,02:16:26,05:45:46 +24,M,02:13:07,05:45:47 +45,M,02:31:13,05:45:47 +20,M,01:33:35,05:45:47 +46,W,02:37:49,05:45:48 +44,W,02:15:52,05:45:49 +35,M,02:21:25,05:45:49 +27,W,02:32:49,05:45:49 +32,W,02:29:21,05:45:50 +42,W,02:29:57,05:45:51 +56,M,02:21:09,05:45:51 +53,M,02:26:15,05:45:52 +60,M,02:19:08,05:45:52 +54,M,02:23:02,05:45:52 +41,M,02:25:18,05:45:52 +49,M,02:12:06,05:45:53 +40,W,02:15:59,05:45:53 +28,W,02:28:13,05:45:53 +42,W,02:24:41,05:45:53 +36,M,02:24:26,05:45:54 +40,W,02:23:21,05:45:55 +50,M,02:24:21,05:45:55 +37,M,02:19:22,05:45:55 +35,W,02:21:41,05:45:56 +54,W,02:21:34,05:45:56 +26,M,02:30:35,05:45:57 +40,W,02:18:38,05:45:58 +35,W,02:32:00,05:46:00 +59,M,02:21:06,05:46:02 +35,M,02:19:44,05:46:02 +62,M,02:22:50,05:46:03 +26,M,02:20:16,05:46:04 +47,M,02:27:00,05:46:05 +36,W,02:22:26,05:46:05 +61,M,02:13:47,05:46:06 +24,W,02:26:20,05:46:06 +23,W,02:08:17,05:46:06 +25,W,02:23:46,05:46:06 +33,M,02:10:02,05:46:06 +55,M,02:15:10,05:46:08 +28,W,02:20:33,05:46:08 +38,W,02:22:43,05:46:08 +53,M,02:19:33,05:46:09 +49,M,02:23:11,05:46:09 +36,M,02:24:22,05:46:09 +24,W,02:16:00,05:46:09 +39,W,02:26:46,05:46:10 +32,M,02:19:20,05:46:10 +49,M,02:19:02,05:46:10 +34,M,02:19:59,05:46:11 +38,W,02:23:02,05:46:11 +59,M,02:33:49,05:46:12 +28,M,02:12:52,05:46:12 +50,M,02:36:16,05:46:12 +50,M,02:25:24,05:46:14 +31,W,02:18:35,05:46:14 +32,W,02:22:01,05:46:14 +42,M,02:12:16,05:46:15 +23,W,02:27:31,05:46:16 +62,W,02:28:51,05:46:16 +63,M,02:24:46,05:46:16 +50,W,02:29:04,05:46:17 +28,W,02:29:39,05:46:17 +41,M,02:18:36,05:46:17 +54,M,02:19:34,05:46:18 +41,W,02:21:13,05:46:18 +52,W,02:23:52,05:46:18 +41,W,02:28:41,05:46:18 +28,M,02:17:24,05:46:18 +30,W,02:22:15,05:46:19 +48,M,02:22:24,05:46:19 +33,W,02:16:02,05:46:19 +42,W,02:30:40,05:46:20 +44,W,02:25:43,05:46:21 +36,M,02:12:31,05:46:21 +33,W,02:25:01,05:46:21 +49,W,02:27:39,05:46:21 +38,W,02:28:26,05:46:22 +24,M,02:21:13,05:46:22 +32,W,02:30:26,05:46:23 +35,W,02:25:34,05:46:23 +38,W,02:24:01,05:46:23 +39,M,02:29:54,05:46:23 +32,M,02:30:32,05:46:23 +24,W,02:13:44,05:46:23 +49,M,02:09:36,05:46:23 +36,W,02:22:54,05:46:23 +30,W,02:22:46,05:46:23 +29,M,02:20:39,05:46:25 +45,W,02:17:15,05:46:26 +41,M,02:16:37,05:46:26 +45,W,02:28:54,05:46:26 +38,M,02:20:11,05:46:26 +33,W,02:25:27,05:46:27 +49,M,02:25:50,05:46:27 +46,M,02:22:59,05:46:27 +63,W,02:25:59,05:46:28 +51,M,02:20:08,05:46:29 +57,M,02:23:04,05:46:29 +40,W,02:31:18,05:46:30 +47,M,02:22:45,05:46:30 +51,M,02:36:30,05:46:30 +58,M,02:24:24,05:46:30 +40,W,02:21:53,05:46:31 +48,M,02:32:48,05:46:31 +29,W,02:30:22,05:46:31 +62,M,02:29:54,05:46:32 +46,W,02:17:23,05:46:32 +49,M,02:25:54,05:46:32 +48,M,02:17:00,05:46:33 +58,W,02:27:29,05:46:34 +18,W,02:19:47,05:46:34 +41,M,02:17:43,05:46:34 +60,M,02:33:42,05:46:35 +59,M,02:27:27,05:46:35 +48,M,02:14:17,05:46:37 +46,W,02:24:13,05:46:37 +29,W,02:20:55,05:46:38 +53,M,02:19:43,05:46:38 +61,M,02:15:07,05:46:38 +29,W,02:30:41,05:46:39 +45,W,02:17:02,05:46:39 +41,W,02:39:30,05:46:39 +48,M,02:38:12,05:46:40 +57,W,02:27:38,05:46:40 +32,W,02:27:17,05:46:40 +28,M,02:25:33,05:46:40 +50,M,02:23:43,05:46:40 +50,M,02:14:38,05:46:40 +61,W,02:31:08,05:46:41 +32,W,02:26:02,05:46:42 +23,M,02:16:10,05:46:43 +44,W,02:36:37,05:46:45 +69,M,02:32:02,05:46:45 +33,M,02:21:49,05:46:47 +29,W,02:22:42,05:46:47 +58,M,02:33:23,05:46:47 +41,W,02:32:14,05:46:49 +40,W,02:26:58,05:46:49 +39,W,02:19:12,05:46:49 +63,M,02:34:48,05:46:50 +29,W,02:33:16,05:46:51 +26,W,02:33:09,05:46:51 +37,W,02:30:19,05:46:52 +51,M,02:27:52,05:46:52 +49,W,02:24:34,05:46:52 +55,W,02:25:56,05:46:53 +60,M,02:34:39,05:46:55 +29,M,02:29:49,05:46:55 +54,W,02:33:36,05:46:56 +44,W,02:22:06,05:46:57 +39,W,02:25:32,05:46:57 +57,M,02:21:19,05:46:58 +35,W,02:11:31,05:46:58 +41,W,02:23:44,05:46:58 +33,W,02:28:31,05:46:58 +33,W,02:20:47,05:46:59 +57,M,02:31:01,05:47:00 +30,W,02:23:58,05:47:00 +61,M,02:09:27,05:47:00 +31,W,02:15:25,05:47:00 +27,W,02:17:22,05:47:01 +51,W,02:22:28,05:47:01 +52,M,02:15:51,05:47:01 +43,M,02:07:34,05:47:02 +64,M,02:20:57,05:47:02 +45,W,02:30:03,05:47:03 +57,M,02:23:49,05:47:03 +32,W,02:23:10,05:47:03 +26,W,02:36:30,05:47:04 +35,W,02:21:14,05:47:04 +32,M,02:23:28,05:47:04 +41,W,02:23:21,05:47:05 +23,W,02:21:23,05:47:05 +54,W,02:23:13,05:47:05 +50,M,02:28:51,05:47:06 +43,W,02:23:23,05:47:06 +27,M,02:19:55,05:47:07 +38,W,02:27:51,05:47:07 +37,W,02:27:39,05:47:08 +45,W,02:20:54,05:47:08 +52,M,02:21:54,05:47:08 +35,W,02:26:21,05:47:09 +42,W,02:29:44,05:47:09 +40,W,02:24:37,05:47:09 +35,M,02:21:52,05:47:10 +47,M,02:18:17,05:47:13 +19,W,02:18:26,05:47:13 +41,W,02:18:26,05:47:14 +60,M,02:19:20,05:47:14 +37,M,02:18:05,05:47:15 +34,W,02:19:34,05:47:17 +56,W,02:30:09,05:47:17 +57,W,02:32:03,05:47:18 +41,W,02:17:08,05:47:18 +57,M,02:24:59,05:47:20 +60,M,02:24:04,05:47:21 +61,M,02:27:53,05:47:21 +33,M,02:12:19,05:47:21 +26,W,02:23:42,05:47:21 +47,M,02:32:49,05:47:22 +27,M,02:26:11,05:47:22 +36,M,02:18:16,05:47:22 +42,W,02:30:45,05:47:24 +40,W,02:29:00,05:47:24 +38,M,02:19:25,05:47:24 +36,M,02:10:29,05:47:24 +46,W,02:16:53,05:47:25 +57,M,02:16:25,05:47:25 +44,M,02:24:14,05:47:25 +52,M,02:10:26,05:47:26 +30,W,02:32:43,05:47:27 +52,M,02:26:24,05:47:27 +52,W,02:31:40,05:47:28 +61,M,02:29:01,05:47:28 +46,W,02:22:09,05:47:28 +37,W,02:24:08,05:47:29 +40,W,02:24:09,05:47:30 +37,W,02:27:06,05:47:30 +43,M,02:17:14,05:47:31 +37,W,02:23:41,05:47:32 +45,M,02:21:47,05:47:32 +24,W,02:36:19,05:47:32 +51,W,02:25:05,05:47:33 +57,M,02:30:51,05:47:35 +40,W,02:26:38,05:47:35 +60,M,02:34:38,05:47:35 +42,M,02:09:26,05:47:37 +53,M,02:27:51,05:47:38 +37,M,02:19:58,05:47:39 +44,W,02:24:04,05:47:40 +47,W,02:23:34,05:47:41 +34,W,02:10:01,05:47:41 +57,W,02:26:59,05:47:41 +44,M,02:30:41,05:47:42 +58,M,02:10:07,05:47:42 +48,M,02:35:52,05:47:44 +60,M,02:24:18,05:47:44 +51,M,02:13:42,05:47:45 +31,W,02:31:22,05:47:45 +50,M,02:25:08,05:47:46 +62,M,02:27:16,05:47:46 +31,W,02:32:50,05:47:46 +20,M,02:05:33,05:47:46 +55,M,02:24:57,05:47:46 +37,W,02:24:41,05:47:46 +41,W,02:23:57,05:47:47 +48,M,02:27:52,05:47:49 +37,W,02:28:00,05:47:49 +27,M,02:25:21,05:47:50 +28,W,02:19:00,05:47:50 +36,W,02:27:07,05:47:50 +61,M,02:25:41,05:47:51 +63,M,02:21:46,05:47:51 +51,M,02:22:56,05:47:52 +56,W,02:24:54,05:47:52 +36,M,02:05:22,05:47:53 +49,W,02:19:36,05:47:53 +29,M,02:23:57,05:47:54 +24,M,02:06:04,05:47:54 +39,W,02:32:41,05:47:54 +50,M,02:12:24,05:47:54 +31,W,02:27:19,05:47:56 +41,W,02:15:42,05:47:57 +42,M,02:27:47,05:47:57 +34,M,02:27:57,05:47:58 +34,W,02:29:08,05:47:58 +39,M,02:20:21,05:47:58 +37,W,02:24:03,05:47:58 +53,W,02:23:03,05:47:59 +40,W,02:19:53,05:47:59 +50,W,02:24:56,05:48:00 +34,W,02:33:07,05:48:00 +64,M,02:14:14,05:48:00 +26,M,02:24:46,05:48:00 +35,W,02:19:19,05:48:01 +59,M,02:20:38,05:48:01 +49,M,02:21:31,05:48:02 +43,W,02:22:39,05:48:02 +37,M,02:19:12,05:48:03 +45,M,02:23:06,05:48:04 +43,W,02:27:37,05:48:04 +48,M,02:13:55,05:48:04 +70,M,02:21:59,05:48:04 +45,W,02:25:36,05:48:04 +35,M,02:20:03,05:48:05 +51,M,02:27:45,05:48:05 +57,M,02:28:22,05:48:06 +35,M,02:17:09,05:48:06 +51,W,02:26:31,05:48:06 +51,M,02:18:52,05:48:06 +49,W,02:25:00,05:48:06 +31,M,02:18:57,05:48:07 +50,M,02:26:17,05:48:07 +48,M,02:18:51,05:48:07 +41,W,02:27:49,05:48:08 +28,W,02:19:58,05:48:08 +33,W,02:29:32,05:48:09 +45,M,02:21:52,05:48:09 +28,W,02:28:17,05:48:09 +23,M,02:26:11,05:48:10 +53,M,02:23:30,05:48:10 +44,M,02:15:49,05:48:12 +40,M,02:09:45,05:48:12 +41,W,02:22:51,05:48:12 +62,M,02:09:16,05:48:14 +35,W,02:30:19,05:48:14 +59,M,02:25:57,05:48:17 +46,W,02:33:03,05:48:17 +48,M,02:36:15,05:48:17 +49,W,02:27:17,05:48:17 +22,W,02:20:08,05:48:17 +39,M,02:13:46,05:48:18 +43,W,02:26:44,05:48:18 +47,M,02:20:36,05:48:19 +28,W,02:08:09,05:48:19 +45,M,02:21:27,05:48:19 +47,W,02:18:05,05:48:20 +42,W,02:37:19,05:48:20 +35,W,02:21:37,05:48:21 +38,M,02:29:22,05:48:21 +50,M,02:22:04,05:48:21 +51,M,02:21:19,05:48:22 +25,W,02:27:37,05:48:22 +58,M,02:26:26,05:48:22 +36,W,02:15:12,05:48:23 +51,M,02:26:56,05:48:24 +25,W,02:15:33,05:48:24 +37,W,02:28:57,05:48:25 +46,M,02:19:39,05:48:25 +62,M,02:23:10,05:48:25 +43,W,02:45:10,05:48:25 +38,W,02:31:25,05:48:25 +36,M,02:25:13,05:48:25 +23,W,02:18:56,05:48:25 +35,M,02:26:05,05:48:25 +47,M,02:21:42,05:48:26 +64,M,02:25:02,05:48:26 +38,M,02:26:49,05:48:26 +43,W,02:22:03,05:48:26 +56,M,02:23:18,05:48:26 +42,M,02:23:47,05:48:26 +24,M,02:20:45,05:48:27 +47,M,02:18:27,05:48:28 +53,M,02:08:55,05:48:28 +27,M,02:14:25,05:48:28 +38,M,02:26:17,05:48:30 +27,W,02:25:12,05:48:30 +64,W,02:22:24,05:48:30 +38,W,02:23:12,05:48:30 +34,W,02:25:31,05:48:30 +43,W,02:34:07,05:48:31 +52,M,02:20:34,05:48:31 +42,W,02:30:34,05:48:31 +47,W,02:24:23,05:48:31 +51,W,02:26:48,05:48:33 +62,M,02:26:12,05:48:33 +52,M,02:24:38,05:48:33 +22,W,02:10:33,05:48:34 +34,W,02:24:14,05:48:34 +31,M,02:18:11,05:48:35 +52,M,02:24:32,05:48:35 +46,W,02:26:05,05:48:35 +33,W,02:17:55,05:48:36 +48,M,02:16:22,05:48:36 +35,W,02:20:10,05:48:36 +38,W,02:28:39,05:48:37 +49,M,02:12:33,05:48:37 +22,W,02:19:00,05:48:37 +45,W,02:26:23,05:48:38 +49,M,02:22:41,05:48:38 +44,M,02:07:22,05:48:39 +43,M,02:29:53,05:48:40 +30,M,02:33:32,05:48:41 +43,M,02:13:10,05:48:41 +46,M,02:27:55,05:48:41 +52,W,02:35:07,05:48:41 +50,M,02:29:11,05:48:42 +26,W,02:29:32,05:48:43 +32,M,02:24:06,05:48:43 +46,W,02:28:05,05:48:43 +39,W,02:19:55,05:48:43 +30,W,02:19:30,05:48:44 +27,W,02:20:38,05:48:44 +28,W,02:25:09,05:48:44 +37,W,02:17:12,05:48:45 +57,M,02:24:37,05:48:46 +51,M,02:28:05,05:48:46 +33,M,02:32:00,05:48:46 +32,M,02:27:56,05:48:47 +35,M,02:26:43,05:48:48 +41,W,02:22:22,05:48:48 +38,M,02:12:55,05:48:49 +38,M,02:12:40,05:48:50 +19,W,02:22:11,05:48:51 +51,M,02:15:52,05:48:51 +53,M,02:21:56,05:48:52 +37,M,02:14:09,05:48:52 +37,W,02:23:40,05:48:52 +34,M,02:28:13,05:48:52 +38,W,02:24:46,05:48:52 +42,M,02:16:26,05:48:53 +51,M,02:12:12,05:48:54 +47,M,02:14:45,05:48:54 +34,M,02:31:05,05:48:54 +31,W,02:20:10,05:48:54 +52,W,02:27:21,05:48:54 +38,M,02:36:40,05:48:54 +45,W,02:35:18,05:48:54 +52,W,02:28:24,05:48:54 +53,W,02:19:06,05:48:55 +26,W,02:18:34,05:48:55 +61,M,02:32:03,05:48:55 +37,W,02:21:37,05:48:55 +64,W,02:31:19,05:48:56 +49,W,02:45:51,05:48:57 +38,W,02:23:46,05:48:57 +37,W,02:15:55,05:48:57 +26,W,02:32:25,05:48:57 +26,W,02:36:02,05:48:57 +35,M,02:28:34,05:48:57 +53,M,02:20:01,05:48:58 +36,M,02:29:22,05:48:58 +31,W,02:27:46,05:48:58 +49,W,02:28:59,05:48:59 +52,W,02:34:11,05:49:00 +48,W,02:35:51,05:49:01 +50,M,02:20:17,05:49:02 +41,W,02:27:20,05:49:02 +41,W,02:23:20,05:49:03 +39,W,02:19:21,05:49:03 +47,W,02:25:36,05:49:04 +48,M,02:17:28,05:49:04 +50,M,02:26:39,05:49:05 +24,M,02:27:47,05:49:06 +58,W,02:17:09,05:49:06 +51,M,02:21:37,05:49:07 +39,W,02:35:02,05:49:07 +56,M,02:22:49,05:49:09 +31,W,02:28:03,05:49:09 +48,W,02:23:04,05:49:10 +57,M,02:18:59,05:49:10 +32,W,02:30:41,05:49:11 +37,M,02:16:46,05:49:12 +45,M,02:15:30,05:49:13 +33,M,02:20:33,05:49:13 +41,W,02:12:34,05:49:13 +57,M,02:36:16,05:49:14 +54,M,02:26:50,05:49:14 +41,M,02:20:18,05:49:15 +43,M,02:11:03,05:49:15 +35,M,02:26:11,05:49:16 +31,M,02:03:03,05:49:16 +40,W,02:25:22,05:49:16 +49,W,02:25:27,05:49:16 +38,W,02:16:40,05:49:16 +37,M,02:21:56,05:49:17 +37,M,02:27:45,05:49:17 +36,M,02:22:42,05:49:17 +38,M,02:31:27,05:49:18 +38,M,02:15:39,05:49:18 +36,W,02:20:54,05:49:21 +34,W,02:20:27,05:49:22 +37,W,02:32:20,05:49:22 +42,M,02:05:33,05:49:23 +47,M,02:27:35,05:49:23 +20,M,02:19:55,05:49:24 +35,W,02:20:28,05:49:24 +53,M,02:19:41,05:49:25 +24,M,02:15:08,05:49:25 +34,M,02:17:07,05:49:25 +43,W,02:22:16,05:49:26 +51,W,02:30:10,05:49:26 +29,W,02:16:07,05:49:26 +27,W,02:32:22,05:49:27 +67,W,02:25:27,05:49:27 +40,M,02:21:40,05:49:27 +32,M,02:25:27,05:49:27 +25,W,02:18:58,05:49:27 +32,M,02:08:03,05:49:28 +40,W,02:46:11,05:49:28 +36,M,02:11:11,05:49:29 +34,W,02:18:25,05:49:29 +38,W,02:07:38,05:49:30 +54,W,02:29:02,05:49:30 +38,W,02:19:28,05:49:30 +26,M,02:23:50,05:49:30 +46,M,02:23:16,05:49:30 +61,M,02:29:56,05:49:31 +34,M,02:09:28,05:49:31 +51,M,02:21:53,05:49:32 +44,M,02:20:22,05:49:33 +49,M,02:31:15,05:49:33 +30,M,02:18:05,05:49:34 +24,M,02:27:22,05:49:34 +43,W,02:26:52,05:49:34 +45,M,02:13:43,05:49:35 +45,W,02:32:58,05:49:35 +48,W,02:27:45,05:49:37 +35,W,02:21:00,05:49:38 +42,M,02:29:52,05:49:39 +27,M,02:26:29,05:49:40 +35,W,02:28:39,05:49:41 +54,W,02:33:13,05:49:42 +44,W,02:21:29,05:49:43 +25,M,02:15:46,05:49:43 +41,W,02:30:00,05:49:43 +51,M,02:35:04,05:49:43 +44,M,02:20:22,05:49:44 +35,M,02:14:37,05:49:44 +29,W,02:22:26,05:49:44 +40,W,02:23:05,05:49:45 +29,W,02:34:12,05:49:45 +50,M,02:11:23,05:49:46 +31,W,02:27:50,05:49:46 +54,M,02:32:25,05:49:46 +34,W,02:15:19,05:49:47 +32,W,02:30:40,05:49:47 +28,W,02:24:48,05:49:48 +51,W,02:46:57,05:49:49 +24,W,02:24:30,05:49:50 +51,M,02:18:28,05:49:50 +36,W,02:29:00,05:49:50 +48,M,02:17:46,05:49:52 +55,M,02:17:23,05:49:53 +40,W,02:33:32,05:49:54 +75,M,02:27:19,05:49:54 +59,M,02:35:16,05:49:54 +24,M,02:20:14,05:49:55 +41,W,02:35:58,05:49:55 +27,M,02:08:18,05:49:55 +27,W,02:20:26,05:49:56 +43,W,02:23:23,05:49:56 +36,M,02:07:44,05:49:56 +56,M,02:34:42,05:49:57 +58,M,02:02:55,05:49:57 +24,M,02:42:31,05:49:58 +40,M,02:20:07,05:49:58 +44,M,02:34:23,05:50:01 +53,M,02:12:31,05:50:02 +43,M,02:14:10,05:50:03 +25,M,02:26:10,05:50:03 +48,W,02:23:50,05:50:03 +51,M,02:15:35,05:50:04 +47,M,02:15:57,05:50:04 +52,M,02:15:02,05:50:04 +43,M,02:33:50,05:50:05 +65,M,02:20:57,05:50:05 +44,M,02:29:40,05:50:05 +54,M,02:18:08,05:50:05 +27,M,02:17:49,05:50:05 +60,M,02:24:17,05:50:06 +24,W,02:17:02,05:50:06 +45,M,02:17:35,05:50:07 +57,M,02:28:39,05:50:08 +38,M,02:12:08,05:50:08 +46,W,02:23:08,05:50:08 +55,W,02:25:09,05:50:08 +39,W,02:27:02,05:50:09 +45,M,02:21:33,05:50:10 +52,M,02:26:43,05:50:10 +27,W,02:28:04,05:50:10 +37,M,02:29:04,05:50:10 +34,W,02:21:17,05:50:11 +34,W,02:45:25,05:50:12 +40,W,02:19:52,05:50:12 +39,W,02:41:23,05:50:13 +41,M,02:18:34,05:50:13 +41,W,02:28:39,05:50:14 +45,W,02:28:53,05:50:14 +45,M,02:19:50,05:50:14 +28,W,02:20:49,05:50:15 +28,W,02:25:04,05:50:16 +57,W,02:28:07,05:50:16 +44,M,02:23:27,05:50:16 +32,W,02:26:40,05:50:16 +45,M,02:23:25,05:50:17 +55,M,02:16:09,05:50:17 +45,W,02:34:23,05:50:18 +47,W,02:26:13,05:50:19 +52,M,02:30:38,05:50:20 +45,W,02:23:38,05:50:20 +32,W,02:05:25,05:50:21 +37,M,02:31:17,05:50:21 +51,M,02:22:56,05:50:21 +66,M,02:14:44,05:50:22 +57,M,02:27:57,05:50:22 +41,W,02:20:17,05:50:22 +38,W,02:29:56,05:50:22 +53,M,02:31:29,05:50:22 +46,M,02:19:44,05:50:22 +40,W,02:18:32,05:50:22 +55,W,02:40:11,05:50:23 +66,W,02:31:11,05:50:23 +47,M,02:32:32,05:50:26 +24,W,02:31:24,05:50:26 +45,M,02:20:44,05:50:27 +28,W,02:29:49,05:50:27 +24,W,02:26:46,05:50:28 +48,M,02:33:18,05:50:29 +44,W,02:22:44,05:50:29 +49,W,02:22:04,05:50:29 +39,M,02:17:05,05:50:30 +45,W,02:15:27,05:50:30 +53,M,02:29:35,05:50:30 +24,M,02:24:01,05:50:31 +51,M,02:18:19,05:50:33 +49,W,02:19:24,05:50:34 +39,W,02:15:03,05:50:35 +35,W,02:23:19,05:50:35 +53,M,02:22:03,05:50:36 +42,M,02:19:48,05:50:36 +43,W,02:34:39,05:50:36 +35,W,02:26:30,05:50:37 +30,M,02:17:35,05:50:37 +28,M,02:21:11,05:50:37 +46,M,02:22:17,05:50:37 +33,M,02:25:28,05:50:38 +53,M,02:18:41,05:50:39 +23,W,02:04:07,05:50:39 +41,W,02:33:38,05:50:39 +34,W,02:40:46,05:50:39 +45,W,02:13:16,05:50:42 +34,M,02:26:29,05:50:42 +38,W,02:20:08,05:50:42 +54,M,02:26:47,05:50:43 +26,M,02:24:45,05:50:43 +44,M,02:16:35,05:50:43 +35,W,02:25:19,05:50:44 +39,W,02:18:42,05:50:44 +45,M,02:31:49,05:50:44 +49,M,02:13:16,05:50:45 +60,M,02:11:10,05:50:45 +40,M,02:08:58,05:50:45 +38,W,02:19:01,05:50:45 +37,W,02:14:30,05:50:45 +28,W,02:40:28,05:50:45 +49,W,02:27:37,05:50:47 +44,W,02:23:31,05:50:47 +50,W,02:31:59,05:50:47 +42,W,02:19:59,05:50:48 +48,M,02:31:22,05:50:50 +46,W,02:39:14,05:50:50 +41,M,02:27:55,05:50:51 +56,W,02:51:35,05:50:51 +57,M,02:17:12,05:50:52 +47,M,02:19:35,05:50:53 +50,M,02:48:44,05:50:53 +51,M,02:18:18,05:50:53 +47,W,02:31:12,05:50:54 +39,M,02:18:23,05:50:54 +55,M,02:25:35,05:50:55 +41,W,02:31:49,05:50:55 +23,M,02:28:15,05:50:56 +51,W,02:14:31,05:50:56 +40,W,02:32:52,05:50:58 +27,W,02:21:24,05:50:58 +79,M,02:52:52,05:50:58 +55,W,02:28:48,05:50:59 +29,M,02:30:49,05:50:59 +54,M,02:22:53,05:50:59 +34,M,02:20:14,05:50:59 +45,W,02:15:02,05:51:00 +67,M,02:33:04,05:51:00 +50,M,02:30:12,05:51:00 +46,M,02:23:25,05:51:01 +71,M,02:20:47,05:51:02 +43,W,02:27:13,05:51:02 +36,W,02:31:17,05:51:04 +67,M,02:40:00,05:51:05 +64,M,02:10:22,05:51:05 +44,W,02:35:10,05:51:05 +53,W,03:06:08,05:51:06 +49,W,02:23:16,05:51:06 +55,W,02:29:16,05:51:06 +50,M,02:27:42,05:51:06 +33,W,02:27:02,05:51:09 +30,M,02:29:57,05:51:10 +32,W,02:21:37,05:51:11 +46,W,02:22:37,05:51:12 +48,M,02:21:46,05:51:12 +38,M,02:28:58,05:51:12 +39,M,02:14:50,05:51:12 +23,W,02:27:15,05:51:13 +41,M,02:25:45,05:51:14 +56,M,02:20:35,05:51:14 +44,W,02:27:43,05:51:15 +31,W,02:12:32,05:51:15 +36,W,02:30:11,05:51:16 +42,W,02:27:00,05:51:17 +26,M,02:05:48,05:51:18 +43,W,02:16:23,05:51:18 +62,W,02:19:41,05:51:19 +54,M,02:19:34,05:51:19 +48,M,02:21:56,05:51:21 +51,M,02:07:46,05:51:21 +42,M,02:14:45,05:51:22 +41,M,02:20:54,05:51:22 +27,W,02:29:00,05:51:23 +47,M,02:13:41,05:51:24 +52,M,02:17:17,05:51:24 +37,M,02:21:22,05:51:25 +47,W,02:30:22,05:51:25 +47,W,02:11:47,05:51:25 +44,M,02:26:03,05:51:26 +36,M,02:26:06,05:51:27 +77,M,02:40:11,05:51:27 +49,W,02:20:33,05:51:27 +52,M,02:21:02,05:51:27 +45,M,02:16:52,05:51:27 +40,M,02:15:17,05:51:28 +26,W,02:28:17,05:51:29 +34,W,02:18:46,05:51:29 +39,W,02:27:15,05:51:30 +52,M,02:24:22,05:51:31 +49,M,02:28:16,05:51:33 +28,W,02:27:30,05:51:34 +38,M,02:13:30,05:51:34 +56,M,02:33:47,05:51:35 +50,M,02:29:54,05:51:35 +48,W,02:21:16,05:51:35 +40,W,02:18:19,05:51:36 +58,W,02:25:06,05:51:37 +42,W,02:09:25,05:51:38 +56,W,02:18:57,05:51:39 +45,M,02:36:30,05:51:39 +46,W,02:21:50,05:51:40 +42,M,02:26:30,05:51:40 +32,W,02:33:52,05:51:40 +50,M,02:23:38,05:51:41 +33,W,02:29:54,05:51:41 +36,W,02:26:41,05:51:41 +54,W,02:18:03,05:51:41 +33,M,02:20:30,05:51:42 +52,W,02:32:26,05:51:43 +39,M,02:26:14,05:51:43 +66,M,02:21:23,05:51:44 +27,M,02:17:12,05:51:44 +40,M,02:15:35,05:51:44 +47,M,02:25:31,05:51:44 +62,M,02:19:44,05:51:45 +50,M,02:24:30,05:51:47 +36,M,02:19:49,05:51:48 +44,W,02:01:04,05:51:49 +54,M,02:11:32,05:51:49 +34,W,02:37:50,05:51:49 +56,M,02:20:27,05:51:49 +49,W,02:19:53,05:51:50 +43,W,02:25:37,05:51:51 +37,M,02:03:11,05:51:51 +48,W,02:33:32,05:51:51 +30,W,02:23:25,05:51:52 +61,M,02:23:41,05:51:53 +48,M,02:27:43,05:51:53 +50,W,02:37:51,05:51:53 +46,M,02:29:16,05:51:53 +26,W,02:27:01,05:51:54 +39,M,02:29:26,05:51:54 +51,W,02:32:45,05:51:55 +61,W,02:35:29,05:51:56 +62,M,02:27:09,05:51:57 +23,M,02:25:48,05:51:58 +37,M,02:29:59,05:51:58 +49,W,02:36:28,05:51:59 +45,M,02:15:02,05:51:59 +38,W,02:20:55,05:51:59 +32,W,02:24:47,05:52:00 +34,M,02:23:41,05:52:00 +43,W,02:31:44,05:52:00 +61,M,02:12:42,05:52:01 +23,W,02:27:58,05:52:01 +38,M,02:06:18,05:52:01 +53,M,02:14:41,05:52:02 +44,W,02:30:49,05:52:02 +19,M,02:22:17,05:52:03 +50,M,02:24:59,05:52:04 +32,W,02:31:25,05:52:04 +35,M,02:04:09,05:52:06 +51,M,02:20:00,05:52:08 +41,W,02:30:27,05:52:09 +22,M,02:23:18,05:52:09 +40,M,02:28:58,05:52:10 +53,W,02:24:15,05:52:11 +36,M,02:15:35,05:52:11 +40,M,02:33:52,05:52:12 +24,M,02:19:41,05:52:12 +28,M,02:14:05,05:52:13 +29,W,02:17:01,05:52:13 +46,W,02:33:20,05:52:14 +25,W,02:23:40,05:52:14 +28,W,02:16:02,05:52:15 +41,M,02:13:43,05:52:15 +60,W,02:25:58,05:52:15 +42,W,02:34:17,05:52:16 +51,M,02:13:15,05:52:16 +30,M,02:13:25,05:52:16 +25,W,02:37:16,05:52:16 +57,M,02:53:08,05:52:17 +54,M,02:18:41,05:52:19 +60,W,02:23:01,05:52:20 +37,W,02:16:21,05:52:20 +48,W,02:28:47,05:52:20 +51,M,02:19:38,05:52:21 +46,W,02:33:12,05:52:23 +52,W,02:29:28,05:52:25 +38,W,02:20:42,05:52:25 +21,W,02:31:17,05:52:25 +35,W,02:29:59,05:52:26 +33,W,02:18:21,05:52:26 +47,M,02:26:23,05:52:26 +37,W,02:23:37,05:52:27 +35,M,02:28:18,05:52:27 +31,W,02:33:39,05:52:28 +46,W,02:24:34,05:52:28 +33,M,02:24:10,05:52:28 +33,W,02:29:51,05:52:29 +35,W,02:37:21,05:52:29 +44,W,02:27:50,05:52:30 +47,M,02:26:48,05:52:30 +25,M,02:29:18,05:52:30 +30,M,02:23:07,05:52:31 +38,W,02:23:46,05:52:31 +51,W,02:34:07,05:52:33 +54,W,02:17:38,05:52:34 +51,W,02:30:15,05:52:34 +40,W,02:26:24,05:52:35 +44,M,02:23:03,05:52:35 +31,M,02:30:07,05:52:35 +41,W,02:23:47,05:52:35 +32,W,02:12:26,05:52:39 +32,W,02:20:58,05:52:39 +52,W,02:22:10,05:52:39 +40,M,02:30:21,05:52:41 +49,M,02:23:32,05:52:41 +24,M,02:28:59,05:52:43 +29,W,02:19:18,05:52:44 +42,W,02:18:11,05:52:44 +65,M,02:31:25,05:52:44 +27,M,02:19:35,05:52:45 +39,W,02:32:34,05:52:45 +34,M,02:17:17,05:52:46 +51,M,02:26:21,05:52:46 +30,M,02:29:03,05:52:47 +33,M,02:15:42,05:52:47 +48,M,02:38:55,05:52:47 +29,M,02:49:29,05:52:47 +28,W,02:23:27,05:52:49 +52,W,02:23:47,05:52:50 +55,M,02:18:50,05:52:51 +46,W,02:22:32,05:52:53 +37,M,02:21:00,05:52:54 +45,M,02:45:04,05:52:54 +44,W,02:24:38,05:52:56 +33,M,02:18:58,05:52:56 +27,M,02:20:30,05:52:57 +37,W,02:30:22,05:52:58 +47,M,02:34:40,05:52:58 +31,M,02:15:58,05:52:58 +48,M,02:28:45,05:52:58 +55,W,02:24:57,05:52:59 +27,W,02:19:03,05:52:59 +33,W,02:20:23,05:52:59 +29,M,02:28:02,05:52:59 +35,W,02:38:58,05:52:59 +50,M,02:36:47,05:53:00 +41,W,02:29:34,05:53:00 +43,W,02:26:01,05:53:00 +46,W,02:25:50,05:53:00 +46,M,02:15:11,05:53:01 +51,M,02:30:10,05:53:01 +45,W,02:29:58,05:53:01 +37,W,02:17:10,05:53:01 +28,M,02:24:28,05:53:01 +36,W,02:28:09,05:53:02 +29,W,02:26:37,05:53:02 +30,M,02:25:10,05:53:03 +59,M,02:15:10,05:53:04 +36,M,02:50:58,05:53:04 +41,W,02:36:06,05:53:04 +56,M,02:26:06,05:53:05 +65,M,02:30:19,05:53:05 +38,W,02:27:12,05:53:06 +43,M,02:21:09,05:53:06 +55,M,02:29:24,05:53:07 +50,W,02:26:26,05:53:07 +34,M,02:13:54,05:53:07 +39,W,02:20:27,05:53:08 +71,M,02:22:26,05:53:08 +31,W,02:07:55,05:53:08 +49,M,02:18:25,05:53:08 +38,W,02:26:43,05:53:10 +41,M,02:31:00,05:53:11 +37,W,02:29:12,05:53:11 +48,M,02:27:24,05:53:12 +37,W,02:32:11,05:53:13 +49,M,02:33:23,05:53:14 +58,M,02:20:42,05:53:15 +22,W,02:26:13,05:53:16 +43,M,02:12:24,05:53:16 +38,W,02:30:48,05:53:16 +48,M,02:29:40,05:53:18 +26,W,02:27:12,05:53:19 +30,W,02:18:46,05:53:19 +38,M,02:35:25,05:53:20 +52,M,02:31:16,05:53:21 +44,W,02:23:00,05:53:21 +42,M,02:22:08,05:53:21 +46,W,02:17:23,05:53:22 +49,W,02:25:10,05:53:25 +50,W,02:28:37,05:53:27 +26,W,02:22:03,05:53:28 +44,W,02:16:29,05:53:28 +30,W,02:24:57,05:53:29 +74,M,02:31:21,05:53:30 +71,W,02:22:20,05:53:30 +51,W,02:40:21,05:53:30 +38,M,02:30:03,05:53:30 +37,W,02:16:23,05:53:30 +32,W,02:23:55,05:53:31 +38,M,02:14:51,05:53:34 +37,M,02:23:09,05:53:34 +72,M,02:35:17,05:53:34 +46,W,02:18:02,05:53:34 +56,M,02:35:31,05:53:35 +51,M,02:19:52,05:53:37 +58,M,02:28:22,05:53:37 +50,M,02:12:08,05:53:38 +41,W,02:27:52,05:53:38 +28,W,02:39:57,05:53:38 +52,M,02:26:18,05:53:38 +40,W,02:28:54,05:53:39 +60,M,02:25:10,05:53:39 +38,W,02:40:39,05:53:41 +38,W,02:27:49,05:53:45 +68,M,02:17:13,05:53:45 +27,W,02:18:56,05:53:46 +23,W,02:25:20,05:53:46 +44,W,02:28:57,05:53:46 +37,W,02:30:01,05:53:47 +41,W,02:27:47,05:53:47 +43,M,02:23:34,05:53:47 +31,W,02:25:15,05:53:47 +32,W,02:12:02,05:53:47 +50,W,02:24:06,05:53:48 +41,W,02:25:14,05:53:48 +37,W,02:31:28,05:53:49 +27,M,02:25:31,05:53:50 +25,W,02:21:27,05:53:50 +38,M,02:26:16,05:53:53 +28,W,02:22:54,05:53:53 +35,M,02:25:43,05:53:53 +34,W,02:32:52,05:53:54 +34,M,02:24:36,05:53:54 +20,M,02:17:30,05:53:54 +42,W,02:29:29,05:53:56 +40,M,02:35:17,05:53:57 +50,W,02:27:52,05:53:59 +64,W,02:28:40,05:53:59 +50,W,02:30:45,05:54:00 +52,M,02:12:20,05:54:00 +45,M,02:31:00,05:54:00 +29,W,02:36:58,05:54:02 +43,W,02:28:02,05:54:02 +35,W,02:19:54,05:54:02 +48,M,02:36:58,05:54:03 +62,M,02:22:55,05:54:04 +48,M,02:35:32,05:54:05 +30,M,02:24:19,05:54:05 +52,M,02:25:31,05:54:05 +53,M,02:20:55,05:54:05 +36,M,02:20:45,05:54:06 +38,W,02:38:42,05:54:06 +54,M,02:11:56,05:54:07 +34,W,02:35:08,05:54:07 +51,M,02:12:20,05:54:08 +28,M,02:20:16,05:54:08 +50,W,02:20:00,05:54:08 +28,W,02:29:46,05:54:09 +35,W,02:31:32,05:54:09 +27,W,02:24:20,05:54:09 +47,M,02:31:38,05:54:10 +48,W,02:38:35,05:54:10 +39,M,02:28:26,05:54:11 +29,W,02:27:59,05:54:11 +42,M,02:22:27,05:54:11 +54,M,02:22:19,05:54:12 +54,M,02:34:25,05:54:13 +27,M,02:25:37,05:54:13 +31,M,02:30:12,05:54:13 +42,M,02:24:57,05:54:13 +55,W,02:37:21,05:54:14 +36,M,02:13:44,05:54:14 +28,W,02:25:12,05:54:15 +32,M,02:31:14,05:54:15 +41,W,02:29:34,05:54:15 +28,M,02:24:52,05:54:15 +30,W,02:29:55,05:54:17 +51,M,02:24:13,05:54:17 +34,W,02:18:41,05:54:17 +56,W,02:24:32,05:54:18 +53,M,02:20:47,05:54:18 +24,M,02:11:03,05:54:18 +53,W,02:23:41,05:54:19 +49,W,02:34:48,05:54:19 +21,M,02:30:01,05:54:19 +54,M,02:23:26,05:54:20 +31,W,02:13:22,05:54:21 +39,W,02:27:59,05:54:22 +48,W,02:49:12,05:54:23 +41,W,02:33:51,05:54:24 +52,W,02:31:21,05:54:24 +32,M,02:14:04,05:54:26 +65,M,02:40:08,05:54:28 +38,W,02:32:29,05:54:28 +35,M,02:20:10,05:54:30 +29,W,02:25:50,05:54:30 +38,W,02:22:18,05:54:30 +39,M,02:18:43,05:54:31 +56,M,02:28:15,05:54:31 +43,W,02:28:33,05:54:31 +54,W,02:28:47,05:54:32 +21,W,02:22:25,05:54:34 +39,W,02:30:55,05:54:34 +53,M,02:17:35,05:54:34 +27,W,02:35:03,05:54:35 +32,W,02:27:39,05:54:35 +30,W,02:24:18,05:54:36 +73,M,02:46:19,05:54:36 +32,M,02:33:36,05:54:36 +45,W,02:27:49,05:54:38 +38,M,02:15:04,05:54:39 +43,W,02:22:37,05:54:39 +47,M,02:24:13,05:54:40 +46,M,02:32:42,05:54:41 +27,W,02:18:14,05:54:41 +55,W,02:29:19,05:54:42 +38,W,02:45:02,05:54:43 +32,W,02:24:48,05:54:44 +43,M,02:21:18,05:54:45 +35,M,02:18:57,05:54:46 +49,M,02:38:29,05:54:46 +33,W,02:21:37,05:54:47 +23,M,02:26:48,05:54:47 +41,M,02:12:21,05:54:47 +30,W,02:14:11,05:54:48 +33,M,02:17:12,05:54:49 +42,W,02:28:24,05:54:49 +28,W,02:22:46,05:54:50 +28,W,02:25:48,05:54:50 +48,M,02:21:29,05:54:50 +62,M,02:22:58,05:54:50 +67,M,02:24:57,05:54:51 +33,M,02:25:52,05:54:52 +59,M,02:35:35,05:54:53 +34,M,02:30:42,05:54:53 +26,W,02:27:34,05:54:54 +28,W,02:27:43,05:54:54 +62,M,02:30:22,05:54:54 +51,M,02:20:54,05:54:55 +20,M,02:11:58,05:54:55 +38,W,02:26:29,05:54:57 +27,W,02:23:52,05:54:57 +37,M,02:17:33,05:54:57 +60,M,02:18:52,05:54:58 +46,M,02:13:47,05:54:59 +23,W,02:23:31,05:54:59 +58,W,02:30:49,05:54:59 +41,M,02:32:33,05:55:02 +50,W,02:22:16,05:55:02 +21,W,02:32:29,05:55:02 +37,W,02:22:00,05:55:03 +33,W,02:19:44,05:55:03 +38,M,02:32:40,05:55:03 +29,W,02:18:18,05:55:04 +54,W,02:34:57,05:55:04 +38,W,02:48:30,05:55:05 +46,M,02:24:16,05:55:05 +39,W,02:33:35,05:55:06 +26,M,02:12:28,05:55:06 +47,M,02:09:57,05:55:06 +35,M,02:06:56,05:55:07 +41,M,02:25:38,05:55:08 +45,W,02:31:29,05:55:08 +57,W,02:26:58,05:55:09 +44,M,02:33:43,05:55:09 +33,M,02:26:57,05:55:10 +34,W,02:17:13,05:55:11 +20,M,02:44:48,05:55:11 +64,M,02:21:28,05:55:13 +36,M,02:08:22,05:55:14 +44,M,02:21:44,05:55:15 +38,M,02:21:54,05:55:17 +50,M,02:22:06,05:55:17 +31,W,02:28:03,05:55:18 +41,W,02:24:51,05:55:18 +30,W,02:30:06,05:55:19 +49,M,02:18:20,05:55:20 +25,W,02:31:53,05:55:20 +28,W,02:27:31,05:55:20 +56,M,02:20:01,05:55:22 +26,M,02:20:54,05:55:22 +42,M,02:25:42,05:55:23 +50,W,02:20:40,05:55:24 +35,W,02:22:56,05:55:26 +50,M,02:12:19,05:55:26 +39,W,02:08:51,05:55:26 +25,W,02:24:46,05:55:27 +23,W,02:25:58,05:55:27 +26,M,02:20:26,05:55:28 +48,W,02:16:42,05:55:29 +20,W,02:32:27,05:55:29 +62,M,02:26:42,05:55:30 +37,M,02:06:50,05:55:30 +30,W,02:22:09,05:55:30 +47,M,02:31:04,05:55:30 +34,W,02:34:03,05:55:31 +52,M,02:37:19,05:55:31 +25,W,02:24:27,05:55:31 +42,W,02:22:27,05:55:33 +45,M,02:14:43,05:55:35 +52,M,02:29:15,05:55:35 +43,M,02:16:22,05:55:36 +48,M,02:21:23,05:55:37 +52,M,02:12:41,05:55:38 +42,W,02:53:46,05:55:38 +34,M,02:24:18,05:55:38 +49,W,02:33:11,05:55:38 +54,W,02:39:58,05:55:39 +46,W,02:20:49,05:55:40 +36,W,02:30:36,05:55:40 +36,M,02:30:27,05:55:42 +33,M,02:27:31,05:55:42 +24,M,02:23:23,05:55:43 +62,M,02:22:32,05:55:43 +47,W,02:24:49,05:55:45 +59,M,02:18:00,05:55:45 +57,M,02:12:35,05:55:45 +30,M,02:33:59,05:55:45 +44,M,02:20:26,05:55:46 +56,M,02:34:31,05:55:47 +31,M,02:21:18,05:55:48 +56,M,02:12:18,05:55:48 +51,M,02:17:13,05:55:49 +32,M,02:25:14,05:55:49 +50,W,02:26:50,05:55:49 +49,M,02:14:23,05:55:51 +32,W,02:33:33,05:55:52 +33,W,02:27:02,05:55:52 +29,M,02:29:34,05:55:53 +60,W,02:22:12,05:55:55 +50,W,02:27:19,05:55:56 +51,W,02:37:40,05:55:56 +37,W,02:16:37,05:55:57 +50,M,02:32:45,05:55:58 +25,W,02:40:20,05:55:58 +67,W,02:36:53,05:55:58 +35,M,02:21:59,05:55:59 +43,W,02:26:40,05:55:59 +59,M,02:28:41,05:55:59 +32,W,02:00:52,05:56:00 +25,M,02:23:42,05:56:01 +27,W,02:35:41,05:56:01 +25,M,02:25:48,05:56:02 +41,W,02:22:46,05:56:03 +65,M,02:24:29,05:56:03 +23,W,02:30:38,05:56:04 +27,W,02:34:04,05:56:04 +61,M,02:21:18,05:56:04 +27,W,02:26:07,05:56:04 +25,W,02:46:23,05:56:05 +39,M,02:42:55,05:56:06 +49,M,02:24:43,05:56:06 +31,M,02:30:27,05:56:06 +26,W,02:22:49,05:56:06 +28,M,02:40:26,05:56:07 +34,W,02:35:08,05:56:07 +36,W,02:22:50,05:56:07 +51,M,02:18:00,05:56:09 +39,W,02:48:39,05:56:10 +50,M,02:46:50,05:56:10 +60,M,02:09:14,05:56:12 +38,W,02:21:23,05:56:12 +25,W,02:27:57,05:56:13 +32,W,02:18:59,05:56:14 +31,W,02:21:53,05:56:14 +42,M,02:32:44,05:56:14 +33,M,02:30:13,05:56:14 +36,W,02:33:36,05:56:15 +35,W,02:25:29,05:56:16 +24,W,02:36:21,05:56:17 +54,W,02:29:16,05:56:17 +24,W,02:27:32,05:56:17 +69,M,02:14:16,05:56:18 +50,M,02:21:28,05:56:19 +25,W,02:22:18,05:56:19 +36,W,02:29:14,05:56:21 +40,W,02:21:34,05:56:23 +24,M,02:21:40,05:56:24 +60,M,02:26:13,05:56:26 +39,M,02:39:22,05:56:26 +34,W,02:43:05,05:56:28 +29,M,02:20:26,05:56:28 +44,W,02:23:02,05:56:30 +48,M,02:33:49,05:56:31 +28,M,02:15:41,05:56:32 +45,W,02:30:33,05:56:32 +40,W,02:23:21,05:56:32 +31,M,02:19:59,05:56:32 +33,M,02:16:01,05:56:32 +36,M,02:29:13,05:56:35 +56,W,02:21:58,05:56:36 +33,M,02:21:46,05:56:36 +41,M,02:36:09,05:56:36 +51,W,02:14:46,05:56:37 +53,W,02:28:50,05:56:38 +42,W,02:22:53,05:56:38 +42,M,02:35:42,05:56:40 +36,W,02:31:18,05:56:40 +38,M,02:22:14,05:56:41 +43,M,02:21:10,05:56:41 +41,M,02:17:52,05:56:41 +61,M,02:33:52,05:56:41 +36,W,02:28:57,05:56:42 +41,M,02:21:32,05:56:43 +35,M,02:39:49,05:56:43 +32,W,02:20:43,05:56:43 +23,W,02:28:08,05:56:43 +68,W,02:33:48,05:56:43 +39,M,02:16:02,05:56:45 +28,M,02:20:07,05:56:45 +45,M,02:39:06,05:56:46 +45,M,02:21:56,05:56:46 +36,W,02:33:58,05:56:46 +22,W,02:30:51,05:56:47 +66,M,02:19:10,05:56:47 +59,W,02:22:06,05:56:48 +36,M,02:55:41,05:56:50 +32,M,02:24:24,05:56:50 +38,W,02:24:26,05:56:51 +23,M,02:10:40,05:56:52 +41,M,02:29:26,05:56:52 +40,M,02:30:03,05:56:53 +54,M,02:14:05,05:56:53 +31,W,02:18:49,05:56:53 +65,W,02:32:04,05:56:53 +58,M,02:22:05,05:56:54 +26,W,02:34:59,05:56:54 +31,M,02:17:01,05:56:54 +60,W,02:31:29,05:56:55 +37,W,02:18:09,05:56:56 +34,M,02:39:52,05:56:57 +27,W,02:36:07,05:56:57 +46,W,02:29:39,05:56:58 +61,W,02:29:42,05:56:58 +41,M,02:17:24,05:56:59 +55,W,02:28:57,05:56:59 +45,W,02:22:52,05:57:00 +35,M,02:31:33,05:57:00 +50,M,02:30:56,05:57:00 +46,M,02:42:55,05:57:01 +24,W,02:28:45,05:57:01 +25,W,02:27:35,05:57:02 +44,M,02:23:59,05:57:03 +34,W,02:28:10,05:57:04 +53,M,02:14:27,05:57:07 +43,W,02:28:52,05:57:07 +29,M,02:33:36,05:57:09 +38,W,02:25:22,05:57:09 +61,M,02:24:22,05:57:10 +52,M,02:09:49,05:57:11 +39,M,02:28:36,05:57:11 +36,W,02:22:45,05:57:12 +36,M,02:19:23,05:57:12 +39,M,02:21:08,05:57:13 +43,M,02:20:34,05:57:13 +42,W,02:21:18,05:57:14 +50,M,02:25:53,05:57:14 +32,W,02:29:41,05:57:15 +28,W,02:37:49,05:57:15 +49,W,02:30:09,05:57:15 +63,M,02:33:30,05:57:15 +43,M,02:30:40,05:57:15 +41,W,02:27:39,05:57:16 +52,W,02:27:48,05:57:16 +46,M,02:22:02,05:57:17 +46,W,02:23:00,05:57:17 +47,M,02:26:04,05:57:19 +52,M,02:13:49,05:57:19 +35,W,02:35:28,05:57:20 +68,M,02:30:08,05:57:21 +34,W,02:31:00,05:57:21 +36,M,02:37:12,05:57:21 +40,W,02:27:46,05:57:22 +43,W,02:35:40,05:57:23 +63,M,02:34:10,05:57:23 +51,W,02:38:15,05:57:24 +30,M,02:13:09,05:57:24 +33,W,02:19:45,05:57:24 +50,M,02:22:17,05:57:27 +38,M,02:21:11,05:57:27 +25,W,02:13:44,05:57:28 +44,M,02:31:44,05:57:28 +41,M,02:13:13,05:57:28 +69,M,02:37:50,05:57:28 +46,M,02:23:44,05:57:29 +48,M,02:39:38,05:57:30 +43,W,02:30:00,05:57:30 +57,M,02:29:02,05:57:31 +47,W,02:35:48,05:57:31 +50,W,02:21:07,05:57:32 +26,M,02:22:50,05:57:32 +42,M,02:17:44,05:57:33 +39,M,02:11:41,05:57:33 +38,M,02:31:35,05:57:34 +50,M,02:24:44,05:57:35 +70,M,02:33:57,05:57:36 +39,W,02:17:05,05:57:37 +44,W,02:27:33,05:57:37 +37,W,02:28:03,05:57:38 +41,M,02:22:50,05:57:39 +60,M,02:34:05,05:57:39 +33,W,02:31:28,05:57:39 +26,W,02:29:21,05:57:39 +67,M,02:39:34,05:57:40 +45,M,02:39:49,05:57:41 +60,M,02:23:16,05:57:41 +39,W,02:27:22,05:57:42 +48,M,02:23:35,05:57:42 +36,W,02:27:31,05:57:42 +46,M,02:25:04,05:57:43 +48,W,02:43:44,05:57:43 +45,M,02:27:22,05:57:44 +45,M,02:30:34,05:57:45 +61,M,02:26:14,05:57:45 +46,W,02:23:52,05:57:46 +44,M,02:06:47,05:57:47 +47,W,02:29:06,05:57:47 +30,W,02:26:49,05:57:48 +31,W,02:25:59,05:57:48 +41,W,02:30:46,05:57:48 +55,M,02:22:44,05:57:48 +39,M,02:47:40,05:57:49 +43,W,02:26:20,05:57:49 +33,M,02:23:18,05:57:49 +30,W,02:35:44,05:57:49 +42,W,02:23:53,05:57:50 +51,W,02:21:26,05:57:50 +51,W,02:30:22,05:57:51 +33,M,02:11:16,05:57:52 +26,W,02:35:08,05:57:52 +33,W,02:23:09,05:57:52 +44,M,02:27:02,05:57:53 +43,M,02:17:13,05:57:53 +28,M,02:07:18,05:57:53 +30,M,02:17:33,05:57:54 +31,M,02:34:16,05:57:54 +62,M,02:28:56,05:57:55 +36,M,02:38:00,05:57:56 +20,M,02:30:33,05:57:56 +31,M,02:21:18,05:57:57 +47,M,02:30:49,05:57:57 +47,W,02:26:55,05:57:57 +28,W,02:24:02,05:57:58 +49,M,02:42:59,05:57:58 +73,W,02:18:43,05:57:59 +45,M,02:25:49,05:57:59 +61,M,02:23:52,05:58:00 +50,W,02:20:25,05:58:00 +45,M,02:35:12,05:58:01 +26,W,02:17:29,05:58:01 +57,W,02:35:05,05:58:02 +39,M,02:31:58,05:58:02 +44,M,02:05:32,05:58:03 +33,M,02:29:58,05:58:04 +53,W,02:29:13,05:58:04 +51,M,02:22:08,05:58:04 +45,M,02:43:21,05:58:05 +44,M,02:17:09,05:58:05 +46,W,02:58:41,05:58:06 +39,M,02:19:18,05:58:07 +36,W,02:33:48,05:58:08 +28,M,02:22:10,05:58:08 +41,W,02:20:22,05:58:09 +33,M,02:27:03,05:58:09 +39,W,02:23:51,05:58:11 +31,W,02:20:42,05:58:12 +39,M,02:22:27,05:58:12 +24,W,02:19:40,05:58:13 +39,W,02:31:24,05:58:13 +60,M,02:21:00,05:58:14 +54,M,02:30:59,05:58:15 +33,W,02:33:59,05:58:15 +53,W,02:30:30,05:58:15 +29,W,02:26:55,05:58:16 +61,M,02:21:13,05:58:16 +59,W,02:23:34,05:58:16 +38,M,02:28:40,05:58:17 +33,W,02:40:32,05:58:17 +18,W,02:25:35,05:58:17 +29,W,02:37:12,05:58:17 +40,W,02:32:09,05:58:18 +45,W,02:22:00,05:58:19 +63,M,02:21:20,05:58:20 +27,W,02:30:49,05:58:21 +45,M,02:31:30,05:58:21 +40,M,02:19:42,05:58:22 +38,W,02:24:50,05:58:22 +39,M,02:31:06,05:58:23 +45,W,02:24:05,05:58:23 +31,W,02:31:46,05:58:23 +28,M,02:17:37,05:58:24 +41,W,02:41:15,05:58:24 +43,M,02:25:36,05:58:24 +36,W,02:24:31,05:58:25 +34,W,02:23:07,05:58:28 +52,M,02:16:04,05:58:29 +40,W,02:22:13,05:58:29 +42,M,02:26:39,05:58:30 +37,W,02:31:59,05:58:31 +47,W,02:32:23,05:58:31 +25,M,02:19:10,05:58:31 +33,M,02:29:56,05:58:31 +25,W,02:29:42,05:58:32 +45,M,02:23:42,05:58:33 +27,M,02:40:43,05:58:34 +44,W,02:26:00,05:58:34 +47,M,02:23:02,05:58:34 +47,M,02:26:01,05:58:34 +26,M,02:39:14,05:58:34 +55,M,02:13:30,05:58:35 +50,W,02:35:15,05:58:35 +54,M,02:18:49,05:58:36 +50,M,02:20:11,05:58:37 +60,M,02:24:03,05:58:41 +52,M,02:25:01,05:58:42 +32,M,02:33:42,05:58:44 +32,W,02:20:14,05:58:44 +58,M,02:18:31,05:58:44 +47,W,02:25:42,05:58:45 +46,W,02:27:35,05:58:45 +57,W,02:35:12,05:58:45 +45,W,02:19:30,05:58:46 +24,W,02:23:54,05:58:47 +49,W,02:25:44,05:58:48 +46,W,02:24:30,05:58:49 +40,M,02:30:54,05:58:50 +40,M,02:27:17,05:58:50 +47,M,02:20:32,05:58:51 +35,W,02:17:31,05:58:51 +25,W,02:17:59,05:58:52 +47,M,02:18:45,05:58:52 +47,M,02:25:51,05:58:52 +27,W,02:17:28,05:58:54 +32,M,02:27:00,05:58:55 +36,M,02:20:36,05:58:55 +25,W,02:23:59,05:58:56 +26,W,02:24:41,05:58:56 +40,M,02:29:26,05:58:58 +52,M,02:19:32,05:58:59 +46,W,02:24:25,05:58:59 +32,W,02:28:36,05:58:59 +48,W,02:49:54,05:58:59 +31,M,02:26:58,05:59:00 +27,W,02:13:30,05:59:00 +33,M,02:27:13,05:59:00 +29,W,02:22:01,05:59:01 +48,M,02:11:10,05:59:02 +48,W,02:33:45,05:59:02 +53,M,02:25:33,05:59:02 +30,W,02:32:18,05:59:04 +39,M,02:28:44,05:59:04 +60,M,02:24:09,05:59:04 +26,M,02:05:54,05:59:05 +23,W,02:41:21,05:59:06 +28,M,02:27:27,05:59:06 +29,W,02:32:45,05:59:06 +41,W,02:31:04,05:59:06 +45,W,02:14:37,05:59:07 +50,W,02:21:50,05:59:07 +29,W,02:36:48,05:59:08 +26,W,02:30:36,05:59:08 +29,M,02:33:24,05:59:10 +30,W,02:38:20,05:59:10 +33,M,02:26:09,05:59:11 +30,M,02:15:33,05:59:11 +32,M,02:25:35,05:59:12 +48,W,02:35:50,05:59:12 +58,M,02:29:57,05:59:12 +43,W,02:27:31,05:59:12 +50,W,02:28:59,05:59:13 +32,W,02:41:34,05:59:13 +58,M,02:34:15,05:59:13 +44,W,02:31:19,05:59:13 +47,M,02:34:25,05:59:14 +54,M,02:12:51,05:59:15 +26,W,02:25:16,05:59:15 +37,W,02:22:46,05:59:17 +33,W,02:23:07,05:59:17 +51,M,02:21:11,05:59:17 +54,M,02:34:36,05:59:18 +49,M,02:27:17,05:59:18 +62,M,02:22:46,05:59:20 +27,W,02:26:05,05:59:21 +42,M,02:20:32,05:59:21 +28,W,02:25:27,05:59:22 +34,M,02:18:16,05:59:22 +35,W,02:26:21,05:59:22 +35,M,02:25:15,05:59:22 +47,M,02:29:52,05:59:22 +46,M,02:18:30,05:59:24 +27,W,02:13:54,05:59:24 +38,M,02:08:41,05:59:24 +31,W,02:28:55,05:59:25 +39,W,02:33:52,05:59:25 +37,W,02:36:48,05:59:25 +51,W,02:30:34,05:59:25 +52,W,02:27:54,05:59:25 +50,W,02:28:43,05:59:26 +51,W,02:32:06,05:59:27 +51,M,02:52:56,05:59:27 +46,M,02:19:41,05:59:27 +31,M,02:18:27,05:59:28 +40,M,02:19:49,05:59:29 +28,W,02:22:23,05:59:30 +25,M,02:21:22,05:59:31 +38,M,02:26:29,05:59:31 +33,W,02:21:54,05:59:32 +32,W,02:32:01,05:59:33 +29,M,02:30:30,05:59:35 +47,W,02:23:14,05:59:36 +47,M,02:19:21,05:59:36 +36,W,02:28:14,05:59:37 +54,M,02:20:21,05:59:38 +30,M,02:33:54,05:59:38 +25,W,02:29:52,05:59:39 +33,W,02:28:11,05:59:39 +50,W,02:29:26,05:59:39 +49,M,02:12:54,05:59:40 +45,M,02:25:48,05:59:40 +44,W,02:18:03,05:59:41 +30,M,02:36:45,05:59:41 +27,W,02:32:02,05:59:41 +42,W,02:45:14,05:59:41 +44,M,02:14:25,05:59:41 +42,W,02:11:29,05:59:41 +30,W,02:25:37,05:59:41 +28,M,02:19:57,05:59:41 +48,M,02:16:21,05:59:42 +50,M,02:26:10,05:59:42 +47,W,02:34:22,05:59:42 +58,M,02:33:52,05:59:43 +29,M,02:23:05,05:59:44 +44,M,02:18:57,05:59:44 +40,W,02:44:53,05:59:44 +50,W,02:25:05,05:59:46 +45,M,02:19:29,05:59:47 +41,W,02:31:39,05:59:47 +36,M,02:31:31,05:59:49 +45,W,02:38:12,05:59:50 +37,M,02:20:28,05:59:51 +37,M,02:41:14,05:59:51 +27,W,02:31:18,05:59:51 +45,W,02:50:09,05:59:52 +59,M,02:35:34,05:59:52 +26,M,02:20:26,05:59:53 +32,W,02:20:12,05:59:53 +28,M,02:14:20,05:59:54 +40,M,02:21:47,05:59:55 +22,W,02:33:36,05:59:55 +48,W,02:33:20,05:59:55 +57,M,02:29:40,05:59:55 +34,W,02:30:54,05:59:55 +40,W,02:36:50,05:59:56 +25,W,02:30:33,05:59:56 +61,M,02:26:43,05:59:56 +45,M,02:19:17,05:59:57 +53,M,02:22:54,05:59:58 +33,M,02:26:23,06:00:00 +43,W,02:34:17,06:00:00 +50,W,02:31:41,06:00:01 +47,W,02:25:49,06:00:02 +39,W,02:27:38,06:00:02 +55,M,02:09:53,06:00:02 +28,W,02:30:43,06:00:02 +36,W,02:19:07,06:00:02 +37,M,02:14:56,06:00:03 +51,M,02:20:38,06:00:03 +63,M,02:30:53,06:00:03 +32,W,02:19:07,06:00:03 +38,M,02:30:00,06:00:03 +32,M,02:28:55,06:00:03 +46,M,02:16:41,06:00:04 +61,M,02:34:16,06:00:05 +59,M,02:26:47,06:00:06 +30,M,02:16:55,06:00:07 +46,W,02:16:21,06:00:07 +40,W,02:30:52,06:00:08 +55,M,02:20:52,06:00:08 +33,W,02:32:20,06:00:09 +33,W,02:37:54,06:00:10 +71,M,02:32:43,06:00:11 +26,W,02:21:06,06:00:11 +32,W,02:40:07,06:00:11 +41,W,02:38:08,06:00:12 +36,W,02:36:21,06:00:13 +24,W,02:16:53,06:00:16 +28,M,02:16:18,06:00:16 +32,M,02:17:44,06:00:16 +59,W,02:26:44,06:00:16 +41,M,02:27:11,06:00:17 +49,W,02:27:12,06:00:19 +68,M,02:30:04,06:00:19 +30,W,02:23:04,06:00:19 +25,M,02:31:29,06:00:19 +31,W,02:25:34,06:00:20 +30,W,02:24:52,06:00:21 +29,W,02:36:37,06:00:22 +34,W,02:37:28,06:00:22 +32,W,02:19:34,06:00:22 +34,M,02:26:46,06:00:24 +47,W,02:22:53,06:00:24 +44,M,02:28:16,06:00:25 +27,W,02:25:51,06:00:25 +45,M,02:15:44,06:00:25 +45,M,02:39:57,06:00:25 +25,W,02:39:44,06:00:26 +26,M,02:09:44,06:00:26 +49,W,02:18:48,06:00:27 +32,W,02:23:10,06:00:28 +57,M,02:24:10,06:00:29 +41,W,02:29:45,06:00:29 +59,M,02:24:06,06:00:29 +42,W,02:29:29,06:00:29 +44,M,02:25:09,06:00:29 +45,W,02:29:28,06:00:30 +32,W,02:37:40,06:00:30 +33,W,02:14:17,06:00:30 +45,M,02:18:34,06:00:31 +54,M,02:22:07,06:00:32 +41,M,02:19:23,06:00:32 +30,W,02:36:24,06:00:32 +36,M,02:13:23,06:00:32 +41,W,02:22:17,06:00:32 +29,M,02:16:19,06:00:33 +40,W,02:23:15,06:00:33 +37,W,02:22:24,06:00:34 +46,M,02:19:30,06:00:34 +40,W,02:20:14,06:00:34 +60,M,02:26:17,06:00:36 +49,M,02:17:34,06:00:36 +30,M,02:36:01,06:00:37 +36,W,02:28:23,06:00:38 +31,W,02:29:25,06:00:38 +26,M,02:18:27,06:00:38 +52,W,02:32:56,06:00:39 +61,M,02:34:08,06:00:40 +52,W,02:19:44,06:00:41 +36,W,02:26:29,06:00:41 +46,W,02:32:12,06:00:41 +55,M,02:15:11,06:00:42 +46,M,02:25:20,06:00:42 +63,M,02:57:08,06:00:43 +62,M,02:25:24,06:00:43 +47,M,02:20:03,06:00:44 +27,W,02:07:04,06:00:44 +46,W,02:33:33,06:00:44 +39,M,02:17:52,06:00:44 +39,W,02:32:27,06:00:44 +30,M,02:15:33,06:00:44 +31,W,02:38:32,06:00:46 +36,M,02:32:30,06:00:46 +36,M,02:45:42,06:00:47 +36,W,02:15:19,06:00:47 +40,W,02:27:28,06:00:48 +44,M,02:33:05,06:00:48 +53,M,02:27:48,06:00:49 +51,M,02:22:09,06:00:49 +67,W,02:37:14,06:00:49 +47,W,02:28:23,06:00:50 +63,M,02:17:12,06:00:50 +40,W,02:29:37,06:00:51 +21,W,02:18:45,06:00:51 +29,M,02:12:25,06:00:51 +36,W,02:28:24,06:00:51 +42,W,02:13:29,06:00:52 +61,M,02:20:31,06:00:53 +34,M,02:25:43,06:00:54 +34,W,02:55:37,06:00:55 +24,W,02:32:58,06:00:56 +58,M,02:23:21,06:00:58 +36,W,02:32:01,06:00:58 +42,M,02:37:23,06:00:58 +29,M,02:35:59,06:00:58 +28,W,02:34:51,06:00:59 +40,M,02:20:33,06:00:59 +30,M,02:23:49,06:00:59 +48,M,02:20:49,06:01:00 +38,W,02:42:25,06:01:00 +45,W,02:28:26,06:01:00 +40,W,02:31:56,06:01:01 +55,W,02:29:54,06:01:03 +35,M,02:17:44,06:01:03 +32,M,02:09:37,06:01:04 +34,W,02:34:47,06:01:05 +55,M,02:25:35,06:01:05 +32,W,02:14:38,06:01:05 +28,M,02:27:01,06:01:05 +57,M,02:23:36,06:01:06 +26,W,02:25:01,06:01:06 +54,W,02:36:16,06:01:07 +60,M,02:20:23,06:01:08 +42,W,02:33:47,06:01:09 +55,M,02:23:29,06:01:09 +36,M,02:38:41,06:01:10 +25,M,02:07:37,06:01:10 +43,W,02:35:04,06:01:12 +57,W,02:29:08,06:01:12 +52,M,02:24:37,06:01:12 +35,M,02:25:13,06:01:13 +31,W,02:39:34,06:01:13 +35,M,02:37:32,06:01:14 +44,W,02:31:46,06:01:14 +35,M,02:25:28,06:01:14 +64,M,02:21:02,06:01:14 +58,W,02:37:23,06:01:15 +40,W,02:39:09,06:01:17 +26,W,02:31:14,06:01:18 +34,W,02:24:35,06:01:18 +44,W,02:24:23,06:01:19 +35,M,02:28:42,06:01:20 +41,W,02:24:38,06:01:20 +35,M,02:13:13,06:01:20 +73,M,02:29:31,06:01:20 +41,M,02:19:00,06:01:21 +54,W,02:31:06,06:01:21 +40,W,02:28:43,06:01:21 +29,W,02:26:22,06:01:22 +43,M,02:33:04,06:01:22 +42,M,02:25:35,06:01:22 +30,W,02:26:09,06:01:23 +40,W,02:33:26,06:01:24 +59,W,02:19:25,06:01:24 +40,W,02:28:37,06:01:24 +55,W,02:30:46,06:01:25 +41,M,02:22:26,06:01:26 +45,W,02:32:23,06:01:26 +56,M,02:20:58,06:01:26 +43,W,02:25:32,06:01:27 +44,M,02:34:35,06:01:28 +33,W,02:26:07,06:01:29 +55,M,02:28:12,06:01:29 +37,M,02:26:12,06:01:30 +24,W,02:23:42,06:01:31 +61,M,02:23:51,06:01:33 +39,W,02:30:00,06:01:33 +26,M,02:07:59,06:01:33 +23,M,02:23:20,06:01:34 +43,W,02:25:47,06:01:34 +30,W,02:24:08,06:01:34 +41,M,02:12:11,06:01:35 +52,W,02:25:21,06:01:35 +64,W,02:17:33,06:01:36 +28,M,02:16:55,06:01:36 +29,W,02:21:18,06:01:36 +50,M,02:21:59,06:01:36 +28,W,02:35:20,06:01:39 +39,W,02:19:39,06:01:40 +31,W,02:26:47,06:01:41 +49,M,02:15:14,06:01:41 +28,W,02:22:40,06:01:41 +25,W,02:47:32,06:01:41 +45,W,02:23:11,06:01:42 +54,W,02:25:36,06:01:43 +38,M,02:25:23,06:01:45 +37,W,02:29:32,06:01:46 +31,W,02:22:14,06:01:49 +31,W,02:32:22,06:01:49 +68,W,02:32:55,06:01:49 +35,W,02:27:41,06:01:50 +33,W,02:26:00,06:01:51 +42,W,02:34:15,06:01:51 +50,M,02:14:06,06:01:52 +48,W,02:35:08,06:01:52 +35,W,02:18:17,06:01:53 +66,M,02:19:36,06:01:54 +44,W,02:30:53,06:01:54 +31,W,02:45:49,06:01:55 +52,M,02:29:31,06:01:55 +41,M,02:31:11,06:01:55 +28,W,02:33:18,06:01:55 +50,M,02:26:26,06:01:56 +39,M,02:32:27,06:01:56 +40,M,02:17:28,06:01:56 +40,W,02:33:27,06:01:57 +34,W,02:25:00,06:01:58 +60,M,02:30:16,06:01:58 +44,M,02:21:01,06:02:00 +44,M,02:25:21,06:02:00 +60,M,02:29:56,06:02:00 +24,W,02:22:36,06:02:00 +48,M,02:18:51,06:02:01 +31,W,02:30:11,06:02:01 +48,W,02:18:03,06:02:01 +59,M,02:32:39,06:02:02 +46,W,02:32:30,06:02:02 +25,W,02:25:47,06:02:04 +33,W,02:37:52,06:02:04 +40,W,02:32:22,06:02:04 +28,W,02:46:07,06:02:04 +27,M,02:38:02,06:02:06 +37,M,02:30:29,06:02:07 +59,W,02:30:40,06:02:07 +48,W,02:23:36,06:02:07 +36,M,02:30:03,06:02:07 +49,W,02:50:48,06:02:09 +47,W,02:35:43,06:02:10 +52,W,02:33:32,06:02:10 +51,M,02:23:18,06:02:10 +43,W,02:31:50,06:02:11 +34,M,02:18:51,06:02:12 +26,W,02:31:13,06:02:12 +32,W,02:26:00,06:02:13 +29,W,02:33:29,06:02:14 +37,W,02:24:57,06:02:14 +65,M,02:36:55,06:02:15 +32,M,02:31:48,06:02:17 +38,W,02:23:26,06:02:17 +36,M,02:24:13,06:02:18 +49,M,02:29:25,06:02:19 +51,M,02:32:52,06:02:19 +54,M,02:24:09,06:02:21 +28,M,02:18:41,06:02:21 +40,M,02:34:09,06:02:22 +25,W,02:25:10,06:02:22 +35,M,02:26:12,06:02:26 +43,M,02:26:34,06:02:26 +50,M,02:20:36,06:02:26 +43,W,02:42:20,06:02:28 +29,W,02:30:29,06:02:29 +41,M,02:21:54,06:02:29 +53,M,02:25:38,06:02:30 +51,M,02:28:54,06:02:31 +27,W,02:30:34,06:02:31 +20,M,02:19:17,06:02:32 +26,M,02:14:20,06:02:34 +40,W,02:21:37,06:02:34 +63,W,02:26:13,06:02:34 +58,M,02:13:18,06:02:34 +41,M,02:23:37,06:02:35 +28,W,02:19:16,06:02:35 +53,M,02:22:56,06:02:35 +40,W,02:40:58,06:02:35 +32,W,02:38:21,06:02:36 +59,M,02:32:29,06:02:38 +46,W,02:40:22,06:02:39 +64,M,02:26:18,06:02:40 +26,M,02:33:55,06:02:41 +53,W,02:32:20,06:02:41 +22,W,02:29:19,06:02:41 +55,M,02:30:40,06:02:41 +44,W,02:24:57,06:02:42 +26,M,02:20:30,06:02:42 +20,W,02:33:37,06:02:42 +28,W,02:33:25,06:02:43 +32,W,02:36:06,06:02:44 +69,W,02:27:11,06:02:44 +47,W,02:42:17,06:02:44 +17,M,02:22:15,06:02:45 +56,W,02:31:28,06:02:45 +43,W,02:34:10,06:02:47 +39,M,02:23:06,06:02:48 +34,M,02:21:26,06:02:49 +53,M,02:20:55,06:02:49 +55,M,02:15:32,06:02:51 +51,M,02:29:52,06:02:51 +30,M,02:33:25,06:02:52 +33,M,02:29:33,06:02:52 +57,M,02:24:15,06:02:53 +53,M,02:39:42,06:02:53 +27,W,02:20:40,06:02:54 +21,W,02:28:00,06:02:55 +38,M,02:21:34,06:02:56 +46,M,02:32:18,06:02:56 +40,W,02:31:59,06:02:56 +30,W,02:35:43,06:02:57 +32,M,02:23:12,06:02:57 +36,M,02:19:46,06:02:58 +27,W,02:17:25,06:02:58 +31,M,02:34:07,06:02:58 +40,M,02:14:38,06:02:58 +31,W,02:32:15,06:02:58 +41,W,02:11:37,06:02:59 +56,M,02:30:35,06:03:00 +71,M,02:25:37,06:03:01 +39,M,02:22:43,06:03:02 +34,M,02:22:33,06:03:04 +43,M,02:27:27,06:03:04 +44,M,02:37:43,06:03:04 +55,M,02:27:50,06:03:04 +26,M,02:23:12,06:03:05 +40,M,02:31:58,06:03:05 +62,M,02:17:20,06:03:06 +28,M,02:31:54,06:03:07 +40,W,02:35:56,06:03:08 +28,W,02:31:39,06:03:08 +41,W,02:24:16,06:03:09 +50,M,02:18:00,06:03:10 +59,M,02:26:06,06:03:11 +32,M,02:31:53,06:03:11 +33,M,02:24:58,06:03:13 +44,W,02:38:04,06:03:13 +42,M,02:03:36,06:03:13 +44,W,02:37:02,06:03:15 +61,M,02:25:08,06:03:16 +50,M,02:35:26,06:03:16 +42,W,02:43:32,06:03:18 +46,M,02:18:08,06:03:19 +50,W,02:44:06,06:03:21 +29,W,02:34:20,06:03:22 +39,M,02:27:42,06:03:24 +31,M,01:55:38,06:03:24 +42,M,02:02:06,06:03:24 +45,W,02:26:44,06:03:25 +38,W,02:30:26,06:03:25 +67,M,02:16:02,06:03:26 +51,M,02:22:41,06:03:26 +46,M,02:30:19,06:03:27 +55,M,02:30:24,06:03:28 +56,M,02:20:23,06:03:28 +50,M,02:40:47,06:03:28 +32,W,02:34:25,06:03:29 +44,M,02:20:17,06:03:30 +55,W,02:28:39,06:03:30 +61,W,02:23:29,06:03:30 +54,M,02:37:34,06:03:31 +27,W,02:31:41,06:03:33 +40,M,02:26:26,06:03:33 +49,W,02:23:21,06:03:33 +46,W,02:34:11,06:03:35 +29,W,02:32:36,06:03:35 +55,W,02:41:13,06:03:35 +50,M,02:20:10,06:03:35 +40,M,02:32:13,06:03:37 +44,M,02:30:09,06:03:37 +42,M,02:16:41,06:03:38 +38,M,02:22:02,06:03:38 +39,M,02:43:47,06:03:38 +44,M,02:16:12,06:03:38 +53,M,02:28:23,06:03:38 +44,W,02:27:18,06:03:39 +46,M,02:21:42,06:03:39 +31,W,02:25:32,06:03:40 +61,W,02:21:12,06:03:40 +34,W,02:36:45,06:03:41 +29,W,02:49:52,06:03:41 +52,W,02:16:58,06:03:44 +50,W,02:38:28,06:03:44 +53,M,02:26:27,06:03:44 +55,M,02:23:22,06:03:44 +49,W,02:29:27,06:03:44 +38,M,02:40:40,06:03:45 +55,M,02:32:13,06:03:45 +48,M,02:16:14,06:03:46 +37,W,02:33:04,06:03:46 +56,M,02:43:04,06:03:46 +60,M,02:34:37,06:03:46 +58,M,02:13:54,06:03:47 +65,M,02:33:52,06:03:48 +31,W,02:34:13,06:03:48 +59,M,02:37:53,06:03:49 +55,M,02:17:12,06:03:51 +63,W,02:36:55,06:03:51 +32,W,02:26:03,06:03:52 +52,M,02:29:27,06:03:52 +55,M,02:20:29,06:03:52 +35,W,02:25:50,06:03:52 +42,W,02:37:39,06:03:53 +34,W,02:32:58,06:03:54 +48,W,02:43:05,06:03:54 +59,M,02:19:42,06:03:54 +54,M,02:36:35,06:03:55 +50,M,02:33:43,06:03:55 +54,M,02:36:07,06:03:56 +53,M,02:31:53,06:03:56 +49,M,02:39:56,06:03:57 +40,W,02:21:39,06:03:57 +48,W,02:42:52,06:03:58 +70,M,02:28:46,06:03:59 +47,M,02:26:22,06:03:59 +42,W,02:28:02,06:04:00 +40,W,02:22:51,06:04:00 +66,M,02:30:02,06:04:01 +44,M,02:23:52,06:04:03 +49,W,02:30:42,06:04:05 +25,M,02:09:45,06:04:05 +39,M,02:26:10,06:04:05 +32,M,02:28:28,06:04:05 +52,W,02:29:30,06:04:06 +48,M,02:27:43,06:04:09 +49,W,02:44:34,06:04:09 +30,W,02:34:13,06:04:10 +22,M,02:19:06,06:04:12 +48,M,02:18:13,06:04:13 +54,W,02:33:32,06:04:14 +38,M,02:40:44,06:04:14 +38,M,02:18:56,06:04:16 +49,M,02:28:10,06:04:17 +56,W,02:31:08,06:04:18 +55,M,02:40:20,06:04:19 +35,W,02:27:39,06:04:19 +27,W,02:34:21,06:04:19 +39,M,02:36:47,06:04:21 +30,W,02:29:16,06:04:22 +39,M,02:40:41,06:04:23 +26,M,02:27:29,06:04:23 +39,W,02:35:45,06:04:24 +50,W,02:20:38,06:04:24 +36,M,02:45:10,06:04:25 +45,W,02:41:49,06:04:26 +31,W,02:15:33,06:04:26 +36,M,02:22:13,06:04:27 +49,W,02:37:14,06:04:27 +52,W,02:25:07,06:04:28 +35,M,02:30:37,06:04:28 +56,W,02:23:56,06:04:28 +56,M,02:50:59,06:04:28 +43,W,02:22:59,06:04:31 +54,M,02:22:14,06:04:31 +26,W,02:29:14,06:04:31 +61,M,02:22:44,06:04:32 +40,W,02:26:21,06:04:33 +39,M,02:20:14,06:04:34 +62,M,02:35:34,06:04:34 +60,M,02:23:35,06:04:34 +49,W,02:28:37,06:04:35 +56,M,02:25:59,06:04:36 +25,W,02:26:40,06:04:36 +52,M,02:30:52,06:04:37 +38,W,02:39:58,06:04:38 +37,M,02:40:47,06:04:39 +51,M,02:45:28,06:04:39 +49,M,02:26:57,06:04:39 +46,M,02:28:07,06:04:39 +34,M,02:40:31,06:04:40 +46,M,02:25:13,06:04:40 +27,M,02:32:06,06:04:41 +44,W,02:37:15,06:04:42 +33,W,02:38:03,06:04:44 +53,W,02:38:10,06:04:45 +49,W,02:40:51,06:04:46 +73,M,02:30:19,06:04:46 +64,M,02:24:37,06:04:46 +59,M,02:18:10,06:04:46 +23,M,02:24:56,06:04:46 +39,M,02:28:33,06:04:47 +43,M,02:27:04,06:04:47 +43,M,02:25:27,06:04:47 +34,W,02:31:23,06:04:48 +43,M,02:23:35,06:04:49 +25,W,02:31:39,06:04:50 +65,M,02:23:14,06:04:51 +47,W,02:28:29,06:04:51 +46,W,02:37:33,06:04:51 +55,M,02:53:02,06:04:51 +29,M,02:21:19,06:04:52 +24,W,02:30:20,06:04:53 +27,W,02:19:37,06:04:55 +62,M,02:33:03,06:04:56 +29,M,02:14:53,06:04:56 +35,M,02:30:34,06:04:57 +57,W,02:43:06,06:04:57 +36,M,02:21:01,06:04:57 +46,M,02:19:11,06:04:57 +63,W,02:30:51,06:04:57 +40,M,02:26:27,06:04:58 +41,M,02:27:37,06:04:58 +44,W,02:28:27,06:05:00 +23,W,02:19:29,06:05:02 +55,M,02:23:55,06:05:02 +39,W,02:38:03,06:05:02 +32,M,02:24:46,06:05:02 +26,W,02:30:04,06:05:02 +38,W,02:16:46,06:05:05 +31,W,02:30:17,06:05:06 +28,M,02:38:35,06:05:06 +26,W,02:37:41,06:05:06 +60,M,02:24:10,06:05:07 +59,M,02:23:19,06:05:09 +26,W,02:31:50,06:05:09 +33,W,02:18:41,06:05:10 +28,M,02:26:17,06:05:10 +39,W,02:23:28,06:05:10 +45,W,02:35:36,06:05:11 +35,W,02:25:48,06:05:11 +51,W,02:44:12,06:05:12 +46,W,02:32:45,06:05:12 +40,M,02:13:50,06:05:13 +30,M,02:15:25,06:05:14 +40,W,02:28:59,06:05:14 +45,M,02:18:02,06:05:14 +56,M,02:35:27,06:05:16 +27,W,02:22:49,06:05:16 +32,M,02:33:02,06:05:18 +56,M,02:20:55,06:05:19 +59,W,02:26:16,06:05:19 +29,W,02:33:54,06:05:20 +56,W,02:33:25,06:05:20 +48,M,02:17:16,06:05:21 +23,W,02:42:31,06:05:21 +40,M,02:42:22,06:05:21 +47,W,02:26:25,06:05:21 +45,W,02:24:31,06:05:21 +33,M,02:24:14,06:05:21 +44,W,02:43:37,06:05:23 +60,M,02:53:45,06:05:23 +55,W,02:29:19,06:05:23 +37,W,02:35:10,06:05:24 +41,W,02:37:13,06:05:24 +30,W,02:38:12,06:05:24 +51,W,02:23:10,06:05:26 +45,M,02:18:44,06:05:26 +51,M,02:44:31,06:05:26 +29,M,02:17:59,06:05:27 +50,M,02:31:59,06:05:29 +26,M,02:31:11,06:05:30 +42,M,02:28:59,06:05:31 +26,W,02:37:19,06:05:32 +33,W,02:25:35,06:05:32 +33,W,02:28:46,06:05:33 +37,W,02:12:51,06:05:34 +59,M,02:33:22,06:05:35 +49,M,02:12:21,06:05:35 +44,M,02:38:06,06:05:35 +38,W,02:28:29,06:05:35 +57,M,03:06:28,06:05:35 +38,W,02:46:59,06:05:37 +38,M,02:42:40,06:05:38 +25,W,02:33:13,06:05:38 +24,W,02:32:32,06:05:39 +28,W,02:30:49,06:05:40 +52,M,02:27:30,06:05:40 +46,M,02:17:55,06:05:40 +24,M,02:25:21,06:05:40 +48,W,02:34:45,06:05:41 +45,W,02:45:23,06:05:41 +55,W,02:41:37,06:05:42 +62,M,02:34:09,06:05:42 +41,W,02:23:28,06:05:42 +32,W,02:32:02,06:05:42 +57,M,02:22:11,06:05:43 +36,M,02:21:13,06:05:43 +30,W,02:28:23,06:05:44 +53,M,02:19:32,06:05:45 +52,M,02:33:20,06:05:45 +51,W,02:32:12,06:05:45 +56,W,02:23:13,06:05:47 +47,W,02:41:23,06:05:48 +49,M,02:19:21,06:05:48 +38,M,02:39:33,06:05:48 +44,M,02:22:09,06:05:48 +35,W,02:32:05,06:05:48 +46,W,02:20:54,06:05:48 +47,M,02:41:58,06:05:49 +40,M,02:14:50,06:05:49 +41,M,02:26:59,06:05:50 +51,W,02:38:48,06:05:50 +38,W,02:27:28,06:05:51 +35,W,02:28:04,06:05:53 +48,W,02:37:16,06:05:53 +50,W,02:23:09,06:05:53 +30,M,02:33:25,06:05:53 +51,W,02:40:56,06:05:53 +36,W,02:45:21,06:05:54 +47,M,02:36:28,06:05:55 +38,M,02:14:33,06:05:56 +48,M,02:29:07,06:05:57 +34,M,02:34:47,06:05:57 +46,M,02:27:54,06:05:57 +72,M,02:30:38,06:05:58 +54,W,02:30:19,06:05:59 +27,W,02:20:17,06:06:00 +36,W,02:24:40,06:06:01 +38,W,02:09:21,06:06:01 +33,W,02:31:43,06:06:02 +32,W,02:31:57,06:06:03 +30,W,02:35:11,06:06:03 +37,M,02:27:48,06:06:03 +36,W,02:33:13,06:06:04 +46,W,02:24:24,06:06:04 +35,W,02:29:45,06:06:04 +48,M,02:25:44,06:06:05 +65,M,02:44:12,06:06:05 +38,M,02:28:37,06:06:06 +30,W,02:28:15,06:06:06 +31,W,02:24:05,06:06:06 +23,M,02:30:01,06:06:07 +40,M,02:09:57,06:06:08 +32,W,02:37:54,06:06:09 +39,M,02:31:56,06:06:09 +63,M,02:30:33,06:06:09 +21,W,02:27:39,06:06:09 +39,M,02:34:02,06:06:09 +44,M,02:32:35,06:06:10 +50,M,02:22:23,06:06:11 +31,W,02:26:08,06:06:11 +39,M,02:13:28,06:06:12 +27,W,02:23:23,06:06:13 +41,W,02:29:21,06:06:13 +47,W,02:30:51,06:06:13 +29,M,02:29:34,06:06:13 +54,W,02:42:44,06:06:15 +44,W,02:43:15,06:06:15 +57,M,02:29:13,06:06:15 +49,W,02:28:39,06:06:16 +62,W,02:38:37,06:06:16 +35,M,02:27:59,06:06:16 +57,W,02:28:51,06:06:16 +46,M,02:31:37,06:06:17 +40,M,02:28:33,06:06:17 +59,M,02:32:35,06:06:17 +39,M,02:26:11,06:06:17 +49,W,02:34:42,06:06:18 +40,W,02:40:44,06:06:19 +43,W,02:55:29,06:06:20 +51,M,02:41:28,06:06:21 +24,M,02:18:14,06:06:22 +27,W,02:29:55,06:06:22 +30,W,02:22:54,06:06:24 +33,M,02:35:39,06:06:25 +33,M,02:22:53,06:06:25 +33,M,02:38:35,06:06:27 +49,M,02:35:35,06:06:27 +26,W,02:36:41,06:06:28 +55,W,02:19:54,06:06:28 +41,M,02:28:13,06:06:28 +30,W,02:23:34,06:06:29 +45,M,02:42:29,06:06:29 +36,W,02:33:06,06:06:29 +29,M,02:27:37,06:06:29 +45,W,02:25:12,06:06:30 +65,W,02:41:53,06:06:32 +42,W,02:29:45,06:06:32 +40,M,02:20:19,06:06:32 +27,W,02:28:27,06:06:32 +49,M,02:41:36,06:06:33 +25,W,02:23:55,06:06:34 +38,M,02:22:30,06:06:34 +27,W,02:32:46,06:06:35 +31,W,02:37:44,06:06:35 +30,M,02:36:25,06:06:39 +39,M,02:15:35,06:06:40 +31,W,02:18:04,06:06:41 +49,M,02:34:22,06:06:44 +61,M,02:23:39,06:06:46 +35,W,02:39:59,06:06:46 +51,M,02:29:37,06:06:46 +47,W,02:30:57,06:06:46 +53,W,02:32:29,06:06:47 +39,W,02:38:34,06:06:47 +44,W,02:35:02,06:06:48 +36,M,02:30:43,06:06:48 +35,M,02:24:16,06:06:48 +35,W,02:28:30,06:06:48 +35,W,02:40:38,06:06:49 +45,M,02:31:10,06:06:49 +28,W,02:25:02,06:06:51 +26,W,02:34:36,06:06:52 +55,M,02:38:23,06:06:52 +40,M,02:26:22,06:06:53 +46,M,02:19:15,06:06:53 +50,M,02:29:26,06:06:54 +28,M,02:04:29,06:06:54 +29,W,02:25:28,06:06:54 +38,W,02:32:42,06:06:54 +44,M,02:34:07,06:06:55 +60,M,02:26:44,06:06:56 +30,W,02:33:25,06:06:56 +34,W,02:34:45,06:06:57 +33,M,02:26:42,06:06:57 +49,W,02:40:46,06:06:58 +49,M,02:25:15,06:06:59 +49,W,02:22:37,06:06:59 +45,W,02:26:36,06:07:00 +38,W,02:26:45,06:07:01 +56,M,02:41:09,06:07:04 +44,W,02:51:19,06:07:04 +36,M,02:30:03,06:07:04 +50,W,02:28:18,06:07:05 +38,W,02:51:28,06:07:05 +31,M,02:32:48,06:07:08 +27,M,02:18:34,06:07:08 +71,M,02:29:45,06:07:08 +36,W,02:25:58,06:07:10 +32,W,02:44:13,06:07:11 +59,W,02:28:04,06:07:11 +41,M,02:25:00,06:07:11 +28,W,02:33:04,06:07:12 +31,W,02:07:57,06:07:12 +45,M,02:20:30,06:07:12 +47,W,02:19:57,06:07:15 +47,W,02:41:46,06:07:16 +36,M,02:37:54,06:07:17 +48,W,02:38:21,06:07:17 +34,W,02:27:35,06:07:17 +37,W,02:26:28,06:07:18 +44,W,02:23:20,06:07:18 +52,W,02:30:51,06:07:18 +23,M,02:18:53,06:07:18 +51,W,02:28:18,06:07:19 +58,W,02:33:31,06:07:19 +32,M,02:19:39,06:07:20 +56,M,02:26:43,06:07:21 +34,W,02:23:22,06:07:22 +49,M,02:37:07,06:07:22 +25,W,02:30:00,06:07:23 +45,M,02:44:16,06:07:24 +66,M,02:13:51,06:07:25 +40,M,02:20:01,06:07:26 +48,M,02:38:02,06:07:27 +20,W,02:39:44,06:07:27 +42,W,02:27:24,06:07:27 +46,M,02:26:17,06:07:27 +39,M,02:32:14,06:07:28 +28,W,02:29:26,06:07:28 +29,W,02:25:33,06:07:28 +49,M,02:36:35,06:07:29 +50,M,02:29:05,06:07:29 +49,M,02:42:58,06:07:30 +60,M,02:36:49,06:07:31 +33,W,02:41:46,06:07:31 +26,M,02:21:38,06:07:31 +58,M,02:21:04,06:07:31 +50,M,02:23:20,06:07:33 +64,M,02:32:24,06:07:34 +56,M,02:28:23,06:07:34 +43,M,02:33:25,06:07:35 +59,M,02:20:48,06:07:35 +36,M,02:34:28,06:07:36 +39,W,02:28:01,06:07:36 +35,M,02:19:55,06:07:38 +60,M,02:26:19,06:07:39 +55,M,02:18:01,06:07:40 +58,M,02:30:37,06:07:42 +47,W,02:31:32,06:07:42 +32,W,02:37:07,06:07:43 +37,W,02:29:28,06:07:43 +59,M,02:23:39,06:07:43 +45,M,02:28:59,06:07:44 +34,M,02:26:31,06:07:44 +38,M,02:30:06,06:07:44 +30,W,02:30:22,06:07:44 +26,W,02:41:14,06:07:45 +55,M,02:24:47,06:07:45 +44,W,02:28:57,06:07:46 +34,W,02:28:47,06:07:48 +53,M,02:40:05,06:07:48 +54,W,02:33:56,06:07:50 +31,M,02:32:51,06:07:51 +23,W,02:23:09,06:07:51 +59,M,02:42:13,06:07:51 +46,W,02:37:54,06:07:52 +60,M,02:24:56,06:07:53 +44,W,02:23:04,06:07:54 +54,W,02:25:45,06:07:55 +39,W,02:33:25,06:07:57 +28,W,02:29:09,06:07:57 +29,W,02:27:14,06:07:57 +45,W,02:30:42,06:07:57 +57,M,02:38:37,06:07:58 +45,W,02:42:00,06:07:58 +46,M,02:29:01,06:08:00 +30,W,02:31:39,06:08:00 +48,W,02:27:35,06:08:00 +44,M,02:21:57,06:08:00 +38,M,02:15:10,06:08:01 +41,W,02:48:18,06:08:01 +38,W,02:30:12,06:08:01 +47,M,02:22:29,06:08:01 +42,M,02:34:11,06:08:02 +42,W,02:24:07,06:08:02 +25,W,02:30:05,06:08:02 +54,W,02:31:20,06:08:03 +46,W,02:28:54,06:08:07 +57,M,02:39:27,06:08:07 +28,M,02:15:59,06:08:08 +37,M,02:11:25,06:08:09 +55,M,02:27:31,06:08:10 +49,M,02:30:43,06:08:10 +57,M,02:19:34,06:08:10 +53,M,02:19:37,06:08:11 +59,M,02:31:19,06:08:11 +49,W,02:28:25,06:08:12 +42,W,02:25:57,06:08:12 +44,M,02:16:45,06:08:12 +24,M,02:13:44,06:08:12 +56,M,02:28:23,06:08:13 +47,M,02:34:03,06:08:14 +46,M,02:29:55,06:08:14 +50,W,02:35:02,06:08:14 +58,M,02:26:26,06:08:15 +23,W,02:29:45,06:08:16 +52,W,02:37:14,06:08:17 +62,M,02:30:32,06:08:17 +33,M,02:32:52,06:08:20 +50,W,02:22:23,06:08:20 +60,M,02:35:07,06:08:21 +41,M,02:29:13,06:08:21 +42,W,02:39:56,06:08:23 +61,M,02:24:43,06:08:23 +71,M,02:06:04,06:08:23 +51,M,02:16:04,06:08:23 +39,M,02:33:21,06:08:24 +45,M,02:19:25,06:08:24 +30,M,02:15:04,06:08:25 +45,M,02:36:15,06:08:26 +42,M,02:17:06,06:08:26 +44,W,02:26:42,06:08:27 +50,M,02:25:55,06:08:27 +30,M,02:24:20,06:08:28 +55,M,02:26:56,06:08:29 +33,W,02:22:51,06:08:29 +42,W,02:28:29,06:08:29 +45,W,02:38:30,06:08:30 +64,M,02:49:45,06:08:30 +60,M,02:22:18,06:08:30 +37,M,02:39:08,06:08:31 +46,W,02:44:04,06:08:32 +36,M,02:35:31,06:08:33 +66,W,02:22:56,06:08:34 +37,W,02:38:29,06:08:35 +34,M,03:02:21,06:08:35 +45,M,02:21:00,06:08:35 +62,M,02:25:40,06:08:36 +30,M,02:34:01,06:08:36 +52,M,02:29:33,06:08:36 +56,M,02:43:16,06:08:38 +50,M,02:17:07,06:08:39 +54,M,02:49:08,06:08:39 +36,W,02:33:17,06:08:39 +28,W,01:58:27,06:08:40 +37,W,02:33:53,06:08:40 +40,W,02:28:05,06:08:41 +25,W,02:34:43,06:08:41 +44,M,02:37:19,06:08:42 +31,W,02:41:10,06:08:42 +25,W,02:28:46,06:08:42 +49,M,02:33:33,06:08:43 +69,M,02:14:24,06:08:43 +23,W,02:29:53,06:08:43 +30,W,02:24:53,06:08:43 +40,W,02:32:27,06:08:44 +32,M,02:30:55,06:08:44 +40,W,02:57:56,06:08:45 +41,W,02:42:53,06:08:46 +29,W,02:31:55,06:08:46 +47,M,02:22:36,06:08:48 +52,W,02:28:09,06:08:50 +27,W,02:30:28,06:08:50 +46,W,02:47:06,06:08:50 +60,M,02:42:40,06:08:51 +42,M,02:21:31,06:08:51 +23,W,02:40:06,06:08:52 +39,W,02:34:14,06:08:53 +43,M,02:48:38,06:08:54 +47,M,02:31:16,06:08:54 +44,M,02:24:47,06:08:54 +43,W,02:20:25,06:08:55 +40,W,02:36:58,06:08:56 +68,M,02:19:20,06:08:57 +48,M,02:36:43,06:08:58 +48,M,02:27:20,06:08:58 +28,M,02:16:07,06:08:58 +37,W,02:35:00,06:08:59 +32,W,02:28:29,06:08:59 +29,W,02:33:21,06:08:59 +44,W,02:33:17,06:08:59 +36,M,02:37:43,06:08:59 +47,W,02:31:24,06:09:01 +41,M,02:30:10,06:09:01 +46,W,02:21:56,06:09:01 +35,M,02:34:28,06:09:02 +39,W,02:41:54,06:09:04 +53,M,02:39:40,06:09:04 +55,M,02:28:24,06:09:05 +28,W,02:31:33,06:09:05 +38,M,02:23:32,06:09:05 +36,M,02:08:13,06:09:05 +42,M,02:18:07,06:09:06 +57,W,02:28:44,06:09:06 +22,W,02:27:06,06:09:06 +41,M,02:28:04,06:09:07 +28,W,02:24:19,06:09:07 +33,W,02:28:05,06:09:08 +41,M,02:32:49,06:09:08 +37,W,02:45:04,06:09:08 +44,W,02:28:01,06:09:09 +31,W,02:26:36,06:09:11 +37,W,02:13:40,06:09:11 +27,M,02:15:33,06:09:12 +46,M,02:29:49,06:09:12 +40,M,02:39:27,06:09:12 +70,M,02:27:19,06:09:13 +30,W,02:22:43,06:09:13 +49,W,02:26:55,06:09:13 +29,W,02:33:50,06:09:13 +57,M,02:40:04,06:09:14 +58,M,02:14:35,06:09:14 +41,W,02:39:46,06:09:15 +49,W,02:17:40,06:09:15 +62,M,02:18:29,06:09:15 +29,M,02:29:33,06:09:15 +47,M,02:30:05,06:09:18 +68,M,02:23:18,06:09:20 +33,M,02:43:10,06:09:20 +32,M,02:33:02,06:09:20 +35,W,02:43:27,06:09:24 +49,M,02:26:45,06:09:25 +31,W,02:33:41,06:09:26 +52,W,02:27:34,06:09:27 +46,W,02:40:30,06:09:27 +24,W,02:22:14,06:09:27 +25,M,02:38:23,06:09:27 +67,M,02:27:11,06:09:28 +26,M,02:34:16,06:09:29 +53,M,02:20:32,06:09:30 +36,W,02:13:34,06:09:31 +40,W,02:35:37,06:09:31 +46,W,02:30:14,06:09:32 +40,W,03:04:04,06:09:33 +48,W,02:34:45,06:09:35 +61,W,02:31:36,06:09:35 +35,W,02:34:39,06:09:35 +28,M,02:28:44,06:09:36 +38,M,02:24:14,06:09:36 +45,W,02:31:18,06:09:37 +35,M,02:16:26,06:09:38 +38,W,02:24:52,06:09:39 +40,W,02:29:32,06:09:40 +52,M,02:15:19,06:09:40 +50,W,02:30:00,06:09:43 +41,M,02:28:36,06:09:43 +41,M,02:13:50,06:09:43 +41,W,02:45:37,06:09:44 +31,M,02:26:56,06:09:44 +30,W,02:35:55,06:09:44 +43,W,02:32:40,06:09:44 +36,M,02:19:41,06:09:44 +73,M,02:15:22,06:09:45 +46,M,02:22:57,06:09:47 +40,M,02:36:53,06:09:47 +52,W,02:26:39,06:09:48 +38,W,02:28:17,06:09:48 +62,M,02:30:44,06:09:48 +31,M,02:30:59,06:09:49 +57,M,02:33:35,06:09:49 +31,M,02:30:56,06:09:50 +53,M,02:43:12,06:09:50 +45,W,02:22:40,06:09:50 +62,M,02:34:11,06:09:51 +34,M,02:22:44,06:09:53 +37,M,02:37:31,06:09:53 +56,M,02:27:50,06:09:53 +29,W,02:26:53,06:09:53 +44,W,02:38:57,06:09:54 +57,W,02:38:18,06:09:55 +62,M,02:30:12,06:09:55 +30,W,02:30:26,06:09:56 +49,M,02:17:53,06:09:56 +49,M,02:41:16,06:09:56 +43,W,02:36:08,06:09:57 +36,W,02:25:26,06:09:59 +71,M,02:50:10,06:10:00 +51,W,02:36:03,06:10:01 +56,W,02:33:27,06:10:02 +62,M,02:28:33,06:10:02 +50,M,02:24:29,06:10:06 +28,W,02:23:45,06:10:06 +55,W,02:34:44,06:10:08 +45,M,02:37:41,06:10:08 +28,M,02:35:15,06:10:08 +26,W,02:31:23,06:10:09 +32,M,02:31:52,06:10:09 +29,W,02:34:37,06:10:10 +25,W,02:33:00,06:10:10 +48,M,02:32:07,06:10:11 +34,W,02:29:40,06:10:11 +33,W,02:36:57,06:10:11 +27,W,02:15:17,06:10:12 +27,M,02:35:47,06:10:13 +52,M,02:44:37,06:10:14 +27,W,02:37:31,06:10:15 +25,M,02:35:27,06:10:15 +32,M,02:24:39,06:10:16 +58,M,02:26:03,06:10:17 +24,W,02:36:25,06:10:18 +42,M,02:22:12,06:10:21 +27,W,02:21:08,06:10:23 +36,M,02:25:46,06:10:24 +61,M,02:37:27,06:10:26 +40,W,02:34:38,06:10:27 +60,M,02:39:07,06:10:28 +43,M,02:42:01,06:10:28 +36,W,02:23:07,06:10:28 +39,M,02:38:39,06:10:28 +48,M,02:31:49,06:10:29 +43,W,02:37:00,06:10:29 +67,M,02:27:34,06:10:30 +51,W,02:27:41,06:10:30 +62,M,02:09:30,06:10:32 +63,M,02:26:12,06:10:33 +51,M,02:24:24,06:10:35 +28,W,02:28:52,06:10:36 +36,W,02:14:30,06:10:36 +43,W,02:23:10,06:10:38 +39,M,02:41:50,06:10:39 +36,M,02:15:04,06:10:40 +52,M,02:30:00,06:10:40 +39,W,02:45:29,06:10:40 +42,W,02:35:33,06:10:40 +40,M,02:29:54,06:10:41 +24,W,02:29:44,06:10:41 +43,M,02:48:27,06:10:42 +46,M,02:28:53,06:10:42 +34,W,02:39:11,06:10:43 +26,W,02:35:07,06:10:43 +28,W,02:28:11,06:10:45 +26,W,02:44:48,06:10:47 +42,M,02:27:19,06:10:47 +58,M,02:34:51,06:10:48 +66,W,02:32:36,06:10:48 +36,M,02:26:50,06:10:49 +37,W,02:48:44,06:10:50 +34,M,02:24:50,06:10:51 +39,W,02:24:18,06:10:51 +29,M,02:20:23,06:10:52 +43,M,02:37:17,06:10:53 +36,M,02:20:00,06:10:53 +43,M,02:30:14,06:10:54 +30,W,02:15:21,06:10:55 +43,W,02:28:10,06:10:56 +31,W,02:32:41,06:10:57 +41,W,02:44:15,06:10:59 +47,W,02:29:08,06:11:01 +22,M,02:31:10,06:11:01 +32,W,02:21:57,06:11:02 +47,M,02:24:16,06:11:02 +33,W,02:35:20,06:11:03 +43,W,02:36:40,06:11:03 +56,M,02:34:24,06:11:04 +36,W,02:23:06,06:11:07 +36,W,02:30:07,06:11:09 +42,W,02:46:10,06:11:09 +37,W,02:30:32,06:11:12 +32,M,02:30:17,06:11:13 +52,M,02:28:46,06:11:13 +20,M,02:40:53,06:11:14 +52,W,02:45:11,06:11:14 +34,W,02:19:02,06:11:17 +60,M,02:24:45,06:11:17 +51,M,02:38:20,06:11:17 +33,W,02:34:34,06:11:18 +30,W,02:32:13,06:11:19 +55,W,02:40:29,06:11:20 +30,W,02:22:44,06:11:21 +32,W,02:40:50,06:11:21 +43,M,02:29:46,06:11:22 +38,M,02:19:25,06:11:22 +38,W,02:38:22,06:11:24 +37,W,02:31:04,06:11:25 +48,M,02:29:19,06:11:26 +39,M,02:30:43,06:11:27 +35,M,02:13:37,06:11:28 +58,W,02:25:09,06:11:29 +42,W,02:38:24,06:11:30 +57,M,02:27:42,06:11:30 +24,W,02:32:32,06:11:31 +45,M,02:18:36,06:11:32 +38,W,02:24:43,06:11:33 +46,M,02:28:18,06:11:33 +56,M,02:31:23,06:11:33 +40,W,02:37:33,06:11:35 +36,W,02:35:16,06:11:37 +48,M,02:20:40,06:11:39 +29,W,02:38:05,06:11:43 +42,W,02:54:35,06:11:43 +32,M,02:29:26,06:11:44 +44,W,02:53:43,06:11:44 +39,M,02:34:34,06:11:44 +28,W,02:06:27,06:11:44 +62,M,02:44:31,06:11:45 +43,M,02:12:53,06:11:45 +43,W,02:29:30,06:11:46 +51,M,02:34:02,06:11:49 +51,M,02:33:30,06:11:50 +41,M,02:38:19,06:11:50 +40,M,02:26:37,06:11:50 +43,W,02:28:41,06:11:51 +69,M,02:34:26,06:11:53 +62,W,02:46:33,06:11:53 +63,M,02:20:58,06:11:53 +56,W,02:38:10,06:11:53 +53,M,02:34:30,06:11:55 +42,M,02:23:17,06:11:55 +31,M,02:32:49,06:11:56 +32,W,02:29:05,06:11:57 +72,M,03:03:36,06:11:57 +46,M,02:35:38,06:11:57 +63,M,02:35:31,06:11:58 +46,W,02:35:39,06:11:58 +40,W,02:36:49,06:11:58 +44,W,02:38:09,06:12:01 +23,M,02:26:52,06:12:01 +73,M,02:26:12,06:12:01 +42,W,02:40:06,06:12:02 +39,M,02:24:27,06:12:02 +36,W,02:20:59,06:12:03 +61,W,02:36:34,06:12:04 +46,M,02:25:50,06:12:05 +57,W,02:36:29,06:12:06 +45,M,02:25:37,06:12:07 +32,M,02:43:48,06:12:07 +33,W,02:25:50,06:12:08 +28,W,02:36:39,06:12:10 +28,W,02:41:24,06:12:13 +70,M,02:27:27,06:12:16 +56,W,02:44:40,06:12:18 +35,W,02:34:50,06:12:18 +41,W,02:31:04,06:12:19 +52,M,02:49:52,06:12:21 +36,W,02:37:10,06:12:21 +37,W,02:30:54,06:12:23 +57,M,02:17:11,06:12:23 +45,W,02:29:08,06:12:23 +42,W,02:34:47,06:12:23 +35,M,02:34:27,06:12:23 +29,W,02:31:32,06:12:25 +47,M,02:37:49,06:12:29 +50,W,02:35:42,06:12:29 +45,M,02:37:21,06:12:30 +33,W,02:31:33,06:12:31 +33,W,02:45:58,06:12:31 +47,W,02:29:44,06:12:32 +59,M,02:44:46,06:12:32 +24,W,02:27:50,06:12:33 +45,M,02:34:29,06:12:35 +42,M,02:30:56,06:12:36 +38,W,02:28:22,06:12:37 +30,W,02:29:19,06:12:38 +66,M,02:40:37,06:12:40 +56,M,02:29:47,06:12:41 +41,M,02:36:21,06:12:41 +27,M,02:42:35,06:12:42 +27,W,02:42:16,06:12:43 +29,W,02:31:55,06:12:43 +35,W,02:23:52,06:12:43 +46,M,02:38:05,06:12:44 +44,M,02:27:33,06:12:45 +22,W,02:24:06,06:12:47 +42,M,02:32:56,06:12:47 +29,M,02:38:01,06:12:48 +48,W,02:42:55,06:12:49 +24,W,02:36:22,06:12:50 +53,M,02:29:37,06:12:51 +50,M,02:21:26,06:12:51 +38,M,02:24:13,06:12:51 +48,M,02:38:35,06:12:52 +51,M,02:41:29,06:12:52 +60,M,02:35:57,06:12:54 +39,M,02:35:21,06:12:56 +39,M,02:29:10,06:12:56 +56,W,02:37:56,06:12:57 +54,M,02:18:50,06:12:57 +44,W,02:35:48,06:12:57 +59,M,02:27:53,06:12:59 +51,W,02:28:48,06:12:59 +26,W,02:18:40,06:13:00 +25,W,02:31:49,06:13:00 +29,M,02:40:36,06:13:01 +33,M,02:28:16,06:13:01 +65,W,02:35:45,06:13:03 +38,W,02:34:53,06:13:04 +24,W,02:31:02,06:13:07 +59,M,02:22:34,06:13:08 +62,W,02:40:14,06:13:09 +41,M,02:38:10,06:13:10 +32,W,02:31:28,06:13:12 +60,W,02:33:08,06:13:13 +36,W,02:36:46,06:13:13 +34,M,02:28:33,06:13:14 +58,W,02:29:00,06:13:15 +37,M,02:27:29,06:13:15 +38,M,02:35:26,06:13:15 +17,M,02:36:03,06:13:16 +45,W,02:36:10,06:13:16 +40,W,02:38:34,06:13:16 +62,M,02:25:15,06:13:17 +32,W,02:43:37,06:13:18 +53,M,02:27:25,06:13:19 +39,M,02:25:24,06:13:19 +39,M,02:28:20,06:13:19 +34,M,02:41:08,06:13:19 +35,W,02:35:22,06:13:20 +56,M,02:21:04,06:13:20 +24,W,02:22:22,06:13:22 +36,W,02:32:55,06:13:22 +45,M,02:33:55,06:13:23 +28,M,02:35:56,06:13:24 +54,M,02:34:24,06:13:24 +57,M,02:31:24,06:13:25 +45,W,02:32:53,06:13:25 +39,M,02:35:05,06:13:25 +32,M,02:28:35,06:13:26 +50,M,02:34:10,06:13:28 +54,W,02:32:21,06:13:29 +51,W,02:32:48,06:13:29 +51,M,02:19:06,06:13:31 +45,M,02:22:33,06:13:31 +35,M,02:27:00,06:13:31 +35,W,02:28:49,06:13:32 +27,M,02:35:25,06:13:32 +63,M,02:24:02,06:13:33 +36,W,02:33:19,06:13:33 +38,M,02:22:26,06:13:33 +34,M,02:13:21,06:13:36 +28,M,02:32:43,06:13:38 +40,M,02:15:33,06:13:39 +65,M,02:36:41,06:13:40 +32,W,02:39:57,06:13:40 +44,W,02:12:57,06:13:41 +37,M,02:16:16,06:13:41 +24,W,02:23:49,06:13:41 +56,M,02:38:03,06:13:41 +49,M,02:31:23,06:13:41 +40,W,02:29:58,06:13:43 +40,M,02:36:14,06:13:43 +39,W,02:42:43,06:13:44 +56,M,02:31:27,06:13:44 +57,M,02:27:56,06:13:46 +36,W,02:27:11,06:13:46 +59,M,02:19:31,06:13:46 +52,M,02:42:33,06:13:46 +34,M,02:27:06,06:13:47 +33,W,02:41:07,06:13:49 +54,M,02:31:35,06:13:51 +47,M,02:38:23,06:13:52 +39,W,02:41:21,06:13:52 +56,M,02:36:21,06:13:54 +44,M,02:35:53,06:13:54 +23,M,02:24:12,06:13:55 +53,W,02:33:28,06:13:55 +25,W,02:24:36,06:13:55 +27,W,02:50:19,06:13:57 +27,W,02:36:39,06:13:57 +38,W,02:40:51,06:13:59 +41,W,02:42:03,06:13:59 +59,W,02:48:19,06:14:00 +49,W,02:21:49,06:14:00 +51,W,02:36:55,06:14:00 +36,M,02:39:58,06:14:02 +24,W,02:30:09,06:14:02 +34,W,02:31:24,06:14:02 +56,M,02:31:56,06:14:03 +59,M,02:38:15,06:14:03 +24,M,02:17:33,06:14:04 +46,M,02:07:25,06:14:04 +60,M,02:42:57,06:14:05 +33,W,02:37:31,06:14:05 +41,W,02:44:28,06:14:06 +44,M,02:34:44,06:14:07 +42,M,02:17:57,06:14:07 +34,M,02:28:35,06:14:08 +40,M,02:34:11,06:14:09 +48,W,02:35:00,06:14:10 +40,M,02:34:25,06:14:10 +40,W,02:18:42,06:14:11 +51,W,02:36:40,06:14:11 +49,M,02:23:43,06:14:12 +63,M,02:29:05,06:14:14 +36,M,02:30:36,06:14:14 +61,M,02:36:41,06:14:14 +49,W,02:22:53,06:14:15 +55,W,02:32:11,06:14:16 +35,W,02:36:14,06:14:16 +45,W,02:26:22,06:14:17 +37,W,02:27:06,06:14:17 +29,M,02:31:50,06:14:19 +24,M,02:30:37,06:14:20 +57,M,02:30:34,06:14:20 +57,W,02:49:39,06:14:21 +55,W,02:44:57,06:14:22 +54,M,02:36:40,06:14:23 +31,W,02:28:40,06:14:26 +43,W,02:30:27,06:14:27 +46,M,02:25:12,06:14:28 +56,M,02:37:25,06:14:29 +48,M,02:30:31,06:14:29 +53,W,02:30:17,06:14:30 +49,W,02:36:23,06:14:32 +27,W,02:39:12,06:14:32 +31,W,02:33:13,06:14:32 +38,W,02:38:51,06:14:33 +35,W,02:38:00,06:14:34 +49,W,02:32:39,06:14:34 +54,W,02:49:45,06:14:35 +40,M,02:39:11,06:14:35 +51,M,02:23:03,06:14:35 +34,W,02:33:42,06:14:38 +31,M,02:14:09,06:14:39 +29,W,02:38:13,06:14:41 +51,W,02:33:15,06:14:41 +56,W,02:33:33,06:14:41 +34,W,02:31:00,06:14:43 +47,W,03:05:36,06:14:43 +39,W,02:36:55,06:14:46 +25,W,02:30:07,06:14:47 +36,W,02:26:48,06:14:49 +45,M,02:32:14,06:14:52 +44,M,02:33:50,06:14:52 +31,M,02:29:04,06:14:54 +48,M,02:32:11,06:14:56 +36,W,02:37:01,06:14:57 +69,M,02:33:48,06:14:57 +34,W,02:36:02,06:14:58 +58,M,02:32:43,06:14:59 +48,W,02:30:02,06:15:00 +23,W,02:32:50,06:15:06 +69,M,02:53:45,06:15:06 +63,M,02:15:18,06:15:06 +42,M,02:35:59,06:15:08 +32,M,02:37:08,06:15:08 +50,W,02:28:53,06:15:09 +46,W,02:28:04,06:15:10 +34,M,02:25:48,06:15:10 +40,M,02:31:55,06:15:10 +49,W,02:37:52,06:15:10 +44,W,02:34:54,06:15:12 +58,M,02:32:26,06:15:12 +38,W,02:48:03,06:15:13 +45,M,02:26:19,06:15:13 +34,M,02:28:14,06:15:13 +39,M,02:00:38,06:15:13 +51,W,02:37:15,06:15:15 +48,W,02:41:08,06:15:15 +53,M,02:35:31,06:15:15 +45,W,02:40:27,06:15:16 +21,M,02:29:27,06:15:16 +32,M,02:18:57,06:15:17 +51,W,02:27:49,06:15:18 +54,W,02:32:04,06:15:20 +56,M,02:24:55,06:15:20 +33,W,02:32:37,06:15:20 +36,W,02:32:05,06:15:21 +59,M,02:35:01,06:15:21 +43,M,02:26:33,06:15:22 +51,M,02:36:22,06:15:23 +27,M,02:43:46,06:15:24 +40,W,02:29:27,06:15:24 +34,W,02:24:46,06:15:25 +45,M,02:41:43,06:15:27 +51,M,02:44:10,06:15:27 +66,M,02:31:43,06:15:28 +45,M,02:23:30,06:15:28 +40,M,02:19:33,06:15:29 +55,M,02:31:48,06:15:29 +46,W,02:35:23,06:15:31 +45,M,02:29:16,06:15:31 +35,M,02:26:21,06:15:31 +45,M,02:24:27,06:15:31 +65,W,02:26:09,06:15:32 +68,W,02:43:49,06:15:32 +42,M,02:35:13,06:15:32 +34,W,02:36:55,06:15:33 +39,W,02:35:05,06:15:33 +42,W,02:38:10,06:15:34 +37,W,02:34:24,06:15:34 +43,W,02:27:35,06:15:35 +38,M,02:19:39,06:15:35 +59,W,02:34:41,06:15:36 +30,W,02:37:36,06:15:37 +53,M,02:27:40,06:15:38 +50,M,02:48:14,06:15:41 +24,W,02:34:33,06:15:41 +33,W,02:36:13,06:15:42 +48,M,02:40:40,06:15:42 +30,M,02:28:42,06:15:42 +49,W,02:40:51,06:15:44 +30,M,02:21:21,06:15:44 +32,M,02:34:00,06:15:44 +29,W,02:19:04,06:15:44 +42,M,02:29:00,06:15:45 +43,W,02:40:05,06:15:45 +38,M,02:37:52,06:15:46 +46,W,02:32:06,06:15:46 +42,W,02:36:36,06:15:47 +53,M,02:40:03,06:15:47 +53,M,02:39:29,06:15:47 +72,M,02:43:22,06:15:48 +33,M,02:21:35,06:15:48 +36,W,02:18:18,06:15:48 +30,W,02:30:24,06:15:48 +35,W,02:32:23,06:15:49 +39,M,02:36:27,06:15:49 +47,M,02:35:27,06:15:51 +55,M,02:25:31,06:15:55 +52,M,02:22:48,06:15:55 +36,M,02:21:52,06:15:56 +68,M,02:26:21,06:15:56 +35,W,02:35:15,06:16:00 +39,M,02:23:39,06:16:00 +41,W,02:30:25,06:16:00 +38,W,02:43:50,06:16:01 +31,W,02:37:25,06:16:02 +45,M,02:30:09,06:16:03 +23,W,02:31:58,06:16:04 +52,M,02:41:55,06:16:06 +42,W,02:34:03,06:16:06 +52,W,02:44:38,06:16:07 +41,W,02:34:53,06:16:08 +39,M,02:36:36,06:16:08 +51,W,02:34:07,06:16:09 +52,M,02:23:33,06:16:10 +53,W,02:33:16,06:16:10 +36,M,02:28:12,06:16:12 +58,M,02:15:24,06:16:13 +31,W,02:35:38,06:16:13 +43,W,02:42:13,06:16:14 +59,M,02:40:34,06:16:14 +27,W,02:30:02,06:16:14 +25,W,02:44:28,06:16:16 +40,W,02:44:56,06:16:16 +50,M,02:35:59,06:16:17 +27,M,02:27:22,06:16:18 +49,M,02:39:22,06:16:18 +35,M,02:39:55,06:16:18 +31,W,02:35:22,06:16:19 +51,M,02:19:10,06:16:19 +34,M,02:47:15,06:16:20 +45,W,02:27:26,06:16:22 +45,W,02:36:25,06:16:26 +70,M,02:44:01,06:16:26 +50,M,02:41:09,06:16:27 +48,M,02:15:35,06:16:27 +32,W,02:45:31,06:16:29 +27,M,02:47:32,06:16:31 +48,W,02:37:04,06:16:31 +55,W,02:41:15,06:16:32 +36,W,02:32:40,06:16:32 +34,M,02:42:04,06:16:34 +50,M,02:22:41,06:16:36 +32,W,02:38:17,06:16:36 +50,M,02:49:06,06:16:36 +41,M,02:23:58,06:16:37 +48,M,02:41:07,06:16:37 +36,W,02:24:13,06:16:38 +40,W,02:54:15,06:16:41 +31,M,02:17:01,06:16:43 +42,M,02:20:25,06:16:43 +55,M,02:34:08,06:16:43 +54,W,02:34:19,06:16:44 +50,M,02:28:36,06:16:44 +37,W,02:27:58,06:16:45 +52,W,02:48:50,06:16:45 +45,W,02:33:52,06:16:46 +39,M,02:19:16,06:16:46 +55,W,02:30:30,06:16:47 +18,M,02:31:40,06:16:47 +34,W,02:41:14,06:16:47 +48,W,02:35:09,06:16:48 +41,W,02:37:18,06:16:49 +39,W,02:27:56,06:16:49 +23,W,02:36:09,06:16:50 +49,M,02:40:48,06:16:52 +48,M,02:37:27,06:16:52 +54,M,02:36:27,06:16:52 +34,W,02:48:47,06:16:53 +30,W,02:30:29,06:16:55 +36,M,02:34:15,06:16:56 +38,M,02:44:12,06:16:58 +54,M,02:37:10,06:16:58 +28,M,02:23:41,06:16:58 +39,W,02:43:41,06:16:59 +52,W,02:35:48,06:17:00 +56,W,02:33:11,06:17:00 +52,M,02:43:58,06:17:01 +41,M,02:27:13,06:17:01 +51,M,02:20:14,06:17:04 +48,W,02:42:26,06:17:05 +39,W,02:34:23,06:17:05 +57,W,02:34:49,06:17:06 +41,W,02:48:41,06:17:07 +57,M,02:26:01,06:17:07 +55,W,02:48:57,06:17:07 +38,M,02:39:18,06:17:07 +47,W,02:52:48,06:17:08 +61,W,02:38:28,06:17:08 +56,M,02:31:27,06:17:08 +27,W,02:15:44,06:17:09 +35,W,02:31:42,06:17:10 +60,M,02:26:34,06:17:13 +33,M,02:37:01,06:17:13 +35,W,02:16:06,06:17:13 +52,M,02:24:31,06:17:14 +52,M,02:35:33,06:17:14 +47,W,02:36:14,06:17:19 +50,M,02:35:21,06:17:21 +40,M,02:32:47,06:17:22 +53,M,02:35:47,06:17:22 +46,W,02:34:58,06:17:23 +38,W,02:29:30,06:17:24 +46,M,02:32:29,06:17:24 +28,W,02:33:57,06:17:25 +30,W,02:38:44,06:17:26 +51,W,02:28:49,06:17:27 +35,W,02:40:02,06:17:27 +42,M,02:42:26,06:17:28 +34,W,02:36:27,06:17:28 +39,M,02:38:13,06:17:29 +62,M,02:37:20,06:17:29 +45,M,02:28:11,06:17:30 +24,M,02:32:10,06:17:30 +32,M,02:24:28,06:17:31 +45,W,02:35:26,06:17:32 +29,W,02:23:51,06:17:33 +43,M,02:14:34,06:17:34 +40,W,02:34:29,06:17:36 +26,W,02:36:20,06:17:37 +67,W,02:27:09,06:17:38 +32,M,02:23:40,06:17:39 +35,W,02:26:47,06:17:39 +72,M,02:26:48,06:17:40 +59,W,02:50:35,06:17:40 +33,W,02:35:43,06:17:42 +25,M,02:31:35,06:17:43 +59,M,02:39:21,06:17:46 +46,W,02:43:22,06:17:47 +27,W,02:38:45,06:17:47 +53,W,02:20:13,06:17:48 +23,W,02:31:15,06:17:49 +24,W,02:37:05,06:17:50 +26,M,02:22:43,06:17:50 +39,W,02:41:52,06:17:50 +36,W,02:33:59,06:17:52 +28,W,02:41:07,06:17:55 +24,W,02:27:37,06:17:56 +31,M,02:38:00,06:17:56 +49,M,02:40:13,06:17:58 +40,W,02:40:45,06:17:59 +54,M,02:27:26,06:17:59 +48,M,02:26:17,06:17:59 +33,W,02:37:20,06:17:59 +43,W,02:44:44,06:18:01 +35,W,02:33:35,06:18:01 +60,M,02:24:12,06:18:03 +59,W,02:40:29,06:18:03 +53,W,02:51:54,06:18:05 +39,W,02:36:43,06:18:07 +30,M,02:24:26,06:18:07 +40,M,02:29:23,06:18:07 +45,W,02:36:00,06:18:07 +39,M,02:21:02,06:18:07 +54,W,02:28:36,06:18:08 +36,W,02:35:49,06:18:08 +52,M,02:44:44,06:18:08 +40,W,02:27:03,06:18:08 +44,M,02:34:59,06:18:09 +41,W,02:36:00,06:18:11 +48,M,02:29:12,06:18:11 +49,W,02:36:03,06:18:11 +41,W,02:40:09,06:18:14 +49,W,02:31:38,06:18:15 +28,W,02:30:21,06:18:16 +57,M,02:31:51,06:18:16 +38,M,02:28:41,06:18:16 +36,W,02:28:47,06:18:18 +37,M,02:39:20,06:18:18 +34,W,02:37:22,06:18:18 +42,W,02:36:53,06:18:20 +42,M,02:36:55,06:18:20 +28,W,02:30:17,06:18:21 +32,W,02:35:49,06:18:22 +37,W,02:34:24,06:18:24 +52,W,02:23:58,06:18:24 +48,W,02:57:29,06:18:25 +39,W,02:38:46,06:18:26 +42,W,02:38:08,06:18:26 +50,M,02:46:29,06:18:26 +59,M,02:44:32,06:18:27 +45,M,02:39:20,06:18:27 +60,W,02:44:35,06:18:31 +33,W,02:35:46,06:18:32 +37,W,02:39:57,06:18:35 +32,M,02:48:20,06:18:35 +53,W,02:36:53,06:18:36 +30,W,02:37:25,06:18:36 +48,W,02:30:56,06:18:37 +51,W,02:28:30,06:18:38 +47,M,02:33:54,06:18:38 +26,M,02:31:58,06:18:38 +27,M,02:29:08,06:18:39 +46,W,02:33:06,06:18:40 +55,W,02:34:48,06:18:40 +52,W,02:28:05,06:18:40 +52,W,02:34:55,06:18:41 +58,W,02:31:36,06:18:43 +27,W,02:32:44,06:18:43 +53,M,02:29:35,06:18:43 +30,W,02:31:15,06:18:43 +30,W,02:45:51,06:18:44 +59,W,02:46:58,06:18:44 +45,M,02:28:24,06:18:44 +33,M,02:05:37,06:18:45 +50,M,02:27:27,06:18:45 +59,M,02:30:47,06:18:46 +49,W,02:35:34,06:18:46 +46,M,02:09:25,06:18:46 +47,W,02:35:07,06:18:46 +50,W,02:41:10,06:18:47 +33,W,02:34:08,06:18:48 +52,M,02:29:25,06:18:50 +33,W,02:37:57,06:18:51 +31,W,02:36:13,06:18:52 +57,W,02:40:01,06:18:53 +56,M,02:03:26,06:18:53 +44,W,02:44:44,06:18:56 +33,W,02:44:13,06:18:56 +59,M,02:21:56,06:18:58 +44,W,02:26:31,06:19:00 +37,W,02:22:51,06:19:01 +35,W,02:30:36,06:19:01 +56,W,02:31:05,06:19:04 +57,M,02:23:30,06:19:05 +29,W,02:35:32,06:19:06 +26,W,02:22:46,06:19:07 +47,W,02:46:48,06:19:07 +60,M,02:30:15,06:19:09 +34,M,02:36:30,06:19:09 +47,W,02:34:44,06:19:10 +52,M,02:20:10,06:19:10 +26,W,02:46:12,06:19:10 +35,W,02:42:46,06:19:10 +57,W,02:48:46,06:19:11 +25,W,02:21:24,06:19:13 +32,W,02:44:16,06:19:16 +63,M,02:30:34,06:19:18 +32,M,02:34:42,06:19:19 +41,W,02:32:42,06:19:21 +46,W,02:33:23,06:19:21 +33,M,02:30:42,06:19:21 +42,M,02:33:49,06:19:22 +51,M,02:25:31,06:19:24 +49,M,02:25:49,06:19:24 +45,M,02:25:48,06:19:24 +43,W,02:48:08,06:19:24 +31,M,02:26:33,06:19:26 +23,W,02:42:48,06:19:27 +52,M,02:44:03,06:19:27 +33,W,02:39:47,06:19:28 +45,W,02:47:15,06:19:28 +47,W,02:35:24,06:19:29 +64,M,02:30:28,06:19:30 +55,M,02:35:39,06:19:30 +38,W,02:40:35,06:19:30 +59,W,02:35:48,06:19:31 +40,W,02:49:59,06:19:32 +46,M,02:24:47,06:19:33 +58,W,02:33:49,06:19:33 +45,W,02:33:43,06:19:34 +50,W,02:25:32,06:19:34 +35,W,02:40:25,06:19:36 +39,M,02:31:55,06:19:40 +24,W,02:17:36,06:19:40 +57,M,02:31:57,06:19:43 +32,W,02:31:52,06:19:43 +30,W,02:42:17,06:19:43 +40,M,02:38:24,06:19:44 +37,W,02:25:43,06:19:44 +41,M,02:31:11,06:19:44 +53,W,02:43:06,06:19:45 +48,M,02:35:23,06:19:46 +36,W,02:35:27,06:19:46 +45,W,02:40:57,06:19:47 +44,M,02:39:52,06:19:47 +38,W,02:30:05,06:19:48 +33,W,02:35:54,06:19:49 +45,M,02:33:31,06:19:50 +53,M,02:37:09,06:19:50 +42,M,02:28:34,06:19:52 +51,M,02:33:47,06:19:53 +32,M,02:45:09,06:19:54 +44,W,02:29:14,06:19:54 +46,W,02:48:08,06:19:55 +38,M,02:38:04,06:19:55 +40,M,02:33:55,06:19:55 +25,M,02:48:15,06:19:55 +36,W,02:29:25,06:19:57 +49,M,02:34:36,06:19:59 +58,M,02:25:18,06:20:00 +42,M,02:33:32,06:20:00 +28,W,02:29:30,06:20:01 +56,W,02:32:11,06:20:02 +57,M,02:41:53,06:20:02 +33,M,02:11:57,06:20:05 +50,M,02:26:00,06:20:05 +46,W,02:20:03,06:20:05 +44,M,02:45:15,06:20:06 +35,M,02:46:57,06:20:08 +50,W,02:37:14,06:20:08 +48,M,02:24:56,06:20:09 +43,W,02:35:25,06:20:09 +65,M,02:50:11,06:20:09 +47,W,02:35:50,06:20:10 +53,M,02:29:59,06:20:10 +53,M,02:33:34,06:20:11 +60,W,02:44:50,06:20:13 +45,M,02:36:57,06:20:13 +42,W,02:37:58,06:20:14 +50,W,02:40:07,06:20:16 +52,M,02:35:57,06:20:16 +31,M,02:06:32,06:20:18 +66,M,02:32:14,06:20:18 +47,M,02:38:21,06:20:18 +41,W,02:25:28,06:20:19 +51,M,02:35:09,06:20:19 +36,W,02:29:20,06:20:19 +39,M,02:38:49,06:20:20 +35,M,02:40:37,06:20:21 +43,W,02:38:04,06:20:21 +36,W,02:29:46,06:20:22 +52,M,02:27:10,06:20:22 +56,M,02:30:43,06:20:22 +23,M,02:16:55,06:20:24 +40,W,02:31:09,06:20:24 +52,W,02:32:21,06:20:24 +49,W,02:47:17,06:20:27 +52,M,02:25:59,06:20:28 +65,M,02:40:18,06:20:28 +53,M,02:32:21,06:20:29 +48,M,02:19:21,06:20:30 +44,W,02:46:27,06:20:31 +22,M,02:24:36,06:20:33 +28,W,02:24:58,06:20:33 +52,M,02:32:48,06:20:33 +30,M,02:36:59,06:20:36 +27,W,02:57:02,06:20:36 +54,W,02:32:55,06:20:37 +41,M,02:40:36,06:20:38 +40,W,02:30:12,06:20:40 +42,M,02:38:59,06:20:40 +23,M,02:11:42,06:20:40 +30,W,02:43:45,06:20:41 +47,M,02:40:04,06:20:41 +46,W,02:36:08,06:20:42 +53,W,02:46:02,06:20:43 +30,W,02:30:47,06:20:44 +34,W,02:37:05,06:20:45 +41,M,02:33:39,06:20:45 +37,W,02:30:27,06:20:47 +30,M,02:16:03,06:20:48 +29,W,02:30:22,06:20:49 +54,M,02:36:36,06:20:49 +46,W,02:35:32,06:20:51 +62,M,02:50:00,06:20:51 +37,W,02:37:29,06:20:51 +48,M,02:36:56,06:20:51 +58,M,02:27:56,06:20:51 +47,W,02:29:54,06:20:51 +45,W,02:40:43,06:20:51 +41,M,02:24:29,06:20:52 +42,W,02:39:57,06:20:55 +61,W,02:52:08,06:20:55 +49,M,02:24:50,06:20:56 +51,M,02:34:46,06:21:00 +36,W,02:25:49,06:21:01 +54,M,02:31:20,06:21:03 +35,M,02:29:29,06:21:03 +43,W,02:39:24,06:21:04 +40,W,02:36:46,06:21:05 +31,W,02:38:55,06:21:08 +42,W,02:29:28,06:21:10 +29,M,02:25:06,06:21:11 +37,M,02:32:20,06:21:12 +26,W,02:39:30,06:21:12 +60,M,02:25:46,06:21:12 +50,M,02:23:42,06:21:12 +51,W,02:34:17,06:21:14 +29,W,02:45:21,06:21:14 +23,W,02:40:54,06:21:15 +55,W,02:28:45,06:21:15 +27,W,02:37:12,06:21:16 +37,W,02:36:28,06:21:18 +53,M,02:36:03,06:21:21 +34,M,02:41:06,06:21:22 +24,W,02:39:49,06:21:22 +59,W,02:39:23,06:21:22 +48,W,02:36:31,06:21:23 +56,M,02:32:24,06:21:23 +41,M,02:30:46,06:21:23 +57,W,02:29:13,06:21:23 +45,M,02:05:44,06:21:24 +54,M,02:32:54,06:21:25 +40,W,02:28:02,06:21:25 +29,W,02:44:04,06:21:25 +33,M,02:33:22,06:21:27 +37,M,02:37:08,06:21:28 +49,M,02:28:17,06:21:28 +29,W,02:30:09,06:21:28 +28,W,02:38:01,06:21:29 +55,M,02:31:32,06:21:36 +57,M,02:15:25,06:21:37 +40,M,02:31:05,06:21:38 +50,M,02:26:29,06:21:38 +35,W,02:46:06,06:21:40 +63,M,02:34:54,06:21:40 +64,M,02:32:30,06:21:41 +45,M,02:30:10,06:21:42 +48,M,02:23:56,06:21:43 +26,W,02:40:28,06:21:43 +32,W,02:45:23,06:21:46 +58,W,02:33:53,06:21:47 +46,M,02:38:27,06:21:47 +32,M,02:33:33,06:21:47 +63,M,02:43:26,06:21:48 +33,W,02:30:08,06:21:50 +42,M,02:36:11,06:21:50 +35,W,02:42:48,06:21:51 +34,W,02:42:02,06:21:52 +61,M,02:43:13,06:21:52 +52,M,02:32:06,06:21:52 +28,M,02:24:51,06:21:53 +55,M,02:33:57,06:21:54 +57,W,02:35:25,06:21:55 +50,W,02:40:09,06:21:55 +44,W,02:47:42,06:21:56 +39,M,02:23:12,06:21:56 +61,W,02:37:16,06:21:57 +32,M,02:27:22,06:21:58 +28,W,02:25:33,06:21:58 +42,W,02:44:36,06:22:01 +43,W,02:08:30,06:22:02 +33,M,02:06:36,06:22:02 +70,M,02:57:36,06:22:03 +61,M,02:28:27,06:22:04 +20,M,02:47:37,06:22:06 +59,W,02:42:52,06:22:06 +46,W,02:47:18,06:22:07 +39,W,02:44:16,06:22:07 +41,W,02:36:27,06:22:07 +25,M,02:39:14,06:22:08 +45,M,02:42:09,06:22:09 +42,W,02:35:06,06:22:09 +43,M,02:30:28,06:22:09 +41,W,02:22:51,06:22:10 +37,W,02:48:18,06:22:11 +26,M,02:27:54,06:22:13 +26,M,02:20:58,06:22:14 +59,M,02:20:00,06:22:15 +64,M,02:32:10,06:22:16 +50,M,02:34:56,06:22:17 +44,M,02:38:03,06:22:17 +25,W,02:28:05,06:22:17 +59,M,02:44:49,06:22:19 +39,M,02:26:54,06:22:21 +34,M,02:26:50,06:22:22 +43,W,02:36:44,06:22:24 +39,M,02:21:12,06:22:24 +36,M,02:32:38,06:22:26 +52,W,02:29:27,06:22:27 +60,M,02:34:25,06:22:28 +55,M,02:50:49,06:22:29 +68,M,02:28:00,06:22:31 +41,M,02:40:41,06:22:33 +24,M,02:12:20,06:22:35 +25,W,02:30:54,06:22:38 +46,W,02:24:43,06:22:38 +45,M,02:50:27,06:22:39 +46,M,02:14:55,06:22:40 +39,W,02:33:40,06:22:40 +36,M,02:37:58,06:22:41 +55,M,02:46:09,06:22:41 +40,W,02:28:59,06:22:42 +51,W,02:41:51,06:22:43 +43,W,02:43:07,06:22:43 +55,M,02:42:01,06:22:44 +60,M,02:28:57,06:22:44 +48,W,02:47:45,06:22:45 +31,W,02:31:04,06:22:45 +30,W,02:43:16,06:22:45 +51,M,02:18:14,06:22:47 +24,W,02:35:04,06:22:52 +25,W,02:30:37,06:22:52 +31,M,02:34:27,06:22:53 +34,M,02:24:12,06:22:55 +20,M,02:39:47,06:22:57 +34,M,02:29:45,06:22:57 +49,M,02:41:50,06:23:00 +55,M,02:47:49,06:23:01 +61,M,02:33:33,06:23:03 +25,W,02:34:43,06:23:03 +45,W,02:36:22,06:23:03 +43,W,02:30:15,06:23:04 +25,M,02:34:45,06:23:04 +43,W,02:35:58,06:23:04 +50,M,02:18:06,06:23:04 +48,M,03:01:43,06:23:04 +33,M,02:37:56,06:23:06 +33,W,02:36:08,06:23:08 +62,W,02:52:49,06:23:08 +32,W,02:41:30,06:23:09 +77,M,02:31:41,06:23:09 +47,W,02:45:00,06:23:10 +38,M,02:18:46,06:23:10 +53,M,02:24:39,06:23:11 +25,M,02:28:59,06:23:11 +18,M,02:40:39,06:23:12 +18,W,02:17:29,06:23:12 +29,W,02:30:22,06:23:13 +65,M,02:42:54,06:23:13 +35,M,02:18:54,06:23:13 +47,M,02:26:17,06:23:15 +32,M,02:26:33,06:23:16 +46,M,02:35:01,06:23:16 +47,M,02:39:31,06:23:16 +30,M,02:29:30,06:23:17 +32,M,02:29:29,06:23:17 +44,W,02:36:38,06:23:19 +59,M,02:38:05,06:23:20 +36,M,02:20:15,06:23:21 +27,M,02:20:56,06:23:22 +35,W,02:35:49,06:23:23 +26,W,02:30:07,06:23:23 +58,W,02:45:39,06:23:24 +28,W,02:33:46,06:23:26 +54,M,02:51:28,06:23:29 +34,W,02:27:31,06:23:29 +32,W,02:51:44,06:23:30 +21,W,02:34:29,06:23:30 +62,M,02:31:12,06:23:33 +40,W,02:57:06,06:23:33 +32,W,02:41:13,06:23:34 +39,W,02:36:56,06:23:35 +32,W,02:43:21,06:23:36 +40,W,02:34:30,06:23:36 +31,W,02:44:39,06:23:38 +33,W,02:45:09,06:23:39 +65,M,02:18:14,06:23:40 +52,M,02:29:17,06:23:40 +27,M,02:28:19,06:23:40 +60,W,02:27:34,06:23:41 +35,W,02:35:37,06:23:41 +39,M,02:41:14,06:23:41 +68,M,02:27:44,06:23:42 +33,W,02:36:53,06:23:42 +34,M,02:37:10,06:23:42 +36,M,02:36:54,06:23:43 +50,M,02:25:36,06:23:44 +38,W,02:34:57,06:23:44 +31,W,02:28:34,06:23:44 +32,M,02:30:48,06:23:45 +24,W,02:32:53,06:23:46 +64,M,02:46:14,06:23:47 +24,W,02:41:03,06:23:48 +28,W,02:30:30,06:23:49 +50,M,02:51:25,06:23:51 +45,M,02:57:11,06:23:52 +36,W,02:34:14,06:23:52 +56,W,02:32:25,06:23:53 +45,W,02:39:41,06:23:53 +51,M,02:39:15,06:23:53 +36,M,02:22:56,06:23:54 +51,M,02:37:24,06:23:54 +42,M,02:24:40,06:23:55 +68,M,02:43:25,06:23:57 +44,M,02:39:34,06:23:57 +47,M,02:35:53,06:23:57 +43,W,02:32:09,06:23:58 +47,W,02:31:45,06:23:58 +30,M,02:30:41,06:23:59 +35,M,02:18:35,06:23:59 +62,M,02:45:18,06:23:59 +35,M,02:37:06,06:23:59 +46,W,02:42:09,06:24:00 +32,W,02:41:08,06:24:00 +29,W,02:43:05,06:24:00 +29,W,02:31:45,06:24:01 +41,M,02:29:55,06:24:02 +41,M,02:30:29,06:24:02 +28,W,02:37:45,06:24:02 +64,W,02:27:48,06:24:06 +61,W,02:32:19,06:24:07 +23,M,02:20:35,06:24:07 +31,W,02:34:35,06:24:09 +50,W,02:34:28,06:24:10 +29,M,02:33:37,06:24:12 +33,M,02:34:03,06:24:14 +37,W,02:56:08,06:24:14 +47,M,02:35:58,06:24:15 +69,M,02:35:55,06:24:16 +54,M,02:46:04,06:24:16 +40,W,02:39:16,06:24:17 +43,W,02:35:57,06:24:19 +26,W,02:46:58,06:24:22 +48,M,02:37:13,06:24:23 +25,W,02:26:03,06:24:25 +41,M,02:35:19,06:24:25 +46,W,02:33:12,06:24:26 +29,M,02:31:25,06:24:28 +34,M,02:34:17,06:24:28 +29,W,02:40:22,06:24:29 +35,M,02:38:24,06:24:29 +25,M,02:18:29,06:24:30 +45,M,02:42:10,06:24:31 +45,W,02:33:33,06:24:32 +28,W,02:42:28,06:24:32 +43,M,02:31:30,06:24:32 +66,M,02:35:32,06:24:36 +29,M,02:33:03,06:24:36 +46,W,02:30:43,06:24:36 +33,W,02:35:38,06:24:38 +41,W,02:40:58,06:24:38 +59,M,02:47:55,06:24:39 +50,W,02:45:31,06:24:41 +41,M,02:52:38,06:24:42 +31,W,02:38:23,06:24:44 +30,M,02:25:44,06:24:45 +32,M,02:30:03,06:24:47 +41,W,02:44:41,06:24:47 +52,M,02:39:34,06:24:47 +33,W,02:21:16,06:24:48 +34,W,02:49:51,06:24:50 +46,W,02:34:03,06:24:50 +34,W,02:37:07,06:24:52 +54,M,02:32:22,06:24:53 +32,M,02:32:19,06:24:55 +51,W,02:33:53,06:24:57 +35,W,02:45:23,06:24:57 +26,W,02:37:38,06:24:57 +46,W,02:32:46,06:24:57 +51,M,02:28:25,06:24:57 +53,W,02:38:04,06:24:59 +50,W,02:42:52,06:24:59 +26,M,02:44:16,06:25:01 +43,W,02:41:16,06:25:03 +49,W,03:25:46,06:25:03 +39,M,02:34:17,06:25:03 +27,M,02:35:56,06:25:04 +35,W,02:13:22,06:25:05 +64,M,02:31:13,06:25:05 +65,M,02:35:15,06:25:07 +27,W,02:32:12,06:25:08 +27,M,02:37:37,06:25:08 +42,M,02:34:21,06:25:08 +43,W,02:37:26,06:25:11 +61,M,02:26:44,06:25:13 +36,M,02:39:10,06:25:14 +66,M,02:53:54,06:25:14 +54,W,02:44:13,06:25:21 +36,M,02:51:41,06:25:21 +30,W,02:18:07,06:25:22 +31,M,02:16:52,06:25:22 +36,W,02:39:03,06:25:23 +50,M,02:31:46,06:25:23 +51,M,02:28:13,06:25:24 +30,W,02:38:17,06:25:25 +44,W,02:37:47,06:25:28 +30,W,02:34:14,06:25:32 +39,M,02:26:33,06:25:33 +37,M,02:41:26,06:25:34 +29,W,02:33:26,06:25:34 +58,W,02:43:49,06:25:35 +45,M,02:50:38,06:25:37 +67,M,02:27:04,06:25:37 +53,M,02:42:01,06:25:38 +54,M,02:42:00,06:25:39 +41,W,02:38:25,06:25:39 +66,M,02:32:12,06:25:40 +30,M,02:24:08,06:25:41 +42,M,02:33:35,06:25:44 +27,W,02:37:42,06:25:46 +49,W,02:34:41,06:25:46 +65,M,02:46:44,06:25:47 +27,W,02:35:44,06:25:47 +50,W,02:29:02,06:25:48 +68,W,02:46:08,06:25:48 +35,W,02:50:57,06:25:48 +59,M,02:29:47,06:25:50 +34,W,02:31:36,06:25:50 +31,W,02:35:40,06:25:51 +31,W,02:32:52,06:25:53 +31,M,02:45:33,06:25:53 +30,W,02:39:45,06:25:56 +40,W,02:37:10,06:25:57 +43,M,02:36:06,06:25:58 +48,W,02:34:17,06:25:58 +36,W,02:43:25,06:25:59 +32,W,02:40:25,06:25:59 +32,W,02:40:14,06:26:00 +32,W,02:41:11,06:26:01 +58,M,02:32:16,06:26:04 +29,W,02:29:23,06:26:04 +35,W,02:28:12,06:26:04 +56,M,02:34:47,06:26:04 +28,W,02:10:08,06:26:06 +46,W,02:37:25,06:26:06 +37,M,02:27:34,06:26:06 +39,M,02:37:28,06:26:07 +45,W,02:38:16,06:26:08 +31,W,02:46:02,06:26:10 +46,M,02:32:55,06:26:11 +40,W,02:25:01,06:26:12 +54,W,02:44:22,06:26:15 +41,W,02:35:48,06:26:15 +26,W,02:37:48,06:26:16 +30,M,02:46:18,06:26:17 +43,W,02:39:24,06:26:17 +41,W,02:43:48,06:26:17 +57,W,02:42:13,06:26:17 +25,W,02:44:10,06:26:18 +50,W,02:36:46,06:26:21 +37,W,02:32:29,06:26:21 +67,M,02:35:17,06:26:22 +53,W,02:35:35,06:26:23 +29,W,02:32:29,06:26:25 +42,W,02:48:09,06:26:26 +40,W,02:40:18,06:26:27 +52,M,02:26:04,06:26:29 +61,M,02:34:04,06:26:30 +30,M,02:32:53,06:26:32 +32,M,02:29:49,06:26:33 +55,W,02:58:46,06:26:33 +22,W,02:32:43,06:26:34 +28,M,02:49:10,06:26:35 +40,M,02:29:29,06:26:35 +31,W,02:41:19,06:26:36 +39,W,02:36:07,06:26:36 +37,M,02:39:28,06:26:37 +38,W,02:41:54,06:26:39 +43,W,02:25:57,06:26:39 +53,W,02:44:20,06:26:40 +58,M,02:43:52,06:26:40 +53,W,02:36:03,06:26:40 +66,M,02:34:48,06:26:41 +26,W,02:34:48,06:26:42 +22,W,02:55:07,06:26:42 +38,W,02:34:30,06:26:44 +35,M,02:46:24,06:26:46 +33,W,02:17:33,06:26:47 +46,W,02:42:16,06:26:47 +52,M,02:25:01,06:26:49 +39,W,02:34:23,06:26:53 +51,M,02:26:28,06:26:53 +31,W,02:30:48,06:26:55 +29,M,02:43:25,06:26:55 +65,M,02:35:04,06:26:56 +41,W,02:18:12,06:26:57 +51,M,02:26:34,06:26:58 +66,M,02:38:41,06:26:58 +71,W,02:51:31,06:26:59 +58,W,02:37:03,06:27:00 +34,W,02:11:21,06:27:02 +43,M,02:36:42,06:27:03 +36,W,02:53:34,06:27:03 +24,M,02:22:40,06:27:04 +60,M,02:33:58,06:27:04 +24,W,02:34:27,06:27:04 +57,W,02:40:48,06:27:05 +36,W,02:42:09,06:27:06 +53,M,02:39:51,06:27:06 +45,W,02:35:09,06:27:06 +53,M,02:37:14,06:27:07 +40,M,02:17:59,06:27:09 +40,M,02:32:58,06:27:09 +20,M,02:32:26,06:27:10 +38,M,02:52:59,06:27:12 +47,M,02:44:56,06:27:12 +30,M,02:30:37,06:27:13 +57,W,02:37:32,06:27:14 +55,W,02:40:42,06:27:15 +43,M,02:07:58,06:27:16 +45,W,02:41:51,06:27:17 +29,W,02:34:51,06:27:18 +32,M,02:12:39,06:27:19 +46,M,02:59:46,06:27:20 +38,W,02:21:43,06:27:22 +55,M,02:48:24,06:27:23 +35,M,02:22:52,06:27:24 +31,W,02:42:56,06:27:25 +28,W,02:48:03,06:27:26 +37,M,02:39:40,06:27:26 +40,W,02:35:56,06:27:26 +45,W,02:33:14,06:27:26 +50,M,02:34:37,06:27:28 +71,M,02:29:02,06:27:28 +30,W,02:45:11,06:27:29 +27,W,02:28:35,06:27:30 +35,M,02:32:45,06:27:31 +18,M,02:29:16,06:27:32 +62,M,02:44:21,06:27:33 +45,M,02:29:46,06:27:33 +24,M,02:25:35,06:27:33 +50,W,02:39:25,06:27:33 +66,W,02:40:10,06:27:34 +32,W,02:33:58,06:27:37 +22,M,02:31:17,06:27:38 +49,W,02:33:37,06:27:39 +25,W,02:17:28,06:27:40 +56,W,02:43:38,06:27:41 +25,W,02:51:56,06:27:41 +35,M,02:35:33,06:27:42 +42,M,02:29:27,06:27:44 +51,W,02:44:26,06:27:45 +39,W,02:31:53,06:27:46 +38,W,02:40:59,06:27:46 +34,W,02:43:43,06:27:47 +37,W,02:36:37,06:27:48 +30,W,02:32:03,06:27:49 +46,M,02:47:18,06:27:50 +31,W,02:27:16,06:27:52 +35,M,02:20:44,06:27:52 +28,W,02:28:25,06:27:54 +40,M,02:20:13,06:27:54 +32,W,02:47:41,06:27:54 +35,W,02:50:34,06:27:55 +46,W,02:48:46,06:27:56 +40,M,02:34:10,06:27:56 +37,W,02:08:32,06:27:57 +28,W,02:44:03,06:27:59 +48,M,02:37:16,06:27:59 +61,W,02:30:13,06:28:00 +44,M,02:43:08,06:28:00 +42,W,02:31:19,06:28:00 +57,W,02:42:17,06:28:00 +52,W,02:42:28,06:28:01 +50,M,02:35:35,06:28:03 +34,M,02:27:56,06:28:04 +41,W,02:44:36,06:28:04 +61,W,02:43:03,06:28:07 +43,M,02:36:25,06:28:10 +46,M,02:21:14,06:28:10 +29,W,02:49:36,06:28:13 +30,M,02:32:48,06:28:13 +56,M,02:57:57,06:28:16 +45,W,02:38:20,06:28:16 +29,M,02:39:14,06:28:17 +55,W,02:47:34,06:28:17 +50,W,02:44:15,06:28:18 +51,W,02:42:57,06:28:20 +39,M,02:24:33,06:28:21 +18,W,02:35:38,06:28:25 +40,M,02:51:41,06:28:26 +46,M,02:34:29,06:28:32 +35,M,02:34:31,06:28:34 +61,M,02:34:14,06:28:37 +36,M,02:51:49,06:28:39 +60,W,02:41:39,06:28:40 +29,W,02:34:27,06:28:40 +47,W,02:48:09,06:28:42 +55,W,02:48:29,06:28:42 +56,W,02:53:45,06:28:42 +26,W,02:37:15,06:28:43 +39,M,02:37:01,06:28:45 +30,M,02:37:48,06:28:45 +24,W,02:46:41,06:28:46 +39,M,02:48:24,06:28:47 +33,W,02:46:53,06:28:48 +18,W,02:41:26,06:28:48 +57,M,02:27:29,06:28:51 +40,W,02:50:44,06:28:51 +54,M,02:37:51,06:28:52 +30,W,02:26:24,06:28:52 +46,M,02:35:27,06:28:52 +46,M,02:29:40,06:28:52 +62,W,02:39:27,06:28:54 +40,W,02:31:49,06:28:55 +32,W,02:47:09,06:28:55 +47,W,02:42:38,06:28:56 +56,M,02:09:58,06:28:57 +34,M,02:45:28,06:28:57 +48,M,02:39:42,06:28:59 +25,W,02:43:23,06:29:00 +51,W,02:34:45,06:29:00 +42,M,02:36:56,06:29:02 +29,W,02:36:11,06:29:02 +55,M,02:31:00,06:29:02 +33,W,02:37:15,06:29:03 +27,W,02:32:18,06:29:03 +36,W,02:44:06,06:29:05 +63,M,02:30:53,06:29:05 +62,M,02:26:10,06:29:08 +38,W,02:47:38,06:29:08 +44,W,02:37:25,06:29:13 +56,M,02:34:00,06:29:17 +36,W,02:40:34,06:29:18 +38,W,02:39:24,06:29:20 +60,W,02:43:18,06:29:22 +33,W,02:41:24,06:29:22 +37,M,02:30:18,06:29:23 +52,M,02:50:20,06:29:24 +28,W,02:36:04,06:29:24 +52,M,02:22:06,06:29:26 +56,M,02:47:36,06:29:26 +41,M,02:37:48,06:29:26 +36,W,02:29:57,06:29:26 +41,W,02:39:23,06:29:28 +51,W,02:49:14,06:29:28 +36,W,02:51:46,06:29:28 +24,W,02:41:36,06:29:29 +42,W,02:27:13,06:29:29 +39,M,02:35:20,06:29:31 +64,M,02:30:25,06:29:32 +40,W,02:34:08,06:29:33 +37,M,02:36:44,06:29:34 +36,W,02:38:11,06:29:34 +50,M,02:40:13,06:29:36 +66,M,02:54:06,06:29:37 +39,W,02:33:44,06:29:37 +74,M,02:32:01,06:29:39 +30,W,02:39:08,06:29:39 +39,M,02:38:33,06:29:40 +31,W,02:30:50,06:29:43 +29,W,02:38:43,06:29:43 +47,M,02:36:14,06:29:43 +32,W,02:40:07,06:29:45 +30,W,02:32:01,06:29:45 +50,W,02:37:27,06:29:46 +31,M,02:47:24,06:29:46 +53,W,02:34:40,06:29:46 +36,W,02:38:21,06:29:49 +36,M,02:37:17,06:29:52 +23,W,02:42:56,06:29:53 +47,W,02:40:52,06:29:53 +35,M,02:34:24,06:29:54 +53,M,02:45:58,06:29:55 +43,M,02:42:20,06:29:55 +25,W,02:44:07,06:29:56 +55,M,02:33:55,06:29:57 +38,W,02:39:22,06:29:58 +36,W,02:39:18,06:29:59 +40,M,02:40:40,06:30:02 +51,W,02:49:11,06:30:03 +32,W,02:36:22,06:30:04 +30,M,02:25:53,06:30:04 +41,W,02:33:53,06:30:04 +47,M,02:40:07,06:30:05 +44,W,02:33:46,06:30:06 +62,M,02:37:33,06:30:06 +29,W,02:39:58,06:30:07 +52,M,02:36:32,06:30:07 +63,W,02:34:24,06:30:08 +43,W,02:40:02,06:30:10 +55,W,02:41:53,06:30:13 +25,M,02:40:07,06:30:13 +51,M,02:37:38,06:30:17 +51,M,02:52:16,06:30:21 +31,W,02:36:05,06:30:24 +50,W,02:33:18,06:30:26 +30,M,02:43:21,06:30:26 +43,M,02:38:59,06:30:26 +34,W,02:52:58,06:30:28 +48,M,02:30:30,06:30:28 +50,M,02:33:23,06:30:30 +22,W,02:56:21,06:30:31 +40,M,02:27:08,06:30:32 +29,W,02:43:16,06:30:34 +29,W,02:33:58,06:30:35 +29,W,02:42:23,06:30:36 +25,W,02:40:56,06:30:38 +47,W,02:55:07,06:30:40 +41,W,02:33:32,06:30:41 +36,W,02:29:43,06:30:42 +64,W,02:42:40,06:30:43 +56,M,02:36:49,06:30:46 +43,M,02:39:47,06:30:48 +46,W,02:36:32,06:30:49 +39,W,02:22:48,06:30:53 +25,M,02:35:25,06:30:54 +47,M,02:54:25,06:30:55 +57,M,02:54:42,06:30:55 +46,W,02:30:51,06:30:57 +49,W,02:58:03,06:30:57 +62,M,02:32:58,06:31:01 +31,M,03:16:43,06:31:02 +50,W,02:53:01,06:31:06 +46,M,02:28:07,06:31:07 +36,W,02:50:16,06:31:07 +37,M,02:30:39,06:31:07 +41,M,02:34:22,06:31:07 +32,M,02:17:31,06:31:08 +47,W,02:30:55,06:31:08 +41,W,02:46:29,06:31:09 +50,W,02:41:34,06:31:10 +41,M,02:40:15,06:31:11 +28,M,02:38:55,06:31:11 +67,M,02:33:00,06:31:11 +45,M,02:36:36,06:31:11 +51,M,02:54:11,06:31:16 +52,M,02:49:56,06:31:17 +49,W,02:42:02,06:31:17 +57,W,02:37:53,06:31:19 +52,M,02:48:24,06:31:21 +43,W,02:54:35,06:31:22 +52,W,02:35:00,06:31:22 +53,W,02:40:44,06:31:24 +26,W,03:16:18,06:31:24 +33,W,02:41:54,06:31:28 +54,M,02:21:15,06:31:30 +49,M,02:31:49,06:31:30 +50,W,02:48:17,06:31:31 +58,M,02:29:03,06:31:34 +69,M,02:31:29,06:31:34 +30,W,02:32:34,06:31:35 +38,M,02:37:43,06:31:36 +26,W,02:33:26,06:31:36 +61,M,02:16:31,06:31:37 +38,M,02:41:28,06:31:38 +60,M,02:44:22,06:31:38 +51,M,02:36:53,06:31:40 +41,W,02:39:42,06:31:40 +31,W,02:39:23,06:31:42 +49,M,02:27:22,06:31:43 +32,W,02:39:44,06:31:44 +34,W,02:42:06,06:31:47 +28,M,02:22:16,06:31:48 +32,W,02:41:46,06:31:49 +66,W,02:49:10,06:31:49 +36,M,02:43:55,06:31:50 +39,M,02:42:16,06:31:53 +28,M,02:38:39,06:31:53 +29,W,02:41:40,06:31:53 +39,W,02:35:29,06:31:54 +47,M,02:36:18,06:31:56 +29,W,02:32:11,06:31:57 +45,M,02:38:12,06:31:57 +46,M,02:39:12,06:31:59 +39,M,02:41:58,06:32:00 +41,W,02:39:43,06:32:01 +51,W,02:53:54,06:32:01 +43,M,02:37:54,06:32:04 +39,W,02:37:01,06:32:04 +27,M,02:38:58,06:32:05 +41,M,02:32:49,06:32:06 +41,M,02:34:18,06:32:12 +49,M,02:28:10,06:32:12 +36,W,02:41:56,06:32:12 +59,M,02:32:51,06:32:15 +34,M,02:25:40,06:32:15 +32,M,02:24:18,06:32:17 +24,W,02:36:58,06:32:18 +67,M,02:32:15,06:32:19 +47,M,02:58:11,06:32:19 +37,W,02:17:53,06:32:19 +71,M,02:47:51,06:32:20 +38,W,02:43:22,06:32:20 +35,W,02:47:43,06:32:20 +41,W,02:42:39,06:32:21 +35,W,02:32:10,06:32:21 +43,M,02:31:23,06:32:21 +27,W,02:47:36,06:32:22 +45,M,02:50:47,06:32:24 +47,W,02:53:12,06:32:24 +39,W,02:39:19,06:32:24 +45,M,02:43:42,06:32:24 +49,M,02:29:08,06:32:26 +38,W,02:21:58,06:32:35 +50,W,02:23:10,06:32:39 +40,M,02:45:08,06:32:41 +35,M,02:32:49,06:32:42 +29,W,02:34:34,06:32:42 +64,M,02:51:52,06:32:42 +61,M,02:17:52,06:32:43 +36,W,02:42:10,06:32:43 +35,M,02:21:59,06:32:43 +37,M,02:52:47,06:32:43 +27,W,02:34:10,06:32:46 +39,W,02:48:34,06:32:47 +35,M,02:37:16,06:32:47 +56,W,02:42:49,06:32:47 +45,W,02:52:18,06:32:48 +30,M,02:39:01,06:32:49 +50,M,02:32:47,06:32:52 +50,W,02:57:38,06:32:52 +26,M,02:48:03,06:32:53 +28,M,02:48:02,06:32:53 +53,M,02:14:01,06:32:56 +68,M,02:46:01,06:32:56 +38,W,02:45:51,06:32:56 +38,M,02:37:50,06:32:57 +31,W,02:47:25,06:32:58 +51,M,02:36:20,06:33:02 +35,W,02:37:05,06:33:02 +43,W,02:42:40,06:33:05 +45,M,02:46:05,06:33:09 +40,W,02:34:12,06:33:11 +57,M,02:47:54,06:33:11 +25,W,02:37:24,06:33:11 +31,W,02:36:59,06:33:12 +36,M,02:44:33,06:33:12 +28,W,02:29:59,06:33:13 +62,M,02:41:41,06:33:15 +29,W,02:43:04,06:33:19 +48,M,02:43:19,06:33:20 +63,M,02:38:27,06:33:21 +51,M,02:24:02,06:33:22 +60,M,02:35:24,06:33:23 +45,M,02:36:41,06:33:26 +50,M,02:35:14,06:33:26 +61,M,02:38:17,06:33:27 +62,M,02:49:25,06:33:27 +37,W,02:46:07,06:33:27 +49,M,02:42:24,06:33:28 +36,M,02:14:55,06:33:29 +41,W,03:02:08,06:33:32 +58,M,02:53:22,06:33:35 +39,M,02:04:04,06:33:35 +29,W,03:15:24,06:33:36 +60,M,02:36:07,06:33:36 +59,M,02:39:01,06:33:39 +31,M,02:39:34,06:33:40 +44,M,02:36:51,06:33:40 +42,W,02:41:53,06:33:41 +36,W,02:46:02,06:33:42 +27,W,02:41:47,06:33:43 +44,W,02:39:22,06:33:44 +35,W,02:48:34,06:33:44 +51,W,02:55:20,06:33:45 +48,M,02:50:03,06:33:46 +59,M,02:26:53,06:33:49 +59,M,02:35:15,06:33:50 +50,W,02:47:00,06:33:52 +42,W,02:39:11,06:33:52 +40,M,02:32:31,06:33:55 +50,W,02:38:50,06:33:55 +43,M,02:44:46,06:33:55 +20,W,02:42:28,06:33:56 +65,M,02:49:34,06:33:56 +46,W,02:37:38,06:34:00 +49,W,02:36:53,06:34:00 +53,W,02:42:08,06:34:04 +36,W,02:24:56,06:34:05 +30,M,02:27:48,06:34:05 +33,M,02:38:47,06:34:08 +69,M,02:49:22,06:34:08 +48,W,02:39:26,06:34:08 +59,M,02:33:28,06:34:10 +60,M,02:47:41,06:34:10 +44,W,02:41:58,06:34:11 +52,M,02:32:41,06:34:12 +29,W,02:45:42,06:34:12 +24,M,02:45:12,06:34:13 +26,M,02:21:51,06:34:13 +63,M,02:49:47,06:34:13 +49,W,03:15:20,06:34:15 +62,W,02:38:48,06:34:16 +32,W,02:39:08,06:34:17 +26,W,02:20:09,06:34:17 +40,W,02:46:51,06:34:18 +36,W,02:42:53,06:34:21 +37,W,02:33:39,06:34:28 +51,W,02:43:20,06:34:28 +38,W,02:40:01,06:34:29 +55,W,02:35:24,06:34:34 +26,W,02:28:42,06:34:35 +39,M,02:20:13,06:34:36 +26,M,02:28:28,06:34:39 +39,M,02:32:17,06:34:39 +39,M,02:21:46,06:34:42 +48,W,02:48:39,06:34:42 +27,M,02:36:36,06:34:43 +53,W,02:49:02,06:34:44 +60,M,02:27:40,06:34:44 +48,M,02:24:18,06:34:45 +43,M,02:33:08,06:34:47 +66,W,02:49:56,06:34:47 +44,M,02:24:51,06:34:48 +41,W,02:36:13,06:34:51 +45,W,02:33:45,06:34:51 +44,M,02:58:49,06:34:55 +43,M,02:35:02,06:34:57 +75,M,02:25:03,06:35:00 +37,W,02:37:06,06:35:02 +75,W,03:10:30,06:35:03 +53,M,02:44:55,06:35:07 +51,M,02:41:33,06:35:10 +36,M,02:15:25,06:35:15 +40,W,02:46:41,06:35:17 +55,W,02:41:13,06:35:18 +40,M,02:42:07,06:35:19 +38,W,02:50:14,06:35:19 +58,M,02:35:09,06:35:20 +47,W,02:45:00,06:35:21 +55,M,02:51:02,06:35:21 +31,M,02:21:50,06:35:23 +17,M,02:51:37,06:35:28 +58,W,02:44:19,06:35:28 +35,M,02:37:36,06:35:29 +20,W,02:42:41,06:35:29 +68,W,02:56:31,06:35:31 +29,M,02:40:18,06:35:32 +38,W,02:35:20,06:35:32 +51,W,02:37:53,06:35:33 +68,M,02:42:53,06:35:34 +30,W,02:19:51,06:35:37 +31,W,02:41:22,06:35:42 +45,M,02:57:50,06:35:42 +44,W,02:44:46,06:35:44 +44,W,02:51:16,06:35:45 +36,M,02:47:03,06:35:45 +39,M,02:29:53,06:35:46 +26,M,02:15:17,06:35:47 +38,W,02:48:14,06:35:49 +32,M,02:58:27,06:35:52 +49,M,02:41:42,06:35:53 +38,W,02:49:17,06:35:54 +40,M,02:28:45,06:35:55 +46,W,02:35:43,06:35:57 +69,M,02:36:28,06:35:58 +41,M,02:45:48,06:35:58 +58,M,03:01:13,06:35:59 +39,M,02:32:13,06:36:01 +49,W,02:23:26,06:36:02 +29,M,02:33:16,06:36:04 +37,W,02:36:00,06:36:06 +39,W,02:42:51,06:36:06 +42,M,02:37:23,06:36:08 +36,W,02:42:11,06:36:09 +57,W,02:57:32,06:36:09 +29,W,02:36:52,06:36:09 +61,M,02:52:47,06:36:11 +62,M,02:35:29,06:36:13 +41,W,02:40:47,06:36:14 +43,W,02:42:22,06:36:15 +62,W,02:56:47,06:36:15 +57,M,02:46:11,06:36:17 +50,M,02:47:34,06:36:18 +42,W,02:32:27,06:36:18 +68,M,02:49:57,06:36:20 +30,M,02:19:56,06:36:20 +24,M,02:41:41,06:36:20 +38,M,02:35:38,06:36:21 +27,W,02:45:51,06:36:24 +18,M,02:47:55,06:36:24 +49,W,02:36:04,06:36:25 +37,W,02:51:49,06:36:28 +41,W,02:41:53,06:36:29 +62,W,02:55:59,06:36:29 +45,W,02:49:04,06:36:30 +39,W,02:49:19,06:36:31 +27,W,03:01:34,06:36:32 +42,M,02:45:49,06:36:32 +40,W,02:28:09,06:36:34 +60,M,02:51:55,06:36:34 +53,M,02:45:28,06:36:35 +32,W,02:52:08,06:36:36 +57,W,02:50:18,06:36:37 +37,M,02:33:33,06:36:37 +50,M,02:37:59,06:36:39 +53,W,02:59:55,06:36:41 +40,W,02:35:43,06:36:41 +39,M,02:35:30,06:36:44 +48,W,02:52:31,06:36:46 +50,W,02:48:41,06:36:47 +34,M,02:37:49,06:36:48 +25,W,02:45:56,06:36:49 +32,W,02:42:09,06:36:50 +58,M,03:10:48,06:36:54 +60,W,02:44:48,06:36:55 +42,W,02:36:09,06:36:56 +25,W,02:22:57,06:36:59 +27,M,02:32:39,06:36:59 +69,M,02:45:28,06:36:59 +44,M,02:41:53,06:36:59 +39,M,02:31:26,06:37:01 +32,W,02:44:32,06:37:02 +62,M,02:40:06,06:37:02 +31,W,02:37:58,06:37:03 +53,M,02:30:05,06:37:03 +40,W,02:37:02,06:37:03 +33,M,02:24:55,06:37:04 +43,W,02:37:51,06:37:08 +45,W,02:54:09,06:37:08 +47,M,03:05:24,06:37:10 +62,W,02:58:41,06:37:10 +29,W,02:43:07,06:37:11 +43,W,02:42:53,06:37:11 +50,M,03:01:49,06:37:11 +31,M,02:45:39,06:37:13 +35,W,02:37:48,06:37:14 +48,W,02:43:15,06:37:15 +48,M,02:35:35,06:37:16 +37,W,02:45:23,06:37:19 +47,M,02:48:10,06:37:22 +42,W,02:39:48,06:37:24 +38,W,02:51:14,06:37:26 +42,W,02:13:40,06:37:27 +50,M,02:47:52,06:37:28 +51,W,02:41:14,06:37:28 +27,M,02:42:34,06:37:29 +29,W,02:14:57,06:37:30 +52,M,02:56:46,06:37:30 +59,M,02:39:52,06:37:31 +43,M,03:01:53,06:37:33 +56,M,02:47:03,06:37:36 +35,W,02:45:09,06:37:37 +28,W,02:34:15,06:37:37 +41,W,02:42:33,06:37:38 +29,M,02:50:44,06:37:39 +39,W,02:43:24,06:37:39 +43,M,02:24:41,06:37:41 +26,W,02:38:02,06:37:41 +56,M,02:50:47,06:37:46 +40,W,02:50:32,06:37:47 +74,M,02:44:14,06:37:48 +40,W,02:44:55,06:37:50 +45,W,02:42:16,06:37:52 +48,M,02:41:43,06:37:54 +58,W,02:45:31,06:37:55 +46,M,02:42:27,06:37:55 +39,W,02:36:18,06:37:56 +42,W,02:42:43,06:38:00 +38,M,02:41:55,06:38:01 +48,M,02:44:35,06:38:02 +43,W,02:41:14,06:38:02 +35,W,02:52:37,06:38:04 +33,M,02:52:06,06:38:05 +39,W,02:38:37,06:38:09 +42,W,02:47:12,06:38:10 +51,M,02:42:06,06:38:11 +26,M,02:36:03,06:38:13 +62,M,02:45:39,06:38:15 +37,M,02:15:30,06:38:15 +50,W,02:43:24,06:38:17 +30,M,02:29:54,06:38:17 +38,M,02:47:52,06:38:17 +50,W,02:36:41,06:38:20 +62,M,02:48:29,06:38:22 +65,M,02:59:37,06:38:24 +35,M,02:38:30,06:38:24 +41,M,02:49:28,06:38:26 +25,M,02:12:51,06:38:29 +62,M,02:47:26,06:38:30 +59,M,02:49:47,06:38:31 +42,M,02:20:10,06:38:31 +59,M,03:00:55,06:38:31 +56,W,02:55:06,06:38:34 +52,W,02:16:55,06:38:34 +29,M,02:39:04,06:38:35 +50,W,02:59:38,06:38:37 +47,M,02:45:08,06:38:38 +59,M,03:08:48,06:38:38 +39,W,02:39:24,06:38:42 +57,W,02:48:49,06:38:44 +66,M,02:34:40,06:38:45 +38,M,02:42:09,06:38:45 +25,W,02:43:02,06:38:45 +45,W,02:56:33,06:38:46 +35,M,02:29:55,06:38:46 +37,M,02:41:15,06:38:48 +46,W,02:55:46,06:38:49 +71,W,02:45:50,06:38:52 +33,M,02:28:03,06:38:57 +33,W,02:47:00,06:39:03 +67,W,03:00:09,06:39:05 +51,M,02:44:45,06:39:05 +44,M,02:26:29,06:39:09 +32,W,02:46:19,06:39:09 +31,W,02:47:17,06:39:09 +49,W,02:55:01,06:39:11 +45,W,02:45:23,06:39:12 +46,M,02:25:15,06:39:12 +40,W,02:54:45,06:39:13 +57,M,02:59:43,06:39:13 +47,W,02:49:09,06:39:14 +31,W,02:48:20,06:39:14 +59,W,02:50:39,06:39:16 +51,W,02:53:23,06:39:16 +34,M,02:27:28,06:39:17 +48,W,02:42:11,06:39:18 +35,M,02:35:40,06:39:20 +51,W,02:59:57,06:39:23 +43,W,02:32:32,06:39:24 +64,M,02:47:48,06:39:25 +39,M,02:37:28,06:39:26 +34,M,03:01:59,06:39:26 +61,M,02:40:47,06:39:30 +50,M,02:41:44,06:39:33 +25,W,02:28:25,06:39:34 +33,W,02:44:03,06:39:37 +38,M,02:53:09,06:39:39 +18,W,02:44:39,06:39:47 +28,W,02:44:10,06:39:47 +22,W,02:30:57,06:39:48 +39,W,02:47:08,06:39:51 +46,M,02:28:57,06:39:58 +47,M,02:58:28,06:39:59 +49,W,02:42:40,06:40:01 +58,M,02:35:04,06:40:02 +43,W,02:44:36,06:40:02 +49,W,02:35:28,06:40:03 +56,M,03:07:40,06:40:03 +42,W,02:57:23,06:40:04 +30,W,02:17:19,06:40:05 +49,W,02:43:52,06:40:06 +57,M,02:44:26,06:40:06 +50,M,02:22:49,06:40:09 +36,M,02:35:52,06:40:11 +38,W,02:46:41,06:40:16 +38,W,02:48:56,06:40:16 +40,W,02:32:13,06:40:17 +38,W,02:45:00,06:40:18 +43,W,02:46:09,06:40:18 +64,M,02:53:27,06:40:23 +46,W,03:06:41,06:40:30 +41,W,02:44:24,06:40:32 +51,M,02:50:18,06:40:35 +29,M,02:30:44,06:40:36 +49,W,02:53:22,06:40:36 +24,W,02:46:42,06:40:37 +26,W,02:29:43,06:40:37 +40,M,02:36:52,06:40:37 +51,W,02:52:17,06:40:37 +40,W,02:57:29,06:40:39 +59,M,03:03:32,06:40:39 +61,W,02:56:49,06:40:39 +38,W,02:31:26,06:40:39 +74,M,02:48:38,06:40:41 +25,W,02:42:14,06:40:43 +38,W,02:52:19,06:40:45 +34,W,03:11:33,06:40:45 +49,W,02:47:09,06:40:45 +33,W,02:46:14,06:40:48 +31,M,02:49:54,06:40:48 +28,W,02:49:48,06:40:48 +62,W,02:34:34,06:40:50 +64,M,02:51:33,06:40:50 +34,W,02:51:48,06:40:52 +50,W,02:53:40,06:40:52 +45,W,02:43:14,06:40:52 +33,W,02:49:22,06:40:53 +33,W,02:45:36,06:40:59 +49,W,02:55:59,06:40:59 +40,W,02:38:17,06:40:59 +52,W,02:49:44,06:41:05 +41,W,02:56:39,06:41:06 +58,M,03:01:50,06:41:06 +49,W,02:43:27,06:41:08 +61,W,02:45:10,06:41:10 +30,W,02:26:42,06:41:10 +22,W,02:37:47,06:41:15 +34,M,02:46:12,06:41:16 +58,M,02:33:58,06:41:18 +38,W,02:40:56,06:41:19 +47,W,02:55:57,06:41:19 +48,W,02:52:50,06:41:22 +31,W,02:32:33,06:41:36 +74,M,02:39:43,06:41:38 +67,M,02:32:35,06:41:40 +44,W,02:47:29,06:41:40 +46,W,02:41:52,06:41:42 +29,W,02:40:41,06:41:43 +35,M,02:30:44,06:41:43 +37,W,02:49:52,06:41:43 +55,M,03:08:16,06:41:51 +63,W,02:42:20,06:41:51 +24,W,02:54:30,06:41:54 +49,M,02:46:39,06:41:55 +30,M,02:38:05,06:41:57 +63,M,02:51:57,06:41:57 +36,W,03:07:48,06:41:58 +60,M,02:52:33,06:42:00 +29,M,02:37:58,06:42:01 +46,W,02:38:04,06:42:02 +41,M,02:47:30,06:42:05 +40,W,02:44:15,06:42:06 +53,M,02:34:38,06:42:08 +33,W,02:48:54,06:42:09 +51,W,02:46:19,06:42:09 +32,W,02:34:36,06:42:12 +56,W,02:47:22,06:42:17 +39,W,02:40:23,06:42:17 +53,M,02:46:42,06:42:17 +61,M,02:56:50,06:42:17 +49,W,02:54:30,06:42:18 +59,M,02:37:20,06:42:21 +36,W,02:52:39,06:42:21 +55,M,02:42:16,06:42:24 +53,W,02:42:59,06:42:24 +48,M,02:38:49,06:42:26 +26,W,02:50:53,06:42:27 +58,W,02:34:43,06:42:38 +59,M,02:52:45,06:42:42 +65,M,02:42:28,06:42:49 +31,W,02:37:48,06:42:51 +34,M,02:39:04,06:42:52 +39,W,02:43:03,06:42:54 +57,M,02:39:09,06:42:54 +42,M,02:36:51,06:43:00 +32,W,02:48:45,06:43:00 +53,M,03:03:14,06:43:02 +80,M,02:46:58,06:43:03 +33,W,02:19:51,06:43:05 +61,M,02:46:02,06:43:06 +39,W,02:49:32,06:43:06 +47,M,02:54:57,06:43:08 +37,M,02:53:15,06:43:11 +50,M,02:46:57,06:43:12 +41,M,02:39:22,06:43:12 +53,W,02:47:12,06:43:13 +32,W,02:37:37,06:43:22 +44,W,02:26:15,06:43:27 +37,W,02:38:29,06:43:27 +23,W,02:33:09,06:43:27 +37,M,02:45:16,06:43:28 +31,W,02:26:26,06:43:29 +39,M,02:17:49,06:43:29 +29,M,03:01:48,06:43:31 +29,M,02:25:41,06:43:32 +26,W,02:36:37,06:43:33 +36,M,02:44:29,06:43:33 +59,W,02:38:04,06:43:43 +32,M,02:27:22,06:43:43 +53,M,02:49:30,06:43:43 +42,M,02:45:18,06:43:44 +35,M,02:45:22,06:43:44 +62,W,02:57:40,06:43:50 +35,W,02:45:14,06:43:50 +25,M,03:00:15,06:43:51 +28,W,02:44:14,06:43:54 +45,M,02:40:47,06:43:55 +54,W,02:49:31,06:43:55 +37,W,02:55:59,06:43:55 +57,W,02:50:36,06:43:58 +62,M,02:43:43,06:44:02 +38,W,02:30:12,06:44:03 +59,M,02:38:41,06:44:03 +32,W,02:57:03,06:44:04 +29,W,02:41:12,06:44:07 +34,W,02:42:55,06:44:12 +59,M,02:40:54,06:44:15 +30,W,02:34:10,06:44:15 +40,W,02:44:55,06:44:17 +35,W,02:38:30,06:44:21 +42,M,02:43:45,06:44:22 +22,M,02:36:01,06:44:24 +50,W,02:40:58,06:44:25 +26,M,02:39:38,06:44:25 +54,W,02:41:22,06:44:25 +46,M,02:24:05,06:44:26 +35,M,02:47:20,06:44:28 +52,W,02:28:21,06:44:30 +50,W,02:38:44,06:44:30 +40,W,03:05:07,06:44:35 +52,W,02:55:21,06:44:35 +49,W,02:54:18,06:44:36 +34,M,02:42:10,06:44:36 +56,M,02:35:32,06:44:38 +62,M,02:23:44,06:44:38 +32,W,02:54:48,06:44:40 +65,M,02:44:13,06:44:42 +19,W,03:09:20,06:44:42 +75,M,02:46:11,06:44:43 +33,W,03:01:01,06:44:45 +61,M,02:46:45,06:44:47 +69,M,02:53:15,06:44:48 +21,W,02:54:19,06:44:49 +34,M,02:28:16,06:44:50 +42,M,02:43:38,06:44:51 +46,M,02:49:34,06:44:52 +54,M,02:41:35,06:44:55 +52,M,02:54:28,06:44:56 +60,M,02:44:23,06:44:56 +56,W,02:50:47,06:44:59 +42,M,02:52:51,06:45:01 +46,W,02:50:03,06:45:01 +31,W,02:44:48,06:45:01 +50,W,02:24:51,06:45:05 +50,W,02:51:08,06:45:06 +49,W,02:40:45,06:45:07 +78,M,02:54:47,06:45:07 +29,W,02:50:22,06:45:08 +40,W,02:57:46,06:45:10 +36,W,02:55:51,06:45:10 +31,M,02:50:20,06:45:10 +31,M,02:29:22,06:45:11 +47,M,02:40:19,06:45:11 +52,M,02:46:34,06:45:13 +40,W,02:56:13,06:45:14 +50,W,02:50:49,06:45:15 +30,W,02:49:27,06:45:15 +67,W,03:07:31,06:45:16 +48,W,02:48:51,06:45:16 +49,M,03:02:24,06:45:16 +28,W,02:19:30,06:45:17 +60,W,02:58:04,06:45:21 +62,M,02:51:24,06:45:22 +39,W,02:41:05,06:45:22 +32,W,02:30:55,06:45:22 +29,M,02:42:20,06:45:23 +19,W,02:33:55,06:45:23 +43,W,02:49:46,06:45:24 +68,W,02:40:24,06:45:25 +49,W,03:02:50,06:45:25 +23,W,02:32:59,06:45:26 +34,W,02:50:08,06:45:30 +66,W,02:54:55,06:45:30 +46,M,02:47:46,06:45:30 +34,W,02:46:12,06:45:35 +48,W,02:32:07,06:45:37 +32,W,02:54:25,06:45:39 +48,W,02:35:52,06:45:41 +44,W,02:58:13,06:45:43 +47,W,02:51:38,06:45:43 +46,M,02:52:10,06:45:47 +40,M,02:46:37,06:45:47 +38,W,02:36:17,06:45:48 +67,M,02:49:19,06:45:50 +43,W,02:59:55,06:45:51 +61,W,02:43:48,06:45:53 +34,W,02:24:27,06:45:54 +42,M,02:38:19,06:45:57 +45,W,02:39:29,06:45:57 +38,M,02:50:01,06:45:58 +41,M,02:34:20,06:45:59 +31,W,02:31:10,06:46:03 +36,M,03:12:58,06:46:05 +35,W,02:45:03,06:46:06 +46,M,02:59:29,06:46:07 +39,M,02:33:55,06:46:09 +39,W,02:48:26,06:46:12 +27,W,02:52:02,06:46:14 +59,W,02:59:05,06:46:16 +54,M,02:23:44,06:46:16 +30,W,02:48:53,06:46:16 +27,M,02:29:22,06:46:16 +56,M,02:39:26,06:46:18 +39,W,03:02:42,06:46:18 +44,M,02:35:30,06:46:18 +51,M,02:36:33,06:46:19 +31,M,02:44:59,06:46:22 +58,W,03:00:00,06:46:27 +32,W,02:59:04,06:46:28 +40,W,02:50:56,06:46:29 +60,W,02:48:14,06:46:31 +32,W,02:31:24,06:46:32 +31,W,02:46:17,06:46:32 +57,M,02:30:08,06:46:35 +54,M,02:46:27,06:46:36 +67,M,02:37:54,06:46:37 +51,W,02:42:39,06:46:38 +40,M,02:43:21,06:46:41 +63,M,02:42:46,06:46:44 +45,M,02:39:36,06:46:49 +29,M,03:01:00,06:46:49 +37,W,02:45:51,06:46:49 +59,M,02:46:53,06:46:49 +30,W,02:38:05,06:46:50 +49,W,02:54:37,06:46:53 +44,M,03:13:08,06:46:53 +25,W,02:43:04,06:46:53 +40,W,02:51:30,06:46:53 +29,W,02:46:46,06:46:54 +31,W,02:44:48,06:46:55 +30,W,02:46:41,06:46:56 +40,W,02:30:44,06:46:57 +59,M,02:45:33,06:46:59 +74,M,02:30:31,06:47:02 +33,M,02:44:24,06:47:05 +56,M,02:52:12,06:47:11 +57,W,03:00:40,06:47:21 +51,W,02:38:04,06:47:23 +72,M,02:50:30,06:47:24 +38,M,02:51:49,06:47:26 +48,W,03:01:53,06:47:26 +60,M,03:09:28,06:47:26 +54,M,03:03:27,06:47:28 +24,M,02:12:30,06:47:30 +51,M,02:36:28,06:47:31 +30,W,02:49:07,06:47:39 +51,W,02:48:20,06:47:41 +32,M,02:46:49,06:47:44 +29,W,02:51:57,06:47:44 +48,W,02:51:29,06:47:44 +50,W,03:04:19,06:47:45 +47,W,02:48:54,06:47:48 +53,W,03:01:17,06:47:49 +41,W,02:36:10,06:47:49 +34,W,02:56:09,06:47:50 +48,M,03:01:51,06:47:50 +36,W,02:52:07,06:47:51 +44,M,02:44:03,06:47:55 +59,M,02:48:01,06:47:55 +26,M,02:39:55,06:47:56 +25,W,02:42:27,06:47:59 +33,W,02:35:23,06:47:59 +31,M,02:46:52,06:48:00 +53,W,02:45:48,06:48:00 +51,M,02:45:32,06:48:02 +33,W,02:42:00,06:48:03 +31,M,02:44:10,06:48:04 +61,M,02:39:24,06:48:06 +43,W,02:53:40,06:48:06 +60,M,02:54:00,06:48:07 +39,W,02:50:33,06:48:10 +57,M,02:41:00,06:48:10 +38,W,02:36:27,06:48:13 +53,M,03:02:19,06:48:13 +25,W,02:49:38,06:48:15 +66,M,03:06:44,06:48:15 +55,M,02:51:03,06:48:15 +35,W,02:49:27,06:48:16 +49,M,02:53:51,06:48:16 +36,W,03:03:35,06:48:17 +36,W,02:56:05,06:48:18 +50,W,02:56:27,06:48:19 +44,M,02:56:11,06:48:19 +59,W,03:05:28,06:48:20 +31,M,02:42:38,06:48:22 +37,W,02:53:22,06:48:22 +21,W,02:33:08,06:48:24 +48,M,02:44:55,06:48:25 +39,W,02:46:26,06:48:28 +36,M,02:48:31,06:48:28 +36,W,02:55:42,06:48:30 +34,W,02:36:05,06:48:32 +45,M,02:55:29,06:48:35 +28,M,02:29:31,06:48:36 +54,M,02:37:52,06:48:37 +45,W,02:51:54,06:48:38 +50,W,02:29:24,06:48:41 +49,W,02:34:11,06:48:45 +45,W,03:00:54,06:48:46 +53,W,02:58:47,06:48:47 +21,M,02:50:05,06:48:50 +48,M,02:39:55,06:48:52 +60,M,02:37:22,06:48:52 +44,W,02:41:29,06:48:53 +80,M,02:41:29,06:48:57 +36,W,02:48:54,06:48:58 +66,M,03:01:03,06:49:00 +40,W,02:40:46,06:49:00 +64,M,02:41:54,06:49:00 +22,W,02:33:35,06:49:02 +53,M,02:40:47,06:49:03 +45,W,02:40:54,06:49:03 +66,W,03:06:30,06:49:03 +50,W,02:48:59,06:49:06 +57,M,02:40:12,06:49:07 +30,M,02:40:14,06:49:07 +81,M,02:42:15,06:49:10 +37,W,02:48:52,06:49:10 +55,W,02:55:55,06:49:11 +50,M,02:45:30,06:49:12 +52,M,02:43:00,06:49:14 +37,W,02:44:26,06:49:18 +43,W,02:46:12,06:49:20 +48,W,02:58:59,06:49:28 +63,W,03:12:38,06:49:31 +50,M,02:38:45,06:49:31 +43,W,02:45:56,06:49:31 +26,M,02:30:14,06:49:37 +39,W,03:00:54,06:49:38 +44,W,02:58:55,06:49:47 +42,W,02:54:00,06:49:48 +28,W,02:53:04,06:49:49 +25,W,02:39:35,06:49:49 +49,M,02:41:32,06:49:53 +32,W,02:49:54,06:49:55 +25,M,03:03:35,06:49:55 +31,M,02:40:54,06:49:56 +41,W,02:35:14,06:49:56 +51,M,03:04:43,06:49:59 +65,W,02:38:19,06:49:59 +37,W,02:54:18,06:50:00 +45,M,02:53:50,06:50:01 +53,W,02:55:17,06:50:03 +44,W,02:55:14,06:50:04 +25,W,02:46:10,06:50:08 +52,W,02:52:15,06:50:09 +57,M,02:45:41,06:50:11 +49,M,02:38:54,06:50:11 +39,M,02:48:22,06:50:12 +47,W,03:04:40,06:50:15 +54,W,02:40:58,06:50:16 +27,W,02:46:14,06:50:18 +42,W,02:46:57,06:50:19 +34,W,02:55:14,06:50:21 +49,W,03:12:15,06:50:21 +46,W,02:46:30,06:50:24 +62,M,02:50:00,06:50:26 +42,M,02:42:38,06:50:27 +27,W,02:40:25,06:50:30 +67,W,02:56:28,06:50:34 +57,M,02:47:42,06:50:35 +28,M,02:37:52,06:50:35 +38,W,02:53:19,06:50:37 +56,W,02:57:30,06:50:38 +42,M,02:43:59,06:50:39 +35,M,02:49:44,06:50:39 +33,M,02:47:55,06:50:40 +23,W,02:57:56,06:50:41 +70,M,02:54:46,06:50:44 +40,M,02:57:21,06:50:51 +32,W,02:44:30,06:50:53 +53,M,02:43:34,06:50:54 +63,W,02:40:33,06:50:54 +46,W,03:02:10,06:50:55 +41,M,02:41:28,06:50:59 +61,W,02:56:55,06:51:04 +33,M,02:44:15,06:51:05 +20,W,02:48:44,06:51:06 +37,M,02:40:48,06:51:11 +65,W,02:55:07,06:51:11 +38,W,02:44:28,06:51:12 +38,W,02:42:46,06:51:12 +52,M,02:49:46,06:51:13 +56,M,02:40:53,06:51:13 +26,M,03:05:52,06:51:14 +35,M,01:58:14,06:51:14 +52,W,02:50:27,06:51:17 +34,M,02:43:25,06:51:17 +41,M,02:46:34,06:51:18 +26,W,02:49:18,06:51:21 +38,W,02:21:08,06:51:25 +41,M,02:48:05,06:51:26 +36,W,02:47:25,06:51:27 +47,W,02:59:17,06:51:28 +31,M,02:42:31,06:51:29 +31,M,02:55:03,06:51:30 +18,W,02:51:37,06:51:31 +32,M,02:41:38,06:51:33 +30,W,02:43:55,06:51:34 +43,M,02:49:50,06:51:34 +29,W,02:38:04,06:51:38 +38,W,02:30:21,06:51:40 +26,W,02:40:20,06:51:40 +50,W,02:46:33,06:51:41 +41,M,02:58:18,06:51:45 +36,M,02:28:18,06:51:50 +44,W,02:49:08,06:51:51 +47,W,03:07:16,06:51:52 +27,W,02:44:33,06:51:53 +50,W,02:51:36,06:51:54 +34,M,02:26:38,06:51:54 +26,W,02:38:18,06:51:58 +40,M,02:45:41,06:52:00 +21,W,02:47:12,06:52:00 +23,M,02:45:36,06:52:02 +41,W,02:43:42,06:52:02 +39,W,02:40:03,06:52:02 +49,W,02:45:27,06:52:03 +25,M,02:47:00,06:52:05 +29,W,02:36:22,06:52:05 +59,M,02:45:15,06:52:08 +36,M,02:32:38,06:52:09 +41,W,03:01:31,06:52:10 +44,W,02:43:57,06:52:11 +65,M,02:54:44,06:52:11 +39,M,02:36:17,06:52:15 +42,M,02:44:29,06:52:16 +57,M,02:37:05,06:52:17 +63,W,03:03:20,06:52:19 +39,W,02:48:54,06:52:19 +36,W,02:47:44,06:52:28 +49,W,02:52:54,06:52:35 +30,W,02:51:58,06:52:36 +53,M,02:46:10,06:52:38 +54,W,02:40:24,06:52:39 +38,M,02:45:28,06:52:39 +35,W,03:13:24,06:52:43 +43,M,02:41:29,06:52:49 +39,M,03:06:57,06:52:50 +27,M,02:56:54,06:52:51 +31,W,02:46:51,06:52:53 +35,W,02:56:27,06:52:54 +19,W,03:00:40,06:52:54 +34,M,02:51:27,06:52:55 +32,W,03:20:49,06:52:56 +36,M,02:51:18,06:52:57 +40,M,02:44:56,06:52:59 +39,W,03:00:25,06:53:00 +37,W,02:43:05,06:53:03 +29,W,02:39:56,06:53:11 +24,W,02:56:48,06:53:12 +52,W,02:51:46,06:53:14 +28,M,02:42:59,06:53:14 +25,M,02:45:12,06:53:15 +34,M,02:36:48,06:53:20 +51,M,02:33:41,06:53:21 +64,M,02:36:36,06:53:22 +47,M,03:00:39,06:53:25 +24,W,02:56:13,06:53:29 +36,W,02:50:13,06:53:30 +30,W,02:52:19,06:53:30 +39,M,02:45:24,06:53:35 +46,M,03:01:36,06:53:35 +43,W,03:00:19,06:53:35 +38,M,02:39:38,06:53:36 +51,W,03:05:39,06:53:40 +41,W,02:36:07,06:53:42 +43,W,03:11:15,06:53:43 +47,W,02:45:01,06:53:45 +23,W,02:50:47,06:53:47 +50,M,02:43:54,06:53:48 +42,M,02:44:57,06:53:49 +38,W,02:50:19,06:53:49 +33,W,02:46:20,06:53:51 +52,W,02:51:49,06:53:52 +44,M,02:46:09,06:53:55 +44,M,02:35:16,06:53:55 +63,M,02:52:05,06:53:55 +37,M,02:13:38,06:53:56 +64,W,03:02:49,06:53:57 +28,M,02:33:55,06:53:57 +48,M,02:44:06,06:53:58 +31,M,02:42:05,06:54:00 +39,M,02:52:12,06:54:00 +30,W,02:54:46,06:54:05 +28,M,02:40:46,06:54:05 +26,M,02:43:52,06:54:08 +40,W,02:46:18,06:54:09 +32,M,02:36:10,06:54:10 +49,W,02:56:01,06:54:10 +25,M,02:44:17,06:54:11 +44,M,02:24:26,06:54:13 +57,M,02:49:18,06:54:13 +64,M,02:48:53,06:54:14 +45,M,02:46:14,06:54:14 +38,W,03:03:37,06:54:15 +40,W,02:49:33,06:54:17 +48,W,02:56:57,06:54:17 +41,W,02:49:34,06:54:18 +35,W,02:49:42,06:54:19 +31,M,02:49:30,06:54:20 +45,M,02:45:06,06:54:23 +30,W,02:41:26,06:54:24 +40,W,02:30:17,06:54:28 +66,M,03:09:02,06:54:29 +45,M,02:33:45,06:54:32 +48,M,02:56:48,06:54:33 +38,W,02:45:59,06:54:40 +42,M,02:38:41,06:54:40 +61,M,02:53:55,06:54:42 +34,M,02:59:15,06:54:48 +60,M,03:21:32,06:54:51 +69,M,02:49:55,06:54:54 +44,W,02:51:59,06:54:54 +46,W,03:28:46,06:54:54 +31,W,02:43:04,06:54:54 +53,W,02:51:05,06:54:54 +29,W,02:56:31,06:54:55 +26,W,02:58:24,06:55:01 +23,W,02:50:02,06:55:05 +28,W,02:56:17,06:55:05 +34,W,03:04:38,06:55:05 +27,W,02:55:20,06:55:08 +39,M,02:54:30,06:55:10 +63,M,02:19:59,06:55:13 +43,M,02:08:52,06:55:14 +59,M,02:42:07,06:55:19 +47,W,02:37:04,06:55:21 +36,W,02:51:30,06:55:21 +41,M,02:55:27,06:55:21 +37,M,02:46:02,06:55:25 +57,W,02:53:04,06:55:25 +47,W,02:54:52,06:55:26 +51,W,02:44:46,06:55:27 +61,M,02:46:56,06:55:28 +48,M,02:40:52,06:55:28 +82,W,02:48:26,06:55:31 +45,W,02:56:08,06:55:34 +35,W,02:49:13,06:55:39 +59,W,03:02:05,06:55:40 +46,M,03:11:22,06:55:43 +58,M,02:47:38,06:55:45 +39,W,02:52:47,06:55:48 +58,M,02:59:44,06:55:52 +37,W,02:48:00,06:55:52 +71,M,03:07:32,06:55:52 +49,W,02:54:42,06:55:53 +48,M,03:11:27,06:55:53 +33,W,02:56:41,06:55:56 +41,W,03:00:30,06:55:56 +49,W,02:56:56,06:55:59 +62,M,02:43:29,06:55:59 +51,W,02:54:52,06:55:59 +53,W,02:48:14,06:55:59 +38,M,03:10:47,06:56:02 +37,M,03:27:18,06:56:04 +47,W,03:11:57,06:56:05 +27,M,03:10:33,06:56:10 +51,M,02:50:22,06:56:11 +54,M,02:52:01,06:56:13 +41,W,02:45:15,06:56:17 +41,W,03:01:37,06:56:18 +28,W,02:33:39,06:56:25 +34,M,02:42:53,06:56:33 +46,W,02:48:05,06:56:34 +52,W,03:07:29,06:56:38 +29,W,02:28:51,06:56:42 +55,M,02:33:52,06:56:44 +57,W,02:52:17,06:56:47 +56,M,03:10:39,06:56:49 +62,W,03:00:55,06:56:50 +39,M,02:42:28,06:56:50 +49,W,03:04:06,06:56:53 +32,M,02:55:35,06:56:54 +57,M,02:53:22,06:56:58 +33,W,02:32:46,06:56:59 +62,W,02:59:57,06:57:02 +28,W,02:43:38,06:57:03 +56,W,02:50:34,06:57:04 +50,W,02:33:29,06:57:04 +58,W,02:59:57,06:57:05 +30,W,03:11:07,06:57:08 +58,M,03:12:40,06:57:09 +40,W,02:55:07,06:57:09 +45,W,03:00:59,06:57:11 +38,M,02:27:23,06:57:11 +62,M,02:48:57,06:57:15 +54,M,02:42:27,06:57:15 +41,M,03:00:51,06:57:16 +27,W,02:54:16,06:57:18 +33,M,02:47:04,06:57:21 +50,W,03:00:43,06:57:22 +47,M,02:51:14,06:57:23 +70,M,02:56:30,06:57:24 +60,M,02:53:53,06:57:24 +63,W,02:58:40,06:57:25 +60,M,02:57:32,06:57:30 +63,W,02:59:41,06:57:33 +67,M,02:40:36,06:57:35 +29,M,03:10:09,06:57:41 +33,M,02:54:10,06:57:41 +58,W,03:01:04,06:57:44 +44,M,02:53:24,06:57:45 +66,M,03:00:40,06:57:50 +36,M,02:43:29,06:57:51 +53,W,02:44:36,06:57:52 +39,W,03:02:34,06:57:53 +45,W,02:56:08,06:57:55 +49,M,03:01:54,06:57:55 +56,M,02:44:29,06:57:57 +22,W,03:06:56,06:57:59 +44,M,02:45:21,06:58:01 +30,W,02:47:45,06:58:02 +33,M,02:51:48,06:58:02 +58,W,03:00:56,06:58:03 +64,W,03:00:08,06:58:03 +52,W,02:51:36,06:58:07 +52,M,03:09:21,06:58:08 +51,W,02:50:33,06:58:09 +55,W,02:43:55,06:58:10 +52,W,02:47:28,06:58:13 +28,W,02:55:12,06:58:14 +39,W,02:48:56,06:58:14 +51,M,02:33:59,06:58:17 +52,M,02:54:00,06:58:19 +50,W,02:49:02,06:58:19 +34,W,03:00:13,06:58:21 +54,M,02:46:03,06:58:25 +41,W,02:46:42,06:58:25 +55,M,02:38:13,06:58:26 +51,W,02:59:08,06:58:26 +63,W,03:01:12,06:58:28 +25,W,02:42:53,06:58:31 +43,W,02:47:15,06:58:31 +56,W,02:46:06,06:58:31 +41,W,02:51:47,06:58:32 +56,M,03:12:30,06:58:33 +40,W,02:43:04,06:58:34 +37,W,02:48:01,06:58:39 +29,M,02:26:59,06:58:39 +58,W,03:00:05,06:58:40 +38,M,03:10:58,06:58:40 +30,W,02:44:53,06:58:42 +55,W,03:00:05,06:58:43 +61,W,02:59:47,06:58:49 +48,W,02:34:34,06:58:51 +51,W,02:59:07,06:58:53 +40,W,03:01:30,06:58:53 +26,W,02:33:12,06:58:54 +55,W,03:01:06,06:58:56 +29,W,02:44:54,06:58:57 +25,W,03:00:47,06:58:57 +50,W,03:00:10,06:58:58 +34,W,02:55:11,06:59:01 +24,W,02:49:54,06:59:02 +58,M,03:00:13,06:59:02 +66,M,02:57:36,06:59:02 +34,M,02:55:52,06:59:03 +41,W,02:49:56,06:59:03 +37,W,02:59:49,06:59:05 +41,M,02:50:16,06:59:05 +50,M,03:00:23,06:59:05 +56,M,03:02:50,06:59:05 +32,W,02:42:11,06:59:08 +36,W,02:56:24,06:59:10 +35,M,02:59:13,06:59:10 +64,W,02:59:49,06:59:10 +35,W,03:05:48,06:59:15 +30,W,02:44:28,06:59:18 +27,W,02:25:00,06:59:19 +30,W,03:01:58,06:59:20 +54,W,02:59:33,06:59:20 +30,W,02:53:35,06:59:20 +59,M,02:51:12,06:59:26 +49,W,02:46:12,06:59:26 +63,M,02:48:37,06:59:27 +67,M,02:47:25,06:59:27 +52,W,02:55:57,06:59:28 +59,M,02:46:31,06:59:30 +30,M,02:52:11,06:59:31 +40,M,02:35:36,06:59:31 +57,W,02:49:21,06:59:34 +29,M,02:45:39,06:59:34 +27,W,02:48:20,06:59:35 +35,M,02:59:20,06:59:35 +26,W,02:39:19,06:59:36 +60,W,03:03:12,06:59:38 +30,M,02:50:00,06:59:42 +31,W,02:34:49,06:59:42 +35,W,02:56:21,06:59:44 +64,W,03:00:40,06:59:45 +74,M,03:06:09,06:59:47 +57,M,03:09:07,06:59:50 +30,M,03:02:25,06:59:50 +54,M,02:43:33,06:59:52 +56,W,02:52:26,07:00:00 +62,W,03:05:59,07:00:00 +54,W,03:04:34,07:00:14 +53,M,02:46:24,07:00:20 +55,W,03:03:06,07:00:25 +55,M,03:02:37,07:00:29 +54,W,02:53:50,07:00:30 +27,M,02:18:16,07:00:32 +29,M,02:34:53,07:00:33 +49,W,03:02:57,07:00:35 +46,M,03:01:26,07:00:35 +33,W,03:01:25,07:00:35 +47,M,02:55:28,07:00:36 +42,W,02:53:56,07:00:38 +27,W,02:45:07,07:00:38 +29,W,03:08:48,07:00:40 +30,M,02:52:03,07:00:43 +44,M,02:54:36,07:00:43 +42,W,02:53:52,07:00:44 +56,W,02:39:35,07:00:44 +39,W,03:02:14,07:00:45 +56,M,02:51:37,07:00:49 +28,W,02:50:21,07:00:52 +39,W,02:50:22,07:00:56 +69,M,02:57:48,07:00:58 +50,W,03:01:47,07:01:00 +43,W,02:44:31,07:01:08 +67,M,03:05:58,07:01:15 +56,W,03:02:19,07:01:15 +47,W,02:44:34,07:01:16 +52,W,02:50:21,07:01:17 +34,M,02:52:03,07:01:19 +40,M,02:52:43,07:01:21 +46,W,03:08:39,07:01:22 +78,M,02:52:54,07:01:29 +76,M,02:53:20,07:01:32 +51,M,02:52:37,07:01:38 +61,W,02:55:39,07:01:39 +38,M,02:51:53,07:01:42 +54,M,03:08:21,07:01:43 +52,M,03:06:12,07:01:48 +57,W,02:57:58,07:01:51 +59,M,02:48:54,07:01:52 +38,M,02:21:19,07:01:57 +34,W,02:51:00,07:01:57 +54,M,02:48:00,07:02:01 +37,W,03:02:41,07:02:04 +56,W,03:02:27,07:02:05 +25,W,02:35:12,07:02:05 +50,W,02:54:50,07:02:08 +38,W,03:02:39,07:02:12 +37,W,03:13:28,07:02:14 +54,M,02:46:07,07:02:16 +25,W,02:41:43,07:02:17 +70,M,02:36:25,07:02:17 +24,M,03:04:19,07:02:19 +66,W,02:43:55,07:02:20 +59,M,02:33:10,07:02:22 +44,W,02:59:05,07:02:23 +50,W,02:45:37,07:02:24 +41,W,03:07:47,07:02:31 +49,W,03:02:11,07:02:35 +58,M,02:55:07,07:02:36 +42,M,02:44:29,07:02:40 +52,M,02:59:21,07:02:42 +41,M,02:56:28,07:02:44 +41,W,02:54:51,07:02:46 +60,M,03:01:36,07:02:49 +48,M,02:20:05,07:02:51 +24,W,02:49:05,07:02:53 +46,M,02:50:37,07:02:54 +34,M,02:39:55,07:02:55 +56,M,02:55:24,07:03:01 +37,W,02:46:28,07:03:04 +44,W,03:13:14,07:03:05 +22,W,02:47:31,07:03:08 +46,W,03:01:18,07:03:09 +44,W,02:58:08,07:03:13 +32,W,02:43:30,07:03:14 +38,M,03:00:18,07:03:22 +64,W,02:59:19,07:03:24 +35,M,02:53:11,07:03:32 +46,W,03:00:53,07:03:34 +45,W,02:44:36,07:03:41 +69,M,02:56:46,07:03:44 +54,W,02:56:15,07:03:44 +36,M,02:59:59,07:03:46 +44,M,02:57:25,07:03:48 +29,M,02:56:12,07:03:54 +30,M,02:48:30,07:03:56 +35,M,02:40:47,07:03:57 +35,M,03:04:22,07:03:57 +33,W,02:54:26,07:03:59 +34,W,02:57:29,07:03:59 +44,M,02:36:12,07:04:01 +59,W,02:48:21,07:04:03 +27,W,02:59:05,07:04:05 +35,W,03:01:45,07:04:10 +43,M,02:35:27,07:04:14 +52,M,02:55:20,07:04:14 +26,W,03:06:28,07:04:14 +26,W,02:31:06,07:04:14 +56,W,02:56:30,07:04:15 +57,W,03:09:07,07:04:19 +43,M,03:00:30,07:04:19 +27,W,02:43:54,07:04:19 +73,W,02:54:33,07:04:20 +60,M,02:48:01,07:04:20 +48,W,02:52:15,07:04:22 +49,W,02:55:08,07:04:23 +37,W,02:45:52,07:04:23 +66,W,03:01:04,07:04:26 +51,W,02:45:07,07:04:32 +45,W,02:55:45,07:04:32 +48,M,03:00:51,07:04:34 +59,W,02:38:01,07:04:35 +42,M,02:42:23,07:04:38 +40,W,02:29:50,07:04:40 +36,M,02:53:10,07:04:48 +58,M,02:55:34,07:04:49 +23,W,03:07:46,07:04:56 +24,W,02:42:32,07:04:56 +25,W,02:44:06,07:05:00 +33,M,02:45:33,07:05:00 +30,W,02:45:29,07:05:01 +40,W,03:02:06,07:05:05 +56,M,02:40:35,07:05:06 +47,W,02:59:52,07:05:07 +35,W,02:52:27,07:05:09 +39,W,03:11:12,07:05:11 +51,W,02:56:22,07:05:17 +43,W,02:42:02,07:05:21 +62,M,02:50:47,07:05:21 +68,W,02:40:55,07:05:21 +52,W,03:21:31,07:05:22 +41,M,02:59:53,07:05:24 +29,W,02:54:24,07:05:27 +30,W,03:15:39,07:05:30 +31,W,03:01:35,07:05:34 +43,W,03:01:42,07:05:35 +43,W,02:59:46,07:05:37 +29,W,02:54:41,07:05:38 +40,W,03:17:00,07:05:40 +39,W,02:55:16,07:05:40 +47,M,02:47:10,07:05:41 +58,M,02:52:28,07:05:42 +39,M,03:02:00,07:05:46 +71,M,02:57:08,07:05:48 +55,M,02:46:04,07:05:48 +42,W,03:01:08,07:05:49 +33,W,02:54:31,07:05:50 +39,W,02:54:43,07:05:51 +30,W,02:32:53,07:06:01 +67,W,03:03:00,07:06:05 +38,W,02:51:11,07:06:05 +45,M,02:55:54,07:06:07 +51,W,02:52:29,07:06:10 +26,M,02:48:21,07:06:19 +34,W,02:35:34,07:06:23 +32,W,02:38:08,07:06:23 +36,W,03:05:38,07:06:25 +48,M,03:15:47,07:06:26 +72,W,02:49:02,07:06:28 +48,W,02:48:11,07:06:32 +60,W,02:55:11,07:06:33 +42,W,02:46:03,07:06:34 +44,M,02:39:56,07:06:34 +34,M,02:51:11,07:06:35 +43,W,03:10:43,07:06:36 +50,M,02:56:09,07:06:37 +22,W,03:01:16,07:06:41 +49,M,03:06:21,07:06:46 +44,M,02:42:31,07:06:46 +49,W,02:51:32,07:06:49 +49,W,02:44:10,07:06:49 +44,W,03:08:06,07:06:52 +30,W,03:05:11,07:06:55 +29,W,02:54:08,07:06:56 +42,W,03:06:14,07:06:58 +58,W,02:52:30,07:06:58 +48,W,03:05:24,07:07:06 +19,M,03:02:18,07:07:07 +45,W,03:15:40,07:07:09 +37,W,03:03:41,07:07:11 +53,W,02:54:36,07:07:14 +24,W,03:03:47,07:07:17 +56,W,02:59:54,07:07:23 +28,W,02:56:23,07:07:24 +37,M,03:02:54,07:07:29 +63,M,03:04:18,07:07:29 +47,W,03:04:13,07:07:29 +39,M,02:53:50,07:07:35 +30,W,03:02:38,07:07:38 +38,W,03:06:32,07:07:38 +26,M,02:44:41,07:07:39 +71,M,02:43:29,07:07:40 +46,W,02:38:55,07:07:41 +51,W,03:04:37,07:07:44 +39,W,02:58:14,07:07:44 +64,W,02:59:14,07:07:44 +51,M,03:00:24,07:07:47 +31,W,02:59:58,07:07:47 +52,W,02:50:19,07:07:49 +48,M,02:10:37,07:07:49 +32,M,02:48:34,07:07:51 +36,M,02:42:35,07:07:51 +41,M,02:49:24,07:07:55 +62,W,03:00:39,07:07:57 +31,W,02:45:16,07:07:57 +63,M,02:58:22,07:07:58 +40,W,03:04:04,07:08:01 +22,W,02:59:54,07:08:01 +33,M,03:01:08,07:08:02 +38,W,03:04:42,07:08:07 +57,M,02:51:55,07:08:10 +50,M,03:06:56,07:08:11 +40,W,02:38:56,07:08:13 +61,W,03:30:53,07:08:14 +32,M,02:44:08,07:08:16 +59,W,02:44:21,07:08:16 +49,M,03:02:40,07:08:17 +60,W,02:52:07,07:08:20 +39,W,03:07:16,07:08:21 +51,W,03:04:54,07:08:25 +36,W,02:36:50,07:08:26 +36,W,02:51:52,07:08:29 +61,M,03:30:53,07:08:29 +29,W,02:58:24,07:08:29 +44,W,03:04:19,07:08:31 +51,W,03:07:21,07:08:32 +65,M,02:59:45,07:08:34 +23,M,02:36:48,07:08:35 +41,W,02:44:06,07:08:36 +32,W,02:35:17,07:08:37 +71,W,03:04:36,07:08:39 +24,W,02:54:20,07:08:40 +58,M,02:45:34,07:08:41 +49,W,02:58:15,07:08:45 +48,M,03:08:31,07:08:46 +60,M,02:52:16,07:08:46 +47,W,03:02:40,07:08:52 +34,M,03:04:41,07:08:54 +40,W,02:56:37,07:08:57 +50,W,02:42:05,07:08:57 +41,W,02:57:09,07:09:00 +50,M,02:47:28,07:09:01 +32,M,02:45:06,07:09:03 +33,W,02:54:10,07:09:06 +59,W,03:06:59,07:09:06 +48,W,02:37:49,07:09:10 +55,M,02:56:04,07:09:10 +54,W,03:06:37,07:09:12 +31,W,02:47:10,07:09:15 +65,M,02:54:21,07:09:15 +57,W,02:56:41,07:09:17 +41,W,03:05:08,07:09:18 +41,W,02:57:12,07:09:18 +50,W,02:53:44,07:09:22 +43,W,02:45:29,07:09:22 +52,W,02:57:21,07:09:31 +37,M,03:03:21,07:09:33 +51,M,02:52:39,07:09:35 +37,M,02:56:26,07:09:40 +52,M,03:06:45,07:09:41 +22,W,03:05:54,07:09:41 +23,W,02:55:39,07:09:45 +48,W,03:00:19,07:09:47 +38,W,03:00:48,07:09:48 +38,W,02:48:06,07:09:56 +22,M,03:13:42,07:09:56 +42,M,02:39:48,07:09:58 +36,W,03:00:00,07:09:58 +40,M,02:50:23,07:10:00 +70,M,03:25:20,07:10:04 +64,M,02:55:46,07:10:07 +50,W,02:48:40,07:10:12 +44,W,02:54:42,07:10:12 +48,W,02:48:59,07:10:14 +63,M,02:51:27,07:10:14 +48,M,02:51:23,07:10:22 +58,W,02:53:49,07:10:27 +43,W,02:55:23,07:10:29 +58,W,03:33:33,07:10:30 +50,M,02:50:05,07:10:31 +24,W,02:58:11,07:10:32 +52,M,02:50:28,07:10:34 +27,M,03:14:00,07:10:34 +38,W,03:11:22,07:10:35 +37,W,02:50:52,07:10:38 +48,M,02:56:13,07:10:45 +55,M,02:41:49,07:10:54 +40,W,03:12:22,07:10:57 +33,W,02:51:04,07:11:00 +45,W,02:59:17,07:11:02 +30,W,03:01:30,07:11:04 +56,M,03:00:42,07:11:04 +54,M,02:37:37,07:11:06 +62,W,03:07:55,07:11:06 +27,M,03:04:59,07:11:06 +28,W,03:05:43,07:11:17 +35,M,03:06:59,07:11:19 +58,M,03:15:22,07:11:22 +62,M,03:11:32,07:11:23 +44,W,03:15:09,07:11:26 +62,M,02:54:26,07:11:28 +39,W,03:05:12,07:11:28 +52,W,03:06:26,07:11:32 +33,W,03:01:41,07:11:32 +70,M,03:09:28,07:11:37 +64,M,02:58:05,07:11:49 +53,W,03:15:06,07:11:51 +42,W,03:01:08,07:12:04 +60,M,02:52:20,07:12:04 +44,M,03:05:22,07:12:08 +61,W,02:56:28,07:12:09 +53,W,02:56:38,07:12:12 +62,M,02:49:56,07:12:12 +35,W,03:12:39,07:12:14 +49,W,02:57:17,07:12:16 +46,W,03:14:31,07:12:18 +58,W,03:00:20,07:12:22 +42,W,03:04:06,07:12:23 +26,W,02:56:33,07:12:24 +59,M,03:19:45,07:12:26 +41,W,03:08:54,07:12:30 +37,M,03:03:19,07:12:31 +51,W,02:57:38,07:12:35 +30,W,02:47:52,07:12:39 +37,W,03:13:25,07:12:42 +48,W,02:52:12,07:12:45 +50,M,02:48:55,07:12:45 +48,W,03:08:45,07:12:46 +63,M,02:54:02,07:12:47 +53,M,02:46:52,07:12:49 +69,M,02:55:07,07:12:50 +51,M,02:56:35,07:12:55 +45,M,03:05:25,07:12:56 +32,W,03:02:22,07:12:57 +80,M,02:38:00,07:12:57 +44,M,03:05:26,07:13:05 +49,M,03:07:41,07:13:07 +33,M,02:50:25,07:13:07 +48,W,03:06:09,07:13:11 +30,W,03:05:07,07:13:13 +32,W,02:55:38,07:13:16 +49,W,02:48:36,07:13:17 +27,W,02:56:18,07:13:23 +42,M,03:03:02,07:13:25 +56,M,02:58:13,07:13:31 +43,W,03:00:46,07:13:31 +54,W,03:05:41,07:13:32 +58,M,03:08:12,07:13:34 +37,W,03:03:30,07:13:40 +42,W,03:12:49,07:13:40 +24,M,02:58:38,07:13:46 +61,M,03:12:36,07:13:55 +71,M,03:15:02,07:13:56 +56,W,03:03:51,07:13:56 +51,W,02:48:45,07:14:03 +26,M,02:58:42,07:14:05 +21,W,02:50:19,07:14:06 +43,W,03:03:17,07:14:11 +40,M,02:35:30,07:14:16 +38,W,03:04:19,07:14:17 +29,M,03:16:00,07:14:20 +61,W,02:58:55,07:14:20 +32,W,03:06:55,07:14:21 +43,W,03:00:15,07:14:25 +49,W,02:56:21,07:14:30 +59,W,03:02:17,07:14:32 +64,M,02:59:05,07:14:32 +53,W,03:09:11,07:14:36 +58,W,02:55:54,07:14:41 +50,W,03:08:13,07:14:44 +42,M,02:42:50,07:14:49 +37,M,02:47:29,07:14:50 +51,W,03:08:16,07:14:51 +41,W,03:03:32,07:14:52 +60,M,02:50:57,07:14:56 +41,W,03:03:41,07:14:59 +43,W,03:03:16,07:15:00 +35,W,03:01:31,07:15:05 +54,W,03:06:16,07:15:07 +70,M,03:30:47,07:15:07 +60,M,02:59:47,07:15:10 +24,M,02:47:02,07:15:11 +55,W,03:13:22,07:15:14 +48,M,03:12:26,07:15:15 +34,M,03:04:27,07:15:16 +41,W,03:03:29,07:15:17 +39,W,02:42:13,07:15:20 +31,W,02:42:15,07:15:20 +61,M,02:43:46,07:15:21 +72,M,03:07:42,07:15:23 +37,W,02:50:40,07:15:26 +64,M,02:58:54,07:15:27 +57,W,02:53:07,07:15:27 +34,W,03:07:41,07:15:29 +36,M,03:11:04,07:15:30 +43,W,03:04:14,07:15:32 +61,M,03:05:37,07:15:34 +48,M,03:12:43,07:15:34 +25,M,02:48:22,07:15:36 +49,W,03:03:56,07:15:37 +22,W,03:06:24,07:15:38 +52,M,02:53:19,07:15:39 +49,M,02:55:49,07:15:41 +61,W,02:52:59,07:15:44 +45,W,03:06:41,07:15:45 +29,W,03:03:29,07:15:50 +44,W,03:04:45,07:15:50 +52,W,02:50:19,07:15:51 +57,M,02:56:07,07:15:51 +70,M,03:14:58,07:15:52 +65,M,03:05:59,07:15:56 +28,M,03:05:22,07:15:58 +69,W,03:11:01,07:15:59 +37,M,03:12:02,07:16:02 +36,M,03:10:42,07:16:04 +44,M,02:34:20,07:16:05 +38,W,03:00:35,07:16:05 +36,M,03:12:54,07:16:07 +41,M,03:03:01,07:16:09 +49,W,03:13:12,07:16:11 +69,M,03:14:47,07:16:14 +46,W,03:05:04,07:16:15 +39,W,03:06:34,07:16:16 +37,M,02:44:56,07:16:23 +43,M,03:05:54,07:16:23 +36,W,03:09:20,07:16:25 +39,M,02:43:00,07:16:31 +64,W,03:05:01,07:16:34 +40,W,03:05:56,07:16:41 +45,W,03:00:56,07:16:47 +43,W,02:55:18,07:16:50 +69,W,03:15:09,07:16:53 +59,W,03:14:04,07:16:55 +28,W,03:04:32,07:17:07 +38,W,03:04:50,07:17:07 +46,W,02:55:34,07:17:13 +48,M,03:03:39,07:17:14 +73,M,03:04:44,07:17:14 +31,W,02:49:09,07:17:18 +29,W,03:12:56,07:17:20 +61,W,02:57:19,07:17:20 +45,W,02:59:41,07:17:21 +54,M,03:10:41,07:17:21 +32,W,03:00:34,07:17:26 +47,W,02:55:41,07:17:29 +43,W,02:50:58,07:17:30 +36,W,03:04:00,07:17:49 +61,M,02:55:42,07:17:50 +38,M,03:06:21,07:17:53 +52,W,03:03:40,07:17:55 +40,W,02:56:07,07:17:58 +37,W,03:05:23,07:18:01 +37,W,03:11:12,07:18:03 +45,W,03:00:55,07:18:04 +56,M,03:15:59,07:18:04 +43,W,03:11:17,07:18:06 +30,M,03:15:12,07:18:07 +69,M,03:02:14,07:18:09 +64,W,03:04:34,07:18:10 +32,M,02:31:53,07:18:10 +46,W,03:12:00,07:18:13 +34,W,02:49:57,07:18:16 +33,W,02:56:15,07:18:23 +35,W,02:47:58,07:18:26 +29,W,02:55:07,07:18:31 +39,W,03:06:22,07:18:37 +50,W,03:29:40,07:18:38 +77,M,02:57:10,07:18:41 +48,M,02:58:34,07:18:45 +63,M,02:58:18,07:18:45 +30,W,02:45:35,07:18:49 +54,W,02:59:36,07:18:53 +41,W,03:19:36,07:18:59 +59,M,03:03:09,07:19:00 +47,W,02:36:13,07:19:00 +35,M,02:52:23,07:19:05 +45,W,03:12:07,07:19:08 +53,W,03:10:49,07:19:08 +45,M,03:20:47,07:19:10 +62,M,03:07:49,07:19:21 +59,W,02:59:15,07:19:26 +37,W,03:13:21,07:19:27 +48,W,03:06:21,07:19:29 +57,W,03:14:54,07:19:30 +70,M,03:04:05,07:19:36 +45,W,03:00:51,07:19:43 +29,W,03:05:44,07:19:51 +52,M,03:04:15,07:19:54 +53,M,02:54:32,07:19:55 +49,W,02:52:50,07:19:56 +27,W,03:12:35,07:19:57 +52,M,02:41:11,07:19:59 +52,M,03:01:05,07:20:05 +27,M,02:50:58,07:20:08 +47,W,03:25:42,07:20:29 +43,M,03:36:06,07:20:33 +44,W,03:14:16,07:20:37 +39,W,03:03:29,07:20:41 +51,M,03:19:48,07:20:48 +30,W,03:22:51,07:20:55 +56,M,03:13:06,07:20:57 +22,M,03:06:42,07:20:59 +43,W,03:11:10,07:21:02 +42,W,03:06:13,07:21:04 +43,M,03:36:22,07:21:07 +49,W,03:14:31,07:21:08 +54,W,03:13:07,07:21:09 +56,W,03:11:27,07:21:10 +33,M,02:31:28,07:21:12 +43,M,02:47:15,07:21:15 +39,W,03:00:41,07:21:17 +35,M,03:36:53,07:21:20 +31,W,03:21:58,07:21:25 +31,W,02:59:44,07:21:28 +44,M,03:36:18,07:21:30 +28,W,02:56:59,07:21:32 +28,W,02:13:48,07:21:35 +51,M,02:55:18,07:21:39 +35,W,02:57:01,07:21:47 +48,W,02:58:05,07:21:48 +59,M,03:09:21,07:21:55 +34,W,03:04:48,07:22:00 +30,W,03:10:04,07:22:04 +54,W,03:00:45,07:22:04 +30,M,02:16:13,07:22:15 +43,W,03:03:55,07:22:18 +67,M,02:36:08,07:22:24 +42,M,02:40:16,07:22:26 +29,W,03:04:22,07:22:31 +45,M,03:02:17,07:22:34 +32,W,03:09:06,07:22:36 +41,M,03:12:18,07:22:42 +40,W,03:03:44,07:22:44 +32,M,02:43:05,07:22:57 +42,M,02:51:49,07:23:04 +61,W,03:05:19,07:23:12 +40,M,03:01:46,07:23:17 +34,W,03:11:38,07:23:19 +61,M,02:55:20,07:23:19 +61,W,03:20:19,07:23:23 +28,M,03:24:05,07:23:23 +28,W,02:44:15,07:23:24 +35,M,02:27:21,07:23:29 +39,W,03:02:27,07:23:49 +52,W,03:04:48,07:23:52 +53,W,03:01:07,07:23:56 +44,M,03:11:49,07:23:56 +56,W,03:08:39,07:24:00 +61,M,02:56:09,07:24:06 +70,M,02:59:14,07:24:12 +32,W,03:07:29,07:24:12 +32,W,02:55:24,07:24:13 +54,M,03:38:13,07:24:14 +30,W,03:01:22,07:24:17 +70,M,03:11:54,07:24:20 +44,W,03:07:45,07:24:29 +42,W,03:02:11,07:24:29 +80,W,03:37:23,07:24:30 +45,W,03:13:01,07:24:34 +56,M,03:12:39,07:24:36 +30,W,03:01:55,07:24:41 +50,W,03:12:57,07:24:44 +38,M,02:42:03,07:24:48 +38,W,03:16:12,07:24:56 +36,W,02:59:40,07:25:05 +25,W,03:02:11,07:25:06 +62,W,03:04:40,07:25:12 +33,M,02:37:24,07:25:27 +43,W,02:52:09,07:25:35 +27,W,03:16:41,07:25:36 +63,M,02:54:41,07:25:36 +39,W,02:56:15,07:25:38 +56,W,02:52:53,07:25:40 +44,M,02:26:59,07:25:44 +71,M,03:16:14,07:25:46 +62,M,02:52:53,07:25:47 +51,M,03:10:05,07:25:50 +48,W,02:56:41,07:25:53 +42,W,03:00:01,07:26:01 +61,M,02:56:21,07:26:01 +23,W,03:12:29,07:26:04 +45,W,03:01:23,07:26:11 +56,W,03:19:47,07:26:13 +48,W,03:13:50,07:26:18 +37,M,02:43:57,07:26:19 +28,W,02:54:37,07:26:21 +39,M,02:49:13,07:26:25 +37,W,03:17:16,07:26:26 +35,W,03:09:45,07:26:33 +67,W,03:13:56,07:26:43 +26,M,02:57:40,07:26:43 +52,W,03:03:14,07:26:44 +47,W,03:04:55,07:26:45 +37,W,02:40:04,07:26:45 +26,W,02:55:45,07:26:45 +31,W,02:55:38,07:26:48 +35,W,02:54:46,07:26:48 +26,M,02:54:00,07:26:50 +48,M,02:55:07,07:26:51 +23,M,02:55:13,07:26:53 +33,M,03:25:34,07:26:57 +48,W,03:10:42,07:26:59 +56,M,02:57:20,07:27:02 +56,W,02:58:21,07:27:07 +28,M,03:08:14,07:27:15 +65,M,02:51:37,07:27:17 +37,W,03:00:05,07:27:21 +35,W,03:20:34,07:27:23 +59,W,03:14:51,07:27:23 +48,W,02:55:25,07:27:24 +33,W,03:02:56,07:27:26 +36,M,02:43:00,07:27:33 +40,W,03:07:22,07:27:35 +58,W,03:14:11,07:27:43 +22,W,03:05:40,07:27:55 +37,M,02:29:20,07:27:55 +72,W,03:08:04,07:27:57 +34,M,03:19:31,07:27:59 +54,W,03:14:22,07:28:07 +39,W,02:39:56,07:28:08 +30,W,03:13:02,07:28:18 +75,M,03:00:57,07:28:22 +64,W,03:15:45,07:28:27 +30,W,03:00:08,07:28:30 +70,W,03:07:16,07:28:35 +32,W,02:59:18,07:28:41 +61,M,03:17:39,07:28:46 +67,M,03:02:32,07:28:47 +20,M,03:05:40,07:28:49 +53,W,03:16:26,07:28:51 +25,W,02:52:20,07:28:55 +48,W,03:10:38,07:28:57 +47,W,02:59:39,07:28:59 +42,M,03:15:01,07:29:04 +25,W,03:06:49,07:29:05 +29,M,01:34:29,07:29:12 +46,W,03:03:20,07:29:17 +64,M,03:18:06,07:29:19 +40,W,03:03:02,07:29:23 +63,M,02:44:50,07:29:26 +62,M,03:03:57,07:29:27 +44,M,03:01:30,07:29:32 +65,M,02:53:46,07:29:35 +64,M,02:52:01,07:29:36 +61,W,03:18:41,07:29:43 +45,W,03:03:40,07:29:44 +30,W,03:07:25,07:29:45 +29,W,03:05:00,07:29:49 +40,W,03:03:38,07:29:50 +58,M,03:09:08,07:29:53 +65,W,03:09:26,07:29:57 +61,M,03:06:45,07:29:58 +35,W,03:12:13,07:29:59 +61,W,03:03:48,07:30:09 +71,M,03:18:16,07:30:13 +25,W,02:47:37,07:30:15 +49,W,03:07:06,07:30:17 +42,W,03:15:34,07:30:18 +28,W,02:57:16,07:30:22 +55,M,02:50:01,07:30:25 +37,W,03:01:40,07:30:34 +51,W,03:14:29,07:30:36 +42,W,03:27:05,07:30:42 +43,W,02:52:57,07:30:46 +27,W,03:15:36,07:30:50 +67,M,02:59:25,07:30:51 +61,M,03:29:43,07:30:53 +32,M,03:01:07,07:30:57 +67,M,03:13:41,07:31:09 +47,W,03:03:50,07:31:12 +28,M,02:45:34,07:31:14 +71,M,03:11:40,07:31:19 +22,M,02:45:30,07:31:23 +47,M,03:00:58,07:31:59 +40,W,03:35:40,07:32:01 +63,W,03:11:39,07:32:01 +63,M,03:09:36,07:32:08 +62,M,02:46:04,07:32:10 +64,M,03:00:49,07:32:13 +53,W,03:10:01,07:32:17 +61,M,03:09:28,07:32:18 +21,W,03:12:09,07:32:23 +54,W,03:10:47,07:32:27 +50,W,03:07:43,07:32:34 +48,M,03:13:26,07:32:34 +36,W,03:08:54,07:32:35 +69,M,03:16:48,07:32:48 +66,M,03:09:27,07:32:51 +84,M,03:18:03,07:32:53 +51,W,02:59:36,07:32:56 +67,M,03:01:11,07:32:58 +26,W,03:04:01,07:33:02 +41,M,03:33:50,07:33:06 +39,W,03:03:36,07:33:07 +36,M,03:04:20,07:33:19 +62,W,03:03:56,07:33:20 +51,W,03:23:18,07:33:20 +36,W,03:02:39,07:33:21 +47,M,03:16:44,07:33:22 +35,W,03:04:41,07:33:24 +42,M,03:19:04,07:33:25 +60,W,03:11:00,07:33:31 +29,M,02:52:41,07:33:32 +43,W,03:04:21,07:33:34 +33,W,03:12:33,07:33:35 +41,M,02:57:31,07:33:40 +46,M,03:00:04,07:33:41 +41,M,02:59:52,07:33:46 +43,M,03:30:38,07:33:54 +69,W,03:21:18,07:33:57 +26,W,03:02:11,07:34:02 +67,W,03:06:41,07:34:04 +49,W,03:30:29,07:34:05 +29,M,03:10:58,07:34:09 +50,M,02:52:46,07:34:16 +55,W,02:37:53,07:34:18 +52,M,03:01:18,07:34:18 +68,M,03:31:20,07:34:32 +28,W,02:53:27,07:34:33 +62,M,03:23:44,07:34:48 +46,W,03:19:40,07:34:51 +75,M,03:26:15,07:34:57 +38,W,03:15:44,07:34:58 +61,M,03:08:53,07:35:00 +42,W,02:51:22,07:35:00 +52,M,03:01:48,07:35:03 +25,W,03:03:08,07:35:07 +56,W,03:12:34,07:35:09 +62,W,03:08:12,07:35:09 +27,W,03:09:53,07:35:17 +42,W,03:18:28,07:35:24 +46,W,03:22:51,07:35:25 +41,M,03:25:56,07:35:28 +58,W,03:14:38,07:35:31 +49,W,03:03:46,07:35:37 +31,M,03:01:22,07:35:42 +36,W,03:12:07,07:35:44 +67,M,03:42:10,07:35:45 +32,W,03:05:09,07:35:49 +42,W,03:09:13,07:35:52 +34,M,03:01:43,07:35:58 +52,W,03:14:07,07:36:02 +39,W,03:18:59,07:36:07 +47,W,03:10:04,07:36:10 +44,M,03:35:34,07:36:10 +58,M,03:09:44,07:36:11 +40,W,03:06:40,07:36:21 +50,M,03:31:10,07:36:23 +37,W,03:08:26,07:36:27 +53,M,03:03:42,07:36:28 +50,M,02:48:26,07:36:35 +31,W,03:21:18,07:36:38 +70,W,03:12:09,07:36:41 +30,M,03:14:24,07:36:46 +40,M,02:57:21,07:36:48 +59,M,03:00:52,07:37:02 +55,M,03:06:00,07:37:06 +62,W,03:19:28,07:37:11 +56,M,03:00:03,07:37:11 +41,W,03:10:24,07:37:14 +52,M,03:20:32,07:37:19 +46,W,03:01:06,07:37:19 +32,W,03:04:49,07:37:23 +60,M,03:18:45,07:37:27 +58,M,03:11:29,07:37:32 +25,W,03:06:24,07:37:37 +51,W,03:20:04,07:37:38 +32,W,03:07:47,07:37:44 +63,W,03:16:28,07:37:45 +53,W,03:09:03,07:37:47 +54,W,03:11:07,07:37:52 +51,W,03:01:18,07:37:57 +54,M,03:19:54,07:38:00 +23,M,02:53:25,07:38:01 +34,W,03:08:37,07:38:03 +48,W,03:17:32,07:38:11 +55,M,04:04:29,07:38:21 +65,M,02:59:03,07:38:33 +64,W,03:07:55,07:38:34 +58,M,03:21:34,07:38:35 +55,W,03:18:40,07:38:37 +62,W,02:56:40,07:38:43 +46,W,03:04:26,07:38:56 +30,M,03:04:24,07:39:03 +45,W,03:17:06,07:39:04 +33,W,03:02:15,07:39:14 +48,W,03:37:08,07:39:17 +42,W,03:12:08,07:39:18 +60,M,03:20:31,07:39:18 +31,M,03:03:25,07:39:21 +61,M,03:05:20,07:39:31 +56,W,03:27:35,07:39:38 +46,W,03:19:48,07:39:42 +39,W,03:19:42,07:39:52 +50,W,03:13:54,07:39:53 +28,M,03:04:08,07:39:54 +48,M,03:42:45,07:39:55 +46,W,03:04:43,07:39:56 +27,W,03:13:22,07:40:05 +55,M,03:04:32,07:40:06 +53,W,03:19:07,07:40:08 +60,W,03:18:08,07:40:11 +37,M,02:48:47,07:40:14 +24,W,02:58:44,07:40:19 +36,M,03:03:07,07:40:21 +35,M,03:01:17,07:40:22 +61,W,03:21:14,07:40:22 +60,W,03:16:29,07:40:25 +36,M,02:52:27,07:40:31 +44,M,03:20:32,07:40:43 +48,W,03:15:11,07:40:44 +53,M,03:17:49,07:40:47 +54,W,03:18:44,07:40:49 +31,W,03:07:56,07:40:52 +18,M,03:09:48,07:40:52 +33,W,03:24:54,07:40:56 +53,M,03:05:51,07:40:56 +38,W,03:15:43,07:40:58 +36,W,03:12:29,07:41:04 +42,W,03:22:18,07:41:06 +54,M,03:26:57,07:41:06 +35,M,03:11:26,07:41:08 +20,M,02:47:51,07:41:13 +35,W,03:13:56,07:41:21 +52,W,03:15:02,07:41:26 +53,W,03:07:42,07:41:26 +36,W,03:05:16,07:41:29 +49,W,03:17:09,07:41:30 +29,W,03:20:59,07:41:35 +64,M,03:17:23,07:41:36 +48,M,03:12:09,07:41:38 +57,W,03:21:24,07:41:44 +26,W,03:23:57,07:41:49 +38,W,03:24:57,07:41:55 +61,W,03:16:41,07:41:55 +61,M,03:21:02,07:42:01 +40,M,03:22:34,07:42:03 +57,W,03:38:50,07:42:04 +81,M,03:32:58,07:42:12 +44,W,03:11:42,07:42:19 +58,M,03:19:49,07:42:19 +49,M,03:18:53,07:42:23 +55,W,03:35:03,07:42:26 +39,W,03:16:22,07:42:32 +49,W,03:15:04,07:42:34 +33,W,03:21:00,07:42:38 +46,W,03:00:14,07:42:39 +57,W,03:36:20,07:42:55 +40,W,03:05:31,07:42:55 +28,W,03:22:31,07:42:59 +70,W,03:00:27,07:43:00 +30,M,03:11:38,07:43:02 +29,W,03:12:39,07:43:07 +66,M,03:36:15,07:43:12 +30,W,03:12:42,07:43:12 +37,W,03:14:56,07:43:12 +50,W,03:12:36,07:43:13 +39,M,03:08:54,07:43:13 +52,W,03:17:26,07:43:13 +31,W,02:47:56,07:43:14 +57,M,03:19:46,07:43:15 +31,W,03:36:08,07:43:20 +33,M,03:15:53,07:43:21 +58,M,02:59:56,07:43:25 +43,M,03:04:28,07:43:34 +63,W,03:09:20,07:43:46 +48,W,03:18:08,07:43:49 +45,W,03:12:12,07:43:53 +68,M,03:18:40,07:44:01 +54,M,03:02:00,07:44:02 +50,M,03:06:10,07:44:10 +41,M,03:05:19,07:44:12 +37,M,02:44:28,07:44:26 +63,M,02:40:05,07:44:29 +42,W,03:06:13,07:44:31 +68,W,03:36:22,07:44:33 +48,M,03:05:56,07:44:33 +64,W,03:23:49,07:44:37 +56,M,03:12:59,07:44:54 +63,M,02:51:49,07:45:00 +36,M,03:09:04,07:45:02 +63,W,03:36:45,07:45:04 +44,W,03:09:19,07:45:18 +62,M,03:02:41,07:45:25 +44,M,02:53:05,07:45:27 +59,W,03:25:04,07:45:35 +29,W,03:25:20,07:45:42 +54,W,03:22:19,07:45:52 +49,W,03:01:45,07:46:00 +42,W,03:16:41,07:46:09 +35,M,03:07:10,07:46:15 +44,W,03:27:50,07:46:25 +45,W,03:27:08,07:46:30 +57,W,03:07:12,07:46:34 +54,M,03:04:55,07:46:35 +59,M,03:02:19,07:46:50 +58,W,03:15:55,07:47:03 +71,M,03:13:59,07:47:10 +27,W,03:13:09,07:47:23 +53,M,03:06:30,07:47:29 +50,M,03:28:28,07:47:32 +30,M,03:24:10,07:47:43 +51,W,03:20:57,07:47:52 +39,M,03:15:17,07:48:03 +48,M,03:04:27,07:48:03 +47,W,03:14:36,07:48:20 +59,M,03:24:07,07:48:36 +41,W,02:59:22,07:48:46 +47,M,03:24:46,07:48:47 +43,M,02:51:11,07:48:58 +35,M,03:22:56,07:49:00 +40,M,03:16:27,07:49:03 +24,M,02:47:32,07:49:22 +28,W,03:06:06,07:49:31 +57,W,02:57:22,07:49:31 +23,W,03:15:55,07:49:35 +26,W,03:21:50,07:49:44 +70,W,03:38:14,07:50:09 +28,W,03:24:42,07:50:15 +26,W,03:05:27,07:50:22 +61,M,03:29:12,07:50:37 +26,M,02:59:01,07:50:42 +32,M,03:02:45,07:50:59 +56,M,03:26:20,07:51:25 +61,W,03:12:00,07:51:30 +32,W,03:19:55,07:51:48 +39,W,03:16:30,07:51:48 +56,M,03:27:48,07:51:50 +48,W,03:54:05,07:51:50 +42,M,03:06:23,07:52:14 +20,M,03:35:29,07:52:46 +79,M,03:22:19,07:52:47 +64,M,03:21:32,07:52:50 +74,W,03:19:19,07:52:59 +55,W,03:53:37,07:53:02 +46,W,03:29:42,07:53:16 +56,W,03:07:48,07:53:22 +46,M,03:35:42,07:53:38 +58,W,03:20:34,07:53:40 +60,W,03:22:09,07:53:47 +47,M,03:20:25,07:53:48 +63,M,03:22:31,07:53:59 +49,W,03:41:29,07:54:21 +35,W,03:21:43,07:54:24 +52,W,03:12:37,07:54:41 +69,M,03:17:00,07:54:43 +42,W,03:20:40,07:54:43 +60,M,03:38:34,07:54:49 +52,W,03:20:33,07:54:50 +41,M,02:52:08,07:54:53 +38,W,03:39:25,07:55:06 +67,M,03:22:34,07:55:20 +41,M,03:21:07,07:55:20 +43,M,03:05:18,07:55:26 +41,W,03:24:12,07:55:37 +49,W,03:11:59,07:55:38 +78,M,03:14:26,07:55:40 +59,M,02:50:16,07:55:43 +80,M,03:20:18,07:55:45 +47,W,03:17:05,07:55:48 +61,M,03:16:38,07:55:51 +29,M,03:05:01,07:55:55 +44,W,03:15:54,07:56:01 +43,W,03:39:24,07:56:11 +44,M,03:14:53,07:56:21 +38,W,03:19:35,07:56:34 +54,W,03:29:16,07:56:37 +31,W,03:20:52,07:56:48 +40,W,02:47:53,07:57:07 +48,W,03:10:06,07:57:10 +29,M,03:05:29,07:57:16 +38,W,03:12:13,07:57:17 +39,M,03:26:35,07:57:33 +70,M,03:00:13,07:57:45 +74,W,03:47:36,07:58:05 +46,M,02:57:56,07:58:06 +49,W,03:47:46,07:58:11 +49,W,03:13:05,07:58:11 +54,W,03:18:23,07:58:12 +32,W,03:03:44,07:58:19 +38,W,03:04:32,07:58:23 +31,M,03:13:08,07:58:23 +39,W,03:12:24,07:58:24 +53,W,03:29:03,07:58:27 +54,M,03:22:43,07:58:32 +34,W,03:05:45,07:58:36 +59,M,03:12:10,07:58:53 +53,W,03:59:21,07:59:20 +35,W,03:29:48,07:59:27 +51,M,03:23:14,07:59:32 +47,W,03:24:06,07:59:33 +25,W,03:13:38,07:59:47 +36,W,03:06:45,07:59:50 +61,W,03:25:08,07:59:54 +44,W,03:26:15,08:00:04 +42,W,03:27:09,08:00:04 +80,M,03:15:36,08:00:15 +31,W,03:35:47,08:00:20 +31,W,03:24:55,08:00:26 +55,W,03:25:20,08:00:27 +36,W,03:13:26,08:00:30 +57,W,03:20:56,08:00:32 +36,W,03:16:04,08:00:38 +57,M,03:59:21,08:00:47 +54,W,03:20:35,08:00:49 +46,W,03:50:57,08:01:02 +29,W,03:35:28,08:01:19 +62,W,03:43:21,08:01:23 +40,W,03:54:12,08:01:37 +63,W,03:01:14,08:01:42 +51,W,03:04:55,08:01:48 +61,W,03:22:53,08:02:00 +32,W,03:14:57,08:02:19 +48,W,03:17:58,08:02:28 +43,W,03:34:04,08:02:31 +59,M,03:15:44,08:03:16 +29,M,03:05:16,08:03:19 +26,M,03:24:45,08:03:35 +52,M,03:14:07,08:03:38 +60,M,03:21:42,08:03:52 +63,M,03:04:40,08:04:18 +59,M,03:51:49,08:04:25 +44,W,03:21:57,08:04:47 +64,M,03:20:45,08:04:51 +45,W,03:18:21,08:05:14 +43,W,03:31:53,08:05:32 +30,M,03:28:37,08:05:42 +31,W,03:31:40,08:05:43 +50,W,03:31:39,08:05:49 +29,W,03:23:56,08:06:03 +51,M,04:00:28,08:06:14 +65,W,03:31:21,08:06:23 +36,W,02:55:26,08:06:24 +50,W,03:31:22,08:06:30 +36,M,03:46:34,08:07:21 +50,W,03:20:48,08:07:21 +73,M,03:23:53,08:07:27 +54,W,03:33:11,08:07:27 +47,W,03:31:41,08:07:41 +70,M,03:40:20,08:07:44 +58,W,03:30:15,08:07:48 +25,W,03:46:32,08:07:59 +54,W,03:32:53,08:08:19 +27,W,03:32:38,08:08:22 +66,M,03:33:21,08:08:28 +26,M,03:35:57,08:08:37 +62,W,03:33:15,08:08:44 +57,W,03:20:19,08:08:46 +57,W,03:33:06,08:09:00 +41,M,03:22:43,08:09:10 +60,M,03:20:31,08:09:23 +49,W,03:18:07,08:09:58 +58,W,03:27:37,08:10:01 +63,W,03:26:19,08:10:14 +49,M,03:16:49,08:10:23 +42,W,03:27:45,08:10:28 +28,W,03:25:12,08:10:46 +35,W,03:27:24,08:10:47 +51,W,03:33:08,08:11:08 +49,M,03:34:41,08:11:17 +51,M,03:17:27,08:11:22 +39,W,03:21:12,08:11:30 +51,W,03:37:02,08:11:41 +33,M,03:38:32,08:11:42 +51,M,03:06:03,08:11:49 +34,M,02:41:56,08:11:56 +59,M,03:13:01,08:12:05 +55,W,03:41:56,08:12:17 +37,M,03:31:29,08:12:22 +34,W,03:15:01,08:12:37 +53,M,03:42:52,08:13:04 +66,M,03:07:04,08:13:51 +43,W,03:05:19,08:14:40 +40,M,03:07:11,08:14:50 +45,M,03:05:17,08:14:57 +54,M,03:28:13,08:15:01 +80,M,03:23:11,08:15:35 +36,W,03:20:20,08:16:08 +26,W,03:27:01,08:16:25 +46,M,03:28:03,08:16:35 +36,M,03:06:53,08:16:58 +63,M,03:31:42,08:17:29 +63,W,03:24:15,08:17:48 +71,M,03:35:08,08:17:48 +56,W,03:27:30,08:17:48 +36,W,03:31:11,08:17:56 +57,M,03:35:48,08:17:57 +49,W,03:29:28,08:18:03 +46,M,03:33:31,08:18:06 +48,W,03:39:29,08:18:17 +56,W,03:25:36,08:18:55 +61,M,03:31:37,08:19:37 +26,W,03:26:02,08:19:49 +66,M,03:41:36,08:20:20 +21,W,03:28:13,08:20:38 +71,M,03:34:11,08:21:09 +61,W,03:46:59,08:21:15 +48,W,03:49:14,08:21:36 +66,M,03:36:02,08:22:28 +59,W,03:25:07,08:22:41 +58,W,03:43:08,08:22:47 +83,M,03:25:06,08:22:47 +27,M,03:19:36,08:22:50 +20,M,04:12:16,08:22:58 +31,W,03:23:49,08:24:47 +39,M,03:17:37,08:25:13 +45,W,03:33:39,08:25:43 +51,M,03:25:41,08:26:06 +49,W,03:21:33,08:26:08 +47,W,03:54:22,08:26:58 +55,M,03:47:46,08:27:30 +55,W,03:29:23,08:27:31 +52,W,03:13:28,08:27:38 +46,W,03:45:42,08:28:48 +19,M,02:47:35,08:29:32 +61,M,03:38:30,08:29:39 +43,W,03:41:01,08:30:44 +47,M,02:59:42,08:31:01 +47,W,03:24:51,08:32:09 +44,W,03:30:59,08:32:19 +39,M,03:25:34,08:32:27 +26,W,03:09:19,08:33:12 +60,M,03:54:38,08:33:27 +37,W,03:15:31,08:33:56 +23,M,03:43:42,08:33:57 +50,W,04:05:13,08:34:28 +64,M,04:04:39,08:34:53 +42,W,03:36:51,08:35:23 +51,M,03:16:57,08:35:23 +86,M,03:41:32,08:35:30 +25,W,03:33:32,08:37:08 +64,W,03:33:19,08:37:34 +42,M,03:15:16,08:37:52 +65,W,03:57:52,08:38:49 +60,M,04:15:18,08:39:10 +34,M,03:00:44,08:39:33 +83,W,03:39:09,08:39:38 +80,W,03:26:44,08:39:42 +36,M,03:38:56,08:42:18 +38,W,03:40:07,08:43:03 +70,M,03:25:11,08:43:26 +39,M,03:14:48,08:45:18 +52,M,03:20:00,08:46:04 +53,M,03:40:34,08:46:39 +60,M,03:39:16,08:46:48 +57,M,03:47:08,08:46:58 +61,M,03:32:20,08:47:25 +41,M,03:43:25,08:49:12 +24,M,03:34:01,08:49:36 +79,M,03:28:31,08:49:46 +24,W,03:35:21,08:50:47 +60,W,03:49:33,08:50:58 +50,W,03:43:48,08:52:14 +35,W,02:53:57,08:54:12 +43,W,03:41:15,08:57:29 +27,W,03:37:49,08:57:41 +47,W,03:38:05,08:57:45 +37,W,03:48:55,08:58:03 +55,M,03:48:44,08:58:38 +59,W,03:40:39,08:58:39 +71,M,03:48:30,09:00:00 +68,W,03:49:51,09:00:48 +73,M,03:46:01,09:01:31 +42,W,03:27:53,09:05:44 +64,W,04:27:19,09:10:58 +67,M,04:19:47,09:16:15 +32,W,04:15:33,09:32:56 +18,M,04:24:24,09:32:57 +36,M,04:35:43,09:33:28 +51,M,04:22:35,09:33:40 +55,W,04:58:06,10:00:40 +58,W,04:59:49,10:01:08 diff --git a/examples/pdsh/pdsh_04_00_introduction_to_matplotlib.ipynb b/examples/pdsh/pdsh_04_00_introduction_to_matplotlib.ipynb new file mode 100644 index 00000000..b32d420b --- /dev/null +++ b/examples/pdsh/pdsh_04_00_introduction_to_matplotlib.ipynb @@ -0,0 +1,226 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Introduction To Matplotlib — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Visualization with Matplotlib" + ] + }, + { + "cell_type": "markdown", + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "source": [ + "# General Matplotlib Tips" + ] + }, + { + "cell_type": "markdown", + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "source": [ + "## Importing Matplotlib" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as mpl\n", + "import xy.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "source": [ + "## Setting Styles" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "plt.style.use(\"default\")" + ] + }, + { + "cell_type": "markdown", + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "source": [ + "## show or No show? How to Display Your Plots" + ] + }, + { + "cell_type": "markdown", + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "source": [ + "### Plotting from a Script" + ] + }, + { + "cell_type": "markdown", + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "source": [ + "### Plotting from an IPython Shell" + ] + }, + { + "cell_type": "markdown", + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "source": [ + "### Plotting from a Jupyter Notebook" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "x = np.linspace(0, 10, 100)\n", + "\n", + "fig = plt.figure()\n", + "plt.plot(x, np.sin(x), \"-\")\n", + "plt.plot(x, np.cos(x), \"--\");" + ] + }, + { + "cell_type": "markdown", + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "source": [ + "### Saving Figures to File" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "outputs": [], + "source": [ + "fig.savefig(\"my_figure.png\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Image\n", + "\n", + "Image(\"my_figure.png\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "outputs": [], + "source": [ + "fig.canvas.get_supported_filetypes()" + ] + }, + { + "cell_type": "markdown", + "id": "c3933fab20d04ec698c2621248eb3be0", + "metadata": {}, + "source": [ + "### Two Interfaces for the Price of One" + ] + }, + { + "cell_type": "markdown", + "id": "4dd4641cc4064e0191573fe9c69df29b", + "metadata": {}, + "source": [ + "#### MATLAB-style Interface" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8309879909854d7188b41380fd92a7c3", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure() # create a plot figure\n", + "\n", + "# create the first of two panels and set current axis\n", + "plt.subplot(2, 1, 1) # (rows, columns, panel number)\n", + "plt.plot(x, np.sin(x))\n", + "\n", + "# create the second panel and set current axis\n", + "plt.subplot(2, 1, 2)\n", + "plt.plot(x, np.cos(x));" + ] + }, + { + "cell_type": "markdown", + "id": "3ed186c9a28b402fb0bc4494df01f08d", + "metadata": {}, + "source": [ + "#### Object-oriented interface" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb1e1581032b452c9409d6c6813c49d1", + "metadata": {}, + "outputs": [], + "source": [ + "# First create a grid of plots\n", + "# ax will be an array of two Axes objects\n", + "fig, ax = plt.subplots(2)\n", + "\n", + "# Call plot() method on the appropriate object\n", + "ax[0].plot(x, np.sin(x))\n", + "ax[1].plot(x, np.cos(x));" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_01_simple_line_plots.ipynb b/examples/pdsh/pdsh_04_01_simple_line_plots.ipynb new file mode 100644 index 00000000..1e5c7949 --- /dev/null +++ b/examples/pdsh/pdsh_04_01_simple_line_plots.ipynb @@ -0,0 +1,259 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Simple Line Plots — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Simple Line Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "\n", + "plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure()\n", + "ax = plt.axes()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure()\n", + "ax = plt.axes()\n", + "\n", + "x = np.linspace(0, 10, 1000)\n", + "ax.plot(x, np.sin(x));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, np.sin(x));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, np.sin(x))\n", + "plt.plot(x, np.cos(x));" + ] + }, + { + "cell_type": "markdown", + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "source": [ + "## Adjusting the Plot: Line Colors and Styles" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, np.sin(x - 0), color=\"blue\") # specify color by name\n", + "plt.plot(x, np.sin(x - 1), color=\"g\") # short color code (rgbcmyk)\n", + "plt.plot(x, np.sin(x - 2), color=\"0.75\") # grayscale between 0 and 1\n", + "plt.plot(x, np.sin(x - 3), color=\"#FFDD44\") # hex code (RRGGBB, 00 to FF)\n", + "plt.plot(x, np.sin(x - 4), color=(1.0, 0.2, 0.3)) # RGB tuple, values 0 to 1\n", + "plt.plot(x, np.sin(x - 5), color=\"chartreuse\"); # HTML color names supported" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, x + 0, linestyle=\"solid\")\n", + "plt.plot(x, x + 1, linestyle=\"dashed\")\n", + "plt.plot(x, x + 2, linestyle=\"dashdot\")\n", + "plt.plot(x, x + 3, linestyle=\"dotted\")\n", + "# For short, you can use the following codes:\n", + "plt.plot(x, x + 4, linestyle=\"-\") # solid\n", + "plt.plot(x, x + 5, linestyle=\"--\") # dashed\n", + "plt.plot(x, x + 6, linestyle=\"-.\") # dashdot\n", + "plt.plot(x, x + 7, linestyle=\":\"); # dotted" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, x + 0, \"-g\") # solid green\n", + "plt.plot(x, x + 1, \"--c\") # dashed cyan\n", + "plt.plot(x, x + 2, \"-.k\") # dashdot black\n", + "plt.plot(x, x + 3, \":r\"); # dotted red" + ] + }, + { + "cell_type": "markdown", + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "source": [ + "## Adjusting the Plot: Axes Limits" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, np.sin(x))\n", + "\n", + "plt.xlim(-1, 11)\n", + "plt.ylim(-1.5, 1.5);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, np.sin(x))\n", + "\n", + "plt.xlim(10, 0)\n", + "plt.ylim(1.2, -1.2);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, np.sin(x))\n", + "plt.axis(\"tight\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, np.sin(x))\n", + "plt.axis(\"equal\");" + ] + }, + { + "cell_type": "markdown", + "id": "c3933fab20d04ec698c2621248eb3be0", + "metadata": {}, + "source": [ + "## Labeling Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dd4641cc4064e0191573fe9c69df29b", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, np.sin(x))\n", + "plt.title(\"A Sine Curve\")\n", + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"sin(x)\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8309879909854d7188b41380fd92a7c3", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, np.sin(x), \"-g\", label=\"sin(x)\")\n", + "plt.plot(x, np.cos(x), \":b\", label=\"cos(x)\")\n", + "plt.axis(\"equal\")\n", + "\n", + "plt.legend();" + ] + }, + { + "cell_type": "markdown", + "id": "3ed186c9a28b402fb0bc4494df01f08d", + "metadata": {}, + "source": [ + "## Matplotlib Gotchas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb1e1581032b452c9409d6c6813c49d1", + "metadata": {}, + "outputs": [], + "source": [ + "ax = plt.axes()\n", + "ax.plot(x, np.sin(x))\n", + "ax.set(xlim=(0, 10), ylim=(-2, 2), xlabel=\"x\", ylabel=\"sin(x)\", title=\"A Simple Plot\");" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_02_simple_scatter_plots.ipynb b/examples/pdsh/pdsh_04_02_simple_scatter_plots.ipynb new file mode 100644 index 00000000..39d1e3a1 --- /dev/null +++ b/examples/pdsh/pdsh_04_02_simple_scatter_plots.ipynb @@ -0,0 +1,176 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Simple Scatter Plots — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Simple Scatter Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "\n", + "plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "source": [ + "## Scatter Plots with plt.plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.linspace(0, 10, 30)\n", + "y = np.sin(x)\n", + "\n", + "plt.plot(x, y, \"o\", color=\"black\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "rng = np.random.default_rng(0)\n", + "for marker in [\"o\", \".\", \",\", \"x\", \"+\", \"v\", \"^\", \"<\", \">\", \"s\", \"d\"]:\n", + " plt.plot(\n", + " rng.random(2), rng.random(2), marker, color=\"black\", label=\"marker='{0}'\".format(marker)\n", + " )\n", + "plt.legend(numpoints=1, fontsize=13)\n", + "plt.xlim(0, 1.8);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, y, \"-ok\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(\n", + " x,\n", + " y,\n", + " \"-p\",\n", + " color=\"gray\",\n", + " markersize=15,\n", + " linewidth=4,\n", + " markerfacecolor=\"white\",\n", + " markeredgecolor=\"gray\",\n", + " markeredgewidth=2,\n", + ")\n", + "plt.ylim(-1.2, 1.2);" + ] + }, + { + "cell_type": "markdown", + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "source": [ + "## Scatter Plots with plt.scatter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "plt.scatter(x, y, marker=\"o\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "outputs": [], + "source": [ + "rng = np.random.default_rng(0)\n", + "x = rng.normal(size=100)\n", + "y = rng.normal(size=100)\n", + "colors = rng.random(100)\n", + "sizes = 1000 * rng.random(100)\n", + "\n", + "plt.scatter(x, y, c=colors, s=sizes, alpha=0.3)\n", + "plt.colorbar(); # show color scale" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.datasets import load_iris\n", + "\n", + "iris = load_iris()\n", + "features = iris.data.T\n", + "\n", + "plt.scatter(features[0], features[1], alpha=0.4, s=100 * features[3], c=iris.target, cmap=\"viridis\")\n", + "plt.xlabel(iris.feature_names[0])\n", + "plt.ylabel(iris.feature_names[1]);" + ] + }, + { + "cell_type": "markdown", + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "source": [ + "## plot Versus scatter: A Note on Efficiency" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_03_errorbars.ipynb b/examples/pdsh/pdsh_04_03_errorbars.ipynb new file mode 100644 index 00000000..aa40514d --- /dev/null +++ b/examples/pdsh/pdsh_04_03_errorbars.ipynb @@ -0,0 +1,124 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Errorbars — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Visualizing Uncertainties" + ] + }, + { + "cell_type": "markdown", + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "source": [ + "## Basic Errorbars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "\n", + "plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.linspace(0, 10, 50)\n", + "dy = 0.8\n", + "y = np.sin(x) + dy * np.random.randn(50)\n", + "\n", + "plt.errorbar(x, y, yerr=dy, fmt=\".k\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "plt.errorbar(x, y, yerr=dy, fmt=\"o\", color=\"black\", ecolor=\"lightgray\", elinewidth=3, capsize=0);" + ] + }, + { + "cell_type": "markdown", + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "source": [ + "## Continuous Errors" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.gaussian_process import GaussianProcessRegressor\n", + "\n", + "# define the model and draw some data\n", + "model = lambda x: x * np.sin(x)\n", + "xdata = np.array([1, 3, 5, 6, 8])\n", + "ydata = model(xdata)\n", + "\n", + "# Compute the Gaussian process fit\n", + "gp = GaussianProcessRegressor()\n", + "gp.fit(xdata[:, np.newaxis], ydata)\n", + "\n", + "xfit = np.linspace(0, 10, 1000)\n", + "yfit, dyfit = gp.predict(xfit[:, np.newaxis], return_std=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize the result\n", + "plt.plot(xdata, ydata, \"or\")\n", + "plt.plot(xfit, yfit, \"-\", color=\"gray\")\n", + "plt.fill_between(xfit, yfit - dyfit, yfit + dyfit, color=\"gray\", alpha=0.2)\n", + "plt.xlim(0, 10);" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_04_density_and_contour_plots.ipynb b/examples/pdsh/pdsh_04_04_density_and_contour_plots.ipynb new file mode 100644 index 00000000..05ef6d45 --- /dev/null +++ b/examples/pdsh/pdsh_04_04_density_and_contour_plots.ipynb @@ -0,0 +1,139 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Density and Contour Plots — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Density and Contour Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "\n", + "plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "source": [ + "## Visualizing a Three-Dimensional Function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "def f(x, y):\n", + " return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.linspace(0, 5, 50)\n", + "y = np.linspace(0, 5, 40)\n", + "\n", + "X, Y = np.meshgrid(x, y)\n", + "Z = f(X, Y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "plt.contour(X, Y, Z, colors=\"black\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "outputs": [], + "source": [ + "plt.contour(X, Y, Z, 20, cmap=\"RdGy\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "outputs": [], + "source": [ + "plt.contourf(X, Y, Z, 20, cmap=\"RdGy\")\n", + "plt.colorbar();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "plt.imshow(\n", + " Z, extent=[0, 5, 0, 5], origin=\"lower\", cmap=\"RdGy\", interpolation=\"gaussian\", aspect=\"equal\"\n", + ")\n", + "plt.colorbar();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "outputs": [], + "source": [ + "contours = plt.contour(X, Y, Z, 3, colors=\"black\")\n", + "plt.clabel(contours, inline=True, fontsize=8)\n", + "\n", + "plt.imshow(Z, extent=[0, 5, 0, 5], origin=\"lower\", cmap=\"RdGy\", alpha=0.5)\n", + "plt.colorbar();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_05_histograms_and_binnings.ipynb b/examples/pdsh/pdsh_04_05_histograms_and_binnings.ipynb new file mode 100644 index 00000000..10dfbf07 --- /dev/null +++ b/examples/pdsh/pdsh_04_05_histograms_and_binnings.ipynb @@ -0,0 +1,211 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Histograms and Binnings — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Histograms, Binnings, and Density" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import xy.pyplot as plt\n", + "\n", + "plt.style.use(\"default\")\n", + "\n", + "rng = np.random.default_rng(1701)\n", + "data = rng.normal(size=1000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "outputs": [], + "source": [ + "plt.hist(data);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "plt.hist(\n", + " data,\n", + " bins=30,\n", + " density=True,\n", + " alpha=0.5,\n", + " histtype=\"stepfilled\",\n", + " color=\"steelblue\",\n", + " edgecolor=\"none\",\n", + ");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "x1 = rng.normal(0, 0.8, 1000)\n", + "x2 = rng.normal(-2, 1, 1000)\n", + "x3 = rng.normal(3, 2, 1000)\n", + "\n", + "kwargs = dict(histtype=\"stepfilled\", alpha=0.3, density=True, bins=40)\n", + "\n", + "plt.hist(x1, **kwargs)\n", + "plt.hist(x2, **kwargs)\n", + "plt.hist(x3, **kwargs);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "counts, bin_edges = np.histogram(data, bins=5)\n", + "print(counts)" + ] + }, + { + "cell_type": "markdown", + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "source": [ + "## Two-Dimensional Histograms and Binnings" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "outputs": [], + "source": [ + "mean = [0, 0]\n", + "cov = [[1, 1], [1, 2]]\n", + "x, y = rng.multivariate_normal(mean, cov, 10000).T" + ] + }, + { + "cell_type": "markdown", + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "source": [ + "### plt.hist2d: Two-dimensional histogram" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "outputs": [], + "source": [ + "plt.hist2d(x, y, bins=30)\n", + "cb = plt.colorbar()\n", + "cb.set_label(\"counts in bin\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "outputs": [], + "source": [ + "counts, xedges, yedges = np.histogram2d(x, y, bins=30)\n", + "print(counts.shape)" + ] + }, + { + "cell_type": "markdown", + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "source": [ + "### plt.hexbin: Hexagonal binnings" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "outputs": [], + "source": [ + "plt.hexbin(x, y, gridsize=30)\n", + "cb = plt.colorbar(label=\"count in bin\")" + ] + }, + { + "cell_type": "markdown", + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "source": [ + "### Kernel density estimation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.stats import gaussian_kde\n", + "\n", + "# fit an array of size [Ndim, Nsamples]\n", + "data = np.vstack([x, y])\n", + "kde = gaussian_kde(data)\n", + "\n", + "# evaluate on a regular grid\n", + "xgrid = np.linspace(-3.5, 3.5, 40)\n", + "ygrid = np.linspace(-6, 6, 40)\n", + "Xgrid, Ygrid = np.meshgrid(xgrid, ygrid)\n", + "Z = kde.evaluate(np.vstack([Xgrid.ravel(), Ygrid.ravel()]))\n", + "\n", + "# Plot the result as an image\n", + "plt.imshow(Z.reshape(Xgrid.shape), origin=\"lower\", aspect=\"auto\", extent=[-3.5, 3.5, -6, 6])\n", + "cb = plt.colorbar()\n", + "cb.set_label(\"density\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_06_customizing_legends.ipynb b/examples/pdsh/pdsh_04_06_customizing_legends.ipynb new file mode 100644 index 00000000..9d2c13f2 --- /dev/null +++ b/examples/pdsh/pdsh_04_06_customizing_legends.ipynb @@ -0,0 +1,230 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Customizing Legends — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Customizing Plot Legends" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "\n", + "plt.style.use(\"default\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.linspace(0, 10, 1000)\n", + "fig, ax = plt.subplots()\n", + "ax.plot(x, np.sin(x), \"-b\", label=\"Sine\")\n", + "ax.plot(x, np.cos(x), \"--r\", label=\"Cosine\")\n", + "ax.axis(\"equal\")\n", + "leg = ax.legend()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "ax.legend(loc=\"upper left\", frameon=True)\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "ax.legend(loc=\"lower center\", ncol=2)\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "outputs": [], + "source": [ + "ax.legend(frameon=True, fancybox=True, framealpha=1, shadow=True, borderpad=1)\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "source": [ + "## Choosing Elements for the Legend" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "y = np.sin(x[:, np.newaxis] + np.pi * np.arange(0, 2, 0.5))\n", + "lines = plt.plot(x, y)\n", + "\n", + "# lines is a list of plt.Line2D instances\n", + "plt.legend(lines[:2], [\"first\", \"second\"], frameon=True);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(x, y[:, 0], label=\"first\")\n", + "plt.plot(x, y[:, 1], label=\"second\")\n", + "plt.plot(x, y[:, 2:])\n", + "plt.legend(frameon=True);" + ] + }, + { + "cell_type": "markdown", + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "source": [ + "## Legend for Size of Points" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "outputs": [], + "source": [ + "# Uncomment to download the data\n", + "# url = ('https://raw.githubusercontent.com/jakevdp/PythonDataScienceHandbook/'\n", + "# 'master/notebooks/data/california_cities.csv')\n", + "# !cd data && curl -O {url}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "cities = pd.read_csv(\"data/california_cities.csv\")\n", + "\n", + "# Extract the data we're interested in\n", + "lat, lon = cities[\"latd\"], cities[\"longd\"]\n", + "population, area = cities[\"population_total\"], cities[\"area_total_km2\"]\n", + "\n", + "# Scatter the points, using size and color but no label\n", + "plt.scatter(\n", + " lon, lat, label=None, c=np.log10(population), cmap=\"viridis\", s=area, linewidth=0, alpha=0.5\n", + ")\n", + "plt.axis(\"equal\")\n", + "plt.xlabel(\"longitude\")\n", + "plt.ylabel(\"latitude\")\n", + "plt.colorbar(label=\"log$_{10}$(population)\")\n", + "plt.clim(3, 7)\n", + "\n", + "# Here we create a legend:\n", + "# we'll plot empty lists with the desired size and label\n", + "for area in [100, 300, 500]:\n", + " plt.scatter([], [], c=\"k\", alpha=0.3, s=area, label=str(area) + \" km$^2$\")\n", + "plt.legend(scatterpoints=1, frameon=False, labelspacing=1, title=\"City Area\")\n", + "\n", + "plt.title(\"California Cities: Area and Population\");" + ] + }, + { + "cell_type": "markdown", + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "source": [ + "## Multiple Legends" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "\n", + "lines = []\n", + "styles = [\"-\", \"--\", \"-.\", \":\"]\n", + "x = np.linspace(0, 10, 1000)\n", + "\n", + "for i in range(4):\n", + " lines += ax.plot(x, np.sin(x - i * np.pi / 2), styles[i], color=\"black\")\n", + "ax.axis(\"equal\")\n", + "\n", + "# Specify the lines and labels of the first legend\n", + "ax.legend(lines[:2], [\"line A\", \"line B\"], loc=\"upper right\")\n", + "\n", + "# Create the second legend and add the artist manually\n", + "from xy.pyplot import Legend # was matplotlib.legend\n", + "\n", + "leg = Legend(ax, lines[2:], [\"line C\", \"line D\"], loc=\"lower right\")\n", + "ax.add_artist(leg);" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_07_customizing_colorbars.ipynb b/examples/pdsh/pdsh_04_07_customizing_colorbars.ipynb new file mode 100644 index 00000000..02c07d80 --- /dev/null +++ b/examples/pdsh/pdsh_04_07_customizing_colorbars.ipynb @@ -0,0 +1,270 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Customizing Colorbars — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Customizing Colorbars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "\n", + "plt.style.use(\"default\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.linspace(0, 10, 1000)\n", + "I = np.sin(x) * np.cos(x[:, np.newaxis])\n", + "\n", + "plt.imshow(I)\n", + "plt.colorbar();" + ] + }, + { + "cell_type": "markdown", + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "source": [ + "## Customizing Colorbars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "plt.imshow(I, cmap=\"Blues\");" + ] + }, + { + "cell_type": "markdown", + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "source": [ + "### Choosing the Colormap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "outputs": [], + "source": [ + "from xy.pyplot import LinearSegmentedColormap # was matplotlib.colors\n", + "\n", + "\n", + "def grayscale_cmap(cmap):\n", + " \"\"\"Return a grayscale version of the given colormap\"\"\"\n", + " cmap = plt.get_cmap(cmap)\n", + " colors = cmap(np.arange(cmap.N))\n", + "\n", + " # Convert RGBA to perceived grayscale luminance\n", + " # cf. http://alienryderflex.com/hsp.html\n", + " RGB_weight = [0.299, 0.587, 0.114]\n", + " luminance = np.sqrt(np.dot(colors[:, :3] ** 2, RGB_weight))\n", + " colors[:, :3] = luminance[:, np.newaxis]\n", + "\n", + " return LinearSegmentedColormap.from_list(cmap.name + \"_gray\", colors, cmap.N)\n", + "\n", + "\n", + "def view_colormap(cmap):\n", + " \"\"\"Plot a colormap with its grayscale equivalent\"\"\"\n", + " cmap = plt.get_cmap(cmap)\n", + " colors = cmap(np.arange(cmap.N))\n", + "\n", + " cmap = grayscale_cmap(cmap)\n", + " grayscale = cmap(np.arange(cmap.N))\n", + "\n", + " fig, ax = plt.subplots(2, figsize=(6, 2), subplot_kw=dict(xticks=[], yticks=[]))\n", + " ax[0].imshow([colors], extent=[0, 10, 0, 1])\n", + " ax[1].imshow([grayscale], extent=[0, 10, 0, 1])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "view_colormap(\"jet\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "outputs": [], + "source": [ + "view_colormap(\"viridis\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "outputs": [], + "source": [ + "view_colormap(\"RdBu\")" + ] + }, + { + "cell_type": "markdown", + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "source": [ + "### Color Limits and Extensions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "outputs": [], + "source": [ + "# make noise in 1% of the image pixels\n", + "speckles = np.random.random(I.shape) < 0.01\n", + "I[speckles] = np.random.normal(0, 3, np.count_nonzero(speckles))\n", + "\n", + "plt.figure(figsize=(10, 3.5))\n", + "\n", + "plt.subplot(1, 2, 1)\n", + "plt.imshow(I, cmap=\"RdBu\")\n", + "plt.colorbar()\n", + "\n", + "plt.subplot(1, 2, 2)\n", + "plt.imshow(I, cmap=\"RdBu\")\n", + "plt.colorbar(extend=\"both\")\n", + "plt.clim(-1, 1)" + ] + }, + { + "cell_type": "markdown", + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "source": [ + "### Discrete Colorbars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "outputs": [], + "source": [ + "plt.imshow(I, cmap=plt.get_cmap(\"Blues\", 6))\n", + "plt.colorbar(extend=\"both\")\n", + "plt.clim(-1, 1);" + ] + }, + { + "cell_type": "markdown", + "id": "c3933fab20d04ec698c2621248eb3be0", + "metadata": {}, + "source": [ + "## Example: Handwritten Digits" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dd4641cc4064e0191573fe9c69df29b", + "metadata": {}, + "outputs": [], + "source": [ + "# load images of the digits 0 through 5 and visualize several of them\n", + "from sklearn.datasets import load_digits\n", + "\n", + "digits = load_digits(n_class=6)\n", + "\n", + "fig, ax = plt.subplots(8, 8, figsize=(6, 6))\n", + "for i, axi in enumerate(ax.flat):\n", + " axi.imshow(digits.images[i], cmap=\"binary\")\n", + " axi.set(xticks=[], yticks=[])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8309879909854d7188b41380fd92a7c3", + "metadata": {}, + "outputs": [], + "source": [ + "# project the digits into 2 dimensions using Isomap\n", + "from sklearn.manifold import Isomap\n", + "\n", + "iso = Isomap(n_components=2, n_neighbors=15)\n", + "projection = iso.fit_transform(digits.data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ed186c9a28b402fb0bc4494df01f08d", + "metadata": {}, + "outputs": [], + "source": [ + "# plot the results\n", + "plt.scatter(\n", + " projection[:, 0], projection[:, 1], lw=0.1, c=digits.target, cmap=plt.get_cmap(\"plasma\", 6)\n", + ")\n", + "plt.colorbar(ticks=range(6), label=\"digit value\")\n", + "plt.clim(-0.5, 5.5)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_08_multiple_subplots.ipynb b/examples/pdsh/pdsh_04_08_multiple_subplots.ipynb new file mode 100644 index 00000000..8c0a647a --- /dev/null +++ b/examples/pdsh/pdsh_04_08_multiple_subplots.ipynb @@ -0,0 +1,211 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Multiple Subplots — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Multiple Subplots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "\n", + "plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "source": [ + "## plt.axes: Subplots by Hand" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "ax1 = plt.axes() # standard axes\n", + "ax2 = plt.axes([0.65, 0.65, 0.2, 0.2])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure()\n", + "ax1 = fig.add_axes([0.1, 0.5, 0.8, 0.4], xticklabels=[], ylim=(-1.2, 1.2))\n", + "ax2 = fig.add_axes([0.1, 0.1, 0.8, 0.4], ylim=(-1.2, 1.2))\n", + "\n", + "x = np.linspace(0, 10)\n", + "ax1.plot(np.sin(x))\n", + "ax2.plot(np.cos(x));" + ] + }, + { + "cell_type": "markdown", + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "source": [ + "## plt.subplot: Simple Grids of Subplots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(1, 7):\n", + " plt.subplot(2, 3, i)\n", + " plt.text(0.5, 0.5, str((2, 3, i)), fontsize=18, ha=\"center\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure()\n", + "fig.subplots_adjust(hspace=0.4, wspace=0.4)\n", + "for i in range(1, 7):\n", + " ax = fig.add_subplot(2, 3, i)\n", + " ax.text(0.5, 0.5, str((2, 3, i)), fontsize=18, ha=\"center\")" + ] + }, + { + "cell_type": "markdown", + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "source": [ + "## plt.subplots: The Whole Grid in One Go" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(2, 3, sharex=\"col\", sharey=\"row\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "outputs": [], + "source": [ + "# axes are in a two-dimensional array, indexed by [row, col]\n", + "for i in range(2):\n", + " for j in range(3):\n", + " ax[i, j].text(0.5, 0.5, str((i, j)), fontsize=18, ha=\"center\")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "source": [ + "## plt.GridSpec: More Complicated Arrangements" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "outputs": [], + "source": [ + "grid = plt.GridSpec(2, 3, wspace=0.4, hspace=0.3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "outputs": [], + "source": [ + "plt.subplot(grid[0, 0])\n", + "plt.subplot(grid[0, 1:])\n", + "plt.subplot(grid[1, :2])\n", + "plt.subplot(grid[1, 2]);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "outputs": [], + "source": [ + "# Create some normally distributed data\n", + "mean = [0, 0]\n", + "cov = [[1, 1], [1, 2]]\n", + "rng = np.random.default_rng(1701)\n", + "x, y = rng.multivariate_normal(mean, cov, 3000).T\n", + "\n", + "# Set up the axes with GridSpec\n", + "fig = plt.figure(figsize=(6, 6))\n", + "grid = plt.GridSpec(4, 4, hspace=0.2, wspace=0.2)\n", + "main_ax = fig.add_subplot(grid[:-1, 1:])\n", + "y_hist = fig.add_subplot(grid[:-1, 0], xticklabels=[], sharey=main_ax)\n", + "x_hist = fig.add_subplot(grid[-1, 1:], yticklabels=[], sharex=main_ax)\n", + "\n", + "# Scatter points on the main axes\n", + "main_ax.plot(x, y, \"ok\", markersize=3, alpha=0.2)\n", + "\n", + "# Histogram on the attached axes\n", + "x_hist.hist(x, 40, histtype=\"stepfilled\", orientation=\"vertical\", color=\"gray\")\n", + "x_hist.invert_yaxis()\n", + "\n", + "y_hist.hist(y, 40, histtype=\"stepfilled\", orientation=\"horizontal\", color=\"gray\")\n", + "y_hist.invert_xaxis()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_09_text_and_annotation.ipynb b/examples/pdsh/pdsh_04_09_text_and_annotation.ipynb new file mode 100644 index 00000000..be961706 --- /dev/null +++ b/examples/pdsh/pdsh_04_09_text_and_annotation.ipynb @@ -0,0 +1,299 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Text and Annotation — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Text and Annotation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "import xy.pyplot as mpl\n", + "\n", + "plt.style.use(\"default\")\n", + "import numpy as np\n", + "import pandas as pd" + ] + }, + { + "cell_type": "markdown", + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "source": [ + "## Example: Effect of Holidays on US Births" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "# shell command to download the data:\n", + "# !cd data && curl -O \\\n", + "# https://raw.githubusercontent.com/jakevdp/data-CDCbirths/master/births.csv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "\n", + "births = pd.read_csv(\"data/births.csv\")\n", + "\n", + "quartiles = np.percentile(births[\"births\"], [25, 50, 75])\n", + "mu, sig = quartiles[1], 0.74 * (quartiles[2] - quartiles[0])\n", + "births = births.query(\"(births > @mu - 5 * @sig) & (births < @mu + 5 * @sig)\")\n", + "\n", + "births[\"day\"] = births[\"day\"].astype(int)\n", + "\n", + "births.index = pd.to_datetime(\n", + " 10000 * births.year + 100 * births.month + births.day, format=\"%Y%m%d\"\n", + ")\n", + "births_by_date = births.pivot_table(\"births\", [births.index.month, births.index.day])\n", + "births_by_date.index = [datetime(2012, month, day) for (month, day) in births_by_date.index]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(figsize=(12, 4))\n", + "births_by_date.plot(ax=ax);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(figsize=(12, 4))\n", + "births_by_date.plot(ax=ax)\n", + "\n", + "# Add labels to the plot\n", + "style = dict(size=10, color=\"gray\")\n", + "\n", + "ax.text(\"2012-1-1\", 3950, \"New Year's Day\", **style)\n", + "ax.text(\"2012-7-4\", 4250, \"Independence Day\", ha=\"center\", **style)\n", + "ax.text(\"2012-9-4\", 4850, \"Labor Day\", ha=\"center\", **style)\n", + "ax.text(\"2012-10-31\", 4600, \"Halloween\", ha=\"right\", **style)\n", + "ax.text(\"2012-11-25\", 4450, \"Thanksgiving\", ha=\"center\", **style)\n", + "ax.text(\"2012-12-25\", 3850, \"Christmas \", ha=\"right\", **style)\n", + "\n", + "# Label the axes\n", + "ax.set(title=\"USA births by day of year (1969-1988)\", ylabel=\"average daily births\")\n", + "\n", + "# Format the x-axis with centered month labels\n", + "ax.xaxis.set_major_locator(mpl.dates.MonthLocator())\n", + "ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))\n", + "ax.xaxis.set_major_formatter(plt.NullFormatter())\n", + "ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter(\"%h\"));" + ] + }, + { + "cell_type": "markdown", + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "source": [ + "## Transforms and Text Position" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(facecolor=\"lightgray\")\n", + "ax.axis([0, 10, 0, 10])\n", + "\n", + "# transform=ax.transData is the default, but we'll specify it anyway\n", + "ax.text(1, 5, \". Data: (1, 5)\", transform=ax.transData)\n", + "ax.text(0.5, 0.1, \". Axes: (0.5, 0.1)\", transform=ax.transAxes)\n", + "ax.text(0.2, 0.2, \". Figure: (0.2, 0.2)\", transform=fig.transFigure);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "outputs": [], + "source": [ + "ax.set_xlim(0, 2)\n", + "ax.set_ylim(-6, 6)\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "source": [ + "## Arrows and Annotation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "\n", + "x = np.linspace(0, 20, 1000)\n", + "ax.plot(x, np.cos(x))\n", + "ax.axis(\"equal\")\n", + "\n", + "ax.annotate(\n", + " \"local maximum\", xy=(6.28, 1), xytext=(10, 4), arrowprops=dict(facecolor=\"black\", shrink=0.05)\n", + ")\n", + "\n", + "ax.annotate(\n", + " \"local minimum\",\n", + " xy=(5 * np.pi, -1),\n", + " xytext=(2, -6),\n", + " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"angle3,angleA=0,angleB=-90\"),\n", + ");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(figsize=(12, 4))\n", + "births_by_date.plot(ax=ax)\n", + "\n", + "# Add labels to the plot\n", + "ax.annotate(\n", + " \"New Year's Day\",\n", + " xy=(\"2012-1-1\", 4100),\n", + " xycoords=\"data\",\n", + " xytext=(50, -30),\n", + " textcoords=\"offset points\",\n", + " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3,rad=-0.2\"),\n", + ")\n", + "\n", + "ax.annotate(\n", + " \"Independence Day\",\n", + " xy=(\"2012-7-4\", 4250),\n", + " xycoords=\"data\",\n", + " bbox=dict(boxstyle=\"round\", fc=\"none\", ec=\"gray\"),\n", + " xytext=(10, -40),\n", + " textcoords=\"offset points\",\n", + " ha=\"center\",\n", + " arrowprops=dict(arrowstyle=\"->\"),\n", + ")\n", + "\n", + "ax.annotate(\n", + " \"Labor Day Weekend\",\n", + " xy=(\"2012-9-4\", 4850),\n", + " xycoords=\"data\",\n", + " ha=\"center\",\n", + " xytext=(0, -20),\n", + " textcoords=\"offset points\",\n", + ")\n", + "ax.annotate(\n", + " \"\",\n", + " xy=(\"2012-9-1\", 4850),\n", + " xytext=(\"2012-9-7\", 4850),\n", + " xycoords=\"data\",\n", + " textcoords=\"data\",\n", + " arrowprops={\n", + " \"arrowstyle\": \"|-|,widthA=0.2,widthB=0.2\",\n", + " },\n", + ")\n", + "\n", + "ax.annotate(\n", + " \"Halloween\",\n", + " xy=(\"2012-10-31\", 4600),\n", + " xycoords=\"data\",\n", + " xytext=(-80, -40),\n", + " textcoords=\"offset points\",\n", + " arrowprops=dict(\n", + " arrowstyle=\"fancy\", fc=\"0.6\", ec=\"none\", connectionstyle=\"angle3,angleA=0,angleB=-90\"\n", + " ),\n", + ")\n", + "\n", + "ax.annotate(\n", + " \"Thanksgiving\",\n", + " xy=(\"2012-11-25\", 4500),\n", + " xycoords=\"data\",\n", + " xytext=(-120, -60),\n", + " textcoords=\"offset points\",\n", + " bbox=dict(boxstyle=\"round4,pad=.5\", fc=\"0.9\"),\n", + " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"angle,angleA=0,angleB=80,rad=20\"),\n", + ")\n", + "\n", + "\n", + "ax.annotate(\n", + " \"Christmas\",\n", + " xy=(\"2012-12-25\", 3850),\n", + " xycoords=\"data\",\n", + " xytext=(-30, 0),\n", + " textcoords=\"offset points\",\n", + " size=13,\n", + " ha=\"right\",\n", + " va=\"center\",\n", + " bbox=dict(boxstyle=\"round\", alpha=0.1),\n", + " arrowprops=dict(arrowstyle=\"wedge,tail_width=0.5\", alpha=0.1),\n", + ")\n", + "# Label the axes\n", + "ax.set(title=\"USA births by day of year (1969-1988)\", ylabel=\"average daily births\")\n", + "\n", + "# Format the x-axis with centered month labels\n", + "ax.xaxis.set_major_locator(mpl.dates.MonthLocator())\n", + "ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))\n", + "ax.xaxis.set_major_formatter(plt.NullFormatter())\n", + "ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter(\"%h\"))\n", + "ax.set_ylim(3600, 5400);" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_10_customizing_ticks.ipynb b/examples/pdsh/pdsh_04_10_customizing_ticks.ipynb new file mode 100644 index 00000000..680ee5b4 --- /dev/null +++ b/examples/pdsh/pdsh_04_10_customizing_ticks.ipynb @@ -0,0 +1,242 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Customizing Ticks — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Customizing Ticks" + ] + }, + { + "cell_type": "markdown", + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "source": [ + "## Major and Minor Ticks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "\n", + "plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "ax = plt.axes(xscale=\"log\", yscale=\"log\")\n", + "ax.set(xlim=(1, 1e3), ylim=(1, 1e3))\n", + "ax.grid(True);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "print(ax.xaxis.get_major_locator())\n", + "print(ax.xaxis.get_minor_locator())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "print(ax.xaxis.get_major_formatter())\n", + "print(ax.xaxis.get_minor_formatter())" + ] + }, + { + "cell_type": "markdown", + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "source": [ + "## Hiding Ticks or Labels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "outputs": [], + "source": [ + "ax = plt.axes()\n", + "rng = np.random.default_rng(1701)\n", + "ax.plot(rng.random(50))\n", + "ax.grid()\n", + "\n", + "ax.yaxis.set_major_locator(plt.NullLocator())\n", + "ax.xaxis.set_major_formatter(plt.NullFormatter())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(5, 5, figsize=(5, 5))\n", + "fig.subplots_adjust(hspace=0, wspace=0)\n", + "\n", + "# Get some face data from Scikit-Learn\n", + "from sklearn.datasets import fetch_olivetti_faces\n", + "\n", + "faces = fetch_olivetti_faces().images\n", + "\n", + "for i in range(5):\n", + " for j in range(5):\n", + " ax[i, j].xaxis.set_major_locator(plt.NullLocator())\n", + " ax[i, j].yaxis.set_major_locator(plt.NullLocator())\n", + " ax[i, j].imshow(faces[10 * i + j], cmap=\"binary_r\")" + ] + }, + { + "cell_type": "markdown", + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "source": [ + "## Reducing or Increasing the Number of Ticks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(4, 4, sharex=True, sharey=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "outputs": [], + "source": [ + "# For every axis, set the x and y major locator\n", + "for axi in ax.flat:\n", + " axi.xaxis.set_major_locator(plt.MaxNLocator(3))\n", + " axi.yaxis.set_major_locator(plt.MaxNLocator(3))\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "source": [ + "## Fancy Tick Formats" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "outputs": [], + "source": [ + "# Plot a sine and cosine curve\n", + "fig, ax = plt.subplots()\n", + "x = np.linspace(0, 3 * np.pi, 1000)\n", + "ax.plot(x, np.sin(x), lw=3, label=\"Sine\")\n", + "ax.plot(x, np.cos(x), lw=3, label=\"Cosine\")\n", + "\n", + "# Set up grid, legend, and limits\n", + "ax.grid(True)\n", + "ax.legend(frameon=False)\n", + "ax.axis(\"equal\")\n", + "ax.set_xlim(0, 3 * np.pi);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "outputs": [], + "source": [ + "ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2))\n", + "ax.xaxis.set_minor_locator(plt.MultipleLocator(np.pi / 4))\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3933fab20d04ec698c2621248eb3be0", + "metadata": {}, + "outputs": [], + "source": [ + "def format_func(value, tick_number):\n", + " # find number of multiples of pi/2\n", + " N = int(np.round(2 * value / np.pi))\n", + " if N == 0:\n", + " return \"0\"\n", + " elif N == 1:\n", + " return r\"$\\pi/2$\"\n", + " elif N == 2:\n", + " return r\"$\\pi$\"\n", + " elif N % 2 > 0:\n", + " return rf\"${N}\\pi/2$\"\n", + " else:\n", + " return rf\"${N // 2}\\pi$\"\n", + "\n", + "\n", + "ax.xaxis.set_major_formatter(plt.FuncFormatter(format_func))\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "4dd4641cc4064e0191573fe9c69df29b", + "metadata": {}, + "source": [ + "## Summary of Formatters and Locators" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_11_settings_and_stylesheets.ipynb b/examples/pdsh/pdsh_04_11_settings_and_stylesheets.ipynb new file mode 100644 index 00000000..d1683359 --- /dev/null +++ b/examples/pdsh/pdsh_04_11_settings_and_stylesheets.ipynb @@ -0,0 +1,316 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Settings and Stylesheets — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Customizing Matplotlib: Configurations and Stylesheets" + ] + }, + { + "cell_type": "markdown", + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "source": [ + "## Plot Customization by Hand" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "\n", + "plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.random.randn(1000)\n", + "plt.hist(x);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "# use a gray background\n", + "fig = plt.figure(facecolor=\"white\")\n", + "ax = plt.axes(facecolor=\"#E6E6E6\")\n", + "ax.set_axisbelow(True)\n", + "\n", + "# draw solid white gridlines\n", + "plt.grid(color=\"w\", linestyle=\"solid\")\n", + "\n", + "# hide axis spines\n", + "for spine in ax.spines.values():\n", + " spine.set_visible(False)\n", + "\n", + "# hide top and right ticks\n", + "ax.xaxis.tick_bottom()\n", + "ax.yaxis.tick_left()\n", + "\n", + "# lighten ticks and labels\n", + "ax.tick_params(colors=\"gray\", direction=\"out\")\n", + "for tick in ax.get_xticklabels():\n", + " tick.set_color(\"gray\")\n", + "for tick in ax.get_yticklabels():\n", + " tick.set_color(\"gray\")\n", + "\n", + "# control face and edge color of histogram\n", + "ax.hist(x, edgecolor=\"#E6E6E6\", color=\"#EE6666\");" + ] + }, + { + "cell_type": "markdown", + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "source": [ + "## Changing the Defaults: rcParams" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "outputs": [], + "source": [ + "from xy.pyplot import cycler\n", + "\n", + "colors = cycler(\"color\", [\"#EE6666\", \"#3388BB\", \"#9988DD\", \"#EECC55\", \"#88BB44\", \"#FFBBBB\"])\n", + "plt.rc(\"figure\", facecolor=\"white\")\n", + "plt.rc(\"axes\", facecolor=\"#E6E6E6\", edgecolor=\"none\", axisbelow=True, grid=True, prop_cycle=colors)\n", + "plt.rc(\"grid\", color=\"w\", linestyle=\"solid\")\n", + "plt.rc(\"xtick\", direction=\"out\", color=\"gray\")\n", + "plt.rc(\"ytick\", direction=\"out\", color=\"gray\")\n", + "plt.rc(\"patch\", edgecolor=\"#E6E6E6\")\n", + "plt.rc(\"lines\", linewidth=2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "outputs": [], + "source": [ + "plt.hist(x);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(4):\n", + " plt.plot(np.random.rand(10))" + ] + }, + { + "cell_type": "markdown", + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "source": [ + "## Stylesheets" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "outputs": [], + "source": [ + "plt.style.available[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "outputs": [], + "source": [ + "def hist_and_lines():\n", + " np.random.seed(0)\n", + " fig, ax = plt.subplots(1, 2, figsize=(11, 4))\n", + " ax[0].hist(np.random.randn(1000))\n", + " for i in range(3):\n", + " ax[1].plot(np.random.rand(10))\n", + " ax[1].legend([\"a\", \"b\", \"c\"], loc=\"lower left\")" + ] + }, + { + "cell_type": "markdown", + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "source": [ + "### Default Style" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "outputs": [], + "source": [ + "with plt.style.context(\"default\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "markdown", + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "source": [ + "### FiveThiryEight Style" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3933fab20d04ec698c2621248eb3be0", + "metadata": {}, + "outputs": [], + "source": [ + "with plt.style.context(\"fivethirtyeight\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "markdown", + "id": "4dd4641cc4064e0191573fe9c69df29b", + "metadata": {}, + "source": [ + "### ggplot Style" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8309879909854d7188b41380fd92a7c3", + "metadata": {}, + "outputs": [], + "source": [ + "with plt.style.context(\"ggplot\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "markdown", + "id": "3ed186c9a28b402fb0bc4494df01f08d", + "metadata": {}, + "source": [ + "### Bayesian Methods for Hackers Style" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb1e1581032b452c9409d6c6813c49d1", + "metadata": {}, + "outputs": [], + "source": [ + "with plt.style.context(\"bmh\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "markdown", + "id": "379cbbc1e968416e875cc15c1202d7eb", + "metadata": {}, + "source": [ + "### Dark Background Style" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277c27b1587741f2af2001be3712ef0d", + "metadata": {}, + "outputs": [], + "source": [ + "with plt.style.context(\"dark_background\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "markdown", + "id": "db7b79bc585a40fcaf58bf750017e135", + "metadata": {}, + "source": [ + "### Grayscale Style" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "916684f9a58a4a2aa5f864670399430d", + "metadata": {}, + "outputs": [], + "source": [ + "with plt.style.context(\"grayscale\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "markdown", + "id": "1671c31a24314836a5b85d7ef7fbf015", + "metadata": {}, + "source": [ + "### Seaborn Style" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33b0902fd34d4ace834912fa1002cf8e", + "metadata": {}, + "outputs": [], + "source": [ + "with plt.style.context(\"default\"):\n", + " hist_and_lines()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_12_three_dimensional_plotting.ipynb b/examples/pdsh/pdsh_04_12_three_dimensional_plotting.ipynb new file mode 100644 index 00000000..7c1ebaf7 --- /dev/null +++ b/examples/pdsh/pdsh_04_12_three_dimensional_plotting.ipynb @@ -0,0 +1,310 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Three Dimensional Plotting — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Three-Dimensional Plotting in Matplotlib" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "outputs": [], + "source": [ + "from mpl_toolkits import mplot3d" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import xy.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure()\n", + "ax = plt.axes(projection=\"3d\")" + ] + }, + { + "cell_type": "markdown", + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "source": [ + "## Three-Dimensional Points and Lines" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "ax = plt.axes(projection=\"3d\")\n", + "\n", + "# Data for a three-dimensional line\n", + "zline = np.linspace(0, 15, 1000)\n", + "xline = np.sin(zline)\n", + "yline = np.cos(zline)\n", + "ax.plot3D(xline, yline, zline, \"gray\")\n", + "\n", + "# Data for three-dimensional scattered points\n", + "zdata = 15 * np.random.random(100)\n", + "xdata = np.sin(zdata) + 0.1 * np.random.randn(100)\n", + "ydata = np.cos(zdata) + 0.1 * np.random.randn(100)\n", + "ax.scatter3D(xdata, ydata, zdata, c=zdata, cmap=\"Greens\");" + ] + }, + { + "cell_type": "markdown", + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "source": [ + "## Three-Dimensional Contour Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "outputs": [], + "source": [ + "def f(x, y):\n", + " return np.sin(np.sqrt(x**2 + y**2))\n", + "\n", + "\n", + "x = np.linspace(-6, 6, 30)\n", + "y = np.linspace(-6, 6, 30)\n", + "\n", + "X, Y = np.meshgrid(x, y)\n", + "Z = f(X, Y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure()\n", + "ax = plt.axes(projection=\"3d\")\n", + "ax.contour3D(X, Y, Z, 40, cmap=\"binary\")\n", + "ax.set_xlabel(\"x\")\n", + "ax.set_ylabel(\"y\")\n", + "ax.set_zlabel(\"z\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "outputs": [], + "source": [ + "ax.view_init(60, 35)\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "source": [ + "## Wireframes and Surface Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure()\n", + "ax = plt.axes(projection=\"3d\")\n", + "ax.plot_wireframe(X, Y, Z)\n", + "ax.set_title(\"wireframe\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "outputs": [], + "source": [ + "ax = plt.axes(projection=\"3d\")\n", + "ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=\"viridis\", edgecolor=\"none\")\n", + "ax.set_title(\"surface\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "outputs": [], + "source": [ + "r = np.linspace(0, 6, 20)\n", + "theta = np.linspace(-0.9 * np.pi, 0.8 * np.pi, 40)\n", + "r, theta = np.meshgrid(r, theta)\n", + "\n", + "X = r * np.sin(theta)\n", + "Y = r * np.cos(theta)\n", + "Z = f(X, Y)\n", + "\n", + "ax = plt.axes(projection=\"3d\")\n", + "ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=\"viridis\", edgecolor=\"none\");" + ] + }, + { + "cell_type": "markdown", + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "source": [ + "## Surface Triangulations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3933fab20d04ec698c2621248eb3be0", + "metadata": {}, + "outputs": [], + "source": [ + "theta = 2 * np.pi * np.random.random(1000)\n", + "r = 6 * np.random.random(1000)\n", + "x = np.ravel(r * np.sin(theta))\n", + "y = np.ravel(r * np.cos(theta))\n", + "z = f(x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dd4641cc4064e0191573fe9c69df29b", + "metadata": {}, + "outputs": [], + "source": [ + "ax = plt.axes(projection=\"3d\")\n", + "ax.scatter(x, y, z, c=z, cmap=\"viridis\", linewidth=0.5);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8309879909854d7188b41380fd92a7c3", + "metadata": {}, + "outputs": [], + "source": [ + "ax = plt.axes(projection=\"3d\")\n", + "ax.plot_trisurf(x, y, z, cmap=\"viridis\", edgecolor=\"none\");" + ] + }, + { + "cell_type": "markdown", + "id": "3ed186c9a28b402fb0bc4494df01f08d", + "metadata": {}, + "source": [ + "## Example: Visualizing a Möbius Strip" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb1e1581032b452c9409d6c6813c49d1", + "metadata": {}, + "outputs": [], + "source": [ + "theta = np.linspace(0, 2 * np.pi, 30)\n", + "w = np.linspace(-0.25, 0.25, 8)\n", + "w, theta = np.meshgrid(w, theta)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "379cbbc1e968416e875cc15c1202d7eb", + "metadata": {}, + "outputs": [], + "source": [ + "phi = 0.5 * theta" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277c27b1587741f2af2001be3712ef0d", + "metadata": {}, + "outputs": [], + "source": [ + "# radius in x-y plane\n", + "r = 1 + w * np.cos(phi)\n", + "\n", + "x = np.ravel(r * np.cos(theta))\n", + "y = np.ravel(r * np.sin(theta))\n", + "z = np.ravel(w * np.sin(phi))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db7b79bc585a40fcaf58bf750017e135", + "metadata": {}, + "outputs": [], + "source": [ + "# triangulate in the underlying parametrization\n", + "from xy.pyplot import Triangulation # was matplotlib.tri\n", + "\n", + "tri = Triangulation(np.ravel(w), np.ravel(theta))\n", + "\n", + "ax = plt.axes(projection=\"3d\")\n", + "ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=\"Greys\", linewidths=0.2)\n", + "ax.set_xlim(-1, 1)\n", + "ax.set_ylim(-1, 1)\n", + "ax.set_zlim(-1, 1)\n", + "ax.axis(\"off\");" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/pdsh/pdsh_04_14_visualization_with_seaborn.ipynb b/examples/pdsh/pdsh_04_14_visualization_with_seaborn.ipynb new file mode 100644 index 00000000..01d2fb72 --- /dev/null +++ b/examples/pdsh/pdsh_04_14_visualization_with_seaborn.ipynb @@ -0,0 +1,475 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Visualization With Seaborn — with `xy.pyplot`\n", + "\n", + "Code cells adapted from the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook) by Jake VanderPlas (MIT-licensed code; the book's prose is omitted).\n", + "The only systematic change is the import: `matplotlib.pyplot` → `xy.pyplot`, plus the same style-name/API modernizations the originals need to run on matplotlib ≥ 3.9 today.\n" + ] + }, + { + "cell_type": "markdown", + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "source": [ + "# Visualization with Seaborn" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "outputs": [], + "source": [ + "import xy.pyplot as plt\n", + "import seaborn as sns\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "sns.set() # seaborn's method to set its chart style" + ] + }, + { + "cell_type": "markdown", + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "source": [ + "## Exploring Seaborn Plots" + ] + }, + { + "cell_type": "markdown", + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "source": [ + "### Histograms, KDE, and Densities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "data = np.random.multivariate_normal([0, 0], [[5, 2], [2, 2]], size=2000)\n", + "data = pd.DataFrame(data, columns=[\"x\", \"y\"])\n", + "\n", + "for col in \"xy\":\n", + " plt.hist(data[col], density=True, alpha=0.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "outputs": [], + "source": [ + "sns.kdeplot(data=data, shade=True);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "outputs": [], + "source": [ + "sns.kdeplot(data=data, x=\"x\", y=\"y\");" + ] + }, + { + "cell_type": "markdown", + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "source": [ + "### Pair Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "iris = sns.load_dataset(\"iris\")\n", + "iris.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "outputs": [], + "source": [ + "sns.pairplot(iris, hue=\"species\", height=2.5);" + ] + }, + { + "cell_type": "markdown", + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "source": [ + "### Faceted Histograms" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "outputs": [], + "source": [ + "tips = sns.load_dataset(\"tips\")\n", + "tips.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "outputs": [], + "source": [ + "tips[\"tip_pct\"] = 100 * tips[\"tip\"] / tips[\"total_bill\"]\n", + "\n", + "grid = sns.FacetGrid(tips, row=\"sex\", col=\"time\", margin_titles=True)\n", + "grid.map(plt.hist, \"tip_pct\", bins=np.linspace(0, 40, 15));" + ] + }, + { + "cell_type": "markdown", + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "source": [ + "### Categorical Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "outputs": [], + "source": [ + "with sns.axes_style(style=\"ticks\"):\n", + " g = sns.catplot(x=\"day\", y=\"total_bill\", hue=\"sex\", data=tips, kind=\"box\")\n", + " g.set_axis_labels(\"Day\", \"Total Bill\")" + ] + }, + { + "cell_type": "markdown", + "id": "c3933fab20d04ec698c2621248eb3be0", + "metadata": {}, + "source": [ + "### Joint Distributions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dd4641cc4064e0191573fe9c69df29b", + "metadata": {}, + "outputs": [], + "source": [ + "with sns.axes_style(\"white\"):\n", + " sns.jointplot(x=\"total_bill\", y=\"tip\", data=tips, kind=\"hex\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8309879909854d7188b41380fd92a7c3", + "metadata": {}, + "outputs": [], + "source": [ + "sns.jointplot(x=\"total_bill\", y=\"tip\", data=tips, kind=\"reg\");" + ] + }, + { + "cell_type": "markdown", + "id": "3ed186c9a28b402fb0bc4494df01f08d", + "metadata": {}, + "source": [ + "### Bar Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb1e1581032b452c9409d6c6813c49d1", + "metadata": {}, + "outputs": [], + "source": [ + "planets = sns.load_dataset(\"planets\")\n", + "planets.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "379cbbc1e968416e875cc15c1202d7eb", + "metadata": {}, + "outputs": [], + "source": [ + "with sns.axes_style(\"white\"):\n", + " g = sns.catplot(x=\"year\", data=planets, aspect=2, kind=\"count\", color=\"steelblue\")\n", + " g.set_xticklabels(step=5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277c27b1587741f2af2001be3712ef0d", + "metadata": {}, + "outputs": [], + "source": [ + "with sns.axes_style(\"white\"):\n", + " g = sns.catplot(\n", + " x=\"year\", data=planets, aspect=4.0, kind=\"count\", hue=\"method\", order=range(2001, 2015)\n", + " )\n", + " g.set_ylabels(\"Number of Planets Discovered\")" + ] + }, + { + "cell_type": "markdown", + "id": "db7b79bc585a40fcaf58bf750017e135", + "metadata": {}, + "source": [ + "## Example: Exploring Marathon Finishing Times" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "916684f9a58a4a2aa5f864670399430d", + "metadata": {}, + "outputs": [], + "source": [ + "# url = ('https://raw.githubusercontent.com/jakevdp/'\n", + "# 'marathon-data/master/marathon-data.csv')\n", + "# !cd data && curl -O {url}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1671c31a24314836a5b85d7ef7fbf015", + "metadata": {}, + "outputs": [], + "source": [ + "data = pd.read_csv(\"data/marathon-data.csv\")\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33b0902fd34d4ace834912fa1002cf8e", + "metadata": {}, + "outputs": [], + "source": [ + "data.dtypes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6fa52606d8c4a75a9b52967216f8f3f", + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "\n", + "\n", + "def convert_time(s):\n", + " h, m, s = map(int, s.split(\":\"))\n", + " return datetime.timedelta(hours=h, minutes=m, seconds=s)\n", + "\n", + "\n", + "data = pd.read_csv(\n", + " \"data/marathon-data.csv\", converters={\"split\": convert_time, \"final\": convert_time}\n", + ")\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5a1fa73e5044315a093ec459c9be902", + "metadata": {}, + "outputs": [], + "source": [ + "data.dtypes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cdf66aed5cc84ca1b48e60bad68798a8", + "metadata": {}, + "outputs": [], + "source": [ + "data[\"split_sec\"] = data[\"split\"].astype(\"int64\") / 1e9\n", + "data[\"final_sec\"] = data[\"final\"].astype(\"int64\") / 1e9\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28d3efd5258a48a79c179ea5c6759f01", + "metadata": {}, + "outputs": [], + "source": [ + "with sns.axes_style(\"white\"):\n", + " g = sns.jointplot(x=\"split_sec\", y=\"final_sec\", data=data, kind=\"hex\")\n", + " g.ax_joint.plot(np.linspace(4000, 16000), np.linspace(8000, 32000), \":k\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f9bc0b9dd2c44919cc8dcca39b469f8", + "metadata": {}, + "outputs": [], + "source": [ + "data[\"split_frac\"] = 1 - 2 * data[\"split_sec\"] / data[\"final_sec\"]\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e382214b5f147d187d36a2058b9c724", + "metadata": {}, + "outputs": [], + "source": [ + "sns.displot(data[\"split_frac\"], kde=False)\n", + "plt.axvline(0, color=\"k\", linestyle=\"--\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b09d5ef5b5e4bb6ab9b829b10b6a29f", + "metadata": {}, + "outputs": [], + "source": [ + "sum(data.split_frac < 0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a50416e276a0479cbe66534ed1713a40", + "metadata": {}, + "outputs": [], + "source": [ + "g = sns.PairGrid(\n", + " data, vars=[\"age\", \"split_sec\", \"final_sec\", \"split_frac\"], hue=\"gender\", palette=\"RdBu_r\"\n", + ")\n", + "g.map(plt.scatter, alpha=0.8)\n", + "g.add_legend();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46a27a456b804aa2a380d5edf15a5daf", + "metadata": {}, + "outputs": [], + "source": [ + "sns.kdeplot(data.split_frac[data.gender == \"M\"], label=\"men\", shade=True)\n", + "sns.kdeplot(data.split_frac[data.gender == \"W\"], label=\"women\", shade=True)\n", + "plt.xlabel(\"split_frac\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1944c39560714e6e80c856f20744a8e5", + "metadata": {}, + "outputs": [], + "source": [ + "sns.violinplot(x=\"gender\", y=\"split_frac\", data=data, palette=[\"lightblue\", \"lightpink\"]);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6ca27006b894b04b6fc8b79396e2797", + "metadata": {}, + "outputs": [], + "source": [ + "data[\"age_dec\"] = data.age.map(lambda age: 10 * (age // 10))\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f61877af4e7f4313ad8234302950b331", + "metadata": {}, + "outputs": [], + "source": [ + "men = data.gender == \"M\"\n", + "women = data.gender == \"W\"\n", + "\n", + "with sns.axes_style(style=None):\n", + " sns.violinplot(\n", + " x=\"age_dec\",\n", + " y=\"split_frac\",\n", + " hue=\"gender\",\n", + " data=data,\n", + " split=True,\n", + " inner=\"quartile\",\n", + " palette=[\"lightblue\", \"lightpink\"],\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84d5ab97d17b4c38ab41a2b065bbd0c0", + "metadata": {}, + "outputs": [], + "source": [ + "(data.age > 80).sum()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35ffc1ce1c7b4df9ace1bc936b8b1dc2", + "metadata": {}, + "outputs": [], + "source": [ + "g = sns.lmplot(\n", + " x=\"final_sec\", y=\"split_frac\", col=\"gender\", data=data, markers=\".\", scatter_kws=dict(color=\"c\")\n", + ")\n", + "g.map(plt.axhline, y=0.0, color=\"k\", ls=\":\");" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/js/src/10_colormaps.js b/js/src/10_colormaps.js index bb9733a2..f299297d 100644 --- a/js/src/10_colormaps.js +++ b/js/src/10_colormaps.js @@ -44,6 +44,8 @@ const COLORMAP_STOPS = { purples: [[252, 251, 253], [239, 237, 245], [218, 218, 235], [188, 189, 220], [158, 154, 200], [128, 125, 186], [106, 81, 163], [84, 39, 143], [63, 0, 125]], pubu: [[255, 247, 251], [236, 231, 242], [208, 209, 230], [166, 189, 219], [116, 169, 207], [54, 144, 192], [5, 112, 176], [4, 90, 141], [2, 56, 88]], prgn: [[64, 0, 75], [118, 42, 131], [153, 112, 171], [194, 165, 207], [231, 212, 232], [247, 247, 247], [217, 240, 211], [166, 219, 160], [90, 174, 97], [27, 120, 55], [0, 68, 27]], + rdgy: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [254, 254, 254], [224, 224, 224], [185, 185, 185], [135, 135, 135], [76, 76, 76], [26, 26, 26]], + jet: [[0, 0, 128], [0, 0, 241], [0, 76, 255], [0, 176, 255], [41, 255, 206], [125, 255, 122], [206, 255, 41], [255, 196, 0], [255, 104, 0], [241, 8, 0], [128, 0, 0]], binary: [[255, 255, 255], [0, 0, 0]], }; diff --git a/js/src/51_annotations.js b/js/src/51_annotations.js index e994ca9b..64a309b3 100644 --- a/js/src/51_annotations.js +++ b/js/src/51_annotations.js @@ -61,6 +61,8 @@ Object.assign(ChartView.prototype, { ctx.strokeStyle = this._annotationPaint(style, [0.4, 0.44, 0.52, 1]); ctx.fillStyle = ctx.strokeStyle; ctx.lineWidth = Math.max(0.5, this._styleNumber(style, "width", 1.5)); + ctx.setLineDash(Array.isArray(style.dash) ? style.dash : + (typeof style.dash === "string" ? style.dash.split(",").map(Number) : [])); ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x1, y1); @@ -118,6 +120,8 @@ Object.assign(ChartView.prototype, { ctx.globalAlpha = this._styleNumber(style, "opacity", 1); ctx.strokeStyle = this._annotationPaint(style, [0.4, 0.44, 0.52, 1]); ctx.lineWidth = Math.max(0.5, this._styleNumber(style, "width", 1.5)); + ctx.setLineDash(Array.isArray(style.dash) ? style.dash : + (typeof style.dash === "string" ? style.dash.split(",").map(Number) : [])); ctx.beginPath(); const start = Math.max(0, Math.min(1, Number(style.span_start) || 0)); const rawEnd = style.span_end === undefined ? 1 : Number(style.span_end); diff --git a/pyproject.toml b/pyproject.toml index 576d97b7..028eb5dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -122,6 +122,11 @@ ignore = [ "RUF100", # unused noqa (kept for editors running broader rule sets) ] +[tool.ruff.lint.per-file-ignores] +# Third-party example code (PDSH notebooks): kept faithful to upstream, so +# upstream's import placement and naming style is not ours to lint. +"examples/pdsh/*.ipynb" = ["E402", "E731", "E741", "F401", "I001", "UP030", "UP032", "B007"] + [tool.ruff.lint.isort] known-first-party = ["fastcharts", "xy"] diff --git a/python/xy/_raster.py b/python/xy/_raster.py index 26c7d741..0142c5e2 100644 --- a/python/xy/_raster.py +++ b/python/xy/_raster.py @@ -489,14 +489,27 @@ def render_raster( cols = spec["columns"] cmd = _Cmd(scale) + dom_style = (spec.get("dom") or {}).get("style") or {} + # The fused PNG path initializes its native canvas white, avoiding a second # full-frame memory pass. Raw RGBA callers still receive an explicit fill. if not fast_png: - cmd.fill(_rect_pts(0, 0, width, height), (255, 255, 255, 255)) + cmd.fill( + _rect_pts(0, 0, width, height), + _parse_color(spec.get("canvas_background", "#ffffff")), + ) + + # Static exports honor the same axes background token as HTML/SVG. This + # is deliberately a plot-rect fill rather than a canvas fill: the latter + # is the Figure patch and is composed by pyplot's grid exporter. + plot_background = _parse_color(_css(dom_style.get("--chart-bg"), "#ffffff")) + cmd.fill( + _rect_pts(plot["x"], plot["y"], plot["x"] + plot["w"], plot["y"] + plot["h"]), + plot_background, + ) xt, xlab, xstep = axis_ticks(xa, plot["w"], True) yt, ylab, ystep = axis_ticks(ya, plot["h"], False) - dom_style = (spec.get("dom") or {}).get("style") or {} grid = _parse_color(_css(dom_style.get("--chart-grid"), _GRID)) px0, py0 = plot["x"], plot["y"] px1, py1 = plot["x"] + plot["w"], plot["y"] + plot["h"] @@ -617,7 +630,16 @@ def _emit_annotations(cmd, annotations, sx, sy, plot, width, height): else: pos = float(sy(float(ann["value"]))) points = [(px0 + start * plot["w"], pos), (px0 + end * plot["w"], pos)] - cmd.stroke(points, float(style.get("width", 1.5)), color) + cmd.stroke( + points, + float(style.get("width", 1.5)), + color, + dash=( + [float(value) for value in style["dash"].split(",")] + if isinstance(style.get("dash"), str) + else style.get("dash") + ), + ) elif ann.get("kind") == "band": a, b = float(ann["start"]), float(ann["end"]) if ann.get("axis") == "x": @@ -643,7 +665,7 @@ def _emit_annotations(cmd, annotations, sx, sy, plot, width, height): first_y + index * line_height + float(ann.get("dy", 0.0)), anchor, font_size, - _rgba(style.get("color"), _TEXT), + _rgba(style.get("color"), _TEXT, float(style.get("opacity", 1.0))), line, ) @@ -1020,9 +1042,40 @@ def _emit_colorbar(cmd, options, plot): y1 = y + height * (64 - index) / 64 cmd.fill(_rect_pts(x, y0, x + width, y1 + 0.5), (*map(int, color), 255)) domain = options.get("domain", [0.0, 1.0]) + lo, hi = float(domain[0]), float(domain[1]) + span = (hi - lo) or 1.0 + ticks = options.get("ticks") + extend = options.get("extend") + if extend in ("max", "both"): + color = (*map(int, colors[-1]), 255) + if orientation == "horizontal": + pts = [(x + width, y), (x + width, y + height), (x + width + 9, y + height / 2)] + else: + pts = [(x, y), (x + width, y), (x + width / 2, y - 9)] + cmd.fill(pts, color) + if extend in ("min", "both"): + color = (*map(int, colors[0]), 255) + if orientation == "horizontal": + pts = [(x, y), (x, y + height), (x - 9, y + height / 2)] + else: + pts = [(x, y + height), (x + width, y + height), (x + width / 2, y + height + 9)] + cmd.fill(pts, color) if orientation == "horizontal": - cmd.text(x, y + height + 13, 0, 10, _parse_color(_TEXT), f"{domain[0]:g}") - cmd.text(x + width, y + height + 13, 2, 10, _parse_color(_TEXT), f"{domain[1]:g}") + if ticks is not None: + for value in ticks: + value = float(value) + if lo <= value <= hi: + cmd.text( + x + width * (value - lo) / span, + y + height + 13, + 1, + 10, + _parse_color(_TEXT), + f"{value:g}", + ) + else: + cmd.text(x, y + height + 13, 0, 10, _parse_color(_TEXT), f"{domain[0]:g}") + cmd.text(x + width, y + height + 13, 2, 10, _parse_color(_TEXT), f"{domain[1]:g}") if options.get("label"): cmd.text( x + width / 2, @@ -1033,11 +1086,15 @@ def _emit_colorbar(cmd, options, plot): str(options["label"]), ) else: - for index in range(5): - value = domain[0] + (domain[1] - domain[0]) * index / 4 + tick_positions = ( + [float(value) for value in ticks if lo <= float(value) <= hi] + if ticks is not None + else [lo + span * index / 4 for index in range(5)] + ) + for value in tick_positions: cmd.text( x + width + 4, - y + height * (1 - index / 4) + 4, + y + height * (1 - (value - lo) / span) + 4, 0, 10, _parse_color(_TEXT), diff --git a/python/xy/_svg.py b/python/xy/_svg.py index 66cba41c..e7bdad1f 100644 --- a/python/xy/_svg.py +++ b/python/xy/_svg.py @@ -221,6 +221,32 @@ (27, 120, 55), (0, 68, 27), ], + "rdgy": [ + (103, 0, 31), + (177, 24, 43), + (214, 96, 77), + (243, 164, 129), + (253, 219, 199), + (254, 254, 254), + (224, 224, 224), + (185, 185, 185), + (135, 135, 135), + (76, 76, 76), + (26, 26, 26), + ], + "jet": [ + (0, 0, 128), + (0, 0, 241), + (0, 76, 255), + (0, 176, 255), + (41, 255, 206), + (125, 255, 122), + (206, 255, 41), + (255, 196, 0), + (255, 104, 0), + (241, 8, 0), + (128, 0, 0), + ], "binary": [(255, 255, 255), (0, 0, 0)], } @@ -671,6 +697,8 @@ def _dash_attr(style: dict[str, Any]) -> str: dash = style.get("dash") if not dash: return "" + if isinstance(dash, str): + dash = dash.split(",") return f' stroke-dasharray="{",".join(_num(float(v)) for v in dash)}"' @@ -957,7 +985,8 @@ def _annotation_svg(annotations, sx, sy, plot, width, height): marks.append( f'' + f'stroke-width="{_num(float(style.get("width", 1.5)))}" stroke-opacity="{_num(opacity)}"' + f"{_dash_attr(style)}/>" ) elif kind == "band": a, b = float(ann["start"]), float(ann["end"]) @@ -997,9 +1026,11 @@ def _annotation_svg(annotations, sx, sy, plot, width, height): f"{escape(line)}" for index, line in enumerate(lines) ) + text_opacity = float(style.get("opacity", 1.0)) labels.append( f'{tspans}' + + (f'fill-opacity="{_num(text_opacity)}" ' if text_opacity < 1 else "") + + f'fill="{color}">{tspans}' ) return marks, labels @@ -1333,21 +1364,57 @@ def _colorbar(options: dict, plot: dict) -> str: else "" ) ) + lo, hi = float(domain[0]), float(domain[1]) + span = (hi - lo) or 1.0 + ticks = options.get("ticks") + tick_positions = ( + [float(value) for value in ticks if lo <= float(value) <= hi] + if ticks is not None + else [lo + span * i / 4 for i in range(5)] + ) tick_nodes = ( "".join( - f'{domain[0] + (domain[1] - domain[0]) * i / 4:g}' - for i in range(5) + f'{value:g}' + for value in tick_positions ) if orientation != "horizontal" - else "" + else "".join( + f'{value:g}' + for value in tick_positions + if ticks is not None + ) ) + extend = options.get("extend") + extend_nodes = "" + if extend in ("max", "both"): + r, g, b = stops[-1] + points = ( + f"{_num(x)},{_num(y)} {_num(x + width)},{_num(y)} {_num(x + width / 2)},{_num(y - 9)}" + if orientation != "horizontal" + else f"{_num(x + width)},{_num(y)} {_num(x + width)},{_num(y + height)} " + f"{_num(x + width + 9)},{_num(y + height / 2)}" + ) + extend_nodes += f'' + if extend in ("min", "both"): + r, g, b = stops[0] + points = ( + f"{_num(x)},{_num(y + height)} {_num(x + width)},{_num(y + height)} " + f"{_num(x + width / 2)},{_num(y + height + 9)}" + if orientation != "horizontal" + else f"{_num(x)},{_num(y)} {_num(x)},{_num(y + height)} " + f"{_num(x - 9)},{_num(y + height / 2)}" + ) + extend_nodes += f'' return ( f'' f"{stop_nodes}" f'' - f"{tick_nodes}{label_node}" + f"{extend_nodes}{tick_nodes}{label_node}" ) diff --git a/python/xy/channels.py b/python/xy/channels.py index 3f7aa47b..44b38d03 100644 --- a/python/xy/channels.py +++ b/python/xy/channels.py @@ -41,6 +41,8 @@ "purples", "pubu", "prgn", + "rdgy", + "jet", "binary", ) @@ -347,15 +349,24 @@ def resolve_color( *, colormap: str = DEFAULT_COLORMAP, default_constant: str, + domain: Optional[tuple[float, float]] = None, ) -> ColorChannel: """Interpret the `color=` argument. - `None` / a CSS color string → constant. - a length-n array of numbers → continuous (normalized + colormap). - a length-n array of strings/categories → categorical (factorized + palette). + + `domain` pins the continuous normalization window (matplotlib's + vmin/vmax); values outside clip to the colormap ends. """ if not is_colormap(colormap): raise ValueError(f"unknown colormap {colormap!r}; known: {COLORMAPS}") + if domain is not None: + lo, hi = float(domain[0]), float(domain[1]) + if not (np.isfinite(lo) and np.isfinite(hi)) or hi <= lo: + raise ValueError(f"color domain must be finite (lo, hi) with lo < hi, got {domain!r}") + domain = (lo, hi) # Constant channels keep the colormap too: it still drives the density # ramp when the trace aggregates (§5 Tier 2), and a typo'd name must @@ -399,7 +410,10 @@ def resolve_color( vals = _as_real_array(arr, "color array") return ColorChannel( - mode="continuous", values=vals, domain=_continuous_domain(vals), colormap=colormap + mode="continuous", + values=vals, + domain=domain if domain is not None else _continuous_domain(vals), + colormap=colormap, ) diff --git a/python/xy/components.py b/python/xy/components.py index ee0ca2e5..f666bce3 100644 --- a/python/xy/components.py +++ b/python/xy/components.py @@ -257,6 +257,7 @@ def scatter( size: Union[str, float, Any] = 4.0, name: Optional[str] = None, colormap: str = channels.DEFAULT_COLORMAP, + color_domain: Optional[tuple[float, float]] = None, size_range: tuple[float, float] = (2.0, 18.0), opacity: float = 0.8, density: Optional[bool] = None, @@ -282,6 +283,7 @@ def scatter( "color": color, "size": size, "colormap": colormap, + "color_domain": color_domain, "size_range": size_range, "opacity": opacity, "density": density, @@ -757,6 +759,9 @@ def hexbin( gridsize: int | tuple[int, int] = 64, range: Optional[tuple[tuple[float, float], tuple[float, float]]] = None, bins: str = "count", + C: Any = None, + reduce_C_function: Any = np.mean, + mincnt: Optional[int] = None, name: Optional[str] = None, colormap: str = channels.DEFAULT_COLORMAP, opacity: float = 0.9, @@ -776,6 +781,9 @@ def hexbin( "gridsize": gridsize, "range": range, "bins": bins, + "C": C, + "reduce_C_function": reduce_C_function, + "mincnt": mincnt, "colormap": colormap, "opacity": opacity, "x_axis": x_axis, @@ -2572,6 +2580,7 @@ def _apply_scatter(fig: Figure, m: Mark, data: Any) -> None: color=_resolve_color(data, m.props["color"], context=f"{m.kind}.color"), size=_resolve(data, size, context=f"{m.kind}.size") if isinstance(size, str) else size, colormap=m.props["colormap"], + color_domain=m.props.get("color_domain"), size_range=m.props["size_range"], opacity=m.props["opacity"], density=m.props["density"], @@ -2776,6 +2785,11 @@ def _apply_hexbin(fig: Figure, m: Mark, data: Any) -> None: gridsize=m.props["gridsize"], range=m.props["range"], bins=m.props["bins"], + C=_resolve(data, m.props["C"], context=f"{m.kind}.C") + if isinstance(m.props["C"], str) + else m.props["C"], + reduce_C_function=m.props["reduce_C_function"], + mincnt=m.props["mincnt"], name=m.name, colormap=m.props["colormap"], opacity=m.props["opacity"], diff --git a/python/xy/marks.py b/python/xy/marks.py index 702aa70c..9790832e 100644 --- a/python/xy/marks.py +++ b/python/xy/marks.py @@ -910,6 +910,7 @@ def scatter( size: Any = 4.0, opacity: float = 0.8, colormap: str = channels.DEFAULT_COLORMAP, + color_domain: Optional[tuple[float, float]] = None, size_range: tuple[float, float] = (2.0, 18.0), density: Optional[bool] = None, symbol: str = "circle", @@ -939,7 +940,7 @@ def scatter( n = len(xc) default_color = DEFAULT_PALETTE[len(self.traces) % len(DEFAULT_PALETTE)] color_ch = channels.resolve_color( - color, n, colormap=colormap, default_constant=default_color + color, n, colormap=colormap, default_constant=default_color, domain=color_domain ) size_ch = channels.resolve_size(size, n, range_px=size_range) @@ -1347,6 +1348,9 @@ def hexbin( gridsize: int | tuple[int, int] = 64, range: Optional[tuple[tuple[float, float], tuple[float, float]]] = None, bins: str = "count", + C: Any = None, + reduce_C_function: Any = np.mean, + mincnt: Optional[int] = None, name: Optional[str] = None, colormap: str = channels.DEFAULT_COLORMAP, opacity: float = 0.9, @@ -1386,10 +1390,18 @@ def hexbin( f"hexbin x and y must have equal length, got {len(x_all)} and {len(y_all)}" ) n_points = len(x_all) + c_all = None + if C is not None: + c_all, _c_kind, _c_copies = columns._canonicalize(C) + if len(c_all) != len(x_all): + raise ValueError("hexbin C must have the same length as x and y") finite = np.isfinite(x_all) & np.isfinite(y_all) + if c_all is not None: + finite &= np.isfinite(c_all) if not np.any(finite): raise ValueError("hexbin x and y must contain at least one finite pair") xv, yv = x_all[finite], y_all[finite] + cv = None if c_all is None else c_all[finite] if range is None: xr = self._auto_domain(kernels.min_max(xv)) yr = self._auto_domain(kernels.min_max(yv)) @@ -1398,15 +1410,38 @@ def hexbin( raise ValueError("hexbin range must be ((x0, x1), (y0, y1))") xr = self._finite_increasing_pair(range[0], "hexbin x range") yr = self._finite_increasing_pair(range[1], "hexbin y range") - grid = kernels.bin_2d(xv, yv, xr[0], xr[1], yr[0], yr[1], w, h) - rows, cols = np.nonzero(grid.reshape(h, w) > 0) - counts = grid.reshape(h, w)[rows, cols] + threshold = 1 if mincnt is None else int(mincnt) + if threshold < 0: + raise ValueError("hexbin mincnt must be nonnegative") + if cv is None: + grid = kernels.bin_2d(xv, yv, xr[0], xr[1], yr[0], yr[1], w, h) + grid2d = grid.reshape(h, w) + rows, cols = np.nonzero(grid2d >= threshold) + else: + # count with the same clip membership the reducer uses below, so + # mincnt filtering and aggregation always agree on bin contents + x_index = np.clip(((xv - xr[0]) / (xr[1] - xr[0]) * w).astype(int), 0, w - 1) + y_index = np.clip(((yv - yr[0]) / (yr[1] - yr[0]) * h).astype(int), 0, h - 1) + grid2d = np.zeros((h, w), dtype=np.float64) + np.add.at(grid2d, (y_index, x_index), 1.0) + rows, cols = np.nonzero(grid2d >= max(1, threshold)) + counts = grid2d[rows, cols] if len(counts) == 0: raise ValueError("hexbin range contains no finite points") dx, dy = (xr[1] - xr[0]) / w, (yr[1] - yr[0]) / h centers_x = xr[0] + (cols + 0.5 + 0.5 * (rows & 1)) * dx centers_y = yr[0] + (rows + 0.5) * dy - metric = np.log1p(counts) if bins == "log" else counts + if cv is None: + metric = np.log1p(counts) if bins == "log" else counts + else: + reduced: list[float] = [] + for row, col in zip(rows, cols, strict=True): + values = cv[(x_index == col) & (y_index == row)] + made = np.asarray(reduce_C_function(values)) + if made.ndim != 0 or not np.isfinite(made): + raise ValueError("hexbin reduce_C_function must return one finite scalar per bin") + reduced.append(float(made)) + metric = np.asarray(reduced, dtype=np.float64) color_ch = channels.resolve_color( metric, len(metric), colormap=colormap, default_constant=DEFAULT_PALETTE[0] ) diff --git a/python/xy/pyplot/__init__.py b/python/xy/pyplot/__init__.py index 550650cc..e6e63cdc 100644 --- a/python/xy/pyplot/__init__.py +++ b/python/xy/pyplot/__init__.py @@ -15,24 +15,59 @@ from __future__ import annotations +import contextlib from typing import Any, Optional, Union import numpy as np from ._axes import Axes -from ._mplfig import Figure, apply_sharing, make_axes_grid -from ._rc import rc, rcParams -from ._state import all_figures, close, figure, gca, gcf, sca +from ._colors import LinearSegmentedColormap, ListedColormap +from ._mplfig import Figure, GridSpec, apply_sharing, make_axes_grid +from ._rc import _PropCycle, rc, rc_context, rcdefaults, rcParams +from ._state import all_figures, close, figlabels, fignum_exists, fignums, figure, gca, gcf, sca +from ._ticker import ( + AutoLocator, + FixedFormatter, + FixedLocator, + FormatStrFormatter, + FuncFormatter, + LinearLocator, + LogLocator, + MaxNLocator, + MultipleLocator, + NullFormatter, + NullLocator, + ScalarFormatter, + StrMethodFormatter, +) from ._translate import not_implemented __all__ = [ + "AutoLocator", "Axes", "Figure", + "FixedFormatter", + "FixedLocator", + "FormatStrFormatter", + "FuncFormatter", + "GridSpec", + "LinearLocator", + "LinearSegmentedColormap", + "ListedColormap", "LogLocator", + "MaxNLocator", + "MultipleLocator", + "NullFormatter", + "NullLocator", + "ScalarFormatter", + "StrMethodFormatter", "acorr", "angle_spectrum", "annotate", "arrow", + "autoscale", + "autoscale_view", + "axes", "axhline", "axhspan", "axis", @@ -43,10 +78,14 @@ "bar_label", "barbs", "barh", + "box", "boxplot", "broken_barh", "bxp", + "cla", "clabel", + "clf", + "clim", "close", "cm", "cohere", @@ -55,44 +94,71 @@ "contour", "contourf", "csd", + "cycler", + "delaxes", "ecdf", "errorbar", "eventplot", + "figlegend", + "fignum_exists", + "figtext", "figure", "fill", "fill_between", "fill_betweenx", + "findobj", "gca", "gcf", + "gci", + "get", "get_cmap", + "get_figlabels", + "get_fignums", + "get_xbound", + "get_ybound", + "getp", + "gray", "grid", "grouped_bar", "hexbin", "hist", "hist2d", "hlines", + "imread", + "imsave", "imshow", "legend", "loglog", "magnitude_spectrum", "matshow", + "minorticks_off", + "minorticks_on", "pcolor", "pcolorfast", "pcolormesh", "phase_spectrum", "pie", "pie_label", + "plasma", "plot", "psd", "quiver", "quiverkey", "rc", "rcParams", + "rc_context", + "rcdefaults", + "relim", "savefig", "sca", "scatter", + "sci", "semilogx", "semilogy", + "set_cmap", + "set_xbound", + "set_ybound", + "setp", "show", "specgram", "spy", @@ -103,12 +169,14 @@ "streamplot", "style", "subplot", + "subplot2grid", "subplot_mosaic", "subplots", "subplots_adjust", "suptitle", "table", "text", + "ticklabel_format", "tight_layout", "title", "tricontour", @@ -116,8 +184,10 @@ "tripcolor", "triplot", "twinx", + "twiny", "violin", "violinplot", + "viridis", "vlines", "xcorr", "xlabel", @@ -130,7 +200,6 @@ "yticks", ] - # -- figure/axes management ---------------------------------------------------- @@ -148,12 +217,16 @@ def subplots( width_ratios = kwargs.pop("width_ratios", None) height_ratios = kwargs.pop("height_ratios", None) gridspec_kw = kwargs.pop("gridspec_kw", None) or {} + subplot_kw = kwargs.pop("subplot_kw", None) or {} width_ratios = gridspec_kw.get("width_ratios", width_ratios) height_ratios = gridspec_kw.get("height_ratios", height_ratios) fig = figure(figsize=figsize, dpi=dpi) if fig._axes and any(ax._entries for ax in fig._axes): fig = figure(None, figsize=figsize, dpi=dpi) # fresh figure, mpl semantics axes = make_axes_grid(fig, nrows, ncols, squeeze=squeeze) + if subplot_kw: + for ax in np.atleast_1d(np.asarray(axes, dtype=object)).ravel(): + ax.set(**subplot_kw) fig._width_ratios = None if width_ratios is None else tuple(map(float, width_ratios)) fig._height_ratios = None if height_ratios is None else tuple(map(float, height_ratios)) apply_sharing(fig, sharex, sharey) @@ -161,7 +234,7 @@ def subplots( def subplot(*args: Any, **kwargs: Any) -> Axes: - return gcf().add_subplot(*args) + return gcf().add_subplot(*args, **kwargs) def subplot_mosaic(mosaic: Any, **kwargs: Any) -> tuple[Figure, dict[Any, Axes]]: @@ -171,10 +244,278 @@ def subplot_mosaic(mosaic: Any, **kwargs: Any) -> tuple[Figure, dict[Any, Axes]] return fig, fig.subplot_mosaic(mosaic, **kwargs) +def axes(arg: Any = None, **kwargs: Any) -> Axes: + if arg is None: + ax = gcf().add_subplot(111) + if kwargs: + ax.set(**kwargs) + return ax + return gcf().add_axes(arg, **kwargs) + + +def delaxes(ax: Optional[Axes] = None) -> None: + gcf().delaxes(ax or gca()) + + +def cla() -> None: + gca().cla() + + +def clf() -> None: + gcf().clf() + + +def get_fignums() -> list[int]: + return fignums() + + +def get_figlabels() -> list[str]: + return figlabels() + + +def figtext(x: float, y: float, s: str, **kwargs: Any) -> Any: + return gcf().text(x, y, s, **kwargs) + + +def figlegend(*args: Any, **kwargs: Any) -> Any: + return gcf().legend(*args, **kwargs) + + def twinx() -> Axes: return gca().twinx() +def twiny() -> Axes: + return gca().twiny() + + +def subplot2grid( + shape: tuple[int, int], + loc: tuple[int, int], + rowspan: int = 1, + colspan: int = 1, + fig: Optional[Figure] = None, + **kwargs: Any, +) -> Axes: + if rowspan != 1 or colspan != 1: + raise not_implemented("subplot2grid(rowspan/colspan)", "single-cell subplot2grid specs") + target = fig or gcf() + target._ensure_grid(int(shape[0]), int(shape[1])) + ax = target._axes_at(int(loc[0]) * int(shape[1]) + int(loc[1])) + target._current_ax = ax + return ax + + +def box(on: Optional[bool] = None) -> None: + ax = gca() + ax._box = True if on is None else bool(on) + ax._invalidate() + + +def setp(obj: Any, *args: Any, **kwargs: Any) -> None: + if args: + if len(args) % 2: + raise ValueError("setp positional arguments must be property/value pairs") + kwargs.update(dict(zip(args[0::2], args[1::2], strict=True))) + targets = obj if isinstance(obj, (list, tuple)) else [obj] + for target in targets: + for name, value in kwargs.items(): + setter = getattr(target, f"set_{name}", None) + if setter is None: + raise AttributeError(f"object has no set_{name}()") + setter(value) + + +def getp(obj: Any, property: Optional[str] = None) -> Any: + if property is None: + return { + name[4:]: method() + for name, method in ((n, getattr(obj, n)) for n in dir(obj) if n.startswith("get_")) + if callable(method) + } + getter = getattr(obj, f"get_{property}", None) + if getter is None: + raise AttributeError(f"object has no get_{property}()") + return getter() + + +def get(obj: Any, property: Optional[str] = None) -> Any: + return getp(obj, property) + + +def findobj(obj: Any = None, match: Any = None) -> list[Any]: + root = obj or gcf() + found: list[Any] = [] + axes = getattr(root, "axes", []) if not isinstance(root, Axes) else [root] + for ax in axes: + if match is None or match(ax): + found.append(ax) + for entry in getattr(ax, "_entries", []): + artist = getattr(entry, "_artist", None) + if artist is not None and (match is None or match(artist)): + found.append(artist) + return found + + +def set_cmap(cmap: Any) -> None: + from ._colors import resolve_cmap + + name = str(getattr(cmap, "name", cmap)) + resolve_cmap(name) # unknown names must fail here, not at render time + rcParams["image.cmap"] = name + + +def viridis() -> Any: + set_cmap("viridis") + return get_cmap("viridis") + + +def plasma() -> Any: + set_cmap("plasma") + return get_cmap("plasma") + + +def gray() -> Any: + set_cmap("gray") + return get_cmap("gray") + + +def imsave(fname: Any, arr: Any, **kwargs: Any) -> None: + format_name = str(kwargs.pop("format", "")).lower() + cmap = kwargs.pop("cmap", None) + if kwargs: + raise TypeError(f"imsave() got unsupported keyword argument {next(iter(kwargs))!r}") + path = str(fname) + if format_name in {"jpg", "jpeg"} or path.lower().endswith((".jpg", ".jpeg")): + raise not_implemented( + "imsave(JPEG)", "PNG output; JPEG remains outside the dependency-free shim" + ) + image = np.asarray(arr) + if image.ndim == 2: + from ._colors import Cmap + + # Colormap the original values: quantizing to uint8 first would + # collapse any range outside [0, 255] to a handful of colors. + scalar = image.astype(np.float64) + finite = scalar[np.isfinite(scalar)] + lo = float(finite.min()) if finite.size else 0.0 + hi = float(finite.max()) if finite.size else 1.0 + normalized = (scalar - lo) / (hi - lo) if hi > lo else np.zeros_like(scalar) + rgba = Cmap(cmap if cmap is not None else rcParams["image.cmap"])(normalized) + image = np.round(np.asarray(rgba, dtype=np.float64) * 255.0).astype(np.uint8) + else: + # cmap is ignored for RGB(A) input, matching matplotlib. + if image.dtype != np.uint8: + finite = image.astype(float) + if finite.size and np.nanmax(finite) <= 1.0 and np.nanmin(finite) >= 0.0: + image = np.clip(finite * 255.0, 0, 255).astype(np.uint8) + else: + image = np.clip(finite, 0, 255).astype(np.uint8) + if image.ndim == 3 and image.shape[2] == 3: + alpha = np.full((*image.shape[:2], 1), 255, dtype=np.uint8) + image = np.concatenate((image, alpha), axis=2) + elif image.ndim != 3 or image.shape[2] != 4: + raise ValueError("imsave() expects a 2-D grayscale, RGB, or RGBA array") + from xy._png import encode + + data = encode(np.ascontiguousarray(image, dtype=np.uint8)) + if hasattr(fname, "write"): + fname.write(data) + else: + from pathlib import Path + + Path(fname).write_bytes(data) + + +def imread(fname: Any, **kwargs: Any) -> np.ndarray: + if kwargs: + raise TypeError(f"imread() got unsupported keyword argument {next(iter(kwargs))!r}") + data = ( + fname.read() if hasattr(fname, "read") else __import__("pathlib").Path(fname).read_bytes() + ) + if data[:2] == b"\xff\xd8": + raise not_implemented( + "imread(JPEG)", "PNG input; JPEG remains outside the dependency-free shim" + ) + if data[:8] != b"\x89PNG\r\n\x1a\n": + raise ValueError("imread() only supports PNG files in the dependency-free shim") + import struct + import zlib + + position = 8 + width = height = color_type = None + palette = b"" + transparency = b"" + idat = bytearray() + while position + 8 <= len(data): + (length,) = struct.unpack(">I", data[position : position + 4]) + kind = data[position + 4 : position + 8] + chunk = data[position + 8 : position + 8 + length] + position += 12 + length + if kind == b"IHDR": + width, height, depth, color_type, _compression, _filter, interlace = struct.unpack( + ">IIBBBBB", chunk + ) + if depth != 8 or interlace != 0: + raise ValueError("imread() supports only 8-bit non-interlaced PNG files") + elif kind == b"PLTE": + palette = chunk + elif kind == b"tRNS": + transparency = chunk + elif kind == b"IDAT": + idat += chunk + elif kind == b"IEND": + break + if width is None or height is None or color_type is None: + raise ValueError("invalid PNG file") + channels = {0: 1, 2: 3, 3: 1, 6: 4}.get(color_type) + if channels is None: + raise ValueError("imread() supports grayscale, RGB, indexed, and RGBA PNG files") + row_length = width * channels + raw = zlib.decompress(bytes(idat)) + previous = bytearray(row_length) + decoded = bytearray(width * height * 4) + source = destination = 0 + for _row_index in range(height): + filter_kind = raw[source] + source += 1 + row = bytearray(raw[source : source + row_length]) + source += row_length + for index, value in enumerate(row): + left = row[index - channels] if index >= channels else 0 + up = previous[index] + up_left = previous[index - channels] if index >= channels else 0 + if filter_kind == 1: + row[index] = (value + left) & 0xFF + elif filter_kind == 2: + row[index] = (value + up) & 0xFF + elif filter_kind == 3: + row[index] = (value + ((left + up) >> 1)) & 0xFF + elif filter_kind == 4: + predictor = left + up - up_left + distances = (abs(predictor - left), abs(predictor - up), abs(predictor - up_left)) + row[index] = (value + (left, up, up_left)[distances.index(min(distances))]) & 0xFF + elif filter_kind != 0: + raise ValueError(f"unsupported PNG filter {filter_kind}") + for column in range(width): + if color_type == 6: + rgba = row[column * 4 : column * 4 + 4] + elif color_type == 2: + rgba = row[column * 3 : column * 3 + 3] + b"\xff" + elif color_type == 0: + gray_value = row[column] + rgba = bytes((gray_value, gray_value, gray_value, 255)) + else: + palette_index = row[column] + base = palette_index * 3 + alpha = transparency[palette_index] if palette_index < len(transparency) else 255 + rgba = palette[base : base + 3] + bytes((alpha,)) + decoded[destination : destination + 4] = rgba + destination += 4 + previous = row + return np.frombuffer(decoded, dtype=np.uint8).reshape(height, width, 4) + + # -- pyplot function surface: delegate to the current axes ---------------------- @@ -188,6 +529,23 @@ def call(*args: Any, **kwargs: Any) -> Any: return call +def _delegated_mappable(name: str): + """Like _delegated, but records the result as the figure's current + mappable so colorbar()/clim() find it, matching pyplot's sci() calls.""" + + def call(*args: Any, **kwargs: Any) -> Any: + result = getattr(gca(), name)(*args, **kwargs) + candidate = result[-1] if isinstance(result, tuple) else result + if hasattr(candidate, "_entry"): + gcf()._gci = candidate + return result + + call.__name__ = name + call.__qualname__ = name + call.__doc__ = f"pyplot {name}(): applies to the current axes (see Axes.{name})." + return call + + plot = _delegated("plot") acorr = _delegated("acorr") angle_spectrum = _delegated("angle_spectrum") @@ -201,7 +559,7 @@ def call(*args: Any, **kwargs: Any) -> Any: fill = _delegated("fill") arrow = _delegated("arrow") axline = _delegated("axline") -scatter = _delegated("scatter") +scatter = _delegated_mappable("scatter") bar = _delegated("bar") bar_label = _delegated("bar_label") grouped_bar = _delegated("grouped_bar") @@ -209,17 +567,17 @@ def call(*args: Any, **kwargs: Any) -> Any: hist = _delegated("hist") fill_between = _delegated("fill_between") fill_betweenx = _delegated("fill_betweenx") -imshow = _delegated("imshow") -matshow = _delegated("matshow") -pcolor = _delegated("pcolor") -pcolorfast = _delegated("pcolorfast") -pcolormesh = _delegated("pcolormesh") +imshow = _delegated_mappable("imshow") +matshow = _delegated_mappable("matshow") +pcolor = _delegated_mappable("pcolor") +pcolorfast = _delegated_mappable("pcolorfast") +pcolormesh = _delegated_mappable("pcolormesh") step = _delegated("step") stem = _delegated("stem") stairs = _delegated("stairs") ecdf = _delegated("ecdf") -hist2d = _delegated("hist2d") -hexbin = _delegated("hexbin") +hist2d = _delegated_mappable("hist2d") +hexbin = _delegated_mappable("hexbin") eventplot = _delegated("eventplot") stackplot = _delegated("stackplot") axhline = _delegated("axhline") @@ -239,8 +597,8 @@ def call(*args: Any, **kwargs: Any) -> Any: violinplot = _delegated("violinplot") violin = _delegated("violin") errorbar = _delegated("errorbar") -contour = _delegated("contour") -contourf = _delegated("contourf") +contour = _delegated_mappable("contour") +contourf = _delegated_mappable("contourf") clabel = _delegated("clabel") quiver = _delegated("quiver") quiverkey = _delegated("quiverkey") @@ -253,10 +611,20 @@ def call(*args: Any, **kwargs: Any) -> Any: vlines = _delegated("vlines") broken_barh = _delegated("broken_barh") spy = _delegated("spy") -tripcolor = _delegated("tripcolor") +tripcolor = _delegated_mappable("tripcolor") triplot = _delegated("triplot") tricontour = _delegated("tricontour") tricontourf = _delegated("tricontourf") +autoscale = _delegated("autoscale") +autoscale_view = _delegated("autoscale_view") +relim = _delegated("relim") +ticklabel_format = _delegated("ticklabel_format") +minorticks_on = _delegated("minorticks_on") +minorticks_off = _delegated("minorticks_off") +get_xbound = _delegated("get_xbound") +set_xbound = _delegated("set_xbound") +get_ybound = _delegated("get_ybound") +set_ybound = _delegated("set_ybound") def title(label: str, **kwargs: Any) -> None: @@ -307,10 +675,6 @@ def subplots_adjust(**kwargs: Any) -> None: gcf().subplots_adjust(**kwargs) -class LogLocator: - pass - - def get_cmap(name: Any = None, lut: Any = None) -> Any: from ._colors import Cmap @@ -318,8 +682,26 @@ def get_cmap(name: Any = None, lut: Any = None) -> Any: return cmap if lut is None else cmap.resampled(int(lut)) -def colorbar(*args: Any, **kwargs: Any) -> None: - gcf().colorbar(*args, **kwargs) +def colorbar(*args: Any, **kwargs: Any) -> Any: + return gcf().colorbar(*args, **kwargs) + + +def gci() -> Any: + """The current color-mapped artist (image/collection), or None.""" + return gcf()._gci + + +def sci(mappable: Any) -> None: + gcf()._gci = mappable + + +def clim(vmin: Any = None, vmax: Any = None) -> None: + image = gci() + if image is None: + raise RuntimeError( + "clim() requires an image or collection; plot one first (e.g. imshow/scatter)" + ) + image.set_clim(vmin, vmax) # -- output --------------------------------------------------------------------- @@ -351,6 +733,11 @@ def show(*args: Any, **kwargs: Any) -> None: class _CmapNamespace: """plt.cm.viridis and friends: name carriers the shim resolves by name.""" + @staticmethod + def get_cmap(name: Any = None, lut: Any = None) -> Any: + # matplotlib removed cm.get_cmap in 3.9; older scripts still call it. + return get_cmap(name, lut) + def __getattr__(self, name: str) -> Any: from ._colors import CMAPS @@ -382,7 +769,9 @@ def __iter__(self): "turbo", "coolwarm", "RdBu", + "RdGy", "bwr", + "jet", "Blues", "RdYlGn", "rainbow", @@ -399,22 +788,186 @@ def __iter__(self): colormaps = _ColormapRegistry() +# The stock stylesheets scripts reach for, reduced to the rcParams subset the +# shim renders (values match matplotlib 3.11's style library; gray shorthands +# are pre-resolved to hex so exporters never see them). +_NAMED_STYLES: dict[str, dict[str, Any]] = { + "fivethirtyeight": { + "figure.facecolor": "#f0f0f0", + "axes.facecolor": "#f0f0f0", + "axes.edgecolor": "#f0f0f0", + "axes.grid": True, + "grid.color": "#cbcbcb", + "lines.linewidth": 4.0, + "font.size": 14.0, + "axes.prop_cycle": _PropCycle( + ["#008fd5", "#fc4f30", "#e5ae38", "#6d904f", "#8b8b8b", "#810f7c"] + ), + }, + "ggplot": { + "figure.facecolor": "white", + "axes.facecolor": "#E5E5E5", + "axes.edgecolor": "white", + "axes.labelcolor": "#555555", + "axes.grid": True, + "grid.color": "white", + "xtick.color": "#555555", + "ytick.color": "#555555", + "font.size": 10.0, + "axes.prop_cycle": _PropCycle( + ["#E24A33", "#348ABD", "#988ED5", "#777777", "#FBC15E", "#8EBA42", "#FFB5B8"] + ), + }, + "bmh": { + "axes.facecolor": "#eeeeee", + "axes.edgecolor": "#bcbcbc", + "axes.grid": True, + "grid.color": "#b2b2b2", + "lines.linewidth": 2.0, + "axes.prop_cycle": _PropCycle( + [ + "#348ABD", + "#A60628", + "#7A68A6", + "#467821", + "#D55E00", + "#CC79A7", + "#56B4E9", + "#009E73", + "#F0E442", + "#0072B2", + ] + ), + }, + "dark_background": { + "figure.facecolor": "black", + "axes.facecolor": "black", + "axes.edgecolor": "white", + "axes.labelcolor": "white", + "grid.color": "white", + "xtick.color": "white", + "ytick.color": "white", + "axes.prop_cycle": _PropCycle( + [ + "#8dd3c7", + "#feffb3", + "#bfbbd9", + "#fa8174", + "#81b1d2", + "#fdb462", + "#b3de69", + "#bc82bd", + "#ccebc4", + "#ffed6f", + ] + ), + }, + "grayscale": { + "figure.facecolor": "#bfbfbf", + "axes.facecolor": "white", + "axes.edgecolor": "black", + "axes.labelcolor": "black", + "grid.color": "black", + "xtick.color": "black", + "ytick.color": "black", + "axes.prop_cycle": _PropCycle(["#000000", "#666666", "#999999", "#b3b3b3"]), + }, + "seaborn-v0_8-white": { + "figure.facecolor": "white", + "axes.facecolor": "white", + "axes.edgecolor": "#262626", + "axes.labelcolor": "#262626", + "axes.grid": False, + "grid.color": "#cccccc", + "xtick.color": "#262626", + "ytick.color": "#262626", + "legend.frameon": False, + }, + "seaborn-v0_8-whitegrid": { + "figure.facecolor": "white", + "axes.facecolor": "white", + "axes.edgecolor": "#cccccc", + "axes.labelcolor": "#262626", + "axes.grid": True, + "grid.color": "#cccccc", + "xtick.color": "#262626", + "ytick.color": "#262626", + "legend.frameon": False, + }, +} +_NAMED_STYLES["seaborn-whitegrid"] = _NAMED_STYLES["seaborn-v0_8-whitegrid"] + + class _StyleNamespace: - available = ("default", "xy") + available = ("default", "xy", *sorted(_NAMED_STYLES)) @staticmethod - def use(name: Union[str, list[str]]) -> None: - if name not in ("default", "xy"): - raise not_implemented(f"style.use({name!r})", "'default' or 'xy'") + def use(name: Union[str, dict[str, Any], list[Union[str, dict[str, Any]]]]) -> None: + if isinstance(name, list): + for item in name: + _StyleNamespace.use(item) + return + if isinstance(name, dict): + unknown = sorted(set(name) - set(rcParams)) + if unknown: + raise not_implemented( + f"style.use() rcParam {unknown[0]!r}", "the documented rcParams subset" + ) + rcParams.update(name) + return from . import _axes + if name in _NAMED_STYLES: + # matplotlib sheets are additive patches over the current params. + rcParams.update(_NAMED_STYLES[name]) + _axes._component_cache.clear() + return + if name not in ("default", "xy"): + raise not_implemented( + f"style.use({name!r})", f"one of {_StyleNamespace.available} or an rcParams dict" + ) if name == "xy": _axes._MPL_THEME_TOKENS.clear() # engine-native look - # 'default' keeps the matplotlib-flavored theme + else: + rcdefaults() + _axes._MPL_THEME_TOKENS.update( + plot_background="#ffffff", axis_color="#000000", text_color="#262626" + ) + _axes._component_cache.clear() + + @staticmethod + @contextlib.contextmanager + def context(name: Union[str, dict[str, Any], list[Union[str, dict[str, Any]]]]): + from . import _axes + + snapshot = dict(rcParams) + tokens = dict(_axes._MPL_THEME_TOKENS) + try: + _StyleNamespace.use(name) + yield + finally: + rcParams.clear() + rcParams.update(snapshot) + _axes._MPL_THEME_TOKENS.clear() + _axes._MPL_THEME_TOKENS.update(tokens) + _axes._component_cache.clear() style = _StyleNamespace() +def cycler(*args: Any, **kwargs: Any) -> Any: + """matplotlib.cycler reduced to the color cycle the engine consumes.""" + if len(args) == 2 and not kwargs: + key, values = args + elif not args and len(kwargs) == 1: + key, values = next(iter(kwargs.items())) + else: + raise not_implemented("cycler() with multiple keys", "a single color cycle") + if key != "color": + raise not_implemented(f"cycler({key!r})", "a 'color' cycle") + return _PropCycle(list(values)) + + def np_asarray_passthrough(x: Any) -> Any: # pragma: no cover - numpy re-export shim return np.asarray(x) diff --git a/python/xy/pyplot/_artists.py b/python/xy/pyplot/_artists.py index 66b53714..8d24ec3d 100644 --- a/python/xy/pyplot/_artists.py +++ b/python/xy/pyplot/_artists.py @@ -14,18 +14,58 @@ import numpy as np from ._colors import resolve_color +from ._transforms import Bbox, IdentityTransform + + +def _set_entry_clim(artist: "Artist", vmin: Any = None, vmax: Any = None) -> None: + """Set a mappable entry's color domain, autoscaling any side left as None.""" + if vmax is None and isinstance(vmin, (tuple, list)): + vmin, vmax = vmin + entry = artist._entry + if vmin is None or vmax is None: + kwargs = entry.get("kwargs", {}) + values = entry.get("source_z", kwargs.get("color", entry.get("z"))) + try: + numeric = np.asarray(values, dtype=np.float64) + finite = numeric[np.isfinite(numeric)] + except (TypeError, ValueError): + finite = np.asarray([], dtype=np.float64) + fallback = ( + float(finite.min()) if finite.size else 0.0, + float(finite.max()) if finite.size else 1.0, + ) + current = entry.get("kwargs", {}).get("domain", fallback) + vmin = current[0] if vmin is None else vmin + vmax = current[1] if vmax is None else vmax + domain = (float(vmin), float(vmax)) + entry["kwargs"]["domain"] = domain + axes = artist._axes + # A live colorbar derived from this mappable tracks the new limits, as in + # matplotlib where the colorbar shares the mappable's norm. + if getattr(axes, "_colorbar_source", None) is entry and axes._colorbar is not None: + axes._colorbar["domain"] = [domain[0], domain[1]] + artist._touch() class Artist: def __init__(self, axes: Any, entry: dict[str, Any]) -> None: self._axes = axes self._entry = entry # the mutable spec dict the Axes rendered from + self._visible = True + self._visible_opacity = float(entry.get("kwargs", {}).get("opacity", 1.0)) + self._zorder = float(entry.get("_zorder", 0.0)) + self._clip_on = bool(entry.get("kwargs", {}).get("clip_on", True)) + self._transform: Any = axes.transData if axes is not None else IdentityTransform() + self._rasterized = False + if axes is not None: + axes._register_artist(self) def _touch(self) -> None: self._axes._invalidate() def remove(self) -> None: self._axes._remove_entry(self._entry) + self._axes._unregister_artist(self) def set_label(self, label: str) -> None: self._entry["kwargs"]["name"] = str(label) @@ -35,9 +75,122 @@ def get_label(self) -> Optional[str]: return self._entry["kwargs"].get("name") def set_alpha(self, alpha: float) -> None: - self._entry["kwargs"]["opacity"] = float(alpha) + self._visible_opacity = float(alpha) + if self._visible: + self._entry["kwargs"]["opacity"] = float(alpha) + self._touch() + + def get_alpha(self) -> Any: + if not self._visible: + return self._visible_opacity + return self._entry["kwargs"].get("opacity") + + def set_visible(self, visible: bool) -> None: + visible = bool(visible) + if visible == self._visible: + return + if not visible: + self._visible_opacity = float(self._entry["kwargs"].get("opacity", 1.0)) + self._visible = visible + self._entry["kwargs"]["opacity"] = self._visible_opacity if visible else 0.0 + self._touch() + + def get_visible(self) -> bool: + return self._visible + + def set_zorder(self, level: float) -> None: + self._zorder = float(level) + self._entry["_zorder"] = self._zorder + host = self._axes._y2_of or self._axes + host._entries.sort(key=lambda item: float(item.get("_zorder", 0.0))) + self._touch() + + def get_zorder(self) -> float: + return self._zorder + + def set_clip_on(self, enabled: bool) -> None: + if not enabled: + raise NotImplementedError( + f"{type(self).__name__} unclipped rendering is not supported by xy.pyplot" + ) + self._clip_on = bool(enabled) + self._touch() + + def get_clip_on(self) -> bool: + return self._clip_on + + def set_clip_path(self, path: Any) -> None: + raise NotImplementedError( + f"{type(self).__name__} clip paths are not supported; image clip paths are supported" + ) + + def get_clip_path(self) -> Any: + return self._entry.get("clip_path") + + def set_transform(self, transform: Any) -> None: + if not hasattr(transform, "transform"): + raise TypeError("transform must provide a transform(xy) method") + coordinate_space = getattr(transform, "coordinate_space", "data") + if coordinate_space != "data" or not hasattr(self._transform, "inverted"): + raise NotImplementedError( + f"{type(self).__name__} requires an invertible data-coordinate transform" + ) + if hasattr(transform, "inverted"): + try: + transform.inverted() # a singular matrix must fail this call, + except np.linalg.LinAlgError as error: # not the next set_transform + raise ValueError("set_transform() requires an invertible transform") from error + old_inverse = self._transform.inverted() + + def convert(x: Any, y: Any) -> tuple[np.ndarray, np.ndarray]: + xa, ya = np.broadcast_arrays(x, y) + points = np.column_stack((xa.ravel(), ya.ravel())) + made = np.asarray(transform.transform(old_inverse.transform(points)), dtype=float) + return made[:, 0].reshape(xa.shape), made[:, 1].reshape(ya.shape) + + if "x" in self._entry and "y" in self._entry: + self._entry["x"], self._entry["y"] = convert(self._entry["x"], self._entry["y"]) + elif self._entry.get("kind") == "@mark": + factory = self._entry.get("factory") + pairs = { + "segments": ((0, 1), (2, 3)), + "triangle_mesh": ((0, 1), (2, 3), (4, 5)), + "step": ((0, 1),), + "stem": ((0, 1),), + "errorbar": ((0, 1),), + }.get(factory) + if pairs is None: + raise NotImplementedError( + f"{type(self).__name__} transform is not supported for {factory!r} geometry" + ) + args = list(self._entry["args"]) + for x_index, y_index in pairs: + args[x_index], args[y_index] = convert(args[x_index], args[y_index]) + self._entry["args"] = tuple(args) + else: + raise NotImplementedError( + f"{type(self).__name__} transform is not supported for this geometry" + ) + for marker_entry in self._marker_entries(): + if marker_entry is not self._entry: + marker_entry["x"], marker_entry["y"] = convert(marker_entry["x"], marker_entry["y"]) + self._transform = transform self._touch() + def get_transform(self) -> Any: + return self._transform + + def set_rasterized(self, rasterized: bool) -> None: + if rasterized: + raise NotImplementedError( + f"{type(self).__name__} selective rasterization is not supported by xy.pyplot; " + "PNG export rasterizes everything already" + ) + self._rasterized = bool(rasterized) + + def get_rasterized(self) -> bool: + return self._rasterized + def set_color(self, color: Any) -> None: self._entry["kwargs"]["color"] = resolve_color(color) self._touch() @@ -45,32 +198,112 @@ def set_color(self, color: Any) -> None: def get_color(self) -> Any: return self._entry["kwargs"].get("color") + def _marker_entries(self) -> list[dict[str, Any]]: + """Return marker specs controlled by this matplotlib-style handle. + + ``plot(..., marker=...)`` is represented internally as a line entry + followed by a scatter overlay. Matplotlib exposes one ``Line2D`` for + both, so visible marker mutations need to follow that adjacent overlay. + Marker-only plots are already backed directly by a scatter entry. + """ + + if self._entry.get("kind") == "scatter": + return [self._entry] + + entries = getattr(self._axes, "_entries", []) + try: + index = next(i for i, entry in enumerate(entries) if entry is self._entry) + except StopIteration: + return [] + if index + 1 >= len(entries): + return [] + + candidate = entries[index + 1] + if candidate.get("kind") != "scatter": + return [] + kwargs = candidate.get("kwargs", {}) + if "symbol" not in kwargs: + return [] + return [candidate] + def set_markerfacecolor(self, color: Any) -> None: - del color + for entry in self._marker_entries(): + entry["kwargs"]["color"] = resolve_color(color) + self._touch() + + set_mfc = set_markerfacecolor def set_markeredgecolor(self, color: Any) -> None: - del color + for entry in self._marker_entries(): + if isinstance(color, str) and color.lower() == "none": + entry["kwargs"].pop("stroke", None) + entry["kwargs"].pop("stroke_width", None) + else: + entry["kwargs"]["stroke"] = resolve_color(color) + entry["kwargs"].setdefault("stroke_width", 1.0) + self._touch() + + set_mec = set_markeredgecolor def set_markersize(self, size: Any) -> None: - del size + # Matplotlib specifies Line2D marker size in points; xy's scatter mark + # consumes CSS-pixel diameters. 96 dpi maps one point to 4/3 pixels. + diameter = float(size) * (4.0 / 3.0) + for entry in self._marker_entries(): + entry["kwargs"]["size"] = diameter + self._touch() + + set_ms = set_markersize class Line2D(Artist): """Handle for plt.plot lines (and their marker overlays).""" + @staticmethod + def _segment_args_from_xy(x: Any, y: Any) -> tuple[Any, Any, Any, Any]: + xv, yv = np.asarray(x), np.asarray(y) + try: + finite_pairs = np.isfinite(xv.astype(np.float64)) & np.isfinite(yv.astype(np.float64)) + except (TypeError, ValueError): + finite_pairs = np.ones(len(xv), dtype=bool) + keep = finite_pairs[:-1] & finite_pairs[1:] + return xv[:-1][keep], yv[:-1][keep], xv[1:][keep], yv[1:][keep] + + @staticmethod + def _same_data(left: Any, right: Any) -> bool: + try: + return bool(np.array_equal(np.asarray(left), np.asarray(right), equal_nan=True)) + except TypeError: + return bool( + np.array_equal(np.asarray(left, dtype=object), np.asarray(right, dtype=object)) + ) + + def _sync_marker_data(self, key: str, old_value: Any, value: Any) -> None: + for entry in self._marker_entries(): + if key not in entry: + continue + if self._same_data(entry[key], old_value): + entry[key] = value + def _set_xy(self, index: int, value: Any) -> None: + key = "x" if index == 0 else "y" if self._entry["kind"] == "@mark" and self._entry.get("factory") == "step": + old_value = self._entry.get(key) args = list(self._entry["args"]) args[index] = value self._entry["args"] = tuple(args) - self._entry["x" if index == 0 else "y"] = value + self._entry[key] = value + self._sync_marker_data(key, old_value, value) return - key = "x" if index == 0 else "y" if key not in self._entry: raise NotImplementedError( - f"set_{key}data is not supported for segment-backed Line2D handles" + f"set_{key}data is not supported for Line2D handles without retained data" ) + old_value = self._entry[key] self._entry[key] = value + if self._entry["kind"] == "@mark" and self._entry.get("factory") == "segments": + self._entry["args"] = self._segment_args_from_xy(self._entry["x"], self._entry["y"]) + self._sync_marker_data(key, old_value, value) def set_data(self, x: Any, y: Any) -> None: self._set_xy(0, x) @@ -85,10 +318,12 @@ def set_ydata(self, y: Any) -> None: self._set_xy(1, y) self._touch() - def get_xdata(self) -> Any: + def get_xdata(self, orig: bool = True) -> Any: + del orig # compat-noop: one canonical data array, no unit-converted copy return self._entry["x"] - def get_ydata(self) -> Any: + def get_ydata(self, orig: bool = True) -> Any: + del orig # compat-noop: one canonical data array, no unit-converted copy return self._entry["y"] def set_linewidth(self, w: float) -> None: @@ -102,17 +337,24 @@ def set_dashes(self, sequence: Any) -> None: self._touch() def set_dash_capstyle(self, style: Any) -> None: - del style + raise NotImplementedError("xy.pyplot does not support dash cap style mutation") - set_solid_capstyle = set_dash_capstyle + def set_solid_capstyle(self, style: Any) -> None: + raise NotImplementedError("xy.pyplot does not support solid cap style mutation") def set_gapcolor(self, color: Any) -> None: - del color + raise NotImplementedError("xy.pyplot does not support gapcolor mutation") class PathCollection(Artist): """Handle for plt.scatter marks.""" + def get_array(self) -> Any: + return self._entry.get("source_array", self._entry.get("kwargs", {}).get("color")) + + def get_offsets(self) -> Any: + return np.column_stack((self._entry.get("x", []), self._entry.get("y", []))) + def set_offsets(self, xy: Any) -> None: import numpy as np @@ -141,6 +383,12 @@ def cmap(self) -> Any: return _colors.Cmap(self._entry["kwargs"].get("colormap", "viridis")) + def set_clim(self, vmin: Any = None, vmax: Any = None) -> None: + _set_entry_clim(self, vmin, vmax) + + def get_cmap(self) -> Any: + return self.cmap + class AxesImage(Artist): """Image handle with the scalar-mappable surface used by gallery helpers.""" @@ -218,6 +466,7 @@ def set_clip_path(self, path: Any) -> None: self._touch() def set_transform(self, transform: Any) -> None: + self._transform = transform self._entry["transform"] = transform if not hasattr(transform, "transform") or not hasattr(transform, "inverted"): self._touch() @@ -277,10 +526,7 @@ def set_transform(self, transform: Any) -> None: self._touch() def set_clim(self, vmin: Any = None, vmax: Any = None) -> None: - if vmax is None and isinstance(vmin, (tuple, list)): - vmin, vmax = vmin - self._entry["kwargs"]["domain"] = (float(vmin), float(vmax)) - self._touch() + _set_entry_clim(self, vmin, vmax) def norm(self, value: Any) -> Any: import numpy as np @@ -299,6 +545,11 @@ def __init__(self, axes: Any, entry: dict[str, Any]) -> None: self.datavalues = entry.get("y") self.orientation = entry.get("kwargs", {}).get("orientation", "vertical") self.errorbar = None + axes._register_container(self) + + def remove(self) -> None: + super().remove() + self._axes._unregister_container(self) @property def position_centers(self) -> Any: @@ -337,12 +588,14 @@ def __init__(self, artist: Artist) -> None: self.markerline = artist self.stemlines = artist self.baseline = artist + artist._axes._register_container(self) def __iter__(self): return iter((self.markerline, self.stemlines, self.baseline)) def remove(self) -> None: self.stemlines.remove() + self.stemlines._axes._unregister_container(self) class ErrorbarContainer: @@ -353,12 +606,14 @@ def __init__(self, artist: Artist, data_line: Optional[Line2D] = None) -> None: self.has_xerr = artist._entry["kwargs"].get("xerr") is not None self.has_yerr = artist._entry["kwargs"].get("yerr") is not None self._artist = artist + artist._axes._register_container(self) def __iter__(self): return iter(self.lines) def remove(self) -> None: self._artist.remove() + self._artist._axes._unregister_container(self) class ContourSet(Artist): @@ -448,6 +703,9 @@ def legend_elements(self, **kwargs: Any) -> tuple[list["ContourSet"], list[str]] class PolyCollection(Artist): """Generic collection handle used by adapter-composed chart families.""" + def set_clim(self, vmin: Any = None, vmax: Any = None) -> None: + _set_entry_clim(self, vmin, vmax) + class Wedge(PolyCollection): """Pie wedge backed by a grouped subset of one native sector mesh.""" @@ -514,6 +772,9 @@ class Table: def __init__(self, artists: list[Artist], cells: dict[tuple[int, int], "Text"]) -> None: self._artists = artists self._cells = cells + if artists: + self._axes = artists[0]._axes + self._axes._register_artist(self) def get_celld(self) -> dict[tuple[int, int], "Text"]: return dict(self._cells) @@ -521,6 +782,8 @@ def get_celld(self) -> dict[tuple[int, int], "Text"]: def remove(self) -> None: for artist in self._artists: artist.remove() + if hasattr(self, "_axes"): + self._axes._unregister_artist(self) class Text(Artist): @@ -535,9 +798,7 @@ def get_text(self) -> str: return str(self._entry["args"][2]) def get_window_extent(self, renderer: Any = None) -> Any: - del renderer - Bbox = __import__("matplotlib.transforms", fromlist=["Bbox"]).Bbox - + del renderer # compat-noop: shim text extents are renderer-independent x, y, text = self._entry["args"] width = max(0.05, len(str(text)) * 0.018) return Bbox.from_bounds(float(x) - width / 2, float(y) - 0.04, width, 0.08) diff --git a/python/xy/pyplot/_axes.py b/python/xy/pyplot/_axes.py index 796f74d4..3c9f1532 100644 --- a/python/xy/pyplot/_axes.py +++ b/python/xy/pyplot/_axes.py @@ -11,6 +11,8 @@ from __future__ import annotations import copy +from contextlib import suppress +from datetime import timedelta from itertools import pairwise from typing import Any, Optional @@ -23,6 +25,8 @@ from ._fmt import parse_fmt from ._plot_types import PlotTypeMixin from ._rc import rcParams +from ._ticker import AutoLocator, NullLocator, ScalarFormatter, as_formatter +from ._transforms import Bbox, CoordinateTransform, IdentityTransform from ._translate import ( LINESTYLE_TO_DASH, MARKER_TO_SYMBOL, @@ -45,28 +49,103 @@ # core once, not once per figure (the perf guardrail in tests/pyplot counts # on this staying O(1) per process). _component_cache: dict[tuple, Any] = {} -_identity_transform_class: Any = None -_identity_transform_checked = False def _identity_transform() -> Any: - """Return Matplotlib's identity transform when available, without retrying imports.""" - global _identity_transform_checked, _identity_transform_class - if not _identity_transform_checked: - try: - _identity_transform_class = __import__( - "matplotlib.transforms", fromlist=["IdentityTransform"] - ).IdentityTransform - except ImportError: - _identity_transform_class = False - _identity_transform_checked = True - return _identity_transform_class() if _identity_transform_class else "data" + return IdentityTransform() + + +def _scale_values(values: Any, spec: Optional[dict[str, Any]], *, inverse: bool = False) -> Any: + """Apply a dependency-free matplotlib-style nonlinear scale.""" + if not spec or spec["name"] == "linear": + return values + source = np.asarray(values, dtype=np.float64) + name = spec["name"] + if name == "symlog": + threshold = spec["linthresh"] + scale = spec["linscale"] + base = spec["base"] + adjusted = scale / (1.0 - base**-1) + if inverse: + absolute = np.abs(source) + result = np.where( + absolute <= threshold * adjusted, + absolute / adjusted, + threshold * np.power(base, absolute / threshold - adjusted), + ) + return np.sign(source) * result + absolute = np.abs(source) + result = absolute * adjusted + outside = absolute > threshold + result = np.asarray(result) + result[outside] = threshold * ( + adjusted + np.log(absolute[outside] / threshold) / np.log(base) + ) + return np.sign(source) * result + if name == "logit": + if inverse: + return 1.0 / (1.0 + np.exp(-source)) + with np.errstate(divide="ignore", invalid="ignore"): + result = np.log(source / (1.0 - source)) + # values at/outside (0, 1) are masked like matplotlib, never ±inf + return np.where((source > 0.0) & (source < 1.0), result, np.nan) + if name == "asinh": + width = spec["linear_width"] + return width * np.sinh(source / width) if inverse else width * np.arcsinh(source / width) + return values + + +def _transform_entry_axis(entry: dict[str, Any], axis: str, old: Any, new: Any) -> None: + def convert(values: Any) -> Any: + return _scale_values(_scale_values(values, old, inverse=True), new) + + key = axis + if key in entry: + with suppress(TypeError, ValueError): + entry[key] = convert(entry[key]) + if entry.get("kind") != "@mark": + return + factory = entry.get("factory") + indexes = { + "segments": (0, 2) if axis == "x" else (1, 3), + "triangle_mesh": (0, 2, 4) if axis == "x" else (1, 3, 5), + "step": (0,) if axis == "x" else (1,), + "stem": (0,) if axis == "x" else (1,), + "errorbar": (0,) if axis == "x" else (1,), + "hexbin": (0,) if axis == "x" else (1,), + }.get(factory, ()) + args = list(entry.get("args", ())) + for index in indexes: + args[index] = convert(args[index]) + entry["args"] = tuple(args) + + +def _nonlinear_ticks(domain: tuple[float, float], spec: dict[str, Any]) -> np.ndarray: + lo, hi = map(float, _scale_values(np.asarray(domain), spec, inverse=True)) + if spec["name"] == "logit": + candidates = np.asarray([0.001, 0.01, 0.1, 0.5, 0.9, 0.99, 0.999]) + return candidates[(candidates >= lo) & (candidates <= hi)] + if spec["name"] == "symlog": + threshold, base = spec["linthresh"], spec["base"] + largest = max(abs(lo), abs(hi), threshold) + powers = threshold * base ** np.arange( + 0, max(1, int(np.ceil(np.log(largest / threshold) / np.log(base)))) + 1 + ) + candidates = np.unique(np.concatenate((-powers[::-1], [0.0], powers))) + return candidates[(candidates >= lo) & (candidates <= hi)] + return np.linspace(lo, hi, 6) class _AxisProxy: def __init__(self, axes: "Axes", axis: str) -> None: self.axes, self.axis = axes, axis + def _ticker_slot(self) -> tuple["Axes", str]: + axes = self.axes + host = axes._y2_of or axes + key = "y2" if (self.axis == "y" and axes._y2_of is not None) else self.axis + return host, key + def set_inverted(self, inverted: bool) -> None: props = self.axes._axis_props(self.axis) props["reverse"] = bool(inverted) @@ -79,29 +158,253 @@ def set_visible(self, visible: bool) -> None: def set(self, **kwargs: Any) -> None: if "visible" in kwargs: self.set_visible(bool(kwargs.pop("visible"))) - # Locator/formatter objects are accepted as layout hints; xy retains its - # deterministic native tick generator when no exact tick values exist. + if "major_locator" in kwargs: + self.set_major_locator(kwargs.pop("major_locator")) + if "major_formatter" in kwargs: + self.set_major_formatter(kwargs.pop("major_formatter")) + + def set_major_locator(self, locator: Any) -> None: + if not hasattr(locator, "tick_values"): + raise TypeError("set_major_locator() requires a Locator with tick_values()") + host, key = self._ticker_slot() + host._tickers[(key, "major_locator")] = locator + # A locator displaces explicit ticks, and vice versa: last call wins. + props = self.axes._axis_props(self.axis) + for stale in ("tick_values", "tick_labels", "tick_count"): + props.pop(stale, None) + host._auto_scale_axis_ticks.discard(key) + self.axes._invalidate() + + def get_major_locator(self) -> Any: + host, key = self._ticker_slot() + return host._tickers.get((key, "major_locator")) or AutoLocator() + + def set_major_formatter(self, formatter: Any) -> None: + host, key = self._ticker_slot() + host._tickers[(key, "major_formatter")] = as_formatter(formatter, "set_major_formatter()") + self.axes._invalidate() + + def get_major_formatter(self) -> Any: + host, key = self._ticker_slot() + return host._tickers.get((key, "major_formatter")) or ScalarFormatter() def set_minor_locator(self, locator: Any) -> None: - del locator + # compat-noop for rendering: minor ticks are outside the native axis + # contract. The locator is retained so get_minor_locator round-trips. + host, key = self._ticker_slot() + host._tickers[(key, "minor_locator")] = locator + + def get_minor_locator(self) -> Any: + host, key = self._ticker_slot() + return host._tickers.get((key, "minor_locator")) or NullLocator() + + def set_minor_formatter(self, formatter: Any) -> None: + # compat-noop for rendering, mirroring set_minor_locator. + host, key = self._ticker_slot() + host._tickers[(key, "minor_formatter")] = as_formatter(formatter, "set_minor_formatter()") + + def tick_bottom(self) -> None: + pass # exact no-op: the engine only draws bottom x ticks + + def tick_left(self) -> None: + pass # exact no-op: the engine only draws left y ticks + + def get_minor_formatter(self) -> Any: + from ._ticker import NullFormatter + + host, key = self._ticker_slot() + return host._tickers.get((key, "minor_formatter")) or NullFormatter() + + +class SecondaryAxis: + """A linked, tick-only secondary axis sharing its parent's plot rectangle.""" + + def __init__(self, parent: "Axes", axis: str, location: Any, functions: Any) -> None: + self._parent, self._axis = parent, axis + if isinstance(location, str): + allowed = {"top", "bottom"} if axis == "x" else {"left", "right"} + if location not in allowed: + raise ValueError(f"secondary {axis} axis location must be one of {sorted(allowed)}") + self._side, self._location = location, None + else: + value = float(location) + if not np.isfinite(value): + raise ValueError("secondary axis location must be finite") + raise NotImplementedError( + "xy.pyplot secondary axes currently support named edge locations only" + ) + if functions is None: + self._forward = self._inverse = lambda values: np.asarray(values, dtype=float) + elif isinstance(functions, (tuple, list)) and len(functions) == 2: + self._forward, self._inverse = functions + if not callable(self._forward) or not callable(self._inverse): + raise TypeError("secondary axis functions must be callable") + elif hasattr(functions, "transform") and hasattr(functions, "inverted"): + self._forward = functions.transform + self._inverse = functions.inverted().transform + else: + raise TypeError("functions must be a (forward, inverse) pair or invertible transform") + self._label = "" + self._ticks: Optional[np.ndarray] = None + self._tick_labels: Optional[list[str]] = None + + def set_xlabel(self, label: Any, **kwargs: Any) -> "SecondaryAxis": + if self._axis != "x": + raise AttributeError("secondary y axes use set_ylabel()") + if kwargs: + raise TypeError(f"set_xlabel() got unsupported keyword argument {next(iter(kwargs))!r}") + self._label = str(label) + self._parent._invalidate() + return self + + def set_ylabel(self, label: Any, **kwargs: Any) -> "SecondaryAxis": + if self._axis != "y": + raise AttributeError("secondary x axes use set_xlabel()") + if kwargs: + raise TypeError(f"set_ylabel() got unsupported keyword argument {next(iter(kwargs))!r}") + self._label = str(label) + self._parent._invalidate() + return self + + def set_ticks(self, ticks: Any, labels: Any = None, **kwargs: Any) -> None: + check_unsupported(kwargs, "secondary-axis set_ticks()") + self._ticks = np.asarray(ticks, dtype=float) + self._tick_labels = None + if labels is not None: + self._tick_labels = [str(label) for label in labels] + if len(self._tick_labels) != len(self._ticks): + raise ValueError("secondary-axis labels must match ticks") + self._parent._invalidate() + + def set_functions(self, functions: Any) -> None: + replacement = SecondaryAxis(self._parent, self._axis, self._side, functions) + self._forward, self._inverse = replacement._forward, replacement._inverse + self._parent._invalidate() + + def remove(self) -> None: + self._parent._secondary_axes.remove(self) + self._parent._invalidate() + + def _component(self, index: int) -> Any: + props = self._parent._axis_props(self._axis) + domain = np.asarray(props.get("domain", self._parent._auto_domain(self._axis)), dtype=float) + primary_spec = self._parent._scale_specs[self._axis] + primary_values = _scale_values( + np.linspace(domain[0], domain[1], 6), primary_spec, inverse=True + ) + secondary_values = np.asarray( + self._ticks if self._ticks is not None else self._forward(primary_values), dtype=float + ) + positions = np.asarray(self._inverse(secondary_values), dtype=float) + positions = np.asarray(_scale_values(positions, primary_spec), dtype=float) + if positions.shape != secondary_values.shape or not np.all(np.isfinite(positions)): + raise ValueError("secondary axis functions must return matching finite values") + labels = self._tick_labels or [f"{value:g}" for value in secondary_values] + factory = fc.x_axis if self._axis == "x" else fc.y_axis + axis_domain = (float(domain[0]), float(domain[1])) + return factory( + id=f"{self._axis}s{index}", + side=self._side, + domain=axis_domain, + tick_values=positions, + tick_labels=labels, + label=self._label or None, + ) + + +class _TickLabel: + """A tick label handle; styling applies to the whole axis, matching the + uniform ``for tick in ax.get_xticklabels()`` loops scripts write.""" + + def __init__(self, axes: "Axes", axis: str, text: str) -> None: + self._axes, self._axis, self._text = axes, axis, text + + def get_text(self) -> str: + return self._text + + def set_color(self, color: Any) -> None: + style = self._axes._axis_props(self._axis).setdefault("style", {}) + style["tick_label_color"] = resolve_color(color) + self._axes._invalidate() + + def set_rotation(self, angle: Any) -> None: + self._axes._axis_props(self._axis)["tick_label_angle"] = float(angle) + self._axes._invalidate() + + +class _SharedAxesGroup: + """matplotlib's shared-axes Grouper over the shim's shared props dicts.""" + + def __init__(self, axis: str) -> None: + self._axis = axis + + def _pool(self, ax: Any) -> list[Any]: + fig = getattr(ax, "figure", None) + return list(fig._axes) if fig is not None else [ax] + + def get_siblings(self, ax: Any) -> list[Any]: + props = ax._axis_props(self._axis) + return [a for a in self._pool(ax) if a._axis_props(self._axis) is props] or [ax] + + def joined(self, a: Any, b: Any) -> bool: + return a._axis_props(self._axis) is b._axis_props(self._axis) + + def join(self, *axes_list: Any) -> None: + first = axes_list[0] + shared = first._axis_props(self._axis) + for other in axes_list[1:]: + key = "y2" if (self._axis == "y" and other._y2_of is not None) else self._axis + (other._y2_of or other)._axis[key] = shared + other._invalidate() class _SpineProxy: + def __init__( + self, axes: "Axes", names: tuple[str, ...] = ("left", "bottom", "top", "right") + ) -> None: + self.axes, self.names = axes, names + def __getitem__(self, key: Any) -> "_SpineProxy": - del key - return self + names = (key,) if isinstance(key, str) else tuple(key) + unknown = set(names) - {"left", "bottom", "top", "right"} + if unknown: + raise KeyError(next(iter(unknown))) + return _SpineProxy(self.axes, names) + + def values(self) -> list["_SpineProxy"]: + return [_SpineProxy(self.axes, (name,)) for name in self.names] + + def keys(self) -> list[str]: + return list(self.names) + + def items(self) -> list[tuple[str, "_SpineProxy"]]: + return [(name, _SpineProxy(self.axes, (name,))) for name in self.names] + + def __iter__(self): + return iter(self.names) def set_visible(self, visible: bool) -> None: - del visible + for name in self.names: + if bool(visible): + if name in {"top", "right"}: + raise NotImplementedError( + f"xy.pyplot cannot show the {name} spine independently" + ) + self.axes._hidden_spines.discard(name) + elif name in {"left", "bottom"}: + # Hiding is deferred: both gone → transparent axis lines; one + # gone is inexpressible and fails loudly at build time. + self.axes._hidden_spines.add(name) + self.axes._invalidate() -def _cached_theme(grid: bool) -> Any: - key = ("theme", grid, tuple(sorted(_MPL_THEME_TOKENS.items()))) +def _cached_theme(grid: bool, tokens: dict[str, Any], style: dict[str, Any]) -> Any: + key = ("theme", grid, tuple(sorted(tokens.items())), tuple(sorted(style.items()))) made = _component_cache.get(key) if made is None: - tokens = dict(_MPL_THEME_TOKENS) - tokens["grid_color"] = _MPL_GRID_COLOR if grid else "transparent" - made = _component_cache[key] = fc.theme(**tokens) # ty: ignore[invalid-argument-type] + applied = dict(tokens) + applied["grid_color"] = _MPL_GRID_COLOR if grid else "transparent" + made = _component_cache[key] = fc.theme(style=style, **applied) return made @@ -120,11 +423,14 @@ class Axes(PlotTypeMixin): def __init__(self, figure: Any, *, y2_of: Optional["Axes"] = None) -> None: self.figure = figure self._entries: list[dict[str, Any]] = [] + self._owned_artists: list[Any] = [] + self._containers: list[Any] = [] self._axis: dict[str, dict[str, Any]] = {"x": {}, "y": {}, "y2": {}} self._title: Optional[str] = None self._legend = False self._legend_options: dict[str, Any] = {} self._colorbar: Optional[dict[str, Any]] = None + self._colorbar_source: Optional[dict[str, Any]] = None # entry the colorbar reads self._aspect_equal = False self._aspect_bounds: Optional[tuple[float, float, float, float]] = None self._insets: list[tuple["Axes", tuple[float, float, float, float]]] = [] @@ -132,21 +438,83 @@ def __init__(self, figure: Any, *, y2_of: Optional["Axes"] = None) -> None: self._figure_rect: Optional[tuple[float, float, float, float]] = None self._absolute_plot_ratio: Optional[float] = None self._padding: Optional[list[float]] = None + self._xmargin = 0.0 + self._ymargin = 0.0 + self._explicit_domains: set[str] = set() + self._secondary_axes: list[SecondaryAxis] = [] + self._scale_specs: dict[str, dict[str, Any]] = { + "x": {"name": "linear"}, + "y": {"name": "linear"}, + "y2": {"name": "linear"}, + } + self._auto_scale_axis_ticks: set[str] = set() + self._tickers: dict[tuple[str, str], Any] = {} + self._hidden_spines: set[str] = set() self._grid = bool(rcParams["axes.grid"]) + self._grid_color = _MPL_GRID_COLOR + self._grid_axis = "both" + self._grid_style: dict[str, Any] = {} + self._anchor: Optional[str] = None self._cycle = 0 + self._prop_cycle: Optional[list[str]] = None + self._load_rc_chrome() self._chart: Any = None self._twin: Optional[Axes] = None self._y2_of = y2_of # when set, our marks target axis id "y2" on the host - self.transAxes = _identity_transform() + self.transAxes = CoordinateTransform("axes_fraction") self.transData = _identity_transform() - if self.transAxes == "data": - self.transAxes = "axes fraction" self.xaxis = _AxisProxy(self, "x") self.yaxis = _AxisProxy(self, "y") - self.spines = _SpineProxy() + self.spines = _SpineProxy(self) + for axis in ("x", "y"): + style = _rc_axis_style(axis) + if style: + self._axis[axis]["style"] = style # -- lifecycle ----------------------------------------------------------- + def _load_rc_chrome(self) -> None: + """Snapshot the rcParams-derived color cycle, theme, and chrome styles.""" + cycle = rcParams["axes.prop_cycle"].by_key().get("color", []) + self._prop_cycle = [ + resolved for color in cycle if (resolved := resolve_color(color)) is not None + ] + self._grid_color = resolve_color(rcParams["grid.color"]) or _MPL_GRID_COLOR + self._theme_tokens = { + "plot_background": resolve_color(rcParams["axes.facecolor"]), + "axis_color": resolve_color(rcParams["axes.edgecolor"]), + "text_color": resolve_color( + rcParams["axes.labelcolor"] + if rcParams["axes.titlecolor"] == "auto" + else rcParams["axes.titlecolor"] + ), + } + family = rcParams["font.family"] + family = family if isinstance(family, str) else ", ".join(map(str, family)) + self._theme_style = { + "font-family": family, + "font-size": f"{float(rcParams['font.size']):g}px", + } + title_color = self._theme_tokens["text_color"] + self._chrome_styles = { + "title": { + "font-size": f"{_font_size(rcParams['axes.titlesize'], rcParams['font.size']):g}px", + "color": title_color, + }, + "axis_title": { + "font-size": f"{_font_size(rcParams['axes.labelsize'], rcParams['font.size']):g}px", + "color": resolve_color(rcParams["axes.labelcolor"]), + }, + "tick_label": { + "font-size": f"{_font_size(rcParams['xtick.labelsize'], rcParams['font.size']):g}px", + "color": resolve_color( + rcParams["xtick.color"] + if rcParams["xtick.labelcolor"] == "inherit" + else rcParams["xtick.labelcolor"] + ), + }, + } + def _invalidate(self) -> None: host = self._y2_of or self host._chart = None @@ -155,13 +523,93 @@ def _invalidate(self) -> None: def _remove_entry(self, entry: dict[str, Any]) -> None: host = self._y2_of or self - if entry in host._entries: - host._entries.remove(entry) + for index, candidate in enumerate(host._entries): + if candidate is entry: + host._entries.pop(index) + break host._invalidate() + def _register_artist(self, artist: Any) -> None: + host = self._y2_of or self + if artist not in host._owned_artists: + host._owned_artists.append(artist) + + def _unregister_artist(self, artist: Any) -> None: + host = self._y2_of or self + if artist in host._owned_artists: + host._owned_artists.remove(artist) + + def _register_container(self, container: Any) -> None: + host = self._y2_of or self + if container not in host._containers: + host._containers.append(container) + + def _unregister_container(self, container: Any) -> None: + host = self._y2_of or self + if container in host._containers: + host._containers.remove(container) + + @property + def lines(self) -> list[Any]: + return [item for item in self._owned_artists if isinstance(item, Line2D)] + + @property + def collections(self) -> list[Any]: + from ._artists import ContourSet + + return [ + item + for item in self._owned_artists + if isinstance(item, (PathCollection, PolyCollection, ContourSet)) + ] + + @property + def patches(self) -> list[Any]: + from ._artists import Wedge + + return [item for item in self._owned_artists if isinstance(item, Wedge)] + + @property + def texts(self) -> list[Any]: + return [item for item in self._owned_artists if isinstance(item, Text)] + + @property + def images(self) -> list[Any]: + return [item for item in self._owned_artists if isinstance(item, AxesImage)] + + @property + def tables(self) -> list[Any]: + from ._artists import Table + + return [item for item in self._owned_artists if isinstance(item, Table)] + + @property + def containers(self) -> list[Any]: + return list(self._containers) + + @property + def artists(self) -> list[Any]: + categorized = set( + self.lines + + self.collections + + self.patches + + self.texts + + self.images + + self.tables + + self.containers + ) + return [item for item in self._owned_artists if item not in categorized] + + def get_children(self) -> list[Any]: + """Return a stable snapshot of shim-owned children in creation order.""" + result = list(self._owned_artists) + result.extend(item for item in self._containers if item not in result) + return result + def _next_color(self) -> str: host = self._y2_of or self - color = PROP_CYCLE[host._cycle % len(PROP_CYCLE)] + cycle = getattr(host, "_prop_cycle", None) or PROP_CYCLE + color = cycle[host._cycle % len(cycle)] host._cycle += 1 return color @@ -176,6 +624,26 @@ def _categorical_position(self, axis: str, label: Any) -> float: props["tick_count"] = len(values) return float(values[labels.index(text)]) + def _transform_points(self, x: Any, y: Any, transform: Any) -> tuple[np.ndarray, np.ndarray]: + x_values, y_values = np.broadcast_arrays(x, y) + if transform in (None, self.transData): + return np.asarray(x_values), np.asarray(y_values) + if not hasattr(transform, "transform"): + raise TypeError("transform must provide a transform(xy) method") + points = np.asarray( + transform.transform(np.column_stack((x_values.ravel(), y_values.ravel()))), dtype=float + ) + if points.shape != (x_values.size, 2): + raise ValueError("transform must return one x/y pair per input point") + if getattr(transform, "coordinate_space", "data") in {"axes_fraction", "figure_fraction"}: + # baking fractions into data coordinates goes silently stale on + # the next limit change; only text/annotations track these spaces + raise not_implemented( + "data artists with transform=transAxes/transFigure", + "affine data transforms composed with ax.transData", + ) + return points[:, 0].reshape(x_values.shape), points[:, 1].reshape(y_values.shape) + def _add(self, kind: str, entry: dict[str, Any]) -> dict[str, Any]: entry["kind"] = kind entry["y_axis"] = "y2" if self._y2_of is not None else "y" @@ -186,39 +654,116 @@ def _add(self, kind: str, entry: dict[str, Any]) -> dict[str, Any]: for key in [k for k, v in kw.items() if v is None]: del kw[key] host = self._y2_of or self + nonlinear_axes = [] + for axis in ("x", "y"): + key = "y2" if axis == "y" and self._y2_of is not None else axis + spec = host._scale_specs[key] + if spec["name"] != "linear": + _transform_entry_axis(entry, axis, {"name": "linear"}, spec) + nonlinear_axes.append((axis, spec)) host._entries.append(entry) + for axis, spec in nonlinear_axes: + # scale-generated ticks were derived from the extent at + # set_*scale time; new data must refresh them (user-set ticks + # clear the marker and are left alone) + key = "y2" if axis == "y" and self._y2_of is not None else axis + if key in host._auto_scale_axis_ticks and spec["name"] in { + "symlog", + "logit", + "asinh", + }: + props = self._axis_props(axis) + ticks = _nonlinear_ticks(self._entry_extent(axis), spec) + props["tick_values"] = list(map(float, _scale_values(ticks, spec))) + props["tick_labels"] = [f"{tick:g}" for tick in ticks] + props["tick_count"] = max(1, len(ticks)) host._invalidate() return entry + def clear(self) -> None: + self._entries.clear() + self._owned_artists.clear() + self._containers.clear() + self._axis = {"x": {}, "y": {}, "y2": {}} + self._secondary_axes.clear() + self._scale_specs = { + "x": {"name": "linear"}, + "y": {"name": "linear"}, + "y2": {"name": "linear"}, + } + self._auto_scale_axis_ticks = set() + self._tickers = {} + self._hidden_spines = set() + self._title = None + self._legend = False + self._legend_options = {} + self._colorbar = None + self._colorbar_source = None + self._aspect_equal = False + self._aspect_bounds = None + self._insets = [] + self._insets_materialized = False + self._absolute_plot_ratio = None + self._padding = None + self._grid = bool(rcParams["axes.grid"]) + self._grid_color = _MPL_GRID_COLOR + self._grid_axis = "both" + self._grid_style = {} + self._cycle = 0 + self._load_rc_chrome() + self._chart = None + self._twin = None + self.xaxis = _AxisProxy(self, "x") + self.yaxis = _AxisProxy(self, "y") + self.spines = _SpineProxy(self) + for axis in ("x", "y"): + style = _rc_axis_style(axis) + if style: + self._axis[axis]["style"] = style + self._invalidate() + + cla = clear + # -- plotting ------------------------------------------------------------ def plot(self, *args: Any, **kwargs: Any) -> list[Line2D]: - scalex = kwargs.pop("scalex", True) # accepted, autorange handles it + scalex = kwargs.pop("scalex", True) scaley = kwargs.pop("scaley", True) - del scalex, scaley + if scalex is not True or scaley is not True: + raise not_implemented("plot(scalex=False/scaley=False)") base = line_kwargs(kwargs) marker = kwargs.pop("marker", None) markersize = kwargs.pop("markersize", kwargs.pop("ms", None)) - kwargs.pop("markerfacecolor", kwargs.pop("mfc", None)) - kwargs.pop("markerfacecoloralt", None) - kwargs.pop("markeredgecolor", kwargs.pop("mec", None)) - kwargs.pop("markeredgewidth", kwargs.pop("mew", None)) - kwargs.pop("fillstyle", None) - kwargs.pop("solid_capstyle", None) - kwargs.pop("solid_joinstyle", None) - kwargs.pop("dash_capstyle", None) - kwargs.pop("dash_joinstyle", None) + markerfacecolor = kwargs.pop("markerfacecolor", kwargs.pop("mfc", None)) + markerfacecoloralt = kwargs.pop("markerfacecoloralt", None) + markeredgecolor = kwargs.pop("markeredgecolor", kwargs.pop("mec", None)) + markeredgewidth = kwargs.pop("markeredgewidth", kwargs.pop("mew", None)) + fillstyle = kwargs.pop("fillstyle", None) + cap_join = { + key: kwargs.pop(key, None) + for key in ("solid_capstyle", "solid_joinstyle", "dash_capstyle", "dash_joinstyle") + } + if markerfacecoloralt is not None: + raise not_implemented("plot(markerfacecoloralt=...)") + if fillstyle not in (None, "full"): + raise not_implemented(f"plot(fillstyle={fillstyle!r})") + if any(value is not None for value in cap_join.values()): + raise not_implemented("plot(capstyle/joinstyle)") markevery = kwargs.pop("markevery", None) drawstyle = kwargs.pop("drawstyle", None) transform = kwargs.pop("transform", None) + if transform is not None and not hasattr(transform, "transform"): + raise TypeError("plot transform must provide transform(xy)") + if drawstyle not in (None, "default", "steps-pre", "steps-mid", "steps-post"): + raise ValueError(f"unsupported drawstyle: {drawstyle!r}") check_unsupported(kwargs, "plot()") handles: list[Line2D] = [] for x, y, fmt in _iter_plot_groups(args): x, y = np.atleast_1d(x), np.atleast_1d(y) - if transform is not None and hasattr(transform, "transform"): - points = np.asarray(transform.transform(np.column_stack((x, y)))) - x, y = points[:, 0], points[:, 1] + x, y = _convert_timedelta_axis(x), _convert_timedelta_axis(y) + if transform is not None: + x, y = self._transform_points(x, y, transform) if x.ndim == 2 or y.ndim == 2: if x.ndim == 1: x = np.broadcast_to(x[:, None], np.asarray(y).shape) @@ -268,7 +813,24 @@ def plot(self, *args: Any, **kwargs: Any) -> list[Line2D]: "kwargs": { **{k: v for k, v in entry_kwargs.items() if k != "width"}, "symbol": _marker_symbol(this_marker or "o"), - "size": float(markersize or rcParams["lines.markersize"]), + "size": float( + rcParams["lines.markersize"] if markersize is None else markersize + ), + **( + {"color": resolve_color(markerfacecolor)} + if markerfacecolor not in (None, "auto") + else {} + ), + **( + { + "stroke": resolve_color(markeredgecolor), + "stroke_width": float( + 1.0 if markeredgewidth is None else markeredgewidth + ), + } + if markeredgecolor not in (None, "auto", "none") + else {} + ), }, }, ) @@ -338,13 +900,35 @@ def plot(self, *args: Any, **kwargs: Any) -> list[Line2D]: # Matplotlib marker sizes are points while the # engine consumes CSS-pixel diameters. At the # default 96 dpi, 6 pt is 8 px. - "size": float(markersize or rcParams["lines.markersize"]) + "size": float( + rcParams["lines.markersize"] + if markersize is None + else markersize + ) * (4.0 / 3.0), "name": None, + **( + {"color": resolve_color(markerfacecolor)} + if markerfacecolor not in (None, "auto") + else {} + ), + **( + { + "stroke": resolve_color(markeredgecolor), + "stroke_width": float( + 1.0 if markeredgewidth is None else markeredgewidth + ), + } + if markeredgecolor not in (None, "auto", "none") + else {} + ), }, }, ) - handles.append(Line2D(self, entry)) + handle = Line2D(self, entry) + if transform is not None: + handle._transform = transform + handles.append(handle) return handles def scatter( @@ -356,29 +940,63 @@ def scatter( alpha = kwargs.pop("alpha", None) label = kwargs.pop("label", None) marker = kwargs.pop("marker", None) + transform = kwargs.pop("transform", None) edgecolors = kwargs.pop("edgecolors", kwargs.pop("edgecolor", None)) - linewidths = kwargs.pop("linewidths", kwargs.pop("linewidth", None)) + linewidths = kwargs.pop("linewidths", kwargs.pop("linewidth", kwargs.pop("lw", None))) plotnonfinite = bool(kwargs.pop("plotnonfinite", False)) - kwargs.pop("vmin", None), kwargs.pop("vmax", None) # autorange handles + vmin, vmax = kwargs.pop("vmin", None), kwargs.pop("vmax", None) + norm = kwargs.pop("norm", None) + if norm is not None: + raise not_implemented("scatter(norm=...)") check_unsupported(kwargs, "scatter()") - xv = np.asarray(x).reshape(-1) - yv = np.asarray(y).reshape(-1) + xv = np.ma.asarray(x).reshape(-1) + yv = np.ma.asarray(y).reshape(-1) + s_arr = None if s is None or np.isscalar(s) else np.ma.asarray(s).reshape(-1) + dropped = np.ma.getmaskarray(xv) | np.ma.getmaskarray(yv) + if s_arr is not None and len(s_arr) == len(dropped): + dropped = dropped | np.ma.getmaskarray(s_arr) + if dropped.any(): + # matplotlib never draws rows masked in x, y, or s + keep = ~dropped + xv, yv = xv[keep], yv[keep] + if s_arr is not None and len(s_arr) == len(dropped): + s_arr = s_arr[keep] + if ( + c is not None + and not isinstance(c, str) + and not ( + isinstance(c, (tuple, list)) + and len(c) in (3, 4) + and not hasattr(c[0], "__len__") + ) + ): + c_rows = np.ma.asarray(c) + if c_rows.ndim >= 1 and c_rows.shape[0] == len(dropped): + c = c_rows[keep] + xv, yv = np.asarray(xv), np.asarray(yv) + if s_arr is not None: + s = np.asarray(s_arr) x, y = xv, yv - if s is not None and not np.isscalar(s): - s = np.asarray(s).reshape(-1) - cv = None if c is None or isinstance(c, str) else np.asarray(c).reshape(-1) + if transform is not None: + x, y = self._transform_points(x, y, transform) + source_color = None + cv = None if c is None or isinstance(c, str) else np.ma.asarray(c).reshape(-1) if ( cv is not None and cv.ndim == 1 and len(cv) == len(xv) and np.issubdtype(cv.dtype, np.number) ): - finite_color = np.isfinite(cv.astype(np.float64, copy=False)) + source_color = cv.copy() + numeric_color = np.ma.asarray(cv, dtype=np.float64) + finite_color = np.isfinite(numeric_color.filled(np.nan)) & ~np.ma.getmaskarray( + numeric_color + ) if plotnonfinite: - cv = np.where(finite_color, cv, 0.0) + cv = np.where(finite_color, numeric_color.filled(0.0), 0.0) else: - xv, yv, cv = xv[finite_color], yv[finite_color], cv[finite_color] + xv, yv, cv = xv[finite_color], yv[finite_color], numeric_color.data[finite_color] if s is not None and not np.isscalar(s): s = np.asarray(s)[finite_color] x, y, c = xv, yv, cv @@ -400,15 +1018,33 @@ def scatter( entry_kwargs["color"] = np.asarray(c) # data array, not a color else: entry_kwargs["color"] = np.asarray(c) # value encoding - entry_kwargs["colormap"] = resolve_cmap(cmap) if cmap else "viridis" + entry_kwargs["colormap"] = resolve_cmap( + cmap if cmap is not None else rcParams["image.cmap"] + ) + if vmin is not None or vmax is not None: + # one-sided limits autoscale the other side, like matplotlib + values = np.asarray(c, dtype=np.float64) + finite = values[np.isfinite(values)] + lo = ( + float(vmin) if vmin is not None else float(finite.min()) if finite.size else 0.0 + ) + hi = ( + float(vmax) if vmax is not None else float(finite.max()) if finite.size else 1.0 + ) + entry_kwargs["domain"] = (lo, hi) no_edges = edgecolors is None or ( isinstance(edgecolors, str) and edgecolors.lower() == "none" ) if not no_edges: entry_kwargs["stroke"] = resolve_color(edgecolors) - entry_kwargs["stroke_width"] = float(linewidths or 1.0) + entry_kwargs["stroke_width"] = float(1.0 if linewidths is None else linewidths) entry = self._add("scatter", {"x": x, "y": y, "kwargs": entry_kwargs}) - return PathCollection(self, entry) + if source_color is not None: + entry["source_array"] = source_color + artist = PathCollection(self, entry) + if transform is not None: + artist._transform = transform + return artist def bar( self, x: Any, height: Any, width: float = 0.8, bottom: Any = None, **kwargs: Any @@ -446,7 +1082,14 @@ def _bar_like( error_kw = kwargs.pop("error_kw", {}) or {} capsize = kwargs.pop("capsize", error_kw.pop("capsize", None)) kwargs.pop("ecolor", error_kw.pop("ecolor", None)) - kwargs.pop("align", None) # engine centers; 'edge' approximated as center + align = kwargs.pop("align", "center") + if align not in {"center", "edge"}: + raise ValueError("bar()/barh() align must be 'center' or 'edge'") + if align == "edge": + try: + cats = np.asarray(cats, dtype=np.float64) + float(thickness) / 2.0 + except (TypeError, ValueError): + raise ValueError("bar align='edge' requires numeric positions") from None check_unsupported(kwargs, "bar()/barh()") colors = None scalar_channels = False @@ -595,7 +1238,26 @@ def hist( for index, values in enumerate(counts): positions = centers if stacked else centers + (index - (len(datasets) - 1) / 2) * width current_base = base.copy() if stacked else np.zeros_like(values) - if histtype.startswith("step"): + if histtype == "stepfilled": + # matplotlib fills the step polygon down to the baseline; the + # area mark takes the pre-expanded step vertices verbatim. + tops = values + current_base + entry = self._add( + "@mark", + { + "factory": "area", + "args": (np.repeat(edges, 2)[1:-1], np.repeat(tops, 2)), + "kwargs": { + "base": np.repeat(current_base, 2), + "color": resolve_color(colors[index]) + if colors[index] is not None + else self._next_color(), + "name": None if labels[index] is None else str(labels[index]), + "opacity": 1.0 if alpha is None else float(alpha), + }, + }, + ) + elif histtype.startswith("step"): step_values = values + current_base entry = self._add( "@mark", @@ -649,7 +1311,11 @@ def fill_between(self, x: Any, y1: Any, y2: Any = 0.0, **kwargs: Any) -> PolyCol where = kwargs.pop("where", None) step = kwargs.pop("step", None) transform = kwargs.pop("transform", None) - kwargs.pop("interpolate", None) + interpolate = kwargs.pop("interpolate", False) + if step not in (None, "pre", "post", "mid"): + raise ValueError("fill_between step must be 'pre', 'post', 'mid', or None") + if transform not in (None, "xaxis transform"): + raise not_implemented("fill_between(transform=...)") check_unsupported(kwargs, "fill_between()") xv, upper, lower = np.broadcast_arrays( _masked_float(x), @@ -680,6 +1346,30 @@ def fill_between(self, x: Any, y1: Any, y2: Any = 0.0, **kwargs: Any) -> PolyCol entries: list[dict[str, Any]] = [] for start, end in zip(starts, ends, strict=True): sx, su, sl = xv[start:end], upper[start:end], lower[start:end] + if interpolate: + # Extend a selected region to the linear intersection of y1 + # and y2 at each where-boundary, matching Matplotlib's useful + # behavior for threshold fills. + if start > 0: + d0 = upper[start - 1] - lower[start - 1] + d1 = upper[start] - lower[start] + if np.isfinite(d0 + d1) and d0 != d1: + t = float(np.clip(-d0 / (d1 - d0), 0.0, 1.0)) + cross_x = xv[start - 1] + t * (xv[start] - xv[start - 1]) + cross_y = upper[start - 1] + t * (upper[start] - upper[start - 1]) + sx = np.r_[cross_x, sx] + su = np.r_[cross_y, su] + sl = np.r_[cross_y, sl] + if end < len(xv): + d0 = upper[end - 1] - lower[end - 1] + d1 = upper[end] - lower[end] + if np.isfinite(d0 + d1) and d0 != d1: + t = float(np.clip(-d0 / (d1 - d0), 0.0, 1.0)) + cross_x = xv[end - 1] + t * (xv[end] - xv[end - 1]) + cross_y = upper[end - 1] + t * (upper[end] - upper[end - 1]) + sx = np.r_[sx, cross_x] + su = np.r_[su, cross_y] + sl = np.r_[sl, cross_y] if step is not None: sx, su = _step_values(sx, su, step) _sx, sl = _step_values(xv[start:end], sl, step) @@ -698,6 +1388,51 @@ def fill_between(self, x: Any, y1: Any, y2: Any = 0.0, **kwargs: Any) -> PolyCol }, ) ) + if interpolate: + # A single selected point between deselected neighbors spans no + # interval, but matplotlib still draws its interpolated wedge. + covered = np.zeros(len(xv), dtype=bool) + for start, end in zip(starts, ends, strict=True): + covered[start:end] = True + finite_pt = np.isfinite(xv + upper + lower) + for i in np.flatnonzero(mask & ~covered & finite_pt): + sx = [float(xv[i])] + su = [float(upper[i])] + sl = [float(lower[i])] + for j, prepend in ((i - 1, True), (i + 1, False)): + if 0 <= j < len(xv) and finite_pt[j]: + d0 = upper[j] - lower[j] + d1 = upper[i] - lower[i] + if d0 != d1: + t = float(np.clip(-d0 / (d1 - d0), 0.0, 1.0)) + cross_x = float(xv[j] + t * (xv[i] - xv[j])) + cross_y = float(upper[j] + t * (upper[i] - upper[j])) + if prepend: + sx.insert(0, cross_x) + su.insert(0, cross_y) + sl.insert(0, cross_y) + else: + sx.append(cross_x) + su.append(cross_y) + sl.append(cross_y) + if len(sx) >= 2: + entries.append( + self._add( + "area", + { + "x": np.asarray(sx), + "y": np.asarray(su), + "kwargs": { + "base": np.asarray(sl), + "color": resolved_color, + "opacity": float(alpha) if alpha is not None else 1.0, + "name": str(label) + if label is not None and not entries + else None, + }, + }, + ) + ) if not entries: entries.append( self._add( @@ -718,18 +1453,51 @@ def fill_between(self, x: Any, y1: Any, y2: Any = 0.0, **kwargs: Any) -> PolyCol def imshow(self, z: Any, cmap: Any = None, **kwargs: Any) -> AxesImage: vmin = kwargs.pop("vmin", None) vmax = kwargs.pop("vmax", None) - origin = kwargs.pop("origin", "upper") + origin = kwargs.pop("origin", rcParams["image.origin"]) + if origin not in {"upper", "lower"}: + raise ValueError("imshow origin must be 'upper' or 'lower'") aspect = kwargs.pop("aspect", None) alpha = kwargs.pop("alpha", None) clim = kwargs.pop("clim", None) transform = kwargs.pop("transform", None) interpolation = kwargs.pop("interpolation", None) - kwargs.pop("interpolation_stage", None) - kwargs.pop("clip_on", None) + interpolation_stage = kwargs.pop("interpolation_stage", None) + clip_on = kwargs.pop("clip_on", True) colorizer = kwargs.pop("colorizer", None) clip_path = kwargs.pop("clip_path", None) extent = kwargs.pop("extent", None) norm = kwargs.pop("norm", None) + supported_interpolation = { + None, + "none", + "nearest", + "bilinear", + "bicubic", + "spline16", + "spline36", + "hanning", + "hamming", + "hermite", + "kaiser", + "quadric", + "catrom", + "gaussian", + "bessel", + "mitchell", + "sinc", + "lanczos", + "antialiased", + } + if interpolation not in supported_interpolation: + raise ValueError(f"unsupported imshow interpolation: {interpolation!r}") + if interpolation_stage not in (None, "data"): + raise not_implemented(f"imshow(interpolation_stage={interpolation_stage!r})") + if clip_on is not True: + raise not_implemented("imshow(clip_on=False)") + if transform not in (None, self.transData, self.transAxes): + raise not_implemented("imshow(transform=...)") + if transform is self.transAxes and extent is None: + raise ValueError("imshow(transform=ax.transAxes) requires extent") if colorizer is not None: norm = getattr(colorizer, "norm", norm) cmap = getattr(colorizer, "cmap", cmap) @@ -766,10 +1534,10 @@ def imshow(self, z: Any, cmap: Any = None, **kwargs: Any) -> AxesImage: rgba[..., 3] = np.where(mask, 0.0, rgba[..., 3]) grid, truecolor = rgba, True if not truecolor and has_extremes: - to_rgba = __import__("matplotlib.colors", fromlist=["to_rgba"]).to_rgba - from xy._svg import _lut + from ._colors import _rgba_floats + finite = grid[np.isfinite(grid)] lo = float(vmin) if vmin is not None else float(finite.min()) hi = float(vmax) if vmax is not None else float(finite.max()) @@ -785,7 +1553,9 @@ def imshow(self, z: Any, cmap: Any = None, **kwargs: Any) -> AxesImage: normalized = np.clip(normalized, 0.0, 1.0) else: normalized = np.clip((grid - lo) / ((hi - lo) or 1.0), 0.0, 1.0) - rgb = _lut(resolve_cmap(cmap), normalized.reshape(-1)).reshape(grid.shape + (3,)) + rgb = _lut(resolve_cmap(cmap), np.nan_to_num(normalized, nan=0.0).reshape(-1)).reshape( + grid.shape + (3,) + ) rgba = np.dstack((rgb / 255.0, np.ones(grid.shape, dtype=float))) def extreme(name: str, default: tuple[float, float, float, float]) -> np.ndarray: @@ -794,7 +1564,7 @@ def extreme(name: str, default: tuple[float, float, float, float]) -> np.ndarray return np.asarray(default) if isinstance(value, tuple) and len(value) == 2 and value[1] is None: value = value[0] - return np.asarray(to_rgba(value), dtype=float) + return np.asarray(_rgba_floats(value), dtype=float) rgba[grid < lo] = extreme("under", (0.0, 0.0, 0.0, 1.0)) rgba[grid > hi] = extreme("over", (1.0, 1.0, 1.0, 1.0)) @@ -817,7 +1587,10 @@ def extreme(name: str, default: tuple[float, float, float, float]) -> np.ndarray lo = float(vmin) if vmin is not None else float(finite.min()) hi = float(vmax) if vmax is not None else float(finite.max()) normalized = np.clip((grid - lo) / ((hi - lo) or 1.0), 0.0, 1.0) - rgb = _lut(resolve_cmap(cmap) if cmap else "viridis", normalized.reshape(-1)) + rgb = _lut( + resolve_cmap(cmap if cmap is not None else rcParams["image.cmap"]), + normalized.reshape(-1), + ) grid = np.dstack((rgb.reshape(grid.shape + (3,)) / 255.0, alpha_array)) truecolor = True if ( @@ -856,11 +1629,11 @@ def extreme(name: str, default: tuple[float, float, float, float]) -> np.ndarray if grid.shape[1] == 1: grid = np.repeat(grid, 2, axis=1) entry_kwargs: dict[str, Any] = { - "colormap": resolve_cmap(cmap) if cmap else "viridis", + "colormap": resolve_cmap(cmap if cmap is not None else rcParams["image.cmap"]), "opacity": 1.0, } if alpha is not None and np.isscalar(alpha): - entry_kwargs["opacity"] = float(alpha) + entry_kwargs["opacity"] = float(np.asarray(alpha, dtype=np.float64)) if extent is not None: left, right, bottom, top = map(float, extent) if not np.isfinite([left, right, bottom, top]).all() or left == right or bottom == top: @@ -973,7 +1746,9 @@ def _annotation(self, kind: str, args: tuple, kwargs: dict[str, Any]) -> dict[st span_start, span_end = float(span_start), float(span_end) if not (0.0 <= span_start <= span_end <= 1.0): raise ValueError("annotation fractional bounds must satisfy 0 <= start <= end <= 1") - kwargs.pop("linestyle", kwargs.pop("ls", None)) # rules render solid + linestyle = kwargs.pop("linestyle", kwargs.pop("ls", None)) + if linestyle is not None and linestyle not in LINESTYLE_TO_DASH: + raise ValueError(f"unsupported annotation linestyle: {linestyle!r}") check_unsupported(kwargs, f"ax{kind}()") akw: dict[str, Any] = {} if color is not None: @@ -986,6 +1761,14 @@ def _annotation(self, kind: str, args: tuple, kwargs: dict[str, Any]) -> dict[st akw["text"] = str(label) if span_start != 0.0 or span_end != 1.0: akw["style"] = {"span_start": span_start, "span_end": span_end} + dash = LINESTYLE_TO_DASH.get(linestyle) + if dash not in (None, "none"): + patterns = { + "dashed": [6.0, 4.0], + "dotted": [1.5, 3.0], + "dashdot": [6.0, 3.0, 1.5, 3.0], + } + akw.setdefault("style", {})["dash"] = ",".join(map(str, patterns[dash])) return self._add(f"@{kind}", {"args": args, "kwargs": akw}) def text( @@ -996,11 +1779,11 @@ def text( color = kwargs.pop("color", kwargs.pop("c", None)) fontsize = kwargs.pop("fontsize", kwargs.pop("size", None)) ha = kwargs.pop("ha", kwargs.pop("horizontalalignment", None)) - kwargs.pop("va", kwargs.pop("verticalalignment", None)) + va = kwargs.pop("va", kwargs.pop("verticalalignment", None)) transform = kwargs.pop("transform", None) - kwargs.pop("fontweight", kwargs.pop("weight", None)) - kwargs.pop("fontfamily", kwargs.pop("family", None)) - kwargs.pop("rotation", None) + fontweight = kwargs.pop("fontweight", kwargs.pop("weight", None)) + fontfamily = kwargs.pop("fontfamily", kwargs.pop("family", None)) + rotation = kwargs.pop("rotation", None) check_unsupported(kwargs, "text()") akw = {"color": resolve_color(color)} if color is not None else {} if ha is not None: @@ -1010,6 +1793,14 @@ def text( style: dict[str, Any] = {} if fontsize is not None: style["font_size"] = float(fontsize) + if va is not None: + style["vertical_align"] = str(va) + if fontweight is not None: + style["font_weight"] = str(fontweight) + if fontfamily is not None: + style["font_family"] = str(fontfamily) + if rotation is not None: + style["rotation"] = 90.0 if rotation == "vertical" else float(rotation) if transform is self.transAxes or transform == "axes fraction": style["coordinate_space"] = "axes_fraction" elif transform in {getattr(self.figure, "transFigure", None), "figure fraction"}: @@ -1019,20 +1810,29 @@ def text( return Text(self, self._add("@text", {"args": (x, y, str(s)), "kwargs": akw})) def annotate(self, text: str, xy: tuple, xytext: Optional[tuple] = None, **kwargs: Any) -> Text: - kwargs.pop("arrowprops", None) # rendered as plain callout text + arrowprops = kwargs.pop("arrowprops", None) fontsize = kwargs.pop("fontsize", None) color = kwargs.pop("color", None) xycoords = kwargs.pop("xycoords", "data") textcoords = kwargs.pop("textcoords", None) - kwargs.pop("ha", kwargs.pop("horizontalalignment", None)) - kwargs.pop("va", kwargs.pop("verticalalignment", None)) - kwargs.pop("family", kwargs.pop("fontfamily", None)) - kwargs.pop("weight", kwargs.pop("fontweight", None)) - kwargs.pop("bbox", None) + ha = kwargs.pop("ha", kwargs.pop("horizontalalignment", None)) + va = kwargs.pop("va", kwargs.pop("verticalalignment", None)) + family = kwargs.pop("family", kwargs.pop("fontfamily", None)) + weight = kwargs.pop("weight", kwargs.pop("fontweight", None)) + rotation = kwargs.pop("rotation", None) + bbox = kwargs.pop("bbox", None) check_unsupported(kwargs, "annotate()") akw: dict[str, Any] = {} if color is not None: akw["color"] = resolve_color(color) + if arrowprops is not None: + akw["arrowprops"] = dict(arrowprops) + if bbox is not None: + akw["bbox"] = dict(bbox) + if ha is not None: + akw["anchor"] = {"left": "start", "center": "middle", "right": "end"}.get( + str(ha), "start" + ) if xytext is not None: if textcoords in {"offset points", "offset pixels"}: scale = 4.0 / 3.0 if textcoords == "offset points" else 1.0 @@ -1057,6 +1857,14 @@ def annotate(self, text: str, xy: tuple, xytext: Optional[tuple] = None, **kwarg style["coordinate_space"] = "figure_fraction" if fontsize is not None: style["font_size"] = float(fontsize) + if va is not None: + style["vertical_align"] = str(va) + if weight is not None: + style["font_weight"] = str(weight) + if family is not None: + style["font_family"] = str(family) + if rotation is not None: + style["rotation"] = 90.0 if rotation == "vertical" else float(rotation) if style: akw["style"] = style return Text( @@ -1067,14 +1875,19 @@ def annotate(self, text: str, xy: tuple, xytext: Optional[tuple] = None, **kwarg # -- axis config ----------------------------------------------------------- def set_xlabel(self, label: str, **kwargs: Any) -> None: - self._axis_props("x")["label"] = _plain_text(label) + props = self._axis_props("x") + props["label"] = _plain_text(label) + _apply_axis_label_kwargs(props, kwargs, "set_xlabel()") self._invalidate() def set_ylabel(self, label: str, **kwargs: Any) -> None: - self._axis_props("y")["label"] = _plain_text(label) + props = self._axis_props("y") + props["label"] = _plain_text(label) + _apply_axis_label_kwargs(props, kwargs, "set_ylabel()") self._invalidate() def set_title(self, title: str, **kwargs: Any) -> None: + _consume_text_kwargs(kwargs, "set_title()") host = self._y2_of or self host._title = _plain_text(title) host._invalidate() @@ -1090,18 +1903,38 @@ def set(self, **kwargs: Any) -> "Axes": "yscale": self.set_yscale, "xticks": self.set_xticks, "yticks": self.set_yticks, + "position": self.set_position, + "anchor": self.set_anchor, + "aspect": self.set_aspect, + "facecolor": self.set_facecolor, + "axisbelow": self.set_axisbelow, } xticklabels = kwargs.pop("xticklabels", None) yticklabels = kwargs.pop("yticklabels", None) + projection = kwargs.pop("projection", None) + if projection not in (None, "rectilinear"): + raise not_implemented(f"projection={projection!r} axes", "2-D rectilinear charts") + unknown: list[str] = [] for name, value in kwargs.items(): setter = aliases.get(name) if setter is None: + unknown.append(name) continue setter(value) - if xticklabels is not None: - self._axis_props("x")["tick_labels"] = [str(value) for value in xticklabels] - if yticklabels is not None: - self._axis_props("y")["tick_labels"] = [str(value) for value in yticklabels] + if unknown: + names = ", ".join(sorted(unknown)) + raise AttributeError(f"Axes.set() got unsupported property name(s): {names}") + for axis, labels in (("x", xticklabels), ("y", yticklabels)): + if labels is None: + continue + labels = [str(value) for value in labels] + props = self._axis_props(axis) + if labels: + props["tick_labels"] = labels + else: + # matplotlib: xticklabels=[] hides labels but keeps the ticks. + props.pop("tick_labels", None) + props["tick_label_strategy"] = "none" self._invalidate() return self @@ -1110,13 +1943,24 @@ def set_xlim(self, left: Any = None, right: Any = None) -> None: left, right = left current = self._axis_props("x").get("domain") lo, hi = current if current is not None else self._entry_extent("x") - start, end = float(lo if left is None else left), float(hi if right is None else right) - self._axis_props("x")["domain"] = tuple(sorted((start, end))) + spec = (self._y2_of or self)._scale_specs["x"] + current_original = _scale_values(np.asarray((lo, hi)), spec, inverse=True) + start = float(current_original[0] if left is None else left) + end = float(current_original[1] if right is None else right) + transformed = _scale_values(np.asarray((start, end)), spec) + self._axis_props("x")["domain"] = tuple(sorted(map(float, transformed))) self._axis_props("x")["reverse"] = start > end + self._explicit_domains.add("x") self._invalidate() def get_xlim(self) -> tuple[float, float]: - lo, hi = self._axis_props("x").get("domain", self._entry_extent("x")) + lo, hi = self._axis_props("x").get("domain", self._auto_domain("x")) + lo, hi = map( + float, + _scale_values( + np.asarray((lo, hi)), (self._y2_of or self)._scale_specs["x"], inverse=True + ), + ) return (hi, lo) if self._axis_props("x").get("reverse") else (lo, hi) def set_ylim(self, bottom: Any = None, top: Any = None) -> None: @@ -1124,22 +1968,35 @@ def set_ylim(self, bottom: Any = None, top: Any = None) -> None: bottom, top = bottom current = self._axis_props("y").get("domain") lo, hi = current if current is not None else self._entry_extent("y") - start, end = float(lo if bottom is None else bottom), float(hi if top is None else top) - self._axis_props("y")["domain"] = tuple(sorted((start, end))) + key = "y2" if self._y2_of is not None else "y" + spec = (self._y2_of or self)._scale_specs[key] + current_original = _scale_values(np.asarray((lo, hi)), spec, inverse=True) + start = float(current_original[0] if bottom is None else bottom) + end = float(current_original[1] if top is None else top) + transformed = _scale_values(np.asarray((start, end)), spec) + self._axis_props("y")["domain"] = tuple(sorted(map(float, transformed))) self._axis_props("y")["reverse"] = start > end + self._explicit_domains.add("y") self._invalidate() def get_ylim(self) -> tuple[float, float]: - lo, hi = self._axis_props("y").get("domain", self._entry_extent("y")) + lo, hi = self._axis_props("y").get("domain", self._auto_domain("y")) + key = "y2" if self._y2_of is not None else "y" + lo, hi = map( + float, + _scale_values( + np.asarray((lo, hi)), (self._y2_of or self)._scale_specs[key], inverse=True + ), + ) return (hi, lo) if self._axis_props("y").get("reverse") else (lo, hi) - def get_position(self) -> Any: - Bbox = __import__("matplotlib.transforms", fromlist=["Bbox"]).Bbox - - return Bbox.from_bounds(0.125, 0.11, 0.775, 0.77) + def get_position(self, original: bool = False) -> Bbox: + del original # compat-noop: shim axes have no active/original position split + return Bbox.from_bounds(*(self._figure_rect or (0.125, 0.11, 0.775, 0.77))) def set_position(self, position: Any) -> None: - del position + self._figure_rect = _parse_bounds(position, "set_position()") + self._invalidate() def _entry_extent(self, axis: str) -> tuple[float, float]: values: list[np.ndarray] = [] @@ -1160,6 +2017,14 @@ def _entry_extent(self, axis: str) -> tuple[float, float]: for index in indexes: array = np.asarray(entry["args"][index], dtype=np.float64).reshape(-1) values.append(array[np.isfinite(array)]) + if factory == "contour": + z = np.asarray(entry["args"][0]) + coordinates = entry.get("kwargs", {}).get(key) + if coordinates is None and z.ndim >= 2: + coordinates = np.arange(z.shape[1 if axis == "x" else 0], dtype=float) + if coordinates is not None: + array = np.asarray(coordinates, dtype=np.float64).reshape(-1) + values.append(array[np.isfinite(array)]) elif entry.get("kind") == "heatmap" and entry.get("extent") is not None: bounds = entry["extent"] values.append(np.asarray(bounds[:2] if axis == "x" else bounds[2:], dtype=float)) @@ -1169,6 +2034,15 @@ def _entry_extent(self, axis: str) -> tuple[float, float]: lo, hi = float(np.min(finite)), float(np.max(finite)) return (lo, hi if hi > lo else lo + 1.0) + def _auto_domain(self, axis: str) -> tuple[float, float]: + lo, hi = self._entry_extent(axis) + margin = self._xmargin if axis == "x" else self._ymargin + if margin == 0.0: + return lo, hi + span = hi - lo + pad = span * margin if span > 0 else abs(lo) * margin or margin + return lo - pad, hi + pad + def axis(self, arg: Any = None, **kwargs: Any) -> tuple[float, float, float, float]: del kwargs if isinstance(arg, (tuple, list)) and len(arg) == 4: @@ -1176,17 +2050,282 @@ def axis(self, arg: Any = None, **kwargs: Any) -> tuple[float, float, float, flo self.set_ylim(arg[2], arg[3]) elif arg == "off": self.set_axis_off() - # 'equal', 'scaled', 'tight', and 'off' are accepted layout policies. - x0, x1 = self._axis_props("x").get("domain", self._entry_extent("x")) - y0, y1 = self._axis_props("y").get("domain", self._entry_extent("y")) + elif arg == "on": + self.xaxis.set_visible(True) + self.yaxis.set_visible(True) + elif arg in ("equal", "scaled"): + self._set_aspect_equal_from_current() + elif arg == "tight": + self._set_tight_domains() + elif arg == "auto": + self._aspect_equal = False + self._aspect_bounds = None + self._invalidate() + elif arg is not None: + raise ValueError(f"unsupported axis() argument {arg!r}") + x0, x1 = self.get_xlim() + y0, y1 = self.get_ylim() return float(x0), float(x1), float(y0), float(y1) def set_aspect(self, aspect: Any, **kwargs: Any) -> None: del kwargs self._aspect_equal = aspect in ("equal", 1, 1.0) + if self._aspect_equal: + self._set_aspect_equal_from_current() + else: + self._aspect_bounds = None + self._invalidate() def margins(self, *args: Any, **kwargs: Any) -> None: - del args, kwargs + tight = kwargs.pop("tight", None) + del tight + x = kwargs.pop("x", None) + y = kwargs.pop("y", None) + if kwargs: + raise TypeError(f"margins() got unsupported keyword argument {next(iter(kwargs))!r}") + if len(args) > 2: + raise TypeError("margins() takes at most two positional arguments") + if len(args) == 1: + x = y = args[0] + elif len(args) == 2: + x, y = args + if x is None and y is None: + return + if x is not None: + self._xmargin = _validate_margin(x, "x") + if "x" not in self._explicit_domains: + self._axis_props("x").pop("domain", None) + if y is not None: + self._ymargin = _validate_margin(y, "y") + if "y" not in self._explicit_domains: + self._axis_props("y").pop("domain", None) + self._invalidate() + + def relim(self, visible_only: bool = False) -> None: + del visible_only # compat-noop: invisible entries retain the same data extent + for axis in ("x", "y"): + if axis not in self._explicit_domains: + self._axis_props(axis).pop("domain", None) + self._invalidate() + + def autoscale( + self, enable: bool = True, axis: str = "both", tight: Optional[bool] = None + ) -> None: + if axis not in {"both", "x", "y"}: + raise ValueError("autoscale() axis must be 'both', 'x', or 'y'") + axes = ("x", "y") if axis == "both" else (axis,) + for item in axes: + if enable: + self._explicit_domains.discard(item) + if tight: + self._axis_props(item)["domain"] = self._entry_extent(item) + self._explicit_domains.add(item) + else: + self._axis_props(item).pop("domain", None) + else: + self._axis_props(item)["domain"] = self._auto_domain(item) + self._explicit_domains.add(item) + self._invalidate() + + def autoscale_view( + self, tight: Optional[bool] = None, scalex: bool = True, scaley: bool = True + ) -> None: + if scalex: + self.autoscale(True, axis="x", tight=tight) + if scaley: + self.autoscale(True, axis="y", tight=tight) + + def get_xbound(self) -> tuple[float, float]: + return self.get_xlim() + + def set_xbound(self, lower: Any = None, upper: Any = None) -> None: + if isinstance(lower, (tuple, list)): + lower, upper = lower + current = self.get_xlim() + self.set_xlim( + current[0] if lower is None else lower, current[1] if upper is None else upper + ) + + def get_ybound(self) -> tuple[float, float]: + return self.get_ylim() + + def set_ybound(self, lower: Any = None, upper: Any = None) -> None: + if isinstance(lower, (tuple, list)): + lower, upper = lower + current = self.get_ylim() + self.set_ylim( + current[0] if lower is None else lower, current[1] if upper is None else upper + ) + + def ticklabel_format(self, **kwargs: Any) -> None: + axis = kwargs.pop("axis", "both") + style = kwargs.pop("style", None) + scilimits = kwargs.pop("scilimits", None) + use_offset = kwargs.pop("useOffset", kwargs.pop("useoffset", None)) + use_locale = kwargs.pop("useLocale", None) + use_math_text = kwargs.pop("useMathText", None) + if use_locale not in (None, False): + raise not_implemented("ticklabel_format(useLocale=True)") + if use_math_text not in (None, False): + raise not_implemented("ticklabel_format(useMathText=True)") + if kwargs: + raise TypeError( + f"ticklabel_format() got unsupported keyword argument {next(iter(kwargs))!r}" + ) + if axis not in {"both", "x", "y"}: + raise ValueError("ticklabel_format() axis must be 'both', 'x', or 'y'") + if style not in {None, "plain", "sci", "scientific"}: + raise ValueError("ticklabel_format() style must be 'plain' or 'sci'") + for item in ("x", "y") if axis == "both" else (axis,): + props = self._axis_props(item) + props["tick_label_format"] = { + "style": "sci" if style == "scientific" else style, + "scilimits": None if scilimits is None else tuple(scilimits), + "use_offset": use_offset, + } + self._invalidate() + + def minorticks_on(self) -> None: + self._axis_props("x")["minor_ticks"] = True + self._axis_props("y")["minor_ticks"] = True + self._invalidate() + + def minorticks_off(self) -> None: + self._axis_props("x")["minor_ticks"] = False + self._axis_props("y")["minor_ticks"] = False + self._invalidate() + + def get_xlabel(self) -> str: + return str(self._axis_props("x").get("label", "")) + + def get_ylabel(self) -> str: + return str(self._axis_props("y").get("label", "")) + + def get_title(self) -> str: + return "" if self._title is None else str(self._title) + + def get_xaxis(self) -> _AxisProxy: + return self.xaxis + + def get_yaxis(self) -> _AxisProxy: + return self.yaxis + + def get_figure(self, root: Any = None) -> Any: + del root # compat-noop: no nested subfigures; both roots are self.figure + return self.figure + + def get_lines(self) -> list[Line2D]: + host = self._y2_of or self + return [artist for artist in host._owned_artists if isinstance(artist, Line2D)] + + def get_shared_x_axes(self) -> _SharedAxesGroup: + return _SharedAxesGroup("x") + + def get_shared_y_axes(self) -> _SharedAxesGroup: + return _SharedAxesGroup("y") + + def get_xticklabels(self) -> list[_TickLabel]: + return self._tick_label_handles("x") + + def get_yticklabels(self) -> list[_TickLabel]: + return self._tick_label_handles("y") + + def _tick_label_handles(self, axis: str) -> list[_TickLabel]: + labels = self._axis_props(axis).get("tick_labels") + if labels is None: + labels = [f"{value:g}" for value in self._computed_ticks(axis, False)] + return [_TickLabel(self, axis, str(text)) for text in labels] + + def set_facecolor(self, color: Any) -> None: + resolved = resolve_color(color) + if resolved is not None: + self._theme_tokens["plot_background"] = resolved + self._invalidate() + + def get_facecolor(self) -> Any: + return self._theme_tokens.get("plot_background") + + def set_axisbelow(self, b: Any) -> None: + # The engine composites grid lines beneath data marks unconditionally, + # which is exactly axisbelow=True; other orders are not expressible. + if b is not True: + raise not_implemented( + f"set_axisbelow({b!r})", "the engine's fixed grid-below-marks order" + ) + + def get_legend(self) -> Any: + return self if (self._y2_of or self)._legend else None + + def get_legend_handles_labels(self) -> tuple[list[Artist], list[str]]: + handles: list[Artist] = [] + labels: list[str] = [] + for entry in (self._y2_of or self)._entries: + label = entry.get("kwargs", {}).get("name") + if label and not str(label).startswith("_"): + handles.append(Artist(self, entry)) + labels.append(str(label)) + return handles, labels + + def set_prop_cycle(self, *args: Any, **kwargs: Any) -> None: + if args and kwargs: + raise TypeError("set_prop_cycle() accepts positional or keyword form, not both") + colors = None + if len(args) == 1: + cycle = args[0] + if hasattr(cycle, "by_key"): + colors = cycle.by_key().get("color") + elif isinstance(cycle, dict): + colors = cycle.get("color") + elif len(args) == 2 and args[0] == "color": + colors = args[1] + elif len(args) > 0: + raise NotImplementedError("xy.pyplot set_prop_cycle() only supports color cycles") + elif kwargs: + unsupported = set(kwargs) - {"color"} + if unsupported: + raise NotImplementedError("xy.pyplot set_prop_cycle() only supports color cycles") + colors = kwargs.get("color") + if colors is None: + self._prop_cycle = None + else: + self._prop_cycle = [ + resolved for color in colors if (resolved := resolve_color(color)) is not None + ] + self._cycle = 0 + self._invalidate() + + def secondary_xaxis( + self, location: Any = "top", functions: Any = None, *, transform: Any = None + ) -> SecondaryAxis: + if transform is not None: + raise not_implemented("secondary_xaxis(transform=...)") + made = SecondaryAxis(self, "x", location, functions) + self._secondary_axes.append(made) + self._invalidate() + return made + + def secondary_yaxis( + self, location: Any = "right", functions: Any = None, *, transform: Any = None + ) -> SecondaryAxis: + if transform is not None: + raise not_implemented("secondary_yaxis(transform=...)") + made = SecondaryAxis(self, "y", location, functions) + self._secondary_axes.append(made) + self._invalidate() + return made + + def _set_tight_domains(self) -> None: + self._axis_props("x")["domain"] = self._entry_extent("x") + self._axis_props("y")["domain"] = self._entry_extent("y") + self._explicit_domains.update({"x", "y"}) + self._invalidate() + + def _set_aspect_equal_from_current(self) -> None: + x0, x1 = self._axis_props("x").get("domain", self._auto_domain("x")) + y0, y1 = self._axis_props("y").get("domain", self._auto_domain("y")) + self._aspect_equal = True + self._aspect_bounds = (float(x0), float(x1), float(y0), float(y1)) + self._invalidate() def set_axis_off(self) -> None: self.xaxis.set_visible(False) @@ -1343,7 +2482,58 @@ def add_artist(self, artist: Any) -> Any: aspect="auto", origin="lower", ) - return artist + raise TypeError( + f"unsupported Artist {type(artist).__name__}; use text(), imshow(), " + "add_line(), add_patch(), or add_collection()" + ) + + def add_line(self, line: Any) -> Line2D: + if isinstance(line, Line2D): + if line._axes is not self: + raise ValueError("cannot move a Line2D between Axes") + return line + get_data = getattr(line, "get_data", None) + if get_data is None: + raise TypeError( + f"unsupported line {type(line).__name__}; add_line() requires Line2D-like get_data()" + ) + x, y = get_data() + kwargs: dict[str, Any] = {} + for getter_name, target in ( + ("get_color", "color"), + ("get_label", "label"), + ("get_linewidth", "linewidth"), + ("get_alpha", "alpha"), + ): + getter = getattr(line, getter_name, None) + if getter is not None: + value = getter() + if value is not None: + kwargs[target] = value + return self.plot(x, y, **kwargs)[0] + + def add_container(self, container: Any) -> Any: + from ._artists import BarContainer, ErrorbarContainer, StemContainer + + if not isinstance(container, (BarContainer, ErrorbarContainer, StemContainer)): + raise TypeError( + f"unsupported container {type(container).__name__}; supported containers are " + "BarContainer, ErrorbarContainer, and StemContainer" + ) + self._register_container(container) + return container + + def add_table(self, table: Any) -> Any: + from ._artists import Table + + if not isinstance(table, Table): + raise TypeError( + f"unsupported table {type(table).__name__}; create tables with Axes.table()" + ) + if getattr(table, "_axes", self) is not self: + raise ValueError("cannot move a Table between Axes") + self._register_artist(table) + return table def add_collection(self, collection: Any) -> Artist: if not hasattr(collection, "get_segments"): @@ -1492,17 +2682,85 @@ def add_image(self, image: Any) -> AxesImage: interpolation="nearest", ) - def set_xscale(self, scale: str) -> None: - self._set_scale("x", scale) + def set_xscale(self, scale: str, **kwargs: Any) -> None: + self._set_scale("x", scale, kwargs) - def set_yscale(self, scale: str) -> None: - self._set_scale("y", scale) + def set_yscale(self, scale: str, **kwargs: Any) -> None: + self._set_scale("y", scale, kwargs) - def _set_scale(self, axis: str, scale: str) -> None: + def _set_scale(self, axis: str, scale: str, kwargs: Optional[dict[str, Any]] = None) -> None: + kwargs = {} if kwargs is None else dict(kwargs) if scale not in ("linear", "log", "symlog", "logit", "asinh"): raise ValueError(f"unknown {axis} scale {scale!r}") - if scale not in ("linear", "log"): - raise not_implemented(f"set_{axis}scale({scale!r})") + host = self._y2_of or self + key = "y2" if axis == "y" and self._y2_of is not None else axis + old = host._scale_specs[key] + if scale == "linear" and kwargs: + check_unsupported(kwargs, f"set_{axis}scale('linear')") + if scale == "log": + base = kwargs.pop("base", 10) + subs = kwargs.pop("subs", None) + nonpositive = kwargs.pop("nonpositive", "clip") + check_unsupported(kwargs, f"set_{axis}scale('log')") + if float(base) != 10.0: + raise not_implemented(f"set_{axis}scale('log', base={base!r})") + if subs is not None: + raise not_implemented(f"set_{axis}scale('log', subs=...)") + if nonpositive != "clip": + raise not_implemented(f"set_{axis}scale('log', nonpositive={nonpositive!r})") + new: dict[str, Any] + if scale == "symlog": + new = { + "name": scale, + "base": float(kwargs.pop("base", 10.0)), + "linthresh": float(kwargs.pop("linthresh", 2.0)), + "linscale": float(kwargs.pop("linscale", 1.0)), + } + elif scale == "asinh": + new = {"name": scale, "linear_width": float(kwargs.pop("linear_width", 1.0))} + else: + new = {"name": scale} + check_unsupported(kwargs, f"set_{axis}scale({scale!r})") + if scale == "symlog" and ( + new["base"] <= 1 or new["linthresh"] <= 0 or new["linscale"] <= 0 + ): + raise ValueError(f"set_{axis}scale({scale!r}) parameters must be positive") + if scale == "asinh" and new["linear_width"] <= 0: + raise ValueError(f"set_{axis}scale({scale!r}) parameters must be positive") + for entry in host._entries: + if axis == "y" and entry.get("y_axis", "y") != key: + continue + _transform_entry_axis(entry, axis, old, new) + props = self._axis_props(axis) + if "domain" in props: + props["domain"] = tuple( + map(float, _scale_values(_scale_values(props["domain"], old, inverse=True), new)) + ) + if key in host._auto_scale_axis_ticks: + # ticks generated for the previous scale, not user-set: + # regenerate for the new scale instead of converting them + props.pop("tick_values", None) + props.pop("tick_labels", None) + props.pop("tick_count", None) + host._auto_scale_axis_ticks.discard(key) + if "tick_values" in props: + labels = props.get("tick_labels") or [ + f"{v:g}" for v in _scale_values(props["tick_values"], old, inverse=True) + ] + props["tick_values"] = list( + map( + float, + _scale_values(_scale_values(props["tick_values"], old, inverse=True), new), + ) + ) + props["tick_labels"] = labels + elif scale in {"symlog", "logit", "asinh"}: + ticks = _nonlinear_ticks(self._entry_extent(axis), new) + props["tick_values"] = list(map(float, _scale_values(ticks, new))) + props["tick_labels"] = [f"{tick:g}" for tick in ticks] + props["tick_count"] = max(1, len(ticks)) + host._auto_scale_axis_ticks.add(key) + host._scale_specs[key] = new self._axis_props(axis)["type_"] = "log" if scale == "log" else None self._invalidate() @@ -1517,10 +2775,39 @@ def invert_xaxis(self) -> None: self._invalidate() def tick_params(self, axis: str = "both", **kwargs: Any) -> None: + if axis not in {"both", "x", "y"}: + raise ValueError("tick_params() axis must be 'both', 'x', or 'y'") rotation = kwargs.pop("labelrotation", kwargs.pop("rotation", None)) - if rotation is not None: - for ax in ("x", "y") if axis == "both" else (axis,): - self._axis_props(ax)["tick_label_angle"] = float(rotation) + colors = kwargs.pop("colors", None) + color = kwargs.pop("color", colors) + labelcolor = kwargs.pop("labelcolor", colors) + length = kwargs.pop("length", None) + width = kwargs.pop("width", None) + direction = kwargs.pop("direction", None) + label_visible = _tick_label_visibility(kwargs) + if kwargs: + raise TypeError( + f"tick_params() got unsupported keyword argument {next(iter(kwargs))!r}" + ) + for ax in ("x", "y") if axis == "both" else (axis,): + props = self._axis_props(ax) + if rotation is not None: + props["tick_label_angle"] = float(rotation) + style = props.setdefault("style", {}) + if color is not None: + style["tick_color"] = resolve_color(color) + if labelcolor is not None: + style["tick_label_color"] = resolve_color(labelcolor) + if length is not None: + style["tick_length"] = float(length) + if width is not None: + style["tick_width"] = float(width) + if direction is not None: + if direction not in {"in", "out", "inout"}: + raise ValueError("tick_params() direction must be 'in', 'out', or 'inout'") + style["tick_direction"] = direction + if label_visible is not None: + props["tick_label_strategy"] = None if label_visible else "none" self._invalidate() def set_xticks( @@ -1530,12 +2817,26 @@ def set_xticks( return props = self._axis_props("x") if ticks is not None: - props["tick_values"] = [float(value) for value in ticks] + spec = (self._y2_of or self)._scale_specs["x"] + (self._y2_of or self)._auto_scale_axis_ticks.discard("x") + (self._y2_of or self)._tickers.pop(("x", "major_locator"), None) + props["tick_values"] = list(map(float, _scale_values(ticks, spec))) props["tick_count"] = max(1, len(props["tick_values"])) + if labels is None: + if spec and spec.get("name") != "linear": + # exporters see transformed positions; label the originals + props["tick_labels"] = [ + f"{tick:g}" for tick in np.asarray(ticks, dtype=float).reshape(-1) + ] + else: + props.pop("tick_labels", None) if labels is not None: props["tick_labels"] = [str(value) for value in labels] if len(props["tick_labels"]) != len(props.get("tick_values", [])): raise ValueError("labels must have the same length as ticks") + # matplotlib: explicit labels install a FixedFormatter, displacing + # any user formatter. + (self._y2_of or self)._tickers.pop(("x", "major_formatter"), None) if rotation is not None: props["tick_label_angle"] = float(rotation) self._invalidate() @@ -1547,18 +2848,75 @@ def set_yticks( return props = self._axis_props("y") if ticks is not None: - props["tick_values"] = [float(value) for value in ticks] + key = "y2" if self._y2_of is not None else "y" + spec = (self._y2_of or self)._scale_specs[key] + (self._y2_of or self)._auto_scale_axis_ticks.discard(key) + (self._y2_of or self)._tickers.pop((key, "major_locator"), None) + props["tick_values"] = list(map(float, _scale_values(ticks, spec))) props["tick_count"] = max(1, len(props["tick_values"])) + if labels is None: + if spec and spec.get("name") != "linear": + # exporters see transformed positions; label the originals + props["tick_labels"] = [ + f"{tick:g}" for tick in np.asarray(ticks, dtype=float).reshape(-1) + ] + else: + props.pop("tick_labels", None) if labels is not None: props["tick_labels"] = [str(value) for value in labels] if len(props["tick_labels"]) != len(props.get("tick_values", [])): raise ValueError("labels must have the same length as ticks") + key = "y2" if self._y2_of is not None else "y" + (self._y2_of or self)._tickers.pop((key, "major_formatter"), None) if rotation is not None: props["tick_label_angle"] = float(rotation) self._invalidate() + def get_xticks(self, *, minor: bool = False) -> np.ndarray: + return self._computed_ticks("x", minor) + + def get_yticks(self, *, minor: bool = False) -> np.ndarray: + return self._computed_ticks("y", minor) + + def _computed_ticks(self, axis: str, minor: bool) -> np.ndarray: + props = self._axis_props(axis) + if minor: + return np.asarray(props.get("minor_tick_values", []), dtype=float) + if "tick_values" in props: + key = "y2" if axis == "y" and self._y2_of is not None else axis + return np.asarray( + _scale_values( + props["tick_values"], (self._y2_of or self)._scale_specs[key], inverse=True + ), + dtype=float, + ) + # Auto-ticked axes report the same nice locations the exporters draw. + from xy._svg import _linear_ticks, _log_ticks + + lo, hi = sorted(self.get_xlim() if axis == "x" else self.get_ylim()) + if not (np.isfinite(lo) and np.isfinite(hi)) or lo == hi: + return np.asarray([], dtype=float) + host = self._y2_of or self + key = "y2" if (axis == "y" and self._y2_of is not None) else axis + locator = host._tickers.get((key, "major_locator")) + if locator is not None: + ticks = np.asarray(locator.tick_values(lo, hi), dtype=float).reshape(-1) + pad = (hi - lo) * 1e-9 + return ticks[(ticks >= lo - pad) & (ticks <= hi + pad)] + if props.get("type_") == "log": + return np.asarray(_log_ticks(float(lo), float(hi))[0], dtype=float) + return np.asarray(_linear_ticks(float(lo), float(hi))[0], dtype=float) + def set_anchor(self, anchor: Any) -> None: - del anchor + if anchor is False: + self._anchor = None + self._invalidate() + return + normalized = str(anchor).upper() + if normalized not in {"C", "SW", "S", "SE", "E", "NE", "N", "NW", "W"}: + raise ValueError(f"unsupported anchor mode {anchor!r}") + self._anchor = normalized + self._invalidate() def locator_params(self, axis: str = "both", nbins: Any = None, **kwargs: Any) -> None: del kwargs @@ -1592,6 +2950,16 @@ def twinx(self) -> "Axes": self._twin = Axes(self.figure, y2_of=self) return self._twin + def twiny(self) -> "Axes": + if self.figure is None: + raise ValueError("twiny() requires an Axes attached to a Figure") + twin = Axes(self.figure) + twin._axis["y"] = self._axis_props("y") + self.figure._axes.append(twin) + self.figure._current_ax = twin + self.figure._invalidate() + return twin + def legend(self, *args: Any, **kwargs: Any) -> None: host = self._y2_of or self if len(args) >= 2: @@ -1612,14 +2980,116 @@ def legend(self, *args: Any, **kwargs: Any) -> None: }, ) host._legend = True - loc = kwargs.pop("loc", None) + loc = kwargs.pop("loc", rcParams["legend.loc"]) ncols = kwargs.pop("ncols", kwargs.pop("ncol", 1)) - host._legend_options = {"loc": loc, "ncols": max(1, int(ncols))} + title = kwargs.pop("title", None) + fontsize = kwargs.pop("fontsize", None) + prop = kwargs.pop("prop", None) + if prop is not None: + if not isinstance(prop, dict): + raise not_implemented("legend(prop=FontProperties)", "prop={'size': ...}") + prop = dict(prop) + size = prop.pop("size", None) + if fontsize is None: + fontsize = size + if prop: + raise not_implemented( + f"legend(prop={{{sorted(prop)[0]!r}: ...}})", "prop={'size': ...}" + ) + if fontsize is None: + fontsize = rcParams["legend.fontsize"] + labelcolor = kwargs.pop("labelcolor", None) + frameon = kwargs.pop("frameon", rcParams["legend.frameon"]) + facecolor = kwargs.pop("facecolor", rcParams["legend.facecolor"]) + edgecolor = kwargs.pop("edgecolor", rcParams["legend.edgecolor"]) + # These affect legend geometry which the current component does not + # expose. Reject them instead of accepting and silently discarding. + layout_options = { + key: kwargs.pop(key) + for key in ( + "title_fontsize", + "borderpad", + "labelspacing", + "handlelength", + "handletextpad", + ) + if key in kwargs + } + if layout_options: + raise not_implemented( + f"legend({sorted(layout_options)[0]}=...)", + "loc, ncols, title, fontsize, colors, and frame styling", + ) + # The engine legend draws one swatch per entry, which is exactly the + # matplotlib default; only the default values are expressible. + for key, default in (("numpoints", 1), ("scatterpoints", 1)): + if key in kwargs and int(kwargs.pop(key)) != default: + raise not_implemented(f"legend({key}=...)", f"the matplotlib default ({default})") + unsupported = set(kwargs) + if unsupported: + raise TypeError(f"legend() got unsupported keyword argument {sorted(unsupported)[0]!r}") + style: dict[str, Any] = {} + if fontsize is not None: + style["fontSize"] = f"{_font_size(fontsize, rcParams['font.size']):g}px" + if labelcolor is not None: + style["color"] = resolve_color(labelcolor) + if frameon is False: + style["background"] = "transparent" + style["borderColor"] = "transparent" + else: + if facecolor == "inherit": + facecolor = rcParams["axes.facecolor"] + if facecolor is not None: + style["background"] = resolve_color(facecolor) + if edgecolor is not None: + style["borderColor"] = resolve_color(edgecolor) + style["borderStyle"] = "solid" + options: dict[str, Any] = {"loc": loc, "ncols": max(1, int(ncols))} + if title is not None: + options["class_name"] = f"legend-title:{_plain_text(title)}" + if style: + options["style"] = style + host._legend_options = options host._invalidate() def grid(self, visible: Any = True, **kwargs: Any) -> None: host = self._y2_of or self + which = kwargs.pop("which", "major") + axis = kwargs.pop("axis", "both") + if which not in {"major", "both"}: + raise ValueError("grid() only supports major grid lines") + if axis not in {"both", "x", "y"}: + raise ValueError("grid() axis must be 'both', 'x', or 'y'") + color = kwargs.pop("color", kwargs.pop("c", None)) + linestyle = kwargs.pop("linestyle", kwargs.pop("ls", None)) + linewidth = kwargs.pop("linewidth", kwargs.pop("lw", None)) + alpha = kwargs.pop("alpha", None) + if kwargs: + raise TypeError(f"grid() got unsupported keyword argument {next(iter(kwargs))!r}") host._grid = bool(visible) if visible is not None else not host._grid + host._grid_axis = axis + style = host._grid_style = {} + if color is not None and (resolved_grid := resolve_color(color)) is not None: + host._grid_color = resolved_grid + if linewidth is not None: + style["grid_width"] = float(linewidth) + if linestyle is not None: + dash = LINESTYLE_TO_DASH.get(linestyle, linestyle) + if dash is not None: # solid is the engine default, not a style key + style["grid_dash"] = dash + if alpha is not None: + style["grid_opacity"] = float(alpha) + grid_color = host._grid_color if host._grid else "transparent" + for item in ("x", "y"): + props = host._axis_props(item) + axis_style = props.setdefault("style", {}) + for stale in ("grid_width", "grid_dash", "grid_opacity"): + axis_style.pop(stale, None) + if axis in {"both", item}: + axis_style["grid_color"] = grid_color + axis_style.update(style) + else: + axis_style["grid_color"] = "transparent" host._invalidate() def _axis_props(self, axis: str) -> dict[str, Any]: @@ -1627,6 +3097,54 @@ def _axis_props(self, axis: str) -> dict[str, Any]: key = "y2" if (axis == "y" and self._y2_of is not None) else axis return host._axis[key] + def _ticker_view(self, key: str, props: dict[str, Any]) -> tuple[float, float]: + """The axis view interval in *data* space, for locator math.""" + axis = "y" if key == "y2" else key + domain = props.get("domain") + if domain is None: + owner = self._twin if (key == "y2" and self._twin is not None) else self + domain = owner._auto_domain(axis) + lo, hi = sorted(map(float, domain)) + spec = self._scale_specs.get(key) or {"name": "linear"} + if spec.get("name") != "linear": + lo, hi = sorted(map(float, _scale_values(np.asarray([lo, hi]), spec, inverse=True))) + return lo, hi + + def _apply_tickers(self, key: str, props: dict[str, Any]) -> None: + """Resolve a user locator/formatter into concrete tick props (in place).""" + locator = self._tickers.get((key, "major_locator")) + formatter = self._tickers.get((key, "major_formatter")) + if locator is None and formatter is None: + return + spec = self._scale_specs.get(key) or {"name": "linear"} + lo, hi = self._ticker_view(key, props) + if locator is not None: + ticks = np.asarray(locator.tick_values(lo, hi), dtype=float).reshape(-1) + pad = (hi - lo) * 1e-9 + ticks = ticks[(ticks >= lo - pad) & (ticks <= hi + pad)] + elif "tick_values" in props: + ticks = np.asarray( + _scale_values(props["tick_values"], spec, inverse=True), dtype=float + ).reshape(-1) + else: + from ._ticker import LogLocator + + auto = LogLocator() if props.get("type_") == "log" else AutoLocator() + ticks = np.asarray(auto.tick_values(lo, hi), dtype=float).reshape(-1) + props["tick_values"] = list(map(float, _scale_values(ticks, spec))) + if formatter is not None: + props["tick_labels"] = [ + formatter(float(value), position) for position, value in enumerate(ticks) + ] + elif spec.get("name") != "linear": + props["tick_labels"] = [f"{value:g}" for value in ticks] + else: + props.pop("tick_labels", None) + if len(ticks): + props["tick_count"] = len(ticks) + else: + props.pop("tick_count", None) + # -- materialization ----------------------------------------------------------- def _chart_children(self) -> list[Any]: @@ -1638,6 +3156,10 @@ def _chart_children(self) -> list[Any]: if kind == "line": children.append(fc.line(x=e["x"], y=e["y"], **kw, **axis_kw)) elif kind == "scatter": + kw = dict(kw) + domain = kw.pop("domain", None) # vmin/vmax → the color channel window + if domain is not None: + kw["color_domain"] = (float(domain[0]), float(domain[1])) children.append(fc.scatter(x=e["x"], y=e["y"], **kw, **axis_kw)) elif kind == "bar": children.append(fc.bar(x=e["x"], y=e["y"], **kw, **axis_kw)) @@ -1658,7 +3180,17 @@ def _chart_children(self) -> list[Any]: elif kind == "@y_band": children.append(fc.y_band(*e["args"], **kw)) elif kind == "@text": - children.append(fc.text(*e["args"], **kw)) + opacity = kw.get("opacity") + if opacity is not None and float(opacity) == 0.0: + continue # set_visible(False) must hide text in every exporter + text_kw = { + key: value + for key, value in kw.items() + if key in {"dx", "dy", "color", "anchor", "class_name", "style"} + } + if opacity is not None and float(opacity) < 1.0: + text_kw["style"] = {**(text_kw.get("style") or {}), "opacity": float(opacity)} + children.append(fc.text(*e["args"], **text_kw)) return children def _build_chart(self, width: int, height: int) -> Any: @@ -1670,6 +3202,7 @@ def _build_chart(self, width: int, height: int) -> Any: children = self._chart_children() if self._twin is not None: children.extend(self._twin._chart_children()) + adjusted_aspect = False if self._aspect_equal and self._aspect_bounds is not None: x0, x1, y0, y1 = self._aspect_bounds x0, x1 = self._axis["x"].get("domain", (x0, x1)) @@ -1690,23 +3223,52 @@ def _build_chart(self, width: int, height: int) -> Any: y0, y1 = center - target * 0.5, center + target * 0.5 self._axis["x"]["domain"] = (x0, x1) self._axis["y"]["domain"] = (y0, y1) + adjusted_aspect = True + if not adjusted_aspect and self._xmargin != 0.0 and "x" not in self._explicit_domains: + self._axis["x"]["domain"] = self._auto_domain("x") + if not adjusted_aspect and self._ymargin != 0.0 and "y" not in self._explicit_domains: + self._axis["y"]["domain"] = self._auto_domain("y") x_props = {k: v for k, v in self._axis["x"].items() if v is not None} y_props = {k: v for k, v in self._axis["y"].items() if v is not None} + self._apply_tickers("x", x_props) + self._apply_tickers("y", y_props) children.append(_cached_axis("x", x_props)) children.append(_cached_axis("y", y_props)) + for index, secondary in enumerate(self._secondary_axes, 1): + children.append(secondary._component(index)) if self._twin is not None: y2_props = {k: v for k, v in self._axis["y2"].items() if v is not None} + self._apply_tickers("y2", y2_props) children.append(fc.y_axis(id="y2", side="right", **y2_props)) if self._legend: children.append(fc.legend(**self._legend_options)) + hidden = self._hidden_spines & {"left", "bottom"} + theme_tokens = self._theme_tokens + if hidden == {"left", "bottom"}: + theme_tokens = dict(theme_tokens) + theme_tokens["axis_color"] = "transparent" + elif hidden: + raise not_implemented( + f"hiding only the {next(iter(hidden))} spine", "hiding both left and bottom" + ) if _MPL_THEME_TOKENS: - children.append(_cached_theme(self._grid)) + if self._grid_axis != "both": + tokens = dict(theme_tokens) + tokens["grid_color"] = "transparent" + children.append(fc.theme(style=self._theme_style, **tokens)) # ty: ignore[invalid-argument-type] + elif self._grid_color == _MPL_GRID_COLOR: + children.append(_cached_theme(self._grid, theme_tokens, self._theme_style)) + else: + tokens = dict(theme_tokens) + tokens["grid_color"] = self._grid_color if self._grid else "transparent" + children.append(fc.theme(style=self._theme_style, **tokens)) # ty: ignore[invalid-argument-type] self._chart = fc.chart( *children, title=self._title, width=width, height=height, padding=self._padding, + styles=self._chrome_styles, ) if self._colorbar is not None: self._chart.figure().colorbar_options = dict(self._colorbar) @@ -1717,6 +3279,145 @@ def _is_number(v: Any) -> bool: return isinstance(v, (int, float, np.integer, np.floating)) +def _font_size(value: Any, base: Any) -> float: + relative = { + "xx-small": 0.6, + "x-small": 0.75, + "small": 0.85, + "medium": 1.0, + "large": 1.2, + "x-large": 1.45, + "xx-large": 1.75, + } + if isinstance(value, str): + if value not in relative: + raise ValueError(f"unsupported relative font size {value!r}") + return float(base) * relative[value] + result = float(value) + if result <= 0: + raise ValueError("font size must be positive") + return result + + +def _rc_axis_style(axis: str) -> dict[str, Any]: + prefix = "xtick" if axis == "x" else "ytick" + tick_color = rcParams[f"{prefix}.color"] + label_color = rcParams[f"{prefix}.labelcolor"] + result: dict[str, Any] = {} + if tick_color != "black": + result["tick_color"] = resolve_color(tick_color) + if label_color != "inherit" or tick_color != "black": + result["tick_label_color"] = resolve_color( + tick_color if label_color == "inherit" else label_color + ) + if rcParams[f"{prefix}.labelsize"] != "medium": + result["tick_label_size"] = _font_size( + rcParams[f"{prefix}.labelsize"], rcParams["font.size"] + ) + if rcParams["axes.labelcolor"] != "black": + result["label_color"] = resolve_color(rcParams["axes.labelcolor"]) + if rcParams["axes.labelsize"] != "medium": + result["label_size"] = _font_size(rcParams["axes.labelsize"], rcParams["font.size"]) + return result + + +def _parse_bounds(value: Any, context: str) -> tuple[float, float, float, float]: + bounds = getattr(value, "bounds", value) + parsed = tuple(float(part) for part in bounds) + if len(parsed) != 4: + raise ValueError(f"{context} expects [left, bottom, width, height]") + left, bottom, width, height = parsed + if width < 0 or height < 0: + raise ValueError(f"{context} width and height must be non-negative") + return left, bottom, width, height + + +def _convert_timedelta_axis(values: np.ndarray) -> np.ndarray: + """Map timedelta coordinates to seconds; dates and categories stay native.""" + array = np.asanyarray(values) + if np.issubdtype(array.dtype, np.timedelta64): + return array.astype("timedelta64[ns]").astype(np.float64) / 1_000_000_000.0 + if array.dtype == object and array.size: + flat = array.reshape(-1) + if all(isinstance(value, timedelta) for value in flat): + return np.asarray([value.total_seconds() for value in flat], dtype=np.float64).reshape( + array.shape + ) + # pandas Periods (its dynamic date-plotting unit) → timestamps, the + # engine's native time axis. + if all(hasattr(value, "to_timestamp") for value in flat): + return np.asarray([np.datetime64(value.to_timestamp()) for value in flat]).reshape( + array.shape + ) + return values + + +def _validate_margin(value: Any, axis: str) -> float: + margin = float(value) + if not np.isfinite(margin) or margin < 0: + raise ValueError(f"{axis} margin must be a finite non-negative number") + return margin + + +def _apply_axis_label_kwargs(props: dict[str, Any], kwargs: dict[str, Any], context: str) -> None: + labelpad = kwargs.pop("labelpad", None) + loc = kwargs.pop("loc", None) + _consume_text_kwargs(kwargs, context) + if labelpad is not None: + props["label_offset"] = float(labelpad) + if loc is not None: + positions = { + "left": "start", + "bottom": "start", + "center": "center", + "right": "end", + "top": "end", + } + if loc not in positions: + raise ValueError(f"{context} loc must be one of {sorted(positions)}") + props["label_position"] = positions[loc] + + +def _consume_text_kwargs(kwargs: dict[str, Any], context: str) -> None: + # Accepted for Matplotlib-flavoured scripts. The native engine currently + # inherits font styling from the chart theme, so these kwargs are validated + # and retained as compatibility inputs rather than silently acting on data. + for key in ( + "fontsize", + "size", + "fontdict", + "fontweight", + "weight", + "fontstyle", + "style", + "fontfamily", + "family", + "color", + "horizontalalignment", + "ha", + "verticalalignment", + "va", + "rotation", + "pad", + "y", + "x", + "transform", + ): + kwargs.pop(key, None) + if kwargs: + raise TypeError(f"{context} got unsupported keyword argument {next(iter(kwargs))!r}") + + +def _tick_label_visibility(kwargs: dict[str, Any]) -> Optional[bool]: + values = [] + for key in ("labelbottom", "labeltop", "labelleft", "labelright"): + if key in kwargs: + values.append(bool(kwargs.pop(key))) + if not values: + return None + return any(values) + + def _marker_symbol(marker: Any) -> str: try: return MARKER_TO_SYMBOL.get(marker, "circle") diff --git a/python/xy/pyplot/_colors.py b/python/xy/pyplot/_colors.py index dc20e737..5a4fb221 100644 --- a/python/xy/pyplot/_colors.py +++ b/python/xy/pyplot/_colors.py @@ -8,6 +8,7 @@ from __future__ import annotations +import re from typing import Optional import numpy as np @@ -75,6 +76,8 @@ "purples": "purples", "pubu": "pubu", "prgn": "prgn", + "rdgy": "rdgy", + "jet": "jet", "binary": "binary", } @@ -85,6 +88,9 @@ class Cmap: def __init__(self, name: str) -> None: self.name = resolve_cmap(name) self.N = 256 + self._bad: object = "transparent" + self._under: object | None = None + self._over: object | None = None def resampled(self, lutsize: int) -> "Cmap": result = Cmap(self.name) @@ -96,34 +102,181 @@ def with_extremes(self, **kwargs: object) -> "Cmap": result.N = self.N for key in ("bad", "under", "over"): if key in kwargs: - setattr(result, f"_{key}", kwargs[key]) + getattr(result, f"set_{key}")(kwargs[key]) return result def set_bad(self, color: object = "transparent", alpha: object = None) -> None: - self._bad = (color, alpha) + self._bad = color if alpha is None else (color, alpha) def set_under(self, color: object = "transparent", alpha: object = None) -> None: - self._under = (color, alpha) + self._under = color if alpha is None else (color, alpha) def set_over(self, color: object = "transparent", alpha: object = None) -> None: - self._over = (color, alpha) + self._over = color if alpha is None else (color, alpha) def __call__(self, values: object) -> object: from xy._svg import _lut + source = np.asarray(values) array = np.asarray(values, dtype=np.float64) normalized = array - if np.issubdtype(array.dtype, np.integer) or ( - np.isfinite(array).any() and np.nanmax(np.abs(array)) > 1.0 - ): + if np.issubdtype(source.dtype, np.integer): normalized = array / max(1, self.N - 1) - flat = _lut(self.name, normalized.reshape(-1)) / 255.0 + flat_values = normalized.reshape(-1) + flat = _lut(self.name, np.clip(np.nan_to_num(flat_values, nan=0.0), 0.0, 1.0)) / 255.0 rgba = np.column_stack((flat, np.ones(len(flat), dtype=np.float64))).reshape( array.shape + (4,) ) + flat_rgba = rgba.reshape(-1, 4) + for mask, extreme in ( + (np.isnan(flat_values), self._bad), + (flat_values < 0.0, self._under), + (flat_values > 1.0, self._over), + ): + if extreme is not None and np.any(mask): + flat_rgba[mask] = _rgba_floats(extreme) + return tuple(rgba.tolist()) if array.ndim == 0 else rgba + + +def _user_color_table(colors: object) -> np.ndarray: + """(M, 3|4) rows of 0-1 floats (or resolvable color strings) → (M, 4) RGBA.""" + if isinstance(colors, (list, tuple)) and colors and all(isinstance(c, str) for c in colors): + rows = [] + for spec in colors: + resolved = resolve_color(spec) or "" + if not re.fullmatch(r"#[0-9a-fA-F]{6}", resolved): + raise ValueError( + f"unsupported colormap color {spec!r}; use hex strings or 0-1 RGB(A) rows" + ) + rows.append([int(resolved[i : i + 2], 16) / 255.0 for i in (1, 3, 5)] + [1.0]) + return np.asarray(rows, dtype=np.float64) + table = np.asarray(colors, dtype=np.float64) + if table.ndim != 2 or table.shape[1] not in (3, 4) or not len(table): + raise ValueError("colormap colors must be an (N, 3) or (N, 4) array of 0-1 floats") + if table.shape[1] == 3: + table = np.column_stack((table, np.ones(len(table), dtype=np.float64))) + return np.clip(table, 0.0, 1.0) + + +class ListedColormap: + """matplotlib.colors.ListedColormap: a user-supplied discrete color table. + + The engine renders *named* colormaps only, so user-built colormaps serve + the Python-side idiom — ``cmap(np.arange(cmap.N))`` — whose RGBA output + feeds image/scatter calls directly. Passing one as ``cmap=`` still fails + loudly in ``resolve_cmap`` (there is no engine table to render). + """ + + def __init__(self, colors: object, name: str = "listed", N: int | None = None) -> None: + table = _user_color_table(colors) + if N is not None: + count = max(1, int(N)) + table = np.tile(table, (-(-count // len(table)), 1))[:count] + self.colors = table + self.name = str(name) + self.N = len(table) + + def __call__(self, values: object) -> object: + array = np.asarray(values) + if np.issubdtype(array.dtype, np.integer): + index = np.clip(array, 0, self.N - 1) + else: + scaled = np.clip(np.nan_to_num(np.asarray(array, np.float64), nan=0.0), 0.0, 1.0) + index = np.minimum((scaled * self.N).astype(int), self.N - 1) + rgba = self.colors[index] + return tuple(rgba.tolist()) if array.ndim == 0 else rgba + + +class LinearSegmentedColormap: + """matplotlib's from_list surface: anchors interpolated Python-side. + + Same engine boundary as ListedColormap — callable for RGBA tables, not + renderable by name. + """ + + def __init__(self, name: str, anchors: np.ndarray, N: int = 256) -> None: + self.name = str(name) + self._anchors = anchors + self.N = max(1, int(N)) + + @classmethod + def from_list( + cls, name: str, colors: object, N: int = 256, **kwargs: object + ) -> "LinearSegmentedColormap": + if kwargs: + raise TypeError(f"from_list() got unsupported keyword argument {next(iter(kwargs))!r}") + return cls(name, _user_color_table(colors), N) + + def resampled(self, lutsize: int) -> "LinearSegmentedColormap": + return LinearSegmentedColormap(self.name, self._anchors, lutsize) + + def __call__(self, values: object) -> object: + array = np.asarray(values) + normalized = np.asarray(array, dtype=np.float64) + if np.issubdtype(array.dtype, np.integer): + normalized = normalized / max(1, self.N - 1) + clipped = np.clip(np.nan_to_num(normalized, nan=0.0), 0.0, 1.0) + position = clipped * (len(self._anchors) - 1) + low = np.floor(position).astype(int) + high = np.minimum(low + 1, len(self._anchors) - 1) + t = (position - low)[..., None] + rgba = self._anchors[low] * (1.0 - t) + self._anchors[high] * t return tuple(rgba.tolist()) if array.ndim == 0 else rgba +def _rgba_floats(value: object) -> tuple[float, float, float, float]: + """Resolve the bounded color forms used by colormap extremes.""" + alpha: object = None + color = value + if isinstance(value, (tuple, list)) and _is_color_alpha_pair(value): + color, alpha = value[0], value[1] + resolved = resolve_color(color) + if resolved is None or resolved == "transparent": + result = (0.0, 0.0, 0.0, 0.0) + elif resolved.startswith("#") and len(resolved) in (7, 9): + channels = [ + int(resolved[index : index + 2], 16) / 255.0 for index in range(1, len(resolved), 2) + ] + result = (channels[0], channels[1], channels[2], channels[3] if len(channels) == 4 else 1.0) + elif resolved.startswith("rgb("): + channels = [float(part) for part in resolved[4:-1].split(",")] + result = (channels[0] / 255.0, channels[1] / 255.0, channels[2] / 255.0, 1.0) + elif resolved.startswith("rgba("): + channels = [float(part) for part in resolved[5:-1].split(",")] + result = (channels[0] / 255.0, channels[1] / 255.0, channels[2] / 255.0, channels[3]) + else: + named = { + "black": (0.0, 0.0, 0.0, 1.0), + "white": (1.0, 1.0, 1.0, 1.0), + "red": (1.0, 0.0, 0.0, 1.0), + "green": (0.0, 0.5, 0.0, 1.0), + "blue": (0.0, 0.0, 1.0, 1.0), + "yellow": (1.0, 1.0, 0.0, 1.0), + "cyan": (0.0, 1.0, 1.0, 1.0), + "magenta": (1.0, 0.0, 1.0, 1.0), + } + if resolved.lower() not in named: + raise ValueError( + f"colormap extremes require a CSS hex/rgb or basic named color, got {color!r}" + ) + result = named[resolved.lower()] + if isinstance(alpha, (int, float)): + result = (result[0], result[1], result[2], float(alpha)) + return result + + +def _is_color_alpha_pair(value: object) -> bool: + """(color, alpha) where color is a str or RGB(A) sequence — never a bare 2-tuple.""" + if not (isinstance(value, (tuple, list)) and len(value) == 2): + return False + color, alpha = value + if alpha is not None and not isinstance(alpha, (int, float)): + return False + return isinstance(color, str) or ( + isinstance(color, (tuple, list, np.ndarray)) and len(color) in (3, 4) + ) + + def resolve_color(value: object) -> Optional[str]: """A matplotlib color spec → CSS color string (None passes through).""" if value is None: @@ -166,4 +319,4 @@ def resolve_cmap(name: object) -> str: return CMAPS[key] if key.endswith("_r") and key[:-2] in CMAPS: return f"{CMAPS[key[:-2]]}_r" - return "viridis" + raise ValueError(f"unsupported colormap: {text!r}") diff --git a/python/xy/pyplot/_grid.py b/python/xy/pyplot/_grid.py index 35710e85..a8f4cdae 100644 --- a/python/xy/pyplot/_grid.py +++ b/python/xy/pyplot/_grid.py @@ -22,7 +22,13 @@ import numpy as np -def compose_html(charts: list[Any], nrows: int, ncols: int, suptitle: Optional[str]) -> str: +def compose_html( + charts: list[Any], + nrows: int, + ncols: int, + suptitle: Optional[str], + suptitle_style: Optional[dict[str, Any]] = None, +) -> str: panels = [] for chart in charts: doc = chart.to_html() @@ -35,7 +41,17 @@ def compose_html(charts: list[Any], nrows: int, ncols: int, suptitle: Optional[s f'style="width:{width}px;height:{height}px" ' f'srcdoc="{_html.escape(doc, quote=True)}">' ) - title_html = f"

{_html.escape(suptitle)}

" if suptitle else "" + style = suptitle_style or {} + title_css = ( + f"font-size:{float(style.get('size', 16)):g}px;font-weight:{_html.escape(str(style.get('weight', 'normal')))};" + f"font-family:{_html.escape(str(style.get('family', 'system-ui, sans-serif')))};" + f"color:{_html.escape(str(style.get('color', '#262626')))}" + ) + title_html = ( + f"

{_html.escape(suptitle)}

" + if suptitle + else "" + ) grid = "\n".join(panels) return f""" @@ -114,7 +130,13 @@ def compose_html(charts: list[Any], nrows: int, ncols: int, suptitle: Optional[s """ -def compose_svg(charts: list[Any], nrows: int, ncols: int, suptitle: Optional[str]) -> str: +def compose_svg( + charts: list[Any], + nrows: int, + ncols: int, + suptitle: Optional[str], + suptitle_style: Optional[dict[str, Any]] = None, +) -> str: """Compose subplot SVGs with isolated ids into one portable SVG document.""" from xy import _svg @@ -143,13 +165,20 @@ def compose_svg(charts: list[Any], nrows: int, ncols: int, suptitle: Optional[st f'width="{int(figure.width)}" height="{int(figure.height)}" ' f'viewBox="0 0 {int(figure.width)} {int(figure.height)}">{inner}' ) + style = suptitle_style or {} + anchor = {"left": "start", "center": "middle", "right": "end"}.get( + str(style.get("ha", "center")), "middle" + ) + width, height = sum(col_widths), title_h + sum(row_heights) + size = float(style.get("size", 16)) + # y is a figure fraction measured from the bottom, like matplotlib. + baseline = min(height - 2.0, (1.0 - float(style.get("y", 0.98))) * height + 0.75 * size) title = ( - f'{_html.escape(suptitle)}' + f'{_html.escape(suptitle)}' if suptitle else "" ) - width, height = sum(col_widths), title_h + sum(row_heights) return ( f'{title}{"".join(body)}' @@ -163,9 +192,12 @@ def stitch_png( suptitle: Optional[str], colorbar: Optional[dict[str, Any]] = None, *, + suptitle_style: Optional[dict[str, Any]] = None, positions: Optional[list[tuple[float, float, float, float]]] = None, canvas_size: Optional[tuple[int, int]] = None, facecolor: str = "white", + bbox_tight: bool = False, + pad_pixels: int = 0, ) -> bytes: from xy import _png, _raster # sanctioned escape hatch (see module doc) @@ -174,6 +206,7 @@ def stitch_png( for chart in charts: fig = chart.figure() spec, blob, borrowed = fig._build_raster_payload(px_width=max(256, int(fig.width))) + spec["canvas_background"] = facecolor img = _raster.render_raster(spec, blob, scale, borrowed=borrowed) if isinstance(img, bytes): raise RuntimeError("pyplot grid rasterizer unexpectedly returned encoded PNG bytes") @@ -212,9 +245,9 @@ def stitch_png( ] title_h = 48 if suptitle else 0 colorbar_h = 52 if colorbar else 0 - canvas = np.full( - (title_h + sum(row_heights) + colorbar_h, sum(col_widths), 4), 255, dtype=np.uint8 - ) + background = np.asarray(_raster._parse_color(facecolor), dtype=np.uint8) + canvas = np.empty((title_h + sum(row_heights) + colorbar_h, sum(col_widths), 4), dtype=np.uint8) + canvas[...] = background for i, tile in enumerate(tiles): r, c = divmod(i, ncols) y = title_h + sum(row_heights[:r]) @@ -224,7 +257,15 @@ def stitch_png( from xy import kernels cmd = _raster._Cmd(scale) - cmd.text(canvas.shape[1] / (2 * scale), 17, 1, 14, (38, 38, 38, 255), suptitle) + style = suptitle_style or {} + cmd.text( + canvas.shape[1] * float(style.get("x", 0.5)) / scale, + 17, + 1, + float(style.get("size", 14)), + _raster._parse_color(str(style.get("color", "#262626"))), + suptitle, + ) overlay = kernels.rasterize(bytes(cmd.buf), canvas.shape[1], title_h) alpha = overlay[:, :, 3:4].astype(np.float64) / 255.0 canvas[:title_h, :, :3] = np.round( @@ -238,4 +279,16 @@ def stitch_png( gradient = _lut(colorbar.get("colormap", "viridis"), np.linspace(0.0, 1.0, max(2, x1 - x0))) canvas[y0 : y0 + 16, x0:x1, :3] = gradient[None, :, :] canvas[y0 : y0 + 16, x0:x1, 3] = 255 + if bbox_tight: + # Crop the figure-colored margin, retaining a Matplotlib-like pad. Do + # this on the composed RGBA buffer so it works for subplot grids and + # absolute axes without asking each renderer for a separate bbox. + delta = np.any(canvas != background, axis=2) + ys, xs = np.nonzero(delta) + if len(xs): + x0 = max(0, int(xs.min()) - pad_pixels) + x1 = min(canvas.shape[1], int(xs.max()) + pad_pixels + 1) + y0 = max(0, int(ys.min()) - pad_pixels) + y1 = min(canvas.shape[0], int(ys.max()) + pad_pixels + 1) + canvas = canvas[y0:y1, x0:x1] return _png.encode(canvas) diff --git a/python/xy/pyplot/_mplfig.py b/python/xy/pyplot/_mplfig.py index a732496c..20f0f11a 100644 --- a/python/xy/pyplot/_mplfig.py +++ b/python/xy/pyplot/_mplfig.py @@ -14,9 +14,39 @@ import numpy as np +from ._artists import Text from ._axes import Axes from ._rc import rc_figsize_px -from ._translate import not_implemented +from ._transforms import CoordinateTransform +from ._translate import check_unsupported, not_implemented + + +def _png_with_metadata(data: bytes, metadata: dict[Any, Any]) -> bytes: + """Insert standards-compliant PNG text chunks before IEND.""" + from xy import _png + + chunks = [] + for raw_key, raw_value in metadata.items(): + key = str(raw_key) + value = str(raw_value) + try: + encoded_key = key.encode("latin-1", "strict") + except UnicodeEncodeError: + encoded_key = b"" + if not encoded_key or len(encoded_key) > 79 or "\x00" in key: + raise ValueError("PNG metadata keys must be 1-79 Latin-1 characters") + try: + payload = key.encode("latin-1") + b"\0" + value.encode("latin-1") + chunks.append(_png._chunk(b"tEXt", payload)) + except UnicodeEncodeError: + # iTXt: keyword, compression flag/method, language, translated + # keyword, then UTF-8 text. + payload = key.encode("latin-1") + b"\0\0\0\0\0" + value.encode("utf-8") + chunks.append(_png._chunk(b"iTXt", payload)) + marker = data.rfind(b"\x00\x00\x00\x00IEND") + if marker < 0: + raise ValueError("invalid PNG output") + return data[:marker] + b"".join(chunks) + data[marker:] class Figure: @@ -31,52 +61,140 @@ def __init__( self._figsize = figsize self._dpi = dpi self._facecolor = facecolor or "white" + self._edgecolor = "white" self._suptitle: Optional[str] = None + self._suptitle_style: dict[str, Any] = {} + self._supxlabel: Optional[str] = None + self._supylabel: Optional[str] = None self._nrows = 1 self._ncols = 1 self._axes: list[Axes] = [] self._current_ax: Optional[Axes] = None self._html_cache: Optional[str] = None - self.transFigure = "figure fraction" + self.transFigure = CoordinateTransform("figure_fraction") self._sharex = False self._sharey = False self._link_group = f"xy-pyplot-{uuid.uuid4().hex[:8]}" self._shared_colorbar: Optional[dict[str, Any]] = None self._width_ratios: Optional[tuple[float, ...]] = None self._height_ratios: Optional[tuple[float, ...]] = None + self._layout_options: dict[str, Any] = {} + self._subplot_adjust: dict[str, float] = {} + self._label = "" + self._gci: Any = None # last color-mapped artist, for plt.colorbar()/clim() # -- layout -------------------------------------------------------------- def _invalidate(self) -> None: self._html_cache = None - def add_subplot(self, *args: Any) -> Axes: - if args and args != (1, 1, 1) and args != (111,): + @property + def canvas(self) -> "_FigureCanvas": + return _FigureCanvas(self) + + def add_subplot(self, *args: Any, **kwargs: Any) -> Axes: + if len(args) == 1 and isinstance(args[0], _SubplotSpec): + spec = args[0] + if spec.is_single and not spec.gridspec.has_custom_geometry: + self._ensure_grid(spec.nrows, spec.ncols) + ax = self._axes_at(spec.index) + else: + # Spans and custom spacing become explicit figure rectangles. + ax = self.add_axes(spec.gridspec.cell_rect(spec.rows, spec.cols)) + elif args and args != (1, 1, 1) and args != (111,): nrows, ncols, index = _parse_subplot_args(args) - self._ensure_grid(nrows, ncols) - ax = self._axes_at(index - 1) + if any(a._figure_rect is not None for a in self._axes): + # matplotlib mixes numbered subplots into figures that already + # hold free-form axes; keep the figure free-form via the cell + # rectangle (and return the existing axes for a repeat spec). + row, col = divmod(index - 1, ncols) + rect = _GridSpec(self, nrows, ncols).cell_rect((row, row + 1), (col, col + 1)) + existing = next((a for a in self._axes if a._figure_rect == rect), None) + ax = existing if existing is not None else self.add_axes(rect) + else: + self._ensure_grid(nrows, ncols) + ax = self._axes_at(index - 1) else: self._ensure_grid(1, 1) ax = self._axes_at(0) self._current_ax = ax # matplotlib: add_subplot activates the axes + sharex = kwargs.pop("sharex", None) + sharey = kwargs.pop("sharey", None) + if sharex is not None: + ax._axis["x"] = sharex._axis_props("x") # static share, as in twiny() + if sharey is not None: + ax._axis["y"] = sharey._axis_props("y") + if kwargs: + ax.set(**kwargs) return ax def add_axes(self, rect: Any, **kwargs: Any) -> Axes: - del kwargs parsed = tuple(float(value) for value in rect) if len(parsed) != 4 or any(value < 0 for value in parsed[2:]): raise ValueError("add_axes rect must be [left, bottom, width, height]") - if not self._axes: - ax = Axes(self) - self._axes.append(ax) - else: - ax = Axes(self) - self._axes.append(ax) + ax = Axes(self) + self._axes.append(ax) ax._figure_rect = parsed self._nrows, self._ncols = 1, len(self._axes) self._current_ax = ax + if kwargs: + ax.set(**kwargs) return ax + def subplots( + self, + nrows: int = 1, + ncols: int = 1, + *, + sharex: bool = False, + sharey: bool = False, + squeeze: bool = True, + width_ratios: Any = None, + height_ratios: Any = None, + gridspec_kw: Optional[dict[str, Any]] = None, + **kwargs: Any, + ) -> Any: + """Create a subplot grid on this figure and return its Axes array. + + This mirrors the axes-returning half of ``matplotlib.figure.Figure.subplots``. + Figure creation and pyplot registration belong to the state module. + """ + del kwargs + gridspec_kw = gridspec_kw or {} + width_ratios = gridspec_kw.get("width_ratios", width_ratios) + height_ratios = gridspec_kw.get("height_ratios", height_ratios) + axes = make_axes_grid(self, int(nrows), int(ncols), squeeze=squeeze) + self._width_ratios = None if width_ratios is None else tuple(map(float, width_ratios)) + self._height_ratios = None if height_ratios is None else tuple(map(float, height_ratios)) + apply_sharing(self, bool(sharex), bool(sharey)) + self._invalidate() + return axes + + def add_gridspec(self, nrows: int = 1, ncols: int = 1, **kwargs: Any) -> "_GridSpec": + """Return a lightweight GridSpec facade backed by the current grid. + + The shim supports row-major single-cell specs such as ``fig.add_subplot(gs[0, 1])``. + General spanning layout is intentionally not exposed as a fake GridSpec. + """ + width_ratios = kwargs.pop("width_ratios", kwargs.pop("widths", None)) + height_ratios = kwargs.pop("height_ratios", kwargs.pop("heights", None)) + if kwargs: + raise not_implemented( + f"add_gridspec({', '.join(sorted(kwargs))})", + "nrows, ncols, width_ratios, and height_ratios", + ) + self._ensure_grid(int(nrows), int(ncols)) + self._width_ratios = None if width_ratios is None else tuple(map(float, width_ratios)) + self._height_ratios = None if height_ratios is None else tuple(map(float, height_ratios)) + self._invalidate() + return _GridSpec( + self, + int(nrows), + int(ncols), + width_ratios=self._width_ratios, + height_ratios=self._height_ratios, + ) + def _ensure_grid(self, nrows: int, ncols: int) -> None: if ( (nrows, ncols) != (self._nrows, self._ncols) @@ -103,20 +221,150 @@ def gca(self) -> Axes: return self._current_ax return self._axes_at(0) + def sca(self, ax: Axes) -> Axes: + if ax not in self._axes: + raise ValueError("Axes must belong to this figure") + self._current_ax = ax + return ax + + def delaxes(self, ax: Axes) -> None: + if ax not in self._axes: + raise ValueError("Axes must belong to this figure") + index = self._axes.index(ax) + self._axes.remove(ax) + ax.figure = None + if self._current_ax is ax: + self._current_ax = self._axes[min(index, len(self._axes) - 1)] if self._axes else None + if not self._axes: + self._nrows, self._ncols = 1, 1 + self._invalidate() + + def clear(self, keep_observers: bool = False) -> None: + del keep_observers # compat-noop: the shim has no observer registry + for ax in self._axes: + ax.figure = None + self._axes = [] + self._current_ax = None + self._nrows, self._ncols = 1, 1 + self._suptitle = None + self._supxlabel = None + self._supylabel = None + self._shared_colorbar = None + self._gci = None + self._width_ratios = None + self._height_ratios = None + self._layout_options = {} + self._subplot_adjust = {} + self._invalidate() + + clf = clear + # -- chrome --------------------------------------------------------------- def suptitle(self, title: str, **kwargs: Any) -> None: + size = kwargs.pop("fontsize", kwargs.pop("size", 16.0)) + weight = kwargs.pop("fontweight", kwargs.pop("weight", "normal")) + family = kwargs.pop("fontfamily", kwargs.pop("family", "system-ui, sans-serif")) + color = kwargs.pop("color", "#262626") + x = kwargs.pop("x", 0.5) + y = kwargs.pop("y", 0.98) + ha = kwargs.pop("ha", kwargs.pop("horizontalalignment", "center")) + va = kwargs.pop("va", kwargs.pop("verticalalignment", "top")) + if kwargs: + raise TypeError(f"suptitle() got unsupported keyword argument {next(iter(kwargs))!r}") self._suptitle = str(title) + self._suptitle_style = { + "size": float(size), + "weight": str(weight), + "family": str(family), + "color": str(color), + "x": float(x), + "y": float(y), + "ha": str(ha), + "va": str(va), + } self._invalidate() + def supxlabel(self, label: str, **kwargs: Any) -> Text: + self._supxlabel = str(label) + return self.text(0.5, 0.01, label, ha=kwargs.pop("ha", "center"), **kwargs) + + def supylabel(self, label: str, **kwargs: Any) -> Text: + self._supylabel = str(label) + return self.text( + 0.01, + 0.5, + label, + va=kwargs.pop("va", "center"), + rotation=kwargs.pop("rotation", "vertical"), + **kwargs, + ) + + def text( + self, + x: Any, + y: Any, + s: str, + fontdict: Optional[dict[str, Any]] = None, + **kwargs: Any, + ) -> Text: + return self.gca().text(x, y, s, fontdict=fontdict, transform=self.transFigure, **kwargs) + + def legend(self, *args: Any, **kwargs: Any) -> None: + axes = self.axes or [self.gca()] + labels = args[1] if len(args) >= 2 else kwargs.get("labels") + if labels is not None: + axes[0].legend(args[0] if args else [], labels, **kwargs) + return None + for ax in axes: + if any(entry.get("kwargs", {}).get("name") for entry in ax._entries): + ax.legend(*args, **kwargs) + if not any(ax._legend for ax in axes): + axes[0].legend(*args, **kwargs) + return None + def tight_layout(self, **kwargs: Any) -> None: - pass # engine layout is label-aware already + pad = kwargs.pop("pad", None) + h_pad = kwargs.pop("h_pad", None) + w_pad = kwargs.pop("w_pad", None) + rect = kwargs.pop("rect", None) + if kwargs: + raise TypeError( + f"tight_layout() got unsupported keyword argument {next(iter(kwargs))!r}" + ) + self._layout_options = { + "engine": "tight", + "pad": pad, + "h_pad": h_pad, + "w_pad": w_pad, + "rect": rect, + } + self._invalidate() def subplots_adjust(self, **kwargs: Any) -> None: - pass + allowed = {"left", "right", "top", "bottom", "wspace", "hspace"} + unsupported = set(kwargs) - allowed + if unsupported: + raise TypeError( + f"subplots_adjust() got unsupported keyword argument {sorted(unsupported)[0]!r}" + ) + for key, value in kwargs.items(): + if value is not None: + self._subplot_adjust[key] = float(value) + self._invalidate() def autofmt_xdate(self, **kwargs: Any) -> None: - del kwargs + rotation = float(kwargs.pop("rotation", 30)) + ha = kwargs.pop("ha", "right") + if kwargs: + raise TypeError( + f"autofmt_xdate() got unsupported keyword argument {next(iter(kwargs))!r}" + ) + for ax in self._axes: + props = ax._axis_props("x") + props["tick_label_angle"] = rotation + props.setdefault("style", {})["tick_label_anchor"] = str(ha) + self._invalidate() def set_size_inches(self, w: Any, h: Any = None) -> None: if h is None: @@ -124,9 +372,48 @@ def set_size_inches(self, w: Any, h: Any = None) -> None: self._figsize = (float(w), float(h)) self._invalidate() - def colorbar(self, mappable: Any = None, *args: Any, **kwargs: Any) -> Any: - del args - axes_arg = kwargs.pop("ax", None) + def get_size_inches(self) -> np.ndarray: + w, h = rc_figsize_px(self._figsize, self._dpi) + dpi = self.get_dpi() + return np.asarray((w / dpi, h / dpi), dtype=float) + + def set_dpi(self, value: Any) -> None: + self._dpi = float(value) + for ax in self._axes: + ax._chart = None + self._invalidate() + + def get_dpi(self) -> float: + return float(self._dpi if self._dpi is not None else 100.0) + + @property + def dpi(self) -> float: + return self.get_dpi() + + @dpi.setter + def dpi(self, value: Any) -> None: + self.set_dpi(value) + + def set_facecolor(self, color: Any) -> None: + self._facecolor = str(color) + self._invalidate() + + def get_facecolor(self) -> str: + return self._facecolor + + def set_edgecolor(self, color: Any) -> None: + self._edgecolor = str(color) + self._invalidate() + + def get_edgecolor(self) -> str: + return self._edgecolor + + def colorbar(self, mappable: Any = None, cax: Any = None, ax: Any = None, **kwargs: Any) -> Any: + if cax is not None: + raise not_implemented("colorbar(cax=...)", "the automatic colorbar placement") + if mappable is None: + mappable = self._gci + axes_arg = ax axes = getattr(mappable, "_axes", None) or self.gca() entry = getattr(mappable, "_entry", {}) props = entry.get("kwargs", {}) @@ -153,11 +440,22 @@ def colorbar(self, mappable: Any = None, *args: Any, **kwargs: Any) -> Any: "label": str(kwargs.pop("label", "")), "orientation": str(kwargs.pop("orientation", "vertical")), } + ticks = kwargs.pop("ticks", None) + if ticks is not None: + options["ticks"] = [float(value) for value in np.asarray(ticks).reshape(-1)] + extend = kwargs.pop("extend", None) + if extend is not None: + if extend not in ("neither", "min", "max", "both"): + raise ValueError("colorbar() extend must be 'neither', 'min', 'max', or 'both'") + if extend != "neither": + options["extend"] = str(extend) + check_unsupported(kwargs, "colorbar()") if isinstance(axes_arg, (list, tuple, np.ndarray)): self._shared_colorbar = options self._invalidate() else: axes._colorbar = options + axes._colorbar_source = entry if entry else None axes._invalidate() class _Colorbar: @@ -173,6 +471,15 @@ def set_label(self, label: str, **kwargs: Any) -> None: self._options["label"] = str(label) self.ax._invalidate() + def set_ticks(self, ticks: Any, labels: Any = None, **kwargs: Any) -> None: + if labels is not None: + raise not_implemented( + "Colorbar.set_ticks(labels=...)", "numeric tick positions" + ) + check_unsupported(kwargs, "Colorbar.set_ticks()") + self._options["ticks"] = [float(value) for value in np.asarray(ticks).reshape(-1)] + self.ax._invalidate() + return _Colorbar(axes, options) def figimage( @@ -229,11 +536,12 @@ def _panel_px(self) -> tuple[int, int]: def _charts(self) -> list[Any]: total_w, total_h = rc_figsize_px(self._figsize, self._dpi) - if self._axes and all(ax._figure_rect is not None for ax in self._axes): + rects = [rect for ax in self._axes if (rect := ax._figure_rect) is not None] + if self._axes and len(rects) == len(self._axes): charts = [] - for ax in self._axes: - plot_w = max(1, round(total_w * ax._figure_rect[2])) - plot_h = max(1, round(total_h * ax._figure_rect[3])) + for ax, rect in zip(self._axes, rects, strict=True): + plot_w = max(1, round(total_w * rect[2])) + plot_h = max(1, round(total_h * rect[3])) # Absolute axes rectangles describe the plot box. Export # chrome lives outside that rectangle in the surrounding # figure buffer, matching Matplotlib add_axes semantics. @@ -284,67 +592,129 @@ def _single(self) -> Optional[Any]: def savefig( self, fname: Any, dpi: Any = None, format: Optional[str] = None, **kwargs: Any ) -> None: - kwargs.pop("bbox_inches", None) # label-aware layout already trims - kwargs.pop("transparent", None) - kwargs.pop("facecolor", None) path = Path(fname) if isinstance(fname, (str, PathLike)) else None - suffix = (format or (path.suffix.lstrip(".") if path is not None else "png")).lower() - if dpi is not None and self._dpi is None: + if path is None and format is None: + raise ValueError("savefig() requires format= for file-like output") + if path is not None and format is None and not path.suffix: + format = "png" # matplotlib's savefig.format default + path = path.with_suffix(".png") + suffix = (format or (path.suffix.lstrip(".") if path is not None else "")).lower() + transparent = bool(kwargs.pop("transparent", False)) + metadata = kwargs.pop("metadata", None) + facecolor = kwargs.pop("facecolor", None) + bbox_inches = kwargs.pop("bbox_inches", None) + pad_inches = float(kwargs.pop("pad_inches", 0.1)) + if bbox_inches not in (None, "tight"): + raise not_implemented("savefig(bbox_inches=Bbox)", "bbox_inches='tight'") + if metadata is not None and not isinstance(metadata, dict): + raise TypeError("savefig metadata must be a mapping") + unsupported = {key for key, value in kwargs.items() if value is not None} + if unsupported: + option = sorted(unsupported)[0] + raise not_implemented( + f"savefig({option}=...)", + "dpi and format; compose backgrounds/layout explicitly for other options", + ) + + old_dpi = self._dpi + old_facecolor = self._facecolor + old_backgrounds = [ax._theme_tokens["plot_background"] for ax in self._axes] + if dpi is not None: self._dpi = float(dpi) for ax in self._axes: ax._chart = None self._invalidate() + if facecolor is not None: + from ._colors import resolve_color - single = self._single() - if suffix in ("png",): - if single is None: - data = self._to_png() - else: - from xy import _raster - - data = _raster.to_png(single.figure(), fast=True) - elif suffix in ("svg",): - if single is None: - from ._grid import compose_svg - - data = compose_svg( - self._charts(), self._nrows, self._ncols, self._suptitle - ).encode() + self._facecolor = resolve_color(facecolor) or "none" + if transparent: + self._facecolor = "none" + for ax in self._axes: + ax._theme_tokens["plot_background"] = "none" + ax._chart = None + self._invalidate() + try: + if suffix == "png": + data = self._to_png( + bbox_tight=bbox_inches == "tight", + pad_inches=pad_inches, + ) + if metadata: + data = _png_with_metadata(data, metadata) + elif suffix == "svg": + single = self._single() + if single is None or self._suptitle is not None: + from ._grid import compose_svg + + data = compose_svg( + self._charts(), + self._nrows, + self._ncols, + self._suptitle, + self._suptitle_style, + ).encode() + else: + data = single.to_svg().encode() + if self._facecolor not in ("none", "white"): + import html + + fill = html.escape(self._facecolor, quote=True) + start = data.find(b">") + 1 + rect = f''.encode() + data = data[:start] + rect + data[start:] + if metadata: + import html + + description = html.escape("; ".join(f"{k}: {v}" for k, v in metadata.items())) + start = data.find(b">") + 1 + data = ( + data[:start] + f"{description}".encode() + data[start:] + ) + elif suffix == "html": + if metadata: + raise not_implemented("savefig(format='html', metadata=...)", "PNG or SVG") + data = self._to_html().encode() + if self._facecolor not in ("none", "white"): + import html + + fill = html.escape(self._facecolor, quote=True) + data = f'
'.encode() + data + b"
" else: - data = single.to_svg().encode() - elif suffix in ("html",): - data = self._to_html().encode() - else: - raise not_implemented(f"savefig(format={suffix!r})", "png, svg, or html") + raise not_implemented(f"savefig(format={suffix!r})", "png, svg, or html") + finally: + self._dpi = old_dpi + self._facecolor = old_facecolor + for ax, background in zip(self._axes, old_backgrounds, strict=True): + ax._theme_tokens["plot_background"] = background + ax._chart = None + self._invalidate() if path is not None: path.write_bytes(data) else: fname.write(data) # file-like - def _to_png(self) -> bytes: + def _to_png(self, *, bbox_tight: bool = False, pad_inches: float = 0.1) -> bytes: from ._grid import stitch_png canvas_size = rc_figsize_px(self._figsize, self._dpi) + rects = [rect for ax in self._axes if (rect := ax._figure_rect) is not None] positions = ( [ ( - ax._figure_rect[0] - - (46 if round(canvas_size[0] * ax._figure_rect[2]) + 54 < 520 else 62) - / canvas_size[0], - ax._figure_rect[1] - - (36 if round(canvas_size[0] * ax._figure_rect[2]) + 54 < 520 else 42) - / canvas_size[1], - ax._figure_rect[2] - + (54 if round(canvas_size[0] * ax._figure_rect[2]) + 54 < 520 else 76) - / canvas_size[0], - ax._figure_rect[3] - + (42 if round(canvas_size[0] * ax._figure_rect[2]) + 54 < 520 else 52) - / canvas_size[1], + rect[0] + - (46 if round(canvas_size[0] * rect[2]) + 54 < 520 else 62) / canvas_size[0], + rect[1] + - (36 if round(canvas_size[0] * rect[2]) + 54 < 520 else 42) / canvas_size[1], + rect[2] + + (54 if round(canvas_size[0] * rect[2]) + 54 < 520 else 76) / canvas_size[0], + rect[3] + + (42 if round(canvas_size[0] * rect[2]) + 54 < 520 else 52) / canvas_size[1], ) - for ax in self._axes + for rect in rects ] - if self._axes and all(ax._figure_rect is not None for ax in self._axes) + if self._axes and len(rects) == len(self._axes) else None ) @@ -354,9 +724,12 @@ def _to_png(self) -> bytes: self._ncols, self._suptitle, self._shared_colorbar, + suptitle_style=self._suptitle_style, positions=positions, canvas_size=canvas_size if positions is not None else None, facecolor=self._facecolor, + bbox_tight=bbox_tight, + pad_pixels=max(0, round(pad_inches * float(self._dpi or 100.0) * 2.0)), ) def _to_html(self) -> str: @@ -368,7 +741,11 @@ def _to_html(self) -> str: from ._grid import compose_html self._html_cache = compose_html( - self._charts(), self._nrows, self._ncols, self._suptitle + self._charts(), + self._nrows, + self._ncols, + self._suptitle, + self._suptitle_style, ) return self._html_cache @@ -384,6 +761,146 @@ def show(self, *args: Any, **kwargs: Any) -> None: webbrowser.open(f"file://{f.name}") +class _FigureCanvas: + """The mpl canvas surface scripts poke: filetypes and draw triggers.""" + + def __init__(self, figure: Figure) -> None: + self.figure = figure + + def get_supported_filetypes(self) -> dict[str, str]: + return { + "png": "Portable Network Graphics", + "svg": "Scalable Vector Graphics", + "html": "xy interactive HTML", + } + + def draw(self) -> None: + self.figure._invalidate() # the next export re-renders from scratch + + draw_idle = draw + + +class _SubplotSpec: + def __init__(self, gridspec: "_GridSpec", rows: tuple[int, int], cols: tuple[int, int]) -> None: + self.gridspec = gridspec + self.rows = rows + self.cols = cols + self.nrows = gridspec.nrows + self.ncols = gridspec.ncols + + @property + def is_single(self) -> bool: + return self.rows[1] - self.rows[0] == 1 and self.cols[1] - self.cols[0] == 1 + + @property + def index(self) -> int: + return self.rows[0] * self.ncols + self.cols[0] + + +# matplotlib's SubplotParams defaults — the frame every gridspec rect lives in. +_SUBPLOT_PARAMS = {"left": 0.125, "right": 0.9, "bottom": 0.11, "top": 0.88} +_SUBPLOT_SPACING = 0.2 # figure.subplot.wspace/hspace default + + +class _GridSpec: + """Grid geometry for subplot specs. + + Single cells on default geometry map onto the figure's uniform subplot + grid; spans and custom spacing resolve to explicit figure rectangles + (the add_axes path), which every exporter already positions. + """ + + def __init__(self, figure: Optional[Figure], nrows: int, ncols: int, **kwargs: Any) -> None: + self.figure = figure + self.nrows = int(nrows) + self.ncols = int(ncols) + if self.nrows < 1 or self.ncols < 1: + raise ValueError("GridSpec must have at least one row and one column") + geometry_keys = ("left", "bottom", "right", "top", "wspace", "hspace") + self._geometry = {key: kwargs.pop(key, None) for key in geometry_keys} + width_ratios = kwargs.pop("width_ratios", None) + height_ratios = kwargs.pop("height_ratios", None) + check_unsupported(kwargs, "GridSpec()") + self._width_ratios = None if width_ratios is None else tuple(map(float, width_ratios)) + self._height_ratios = None if height_ratios is None else tuple(map(float, height_ratios)) + if self._width_ratios is not None and len(self._width_ratios) != self.ncols: + raise ValueError("width_ratios must match the number of columns") + if self._height_ratios is not None and len(self._height_ratios) != self.nrows: + raise ValueError("height_ratios must match the number of rows") + + @property + def has_custom_geometry(self) -> bool: + return any(value is not None for value in self._geometry.values()) + + @staticmethod + def _span(key: Any, count: int) -> tuple[int, int]: + if isinstance(key, slice): + if key.step not in (None, 1): + raise not_implemented("GridSpec slicing with a step", "contiguous spans") + start, stop, _ = key.indices(count) + if stop <= start: + raise IndexError("GridSpec slice selects no cells") + return start, stop + index = int(key) + if index < 0: + index += count + if not 0 <= index < count: + raise IndexError("GridSpec index out of range") + return index, index + 1 + + def __getitem__(self, key: Any) -> _SubplotSpec: + if isinstance(key, tuple): + if len(key) != 2: + raise IndexError("GridSpec indexes are [row, col]") + rows = self._span(key[0], self.nrows) + cols = self._span(key[1], self.ncols) + return _SubplotSpec(self, rows, cols) + # Flat row-major indexing; a flat slice spans the bounding box of its + # first and last cell, matching matplotlib's SubplotSpec corners. + total = self.nrows * self.ncols + first, stop = self._span(key, total) + last = stop - 1 + r0, c0 = divmod(first, self.ncols) + r1, c1 = divmod(last, self.ncols) + return _SubplotSpec(self, (min(r0, r1), max(r0, r1) + 1), (min(c0, c1), max(c0, c1) + 1)) + + def cell_rect(self, rows: tuple[int, int], cols: tuple[int, int]) -> tuple[float, ...]: + """[left, bottom, width, height] figure fractions for a cell span.""" + frame = { + key: (self._geometry[key] if self._geometry[key] is not None else default) + for key, default in _SUBPLOT_PARAMS.items() + } + wspace = self._geometry["wspace"] + hspace = self._geometry["hspace"] + wspace = _SUBPLOT_SPACING if wspace is None else float(wspace) + hspace = _SUBPLOT_SPACING if hspace is None else float(hspace) + span_w = float(frame["right"]) - float(frame["left"]) + span_h = float(frame["top"]) - float(frame["bottom"]) + # wspace/hspace are fractions of the *average* cell size (matplotlib). + avail_w = span_w / (1.0 + wspace * (self.ncols - 1) / self.ncols) + avail_h = span_h / (1.0 + hspace * (self.nrows - 1) / self.nrows) + gap_w = (span_w - avail_w) / (self.ncols - 1) if self.ncols > 1 else 0.0 + gap_h = (span_h - avail_h) / (self.nrows - 1) if self.nrows > 1 else 0.0 + wratios = self._width_ratios or (1.0,) * self.ncols + hratios = self._height_ratios or (1.0,) * self.nrows + widths = [avail_w * ratio / sum(wratios) for ratio in wratios] + heights = [avail_h * ratio / sum(hratios) for ratio in hratios] + c0, c1 = cols + r0, r1 = rows + x0 = float(frame["left"]) + sum(widths[:c0]) + c0 * gap_w + width = sum(widths[c0:c1]) + (c1 - c0 - 1) * gap_w + y_top = float(frame["top"]) - (sum(heights[:r0]) + r0 * gap_h) + height = sum(heights[r0:r1]) + (r1 - r0 - 1) * gap_h + return (x0, y_top - height, width, height) + + +class GridSpec(_GridSpec): + """plt.GridSpec: figure-optional grid geometry with span support.""" + + def __init__(self, nrows: int, ncols: int, figure: Optional[Figure] = None, **kwargs: Any): + super().__init__(figure, nrows, ncols, **kwargs) + + def _parse_subplot_args(args: tuple) -> tuple[int, int, int]: if len(args) == 1 and isinstance(args[0], int) and args[0] >= 111: code = args[0] diff --git a/python/xy/pyplot/_plot_types.py b/python/xy/pyplot/_plot_types.py index aa9afab7..b3d27d04 100644 --- a/python/xy/pyplot/_plot_types.py +++ b/python/xy/pyplot/_plot_types.py @@ -82,6 +82,191 @@ def _masked_float(value: Any) -> np.ndarray: return np.ma.asarray(value, dtype=np.float64).filled(np.nan) +def _reject_spectral_options(where: str, **options: Any) -> None: + specified = {name: value for name, value in options.items() if value is not None} + if specified: + check_unsupported(specified, where) + + +def _reject_non_default(where: str, option: str, value: Any, *defaults: Any) -> None: + """Fail loudly on option values the engine cannot honor. + + ``None`` (unspecified) and the exact Matplotlib default pass through. + """ + if value is None: + return + for default in defaults: + if isinstance(default, bool) or isinstance(value, bool): + if value is default: + return + elif isinstance(default, (int, float)) and isinstance(value, (int, float)): + if float(value) == float(default): + return + else: + equal = value == default + if isinstance(equal, bool) and equal: + return + raise not_implemented(f"{where}({option}=...)") + + +def _textprops_kwargs(textprops: Any, where: str) -> dict[str, Any]: + """Translate a textprops dict onto @text entry kwargs (mirrors text()).""" + source = dict(textprops or {}) + color = source.pop("color", None) + fontsize = source.pop("fontsize", source.pop("size", None)) + ha = source.pop("ha", source.pop("horizontalalignment", None)) + va = source.pop("va", source.pop("verticalalignment", None)) + check_unsupported(source, where) + out: dict[str, Any] = {} + if color is not None: + out["color"] = resolve_color(color) + if ha is not None: + out["anchor"] = {"left": "start", "center": "middle", "right": "end"}.get(str(ha), "start") + style: dict[str, Any] = {} + if fontsize is not None: + style["font_size"] = float(fontsize) + if va is not None: + style["vertical_align"] = str(va) + if style: + out["style"] = style + return out + + +def _bilinear_grid_sample( + x_coords: np.ndarray, y_coords: np.ndarray, grid: np.ndarray, px: Any, py: Any +) -> np.ndarray: + """Bilinear samples of a scalar grid; NaN outside the grid bounds.""" + px = np.asarray(px, dtype=np.float64) + py = np.asarray(py, dtype=np.float64) + col = np.clip(np.searchsorted(x_coords, px, side="right") - 1, 0, len(x_coords) - 2) + row = np.clip(np.searchsorted(y_coords, py, side="right") - 1, 0, len(y_coords) - 2) + tx = np.clip((px - x_coords[col]) / (x_coords[col + 1] - x_coords[col]), 0.0, 1.0) + ty = np.clip((py - y_coords[row]) / (y_coords[row + 1] - y_coords[row]), 0.0, 1.0) + values = ( + grid[row, col] * (1.0 - tx) * (1.0 - ty) + + grid[row, col + 1] * tx * (1.0 - ty) + + grid[row + 1, col] * (1.0 - tx) * ty + + grid[row + 1, col + 1] * tx * ty + ) + inside = (px >= x_coords[0]) & (px <= x_coords[-1]) & (py >= y_coords[0]) & (py <= y_coords[-1]) + return np.where(inside, values, np.nan) + + +def _integrate_streamlines( + x_coords: np.ndarray, + y_coords: np.ndarray, + u: np.ndarray, + v: np.ndarray, + seeds: np.ndarray, + direction: str, + max_steps: int, +) -> list[np.ndarray]: + """Fixed-step field-line integration matching the native kernel's scheme.""" + step = 0.35 * min(float(np.min(np.diff(x_coords))), float(np.min(np.diff(y_coords)))) + signs = {"forward": (1.0,), "backward": (-1.0,), "both": (-1.0, 1.0)}[direction] + lines: list[np.ndarray] = [] + for seed_x, seed_y in seeds: + branches: list[list[tuple[float, float]]] = [] + for sign in signs: + px, py = float(seed_x), float(seed_y) + points = [(px, py)] + for _ in range(max_steps): + su = float(_bilinear_grid_sample(x_coords, y_coords, u, px, py)) + sv = float(_bilinear_grid_sample(x_coords, y_coords, v, px, py)) + if not (np.isfinite(su) and np.isfinite(sv)): + break + speed = float(np.hypot(su, sv)) + if speed <= np.finfo(float).eps: + break + nx = px + sign * step * su / speed + ny = py + sign * step * sv / speed + if not (x_coords[0] <= nx <= x_coords[-1] and y_coords[0] <= ny <= y_coords[-1]): + break + px, py = nx, ny + points.append((px, py)) + branches.append(points) + combined = branches[0][::-1] + branches[1][1:] if len(branches) == 2 else branches[0] + if len(combined) >= 2: + lines.append(np.asarray(combined, dtype=np.float64)) + return lines + + +# On/off spans within one dash cycle; segments marks have no screen-space dash +# primitive, so dash geometry is emitted as data-space sub-segments. +_DASH_SEGMENT_PATTERNS: dict[str, tuple[tuple[float, float], ...]] = { + "--": ((0.0, 0.62),), + "-.": ((0.0, 0.5), (0.66, 0.8)), + ":": ((0.0, 0.18), (0.5, 0.68)), +} + +_LINESTYLE_TO_FMT_TOKEN = {"solid": "-", "dashed": "--", "dashdot": "-.", "dotted": ":"} + + +def _dash_segment_pattern(where: str, linestyle: Any) -> Optional[tuple[tuple[float, float], ...]]: + """Dash pattern for a linestyle token or name; None means solid.""" + if linestyle is None: + return None + key = _LINESTYLE_TO_FMT_TOKEN.get(str(linestyle), str(linestyle)) + if key in ("-", "", " ", "none", "None"): + return None + pattern = _DASH_SEGMENT_PATTERNS.get(key) + if pattern is None: + raise not_implemented(f"{where}(linestyle={linestyle!r})") + return pattern + + +def _dashed_segments( + x0: np.ndarray, + y0: np.ndarray, + x1: np.ndarray, + y1: np.ndarray, + pattern: tuple[tuple[float, float], ...], +) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + """Split segments into dash pieces; the cycle repeats ~8× per longest run.""" + lengths = np.hypot(x1 - x0, y1 - y0) + finite = lengths[np.isfinite(lengths)] + longest = float(finite.max()) if len(finite) else 0.0 + if longest <= 0.0: + return x0, y0, x1, y1 + period = longest / 8.0 + pieces: tuple[list[float], list[float], list[float], list[float]] = ([], [], [], []) + for ax, ay, bx, by, length in zip(x0, y0, x1, y1, lengths, strict=True): + if not np.isfinite(length) or length <= 0.0: + continue + for cycle in range(int(np.ceil(length / period))): + for start, stop in pattern: + t0 = (cycle + start) * period / length + t1 = min((cycle + stop) * period / length, 1.0) + if t0 >= 1.0 or t1 <= t0: + continue + pieces[0].append(ax + (bx - ax) * t0) + pieces[1].append(ay + (by - ay) * t0) + pieces[2].append(ax + (bx - ax) * t1) + pieces[3].append(ay + (by - ay) * t1) + return ( + np.asarray(pieces[0], dtype=np.float64), + np.asarray(pieces[1], dtype=np.float64), + np.asarray(pieces[2], dtype=np.float64), + np.asarray(pieces[3], dtype=np.float64), + ) + + +def _limit_error(error: Any, lower_limits: Any, upper_limits: Any, size: int) -> Any: + """Convert limit flags into Matplotlib's two-sided error-array geometry.""" + if error is None or (not np.any(lower_limits) and not np.any(upper_limits)): + return error + raw = np.asarray(error, dtype=np.float64) + if raw.ndim >= 2 and raw.shape[0] == 2: + low = np.broadcast_to(raw[0], (size,)).copy() + high = np.broadcast_to(raw[1], (size,)).copy() + else: + low = np.broadcast_to(raw, (size,)).copy() + high = np.broadcast_to(raw, (size,)).copy() + low[np.broadcast_to(np.asarray(lower_limits, dtype=bool), (size,))] = 0.0 + high[np.broadcast_to(np.asarray(upper_limits, dtype=bool), (size,))] = 0.0 + return np.vstack((low, high)) + + def _plain_label(value: Any) -> str: text = str(value).replace("$", "") for source, target in { @@ -251,6 +436,10 @@ def _entry_extent(self, axis: str) -> tuple[float, float]: ... def _categorical_position(self, axis: str, label: Any) -> float: ... + def _transform_points( + self, x: Any, y: Any, transform: Any + ) -> tuple[np.ndarray, np.ndarray]: ... + def plot(self, *args: Any, **kwargs: Any) -> list[Line2D]: ... def bar(self, *args: Any, **kwargs: Any) -> BarContainer: ... @@ -269,37 +458,44 @@ def set_xlim(self, *args: Any, **kwargs: Any) -> None: ... def set_ylim(self, *args: Any, **kwargs: Any) -> None: ... - def set_xscale(self, scale: str) -> None: ... + def set_xscale(self, scale: str, **kwargs: Any) -> None: ... + + def set_yscale(self, scale: str, **kwargs: Any) -> None: ... + + def _axis_props(self, axis: str) -> dict[str, Any]: ... - def set_yscale(self, scale: str) -> None: ... + def _invalidate(self) -> None: ... def semilogx(self, *args: Any, **kwargs: Any) -> list[Line2D]: base = kwargs.pop("base", kwargs.pop("basex", None)) - kwargs.pop("subs", kwargs.pop("subsx", None)) - kwargs.pop("nonpositive", kwargs.pop("nonposx", None)) - del base - result = self.plot(*args, **kwargs) - self.set_xscale("log") - return result + subs = kwargs.pop("subs", kwargs.pop("subsx", None)) + nonpositive = kwargs.pop("nonpositive", kwargs.pop("nonposx", "clip")) + self.set_xscale( + "log", base=10 if base is None else base, subs=subs, nonpositive=nonpositive + ) + return self.plot(*args, **kwargs) def semilogy(self, *args: Any, **kwargs: Any) -> list[Line2D]: base = kwargs.pop("base", kwargs.pop("basey", None)) - kwargs.pop("subs", kwargs.pop("subsy", None)) - kwargs.pop("nonpositive", kwargs.pop("nonposy", None)) - del base - result = self.plot(*args, **kwargs) - self.set_yscale("log") - return result + subs = kwargs.pop("subs", kwargs.pop("subsy", None)) + nonpositive = kwargs.pop("nonpositive", kwargs.pop("nonposy", "clip")) + self.set_yscale( + "log", base=10 if base is None else base, subs=subs, nonpositive=nonpositive + ) + return self.plot(*args, **kwargs) def loglog(self, *args: Any, **kwargs: Any) -> list[Line2D]: base = kwargs.pop("base", None) - kwargs.pop("subs", None) - kwargs.pop("nonpositive", None) - del base - result = self.plot(*args, **kwargs) - self.set_xscale("log") - self.set_yscale("log") - return result + subs = kwargs.pop("subs", None) + nonpositive = kwargs.pop("nonpositive", "clip") + scale_kwargs = { + "base": 10 if base is None else base, + "subs": subs, + "nonpositive": nonpositive, + } + self.set_xscale("log", **scale_kwargs) + self.set_yscale("log", **scale_kwargs) + return self.plot(*args, **kwargs) def hlines( self, @@ -313,7 +509,8 @@ def hlines( ) -> PolyCollection: width = kwargs.pop("linewidth", kwargs.pop("linewidths", kwargs.pop("lw", 1.2))) alpha = kwargs.pop("alpha", None) - kwargs.pop("data", None) + data = kwargs.pop("data", None) + y, xmin, xmax = (_from_data(value, data) for value in (y, xmin, xmax)) transform = kwargs.pop("transform", None) check_unsupported(kwargs, "hlines()") yv, x0, x1 = np.broadcast_arrays(y, xmin, xmax) @@ -321,8 +518,13 @@ def hlines( if transform == "yaxis transform": lo, hi = self._entry_extent("x") x0, x1 = lo + x0 * (hi - lo), lo + x1 * (hi - lo) - if linestyles not in (None, "solid", "-"): - pass # generic segments are solid; geometry remains exact + dash_pattern = _dash_segment_pattern("hlines", linestyles) + sx0, sy0, sx1, sy1 = x0.reshape(-1), yv.reshape(-1), x1.reshape(-1), yv.reshape(-1) + if transform not in (None, "yaxis transform"): + sx0, sy0 = self._transform_points(sx0, sy0, transform) + sx1, sy1 = self._transform_points(sx1, sy1, transform) + if dash_pattern is not None: + sx0, sy0, sx1, sy1 = _dashed_segments(sx0, sy0, sx1, sy1, dash_pattern) chosen_color = colors if chosen_color is not None and not isinstance(chosen_color, str) and len(chosen_color): chosen_color = chosen_color[0] @@ -330,7 +532,7 @@ def hlines( "@mark", { "factory": "segments", - "args": (x0.reshape(-1), yv.reshape(-1), x1.reshape(-1), yv.reshape(-1)), + "args": (sx0, sy0, sx1, sy1), "kwargs": { "color": resolve_color(chosen_color) if chosen_color is not None @@ -353,6 +555,8 @@ def vlines( label: Any = "", **kwargs: Any, ) -> PolyCollection: + data = kwargs.pop("data", None) + x, ymin, ymax = (_from_data(value, data) for value in (x, ymin, ymax)) xv, y0, y1 = np.broadcast_arrays(x, ymin, ymax) xv, y0, y1 = (_segment_values(value) for value in (xv, y0, y1)) return self._vlines_entry(xv, y0, y1, colors, linestyles, label, kwargs) @@ -377,18 +581,23 @@ def _vlines_entry( and not (len(color) in (3, 4) and all(np.isscalar(value) for value in color)) ): color = color[0] - kwargs.pop("data", None) transform = kwargs.pop("transform", None) + dash_pattern = _dash_segment_pattern("vlines", linestyles) if transform == "xaxis transform": lo, hi = self._entry_extent("y") y0, y1 = lo + y0 * (hi - lo), lo + y1 * (hi - lo) check_unsupported(kwargs, "vlines()") - del linestyles + sx0, sy0, sx1, sy1 = xv.reshape(-1), y0.reshape(-1), xv.reshape(-1), y1.reshape(-1) + if transform not in (None, "xaxis transform"): + sx0, sy0 = self._transform_points(sx0, sy0, transform) + sx1, sy1 = self._transform_points(sx1, sy1, transform) + if dash_pattern is not None: + sx0, sy0, sx1, sy1 = _dashed_segments(sx0, sy0, sx1, sy1, dash_pattern) entry = self._add( "@mark", { "factory": "segments", - "args": (xv.reshape(-1), y0.reshape(-1), xv.reshape(-1), y1.reshape(-1)), + "args": (sx0, sy0, sx1, sy1), "kwargs": { "color": resolve_color(color) if color is not None else self._next_color(), "width": _float(np.asarray(width).reshape(-1)[0]), @@ -415,7 +624,9 @@ def broken_barh(self, xranges: Any, yrange: Any, **kwargs: Any) -> PolyCollectio label = kwargs.pop("label", None) edgecolor = kwargs.pop("edgecolors", kwargs.pop("edgecolor", None)) linewidth = kwargs.pop("linewidth", kwargs.pop("linewidths", None)) - kwargs.pop("align", None) + align = kwargs.pop("align", "center") + if align != "center": + raise not_implemented(f"broken_barh(align={align!r})") check_unsupported(kwargs, "broken_barh()") entry_kwargs: dict[str, Any] = { "base": ranges[:, 0], @@ -441,6 +652,10 @@ def broken_barh(self, xranges: Any, yrange: Any, **kwargs: Any) -> PolyCollectio def fill_betweenx( self, y: Any, x1: Any, x2: Any = 0, where: Any = None, **kwargs: Any ) -> PolyCollection: + data = kwargs.pop("data", None) + if data is not None: + # resolve string keys before any float coercion sees them + y, x1, x2 = (_from_data(value, data) for value in (y, x1, x2)) yv, left, right = np.broadcast_arrays( _masked_float(y), _masked_float(x1), @@ -453,11 +668,19 @@ def fill_betweenx( color = kwargs.pop("color", kwargs.pop("facecolor", None)) alpha = kwargs.pop("alpha", None) label = kwargs.pop("label", None) - kwargs.pop("edgecolor", None) - kwargs.pop("linewidth", None) - kwargs.pop("interpolate", None) - kwargs.pop("step", None) - kwargs.pop("data", None) + edgecolor = kwargs.pop("edgecolor", None) + linewidth = kwargs.pop("linewidth", None) + interpolate = kwargs.pop("interpolate", False) + step = kwargs.pop("step", None) + transform = kwargs.pop("transform", None) + if edgecolor is not None or linewidth is not None: + raise not_implemented("fill_betweenx(edge rendering)") + if interpolate: + raise not_implemented("fill_betweenx(interpolate=True)") + if step is not None: + raise not_implemented("fill_betweenx(step=...)") + if transform is not None: + raise not_implemented("fill_betweenx(transform=...)") check_unsupported(kwargs, "fill_betweenx()") valid = np.isfinite(yv + left + right) if where is not None: @@ -592,10 +815,13 @@ def arrow(self, x: float, y: float, dx: float, dy: float, **kwargs: Any) -> Poly width = kwargs.pop("linewidth", kwargs.pop("width", 1.2)) head_width = kwargs.pop("head_width", None) head_length = kwargs.pop("head_length", None) - kwargs.pop("length_includes_head", None) - kwargs.pop("shape", None) - kwargs.pop("overhang", None) - kwargs.pop("head_starts_at_zero", None) + length_includes_head = kwargs.pop("length_includes_head", False) + shape = kwargs.pop("shape", "full") + overhang = kwargs.pop("overhang", 0) + head_starts_at_zero = kwargs.pop("head_starts_at_zero", False) + transform = kwargs.pop("transform", None) + if length_includes_head or shape != "full" or overhang != 0 or head_starts_at_zero: + raise not_implemented("arrow(head shape/overhang options)") check_unsupported(kwargs, "arrow()") ratio = 0.22 if head_length is not None: @@ -613,6 +839,9 @@ def arrow(self, x: float, y: float, dx: float, dy: float, **kwargs: Any) -> Poly np.array([dy]), head_ratio=ratio, ) + if transform is not None: + x0, y0 = self._transform_points(x0, y0, transform) + x1, y1 = self._transform_points(x1, y1, transform) entry = self._add( "@mark", { @@ -634,7 +863,10 @@ def axline( raise TypeError("axline() requires exactly one of xy2 or slope") if xy2 is None: xy2 = (float(xy1[0]) + 1.0, float(xy1[1]) + float(slope)) - kwargs.pop("transform", None) + transform = kwargs.pop("transform", None) + if transform is not None: + tx, ty = self._transform_points([xy1[0], xy2[0]], [xy1[1], xy2[1]], transform) + xy1, xy2 = (float(tx[0]), float(ty[0])), (float(tx[1]), float(ty[1])) props = _line_props(self, kwargs) check_unsupported(kwargs, "axline()") entry = self._add( @@ -671,7 +903,9 @@ def magnitude_spectrum( data: Any = None, **kwargs: Any, ) -> tuple[np.ndarray, np.ndarray, Line2D]: - del window, sides + _reject_spectral_options("magnitude_spectrum()", window=window, sides=sides) + if scale not in (None, "linear", "dB"): + raise ValueError("magnitude_spectrum scale must be 'linear' or 'dB'") values = np.asarray(_from_data(x, data), dtype=np.float64) nfft = len(values) if pad_to is None else int(pad_to) from xy import kernels @@ -697,7 +931,7 @@ def angle_spectrum( data: Any = None, **kwargs: Any, ) -> tuple[np.ndarray, np.ndarray, Line2D]: - del window, sides + _reject_spectral_options("angle_spectrum()", window=window, sides=sides) values = np.asarray(_from_data(x, data), dtype=np.float64) nfft = len(values) if pad_to is None else int(pad_to) from xy import kernels @@ -718,7 +952,7 @@ def phase_spectrum( data: Any = None, **kwargs: Any, ) -> tuple[np.ndarray, np.ndarray, Line2D]: - del window, sides + _reject_spectral_options("phase_spectrum()", window=window, sides=sides) values = np.asarray(_from_data(x, data), dtype=np.float64) nfft = len(values) if pad_to is None else int(pad_to) from xy import kernels @@ -834,8 +1068,9 @@ def bar_label( if len(raw_labels) != len(values): raise ValueError("bar_label labels must match the number of bars") color = kwargs.pop("color", None) - kwargs.pop("fontsize", None) - kwargs.pop("fontproperties", None) + fontsize = kwargs.pop("fontsize", None) + if kwargs.pop("fontproperties", None) is not None: + raise not_implemented("bar_label(fontproperties=...)", alternative="fontsize=") check_unsupported(kwargs, "bar_label()") result: list[Text] = [] for index, value in enumerate(values): @@ -867,18 +1102,15 @@ def bar_label( anchor = "start" if positive else "end" dx = (4.0 + pixel_padding) * (1.0 if positive else -1.0) dy = 4.0 - entry = self._add( - "@text", - { - "args": (x, y, label), - "kwargs": { - "color": resolve_color(color) if color is not None else None, - "anchor": anchor, - "dx": dx, - "dy": dy, - }, - }, - ) + text_kwargs: dict[str, Any] = { + "color": resolve_color(color) if color is not None else None, + "anchor": anchor, + "dx": dx, + "dy": dy, + } + if fontsize is not None: + text_kwargs["style"] = {"font_size": float(fontsize)} + entry = self._add("@text", {"args": (x, y, label), "kwargs": text_kwargs}) result.append(Text(self, entry)) return result @@ -898,7 +1130,14 @@ def psd( data: Any = None, **kwargs: Any, ) -> Any: - del detrend, window, pad_to, sides, scale_by_freq + _reject_spectral_options( + "psd()", + detrend=detrend, + window=window, + pad_to=pad_to, + sides=sides, + scale_by_freq=scale_by_freq, + ) values = np.asarray(_from_data(x, data), dtype=np.float64) from xy import kernels @@ -927,7 +1166,14 @@ def csd( data: Any = None, **kwargs: Any, ) -> Any: - del detrend, window, pad_to, sides, scale_by_freq + _reject_spectral_options( + "csd()", + detrend=detrend, + window=window, + pad_to=pad_to, + sides=sides, + scale_by_freq=scale_by_freq, + ) xv = np.asarray(_from_data(x, data), dtype=np.float64) yv = np.asarray(_from_data(y, data), dtype=np.float64) from xy import kernels @@ -957,7 +1203,14 @@ def cohere( data: Any = None, **kwargs: Any, ) -> tuple[np.ndarray, np.ndarray]: - del detrend, window, pad_to, sides, scale_by_freq + _reject_spectral_options( + "cohere()", + detrend=detrend, + window=window, + pad_to=pad_to, + sides=sides, + scale_by_freq=scale_by_freq, + ) xv = np.asarray(_from_data(x, data), dtype=np.float64) yv = np.asarray(_from_data(y, data), dtype=np.float64) from xy import kernels @@ -991,7 +1244,17 @@ def specgram( data: Any = None, **kwargs: Any, ) -> tuple[np.ndarray, np.ndarray, np.ndarray, PolyCollection]: - del detrend, window, xextent, pad_to, sides, scale_by_freq, mode, scale + _reject_spectral_options( + "specgram()", + detrend=detrend, + window=window, + xextent=xextent, + pad_to=pad_to, + sides=sides, + scale_by_freq=scale_by_freq, + mode=mode, + scale=scale, + ) values = np.asarray(_from_data(x, data), dtype=np.float64) from xy import kernels @@ -1077,10 +1340,11 @@ def stem( else: raise TypeError("stem() takes y or x, y") color = None + dash_pattern = None if linefmt: color_spec, linestyle, _marker = parse_fmt(str(linefmt)) color = resolve_color(color_spec) if color_spec else None - del linestyle + dash_pattern = _dash_segment_pattern("stem", linestyle) symbol = "circle" if markerfmt: marker_color, _linestyle, marker = parse_fmt(str(markerfmt)) @@ -1088,9 +1352,10 @@ def stem( from ._translate import MARKER_TO_SYMBOL symbol = MARKER_TO_SYMBOL.get(marker or "o", "circle") - del basefmt + # The shim renders no baseline rule, so only the default basefmt passes. + _reject_non_default("stem", "basefmt", basefmt, "C3-") chosen = color or self._next_color() - if orientation == "vertical": + if orientation == "vertical" and dash_pattern is None: entry = self._add( "@mark", { @@ -1104,14 +1369,23 @@ def stem( }, }, ) - elif orientation == "horizontal": + elif orientation in ("vertical", "horizontal"): xv = np.asarray(x, dtype=np.float64) yv = np.asarray(y, dtype=np.float64) + base = np.full_like(xv, float(bottom)) + if orientation == "vertical": + segments = (xv, base, xv, yv) + marker_x, marker_y = xv, yv + else: + segments = (base, xv, yv, xv) + marker_x, marker_y = yv, xv + if dash_pattern is not None: + segments = _dashed_segments(*segments, dash_pattern) entry = self._add( "@mark", { "factory": "segments", - "args": (np.full_like(xv, float(bottom)), xv, yv, xv), + "args": segments, "kwargs": { "name": str(label) if label is not None else None, "color": chosen, @@ -1121,7 +1395,11 @@ def stem( ) self._add( "scatter", - {"x": yv, "y": xv, "kwargs": {"color": chosen, "symbol": symbol, "size": 5.0}}, + { + "x": marker_x, + "y": marker_y, + "kwargs": {"color": chosen, "symbol": symbol, "size": 5.0}, + }, ) else: raise ValueError("stem orientation must be 'vertical' or 'horizontal'") @@ -1342,101 +1620,168 @@ def boxplot( label: Any = None, data: Any = None, ) -> dict[str, list[Artist]]: - del sym, patch_artist, manage_ticks, zorder, tick_labels if vert is not None: orientation = "vertical" if vert else "horizontal" - unsupported = { - "notch": True if notch else None, - "whis": whis if whis not in (None, 1.5) else None, - "bootstrap": bootstrap, - "usermedians": usermedians, - "conf_intervals": conf_intervals, - "showcaps": False if showcaps is False else None, - "showbox": False if showbox is False else None, - "autorange": True if autorange else None, - "capwidths": capwidths, - } - check_unsupported( - {name: value for name, value in unsupported.items() if value is not None}, - "boxplot()", - ) values = _from_data(x, data) - color = None - for props in (boxprops, medianprops, whiskerprops, capprops, flierprops, meanprops): - if props and color is None: - color = props.get("color", props.get("facecolor")) - entry = self._add( - "@mark", - { - "factory": "box", - "args": (values,), - "kwargs": { - "x": positions, - "name": str(label) if label is not None else None, - "color": resolve_color(color) if color is not None else self._next_color(), - "width": _float(widths) if np.isscalar(widths) and widths is not None else 0.6, - "orientation": orientation, - "show_outliers": True if showfliers is None else bool(showfliers), - }, - }, + advanced = any( + ( + bool(notch), + whis not in (None, 1.5), + bootstrap is not None, + usermedians is not None, + conf_intervals is not None, + bool(showmeans), + showcaps is False, + showbox is False, + sym is not None, + autorange, + capwidths is not None, + bool(patch_artist), + not manage_ticks, + zorder is not None, + tick_labels is not None, + any( + item is not None + for item in ( + boxprops, + flierprops, + medianprops, + meanprops, + capprops, + whiskerprops, + ) + ), + ) ) - artist = Artist(self, entry) - mean_artists: list[Artist] = [] - if showmeans: - raw = _from_data(x, data) - if isinstance(raw, (list, tuple)) and raw and all(np.ndim(v) == 1 for v in raw): - groups = [np.asarray(v, dtype=np.float64) for v in raw] - else: - arr = np.asarray(raw, dtype=np.float64) - groups = [arr[:, i] for i in range(arr.shape[1])] if arr.ndim == 2 else [arr] - centers = ( - np.arange(len(groups), dtype=np.float64) - if positions is None - else np.asarray(positions, dtype=np.float64) + if not advanced: + color = self._next_color() + entry = self._add( + "@mark", + { + "factory": "box", + "args": (values,), + "kwargs": { + "x": positions, + "name": str(label) if label is not None else None, + "color": color, + "width": _float(widths) + if np.isscalar(widths) and widths is not None + else 0.6, + "orientation": orientation, + "show_outliers": True if showfliers is None else bool(showfliers), + }, + }, ) - means = np.asarray([np.nanmean(group) for group in groups], dtype=np.float64) - mean_color = resolve_color((meanprops or {}).get("color", color or "#2ca02c")) - if meanline: - half = (_float(widths) if np.isscalar(widths) and widths is not None else 0.6) * 0.5 - for center, mean in zip(centers, means, strict=True): - if orientation == "vertical": - args = ([center - half, center + half], [mean, mean]) - else: - args = ([mean, mean], [center - half, center + half]) - line_entry = self._add( - "line", - { - "x": args[0], - "y": args[1], - "kwargs": {"color": mean_color, "width": 1.5, "name": None}, - }, - ) - mean_artists.append(Line2D(self, line_entry)) + artist = Artist(self, entry) + return { + "whiskers": [artist], + "caps": [artist], + "boxes": [artist], + "medians": [artist], + "fliers": [artist] if showfliers is not False else [], + "means": [], + } + if isinstance(values, (list, tuple)) and values and all(np.ndim(v) == 1 for v in values): + groups = [np.asarray(v, dtype=np.float64) for v in values] + else: + arr = np.asarray(values, dtype=np.float64) + groups = [arr[:, i] for i in range(arr.shape[1])] if arr.ndim == 2 else [arr] + groups = [group[np.isfinite(group)] for group in groups] + if any(len(group) == 0 for group in groups): + raise ValueError("boxplot groups must each contain a finite value") + count = len(groups) + medians_override = [None] * count if usermedians is None else list(usermedians) + intervals_override = [None] * count if conf_intervals is None else list(conf_intervals) + if len(medians_override) != count or len(intervals_override) != count: + raise ValueError("usermedians/conf_intervals must match the number of boxes") + whisker = 1.5 if whis is None else whis + stats: list[dict[str, Any]] = [] + for index, group in enumerate(groups): + q1, med, q3 = np.percentile(group, [25, 50, 75]) + data_median = med # usermedians replaces the drawn median only; + median_override = medians_override[index] # CIs stay data-derived + if median_override is not None: + med = _float(median_override) + effective_whis = (0.0, 100.0) if autorange and q1 == q3 else whisker + if np.isscalar(effective_whis): + iqr = q3 - q1 + whisker_factor = _float(effective_whis) + lower_bound = q1 - whisker_factor * iqr + upper_bound = q3 + whisker_factor * iqr else: - mx, my = (centers, means) if orientation == "vertical" else (means, centers) - mean_entry = self._add( - "scatter", - { - "x": mx, - "y": my, - "kwargs": { - "color": mean_color, - "symbol": "diamond", - "size": 6.0, - "opacity": 1.0, - "name": None, - }, - }, - ) - mean_artists.append(Artist(self, mean_entry)) - return { - "whiskers": [artist], - "caps": [artist], - "boxes": [artist], - "medians": [artist], - "fliers": [artist] if showfliers is not False else [], - "means": mean_artists, - } + percentile_whis = np.asarray(effective_whis, dtype=np.float64).reshape(-1) + if percentile_whis.size != 2: + raise ValueError("whis must be a scalar or a two-percentile sequence") + lower_bound, upper_bound = np.percentile(group, percentile_whis) + below = group[group >= lower_bound] + above = group[group <= upper_bound] + whislo = float(np.min(below)) if len(below) else float(q1) + whishi = float(np.max(above)) if len(above) else float(q3) + fliers = group[(group < whislo) | (group > whishi)] + interval = intervals_override[index] + if interval is not None: + cilo, cihi = map(float, interval) + elif bootstrap is not None: + samples = int(bootstrap) + if samples <= 0: + raise ValueError("bootstrap must be a positive integer") + indices = np.random.randint(0, len(group), size=(samples, len(group))) + cilo, cihi = np.percentile(np.median(group[indices], axis=1), [2.5, 97.5]) + else: + delta = 1.57 * (q3 - q1) / np.sqrt(len(group)) + cilo, cihi = data_median - delta, data_median + delta + stats.append( + { + "q1": float(q1), + "med": float(med), + "q3": float(q3), + "whislo": whislo, + "whishi": whishi, + "fliers": fliers, + "mean": float(np.mean(group)), + "cilo": float(cilo), + "cihi": float(cihi), + } + ) + if sym == "": + showfliers = False # matplotlib: an empty sym string suppresses fliers + elif sym is not None: + sym_color, _sym_line, sym_marker = parse_fmt(str(sym)) + overrides: dict[str, Any] = {} + if sym_marker is not None: + overrides["marker"] = sym_marker + if sym_color is not None: + overrides["color"] = sym_color + flierprops = {**(flierprops or {}), **overrides} + result = self.bxp( + stats, + positions=positions, + widths=widths, + orientation=orientation, + patch_artist=bool(patch_artist), + shownotches=bool(notch), + showmeans=bool(showmeans), + showcaps=True if showcaps is None else bool(showcaps), + showbox=True if showbox is None else bool(showbox), + showfliers=True if showfliers is None else bool(showfliers), + boxprops=boxprops, + whiskerprops=whiskerprops, + flierprops=flierprops, + medianprops=medianprops, + capprops=capprops, + meanprops=meanprops, + meanline=bool(meanline), + manage_ticks=manage_ticks, + zorder=zorder, + capwidths=capwidths, + label=label, + ) + if tick_labels is not None: + centers = np.arange(1, count + 1) if positions is None else positions + (self.set_xticks if orientation == "vertical" else self.set_yticks)( + centers, tick_labels + ) + return result def violinplot( self, @@ -1457,62 +1802,137 @@ def violinplot( linecolor: Any = None, data: Any = None, ) -> dict[str, Any]: - del showextrema, linecolor if vert is not None: orientation = "vertical" if vert else "horizontal" - del bw_method, side values = _from_data(dataset, data) - entry = self._add( - "@mark", - { - "factory": "violin", - "args": (values,), - "kwargs": { - "x": positions, - "color": resolve_color(facecolor) - if facecolor is not None - else self._next_color(), - "width": float(widths), - "bins": max(4, min(1024, int(points))), - "orientation": orientation, + if ( + isinstance(values, (list, tuple)) + and values + and all(np.ndim(value) == 1 for value in values) + ): + groups = [np.asarray(group, dtype=np.float64) for group in values] + else: + array = np.asarray(values, dtype=np.float64) + groups = ( + [array] + if array.ndim == 1 + else [np.asarray(array[:, index]) for index in range(array.shape[1])] + ) + if bw_method is None and side == "both" and quantiles is None: + entry = self._add( + "@mark", + { + "factory": "violin", + "args": (values,), + "kwargs": { + "x": positions, + "color": resolve_color(facecolor) + if facecolor is not None + else self._next_color(), + "width": float(widths), + "bins": max(4, min(1024, int(points))), + "orientation": orientation, + }, }, - }, - ) - result: dict[str, Any] = {"bodies": [Artist(self, entry)]} - groups = ( - [np.asarray(values, dtype=np.float64)] - if np.asarray(values).ndim == 1 - else [np.asarray(group, dtype=np.float64) for group in values] - ) - centers = np.arange(1, len(groups) + 1) if positions is None else np.asarray(positions) - if showmeans: - means = [float(np.nanmean(group)) for group in groups] - result["cmeans"] = ( - self.hlines(means, centers - widths * 0.2, centers + widths * 0.2) - if orientation == "vertical" - else self.vlines(means, centers - widths * 0.2, centers + widths * 0.2) ) - if showmedians: - medians = [float(np.nanmedian(group)) for group in groups] - result["cmedians"] = ( - self.hlines(medians, centers - widths * 0.2, centers + widths * 0.2) - if orientation == "vertical" - else self.vlines(medians, centers - widths * 0.2, centers + widths * 0.2) + result: dict[str, Any] = {"bodies": [Artist(self, entry)]} + centers = np.arange(1, len(groups) + 1) if positions is None else np.asarray(positions) + extrema_color = linecolor if linecolor is not None else "#222222" + minima = np.asarray([np.nanmin(group) for group in groups]) + maxima = np.asarray([np.nanmax(group) for group in groups]) + if showextrema: + if orientation == "vertical": + result["cbars"] = self.vlines(centers, minima, maxima, colors=extrema_color) + result["cmins"] = self.hlines( + minima, centers - widths * 0.2, centers + widths * 0.2, colors=extrema_color + ) + result["cmaxes"] = self.hlines( + maxima, centers - widths * 0.2, centers + widths * 0.2, colors=extrema_color + ) + else: + result["cbars"] = self.hlines(centers, minima, maxima, colors=extrema_color) + result["cmins"] = self.vlines( + minima, centers - widths * 0.2, centers + widths * 0.2, colors=extrema_color + ) + result["cmaxes"] = self.vlines( + maxima, centers - widths * 0.2, centers + widths * 0.2, colors=extrema_color + ) + if showmeans: + made = np.asarray([np.nanmean(group) for group in groups]) + result["cmeans"] = ( + self.hlines(made, centers - widths * 0.2, centers + widths * 0.2) + if orientation == "vertical" + else self.vlines(made, centers - widths * 0.2, centers + widths * 0.2) + ) + if showmedians: + made = np.asarray([np.nanmedian(group) for group in groups]) + result["cmedians"] = ( + self.hlines(made, centers - widths * 0.2, centers + widths * 0.2) + if orientation == "vertical" + else self.vlines(made, centers - widths * 0.2, centers + widths * 0.2) + ) + return result + if points < 2: + raise ValueError("violinplot points must be at least 2") + vpstats: list[dict[str, Any]] = [] + for group in groups: + group = group[np.isfinite(group)] + if len(group) == 0: + raise ValueError("violinplot groups must each contain a finite value") + coords = np.linspace(float(np.min(group)), float(np.max(group)), int(points)) + std = float(np.std(group, ddof=1)) if len(group) > 1 else 0.0 + if callable(bw_method): + + class _KDECarrier: + dataset = group[None, :] + n, d = len(group), 1 + + def scotts_factor(self) -> float: + return self.n ** (-1 / 5) + + def silverman_factor(self) -> float: + return (self.n * 3 / 4) ** (-1 / 5) + + factor = float(bw_method(_KDECarrier())) + elif bw_method in (None, "scott"): + factor = len(group) ** (-1 / 5) + elif bw_method == "silverman": + factor = (len(group) * 3 / 4) ** (-1 / 5) + elif np.isscalar(bw_method): + factor = _float(bw_method) + else: + raise ValueError("bw_method must be 'scott', 'silverman', a scalar, or callable") + bandwidth = max(std * factor, np.finfo(float).eps) + delta = (coords[:, None] - group[None, :]) / bandwidth + density = np.mean(np.exp(-0.5 * delta * delta), axis=1) / ( + bandwidth * np.sqrt(2 * np.pi) ) + item: dict[str, Any] = { + "coords": coords, + "vals": density, + "mean": float(np.mean(group)), + "median": float(np.median(group)), + "min": float(np.min(group)), + "max": float(np.max(group)), + } + vpstats.append(item) if quantiles is not None: - quantile_values: list[float] = [] - quantile_positions: list[float] = [] - for center, group, requested in zip(centers, groups, quantiles, strict=True): - made = np.quantile(group[np.isfinite(group)], requested) - quantile_values.extend(np.asarray(made).reshape(-1)) - quantile_positions.extend([float(center)] * np.asarray(made).size) - qv, qp = np.asarray(quantile_values), np.asarray(quantile_positions) - result["cquantiles"] = ( - self.hlines(qv, qp - widths * 0.2, qp + widths * 0.2) - if orientation == "vertical" - else self.vlines(qv, qp - widths * 0.2, qp + widths * 0.2) - ) - return result + if len(quantiles) != len(groups): + raise ValueError("quantiles must contain one sequence per violin") + for item, group, requested in zip(vpstats, groups, quantiles, strict=True): + item["quantiles"] = np.quantile(group[np.isfinite(group)], requested) + return self.violin( + vpstats, + positions=positions, + orientation=orientation, + widths=widths, + showmeans=showmeans, + showextrema=showextrema, + showmedians=showmedians, + side=side, + facecolor=facecolor, + linecolor=linecolor, + ) def errorbar( self, @@ -1536,8 +1956,15 @@ def errorbar( data: Any = None, **kwargs: Any, ) -> ErrorbarContainer: - del barsabove, capthick, elinestyle - del lolims, uplims, xlolims, xuplims + unsupported = { + "barsabove": True if barsabove else None, + "capthick": capthick, + "elinestyle": elinestyle, + } + check_unsupported( + {name: value for name, value in unsupported.items() if value is not None}, + "errorbar()", + ) x, y = _from_data(x, data), _from_data(y, data) yerr, xerr = _from_data(yerr, data), _from_data(xerr, data) if errorevery != 1: @@ -1556,6 +1983,14 @@ def subset_error(error: Any) -> Any: return arr[..., selection] yerr, xerr = subset_error(yerr), subset_error(xerr) + + def subset_limit(flag: Any) -> Any: + return flag if np.isscalar(flag) else np.asarray(flag)[selection] + + lolims, uplims = subset_limit(lolims), subset_limit(uplims) + xlolims, xuplims = subset_limit(xlolims), subset_limit(xuplims) + yerr = _limit_error(yerr, lolims, uplims, len(np.asarray(y))) + xerr = _limit_error(xerr, xlolims, xuplims, len(np.asarray(x))) base = line_kwargs(kwargs) check_unsupported(kwargs, "errorbar()") color = ( @@ -1616,12 +2051,12 @@ def hexbin( data: Any = None, **kwargs: Any, ) -> PathCollection: - del linewidths, edgecolors - if C is not None: - raise not_implemented("hexbin(C=..., reduce_C_function=...)") + if linewidths is not None: + raise not_implemented("hexbin(linewidths=...)") + if edgecolors not in (None, "face"): + raise not_implemented("hexbin(edgecolors=...)") unsupported_options = { "norm": norm, - "mincnt": mincnt, "marginals": True if marginals else None, "colorizer": colorizer, "vmin": vmin, @@ -1631,8 +2066,6 @@ def hexbin( {name: value for name, value in unsupported_options.items() if value is not None}, "hexbin()", ) - if reduce_C_function is not np.mean: - raise not_implemented("hexbin(reduce_C_function=...) without C") check_unsupported(kwargs, "hexbin()") x, y = _from_data(x, data), _from_data(y, data) if xscale != "linear": @@ -1655,6 +2088,9 @@ def hexbin( "gridsize": gridsize, "range": data_range, "bins": mode, + "C": None if C is None else _from_data(C, data), + "reduce_C_function": reduce_C_function, + "mincnt": mincnt, "colormap": resolve_cmap(cmap) if cmap is not None else "viridis", "opacity": 0.9 if alpha is None else float(alpha), }, @@ -1698,15 +2134,23 @@ def _contour(self, filled: bool, args: tuple[Any, ...], kwargs: dict[str, Any]) colors = kwargs.pop("colors", None) linewidths = kwargs.pop("linewidths", None) alpha = kwargs.pop("alpha", None) - kwargs.pop("origin", None) - kwargs.pop("extent", None) + if kwargs.pop("origin", None) is not None: + raise not_implemented("contour(origin=...)") + extent = kwargs.pop("extent", None) norm = kwargs.pop("norm", None) - kwargs.pop("linestyles", None) - kwargs.pop("corner_mask", None) + if kwargs.pop("linestyles", None) is not None: + raise not_implemented("contour(linestyles=...)") + _reject_non_default("contour", "corner_mask", kwargs.pop("corner_mask", None), True) extend = kwargs.pop("extend", None) hatches = kwargs.pop("hatches", None) locator = kwargs.pop("locator", None) za = np.asarray(z, dtype=np.float64) + if extent is not None and x is None and za.ndim == 2: + # origin=None semantics: extent gives the positions of Z[0, 0] and + # Z[-1, -1]; extent is documented to be ignored when X/Y are given. + x0_extent, x1_extent, y0_extent, y1_extent = map(float, extent) + x = np.linspace(x0_extent, x1_extent, za.shape[1]) + y = np.linspace(y0_extent, y1_extent, za.shape[0]) if np.isscalar(levels): finite = za[np.isfinite(za)] if locator is not None and "LogLocator" in type(locator).__name__: @@ -1718,7 +2162,8 @@ def _contour(self, filled: bool, args: tuple[Any, ...], kwargs: dict[str, Any]) high_power = int(np.ceil(np.log(positive.max()) / np.log(base))) levels = base ** np.arange(low_power, high_power + 1, dtype=np.float64) else: - levels = _nice_contour_levels(float(finite.min()), float(finite.max()), int(levels)) + count = int(np.asarray(levels, dtype=np.float64).item()) + levels = _nice_contour_levels(float(finite.min()), float(finite.max()), count) public_levels = np.asarray(levels, dtype=np.float64) rendered_z = z rendered_levels = public_levels @@ -1856,12 +2301,10 @@ def stroke( return ContourSet(self, entry) def contour(self, *args: Any, data: Any = None, **kwargs: Any) -> ContourSet: - del data - return self._contour(False, args, kwargs) + return self._contour(False, tuple(_from_data(value, data) for value in args), kwargs) def contourf(self, *args: Any, data: Any = None, **kwargs: Any) -> ContourSet: - del data - return self._contour(True, args, kwargs) + return self._contour(True, tuple(_from_data(value, data) for value in args), kwargs) def clabel( self, @@ -1879,7 +2322,14 @@ def clabel( zorder: Any = None, ) -> list[Text]: """Label contour levels without exposing contour semantics to core.""" - del fontsize, inline, inline_spacing, use_clabeltext, rightside_up, zorder + del ( + fontsize, + inline, + inline_spacing, + use_clabeltext, + rightside_up, + zorder, + ) # compat-noop: deterministic shim contour-label placement and styling chosen = np.asarray(CS.levels if levels is None else levels, dtype=np.float64).reshape(-1) if isinstance(manual, (list, tuple, np.ndarray)) and len(manual): locations = list(manual) @@ -1976,7 +2426,8 @@ def bxp( label: Any = None, ) -> dict[str, list[Artist]]: """Draw exact precomputed box geometry with generic segment/scatter marks.""" - del patch_artist, shownotches, manage_ticks, zorder + if patch_artist: + raise not_implemented("bxp(patch_artist=True)") stats = list(bxpstats) count = len(stats) if vert is not None: @@ -2003,7 +2454,11 @@ def style(props: Any, fallback: Any = None) -> dict[str, Any]: color = source.pop("color", source.pop("edgecolor", fallback)) width = source.pop("linewidth", source.pop("lw", 1.2)) alpha = source.pop("alpha", 1.0) - source.pop("linestyle", source.pop("ls", None)) + linestyle = source.pop("linestyle", source.pop("ls", None)) + if linestyle not in (None, "-", "solid"): + raise not_implemented( + "bxp component linestyle", "solid component lines with color/width/alpha" + ) check_unsupported(source, "bxp component properties") return { "color": resolve_color(color) if color is not None else fallback, @@ -2045,14 +2500,33 @@ def emit(coords: list[tuple[float, float, float, float]], props: Any) -> list[Ar med = float(item["med"]) low, high = float(item["whislo"]), float(item["whishi"]) if orientation == "vertical": - box_segments.extend( - [ - (center - half, q1, center + half, q1), - (center + half, q1, center + half, q3), - (center + half, q3, center - half, q3), - (center - half, q3, center - half, q1), - ] - ) + if shownotches: + cilo = float(item.get("cilo", med)) + cihi = float(item.get("cihi", med)) + notch_half = half * 0.5 + box_segments.extend( + [ + (center - half, q1, center + half, q1), + (center + half, q1, center + half, cilo), + (center + half, cilo, center + notch_half, med), + (center + notch_half, med, center + half, cihi), + (center + half, cihi, center + half, q3), + (center + half, q3, center - half, q3), + (center - half, q3, center - half, cihi), + (center - half, cihi, center - notch_half, med), + (center - notch_half, med, center - half, cilo), + (center - half, cilo, center - half, q1), + ] + ) + else: + box_segments.extend( + [ + (center - half, q1, center + half, q1), + (center + half, q1, center + half, q3), + (center + half, q3, center - half, q3), + (center - half, q3, center - half, q1), + ] + ) median_segments.append((center - half, med, center + half, med)) whisker_segments.extend([(center, low, center, q1), (center, q3, center, high)]) cap_segments.extend( @@ -2071,14 +2545,33 @@ def emit(coords: list[tuple[float, float, float, float]], props: Any) -> list[Ar else (center, mean, center, mean) ) else: - box_segments.extend( - [ - (q1, center - half, q1, center + half), - (q1, center + half, q3, center + half), - (q3, center + half, q3, center - half), - (q3, center - half, q1, center - half), - ] - ) + if shownotches: + cilo = float(item.get("cilo", med)) + cihi = float(item.get("cihi", med)) + notch_half = half * 0.5 + box_segments.extend( + [ + (q1, center - half, q1, center + half), + (q1, center + half, cilo, center + half), + (cilo, center + half, med, center + notch_half), + (med, center + notch_half, cihi, center + half), + (cihi, center + half, q3, center + half), + (q3, center + half, q3, center - half), + (q3, center - half, cihi, center - half), + (cihi, center - half, med, center - notch_half), + (med, center - notch_half, cilo, center - half), + (cilo, center - half, q1, center - half), + ] + ) + else: + box_segments.extend( + [ + (q1, center - half, q1, center + half), + (q1, center + half, q3, center + half), + (q3, center + half, q3, center - half), + (q3, center - half, q1, center - half), + ] + ) median_segments.append((med, center - half, med, center + half)) whisker_segments.extend([(low, center, q1, center), (q3, center, high, center)]) cap_segments.extend( @@ -2109,8 +2602,13 @@ def emit(coords: list[tuple[float, float, float, float]], props: Any) -> list[Ar color = source.pop("color", default_color) marker = source.pop("marker", "o") size = source.pop("markersize", source.pop("ms", 5.0)) - source.pop("markerfacecolor", None) - source.pop("markeredgecolor", None) + # single-color flier dots: face color wins, else the edge color + facecolor = source.pop("markerfacecolor", source.pop("mfc", None)) + edgecolor = source.pop("markeredgecolor", source.pop("mec", None)) + if facecolor is not None: + color = facecolor + elif edgecolor is not None: + color = edgecolor check_unsupported(source, "bxp(flierprops=)") entry = self._add( "scatter", @@ -2120,13 +2618,19 @@ def emit(coords: list[tuple[float, float, float, float]], props: Any) -> list[Ar "kwargs": { "color": resolve_color(color), "size": float(size), - "symbol": "circle" if marker == "o" else "square", + "symbol": MARKER_TO_SYMBOL.get(marker or "o", "circle"), }, }, ) result["fliers"] = [Artist(self, entry)] if label is not None and result["medians"]: result["medians"][0].set_label(str(label)) + if zorder is not None: + for artists in result.values(): + for artist in artists: + artist.set_zorder(float(zorder)) + if manage_ticks: + (self.set_xticks if orientation == "vertical" else self.set_yticks)(pos) return result def violin( @@ -2189,16 +2693,34 @@ def violin( else: polygon_x = np.concatenate((coords, coords[::-1])) polygon_y = np.concatenate((low, high[::-1])) - topology = kernels.polygon_triangles(polygon_x, polygon_y) - x0, y0, x1, y1, x2, y2, _ = kernels.indexed_triangles(polygon_x, polygon_y, topology) - entry = self._add( - "@mark", - { - "factory": "triangle_mesh", - "args": (x0, y0, x1, y1, x2, y2), - "kwargs": {"color": body_color, "opacity": 0.8}, - }, - ) + if float(np.ptp(coords)) == 0.0 or peak == 0: + # constant data: matplotlib draws a zero-area body; emit an + # invisible placeholder instead of a degenerate mesh + entry = self._add( + "area", + { + "x": [center, center], + "y": [np.nan, np.nan], + "kwargs": { + "base": [np.nan, np.nan], + "color": body_color, + "opacity": 0.0, + }, + }, + ) + else: + topology = kernels.polygon_triangles(polygon_x, polygon_y) + x0, y0, x1, y1, x2, y2, _ = kernels.indexed_triangles( + polygon_x, polygon_y, topology + ) + entry = self._add( + "@mark", + { + "factory": "triangle_mesh", + "args": (x0, y0, x1, y1, x2, y2), + "kwargs": {"color": body_color, "opacity": 0.8}, + }, + ) bodies.append(Artist(self, entry)) half = width_values[index] * 0.25 @@ -2377,7 +2899,6 @@ def eventplot( **kwargs: Any, ) -> list[PolyCollection]: check_unsupported(kwargs, "eventplot()") - del linestyles source = _from_data(positions, data) try: arr = np.asarray(source) @@ -2394,37 +2915,58 @@ def eventplot( widths = _sequence_param( 1.5 if linewidths is None else linewidths, len(groups), "linewidths" ) + styles = _sequence_param(linestyles, len(groups), "linestyles") palette = PROP_CYCLE if colors is None else _sequence_param(colors, len(groups), "colors") if colors is None: palette = [self._next_color() for _ in groups] result: list[PolyCollection] = [] - for group, offset, length, width, color in zip( - groups, offsets, lengths, widths, palette, strict=True + for group, offset, length, width, color, style in zip( + groups, offsets, lengths, widths, palette, styles, strict=True ): values = np.asarray(group, dtype=np.float64) fixed = np.full(len(values), float(offset), dtype=np.float64) + half = float(length) * 0.5 if orientation == "horizontal": x, y = values, fixed - err_kwargs = {"yerr": float(length) * 0.5} + err_kwargs = {"yerr": half} elif orientation == "vertical": x, y = fixed, values - err_kwargs = {"xerr": float(length) * 0.5} + err_kwargs = {"xerr": half} else: raise ValueError("eventplot orientation must be 'horizontal' or 'vertical'") - entry = self._add( - "@mark", - { - "factory": "errorbar", - "args": (x, y), - "kwargs": { - **err_kwargs, - "cap_size": 0.0, - "color": resolve_color(color), - "width": float(width), - "opacity": 1.0 if alpha is None else float(alpha), + pattern = _dash_segment_pattern("eventplot", style) + if pattern is None: + entry = self._add( + "@mark", + { + "factory": "errorbar", + "args": (x, y), + "kwargs": { + **err_kwargs, + "cap_size": 0.0, + "color": resolve_color(color), + "width": float(width), + "opacity": 1.0 if alpha is None else float(alpha), + }, }, - }, - ) + ) + else: + if orientation == "horizontal": + ticks = (values, fixed - half, values, fixed + half) + else: + ticks = (fixed - half, values, fixed + half, values) + entry = self._add( + "@mark", + { + "factory": "segments", + "args": _dashed_segments(*ticks, pattern), + "kwargs": { + "color": resolve_color(color), + "width": float(width), + "opacity": 1.0 if alpha is None else float(alpha), + }, + }, + ) result.append(PolyCollection(self, entry)) return result @@ -2466,8 +3008,12 @@ def stackplot( color_values = [raw_colors[i % len(raw_colors)] for i in range(values.shape[0])] alpha = kwargs.pop("alpha", None) linewidth = kwargs.pop("linewidth", kwargs.pop("lw", None)) - kwargs.pop("edgecolor", None) - kwargs.pop("facecolor", None) + edgecolor = kwargs.pop("edgecolor", None) + facecolor = kwargs.pop("facecolor", None) + if edgecolor is not None: + raise not_implemented("stackplot(edgecolor=...)") + if facecolor is not None: + color_values = [facecolor] * values.shape[0] check_unsupported(kwargs, "stackplot()") result: list[PolyCollection] = [] for row in range(values.shape[0]): @@ -2504,10 +3050,15 @@ def pcolormesh(self, *args: Any, **kwargs: Any) -> PolyCollection: vmin = kwargs.pop("vmin", None) vmax = kwargs.pop("vmax", None) shading = kwargs.pop("shading", None) - kwargs.pop("antialiased", None) + _reject_non_default("pcolormesh", "antialiased", kwargs.pop("antialiased", None), True) edgecolors = kwargs.pop("edgecolors", kwargs.pop("edgecolor", None)) linewidth = kwargs.pop("linewidth", kwargs.pop("linewidths", None)) norm = kwargs.pop("norm", None) + if norm is not None and type(norm).__name__ != "Normalize": + # Only the linear Normalize maps onto the engine's domain contract. + raise not_implemented( + f"pcolormesh(norm={type(norm).__name__})", alternative="vmin=/vmax=" + ) if shading not in (None, "auto", "flat", "nearest", "gouraud"): raise ValueError(f"invalid pcolormesh shading {shading!r}") check_unsupported(kwargs, "pcolormesh()") @@ -2531,29 +3082,11 @@ def pcolormesh(self, *args: Any, **kwargs: Any) -> PolyCollection: } if domain is not None: mark_kwargs["domain"] = domain - rendered_z = z - if norm is not None and callable(norm): - mapped = np.ma.asarray(norm(z), dtype=np.float64) - cmap_callable = cmap if callable(cmap) else None - if cmap_callable is None: - try: - mpl_colormaps = __import__("matplotlib", fromlist=["colormaps"]).colormaps - - cmap_callable = mpl_colormaps.get_cmap(cmap or "viridis") - except (ImportError, ValueError): - from ._colors import Cmap - - cmap_callable = Cmap(cmap or "viridis") - rendered_z = np.asarray(cmap_callable(mapped), dtype=np.float64) - mask = np.ma.getmaskarray(mapped) | ~np.isfinite(z) - if rendered_z.shape[-1] == 3: - rendered_z = np.dstack((rendered_z, np.ones(z.shape, dtype=np.float64))) - rendered_z[..., 3] = np.where(mask, 0.0, rendered_z[..., 3]) entry = self._add( "@mark", { "factory": "heatmap", - "args": (rendered_z,), + "args": (z,), "kwargs": mark_kwargs, "source_z": z, }, @@ -2570,20 +3103,8 @@ def pcolormesh(self, *args: Any, **kwargs: Any) -> PolyCollection: x0, y0, x1, y1, x2, y2, scalar = ( values[finite_triangles] for values in (x0, y0, x1, y1, x2, y2, scalar) ) - source_scalar = scalar - rendered_scalar = scalar - source_domain = domain - if norm is not None and callable(norm): - rendered_scalar = np.ma.asarray(norm(scalar), dtype=np.float64).filled(np.nan) - source_vmin = getattr(norm, "vmin", None) - source_vmax = getattr(norm, "vmax", None) - source_domain = ( - float(np.nanmin(source_scalar) if source_vmin is None else source_vmin), - float(np.nanmax(source_scalar) if source_vmax is None else source_vmax), - ) - domain = (0.0, 1.0) - mark_kwargs: dict[str, Any] = { - "color": rendered_scalar, + mark_kwargs = { + "color": scalar, "colormap": colormap, "opacity": opacity, } @@ -2601,8 +3122,8 @@ def pcolormesh(self, *args: Any, **kwargs: Any) -> PolyCollection: "factory": "triangle_mesh", "args": (x0, y0, x1, y1, x2, y2), "kwargs": mark_kwargs, - "source_z": source_scalar, - "domain": source_domain, + "source_z": scalar, + "domain": domain, }, ) return PolyCollection(self, entry) @@ -2631,7 +3152,7 @@ def spy( origin: str = "upper", **kwargs: Any, ) -> Any: - del aspect + _reject_non_default("spy", "aspect", aspect, "equal") values = z.toarray() if hasattr(z, "toarray") else np.asarray(z) threshold = 0.0 if precision in (None, "present") else float(precision) present = np.abs(np.asarray(values, dtype=np.float64)) > threshold @@ -2685,7 +3206,11 @@ def pie( *, data: Any = None, ) -> Any: - del shadow, frame, rotatelabels, hatch + _reject_non_default("pie", "shadow", shadow, False) + _reject_non_default("pie", "frame", frame, False) + _reject_non_default("pie", "rotatelabels", rotatelabels, False) + if hatch is not None: + raise not_implemented("pie(hatch=...)") values = np.asarray(_from_data(x, data), dtype=np.float64) if values.ndim != 1 or len(values) == 0: raise ValueError("pie x must be a non-empty 1-D array") @@ -2711,7 +3236,8 @@ def pie( edgecolor = wedge_style.pop("edgecolor", wedge_style.pop("ec", None)) linewidth = wedge_style.pop("linewidth", wedge_style.pop("lw", None)) alpha = wedge_style.pop("alpha", None) - wedge_style.pop("hatch", None) + if wedge_style.pop("hatch", None) is not None: + raise not_implemented("pie(wedgeprops={'hatch': ...})") if wedge_style: check_unsupported(wedge_style, "pie(wedgeprops=)") inner_radius = 0.0 if width is None else max(0.0, float(radius) - float(width)) @@ -2766,13 +3292,7 @@ def pie( wedges.append(Wedge(self, entry)) angle = np.deg2rad(float(startangle)) - text_style = dict(textprops or {}) - text_color = text_style.pop("color", None) - text_style.pop("fontsize", None) - text_style.pop("ha", None) - text_style.pop("va", None) - if text_style: - check_unsupported(text_style, "pie(textprops=)") + text_kwargs = _textprops_kwargs(textprops, "pie(textprops=)") def add_text(distance: float, mid: float, value: str, offset: float) -> Text: local_center_x = float(center[0]) + offset * float(radius) * np.cos(mid) @@ -2785,9 +3305,7 @@ def add_text(distance: float, mid: float, value: str, offset: float) -> Text: local_center_y + distance * float(radius) * np.sin(mid), value, ), - "kwargs": {"color": resolve_color(text_color)} - if text_color is not None - else {}, + "kwargs": dict(text_kwargs), }, ) return Text(self, entry) @@ -2828,7 +3346,7 @@ def pie_label( rotate: bool = False, alignment: str = "auto", ) -> list[Text]: - del rotate + _reject_non_default("pie_label", "rotate", rotate, False) if alignment not in ("auto", "center", "outer"): raise ValueError("pie_label alignment must be 'auto', 'center', or 'outer'") if isinstance(labels, str): @@ -2840,12 +3358,7 @@ def pie_label( formatted = list(labels) if len(formatted) != len(container.wedges): raise ValueError("pie_label labels must match the wedge count") - style = dict(textprops or {}) - color = style.pop("color", None) - style.pop("fontsize", None) - style.pop("ha", None) - style.pop("va", None) - check_unsupported(style, "pie_label(textprops=)") + text_kwargs = _textprops_kwargs(textprops, "pie_label(textprops=)") result: list[Text] = [] for wedge, label in zip(container.wedges, formatted, strict=True): entry_data = wedge._entry @@ -2862,7 +3375,7 @@ def pie_label( center_y + radial * np.sin(mid), str(label), ), - "kwargs": {"color": resolve_color(color)} if color is not None else {}, + "kwargs": dict(text_kwargs), }, ) result.append(Text(self, entry)) @@ -2887,7 +3400,10 @@ def table( **kwargs: Any, ) -> Table: """Render an Axes table as generic colored cells, rules, and text.""" - del cellLoc, rowLoc, colLoc, loc + _reject_non_default("table", "cellLoc", cellLoc, "right") + _reject_non_default("table", "rowLoc", rowLoc, "left") + _reject_non_default("table", "colLoc", colLoc, "center") + _reject_non_default("table", "loc", loc, "bottom") if cellText is None: if cellColours is None: raise ValueError("table requires cellText or cellColours") @@ -2994,8 +3510,13 @@ def table( ) artists.append(Artist(self, rule_entry)) text_color = kwargs.pop("color", None) - kwargs.pop("fontsize", None) + fontsize = kwargs.pop("fontsize", None) check_unsupported(kwargs, "table()") + cell_text_kwargs: dict[str, Any] = {} + if text_color is not None: + cell_text_kwargs["color"] = resolve_color(text_color) + if fontsize is not None: + cell_text_kwargs["style"] = {"font_size": float(fontsize)} cells: dict[tuple[int, int], Text] = {} for row in range(rows): display_row = rows - row - 1 @@ -3008,9 +3529,7 @@ def table( (y_edges[display_row] + y_edges[display_row + 1]) * 0.5, str(raw_text[row][col]), ), - "kwargs": {"color": resolve_color(text_color)} - if text_color is not None - else {}, + "kwargs": dict(cell_text_kwargs), }, ) handle = Text(self, entry) @@ -3047,8 +3566,17 @@ def tripcolor( edgecolors = kwargs.pop("edgecolors", kwargs.pop("edgecolor", None)) linewidth = kwargs.pop("linewidth", kwargs.pop("linewidths", None)) label = kwargs.pop("label", None) - kwargs.pop("norm", None) - kwargs.pop("antialiased", None) + norm = kwargs.pop("norm", None) + if norm is not None and type(norm).__name__ != "Normalize": + # Only the linear Normalize maps onto the engine's domain contract. + raise not_implemented( + f"tripcolor(norm={type(norm).__name__})", alternative="vmin=/vmax=" + ) + if vmin is None: + vmin = getattr(norm, "vmin", None) + if vmax is None: + vmax = getattr(norm, "vmax", None) + _reject_non_default("tripcolor", "antialiased", kwargs.pop("antialiased", None), False) check_unsupported(kwargs, "tripcolor()") from xy import kernels @@ -3088,16 +3616,22 @@ def triplot( marker_size = float(kwargs.pop("markersize", kwargs.pop("ms", 6.0))) * (4.0 / 3.0) props = _line_props(self, kwargs) marker = None + dash_value = props.pop("dash", None) if fmt is not None: color_spec, linestyle, marker = parse_fmt(str(fmt)) if color_spec is not None: props["color"] = resolve_color(color_spec) if linestyle is not None: - props["dash"] = LINESTYLE_TO_DASH.get(linestyle) + dash_value = linestyle + if isinstance(dash_value, (list, tuple)): + raise not_implemented("triplot(dashes=...)") + pattern = _dash_segment_pattern("triplot", dash_value) check_unsupported(kwargs, "triplot()") from xy import kernels x0, x1, y0, y1 = kernels.triangle_edges(x, y, topology) + if pattern is not None: + x0, y0, x1, y1 = _dashed_segments(x0, y0, x1, y1, pattern) entry = self._add( "@mark", { @@ -3148,11 +3682,17 @@ def _tricontour( linewidths = kwargs.pop("linewidths", None) alpha = kwargs.pop("alpha", None) label = kwargs.pop("label", None) - kwargs.pop("norm", None) - kwargs.pop("antialiased", None) - kwargs.pop("extend", None) + where = "tricontourf" if filled else "tricontour" + norm = kwargs.pop("norm", None) + if norm is not None and type(norm).__name__ != "Normalize": + # Only the linear Normalize maps onto the engine's domain contract. + raise not_implemented(f"{where}(norm={type(norm).__name__})", alternative="vmin=/vmax=") + # Matplotlib antialiases contour lines but not filled bands by default. + _reject_non_default(where, "antialiased", kwargs.pop("antialiased", None), not filled) + if kwargs.pop("linestyles", None) is not None: + raise not_implemented(f"{where}(linestyles=...)") + _reject_non_default(where, "extend", kwargs.pop("extend", None), "neither") hatches = kwargs.pop("hatches", None) - kwargs.pop("linestyles", None) check_unsupported(kwargs, "tricontour()/tricontourf()") colormap = resolve_cmap(cmap) if cmap is not None else "viridis" transparent_fill = filled and isinstance(colors, str) and colors.lower() == "none" @@ -3161,6 +3701,11 @@ def _tricontour( if domain_lo == domain_hi: padding = abs(domain_lo) * 0.05 or 0.5 domain_lo, domain_hi = domain_lo - padding, domain_hi + padding + norm_vmin, norm_vmax = getattr(norm, "vmin", None), getattr(norm, "vmax", None) + if norm_vmin is not None: + domain_lo = float(norm_vmin) + if norm_vmax is not None: + domain_hi = float(norm_vmax) explicit_color = None if colors is not None: explicit_color = resolve_color( @@ -3307,16 +3852,19 @@ def _vector_field( pivot = kwargs.pop("pivot", "tail") angles = kwargs.pop("angles", "uv") scale_units = kwargs.pop("scale_units", None) - kwargs.pop("units", None) - kwargs.pop("headwidth", None) - kwargs.pop("headlength", None) - kwargs.pop("headaxislength", None) - kwargs.pop("minshaft", None) - kwargs.pop("minlength", None) + _reject_non_default(name, "units", kwargs.pop("units", None), "width") + _reject_non_default(name, "headwidth", kwargs.pop("headwidth", None), 3.0) + _reject_non_default(name, "headlength", kwargs.pop("headlength", None), 5.0) + _reject_non_default(name, "headaxislength", kwargs.pop("headaxislength", None), 4.5) + _reject_non_default(name, "minshaft", kwargs.pop("minshaft", None), 1.0) + _reject_non_default(name, "minlength", kwargs.pop("minlength", None), 1.0) cmap = kwargs.pop("cmap", None) - kwargs.pop("norm", None) - kwargs.pop("clim", None) - kwargs.pop("zorder", None) + if kwargs.pop("norm", None) is not None: + raise not_implemented(f"{name}(norm=...)") + if kwargs.pop("clim", None) is not None: + raise not_implemented(f"{name}(clim=...)") + if kwargs.pop("zorder", None) is not None: + raise not_implemented(f"{name}(zorder=...)") check_unsupported(kwargs, f"{name}()") if not isinstance(angles, str): directions = np.deg2rad(np.asarray(angles, dtype=np.float64).reshape(-1)) @@ -3414,19 +3962,19 @@ def _vector_field( return PolyCollection(self, entry) def quiver(self, *args: Any, data: Any = None, **kwargs: Any) -> PolyCollection: - del data - return self._vector_field(args, kwargs, "quiver") + return self._vector_field( + tuple(_from_data(value, data) for value in args), kwargs, "quiver" + ) def barbs(self, *args: Any, data: Any = None, **kwargs: Any) -> PolyCollection: - del data - kwargs.pop("length", None) - kwargs.pop("fill_empty", None) - kwargs.pop("rounding", None) - kwargs.pop("sizes", None) - kwargs.pop("barbcolor", None) - kwargs.pop("flagcolor", None) - kwargs.pop("barb_increments", None) - kwargs.pop("flip_barb", None) + args = tuple(_from_data(value, data) for value in args) + _reject_non_default("barbs", "length", kwargs.pop("length", None), 7.0) + _reject_non_default("barbs", "fill_empty", kwargs.pop("fill_empty", None), False) + _reject_non_default("barbs", "rounding", kwargs.pop("rounding", None), True) + _reject_non_default("barbs", "flip_barb", kwargs.pop("flip_barb", None), False) + for option in ("sizes", "barbcolor", "flagcolor", "barb_increments"): + if kwargs.pop(option, None) is not None: + raise not_implemented(f"barbs({option}=...)") return self._vector_field(args, kwargs, "barbs") def quiverkey( @@ -3444,8 +3992,10 @@ def quiverkey( labelsep = float(kwargs.pop("labelsep", 0.1)) color = kwargs.pop("color", Q.get_color()) labelcolor = kwargs.pop("labelcolor", None) - kwargs.pop("fontproperties", None) - kwargs.pop("zorder", None) + if kwargs.pop("fontproperties", None) is not None: + raise not_implemented("quiverkey(fontproperties=...)") + if kwargs.pop("zorder", None) is not None: + raise not_implemented("quiverkey(zorder=...)") check_unsupported(kwargs, "quiverkey()") from xy import kernels @@ -3521,7 +4071,32 @@ def streamplot( num_arrows: int = 1, data: Any = None, ) -> StreamplotSet: - del transform, zorder + if transform is not None: + raise not_implemented("streamplot(transform=...)") + if zorder is not None: + raise not_implemented("streamplot(zorder=...)") + _reject_non_default("streamplot", "arrowstyle", arrowstyle, "-|>") + _reject_non_default("streamplot", "minlength", minlength, 0.1) + _reject_non_default("streamplot", "broken_streamlines", broken_streamlines, True) + _reject_non_default( + "streamplot", "integration_max_step_scale", integration_max_step_scale, 1.0 + ) + _reject_non_default( + "streamplot", "integration_max_error_scale", integration_max_error_scale, 1.0 + ) + if norm is not None and type(norm).__name__ != "Normalize": + # Only the linear Normalize maps onto the engine's domain contract. + raise not_implemented( + f"streamplot(norm={type(norm).__name__})", + alternative="a plain Normalize(vmin=..., vmax=...)", + ) + if integration_direction not in ("both", "forward", "backward"): + raise ValueError( + "streamplot integration_direction must be 'both', 'forward', or 'backward'" + ) + num_arrows = int(num_arrows) + if num_arrows < 0: + raise ValueError("streamplot num_arrows must be non-negative") x_values = np.asarray(_from_data(x, data), dtype=np.float64) y_values = np.asarray(_from_data(y, data), dtype=np.float64) u_values = _masked_float(_from_data(u, data)) @@ -3532,50 +4107,14 @@ def streamplot( x_values, y_values = _regular_mesh_axes(x_values, y_values, u_values.shape) if x_values.ndim != 1 or y_values.ndim != 1: raise ValueError("streamplot X and Y must define a regular grid") - source_segments: list[np.ndarray] = [] - mapped_color: Any = None - mapped_width: Any = None - arrow_count = 0 - try: - # The compatibility layer can reuse Matplotlib's well-tested - # integrator without using its renderer. This preserves explicit - # seeds, masks, adaptive controls, and per-segment scalar values; - # the resulting paths still render entirely through xy. - MatplotlibFigure = __import__("matplotlib.figure", fromlist=["Figure"]).Figure - - mpl_ax = MatplotlibFigure().subplots() - mpl_kwargs: dict[str, Any] = { - "density": density, - "linewidth": linewidth, - "color": color, - "cmap": cmap, - "norm": norm, - "arrowsize": arrowsize, - "arrowstyle": arrowstyle, - "minlength": minlength, - "start_points": start_points, - "maxlength": maxlength, - "integration_direction": integration_direction, - "broken_streamlines": broken_streamlines, - "integration_max_step_scale": integration_max_step_scale, - "integration_max_error_scale": integration_max_error_scale, - "num_arrows": num_arrows, - } - mpl_kwargs = {key: value for key, value in mpl_kwargs.items() if value is not None} - mpl_result = mpl_ax.streamplot(x_values, y_values, u_values, v_values, **mpl_kwargs) - source_segments = [ - np.asarray(segment, dtype=np.float64) - for segment in mpl_result.lines.get_segments() - if len(segment) >= 2 - ] - mapped_color = mpl_result.lines.get_array() - mapped_width = np.asarray(mpl_result.lines.get_linewidths(), dtype=np.float64) - arrow_count = len(mpl_result.arrows.get_paths()) - except (ImportError, TypeError, ValueError): + density_value = float(np.max(np.asarray(density, dtype=np.float64))) + max_steps = max(1, min(100_000, int(float(maxlength) * max(u_values.shape) * 8))) + # One dependency-free integrator regardless of environment: the native + # kernel covers default grid seeding; explicit seeds and one-sided + # integration run the same fixed-step scheme in Python. + if start_points is None and integration_direction == "both": from xy import kernels - density_value = float(np.max(np.asarray(density, dtype=np.float64))) - max_steps = max(1, min(100_000, int(float(maxlength) * max(u_values.shape) * 8))) kx0, kx1, ky0, ky1 = kernels.streamlines( x_values, y_values, @@ -3588,17 +4127,42 @@ def streamplot( np.asarray([[sx, sy], [ex, ey]], dtype=np.float64) for sx, ex, sy, ey in zip(kx0, kx1, ky0, ky1, strict=True) ] - arrow_count = max( - 1, min(len(source_segments), int(30 * float(np.max(np.asarray(density))))) + arrow_budget = max(1, min(len(source_segments), int(30 * density_value))) + else: + if start_points is not None: + seeds = np.asarray(start_points, dtype=np.float64) + if seeds.ndim != 2 or seeds.shape[1] != 2: + raise ValueError("streamplot start_points must have shape (n, 2)") + inside = ( + (seeds[:, 0] >= x_values[0]) + & (seeds[:, 0] <= x_values[-1]) + & (seeds[:, 1] >= y_values[0]) + & (seeds[:, 1] <= y_values[-1]) + ) + if not np.all(inside): + raise ValueError("streamplot start_points must lie inside the x/y grid") + else: + rows, cols = u_values.shape + stride = max(1, int(min(rows, cols) / (12.0 * density_value))) + seed_x, seed_y = np.meshgrid(x_values[::stride], y_values[::stride]) + seeds = np.column_stack((seed_x.reshape(-1), seed_y.reshape(-1))) + source_segments = _integrate_streamlines( + x_values, + y_values, + u_values, + v_values, + seeds, + integration_direction, + max_steps, ) + arrow_budget = max(1, len(source_segments)) + arrow_count = num_arrows * arrow_budget x0_values: list[float] = [] y0_values: list[float] = [] x1_values: list[float] = [] y1_values: list[float] = [] - repeats: list[int] = [] for segment in source_segments: - repeats.append(len(segment) - 1) x0_values.extend(segment[:-1, 0]) y0_values.extend(segment[:-1, 1]) x1_values.extend(segment[1:, 0]) @@ -3608,34 +4172,26 @@ def streamplot( (x0_values, y0_values, x1_values, y1_values), ) - if mapped_color is not None: - numeric_color = np.asarray(mapped_color, dtype=np.float64).reshape(-1) - chosen_color: Any = ( - np.repeat(numeric_color, repeats) - if len(numeric_color) == len(repeats) - else np.resize(numeric_color, len(x0)) - ) - elif color is not None and not isinstance(color, str): - numeric_color = np.asarray(color, dtype=np.float64) - chosen_color = np.full(len(x0), float(np.nanmean(numeric_color))) + mid_x = (x0 + x1) * 0.5 + mid_y = (y0 + y1) * 0.5 + # Grid-valued color/linewidth arrays are sampled at segment midpoints, + # so scalar encodings survive without any external integrator. + if color is not None and not isinstance(color, str): + color_grid = _masked_float(color) + if color_grid.shape != u_values.shape: + raise ValueError("streamplot color array must match the U and V grid shape") + chosen_color: Any = _bilinear_grid_sample(x_values, y_values, color_grid, mid_x, mid_y) else: chosen_color = resolve_color(color) if color is not None else self._next_color() - if mapped_width is not None and mapped_width.size > 1: - width_value: Any = ( - np.repeat(mapped_width, repeats) - if len(mapped_width) == len(repeats) - else np.resize(mapped_width, len(x0)) - ) + if linewidth is None: + width_value: Any = 1.2 + elif np.isscalar(linewidth): + width_value = float(np.asarray(linewidth, dtype=np.float64).item()) else: - width_value = ( - float(mapped_width[0]) - if mapped_width is not None and mapped_width.size - else ( - 1.2 - if linewidth is None - else float(np.nanmean(np.asarray(linewidth, dtype=np.float64))) - ) - ) + width_grid = _masked_float(linewidth) + if width_grid.shape != u_values.shape: + raise ValueError("streamplot linewidth array must match the U and V grid shape") + width_value = _bilinear_grid_sample(x_values, y_values, width_grid, mid_x, mid_y) colormap = resolve_cmap(cmap) if cmap is not None else "viridis" color_domain = None if color is not None and not isinstance(color, str): @@ -3646,18 +4202,14 @@ def streamplot( color_domain = (float(norm_lo), float(norm_hi)) elif original_color.size and float(original_color.min()) != float(original_color.max()): color_domain = (float(original_color.min()), float(original_color.max())) - elif not isinstance(chosen_color, str): - finite_color = np.asarray(chosen_color, dtype=np.float64) - finite_color = finite_color[np.isfinite(finite_color)] - if finite_color.size and float(finite_color.min()) != float(finite_color.max()): - color_domain = (float(finite_color.min()), float(finite_color.max())) entries: list[dict[str, Any]] = [] if isinstance(width_value, np.ndarray) and len(width_value) == len(x0): - finite_width = width_value[np.isfinite(width_value)] + width_array = np.asarray(width_value, dtype=np.float64) + finite_width = width_array[np.isfinite(width_array)] if finite_width.size: edges = np.unique(np.quantile(finite_width, np.linspace(0.0, 1.0, 7))) - bins = np.clip(np.digitize(width_value, edges[1:-1]), 0, max(0, len(edges) - 2)) + bins = np.clip(np.digitize(width_array, edges[1:-1]), 0, max(0, len(edges) - 2)) for bin_index in np.unique(bins): keep = bins == bin_index kwargs_for_bin: dict[str, Any] = { @@ -3667,7 +4219,7 @@ def streamplot( else chosen_color ), "colormap": colormap, - "width": float(np.nanmean(width_value[keep])), + "width": float(np.nanmean(width_array[keep])), } if color_domain is not None and not isinstance(chosen_color, str): kwargs_for_bin["domain"] = color_domain @@ -3682,10 +4234,14 @@ def streamplot( ) ) if not entries: + if isinstance(width_value, np.ndarray): + width_scalar = float(np.nanmean(width_value)) if width_value.size else 1.2 + else: + width_scalar = float(width_value) entry_kwargs: dict[str, Any] = { "color": chosen_color, "colormap": colormap, - "width": float(width_value), + "width": width_scalar, } if color_domain is not None and not isinstance(chosen_color, str): entry_kwargs["domain"] = color_domain diff --git a/python/xy/pyplot/_rc.py b/python/xy/pyplot/_rc.py index 5d6034b9..c0d17d9a 100644 --- a/python/xy/pyplot/_rc.py +++ b/python/xy/pyplot/_rc.py @@ -3,6 +3,7 @@ from __future__ import annotations +import contextlib import warnings from typing import Any @@ -10,23 +11,49 @@ class _PropCycle: + def __init__(self, colors: Any = None) -> None: + self._colors = None if colors is None else tuple(str(color) for color in colors) + def by_key(self) -> dict[str, list[str]]: from ._colors import PROP_CYCLE - return {"color": list(PROP_CYCLE)} + return {"color": list(self._colors or PROP_CYCLE)} _DEFAULTS: dict[str, Any] = { "figure.figsize": (6.4, 4.8), # inches, matplotlib default "figure.dpi": 100.0, + "figure.facecolor": "white", "lines.linewidth": 1.5, "lines.markersize": 6.0, "font.size": 10.0, + "font.family": ["sans-serif"], "axes.grid": False, + "grid.color": "#b0b0b0", + "axes.facecolor": "white", + "axes.edgecolor": "black", + "axes.labelcolor": "black", + "axes.labelsize": "medium", "axes.titlesize": "large", + "axes.titlecolor": "auto", + "axes.spines.left": True, + "axes.spines.bottom": True, + "axes.spines.top": False, + "axes.spines.right": False, + "xtick.color": "black", + "ytick.color": "black", + "xtick.labelcolor": "inherit", + "ytick.labelcolor": "inherit", + "xtick.labelsize": "medium", + "ytick.labelsize": "medium", "legend.loc": "best", + "legend.fontsize": "medium", + "legend.facecolor": "inherit", + "legend.edgecolor": "#cccccc", + "legend.frameon": True, "text.usetex": False, "image.cmap": "viridis", + "image.origin": "upper", "axes.prop_cycle": _PropCycle(), } @@ -43,8 +70,33 @@ def __setitem__(self, key: str, value: Any) -> None: f"xy.pyplot ignores rcParams[{key!r}] — see {COMPAT_URL}", stacklevel=2, ) + if key in {"axes.spines.left", "axes.spines.bottom"} and value is not True: + raise NotImplementedError( + f"xy.pyplot cannot hide {key.removeprefix('axes.spines.')} spines independently" + ) + if key in {"axes.spines.top", "axes.spines.right"} and value is not False: + raise NotImplementedError( + f"xy.pyplot does not render {key.removeprefix('axes.spines.')} spines" + ) + if key == "axes.prop_cycle": + by_key = getattr(value, "by_key", None) + colors = by_key().get("color") if by_key is not None else None + if not colors: + raise ValueError("axes.prop_cycle must provide a non-empty color cycle") + if key in {"font.size"}: + value = float(value) + if value <= 0: + raise ValueError(f"{key} must be positive") + if isinstance(value, list): + value = list(value) # never share list defaults; rcdefaults() must stay pristine super().__setitem__(key, value) + def update(self, *args: Any, **kwargs: Any) -> None: # type: ignore[override] + # C-level dict.update skips __setitem__; route every entry point + # (style.use, rc_context, reset) through the same validation. + for key, value in dict(*args, **kwargs).items(): + self[key] = value + def reset(self) -> None: self.clear() self.update(_DEFAULTS) @@ -66,3 +118,19 @@ def rc_figsize_px(figsize: Any = None, dpi: Any = None) -> tuple[int, int]: w_in, h_in = figsize if figsize is not None else rcParams["figure.figsize"] d = float(dpi if dpi is not None else rcParams["figure.dpi"]) return max(1, round(w_in * d)), max(1, round(h_in * d)) + + +def rcdefaults() -> None: + rcParams.reset() + + +@contextlib.contextmanager +def rc_context(rc: dict[str, Any] | None = None): + old = dict(rcParams) + try: + if rc: + rcParams.update(rc) + yield + finally: + rcParams.clear() + rcParams.update(old) diff --git a/python/xy/pyplot/_state.py b/python/xy/pyplot/_state.py index 4847deb3..5a238229 100644 --- a/python/xy/pyplot/_state.py +++ b/python/xy/pyplot/_state.py @@ -9,6 +9,7 @@ from typing import Any, Optional, Union from ._mplfig import Figure +from ._rc import rcParams _figures: dict[int, Figure] = {} _current: Optional[int] = None @@ -29,8 +30,9 @@ def figure( key, figsize=figsize, dpi=dpi, - facecolor=kwargs.get("facecolor"), + facecolor=kwargs.get("facecolor", rcParams["figure.facecolor"]), ) + _figures[key]._label = "" if isinstance(num, int) else str(num) elif figsize is not None or dpi is not None: fig = _figures[key] fig._figsize = figsize or fig._figsize @@ -75,6 +77,23 @@ def close(target: Any = None) -> None: _current = max(_figures) if _figures else None +def fignums() -> list[int]: + return sorted(key for key in _figures if isinstance(key, int)) + + +def fignum_exists(num: Union[int, str]) -> bool: + key = num if isinstance(num, int) else hash(num) + return key in _figures + + +def figlabels() -> list[str]: + return [ + getattr(_figures[key], "_label", "") + for key in sorted(_figures) + if getattr(_figures[key], "_label", "") + ] + + def all_figures() -> list[Figure]: figures = list(_figures.values()) if figures: diff --git a/python/xy/pyplot/_ticker.py b/python/xy/pyplot/_ticker.py new file mode 100644 index 00000000..3428fac8 --- /dev/null +++ b/python/xy/pyplot/_ticker.py @@ -0,0 +1,204 @@ +"""Tick locators/formatters: the matplotlib.ticker subset gallery scripts use. + +Locators own tick *positions* over the axis view interval; formatters own +label text. The Axes applies them at chart-build time, when data limits are +known, so locator-driven axes keep refreshing as data lands — the same +contract as the native tick generator they displace. The math is xy-owned +and approximates matplotlib's locators (documented in the compat table); +positions are exact for Null/Fixed/Multiple, heuristic for MaxN. +""" + +from __future__ import annotations + +from collections.abc import Callable +from typing import Any, Optional + +import numpy as np + +from ._translate import check_unsupported + + +class Locator: + def tick_values(self, vmin: float, vmax: float) -> np.ndarray: + raise NotImplementedError + + def __repr__(self) -> str: + return f"" + + +class AutoLocator(Locator): + """The default: the engine's nice-linear tick generator.""" + + def tick_values(self, vmin: float, vmax: float) -> np.ndarray: + from xy._svg import _linear_ticks + + if not (np.isfinite(vmin) and np.isfinite(vmax)) or vmin == vmax: + return np.asarray([], dtype=float) + return np.asarray(_linear_ticks(float(vmin), float(vmax))[0], dtype=float) + + +class NullLocator(Locator): + def tick_values(self, vmin: float, vmax: float) -> np.ndarray: + return np.asarray([], dtype=float) + + +class FixedLocator(Locator): + def __init__(self, locs: Any, nbins: Optional[int] = None) -> None: + self.locs = np.asarray(locs, dtype=float).reshape(-1) + self._nbins = None if nbins is None else max(1, int(nbins)) + + def tick_values(self, vmin: float, vmax: float) -> np.ndarray: + if self._nbins is None or len(self.locs) <= self._nbins + 1: + return self.locs + step = max(1, len(self.locs) // self._nbins) + return self.locs[::step] + + +class MultipleLocator(Locator): + def __init__(self, base: float = 1.0, offset: float = 0.0) -> None: + self._base = float(base) + self._offset = float(offset) + if not (np.isfinite(self._base) and self._base > 0): + raise ValueError("MultipleLocator base must be positive") + + def tick_values(self, vmin: float, vmax: float) -> np.ndarray: + vmin, vmax = sorted((float(vmin), float(vmax))) + first = np.ceil((vmin - self._offset) / self._base - 1e-9) + last = np.floor((vmax - self._offset) / self._base + 1e-9) + if last < first: + return np.asarray([], dtype=float) + return self._offset + np.arange(first, last + 1) * self._base + + +class MaxNLocator(Locator): + """At most *nbins* intervals on nice step sizes (1, 2, 2.5, 5) × 10^k.""" + + _default_steps = (1.0, 2.0, 2.5, 5.0, 10.0) + + def __init__(self, nbins: Any = 10, **kwargs: Any) -> None: + self._integer = bool(kwargs.pop("integer", False)) + steps = kwargs.pop("steps", None) + kwargs.pop("prune", None) # compat-noop: ticks outside the view never draw + check_unsupported(kwargs, "MaxNLocator()") + if nbins == "auto": + nbins = 9 # matplotlib's density heuristic collapsed to its default + self._nbins = max(1, int(nbins)) + self._steps = ( + tuple(sorted(float(step) for step in steps)) + if steps is not None + else MaxNLocator._default_steps + ) + + def tick_values(self, vmin: float, vmax: float) -> np.ndarray: + vmin, vmax = sorted((float(vmin), float(vmax))) + if not (np.isfinite(vmin) and np.isfinite(vmax)) or vmin == vmax: + return np.asarray([vmin], dtype=float) + raw = (vmax - vmin) / self._nbins + magnitude = 10.0 ** np.floor(np.log10(raw)) + for scale in (magnitude, magnitude * 10.0, magnitude * 100.0): + for step in self._steps: + candidate = step * scale + if self._integer: + candidate = max(1.0, np.round(candidate)) + first = np.ceil(vmin / candidate - 1e-9) + last = np.floor(vmax / candidate + 1e-9) + if last < first or last - first > self._nbins: + continue + return np.arange(first, last + 1) * candidate + return np.asarray([vmin, vmax], dtype=float) + + +class LinearLocator(Locator): + def __init__(self, numticks: Optional[int] = None) -> None: + self._numticks = 11 if numticks is None else max(2, int(numticks)) + + def tick_values(self, vmin: float, vmax: float) -> np.ndarray: + vmin, vmax = sorted((float(vmin), float(vmax))) + return np.linspace(vmin, vmax, self._numticks) + + +class LogLocator(Locator): + def __init__(self, base: float = 10.0, subs: Any = (1.0,), **kwargs: Any) -> None: + kwargs.pop("numticks", None) # compat-noop: every decade tick fits our axes + check_unsupported(kwargs, "LogLocator()") + self._base = float(base) + if self._base <= 1.0: + raise ValueError("LogLocator base must be greater than 1") + self._subs = (1.0,) if subs is None else tuple(float(sub) for sub in subs) + + def tick_values(self, vmin: float, vmax: float) -> np.ndarray: + vmin, vmax = sorted((float(vmin), float(vmax))) + if vmax <= 0: + return np.asarray([], dtype=float) + vmin = max(vmin, np.finfo(float).tiny) + first = np.floor(np.log(vmin) / np.log(self._base)) - 1 + last = np.ceil(np.log(vmax) / np.log(self._base)) + 1 + decades = self._base ** np.arange(first, last + 1) + ticks = np.sort(np.concatenate([decades * sub for sub in self._subs])) + return ticks[(ticks >= vmin) & (ticks <= vmax)] + + +class Formatter: + def __call__(self, value: float, pos: Optional[int] = None) -> str: + raise NotImplementedError + + def __repr__(self) -> str: + return f"" + + +class ScalarFormatter(Formatter): + """The default: the shim's ``%g`` rendering of tick values.""" + + def __call__(self, value: float, pos: Optional[int] = None) -> str: + return f"{value:g}" + + +class NullFormatter(Formatter): + def __call__(self, value: float, pos: Optional[int] = None) -> str: + return "" + + +class FixedFormatter(Formatter): + def __init__(self, seq: Any) -> None: + self.seq = [str(item) for item in seq] + + def __call__(self, value: float, pos: Optional[int] = None) -> str: + index = 0 if pos is None else int(pos) + return self.seq[index] if 0 <= index < len(self.seq) else "" + + +class FuncFormatter(Formatter): + def __init__(self, func: Callable[[float, Optional[int]], Any]) -> None: + if not callable(func): + raise TypeError("FuncFormatter requires a callable(value, pos)") + self._func = func + + def __call__(self, value: float, pos: Optional[int] = None) -> str: + return str(self._func(value, pos)) + + +class FormatStrFormatter(Formatter): + def __init__(self, fmt: str) -> None: + self._fmt = str(fmt) + + def __call__(self, value: float, pos: Optional[int] = None) -> str: + return self._fmt % value + + +class StrMethodFormatter(Formatter): + def __init__(self, fmt: str) -> None: + self._fmt = str(fmt) + + def __call__(self, value: float, pos: Optional[int] = None) -> str: + return self._fmt.format(x=value, pos=pos) + + +def as_formatter(value: Any, where: str) -> Formatter: + """matplotlib's set_major_formatter coercions: Formatter, str, callable.""" + if isinstance(value, Formatter): + return value + if isinstance(value, str): + return StrMethodFormatter(value) + if callable(value): + return FuncFormatter(value) + raise TypeError(f"{where} requires a Formatter, format string, or callable") diff --git a/python/xy/pyplot/_transforms.py b/python/xy/pyplot/_transforms.py new file mode 100644 index 00000000..e695fcee --- /dev/null +++ b/python/xy/pyplot/_transforms.py @@ -0,0 +1,111 @@ +"""Small, dependency-free transform values used by the pyplot shim. + +This is deliberately not a replacement for Matplotlib's transform graph. It +provides the affine and coordinate-space behavior needed by supported pyplot +calls while giving unsupported composition a clear boundary. +""" + +from __future__ import annotations + +from typing import Any + +import numpy as np + + +class Bbox: + def __init__(self, bounds: tuple[float, float, float, float]) -> None: + if len(bounds) != 4: + raise ValueError("Bbox bounds must contain x0, y0, width, height") + self._bounds: tuple[float, float, float, float] = ( + float(bounds[0]), + float(bounds[1]), + float(bounds[2]), + float(bounds[3]), + ) + + @classmethod + def from_bounds(cls, x0: float, y0: float, width: float, height: float) -> "Bbox": + return cls((x0, y0, width, height)) + + @property + def bounds(self) -> tuple[float, float, float, float]: + return self._bounds + + x0 = property(lambda self: self._bounds[0]) + y0 = property(lambda self: self._bounds[1]) + width = property(lambda self: self._bounds[2]) + height = property(lambda self: self._bounds[3]) + x1 = property(lambda self: self.x0 + self.width) + y1 = property(lambda self: self.y0 + self.height) + + def frozen(self) -> "Bbox": + return Bbox(self._bounds) + + +class Affine2D: + """A bounded homogeneous 2-D affine transform.""" + + coordinate_space = "data" + + def __init__(self, matrix: Any = None, *, coordinate_space: str = "data") -> None: + self._matrix = np.eye(3, dtype=float) if matrix is None else np.asarray(matrix, dtype=float) + if self._matrix.shape != (3, 3): + raise ValueError("Affine2D matrix must have shape (3, 3)") + self.coordinate_space = coordinate_space + + def get_matrix(self) -> np.ndarray: + return self._matrix.copy() + + def transform(self, values: Any) -> np.ndarray: + points = np.asarray(values, dtype=float) + scalar = points.ndim == 1 + points = np.atleast_2d(points) + if points.shape[1] != 2: + raise ValueError("transform input must contain x/y pairs") + homogeneous = np.column_stack((points, np.ones(len(points)))) + result = (homogeneous @ self._matrix.T)[:, :2] + return result[0] if scalar else result + + transform_point = transform + + def inverted(self) -> "Affine2D": + return Affine2D(np.linalg.inv(self._matrix), coordinate_space=self.coordinate_space) + + def translate(self, tx: float, ty: float) -> "Affine2D": + operation = np.eye(3) + operation[:2, 2] = (float(tx), float(ty)) + self._matrix = operation @ self._matrix + return self + + def scale(self, sx: float, sy: Any = None) -> "Affine2D": + sy = sx if sy is None else sy + operation = np.diag((float(sx), float(sy), 1.0)) + self._matrix = operation @ self._matrix + return self + + def rotate_deg(self, degrees: float) -> "Affine2D": + angle = np.deg2rad(float(degrees)) + cosine, sine = np.cos(angle), np.sin(angle) + operation = np.asarray([[cosine, -sine, 0], [sine, cosine, 0], [0, 0, 1]]) + self._matrix = operation @ self._matrix + return self + + def __add__(self, other: Any) -> "Affine2D": + if not isinstance(other, Affine2D): + raise TypeError("xy.pyplot only composes affine transforms with affine transforms") + return Affine2D(other._matrix @ self._matrix, coordinate_space=other.coordinate_space) + + +class IdentityTransform(Affine2D): + def __init__(self, *, coordinate_space: str = "data") -> None: + super().__init__(coordinate_space=coordinate_space) + + def inverted(self) -> "IdentityTransform": + return IdentityTransform(coordinate_space=self.coordinate_space) + + +class CoordinateTransform(IdentityTransform): + """Identity-valued token that identifies a renderer coordinate space.""" + + def __init__(self, coordinate_space: str) -> None: + super().__init__(coordinate_space=coordinate_space) diff --git a/python/xy/pyplot/_translate.py b/python/xy/pyplot/_translate.py index 2378b074..2c041dc7 100644 --- a/python/xy/pyplot/_translate.py +++ b/python/xy/pyplot/_translate.py @@ -88,8 +88,12 @@ def line_kwargs(kwargs: dict[str, Any]) -> dict[str, Any]: dashes = kwargs.pop("dashes", None) if dashes is not None: out["dash"] = list(dashes) - kwargs.pop("gapcolor", None) - kwargs.pop("path_effects", None) + gapcolor = kwargs.pop("gapcolor", None) + if gapcolor is not None: + raise not_implemented("Line2D gapcolor") + path_effects = kwargs.pop("path_effects", None) + if path_effects: + raise not_implemented("Line2D path_effects") label = kwargs.pop("label", None) if label is not None: out["name"] = str(label) diff --git a/python/xy/static/index.js b/python/xy/static/index.js index ef013494..b9dc9360 100644 --- a/python/xy/static/index.js +++ b/python/xy/static/index.js @@ -42,6 +42,8 @@ piyg: [[142, 1, 82], [197, 27, 125], [222, 119, 174], [241, 182, 218], [253, 224 purples: [[252, 251, 253], [239, 237, 245], [218, 218, 235], [188, 189, 220], [158, 154, 200], [128, 125, 186], [106, 81, 163], [84, 39, 143], [63, 0, 125]], pubu: [[255, 247, 251], [236, 231, 242], [208, 209, 230], [166, 189, 219], [116, 169, 207], [54, 144, 192], [5, 112, 176], [4, 90, 141], [2, 56, 88]], prgn: [[64, 0, 75], [118, 42, 131], [153, 112, 171], [194, 165, 207], [231, 212, 232], [247, 247, 247], [217, 240, 211], [166, 219, 160], [90, 174, 97], [27, 120, 55], [0, 68, 27]], +rdgy: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [254, 254, 254], [224, 224, 224], [185, 185, 185], [135, 135, 135], [76, 76, 76], [26, 26, 26]], +jet: [[0, 0, 128], [0, 0, 241], [0, 76, 255], [0, 176, 255], [41, 255, 206], [125, 255, 122], [206, 255, 41], [255, 196, 0], [255, 104, 0], [241, 8, 0], [128, 0, 0]], binary: [[255, 255, 255], [0, 0, 0]], }; function colormapStops(name) { @@ -4171,6 +4173,8 @@ ctx.globalAlpha = this._styleNumber(style, "opacity", 1); ctx.strokeStyle = this._annotationPaint(style, [0.4, 0.44, 0.52, 1]); ctx.fillStyle = ctx.strokeStyle; ctx.lineWidth = Math.max(0.5, this._styleNumber(style, "width", 1.5)); +ctx.setLineDash(Array.isArray(style.dash) ? style.dash : +(typeof style.dash === "string" ? style.dash.split(",").map(Number) : [])); ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x1, y1); @@ -4227,6 +4231,8 @@ ctx.save(); ctx.globalAlpha = this._styleNumber(style, "opacity", 1); ctx.strokeStyle = this._annotationPaint(style, [0.4, 0.44, 0.52, 1]); ctx.lineWidth = Math.max(0.5, this._styleNumber(style, "width", 1.5)); +ctx.setLineDash(Array.isArray(style.dash) ? style.dash : +(typeof style.dash === "string" ? style.dash.split(",").map(Number) : [])); ctx.beginPath(); const start = Math.max(0, Math.min(1, Number(style.span_start) || 0)); const rawEnd = style.span_end === undefined ? 1 : Number(style.span_end); diff --git a/python/xy/static/standalone.js b/python/xy/static/standalone.js index 1b4f3435..085a40c5 100644 --- a/python/xy/static/standalone.js +++ b/python/xy/static/standalone.js @@ -43,6 +43,8 @@ piyg: [[142, 1, 82], [197, 27, 125], [222, 119, 174], [241, 182, 218], [253, 224 purples: [[252, 251, 253], [239, 237, 245], [218, 218, 235], [188, 189, 220], [158, 154, 200], [128, 125, 186], [106, 81, 163], [84, 39, 143], [63, 0, 125]], pubu: [[255, 247, 251], [236, 231, 242], [208, 209, 230], [166, 189, 219], [116, 169, 207], [54, 144, 192], [5, 112, 176], [4, 90, 141], [2, 56, 88]], prgn: [[64, 0, 75], [118, 42, 131], [153, 112, 171], [194, 165, 207], [231, 212, 232], [247, 247, 247], [217, 240, 211], [166, 219, 160], [90, 174, 97], [27, 120, 55], [0, 68, 27]], +rdgy: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [254, 254, 254], [224, 224, 224], [185, 185, 185], [135, 135, 135], [76, 76, 76], [26, 26, 26]], +jet: [[0, 0, 128], [0, 0, 241], [0, 76, 255], [0, 176, 255], [41, 255, 206], [125, 255, 122], [206, 255, 41], [255, 196, 0], [255, 104, 0], [241, 8, 0], [128, 0, 0]], binary: [[255, 255, 255], [0, 0, 0]], }; function colormapStops(name) { @@ -4172,6 +4174,8 @@ ctx.globalAlpha = this._styleNumber(style, "opacity", 1); ctx.strokeStyle = this._annotationPaint(style, [0.4, 0.44, 0.52, 1]); ctx.fillStyle = ctx.strokeStyle; ctx.lineWidth = Math.max(0.5, this._styleNumber(style, "width", 1.5)); +ctx.setLineDash(Array.isArray(style.dash) ? style.dash : +(typeof style.dash === "string" ? style.dash.split(",").map(Number) : [])); ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x1, y1); @@ -4228,6 +4232,8 @@ ctx.save(); ctx.globalAlpha = this._styleNumber(style, "opacity", 1); ctx.strokeStyle = this._annotationPaint(style, [0.4, 0.44, 0.52, 1]); ctx.lineWidth = Math.max(0.5, this._styleNumber(style, "width", 1.5)); +ctx.setLineDash(Array.isArray(style.dash) ? style.dash : +(typeof style.dash === "string" ? style.dash.split(",").map(Number) : [])); ctx.beginPath(); const start = Math.max(0, Math.min(1, Number(style.span_start) || 0)); const rawEnd = style.span_end === undefined ? 1 : Number(style.span_end); diff --git a/scripts/sync_matplotlib_compat.py b/scripts/sync_matplotlib_compat.py new file mode 100644 index 00000000..de391974 --- /dev/null +++ b/scripts/sync_matplotlib_compat.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +"""Check the pinned Matplotlib inventory and generate compatibility docs.""" + +from __future__ import annotations + +import argparse +import ast +import json +import re +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +SNAPSHOT = ROOT / "tests/pyplot/matplotlib_311_plotting.json" +METADATA = ROOT / "tests/pyplot/compatibility.json" +CORPUS = ROOT / "tests/pyplot/corpus" +OUTPUT = ROOT / "docs/matplotlib-compat-matrix.md" + + +def _load(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def _upstream_inventory(checkout: Path) -> tuple[dict[str, list[str]], str, str]: + snapshot = _load(SNAPSHOT) + source = checkout / snapshot["upstream"]["source"] + text = source.read_text(encoding="utf-8") + plotting = text.split("\nPlotting\n========\n", 1)[1].split("\nClearing\n========\n", 1)[0] + excluded = set(snapshot["excluded"]) + families: dict[str, list[str]] = {} + family = "" + lines = plotting.splitlines() + for index, line in enumerate(lines): + if index + 1 < len(lines) and re.fullmatch(r"-+", lines[index + 1]): + family = line + families[family] = [] + match = re.fullmatch(r"\s+Axes\.([A-Za-z0-9_]+)", line) + if match and match.group(1) not in excluded: + families[family].append(match.group(1)) + revision = subprocess.run( + ["git", "rev-parse", "--short=10", "HEAD"], + cwd=checkout, + check=True, + capture_output=True, + text=True, + ).stdout.strip() + describe = subprocess.run( + ["git", "describe", "--tags", "--always"], + cwd=checkout, + check=True, + capture_output=True, + text=True, + ).stdout.strip() + return families, revision, describe + + +def _root_name(node: ast.AST) -> str | None: + while isinstance(node, (ast.Attribute, ast.Subscript)): + node = node.value + return node.id if isinstance(node, ast.Name) else None + + +def _axes_receivers(tree: ast.AST) -> set[str]: + """Find names that are demonstrably populated with Axes objects.""" + names = {"ax", "axes"} + for node in ast.walk(tree): + if isinstance(node, (ast.Assign, ast.AnnAssign)): + value = node.value + targets = node.targets if isinstance(node, ast.Assign) else [node.target] + if isinstance(value, ast.Call) and isinstance(value.func, ast.Attribute): + if value.func.attr in { + "subplot", + "gca", + "axes", + "add_subplot", + "add_axes", + "inset_axes", + "twinx", + "twiny", + }: + for target in targets: + if isinstance(target, ast.Name): + names.add(target.id) + if value.func.attr == "subplots": + for target in targets: + if isinstance(target, (ast.Tuple, ast.List)) and len(target.elts) >= 2: + axes_target = target.elts[1] + if isinstance(axes_target, ast.Name): + names.add(axes_target.id) + elif isinstance(node, ast.For) and isinstance(node.target, ast.Name): + if _root_name(node.iter) in names: + names.add(node.target.id) + return names + + +def _corpus_calls() -> dict[str, list[str]]: + calls: dict[str, list[str]] = {} + for path in sorted(CORPUS.glob("[0-9][0-9]_*.py")): + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + axes_names = _axes_receivers(tree) + for node in ast.walk(tree): + if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute): + receiver = _root_name(node.func.value) + if receiver != "plt" and receiver not in axes_names: + continue + names = calls.setdefault(node.func.attr, []) + if path.name not in names: + names.append(path.name) + return calls + + +def _render() -> str: + snapshot = _load(SNAPSHOT) + metadata = _load(METADATA) + calls = _corpus_calls() + lines = [ + "", + "# Matplotlib compatibility matrix", + "", + f"Pinned upstream: `{snapshot['upstream']['describe']}` (`{snapshot['upstream']['revision']}`).", + "", + "The level describes the intended compatibility contract, not pixel identity.", + "Corpus links are executable examples and are checked for every supported method.", + "", + "| Family | Level | Methods | Executable corpus |", + "|---|---|---:|---:|", + ] + for family, methods in snapshot["families"].items(): + examples = sorted({name for method in methods for name in calls.get(method, [])}) + level = metadata["families"][family]["level"] + lines.append(f"| {family} | {level} | {len(methods)} | {len(examples)} |") + lines += ["", "## Method inventory", ""] + for family, methods in snapshot["families"].items(): + level = metadata["families"][family]["level"] + lines += [f"### {family}", "", f"Approximation level: **{level}**.", ""] + for method in methods: + examples = calls.get(method, []) + refs = ", ".join(f"[`{name}`](../tests/pyplot/corpus/{name})" for name in examples) + lines.append(f"- `{method}` — {refs or '**missing executable corpus coverage**'}") + lines.append("") + return "\n".join(lines) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--upstream", type=Path) + parser.add_argument("--check", action="store_true") + parser.add_argument( + "--update-snapshot", + action="store_true", + help="replace the reviewed snapshot from --upstream after moving the pin", + ) + args = parser.parse_args() + snapshot = _load(SNAPSHOT) + errors: list[str] = [] + if args.update_snapshot and not args.upstream: + parser.error("--update-snapshot requires --upstream") + if args.upstream: + families, revision, describe = _upstream_inventory(args.upstream) + if args.update_snapshot: + snapshot["upstream"]["revision"] = revision + snapshot["upstream"]["describe"] = describe + snapshot["families"] = families + SNAPSHOT.write_text(json.dumps(snapshot, indent=2) + "\n", encoding="utf-8") + else: + if revision != snapshot["upstream"]["revision"]: + errors.append( + f"upstream revision is {revision}, expected {snapshot['upstream']['revision']}" + ) + if families != snapshot["families"]: + errors.append("upstream Axes Plotting inventory differs from the reviewed snapshot") + rendered = _render() + if args.check: + if not OUTPUT.exists() or OUTPUT.read_text(encoding="utf-8") != rendered: + errors.append(f"{OUTPUT.relative_to(ROOT)} is stale; run {Path(__file__).name}") + else: + OUTPUT.write_text(rendered, encoding="utf-8") + if errors: + print("\n".join(errors), file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify_ci_workflow.py b/scripts/verify_ci_workflow.py index 28abcf43..3749a439 100644 --- a/scripts/verify_ci_workflow.py +++ b/scripts/verify_ci_workflow.py @@ -21,6 +21,7 @@ DEFAULT_RELEASE_WORKFLOW = ROOT / ".github" / "workflows" / "release.yml" DEFAULT_WORKFLOW = DEFAULT_CI_WORKFLOW REQUIRED_CI_JOBS = { + "matplotlib_reference", "test", "python_floor", "benchmark_vs", @@ -58,6 +59,36 @@ def _missing_needles(block: str, needles: tuple[str, ...]) -> list[str]: return [needle for needle in needles if needle not in block] +def _named_step_blocks(job_text: str) -> dict[str, str]: + """Return step-local blocks; comments elsewhere cannot satisfy a gate.""" + lines = job_text.splitlines() + blocks: dict[str, list[str]] = {} + current: Optional[str] = None + for line in lines: + match = re.match(r"^ - name:\s*(.+?)\s*$", line) + if match: + current = match.group(1) + blocks[current] = [line] + continue + if re.match(r"^ - ", line): + current = None + elif current is not None: + blocks[current].append(line) + return {name: "\n".join(lines) for name, lines in blocks.items()} + + +def _require_step_contains( + errors: list[str], job_text: str, step: str, description: str, *needles: str +) -> None: + block = _named_step_blocks(job_text).get(step) + if block is None: + errors.append(f"missing required CI step {step!r}") + return + missing = _missing_needles(block, needles) + if missing: + errors.append(f"CI step {step!r} missing {description}: {missing}") + + def _require_job_contains( errors: list[str], jobs: dict[str, str], @@ -119,6 +150,46 @@ def validate_ci_workflow(path: Path = DEFAULT_CI_WORKFLOW) -> list[str]: if missing_jobs: errors.append(f"CI workflow missing required jobs: {missing_jobs}") + _require_job_contains( + errors, + jobs, + "matplotlib_reference", + "CI", + "released Matplotlib compatibility gates", + "matplotlib==3.11.0", + "matplotlib.__version__ == '3.11.0'", + "scripts/sync_matplotlib_compat.py --check", + "tests/pyplot/test_launch_compat.py", + "tests/pyplot/test_reference_corpus.py", + "tests/pyplot/test_reference_semantics.py", + "MPLBACKEND: Agg", + ) + reference = jobs.get("matplotlib_reference", "") + _require_step_contains( + errors, + reference, + "Install xy and released reference wheel", + "released reference installation", + 'uv pip install -p .venv/bin/python "matplotlib==3.11.0"', + ) + _require_step_contains( + errors, + reference, + "Verify released reference and reviewed snapshot", + "version and snapshot checks", + "matplotlib.__version__ == '3.11.0'", + "scripts/sync_matplotlib_compat.py --check", + ) + _require_step_contains( + errors, + reference, + "Run optional-interoperability and dual-engine corpus tests", + "reference test commands", + ".venv/bin/pytest -q tests/pyplot/test_launch_compat.py", + ".venv/bin/pytest -q tests/pyplot/test_reference_corpus.py", + ".venv/bin/pytest -q tests/pyplot/test_reference_semantics.py", + ) + _require_job_contains( errors, jobs, diff --git a/tests/pyplot/compatibility.json b/tests/pyplot/compatibility.json new file mode 100644 index 00000000..27848cfb --- /dev/null +++ b/tests/pyplot/compatibility.json @@ -0,0 +1,27 @@ +{ + "schema_version": 1, + "levels": { + "exact geometry": "Data-space geometry and material returned values are intended to match.", + "equivalent semantics": "The same user intent and data result are preserved with an xy-owned return type.", + "visual approximation": "The chart family is rendered, but artist styling or renderer details may differ." + }, + "families": { + "Basic": {"level": "equivalent semantics"}, + "Spans": {"level": "exact geometry"}, + "Spectral": {"level": "equivalent semantics"}, + "Statistics": {"level": "equivalent semantics"}, + "Binned": {"level": "exact geometry"}, + "Contours": {"level": "visual approximation"}, + "2D arrays": {"level": "equivalent semantics"}, + "Unstructured triangles": {"level": "equivalent semantics"}, + "Text and annotations": {"level": "visual approximation"}, + "Vector fields": {"level": "visual approximation"} + }, + "material_keyword_guards": [ + {"method": "plot", "keyword": "linewidth", "values": [1.0, 4.0]}, + {"method": "scatter", "keyword": "s", "values": [9.0, 100.0]}, + {"method": "bar", "keyword": "width", "values": [0.4, 0.8]}, + {"method": "hist", "keyword": "density", "values": [false, true]}, + {"method": "imshow", "keyword": "origin", "values": ["upper", "lower"]} + ] +} diff --git a/tests/pyplot/corpus/53_matplotlib_311_plotting.py b/tests/pyplot/corpus/53_matplotlib_311_plotting.py index 1ba77f97..473f3d92 100644 --- a/tests/pyplot/corpus/53_matplotlib_311_plotting.py +++ b/tests/pyplot/corpus/53_matplotlib_311_plotting.py @@ -25,7 +25,16 @@ axes[1, 0].bxp(stats) coords = np.linspace(-2, 2, 40) axes[1, 0].violin( - [{"coords": coords, "vals": np.exp(-(coords**2)), "min": -2, "max": 2}], + [ + { + "coords": coords, + "vals": np.exp(-(coords**2)), + "mean": 0, + "median": 0, + "min": -2, + "max": 2, + } + ], positions=[3], ) diff --git a/tests/pyplot/corpus/54_plotting_method_coverage.py b/tests/pyplot/corpus/54_plotting_method_coverage.py new file mode 100644 index 00000000..19e200e8 --- /dev/null +++ b/tests/pyplot/corpus/54_plotting_method_coverage.py @@ -0,0 +1,32 @@ +"""Direct calls for plotting methods that were previously covered only by family tests.""" + +import numpy as np + +import xy.pyplot as plt + +fig, axes = plt.subplots(5, 4, figsize=(12, 12)) +axes = axes.ravel() +x = np.linspace(0.1, 2.0, 32) +y = np.sin(x) +z = np.arange(16.0).reshape(4, 4) + +axes[0].semilogx(x, y) +axes[1].semilogy(x, x) +axes[2].stem(x[:5], y[:5]) +axes[3].stairs([1, 3, 2], [0, 1, 2, 3]) +axes[4].eventplot([[0.2, 0.5, 0.9], [0.1, 0.7]]) +axes[5].fill([0, 1, 1], [0, 0, 1]) +axes[6].arrow(0, 0, 1, 1) +axes[7].axline((0, 0), (1, 1)) +axes[8].acorr(y, maxlags=8) +axes[9].angle_spectrum(y) +axes[10].csd(y, y * 0.5, NFFT=16) +axes[11].matshow(z) +axes[12].pcolor(z) +axes[13].pcolorfast(z) +axes[14].pcolormesh(z) +contours = axes[15].contour(z) +axes[15].clabel(contours) +axes[16].contourf(z) +q = axes[17].quiver([0], [0], [1], [1]) +axes[17].quiverkey(q, 0.8, 0.9, 1, "1") diff --git a/tests/pyplot/matplotlib_311_plotting.json b/tests/pyplot/matplotlib_311_plotting.json new file mode 100644 index 00000000..cbc4cba0 --- /dev/null +++ b/tests/pyplot/matplotlib_311_plotting.json @@ -0,0 +1,29 @@ +{ + "schema_version": 1, + "upstream": { + "repository": "https://github.com/matplotlib/matplotlib", + "revision": "bde111fb4e", + "describe": "v3.11.0-348-gbde111fb4e", + "source": "doc/api/axes_api.rst" + }, + "scope": "Axes Plotting section, excluding layout helpers outside xy's 2-D chart-method target", + "excluded": [ + "indicate_inset", + "indicate_inset_zoom", + "inset_axes", + "secondary_xaxis", + "secondary_yaxis" + ], + "families": { + "Basic": ["plot", "errorbar", "scatter", "step", "loglog", "semilogx", "semilogy", "fill_between", "fill_betweenx", "bar", "barh", "bar_label", "grouped_bar", "stem", "eventplot", "pie", "pie_label", "stackplot", "broken_barh", "vlines", "hlines", "fill"], + "Spans": ["axhline", "axhspan", "axvline", "axvspan", "axline"], + "Spectral": ["acorr", "angle_spectrum", "cohere", "csd", "magnitude_spectrum", "phase_spectrum", "psd", "specgram", "xcorr"], + "Statistics": ["ecdf", "boxplot", "violinplot", "bxp", "violin"], + "Binned": ["hexbin", "hist", "hist2d", "stairs"], + "Contours": ["clabel", "contour", "contourf"], + "2D arrays": ["imshow", "matshow", "pcolor", "pcolorfast", "pcolormesh", "spy"], + "Unstructured triangles": ["tripcolor", "triplot", "tricontour", "tricontourf"], + "Text and annotations": ["annotate", "text", "table", "arrow"], + "Vector fields": ["barbs", "quiver", "quiverkey", "streamplot"] + } +} diff --git a/tests/pyplot/test_advanced_compatibility.py b/tests/pyplot/test_advanced_compatibility.py new file mode 100644 index 00000000..731744f9 --- /dev/null +++ b/tests/pyplot/test_advanced_compatibility.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +import numpy as np + +import xy.pyplot as plt +from xy.pyplot._transforms import Affine2D + + +def test_boxplot_notches_bootstrap_overrides_and_custom_whiskers() -> None: + np.random.seed(4) + _, ax = plt.subplots() + result = ax.boxplot( + [[1, 2, 3, 4, 40]], + notch=True, + usermedians=[2.75], + conf_intervals=[[2.25, 3.25]], + whis=(5, 95), + capwidths=0.2, + showmeans=True, + ) + assert all(result[name] for name in ("boxes", "medians", "whiskers", "caps", "means")) + box_segments = result["boxes"][0]._entry["args"] + assert len(box_segments[0]) == 10 # notched outline, not a rectangular approximation + median_segments = result["medians"][0]._entry["args"] + np.testing.assert_allclose(median_segments[1], [2.75]) + + _, bootstrap_ax = plt.subplots() + bootstrapped = bootstrap_ax.boxplot([[1, 2, 3, 4, 40]], notch=True, bootstrap=1) + outline = bootstrapped["boxes"][0]._entry["args"] + assert outline[3][1] == outline[3][3] # one resample gives a collapsed bootstrap CI + + +def test_violin_kde_bandwidth_quantiles_and_sides() -> None: + _, ax = plt.subplots() + result = ax.violinplot( + [[0, 0.5, 1, 2]], + points=41, + bw_method="silverman", + quantiles=[[0.25, 0.75]], + side="low", + ) + assert "cquantiles" in result + body = result["bodies"][0]._entry + x_coordinates = np.concatenate((body["args"][0], body["args"][2], body["args"][4])) + assert np.max(x_coordinates) <= 1.0 # center is position 1; only the low half is drawn + + +def test_hexbin_custom_reducer_is_materialized() -> None: + _, ax = plt.subplots() + ax.hexbin( + [0.0, 0.01, 0.02], + [0.0, 0.01, 0.02], + C=[1.0, 8.0, 3.0], + gridsize=4, + reduce_C_function=np.max, + mincnt=1, + ) + trace = ax._build_chart(640, 480).figure().traces[0] + # every point aggregates, including the one on the domain edge; a mean + # reducer would produce different values, so this discriminates np.max + np.testing.assert_allclose(sorted(trace.color_ch.values), [1.0, 3.0, 8.0]) + + +def test_nonlinear_scales_secondary_axes_and_affine_transforms() -> None: + _, ax = plt.subplots() + line = ax.plot([-10, -1, 0, 1, 10], [0, 1, 2, 3, 4])[0] + ax.set_xscale("symlog", linthresh=1) + adjusted = 1 / (1 - 0.1) + np.testing.assert_allclose( + line.get_xdata(), [-(adjusted + 1), -adjusted, 0, adjusted, adjusted + 1] + ) + assert ax.get_xlim() == (-10, 10) + secondary = ax.secondary_xaxis("top", functions=(lambda x: x * 100, lambda x: x / 100)) + secondary.set_xlabel("percent") + axis = ax._build_chart(640, 480).figure().axis_options["xs1"] + assert axis["side"] == "top" and axis["tick_labels"][-1] == "1000" + + _, transformed = plt.subplots() + line = transformed.plot([0, 1], [0, 1], transform=Affine2D().translate(2, 3))[0] + np.testing.assert_allclose(line.get_xdata(), [2, 3]) + line.set_transform(Affine2D().scale(2)) + np.testing.assert_allclose(line.get_xdata(), [0, 2]) diff --git a/tests/pyplot/test_artist_mutations.py b/tests/pyplot/test_artist_mutations.py new file mode 100644 index 00000000..cff831ae --- /dev/null +++ b/tests/pyplot/test_artist_mutations.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +import numpy as np +import pytest + +import xy.pyplot as plt + + +@pytest.fixture(autouse=True) +def _clean(): + plt.close("all") + yield + plt.close("all") + + +def _traces(ax): + return ax._build_chart(640, 480).figure().traces + + +def test_line2d_marker_style_setters_mutate_marker_overlay() -> None: + _fig, ax = plt.subplots() + (line,) = ax.plot([0, 1], [1, 2], "o-") + + first = ax._build_chart(640, 480) + line.set_markerfacecolor("tab:red") + line.set_markeredgecolor("k") + line.set_markersize(9) + second = ax._build_chart(640, 480) + + assert first is not second + traces = second.figure().traces + assert [trace.kind for trace in traces] == ["line", "scatter"] + assert traces[1].color_ch.constant == "#d62728" + assert traces[1].style["stroke"] == "#000000" + assert traces[1].size_ch.constant == 12.0 + + +def test_marker_only_plot_handle_supports_marker_style_setters() -> None: + _fig, ax = plt.subplots() + (line,) = ax.plot([0, 1], [1, 2], "o") + + line.set_markerfacecolor("tab:green") + line.set_markeredgecolor("none") + line.set_ms(6) + + (trace,) = _traces(ax) + assert trace.kind == "scatter" + assert trace.color_ch.constant == "#2ca02c" + assert "stroke" not in trace.style + assert trace.size_ch.constant == 8.0 + + +def test_segment_backed_line2d_set_ydata_rebuilds_retained_logical_data() -> None: + _fig, ax = plt.subplots() + (line,) = ax.plot([0, 2, 1], [1, 2, 3]) + + line.set_ydata([4, 5, 6]) + + (trace,) = _traces(ax) + assert trace.kind == "segments" + np.testing.assert_array_equal(trace.x0.values, [0, 2]) + np.testing.assert_array_equal(trace.y0.values, [4, 5]) + np.testing.assert_array_equal(trace.x1.values, [2, 1]) + np.testing.assert_array_equal(trace.y1.values, [5, 6]) + np.testing.assert_array_equal(line.get_ydata(), [4, 5, 6]) + + +def test_segment_backed_line2d_set_xdata_rebuilds_retained_logical_data() -> None: + _fig, ax = plt.subplots() + (line,) = ax.plot([0, 2, 1], [1, 2, 3]) + + line.set_xdata([3, 1, 2]) + + (trace,) = _traces(ax) + assert trace.kind == "segments" + np.testing.assert_array_equal(trace.x0.values, [3, 1]) + np.testing.assert_array_equal(trace.y0.values, [1, 2]) + np.testing.assert_array_equal(trace.x1.values, [1, 2]) + np.testing.assert_array_equal(trace.y1.values, [2, 3]) + np.testing.assert_array_equal(line.get_xdata(), [3, 1, 2]) diff --git a/tests/pyplot/test_artist_transform_contracts.py b/tests/pyplot/test_artist_transform_contracts.py new file mode 100644 index 00000000..656df666 --- /dev/null +++ b/tests/pyplot/test_artist_transform_contracts.py @@ -0,0 +1,102 @@ +from __future__ import annotations + +import builtins + +import numpy as np +import pytest + +from xy import pyplot as plt +from xy.pyplot._transforms import Affine2D, Bbox, IdentityTransform + + +def test_owned_artist_views_children_and_removal_are_stable() -> None: + _fig, ax = plt.subplots() + line = ax.plot([0, 1], [1, 2])[0] + points = ax.scatter([0, 1], [2, 3]) + image = ax.imshow([[0, 1], [1, 0]]) + text = ax.text(0.5, 0.5, "hello") + bars = ax.bar([0, 1], [2, 3]) + + assert ax.lines == [line] + assert points in ax.collections + assert ax.images == [image] + assert ax.texts == [text] + assert bars in ax.containers + children = ax.get_children() + assert children == ax.get_children() + assert all(item in children for item in (line, points, image, text, bars)) + + points.remove() + bars.remove() + assert points not in ax.collections + assert bars not in ax.containers + assert points not in ax.get_children() + + +def test_artist_common_properties_apply_or_fail_loudly() -> None: + _fig, ax = plt.subplots() + low, high = ax.plot([0, 1], [0, 1], [0, 1], [1, 0]) + low.set_label("diagonal") + low.set_alpha(0.4) + low.set_visible(False) + assert not low.get_visible() + low.set_visible(True) + assert low.get_visible() and low.get_alpha() == pytest.approx(0.4) + + high.set_zorder(-2) + assert high.get_zorder() == -2 + assert ax._entries[0] is high._entry + low.set_transform(IdentityTransform()) + assert isinstance(low.get_transform(), IdentityTransform) + low.set_transform(Affine2D().translate(1, 2)) + np.testing.assert_allclose(low.get_xdata(), [1, 2]) + np.testing.assert_allclose(low.get_ydata(), [2, 3]) + with pytest.raises(NotImplementedError, match="unclipped"): + low.set_clip_on(False) + with pytest.raises(NotImplementedError, match="clip paths"): + low.set_clip_path(object()) + + +def test_dependency_free_bbox_affine_image_norm_and_text_extent(monkeypatch) -> None: + real_import = builtins.__import__ + + def block_matplotlib(name, *args, **kwargs): + if name.startswith("matplotlib"): + raise ImportError("blocked by dependency-free contract") + return real_import(name, *args, **kwargs) + + monkeypatch.setattr(builtins, "__import__", block_matplotlib) + _fig, ax = plt.subplots() + assert isinstance(ax.get_position(), Bbox) + transform = Affine2D().scale(2).translate(3, 4) + np.testing.assert_allclose( + transform.inverted().transform(transform.transform([[1, 2]])), [[1, 2]] + ) + + cmap = plt.get_cmap("viridis").with_extremes(bad="red", under="blue", over="yellow") + image = ax.imshow([[np.nan, -1], [0.5, 2]], cmap=cmap, vmin=0, vmax=1) + assert image._entry["z"].shape == (2, 2, 4) + image.set_transform(Affine2D().translate(1, 1)) + assert image._entry["z"].shape[-1] == 4 + assert ax.text(0.5, 0.5, "extent").get_window_extent().width > 0 + + +def test_add_adapters_are_bounded_and_unknown_artists_are_rejected() -> None: + _fig, ax = plt.subplots() + + class LineLike: + def get_data(self): + return [0, 1], [2, 3] + + def get_color(self): + return "red" + + line = ax.add_line(LineLike()) + assert line in ax.lines and line.get_color() == "red" + assert ax.add_container(ax.bar([0], [1])) in ax.containers + with pytest.raises(TypeError, match="supported container"): + ax.add_container(object()) + with pytest.raises(TypeError, match="create tables"): + ax.add_table(object()) + with pytest.raises(TypeError, match=r"use text\(\), imshow\(\)"): + ax.add_artist(object()) diff --git a/tests/pyplot/test_axes_charts.py b/tests/pyplot/test_axes_charts.py index d3468fa5..0c4e2a56 100644 --- a/tests/pyplot/test_axes_charts.py +++ b/tests/pyplot/test_axes_charts.py @@ -1,5 +1,8 @@ from __future__ import annotations +import json +from pathlib import Path + import numpy as np import pytest @@ -144,7 +147,7 @@ def test_existing_core_plot_families_are_exposed_by_adapter() -> None: ax.contour(np.arange(16, dtype=float).reshape(4, 4), levels=3) ax.contourf(np.arange(16, dtype=float).reshape(4, 4), levels=3) assert set(box) == {"whiskers", "caps", "boxes", "medians", "fliers", "means"} - assert set(violin) == {"bodies"} + assert set(violin) == {"bodies", "cbars", "cmins", "cmaxes"} kinds = [trace.kind for trace in _traces(ax)] assert "stem" in kinds assert "box" in kinds @@ -406,12 +409,12 @@ def test_errorbar_default_format_draws_data_line_and_none_opts_out() -> None: def test_unsupported_compatibility_options_fail_loudly() -> None: _fig, ax = plt.subplots() - with pytest.raises(NotImplementedError, match="hexbin"): - ax.hexbin([0, 1], [0, 1], C=[2, 3]) - with pytest.raises(TypeError, match="notch"): - ax.boxplot([[1, 2, 3]], notch=True) - with pytest.raises(NotImplementedError, match="symlog"): - ax.set_xscale("symlog") + collection = ax.hexbin([0, 0.1], [0, 0.1], C=[2, 3], reduce_C_function=np.max) + assert collection._entry["kwargs"]["C"] == [2, 3] + result = ax.boxplot([[1, 2, 3]], notch=True, conf_intervals=[[1.5, 2.5]]) + assert result["boxes"] + ax.set_xscale("symlog") + assert ax._scale_specs["x"]["name"] == "symlog" def test_artist_remove() -> None: @@ -423,74 +426,9 @@ def test_artist_remove() -> None: def test_official_matplotlib_311_2d_plotting_surface_is_complete() -> None: - names = [ - "plot", - "errorbar", - "scatter", - "step", - "loglog", - "semilogx", - "semilogy", - "fill_between", - "fill_betweenx", - "bar", - "barh", - "bar_label", - "grouped_bar", - "stem", - "eventplot", - "pie", - "pie_label", - "stackplot", - "broken_barh", - "vlines", - "hlines", - "fill", - "axhline", - "axhspan", - "axvline", - "axvspan", - "axline", - "acorr", - "angle_spectrum", - "cohere", - "csd", - "magnitude_spectrum", - "phase_spectrum", - "psd", - "specgram", - "xcorr", - "ecdf", - "boxplot", - "violinplot", - "bxp", - "violin", - "hexbin", - "hist", - "hist2d", - "stairs", - "clabel", - "contour", - "contourf", - "imshow", - "matshow", - "pcolor", - "pcolorfast", - "pcolormesh", - "spy", - "tripcolor", - "triplot", - "tricontour", - "tricontourf", - "annotate", - "text", - "table", - "arrow", - "barbs", - "quiver", - "quiverkey", - "streamplot", - ] + snapshot = json.loads((Path(__file__).with_name("matplotlib_311_plotting.json")).read_text()) + names = [name for family in snapshot["families"].values() for name in family] + assert len(names) == 66 assert not [name for name in names if not hasattr(plt.Axes, name)] assert not [name for name in names if not hasattr(plt, name)] diff --git a/tests/pyplot/test_axes_helpers.py b/tests/pyplot/test_axes_helpers.py new file mode 100644 index 00000000..4aab7194 --- /dev/null +++ b/tests/pyplot/test_axes_helpers.py @@ -0,0 +1,116 @@ +import numpy as np +import pytest + +import xy.pyplot as plt +from xy.pyplot._rc import rcParams + + +def teardown_function(): + plt.close("all") + plt.rcdefaults() + + +def test_autoscale_bounds_and_relim_helpers(): + fig, ax = plt.subplots() + ax.plot([0, 10], [2, 4]) + ax.margins(0.1) + assert ax.get_xbound() == pytest.approx((-1, 11)) + + ax.set_xbound(1, 3) + assert ax.get_xlim() == (1, 3) + ax.relim() + ax.autoscale(axis="x") + assert ax.get_xlim() == pytest.approx((-1, 11)) + + plt.set_ybound(0, 8) + assert plt.get_ybound() == (0, 8) + plt.autoscale(axis="y", tight=True) + assert ax.get_ylim() == (2, 4) + + +def test_ticklabel_minor_label_axis_and_legend_helpers(): + _, ax = plt.subplots() + line = ax.plot([0, 1], [1, 2], label="series")[0] + ax.set_xlabel("x label") + ax.set_ylabel("y label") + ax.set_title("title") + ax.ticklabel_format(axis="x", style="sci", scilimits=(-2, 3), useOffset=False) + ax.minorticks_on() + ax.legend() + + assert ax.get_xlabel() == "x label" + assert ax.get_ylabel() == "y label" + assert ax.get_title() == "title" + assert ax.get_xaxis() is ax.xaxis + assert ax.get_yaxis() is ax.yaxis + assert ax._axis_props("x")["tick_label_format"]["style"] == "sci" + assert ax._axis_props("x")["minor_ticks"] is True + assert ax.get_legend() is ax + handles, labels = ax.get_legend_handles_labels() + assert len(handles) == 1 + assert labels == ["series"] + assert line.get_label() == "series" + + ax.minorticks_off() + assert ax._axis_props("x")["minor_ticks"] is False + + +def test_prop_cycle_setp_getp_rc_context_and_colormap_helpers(): + _, ax = plt.subplots() + ax.set_prop_cycle(color=["red", "blue"]) + first = ax.plot([0, 1], [0, 1])[0] + second = ax.plot([0, 1], [1, 2])[0] + assert first.get_color() == "red" + assert second.get_color() == "blue" + + plt.setp(first, color="green", label="renamed") + assert plt.getp(first, "label") == "renamed" + assert plt.get(first, "color") == "green" + + with plt.rc_context({"image.cmap": "plasma"}): + assert rcParams["image.cmap"] == "plasma" + assert rcParams["image.cmap"] == "viridis" + + assert plt.plasma().name == "plasma" + assert rcParams["image.cmap"] == "plasma" + assert plt.gray().name == "gray" + + +def test_subplot2grid_box_and_secondary_axes_contract(): + fig = plt.figure() + ax = plt.subplot2grid((2, 2), (1, 0)) + assert fig.axes[2] is ax + plt.box(False) + assert ax._box is False + + with pytest.raises(NotImplementedError): + plt.subplot2grid((2, 2), (0, 0), colspan=2) + secondary_x = ax.secondary_xaxis("top", functions=(lambda x: x * 2, lambda x: x / 2)) + secondary_y = ax.secondary_yaxis("right") + secondary_x.set_xlabel("double") + secondary_y.set_ylabel("copy") + built = ax._build_chart(640, 480).figure() + assert built.axis_options["xs1"]["side"] == "top" + assert built.axis_options["ys2"]["side"] == "right" + + +def test_imread_imsave_png_roundtrip_and_jpeg_exclusion(tmp_path): + image = np.array( + [ + [[255, 0, 0, 255], [0, 255, 0, 128]], + [[0, 0, 255, 255], [255, 255, 255, 0]], + ], + dtype=np.uint8, + ) + path = tmp_path / "image.png" + plt.imsave(path, image) + + loaded = plt.imread(path) + + np.testing.assert_array_equal(loaded, image) + with pytest.raises(NotImplementedError): + plt.imsave(tmp_path / "image.jpg", image) + jpeg = tmp_path / "image.jpeg" + jpeg.write_bytes(b"\xff\xd8not really jpeg") + with pytest.raises(NotImplementedError): + plt.imread(jpeg) diff --git a/tests/pyplot/test_axes_layout.py b/tests/pyplot/test_axes_layout.py new file mode 100644 index 00000000..bb885281 --- /dev/null +++ b/tests/pyplot/test_axes_layout.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +import builtins + +import pytest + +import xy.pyplot as plt + + +@pytest.fixture(autouse=True) +def _clean(): + plt.close("all") + yield + plt.close("all") + + +def _axis_child(ax, which: str): + chart = ax._build_chart(640, 480) + return next(child for child in chart.children if getattr(child, "which", None) == which) + + +def test_get_position_is_dependency_free_and_set_position_preserves_bounds(monkeypatch) -> None: + real_import = builtins.__import__ + + def no_matplotlib(name, *args, **kwargs): + if name.startswith("matplotlib"): + raise ImportError(name) + return real_import(name, *args, **kwargs) + + _fig, ax = plt.subplots() + monkeypatch.setattr(builtins, "__import__", no_matplotlib) + + default = ax.get_position() + assert default.bounds == (0.125, 0.11, 0.775, 0.77) + assert (default.x0, default.y0, default.x1, default.y1) == (0.125, 0.11, 0.9, 0.88) + + ax.set_position([0.2, 0.3, 0.4, 0.5]) + + moved = ax.get_position() + assert moved.bounds == (0.2, 0.3, 0.4, 0.5) + assert ax._figure_rect == (0.2, 0.3, 0.4, 0.5) + + +def test_margins_expand_only_automatic_domains() -> None: + _fig, ax = plt.subplots() + ax.plot([10.0, 20.0], [100.0, 140.0]) + + ax.margins(x=0.1, y=0.25) + + assert ax.get_xlim() == (9.0, 21.0) + assert ax.get_ylim() == (90.0, 150.0) + assert _axis_child(ax, "x").domain == (9.0, 21.0) + assert _axis_child(ax, "y").domain == (90.0, 150.0) + + ax.set_xlim(0.0, 1.0) + ax.margins(x=0.5) + + assert ax.get_xlim() == (0.0, 1.0) + + +def test_axis_tight_sets_data_domains_and_equal_expands_to_panel_ratio() -> None: + _fig, ax = plt.subplots() + ax.plot([0.0, 2.0], [0.0, 1.0]) + + assert ax.axis("tight") == (0.0, 2.0, 0.0, 1.0) + assert ax._axis["x"]["domain"] == (0.0, 2.0) + assert ax._axis["y"]["domain"] == (0.0, 1.0) + + ax.axis("equal") + x_axis = _axis_child(ax, "x") + y_axis = _axis_child(ax, "y") + + assert x_axis.domain == (0.0, 2.0) + assert y_axis.domain[0] < 0.0 + assert y_axis.domain[1] > 1.0 + + +def test_tick_params_records_supported_style_and_rejects_unknown() -> None: + _fig, ax = plt.subplots() + + ax.tick_params( + axis="x", + labelrotation=45, + colors="tab:red", + length=7, + width=2, + direction="in", + labelbottom=False, + ) + + x_axis = _axis_child(ax, "x") + assert x_axis.tick_label_angle == 45.0 + assert x_axis.tick_label_strategy == "none" + assert x_axis.style == { + "tick_color": "#d62728", + "tick_label_color": "#d62728", + "tick_length": 7.0, + "tick_width": 2.0, + "tick_direction": "in", + } + + with pytest.raises(TypeError, match="unsupported keyword"): + ax.tick_params(which="minor") + + +def test_axes_set_rejects_unknown_properties_after_applying_known_setters() -> None: + _fig, ax = plt.subplots() + + with pytest.raises(AttributeError, match="unsupported property"): + ax.set(xlabel="time", ylabel="value", made_up=True) + + assert ax._axis["x"]["label"] == "time" + assert ax._axis["y"]["label"] == "value" + + +def test_set_anchor_accepts_mpl_anchor_codes_and_rejects_unknown() -> None: + _fig, ax = plt.subplots() + + ax.set_anchor("SW") + assert ax._anchor == "SW" + + with pytest.raises(ValueError, match="unsupported anchor"): + ax.set_anchor("baseline") diff --git a/tests/pyplot/test_boundaries.py b/tests/pyplot/test_boundaries.py index ff914b60..06890b22 100644 --- a/tests/pyplot/test_boundaries.py +++ b/tests/pyplot/test_boundaries.py @@ -82,3 +82,29 @@ def test_shim_never_imports_real_matplotlib_statically() -> None: assert not any(a.name.split(".")[0] == "matplotlib" for a in node.names), path if isinstance(node, ast.ImportFrom): assert (node.module or "").split(".")[0] != "matplotlib", path + + +def test_complete_supported_corpus_runs_when_matplotlib_imports_fail() -> None: + """Every advertised plotting method remains usable in dependency-free installs.""" + corpus = Path(__file__).with_name("corpus") + _run_fresh( + f""" + import builtins + import pathlib + import runpy + + real_import = builtins.__import__ + def blocked_import(name, *args, **kwargs): + if name == "matplotlib" or name.startswith("matplotlib."): + raise ImportError("matplotlib intentionally unavailable") + return real_import(name, *args, **kwargs) + builtins.__import__ = blocked_import + + import xy.pyplot as plt + for path in sorted(pathlib.Path({str(corpus)!r}).glob("[0-9][0-9]_*.py")): + runpy.run_path(path, run_name="__main__") + for figure in tuple(__import__("xy.pyplot._state", fromlist=["all_figures"]).all_figures()): + assert figure._repr_html_().startswith("") + plt.close("all") + """ + ) diff --git a/tests/pyplot/test_compatibility_metadata.py b/tests/pyplot/test_compatibility_metadata.py new file mode 100644 index 00000000..bfdac48b --- /dev/null +++ b/tests/pyplot/test_compatibility_metadata.py @@ -0,0 +1,90 @@ +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + +import numpy as np +from scripts.sync_matplotlib_compat import _corpus_calls + +import xy.pyplot as plt + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[1] + + +def _load(name: str) -> dict: + return json.loads((HERE / name).read_text()) + + +def test_compatibility_metadata_covers_the_reviewed_snapshot() -> None: + snapshot = _load("matplotlib_311_plotting.json") + metadata = _load("compatibility.json") + assert metadata["families"].keys() == snapshot["families"].keys() + assert all(item["level"] in metadata["levels"] for item in metadata["families"].values()) + + +def test_every_supported_plotting_method_has_direct_corpus_coverage() -> None: + snapshot = _load("matplotlib_311_plotting.json") + expected = {method for methods in snapshot["families"].values() for method in methods} + covered = set(_corpus_calls()) + assert expected <= covered, f"missing direct corpus calls: {sorted(expected - covered)}" + + +def test_corpus_coverage_ignores_calls_on_unrelated_receivers(tmp_path, monkeypatch) -> None: + corpus = tmp_path / "corpus" + corpus.mkdir() + (corpus / "01_false_credit.py").write_text( + "import xy.pyplot as plt\nthing.fill([1, 2])\nfig, ax = plt.subplots()\nax.plot([1])\n" + ) + monkeypatch.setattr("scripts.sync_matplotlib_compat.CORPUS", corpus) + calls = _corpus_calls() + assert "plot" in calls + assert "fill" not in calls + + +def test_generated_compatibility_documentation_is_fresh() -> None: + subprocess.run( + [sys.executable, str(ROOT / "scripts/sync_matplotlib_compat.py"), "--check"], + check=True, + ) + + +def _material_observation(method: str, keyword: str, value: object) -> object: + _fig, ax = plt.subplots() + if method == "plot": + artist = ax.plot([0, 1], [0, 1], **{keyword: value})[0] + return artist._entry["kwargs"]["width"] + if method == "scatter": + artist = ax.scatter([0, 1], [0, 1], **{keyword: value}) + return np.asarray(artist._entry["kwargs"]["size"]).tolist() + if method == "bar": + artist = ax.bar([0, 1], [1, 2], **{keyword: value}) + return artist._entry["kwargs"]["width"] + if method == "hist": + counts, _edges, _patches = ax.hist([0, 0, 1, 2], bins=2, **{keyword: value}) + return np.asarray(counts).tolist() + if method == "imshow": + ax.imshow([[0, 1], [2, 3]], **{keyword: value}) + limits = ax.get_ylim() + return limits[0] > limits[1] + raise AssertionError(f"material keyword metadata has no observation adapter for {method}") + + +def test_material_keyword_metadata_detects_accepted_but_discarded_values() -> None: + """Changing every declared material value must change observable state.""" + metadata = _load("compatibility.json") + try: + for guard in metadata["material_keyword_guards"]: + first, second = guard["values"] + before = _material_observation(guard["method"], guard["keyword"], first) + plt.close("all") + after = _material_observation(guard["method"], guard["keyword"], second) + assert before != after, ( + f"{guard['method']} accepted {guard['keyword']}={first!r}/{second!r} " + "but produced identical observable state" + ) + plt.close("all") + finally: + plt.close("all") diff --git a/tests/pyplot/test_figure_state.py b/tests/pyplot/test_figure_state.py new file mode 100644 index 00000000..dfd454a2 --- /dev/null +++ b/tests/pyplot/test_figure_state.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +import numpy as np +import pytest + +from xy.pyplot._mplfig import Figure + + +def test_figure_clear_clf_removes_axes_and_chrome() -> None: + fig = Figure(1) + ax = fig.add_subplot(111) + ax.plot([0, 1], [1, 2]) + fig.suptitle("title") + fig.colorbar(ax.imshow([[1, 2], [3, 4]]), ax=[ax]) + + fig.clear() + + assert fig.axes == [] + assert fig._current_ax is None + assert fig._suptitle is None + assert fig._shared_colorbar is None + fresh = fig.gca() + assert fresh.figure is fig + assert fresh._entries == [] + + fig.clf() + assert fig.axes == [] + + +def test_figure_sca_and_delaxes_keep_current_axes_consistent() -> None: + fig = Figure(1) + axes = fig.subplots(1, 3) + + assert fig.gca() is axes[-1] + assert fig.sca(axes[0]) is axes[0] + assert fig.gca() is axes[0] + + fig.delaxes(axes[0]) + + assert axes[0].figure is None + assert fig.gca() is axes[1] + assert fig.axes == [axes[1], axes[2]] + + fig.delaxes(axes[2]) + assert fig.gca() is axes[1] + + with pytest.raises(ValueError, match="belong to this figure"): + fig.delaxes(axes[0]) + + +def test_figure_text_legend_and_super_labels_use_figure_transform() -> None: + fig = Figure(1) + ax = fig.add_subplot(111) + ax.plot([0, 1], [1, 2], label="line label") + + text = fig.text(0.25, 0.75, "figure note", color="red") + xlabel = fig.supxlabel("shared x") + ylabel = fig.supylabel("shared y") + fig.legend() + + assert text._entry["args"] == (0.25, 0.75, "figure note") + assert text._entry["kwargs"]["style"]["coordinate_space"] == "figure_fraction" + assert xlabel._entry["kwargs"]["style"]["coordinate_space"] == "figure_fraction" + assert ylabel._entry["kwargs"]["style"]["coordinate_space"] == "figure_fraction" + assert fig._supxlabel == "shared x" + assert fig._supylabel == "shared y" + assert ax._legend is True + assert "figure note" in fig._repr_html_() + + +def test_figure_size_dpi_and_color_getters_setters() -> None: + fig = Figure(1, figsize=(4, 3), dpi=120, facecolor="#112233") + + np.testing.assert_allclose(fig.get_size_inches(), np.asarray([4.0, 3.0])) + assert fig.get_dpi() == 120.0 + assert fig.dpi == 120.0 + assert fig.get_facecolor() == "#112233" + assert fig.get_edgecolor() == "white" + + fig.set_size_inches(5, 2) + fig.set_dpi(80) + fig.set_facecolor("black") + fig.set_edgecolor("blue") + + np.testing.assert_allclose(fig.get_size_inches(), np.asarray([5.0, 2.0])) + assert fig.dpi == 80.0 + assert fig.get_facecolor() == "black" + assert fig.get_edgecolor() == "blue" + + fig.dpi = 96 + assert fig.get_dpi() == 96.0 + + +def test_figure_subplots_sharing_ratios_and_squeeze() -> None: + fig = Figure(1) + + axes = fig.subplots( + 2, + 2, + sharex=True, + sharey=True, + squeeze=False, + gridspec_kw={"width_ratios": [2, 1], "height_ratios": [1, 3]}, + ) + + assert axes.shape == (2, 2) + assert fig.axes == list(axes.ravel()) + assert fig.gca() is axes[-1, -1] + assert fig._sharex is True + assert fig._sharey is True + assert fig._width_ratios == (2.0, 1.0) + assert fig._height_ratios == (1.0, 3.0) + + +def test_add_gridspec_supports_single_cell_specs() -> None: + fig = Figure(1) + + gs = fig.add_gridspec(2, 2, width_ratios=[1, 2]) + ax = fig.add_subplot(gs[1, 0]) + same = fig.add_subplot(gs[2]) + + assert ax is same + assert fig._width_ratios == (1.0, 2.0) + assert fig.gca() is ax + + span = gs[0:2, 0] + assert span.rows == (0, 2) + assert span.cols == (0, 1) + with pytest.raises(NotImplementedError): + _ = gs[0:2:2, 0] # step slicing stays out of the span contract diff --git a/tests/pyplot/test_grid_legend_contracts.py b/tests/pyplot/test_grid_legend_contracts.py new file mode 100644 index 00000000..4d3b7d24 --- /dev/null +++ b/tests/pyplot/test_grid_legend_contracts.py @@ -0,0 +1,70 @@ +import pytest + +import xy.pyplot as plt + + +def teardown_function(): + plt.close("all") + + +def test_grid_selects_axis_and_records_supported_style(): + _, ax = plt.subplots() + ax.grid(True, axis="x", which="major", color="red", linewidth=2, linestyle="--", alpha=0.5) + + x_style = ax._axis_props("x")["style"] + y_style = ax._axis_props("y")["style"] + assert ax._grid is True + assert ax._grid_axis == "x" + assert x_style["grid_color"] == "red" + assert x_style["grid_width"] == 2.0 + assert x_style["grid_dash"] == "dashed" + assert x_style["grid_opacity"] == 0.5 + assert y_style["grid_color"] == "transparent" + + ax.grid(False, axis="y", color="blue") + assert ax._axis_props("y")["style"]["grid_color"] == "transparent" + with pytest.raises(ValueError): + ax.grid(True, axis="z") + with pytest.raises(ValueError): + ax.grid(True, which="minor") + with pytest.raises(TypeError): + ax.grid(True, unsupported=True) + + +def test_legend_maps_supported_style_and_rejects_unknown_options(): + _, ax = plt.subplots() + ax.plot([0, 1], [1, 2], label="line") + ax.legend( + loc="upper right", + ncols=2, + title="Legend", + fontsize=13, + labelcolor="green", + frameon=True, + facecolor="white", + edgecolor="black", + ) + + assert ax._legend is True + assert ax._legend_options["loc"] == "upper right" + assert ax._legend_options["ncols"] == 2 + assert ax._legend_options["class_name"] == "legend-title:Legend" + assert ax._legend_options["style"] == { + "fontSize": "13px", + "color": "green", + "background": "white", + "borderColor": "black", + "borderStyle": "solid", + } + + with pytest.raises(TypeError): + ax.legend(shadow=True) + + +def test_legend_frameoff_maps_to_transparent_style(): + _, ax = plt.subplots() + ax.plot([0, 1], [1, 2], label="line") + ax.legend(frameon=False) + + assert ax._legend_options["style"]["background"] == "transparent" + assert ax._legend_options["style"]["borderColor"] == "transparent" diff --git a/tests/pyplot/test_layout_noops.py b/tests/pyplot/test_layout_noops.py new file mode 100644 index 00000000..eac8e69e --- /dev/null +++ b/tests/pyplot/test_layout_noops.py @@ -0,0 +1,55 @@ +import pytest + +import xy.pyplot as plt + + +def teardown_function(): + plt.close("all") + + +def test_tight_layout_records_validated_noop_contract(): + fig, _ = plt.subplots(1, 2) + fig.tight_layout(pad=1.2, h_pad=0.5, w_pad=0.7, rect=(0, 0, 1, 1)) + + assert fig._layout_options == { + "engine": "tight", + "pad": 1.2, + "h_pad": 0.5, + "w_pad": 0.7, + "rect": (0, 0, 1, 1), + } + with pytest.raises(TypeError): + fig.tight_layout(foo=1) + + +def test_subplots_adjust_records_supported_spacing_values(): + fig, _ = plt.subplots(2, 2) + fig.subplots_adjust(left=0.1, right=0.9, top=0.8, bottom=0.2, wspace=0.3, hspace=0.4) + + assert fig._subplot_adjust == { + "left": 0.1, + "right": 0.9, + "top": 0.8, + "bottom": 0.2, + "wspace": 0.3, + "hspace": 0.4, + } + with pytest.raises(TypeError): + fig.subplots_adjust(foo=1) + + +def test_autofmt_xdate_rotates_x_tick_labels_on_all_axes(): + fig, axes = plt.subplots(1, 2) + fig.autofmt_xdate(rotation=45, ha="center") + + for ax in axes: + assert ax._axis_props("x")["tick_label_angle"] == 45 + assert ax._axis_props("x")["style"]["tick_label_anchor"] == "center" + + +def test_suptitle_accepts_supported_font_kwargs_and_rejects_unknown(): + fig, _ = plt.subplots() + fig.suptitle("title", fontsize=14, fontweight="bold", color="red", x=0.5, y=0.95) + assert fig._suptitle == "title" + with pytest.raises(TypeError): + fig.suptitle("bad", unknown=True) diff --git a/tests/pyplot/test_p3_option_contracts.py b/tests/pyplot/test_p3_option_contracts.py new file mode 100644 index 00000000..eb62853e --- /dev/null +++ b/tests/pyplot/test_p3_option_contracts.py @@ -0,0 +1,417 @@ +from __future__ import annotations + +from datetime import datetime, timedelta + +import numpy as np +import pytest + +import xy.pyplot as plt + + +def test_plot_marker_styles_and_markevery_reach_marker_entry() -> None: + _fig, ax = plt.subplots() + ax.plot( + [0, 1, 2, 3], + [1, 2, 3, 4], + marker="o", + markevery=2, + markerfacecolor="red", + markeredgecolor="blue", + markeredgewidth=3, + ) + marker = ax._entries[1] + np.testing.assert_array_equal(marker["x"], [0, 2]) + assert marker["kwargs"]["color"] == "red" + assert marker["kwargs"]["stroke"] == "blue" + assert marker["kwargs"]["stroke_width"] == 3 + + +@pytest.mark.parametrize( + ("call", "match"), + [ + (lambda ax: ax.plot([0, 1], [1, 2], scalex=False), "scalex"), + (lambda ax: ax.plot([0, 1], [1, 2], fillstyle="left"), "fillstyle"), + (lambda ax: ax.plot([0, 1], [1, 2], solid_capstyle="round"), "capstyle"), + (lambda ax: ax.fill_betweenx([0, 1], [0, 1], step="pre"), "step"), + (lambda ax: ax.arrow(0, 0, 1, 1, shape="left"), "head shape"), + (lambda ax: ax.errorbar([0], [1], yerr=0.2, barsabove=True), "barsabove"), + (lambda ax: ax.imshow([[1]], interpolation_stage="rgba"), "interpolation_stage"), + (lambda ax: ax.psd([1, 2, 3], window=np.ones(3)), "window"), + ], +) +def test_material_options_are_not_silently_discarded(call, match: str) -> None: + _fig, ax = plt.subplots() + with pytest.raises((TypeError, NotImplementedError), match=match): + call(ax) + + +def test_scatter_color_domain_is_retained_and_norm_is_loud() -> None: + _fig, ax = plt.subplots() + ax.scatter([0, 1], [1, 2], c=[10, 20], vmin=0, vmax=30) + assert ax._entries[0]["kwargs"]["domain"] == (0.0, 30.0) + with pytest.raises(NotImplementedError, match=r"scatter\(norm"): + ax.scatter([0], [1], c=[2], norm=lambda value: value) + + +def test_rule_linestyle_is_retained_as_dash_geometry() -> None: + _fig, ax = plt.subplots() + ax.axvline(1, linestyle="--") + assert ax._entries[0]["kwargs"]["style"]["dash"] == "6.0,4.0" + + +def test_hlines_and_vlines_emit_dash_geometry() -> None: + _fig, ax = plt.subplots() + horizontal = ax.hlines([1], [0], [2], linestyles="dashed") + vertical = ax.vlines([1], [0], [2], linestyles="dotted") + assert len(horizontal._entry["args"][0]) > 1 + assert len(vertical._entry["args"][0]) > 1 + + +def test_fill_between_interpolate_extends_to_curve_crossing() -> None: + _fig, ax = plt.subplots() + collection = ax.fill_between( + [0, 1, 2, 3], + [-1, 1, 1, -1], + 0, + where=[False, True, True, False], + interpolate=True, + ) + np.testing.assert_allclose(collection._entry["x"], [0.5, 1.0, 2.0, 2.5]) + + +def test_log_wrappers_accept_only_the_native_log_contract() -> None: + _fig, ax = plt.subplots() + ax.loglog([1, 10], [1, 100], base=10, nonpositive="clip") + assert ax._axis["x"]["type_"] == "log" + assert ax._axis["y"]["type_"] == "log" + with pytest.raises(NotImplementedError, match="base=2"): + ax.semilogx([1, 2], [1, 2], base=2) + with pytest.raises(NotImplementedError, match="subs"): + ax.semilogy([1, 2], [1, 2], subs=[1, 2]) + with pytest.raises(NotImplementedError, match="nonpositive"): + ax.set_xscale("log", nonpositive="mask") + for scale in ("symlog", "logit", "asinh"): + ax.set_xscale(scale) + assert ax._scale_specs["x"]["name"] == scale + + +def test_datetime_timedelta_and_categories_have_bounded_native_conversions() -> None: + _fig, ax = plt.subplots() + dates = [datetime(2024, 1, 1), datetime(2024, 1, 2)] + ax.plot(dates, [1, 2]) + date_values = ax._build_chart(640, 480).figure().traces[0].x.values + np.testing.assert_array_equal(date_values, [1704067200000, 1704153600000]) + + _fig, ax = plt.subplots() + ax.plot([timedelta(hours=1), timedelta(hours=2)], [1, 2]) + np.testing.assert_array_equal(ax._entries[0]["x"], [3600, 7200]) + + _fig, ax = plt.subplots() + ax.plot(["alpha", "beta"], [1, 2]) + categorical = ax._build_chart(640, 480).figure().traces[0].x.values + np.testing.assert_array_equal(categorical, [0, 1]) + + +def test_imshow_uses_rc_image_origin_default() -> None: + plt.rcParams["image.origin"] = "lower" + _fig, ax = plt.subplots() + ax.imshow([[1, 2], [3, 4]]) + assert not ax._axis["y"].get("reverse", False) + + plt.rcParams["image.origin"] = "upper" + _fig, ax = plt.subplots() + ax.imshow([[1, 2], [3, 4]]) + assert ax._axis["y"]["reverse"] is True + + +def test_errorbar_limit_flags_change_one_sided_geometry() -> None: + _fig, ax = plt.subplots() + ax.errorbar([0, 1], [2, 3], yerr=[0.5, 1], lolims=[True, False], uplims=[False, True]) + yerr = ax._entries[0]["kwargs"]["yerr"] + np.testing.assert_array_equal(yerr, [[0, 1], [0.5, 0]]) + + +class Normalize: + """Stand-in for matplotlib.colors.Normalize (accepted by type name).""" + + def __init__(self, vmin=None, vmax=None) -> None: + self.vmin = vmin + self.vmax = vmax + + +class LogNorm(Normalize): + pass + + +def _stream_args() -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + coords = np.arange(4.0) + return coords, coords, np.ones((4, 4)), np.ones((4, 4)) + + +_Z = np.arange(16.0).reshape(4, 4) + + +@pytest.mark.parametrize( + ("call", "match"), + [ + (lambda ax: ax.pie([1, 2], shadow=True), "shadow"), + (lambda ax: ax.pie([1, 2], frame=True), "frame"), + (lambda ax: ax.pie([1, 2], rotatelabels=True), "rotatelabels"), + (lambda ax: ax.pie([1, 2], hatch="//"), "hatch"), + (lambda ax: ax.pie([1, 2], wedgeprops={"hatch": "x"}), "hatch"), + (lambda ax: ax.quiver([0, 1], [0, 1], [1, 0], [0, 1], units="xy"), "units"), + (lambda ax: ax.quiver([0, 1], [0, 1], [1, 0], [0, 1], headwidth=6), "headwidth"), + (lambda ax: ax.quiver([0, 1], [0, 1], [1, 0], [0, 1], headlength=2), "headlength"), + (lambda ax: ax.quiver([0, 1], [0, 1], [1, 0], [0, 1], headaxislength=2), "headaxislength"), + (lambda ax: ax.quiver([0, 1], [0, 1], [1, 0], [0, 1], minshaft=2), "minshaft"), + (lambda ax: ax.quiver([0, 1], [0, 1], [1, 0], [0, 1], minlength=0), "minlength"), + (lambda ax: ax.quiver([0, 1], [0, 1], [1, 0], [0, 1], norm=Normalize(0, 1)), "norm"), + (lambda ax: ax.quiver([0, 1], [0, 1], [1, 0], [0, 1], clim=(0, 1)), "clim"), + (lambda ax: ax.quiver([0, 1], [0, 1], [1, 0], [0, 1], zorder=3), "zorder"), + (lambda ax: ax.barbs([0, 1], [0, 1], [1, 0], [0, 1], length=9), "length"), + (lambda ax: ax.barbs([0, 1], [0, 1], [1, 0], [0, 1], fill_empty=True), "fill_empty"), + (lambda ax: ax.barbs([0, 1], [0, 1], [1, 0], [0, 1], rounding=False), "rounding"), + (lambda ax: ax.barbs([0, 1], [0, 1], [1, 0], [0, 1], flip_barb=True), "flip_barb"), + (lambda ax: ax.barbs([0, 1], [0, 1], [1, 0], [0, 1], sizes={"spacing": 0.2}), "sizes"), + (lambda ax: ax.barbs([0, 1], [0, 1], [1, 0], [0, 1], barbcolor="red"), "barbcolor"), + (lambda ax: ax.barbs([0, 1], [0, 1], [1, 0], [0, 1], flagcolor="red"), "flagcolor"), + ( + lambda ax: ax.barbs([0, 1], [0, 1], [1, 0], [0, 1], barb_increments={"half": 3}), + "barb_increments", + ), + (lambda ax: ax.contour(_Z, origin="lower"), "origin"), + (lambda ax: ax.contour(_Z, linestyles="dashed"), "linestyles"), + (lambda ax: ax.contourf(_Z, corner_mask=False), "corner_mask"), + (lambda ax: ax.contourf(_Z, corner_mask="legacy"), "corner_mask"), + (lambda ax: ax.streamplot(*_stream_args(), transform="data"), "transform"), + (lambda ax: ax.streamplot(*_stream_args(), zorder=2), "zorder"), + (lambda ax: ax.streamplot(*_stream_args(), minlength=0.5), "minlength"), + ( + lambda ax: ax.streamplot(*_stream_args(), broken_streamlines=False), + "broken_streamlines", + ), + (lambda ax: ax.streamplot(*_stream_args(), arrowstyle="->"), "arrowstyle"), + ( + lambda ax: ax.streamplot(*_stream_args(), integration_max_step_scale=2.0), + "integration_max_step_scale", + ), + ( + lambda ax: ax.streamplot(*_stream_args(), integration_max_error_scale=0.5), + "integration_max_error_scale", + ), + (lambda ax: ax.pcolormesh(_Z, antialiased=False), "antialiased"), + (lambda ax: ax.pcolor(_Z, antialiased=False), "antialiased"), + (lambda ax: ax.table(cellText=[["a"]], cellLoc="center"), "cellLoc"), + (lambda ax: ax.table(cellText=[["a"]], rowLoc="center"), "rowLoc"), + (lambda ax: ax.table(cellText=[["a"]], colLoc="left"), "colLoc"), + (lambda ax: ax.table(cellText=[["a"]], loc="top"), "loc"), + (lambda ax: ax.stem([0, 1], [1, 2], basefmt="k-"), "basefmt"), + ( + lambda ax: ax.quiverkey(_quiver(ax), 0.5, 0.5, 1, "k", fontproperties={"size": 9}), + "fontproperties", + ), + (lambda ax: ax.quiverkey(_quiver(ax), 0.5, 0.5, 1, "k", zorder=5), "zorder"), + (lambda ax: ax.bar_label(ax.bar([0], [1]), fontproperties="serif"), "fontproperties"), + (lambda ax: ax.spy(np.eye(3), aspect="auto"), "aspect"), + ( + lambda ax: ax.tripcolor([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], antialiased=True), + "antialiased", + ), + ( + lambda ax: ax.tricontour([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], antialiased=False), + "antialiased", + ), + ( + lambda ax: ax.tricontour([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], linestyles="dashed"), + "linestyles", + ), + ( + lambda ax: ax.tricontourf([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], antialiased=True), + "antialiased", + ), + (lambda ax: ax.eventplot([[1, 2]], linestyles="steps"), "linestyle"), + ( + lambda ax: ax.triplot([0, 1, 2], [0, 1, 0], triangles=[[0, 1, 2]], dashes=(2, 1)), + "dashes", + ), + ( + lambda ax: ax.tricontour([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], extend="both"), + "extend", + ), + ( + lambda ax: ax.tripcolor([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], norm=LogNorm()), + r"tripcolor\(norm=LogNorm\)", + ), + ( + lambda ax: ax.tricontour([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], norm=LogNorm()), + r"tricontour\(norm=LogNorm\)", + ), + ( + lambda ax: ax.pie_label(ax.pie([1.0, 2.0]), "{frac:.0%}", rotate=True), + "rotate", + ), + ], +) +def test_p3_options_are_rejected_instead_of_silently_discarded(call, match: str) -> None: + _fig, ax = plt.subplots() + with pytest.raises((TypeError, NotImplementedError), match=match): + call(ax) + + +def _quiver(ax): + return ax.quiver([0, 1], [0, 1], [1, 0], [0, 1]) + + +def test_matplotlib_default_option_values_pass_through() -> None: + _fig, ax = plt.subplots() + ax.pie([1, 2], shadow=False, frame=False, rotatelabels=False) + ax.quiver( + [0, 1], + [0, 1], + [1, 0], + [0, 1], + units="width", + headwidth=3, + headlength=5, + headaxislength=4.5, + minshaft=1, + minlength=1, + ) + ax.barbs( + [0, 1], [0, 1], [1, 0], [0, 1], rounding=True, fill_empty=False, flip_barb=False, length=7 + ) + ax.contour(_Z, corner_mask=True) + ax.pcolormesh(_Z, antialiased=True) + ax.table(cellText=[["a"]], cellLoc="right", rowLoc="left", colLoc="center", loc="bottom") + ax.stem([0, 1], [1, 2], basefmt="C3-") + ax.spy(np.eye(3), aspect="equal") + ax.tripcolor([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], antialiased=False) + ax.tricontour([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], antialiased=True, extend="neither") + ax.tricontourf([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], antialiased=False) + assert ax._entries + + +def test_contour_extent_generates_coordinate_grids() -> None: + _fig, ax = plt.subplots() + ax.contour(np.arange(12.0).reshape(3, 4), extent=(0, 6, 10, 40)) + entry = ax._entries[0] + np.testing.assert_allclose(entry["kwargs"]["x"], [0.0, 2.0, 4.0, 6.0]) + np.testing.assert_allclose(entry["kwargs"]["y"], [10.0, 25.0, 40.0]) + + +def test_stem_dashed_linefmt_emits_dash_segments_and_markers() -> None: + _fig, ax = plt.subplots() + ax.stem([0, 1], [4, 8], linefmt="r--") + segments = ax._entries[0] + assert segments["factory"] == "segments" + assert segments["kwargs"]["color"] == "#ff0000" + assert len(segments["args"][0]) > 2 # each stem splits into dash pieces + np.testing.assert_array_equal(segments["args"][0], segments["args"][2]) # stems stay vertical + assert ax._entries[1]["kind"] == "scatter" + + +def test_pcolormesh_plain_normalize_maps_to_domain() -> None: + _fig, ax = plt.subplots() + ax.pcolormesh(_Z, norm=Normalize(1.0, 4.0)) + assert ax._entries[0]["kwargs"]["domain"] == (1.0, 4.0) + with pytest.raises(NotImplementedError, match=r"pcolormesh\(norm=LogNorm\)"): + ax.pcolormesh(_Z, norm=LogNorm()) + + +def test_bar_label_fontsize_reaches_text_style() -> None: + _fig, ax = plt.subplots() + ax.bar_label(ax.bar([0, 1], [2, 3]), fontsize=11) + texts = [entry for entry in ax._entries if entry["kind"] == "@text"] + assert len(texts) == 2 + assert all(entry["kwargs"]["style"]["font_size"] == 11.0 for entry in texts) + + +def test_streamplot_seeds_and_direction_drive_the_native_integrator() -> None: + x = np.linspace(0.0, 1.0, 5) + y = np.linspace(0.0, 1.0, 5) + u, v = np.ones((5, 5)), np.zeros((5, 5)) + _fig, ax = plt.subplots() + ax.streamplot(x, y, u, v, start_points=[[0.5, 0.5]], integration_direction="forward") + segments = [entry for entry in ax._entries if entry.get("factory") == "segments"] + starts_x = np.concatenate([np.asarray(entry["args"][0]) for entry in segments]) + starts_y = np.concatenate([np.asarray(entry["args"][1]) for entry in segments]) + assert np.all(starts_x >= 0.5) # forward-only integration from the seed + np.testing.assert_allclose(starts_y, 0.5) + + _fig, ax = plt.subplots() + ax.streamplot(x, y, u, v, start_points=[[0.5, 0.5]], integration_direction="backward") + segments = [entry for entry in ax._entries if entry.get("factory") == "segments"] + ends_x = np.concatenate([np.asarray(entry["args"][2]) for entry in segments]) + assert np.all(ends_x <= 0.5) + + _fig, ax = plt.subplots() + with pytest.raises(ValueError, match="start_points"): + ax.streamplot(x, y, u, v, start_points=[[5.0, 5.0]]) + + +def test_streamplot_array_linewidth_and_color_are_sampled_per_segment() -> None: + x = np.linspace(-1.0, 1.0, 8) + y = np.linspace(-1.0, 1.0, 8) + xx, yy = np.meshgrid(x, y) + _fig, ax = plt.subplots() + ax.streamplot(x, y, -yy, xx, color=xx, linewidth=1.0 + np.abs(yy), norm=Normalize(-2.0, 2.0)) + segments = [entry for entry in ax._entries if entry.get("factory") == "segments"] + assert len(segments) > 1 # varying widths split into width bins + assert len({entry["kwargs"]["width"] for entry in segments}) > 1 + assert all(entry["kwargs"]["domain"] == (-2.0, 2.0) for entry in segments) + assert any(np.ptp(np.asarray(entry["kwargs"]["color"])) > 0 for entry in segments) + with pytest.raises(NotImplementedError, match=r"streamplot\(norm=LogNorm\)"): + ax.streamplot(x, y, -yy, xx, color=xx, norm=LogNorm()) + + +def test_eventplot_linestyles_render_dash_segments() -> None: + _fig, ax = plt.subplots() + ax.eventplot([[1, 2], [3]], linestyles=["dashed", "dotted"]) + assert [entry["factory"] for entry in ax._entries] == ["segments", "segments"] + assert len(ax._entries[0]["args"][0]) > 2 # each event tick splits into dashes + + _fig, ax = plt.subplots() + ax.eventplot([[1, 2]], linestyles="solid") + assert ax._entries[0]["factory"] == "errorbar" + + +def test_triplot_dashed_fmt_splits_edges() -> None: + _fig, ax = plt.subplots() + ax.triplot([0.0, 1.0, 0.5], [0.0, 0.0, 1.0], "k--", triangles=[[0, 1, 2]]) + entry = ax._entries[0] + assert entry["factory"] == "segments" + assert len(entry["args"][0]) > 3 # three edges split into dash pieces + + +def test_tri_plain_normalize_maps_to_domain() -> None: + _fig, ax = plt.subplots() + ax.tripcolor([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], norm=Normalize(1.0, 3.0)) + assert ax._entries[-1]["kwargs"]["domain"] == (1.0, 3.0) + ax.tricontour([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], norm=Normalize(-5.0, 5.0)) + assert ax._entries[-1]["kwargs"]["domain"] == (-5.0, 5.0) + ax.tricontourf([0, 1, 2], [0, 1, 0], [1.0, 2.0, 3.0], norm=Normalize(0.0, 4.0)) + assert ax._entries[-1]["kwargs"]["domain"] == (0.0, 4.0) + + +def test_pie_pie_label_and_table_text_options_reach_text_style() -> None: + _fig, ax = plt.subplots() + pie = ax.pie( + [1, 2], + labels=["a", "b"], + textprops={"fontsize": 9, "color": "red", "ha": "center", "va": "center"}, + ) + texts = [entry for entry in ax._entries if entry["kind"] == "@text"] + assert len(texts) == 2 + assert all(entry["kwargs"]["style"]["font_size"] == 9.0 for entry in texts) + assert all(entry["kwargs"]["style"]["vertical_align"] == "center" for entry in texts) + assert all(entry["kwargs"]["anchor"] == "middle" for entry in texts) + assert all(entry["kwargs"]["color"] == "red" for entry in texts) + ax.pie_label(pie, "{frac:.0%}", textprops={"fontsize": 8}) + assert ax._entries[-1]["kind"] == "@text" + assert ax._entries[-1]["kwargs"]["style"]["font_size"] == 8.0 + + _fig, ax = plt.subplots() + ax.table(cellText=[["a", "b"]], fontsize=10) + cell_texts = [entry for entry in ax._entries if entry["kind"] == "@text"] + assert len(cell_texts) == 2 + assert all(entry["kwargs"]["style"]["font_size"] == 10.0 for entry in cell_texts) diff --git a/tests/pyplot/test_pdsh_gap_features.py b/tests/pyplot/test_pdsh_gap_features.py new file mode 100644 index 00000000..d8f69cd5 --- /dev/null +++ b/tests/pyplot/test_pdsh_gap_features.py @@ -0,0 +1,418 @@ +"""The PDSH-benchmark gap features: locators/formatters, named styles, +clim/gci, colorbar handles, GridSpec spans, and the new colormaps. + +Each block mirrors real notebook usage (Python Data Science Handbook ch. 4), +so a regression here means real scripts break again. +""" + +import io + +import numpy as np +import pytest + +import xy.pyplot as plt + + +@pytest.fixture(autouse=True) +def _clean_state(): + yield + plt.close("all") + plt.rcdefaults() + + +def _png(fig=None): + buffer = io.BytesIO() + (fig or plt.gcf()).savefig(buffer, format="png") + data = buffer.getvalue() + assert data[:4] == b"\x89PNG" + return data + + +def _svg(): + buffer = io.BytesIO() + plt.savefig(buffer, format="svg") + return buffer.getvalue().decode() + + +# -- locators / formatters ------------------------------------------------- + + +def test_axis_proxy_getters_return_ticker_objects(): + fig, ax = plt.subplots() + assert isinstance(ax.xaxis.get_major_locator(), plt.AutoLocator) + assert isinstance(ax.xaxis.get_minor_locator(), plt.NullLocator) + assert isinstance(ax.yaxis.get_major_formatter(), plt.ScalarFormatter) + assert isinstance(ax.yaxis.get_minor_formatter(), plt.NullFormatter) + + +def test_null_locator_removes_ticks_from_the_export(): + fig, ax = plt.subplots() + ax.plot(np.arange(10), np.arange(10)) + ax.yaxis.set_major_locator(plt.NullLocator()) + ax.xaxis.set_major_formatter(plt.NullFormatter()) + assert len(ax.get_yticks()) == 0 + _png() + + +def test_multiple_locator_positions_are_exact_multiples(): + fig, ax = plt.subplots() + x = np.linspace(0, 3 * np.pi, 50) + ax.plot(x, np.sin(x)) + ax.set_xlim(0, 3 * np.pi) + ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2)) + assert np.allclose(ax.get_xticks(), np.arange(0, 3 * np.pi + 1e-9, np.pi / 2)) + + +def test_maxn_locator_caps_tick_count(): + fig, ax = plt.subplots() + ax.plot([0, 63], [0, 63]) + ax.xaxis.set_major_locator(plt.MaxNLocator(3)) + ticks = ax.get_xticks() + assert 2 <= len(ticks) <= 4 + + +def test_func_formatter_labels_reach_the_export(): + fig, ax = plt.subplots() + ax.plot([0, 4], [0, 4]) + ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda v, pos: f"{v:.0f}%")) + assert "%" in _svg() + + +def test_locator_refreshes_as_data_lands(): + fig, ax = plt.subplots() + ax.xaxis.set_major_locator(plt.MultipleLocator(10)) + ax.plot([0, 25], [0, 1]) + before = len(ax.get_xticks()) + ax.plot([0, 95], [0, 1]) + assert len(ax.get_xticks()) > before + + +def test_set_xticks_displaces_a_stored_locator(): + fig, ax = plt.subplots() + ax.plot([0, 100], [0, 1]) + ax.xaxis.set_major_locator(plt.MultipleLocator(10)) + ax.set_xticks([0, 50]) + assert list(ax.get_xticks()) == [0.0, 50.0] + + +def test_explicit_labels_displace_a_stored_formatter(): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + ax.xaxis.set_major_formatter(plt.FuncFormatter(lambda v, p: "F")) + ax.set_xticks([0, 1], labels=["lo", "hi"]) + svg = _svg() + assert "lo" in svg and "F" not in svg + + +def test_set_major_locator_rejects_non_locators(): + fig, ax = plt.subplots() + with pytest.raises(TypeError): + ax.xaxis.set_major_locator(object()) + + +def test_log_axis_formatter_formats_log_ticks(): + fig, ax = plt.subplots() + ax.plot([1, 10, 100], [1, 10, 100]) + ax.set_xscale("log") + ax.xaxis.set_major_formatter(plt.FormatStrFormatter("%g!")) + assert "10!" in _svg() + + +# -- style.context and named styles ------------------------------------------ + + +def test_style_context_applies_and_restores_rcparams(): + base = dict(plt.rcParams) + with plt.style.context("ggplot"): + assert plt.rcParams["axes.facecolor"] == "#E5E5E5" + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + assert ax._grid is True + assert ax._theme_tokens["plot_background"] == "#E5E5E5" + assert ax._prop_cycle[0] == "#E24A33" + assert dict(plt.rcParams) == base + + +@pytest.mark.parametrize( + "name", + [ + "fivethirtyeight", + "ggplot", + "bmh", + "dark_background", + "grayscale", + "seaborn-whitegrid", + "seaborn-v0_8-whitegrid", + "seaborn-v0_8-white", + ], +) +def test_named_styles_render(name): + with plt.style.context(name): + fig, ax = plt.subplots() + ax.plot([0, 1, 2], [0, 1, 4]) + _png(fig) + + +def test_unknown_style_still_fails_loudly(): + with pytest.raises(NotImplementedError): + plt.style.use("solarize_light2") + + +def test_grid_color_rcparam_reaches_the_axes(): + with plt.rc_context({"grid.color": "#123456", "axes.grid": True}): + fig, ax = plt.subplots() + assert ax._grid_color == "#123456" + + +def test_cycler_routes_into_prop_cycle(): + colors = plt.cycler("color", ["#EE6666", "#3388BB"]) + with plt.rc_context({"axes.prop_cycle": colors}): + fig, ax = plt.subplots() + assert ax._prop_cycle == ["#EE6666", "#3388BB"] + with pytest.raises(NotImplementedError): + plt.cycler("linestyle", ["-", "--"]) + + +# -- colormaps ---------------------------------------------------------------- + + +def test_rdgy_and_jet_resolve_and_render(): + assert plt.get_cmap("RdGy").name == "rdgy" + assert plt.get_cmap("RdGy_r").name == "rdgy_r" + assert plt.get_cmap("jet").name == "jet" + fig, ax = plt.subplots() + ax.contour(np.random.default_rng(0).normal(size=(12, 12)), cmap="RdGy") + _png() + + +def test_linear_segmented_colormap_from_list_matches_anchors(): + table = np.array([[0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0, 1.0]]) + cmap = plt.LinearSegmentedColormap.from_list("g", table, 8) + out = cmap(np.arange(8)) + assert out.shape == (8, 4) + assert np.allclose(out[0], [0, 0, 0, 1]) + assert np.allclose(out[-1], [1, 1, 1, 1]) + assert np.all(np.diff(out[:, 0]) > 0) + + +def test_listed_colormap_indexes_discretely(): + cmap = plt.ListedColormap([[1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], name="rb") + assert cmap.N == 2 + assert np.allclose(cmap(np.array([0, 1]))[:, :3], [[1, 0, 0], [0, 0, 1]]) + + +def test_user_colormaps_reject_unresolvable_colors(): + with pytest.raises(ValueError): + plt.LinearSegmentedColormap.from_list("bad", ["definitely-not-a-color"]) + + +def test_cm_get_cmap_returns_resampled_cmap(): + cmap = plt.cm.get_cmap("Blues", 6) + assert cmap.N == 6 + + +# -- gci / clim / colorbar ------------------------------------------------------ + + +def test_gci_tracks_the_last_mappable(): + fig, ax = plt.subplots() + image = plt.imshow(np.eye(3)) + assert plt.gci() is image + collection = plt.scatter([0, 1], [0, 1], c=[0.0, 1.0]) + assert plt.gci() is collection + + +def test_clim_updates_entry_and_live_colorbar(): + fig, ax = plt.subplots() + image = plt.imshow(np.random.default_rng(0).normal(size=(4, 4)), cmap="RdGy") + plt.colorbar() + plt.clim(-1, 1) + assert image._entry["kwargs"]["domain"] == (-1.0, 1.0) + assert ax._colorbar["domain"] == [-1.0, 1.0] + + +def test_clim_one_sided_autoscales_the_other_side(): + fig, ax = plt.subplots() + plt.scatter([0, 1, 2], [0, 1, 2], c=[1.0, 5.0, 9.0]) + plt.clim(2, None) + assert plt.gci()._entry["kwargs"]["domain"] == (2.0, 9.0) + + +def test_clim_without_a_mappable_fails_loudly(): + plt.figure() + with pytest.raises(RuntimeError): + plt.clim(0, 1) + + +def test_colorbar_returns_handle_and_set_label_lands(): + fig, ax = plt.subplots() + plt.hist2d(*np.random.default_rng(0).normal(size=(2, 200)), bins=10) + handle = plt.colorbar() + handle.set_label("counts in bin") + assert ax._colorbar["label"] == "counts in bin" + + +def test_colorbar_ticks_and_extend_reach_both_exports(): + fig, ax = plt.subplots() + image = plt.imshow(np.eye(4), cmap="viridis") + plt.colorbar(image, ticks=[0.25, 0.75], extend="both") + svg = _svg() + assert "0.25" in svg and "0.75" in svg and "polygon" in svg + _png() + + +def test_colorbar_rejects_unknown_kwargs_and_cax(): + fig, ax = plt.subplots() + image = plt.imshow(np.eye(3)) + with pytest.raises(TypeError): + plt.colorbar(image, fraction=0.05) + with pytest.raises(NotImplementedError): + plt.colorbar(image, cax=ax) + + +# -- axes surface ---------------------------------------------------------------- + + +def test_get_figure_returns_owner(): + fig, ax = plt.subplots() + assert ax.get_figure() is fig + + +def test_set_axisbelow_true_only(): + fig, ax = plt.subplots() + ax.set_axisbelow(True) + with pytest.raises(NotImplementedError): + ax.set_axisbelow(False) + + +def test_axes_facecolor_kwarg_lands_in_theme(): + ax = plt.axes(facecolor="#E6E6E6") + assert ax.get_facecolor() == "#E6E6E6" + + +def test_spines_values_hide_all_renders_transparent_axis(): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + for spine in ax.spines.values(): + spine.set_visible(False) + _png() + + +def test_hiding_a_single_left_spine_fails_at_build(): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + ax.spines["left"].set_visible(False) + with pytest.raises(NotImplementedError): + _png() + + +def test_tick_label_handles_recolor_axis_labels(): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + labels = ax.get_xticklabels() + assert labels + for tick in labels: + tick.set_color("gray") + assert ax._axis["x"]["style"]["tick_label_color"] == "gray" + + +def test_shared_axes_group_reflects_static_sharing(): + fig = plt.figure() + grid = plt.GridSpec(2, 2) + first = fig.add_subplot(grid[0, 0]) + second = fig.add_subplot(grid[0, 1], sharey=first) + group = first.get_shared_y_axes() + assert group.joined(first, second) + assert second in group.get_siblings(first) + + +def test_legend_numpoints_default_accepted_others_loud(): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1], label="a") + ax.legend(numpoints=1, scatterpoints=1) + with pytest.raises(NotImplementedError): + ax.legend(numpoints=3) + + +def test_figure_canvas_surface(): + fig = plt.figure() + assert "png" in fig.canvas.get_supported_filetypes() + fig.canvas.draw() + + +# -- GridSpec spans --------------------------------------------------------------- + + +def test_gridspec_span_rects_are_consistent(): + plt.figure() + grid = plt.GridSpec(2, 3, wspace=0.4, hspace=0.3) + single = plt.subplot(grid[0, 0]) + span = plt.subplot(grid[0, 1:]) + assert span._figure_rect[2] > single._figure_rect[2] * 1.8 + assert abs(single._figure_rect[1] - span._figure_rect[1]) < 1e-9 + _png() + + +def test_gridspec_row_zero_is_at_the_top(): + plt.figure() + grid = plt.GridSpec(2, 2, hspace=0.3) + top = plt.subplot(grid[0, 0]) + bottom = plt.subplot(grid[1, 0]) + assert top._figure_rect[1] > bottom._figure_rect[1] + + +def test_single_cell_specs_keep_the_uniform_grid(): + fig = plt.figure() + gs = fig.add_gridspec(2, 2) + ax = fig.add_subplot(gs[0, 1]) + assert ax._figure_rect is None + + +def test_gridspec_flat_and_negative_indexes(): + grid = plt.GridSpec(4, 4) + spec = grid[-1, 1:] + assert spec.rows == (3, 4) + assert spec.cols == (1, 4) + flat = grid[5] + assert flat.rows == (1, 2) and flat.cols == (1, 2) + + +def test_gridspec_step_slicing_fails_loudly(): + grid = plt.GridSpec(4, 4) + with pytest.raises(NotImplementedError): + grid[::2, 0] + + +def test_subplot_mixes_into_free_form_figures(): + fig = plt.figure() + fig.add_axes([0.1, 0.5, 0.8, 0.4]).plot([0, 1], [0, 1]) + ax = plt.subplot(2, 3, 1) + ax.text(0.5, 0.5, "(2, 3, 1)") + again = plt.subplot(2, 3, 1) + assert again is ax + _png() + + +def test_subplots_subplot_kw_applies_everywhere(): + fig, axes = plt.subplots(2, subplot_kw=dict(xticks=[], yticks=[])) + for ax in axes: + assert list(ax._axis["x"].get("tick_values", [])) == [] + _png() + + +def test_add_subplot_xticklabels_empty_hides_labels(): + fig = plt.figure() + grid = plt.GridSpec(2, 2) + ax = fig.add_subplot(grid[0, 0], xticklabels=[]) + assert ax._axis["x"]["tick_label_strategy"] == "none" + + +# -- pandas period interop --------------------------------------------------------- + + +def test_period_values_plot_as_timestamps(): + pd = pytest.importorskip("pandas") + fig, ax = plt.subplots() + (line,) = ax.plot(pd.period_range("2012-01", periods=5, freq="M"), np.arange(5)) + assert np.issubdtype(np.asarray(line.get_xdata()).dtype, np.datetime64) + _png() diff --git a/tests/pyplot/test_pyplot_state_management.py b/tests/pyplot/test_pyplot_state_management.py new file mode 100644 index 00000000..3d8899b1 --- /dev/null +++ b/tests/pyplot/test_pyplot_state_management.py @@ -0,0 +1,53 @@ +import xy.pyplot as plt + + +def teardown_function(): + plt.close("all") + + +def test_pyplot_figure_registry_and_labels(): + plt.figure(3) + plt.figure("named") + + assert plt.fignum_exists(3) + assert plt.fignum_exists("named") + assert "named" in plt.get_figlabels() + assert 3 in plt.get_fignums() + + +def test_pyplot_cla_and_clf_clear_current_scope(): + fig, ax = plt.subplots() + ax.plot([0, 1], [1, 2]) + plt.cla() + assert ax._entries == [] + + ax.plot([0, 1], [2, 3]) + plt.clf() + assert fig.axes == [] + assert plt.gca().figure is fig + + +def test_pyplot_axes_delaxes_figtext_and_figlegend(): + fig = plt.figure() + ax1 = plt.axes([0.1, 0.1, 0.3, 0.3]) + ax2 = plt.axes([0.5, 0.5, 0.3, 0.3]) + assert fig.axes[-1] is ax2 + + text = plt.figtext(0.2, 0.8, "figure note") + assert text._entry["kwargs"]["style"]["coordinate_space"] == "figure_fraction" + + ax1.plot([0, 1], [0, 1], label="line") + plt.figlegend() + assert ax1._legend + + plt.delaxes(ax2) + assert ax2 not in fig.axes + + +def test_pyplot_twiny_creates_current_axes_on_same_figure(): + fig, ax = plt.subplots() + twin = plt.twiny() + assert twin.figure is fig + assert plt.gca() is twin + assert twin in fig.axes + assert twin is not ax diff --git a/tests/pyplot/test_rc_chrome_contracts.py b/tests/pyplot/test_rc_chrome_contracts.py new file mode 100644 index 00000000..7d5292c9 --- /dev/null +++ b/tests/pyplot/test_rc_chrome_contracts.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +import io + +import pytest + +import xy.pyplot as plt + + +class _Cycle: + def by_key(self): + return {"color": ["red", "blue"]} + + +@pytest.fixture(autouse=True) +def _reset(): + plt.close("all") + plt.rcdefaults() + yield + plt.close("all") + plt.rcdefaults() + + +def test_rc_axes_font_label_tick_and_cycle_values_reach_chart_state() -> None: + with plt.rc_context( + { + "axes.facecolor": "#102030", + "axes.edgecolor": "red", + "axes.labelcolor": "blue", + "axes.titlesize": 18, + "axes.labelsize": 14, + "font.family": ["serif"], + "font.size": 12, + "xtick.color": "green", + "xtick.labelsize": 9, + "axes.prop_cycle": _Cycle(), + } + ): + _fig, ax = plt.subplots() + first, second = ax.plot([0, 1], [0, 1], [0, 1], [1, 0]) + ax.set_title("title") + ax.set_xlabel("x") + built = ax._build_chart(640, 480).figure() + + assert first.get_color() == "red" + assert second.get_color() == "blue" + assert built.style["--chart-bg"] == "#102030" + assert built.style["--chart-axis"] == "red" + assert built.style["font-family"] == "serif" + assert built.style["font-size"] == "12px" + assert built.chrome_styles["title"]["font-size"] == "18px" + assert built.chrome_styles["axis_title"] == {"font-size": "14px", "color": "blue"} + assert built.chrome_styles["tick_label"]["font-size"] == "9px" + assert built.axis_options["x"]["style"]["tick_color"] == "green" + + +def test_legend_rc_defaults_reach_legend_component() -> None: + with plt.rc_context( + { + "legend.loc": "upper right", + "legend.fontsize": 13, + "legend.facecolor": "yellow", + "legend.edgecolor": "blue", + "legend.frameon": True, + } + ): + _fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1], label="line") + ax.legend() + assert ax._legend_options["loc"] == "upper right" + assert ax._legend_options["style"] == { + "fontSize": "13px", + "background": "yellow", + "borderColor": "blue", + "borderStyle": "solid", + } + + +def test_spine_and_invalid_cycle_boundaries_fail_loudly() -> None: + with pytest.raises(NotImplementedError, match="cannot hide left"): + plt.rcParams["axes.spines.left"] = False + with pytest.raises(NotImplementedError, match="does not render top"): + plt.rcParams["axes.spines.top"] = True + with pytest.raises(ValueError, match="non-empty color cycle"): + plt.rcParams["axes.prop_cycle"] = object() + + _fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + ax.spines[["top", "right"]].set_visible(False) + # Hiding only one of left/bottom is inexpressible and fails at build time; + # hiding both renders with transparent axis lines. + ax.spines["left"].set_visible(False) + with pytest.raises(NotImplementedError, match="hiding only the left spine"): + _fig.savefig(io.BytesIO(), format="png") + ax.spines["bottom"].set_visible(False) + buffer = io.BytesIO() + _fig.savefig(buffer, format="png") + assert buffer.getvalue()[:4] == b"\x89PNG" diff --git a/tests/pyplot/test_rc_color_export_contracts.py b/tests/pyplot/test_rc_color_export_contracts.py new file mode 100644 index 00000000..7e493aa7 --- /dev/null +++ b/tests/pyplot/test_rc_color_export_contracts.py @@ -0,0 +1,114 @@ +from __future__ import annotations + +import io +import struct + +import numpy as np +import pytest + +import xy.pyplot as plt + + +@pytest.fixture(autouse=True) +def _reset_pyplot_state(): + plt.close("all") + plt.style.use("default") + yield + plt.close("all") + plt.style.use("default") + + +def test_nested_rc_context_restores_each_level_and_rcdefaults() -> None: + original = plt.rcParams["lines.linewidth"] + with plt.rc_context({"lines.linewidth": 2.0}): + assert plt.rcParams["lines.linewidth"] == 2.0 + with plt.rc_context({"lines.linewidth": 7.0}): + assert plt.rcParams["lines.linewidth"] == 7.0 + assert plt.rcParams["lines.linewidth"] == 2.0 + assert plt.rcParams["lines.linewidth"] == original + plt.rcParams["lines.linewidth"] = 9.0 + plt.rcdefaults() + assert plt.rcParams["lines.linewidth"] == 1.5 + + +def test_style_use_supports_bounded_dicts_and_ordered_lists() -> None: + plt.style.use({"lines.linewidth": 3.0}) + assert plt.rcParams["lines.linewidth"] == 3.0 + plt.style.use(["default", {"lines.linewidth": 4.0}]) + assert plt.rcParams["lines.linewidth"] == 4.0 + with pytest.raises(NotImplementedError, match=r"unknown\.style\.key"): + plt.style.use({"unknown.style.key": 1}) + plt.style.use("ggplot") # stock sheets apply their bounded rcParams subset + assert plt.rcParams["axes.facecolor"] == "#E5E5E5" + with pytest.raises(NotImplementedError, match="rcParams dict"): + plt.style.use("Solarize_Light2") + plt.rcdefaults() + + +def test_figure_facecolor_rcparam_affects_new_figures() -> None: + with plt.rc_context({"figure.facecolor": "#123456"}): + assert plt.figure().get_facecolor() == "#123456" + + +def test_colormap_extremes_alpha_and_reversal_are_preserved() -> None: + cmap = plt.colormaps["viridis"].with_extremes( + bad=("red", 0.25), under="#00ff00", over=("blue", 0.75) + ) + rgba = cmap(np.array([np.nan, -1.0, 0.5, 2.0])) + np.testing.assert_allclose(rgba[0], [1.0, 0.0, 0.0, 0.25]) + np.testing.assert_allclose(rgba[1], [0.0, 1.0, 0.0, 1.0]) + np.testing.assert_allclose(rgba[3], [0.0, 0.0, 1.0, 0.75]) + forward = plt.colormaps["viridis"](np.array([0.0, 1.0])) + reverse = plt.colormaps["viridis_r"](np.array([1.0, 0.0])) + np.testing.assert_allclose(forward, reverse) + + +def test_savefig_file_objects_support_common_export_options() -> None: + fig, ax = plt.subplots(figsize=(4, 3), dpi=80) + ax.plot([0, 1], [0, 1]) + with pytest.raises(ValueError, match="requires format"): + fig.savefig(io.BytesIO()) + output = io.BytesIO() + fig.savefig(output, format="png") + assert output.getvalue().startswith(b"\x89PNG\r\n\x1a\n") + transparent = io.BytesIO() + fig.savefig(transparent, format="png", transparent=True) + transparent.seek(0) + assert np.asarray(plt.imread(transparent))[0, 0, 3] == 0 + + metadata = io.BytesIO() + fig.savefig(metadata, format="png", metadata={"Author": "xy", "Title": "chart"}) + assert b"Author\x00xy" in metadata.getvalue() + + regular = io.BytesIO() + tight = io.BytesIO() + fig.savefig(regular, format="png") + fig.savefig(tight, format="png", bbox_inches="tight", pad_inches=0) + regular_size = struct.unpack(">II", regular.getvalue()[16:24]) + tight_size = struct.unpack(">II", tight.getvalue()[16:24]) + # a strict shrink on both axes: equality would mean tight-crop is a no-op + assert tight_size[0] < regular_size[0] and tight_size[1] < regular_size[1] + + colored = io.BytesIO() + fig.savefig(colored, format="png", facecolor="#123456") + colored.seek(0) + np.testing.assert_array_equal(np.asarray(plt.imread(colored))[0, 0, :3], [18, 52, 86]) + for unsupported_format in ("jpeg", "webp", "pdf"): + with pytest.raises(NotImplementedError, match=unsupported_format): + fig.savefig(io.BytesIO(), format=unsupported_format) + + +def test_savefig_dpi_changes_output_dimensions_without_mutating_figure() -> None: + fig, ax = plt.subplots(figsize=(4, 3), dpi=80) + ax.plot([0, 1], [0, 1]) + + def dimensions(dpi: int) -> tuple[int, int]: + output = io.BytesIO() + fig.savefig(output, format="png", dpi=dpi) + return struct.unpack(">II", output.getvalue()[16:24]) + + low = dimensions(60) + high = dimensions(120) + assert high[0] == 2 * low[0] + assert high[1] == 2 * low[1] + assert fig.get_dpi() == 80 diff --git a/tests/pyplot/test_reference_corpus.py b/tests/pyplot/test_reference_corpus.py new file mode 100644 index 00000000..09ebd645 --- /dev/null +++ b/tests/pyplot/test_reference_corpus.py @@ -0,0 +1,136 @@ +"""Run the complete corpus in fresh processes against xy and pinned Matplotlib.""" + +from __future__ import annotations + +import os +import pathlib +import subprocess +import sys +import textwrap +from io import BytesIO + +import numpy as np +import pytest +from tests.pyplot.test_corpus import CORPUS + +import xy.pyplot as xyplt + + +def _adapt_reference_source(path: pathlib.Path, source: str) -> str: + """Normalize the few intentional xy shorthand signatures for Matplotlib. + + The source corpus remains dependency-free. Matplotlib's triangular APIs + require a ``Triangulation`` positional object where xy also accepts a + ``triangles=`` keyword alongside x/y arrays. + """ + if path.name == "49_unstructured_mesh.py": + source = source.replace( + "fig, axes = plt.subplots(1, 3, figsize=(10, 3))", + "from matplotlib.tri import Triangulation\n\n" + "triangulation = Triangulation(x, y, triangles)\n\n" + "fig, axes = plt.subplots(1, 3, figsize=(10, 3))", + ) + source = source.replace( + 'axes[0].tripcolor(x, y, z, triangles=triangles, cmap="viridis")', + 'axes[0].tripcolor(triangulation, z, cmap="viridis")', + ) + source = source.replace( + 'axes[1].triplot(x, y, "k-", triangles=triangles)', + 'axes[1].triplot(triangulation, "k-")', + ) + source = source.replace( + "axes[1].tricontour(x, y, z, triangles=triangles, levels=4)", + "axes[1].tricontour(triangulation, z, levels=4)", + ) + source = source.replace( + 'axes[2].tricontourf(x, y, z, triangles=triangles, levels=5, cmap="plasma")', + 'axes[2].tricontourf(triangulation, z, levels=5, cmap="plasma")', + ) + return source + + +def _has_reference_surface() -> bool: + try: + import matplotlib + from matplotlib.axes import Axes + except ImportError: + return False + version = tuple(int(part) for part in matplotlib.__version__.split(".")[:2]) + return version >= (3, 11) and hasattr(Axes, "grouped_bar") and hasattr(Axes, "pie_label") + + +pytestmark = pytest.mark.skipif( + not _has_reference_surface(), + reason="requires the pinned released Matplotlib 3.11 reference", +) + + +def _run_engine(path: pathlib.Path, engine: str, artifact: pathlib.Path) -> None: + source = path.read_text() + if engine == "matplotlib": + source = source.replace("import xy.pyplot as plt", "import matplotlib.pyplot as plt") + source = _adapt_reference_source(path, source) + bootstrap = "" + if engine == "matplotlib": + # HTML is an xy exporter, not part of Matplotlib's renderer contract. + # Keep those corpus cases exercising all chart construction while making + # their final exporter call an explicit reference-side no-op. + bootstrap = textwrap.dedent( + """ + from pathlib import Path + from matplotlib.figure import Figure + _reference_savefig = Figure.savefig + def _savefig(self, target, *args, **kwargs): + if isinstance(target, (str, Path)) and Path(target).suffix == ".html": + return None + return _reference_savefig(self, target, *args, **kwargs) + Figure.savefig = _savefig + """ + ) + env = os.environ.copy() + env["MPLBACKEND"] = "Agg" + env["XY_REFERENCE_ARTIFACT"] = str(artifact) + capture = textwrap.dedent( + """ + import os as _os + if plt.get_fignums(): + plt.gcf().savefig(_os.environ["XY_REFERENCE_ARTIFACT"], format="png") + """ + ) + subprocess.run( + [sys.executable, "-c", bootstrap + "\n" + source + "\n" + capture], + check=True, + capture_output=True, + env=env, + text=True, + timeout=60, + ) + + +def _artifact_geometry(path: pathlib.Path) -> tuple[float, float]: + pixels = np.asarray(xyplt.imread(BytesIO(path.read_bytes()))) + rgb = pixels[..., :3].astype(np.float64) + corners = np.stack((rgb[0, 0], rgb[0, -1], rgb[-1, 0], rgb[-1, -1])) + background = np.median(corners, axis=0) + foreground = np.linalg.norm(rgb - background, axis=-1) > 12.0 + if pixels.shape[-1] == 4: + foreground &= pixels[..., 3] > 12 + ys, xs = np.nonzero(foreground) + assert len(xs), f"blank reference artifact: {path}" + ink_fraction = float(np.mean(foreground)) + bbox_aspect = float((xs.max() - xs.min() + 1) / (ys.max() - ys.min() + 1)) + return ink_fraction, bbox_aspect + + +@pytest.mark.parametrize("path", CORPUS, ids=lambda path: path.name) +def test_corpus_in_isolated_reference_process(path: pathlib.Path, tmp_path: pathlib.Path) -> None: + artifacts = {engine: tmp_path / f"{engine}.png" for engine in ("xy", "matplotlib")} + for engine, artifact in artifacts.items(): + _run_engine(path, engine, artifact) + assert artifact.exists(), f"{engine} did not produce a comparison artifact" + xy_ink, xy_aspect = _artifact_geometry(artifacts["xy"]) + mpl_ink, mpl_aspect = _artifact_geometry(artifacts["matplotlib"]) + # These are deliberately renderer-tolerant but materially compare output: + # a missing family, wildly wrong layout, or mostly blank render fails. + assert 0.2 < xy_ink / mpl_ink < 5.0 + assert 0.5 < xy_aspect / mpl_aspect < 2.0 diff --git a/tests/pyplot/test_reference_semantics.py b/tests/pyplot/test_reference_semantics.py new file mode 100644 index 00000000..4923940d --- /dev/null +++ b/tests/pyplot/test_reference_semantics.py @@ -0,0 +1,268 @@ +"""Bounded semantic and perceptual comparisons with the pinned reference.""" + +from __future__ import annotations + +from io import BytesIO + +import numpy as np +import pytest + +import xy.pyplot as xyplt + +mpl = pytest.importorskip("matplotlib") +mpl.use("Agg") +import matplotlib.pyplot as mplplt # noqa: E402 +from matplotlib.colors import to_rgba # noqa: E402 + + +@pytest.fixture(autouse=True) +def _isolated_state(): + xyplt.close("all") + mplplt.close("all") + yield + xyplt.close("all") + mplplt.close("all") + + +def test_reference_line_data_cycle_limits_ticks_and_shared_axes() -> None: + xyfig, xyaxes = xyplt.subplots(2, 1, sharex=True) + mplfig, mplaxes = mplplt.subplots(2, 1, sharex=True) + del mplfig + for axes in (xyaxes, mplaxes): + axes[0].plot([0, 1, 2], [2, 1, 4]) + axes[0].plot([0, 1, 2], [1, 3, 2]) + axes[0].set_xlim(-0.25, 2.25) + axes[0].set_ylim(0.5, 4.5) + axes[0].set_xticks([0, 1, 2], ["a", "b", "c"]) + axes[0].set_xlabel("category") + for xyline, mplline in zip(xyaxes[0].lines, mplaxes[0].lines, strict=True): + np.testing.assert_array_equal(xyline.get_xdata(), mplline.get_xdata()) + np.testing.assert_array_equal(xyline.get_ydata(), mplline.get_ydata()) + np.testing.assert_allclose(to_rgba(xyline.get_color()), to_rgba(mplline.get_color())) + np.testing.assert_allclose(xyaxes[0].get_xlim(), mplaxes[0].get_xlim()) + np.testing.assert_allclose(xyaxes[0].get_ylim(), mplaxes[0].get_ylim()) + assert xyfig._sharex + assert mplaxes[0].get_shared_x_axes().joined(mplaxes[0], mplaxes[1]) + np.testing.assert_array_equal(xyaxes[0].get_xticks(), mplaxes[0].get_xticks()) + assert xyaxes[0].get_xlabel() == mplaxes[0].get_xlabel() + + +def test_reference_bar_geometry_stacking_and_container_shape() -> None: + xyfig, xyax = xyplt.subplots() + mplfig, mplax = mplplt.subplots() + del xyfig, mplfig + x = np.array([0.0, 1.0, 2.0]) + heights = np.array([2.0, 3.0, 1.0]) + bottoms = np.array([1.0, 0.5, 2.0]) + xycontainer = xyax.bar(x, heights, width=0.6, bottom=bottoms, label="values") + mplcontainer = mplax.bar(x, heights, width=0.6, bottom=bottoms, label="values") + mpl_centers = [patch.get_x() + patch.get_width() / 2 for patch in mplcontainer.patches] + np.testing.assert_allclose(xycontainer.position_centers, mpl_centers) + np.testing.assert_allclose(xycontainer.bottoms, [patch.get_y() for patch in mplcontainer]) + np.testing.assert_allclose( + xycontainer.tops, [patch.get_y() + patch.get_height() for patch in mplcontainer] + ) + assert np.asarray(xycontainer.datavalues).shape == mplcontainer.datavalues.shape + + +@pytest.mark.parametrize( + "options", + [ + {"stacked": True}, + {"density": True}, + {"cumulative": True}, + {"density": True, "cumulative": True}, + {"stacked": True, "cumulative": True}, + ], +) +def test_reference_histogram_counts_edges_density_cumulative_and_stacking(options) -> None: + values = [np.array([0.0, 0.2, 0.8, 1.5]), np.array([0.1, 0.7, 1.2, 1.8])] + weights = [np.array([1.0, 2.0, 1.0, 3.0]), np.array([2.0, 1.0, 2.0, 1.0])] + _xyfig, xyax = xyplt.subplots() + _mplfig, mplax = mplplt.subplots() + xycounts, xyedges, xycontainers = xyax.hist( + values, bins=[0, 0.5, 1, 2], weights=weights, **options + ) + mplcounts, mpledges, mplcontainers = mplax.hist( + values, bins=[0, 0.5, 1, 2], weights=weights, **options + ) + np.testing.assert_allclose(xycounts, mplcounts) + np.testing.assert_allclose(xyedges, mpledges) + assert len(xycontainers) == len(mplcontainers) + + +def test_reference_image_extent_dimensions_origin_and_normalization_domain() -> None: + data = np.arange(12.0).reshape(3, 4) + extent = (10.0, 14.0, -2.0, 1.0) + _xyfig, xyax = xyplt.subplots() + _mplfig, mplax = mplplt.subplots() + xyimage = xyax.imshow(data, extent=extent, origin="lower", vmin=2.0, vmax=9.0) + mplimage = mplax.imshow(data, extent=extent, origin="lower", vmin=2.0, vmax=9.0) + assert np.asarray(xyimage.get_array()).shape == np.asarray(mplimage.get_array()).shape + np.testing.assert_array_equal(xyimage.get_array(), mplimage.get_array()) + np.testing.assert_allclose(xyimage.get_extent(), mplimage.get_extent()) + np.testing.assert_allclose(xyimage._entry["kwargs"]["domain"], mplimage.get_clim()) + assert (xyax.get_ylim()[0] > xyax.get_ylim()[1]) == mplax.yaxis_inverted() + + +def test_reference_contour_levels_and_triangle_topology() -> None: + grid = np.arange(16.0).reshape(4, 4) + _xyfig, xyax = xyplt.subplots() + _mplfig, mplax = mplplt.subplots() + xy_contour = xyax.contour(grid, levels=[3.0, 7.0, 11.0]) + mpl_contour = mplax.contour(grid, levels=[3.0, 7.0, 11.0]) + np.testing.assert_allclose(xy_contour.levels, mpl_contour.levels) + + x = np.array([0.0, 1.0, 0.0, 1.0]) + y = np.array([0.0, 0.0, 1.0, 1.0]) + triangles = np.array([[0, 1, 2], [1, 3, 2]]) + values = np.array([0.0, 1.0, 2.0, 3.0]) + xy_tri = xyax.tripcolor(x, y, values, triangles=triangles) + mpl_tri = mplax.tripcolor(x, y, triangles, values) + assert len(xy_tri._entry["args"][0]) == len(mpl_tri.get_paths()) + np.testing.assert_allclose(xyax.get_xlim(), mplax.get_xlim(), atol=0.06) + np.testing.assert_allclose(xyax.get_ylim(), mplax.get_ylim(), atol=0.06) + + +def test_reference_vector_directions_scatter_masks_and_removable_handles() -> None: + x = np.array([0.0, 1.0, 2.0]) + y = np.array([1.0, 2.0, 3.0]) + u = np.array([1.0, 0.0, -1.0]) + v = np.array([0.0, 1.0, 0.0]) + _xyfig, xyax = xyplt.subplots() + _mplfig, mplax = mplplt.subplots() + xy_quiver = xyax.quiver(x, y, u, v, scale=1) + mpl_quiver = mplax.quiver(x, y, u, v, scale=1) + np.testing.assert_allclose(mpl_quiver.X, x) + np.testing.assert_allclose(mpl_quiver.Y, y) + shafts = np.arange(0, len(xy_quiver._entry["args"][0]), 3) + dx = xy_quiver._entry["args"][2][shafts] - xy_quiver._entry["args"][0][shafts] + dy = xy_quiver._entry["args"][3][shafts] - xy_quiver._entry["args"][1][shafts] + np.testing.assert_allclose(np.arctan2(dy, dx), np.arctan2(v, u)) + + colors = np.ma.array([0.0, 1.0, 2.0], mask=[False, True, False]) + xy_scatter = xyax.scatter(x, y, c=colors, s=[4.0, 9.0, 16.0]) + mpl_scatter = mplax.scatter(x, y, c=colors, s=[4.0, 9.0, 16.0]) + np.testing.assert_array_equal( + np.ma.getmaskarray(xy_scatter.get_array()), np.ma.getmaskarray(mpl_scatter.get_array()) + ) + before_xy, before_mpl = len(xyax.collections), len(mplax.collections) + xy_scatter.remove() + mpl_scatter.remove() + assert len(xyax.collections) == before_xy - 1 + assert len(mplax.collections) == before_mpl - 1 + + +def test_reference_truecolor_rgba_is_preserved() -> None: + rgba = np.array( + [ + [[1.0, 0.0, 0.0, 0.25], [0.0, 1.0, 0.0, 0.5]], + [[0.0, 0.0, 1.0, 0.75], [1.0, 1.0, 1.0, 1.0]], + ] + ) + _xyfig, xyax = xyplt.subplots() + _mplfig, mplax = mplplt.subplots() + xy_image = xyax.imshow(rgba, interpolation="nearest") + mpl_image = mplax.imshow(rgba, interpolation="nearest") + np.testing.assert_allclose(xy_image.get_array(), mpl_image.get_array()) + + +def _png_pixels(data: bytes) -> np.ndarray: + pixels = np.asarray(xyplt.imread(BytesIO(data)), dtype=np.float64) + if pixels.max(initial=0.0) > 1.0: + pixels /= 255.0 + return pixels + + +def _foreground_mask(pixels: np.ndarray) -> np.ndarray: + rgb = pixels[..., :3] + corners = np.stack((rgb[0, 0], rgb[0, -1], rgb[-1, 0], rgb[-1, -1])) + background = np.median(corners, axis=0) + distance = np.linalg.norm(rgb - background, axis=-1) + alpha = pixels[..., 3] if pixels.shape[-1] == 4 else np.ones(distance.shape) + return (distance > 0.08) & (alpha > 0.1) + + +# Per-family floors for the cross-engine mask IoU; the negative control below +# asserts a wrong-geometry render scores under every one of these. +MINIMUM_IOU = {"line": 0.20, "bar": 0.70, "image": 0.55} + + +def _dilate(mask: np.ndarray, radius: int = 5) -> np.ndarray: + padded = np.pad(mask, radius) + result = np.zeros_like(mask) + for dy in range(2 * radius + 1): + for dx in range(2 * radius + 1): + result |= padded[dy : dy + mask.shape[0], dx : dx + mask.shape[1]] + return result + + +def _normalize_mask(mask: np.ndarray, size: int = 256) -> np.ndarray: + """Crop renderer margins and fit geometry into an aspect-preserving box.""" + ys, xs = np.nonzero(mask) + if not len(xs): + return np.zeros((size, size), dtype=bool) + crop = mask[ys.min() : ys.max() + 1, xs.min() : xs.max() + 1] + scale = size / max(crop.shape) + height = max(1, round(crop.shape[0] * scale)) + width = max(1, round(crop.shape[1] * scale)) + yi = np.linspace(0, crop.shape[0] - 1, height).astype(int) + xi = np.linspace(0, crop.shape[1] - 1, width).astype(int) + result = np.zeros((size, size), dtype=bool) + y0, x0 = (size - height) // 2, (size - width) // 2 + result[y0 : y0 + height, x0 : x0 + width] = crop[np.ix_(yi, xi)] + return result + + +@pytest.mark.parametrize("family", ["line", "bar", "image"]) +def test_reference_pngs_have_tolerant_perceptual_and_geometry_agreement(family: str) -> None: + # xy's static renderer has a documented 640x480 minimum canvas. Render the + # reference at that same comparison canvas; figsize parity itself is not an + # exact contract at sub-minimum sizes. + xyfig, xyax = xyplt.subplots(figsize=(4, 3), dpi=80) + mplfig, mplax = mplplt.subplots(figsize=(8, 6), dpi=80) + if family == "line": + for ax in (xyax, mplax): + ax.plot([0, 1, 2, 3], [0, 2, 1, 3], "o-", color="#2563eb", linewidth=2) + ax.set(xlabel="x", ylabel="y", title="line") + elif family == "bar": + for ax in (xyax, mplax): + ax.bar([0, 1, 2], [2, 4, 3], color="#f97316", width=0.7) + ax.set_title("bar") + else: + grid = np.arange(25.0).reshape(5, 5) + for ax in (xyax, mplax): + ax.imshow(grid, origin="lower", cmap="viridis", interpolation="nearest") + ax.set_title("image") + xybytes = xyfig._to_png() + reference = BytesIO() + mplfig.savefig(reference, format="png", dpi=80) + xypixels, mplpixels = _png_pixels(xybytes), _png_pixels(reference.getvalue()) + assert xypixels.shape == mplpixels.shape == (480, 640, 4) + xymask, mplmask = _foreground_mask(xypixels), _foreground_mask(mplpixels) + normalized_xy = _dilate(_normalize_mask(xymask)) + normalized_mpl = _dilate(_normalize_mask(mplmask)) + overlap = np.count_nonzero(normalized_xy & normalized_mpl) + union = np.count_nonzero(normalized_xy | normalized_mpl) + assert overlap / max(1, union) > MINIMUM_IOU[family] + xy_fraction = np.mean(xymask) + mpl_fraction = np.mean(mplmask) + assert 0.5 < xy_fraction / mpl_fraction < 2.0 + xy_luma = np.mean(xypixels[..., :3], axis=-1)[xymask].mean() + mpl_luma = np.mean(mplpixels[..., :3], axis=-1)[mplmask].mean() + assert abs(xy_luma - mpl_luma) < 0.20 + + +def test_perceptual_oracle_rejects_blank_and_wrong_geometry() -> None: + reference = np.zeros((100, 100), dtype=bool) + reference[20:80, 45:55] = True + blank = np.zeros_like(reference) + wrong = np.zeros_like(reference) + wrong[45:55, 20:80] = True + for candidate in (blank, wrong): + left = _dilate(_normalize_mask(candidate)) + right = _dilate(_normalize_mask(reference)) + iou = np.count_nonzero(left & right) / max(1, np.count_nonzero(left | right)) + # tied to the live thresholds: loosening MINIMUM_IOU below the score a + # wrong-geometry render can reach must fail here, not pass silently + assert iou < min(MINIMUM_IOU.values()) diff --git a/tests/pyplot/test_silent_drop_regressions.py b/tests/pyplot/test_silent_drop_regressions.py new file mode 100644 index 00000000..289de43f --- /dev/null +++ b/tests/pyplot/test_silent_drop_regressions.py @@ -0,0 +1,378 @@ +"""Regressions from the adversarial completion review: values that previously +crashed, were silently dropped, or bypassed validation must now behave.""" + +import ast +import warnings +from pathlib import Path + +import numpy as np +import pytest + +import xy.pyplot as plt +from xy.pyplot._colors import Cmap + + +def test_public_adapters_cannot_discard_parameters_without_an_explicit_marker(): + """Mechanical guard against newly accepted-and-dropped kwargs. + + A deliberate compatibility no-op must carry an inline ``compat-noop:`` + explanation. Bare ``kwargs.pop`` calls and deleting named public method + parameters otherwise fail automatically; no hand-maintained keyword list + is involved. + """ + root = Path(__file__).resolve().parents[2] / "python" / "xy" / "pyplot" + violations = [] + for path in root.glob("*.py"): + source = path.read_text() + lines = source.splitlines() + tree = ast.parse(source, filename=str(path)) + for function in ( + node + for node in ast.walk(tree) + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) + and not node.name.startswith("_") + ): + parameters = { + arg.arg + for arg in ( + *function.args.posonlyargs, + *function.args.args, + *function.args.kwonlyargs, + ) + } + for node in ast.walk(function): + if isinstance(node, ast.Expr) and isinstance(node.value, ast.Call): + marked = "compat-noop:" in lines[node.lineno - 1] + call = node.value + if ( + isinstance(call.func, ast.Attribute) + and call.func.attr == "pop" + and isinstance(call.func.value, ast.Name) + and call.func.value.id in {"kwargs", "options"} + and not marked + ): + violations.append( + f"{path.name}:{node.lineno} bare {call.func.value.id}.pop" + ) + if isinstance(node, ast.Delete): + marked = "compat-noop:" in lines[node.lineno - 1] + discarded = { + target.id for target in node.targets if isinstance(target, ast.Name) + } & parameters + if discarded and not marked: + violations.append(f"{path.name}:{node.lineno} deletes {sorted(discarded)}") + assert not violations, "unexplained accepted-and-dropped options:\n" + "\n".join(violations) + + +def teardown_function(): + plt.close("all") + plt.rcdefaults() + + +def test_legend_prop_dict_maps_size_and_rejects_other_font_properties(): + _, ax = plt.subplots() + ax.plot([0, 1], [0, 1], label="a") + + ax.legend(prop={"size": 8}) + assert ax._legend_options["style"]["fontSize"] == "8px" + + with pytest.raises(NotImplementedError): + ax.legend(prop={"family": "serif"}) + + +def test_zero_marker_edge_width_and_size_are_not_treated_as_unset(): + _, ax = plt.subplots() + (line,) = ax.plot([0, 1], [0, 1], "o", mec="black", mew=0) + assert line._entry["kwargs"]["stroke_width"] == 0.0 + + collection = ax.scatter([0, 1], [0, 1], edgecolors="black", linewidths=0) + assert collection._entry["kwargs"]["stroke_width"] == 0.0 + + +def test_text_visibility_and_alpha_reach_rendered_output(): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + text = ax.text(0.5, 0.5, "SECRET") + + text.set_visible(False) + svg = ax._build_chart(640, 480).figure().to_svg() + assert "SECRET" not in svg + + text.set_visible(True) + text.set_alpha(0.5) + ax._chart = None + svg = ax._build_chart(640, 480).figure().to_svg() + assert "SECRET" in svg + assert 'fill-opacity="0.5"' in svg + + +def test_set_visible_true_on_visible_artist_preserves_alpha(): + _, ax = plt.subplots() + (line,) = ax.plot([0, 1], [0, 1]) + line.set_alpha(0.5) + line.set_visible(True) # no-op in matplotlib; must not clobber alpha + assert line.get_alpha() == 0.5 + + line.set_alpha(0.3) + line.set_visible(False) + assert line.get_alpha() == 0.3 + line.set_visible(True) + assert line._entry["kwargs"]["opacity"] == 0.3 + + +def test_set_rasterized_true_fails_loudly(): + _, ax = plt.subplots() + (line,) = ax.plot([0, 1], [0, 1]) + with pytest.raises(NotImplementedError): + line.set_rasterized(True) + line.set_rasterized(False) + assert line.get_rasterized() is False + + +def test_hist_stepfilled_produces_filled_step_geometry(): + _, ax = plt.subplots() + counts, edges, _ = ax.hist([0, 1, 1, 2, 2, 2], bins=3, histtype="stepfilled") + + entry = ax._entries[-1] + assert entry["kind"] == "@mark" + assert entry["factory"] == "area" + xs, tops = entry["args"] + np.testing.assert_allclose(xs, np.repeat(edges, 2)[1:-1]) + np.testing.assert_allclose(tops, np.repeat(counts, 2)) + + +def test_scatter_one_sided_vmin_vmax_autoscale_the_other_side(): + _, ax = plt.subplots() + low = ax.scatter([0, 1, 2], [0, 1, 2], c=[1.0, 5.0, 9.0], vmin=2.0) + assert low._entry["kwargs"]["domain"] == (2.0, 9.0) + + high = ax.scatter([0, 1, 2], [0, 1, 2], c=[1.0, 5.0, 9.0], vmax=6.0) + assert high._entry["kwargs"]["domain"] == (1.0, 6.0) + + +def test_clear_reapplies_current_rc_chrome_not_just_prop_cycle(): + with plt.rc_context({"axes.facecolor": "#102030", "font.size": 20.0}): + _, ax = plt.subplots() + ax.cla() + assert ax._theme_tokens["plot_background"] == "#102030" + assert ax._theme_style["font-size"] == "20px" + + +def test_auto_ticks_report_exporter_locations_instead_of_empty(): + _, ax = plt.subplots() + ax.plot([0, 10], [0, 100]) + np.testing.assert_allclose(ax.get_xticks(), [0.0, 2.0, 4.0, 6.0, 8.0, 10.0]) + + ax.set_yscale("log") + ax.set_ylim(1, 1000) + ticks = ax.get_yticks() + assert ticks[0] == 1.0 and ticks[-1] == 1000.0 and len(ticks) > 2 + + ax.set_xticks([1.0, 5.0]) + np.testing.assert_allclose(ax.get_xticks(), [1.0, 5.0]) + + +def test_savefig_without_extension_defaults_to_png(tmp_path): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + fig.savefig(tmp_path / "noext") + data = (tmp_path / "noext.png").read_bytes() + assert data[:8] == b"\x89PNG\r\n\x1a\n" + + +def test_rc_update_paths_enforce_the_same_validation_as_setitem(): + with pytest.raises(NotImplementedError), plt.rc_context({"axes.spines.left": False}): + pass + with pytest.raises(NotImplementedError): + plt.style.use({"axes.spines.top": True}) + + plt.style.use({"font.size": "14"}) + assert plt.rcParams["font.size"] == 14.0 + + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + with plt.rc_context({"lines.antialiased": True}): + pass + assert any("lines.antialiased" in str(item.message) for item in caught) + + +def test_rcdefaults_is_immune_to_list_default_mutation(): + plt.rcParams["font.family"].append("Comic Sans") + plt.rcdefaults() + assert plt.rcParams["font.family"] == ["sans-serif"] + + +def test_cmap_extremes_accept_tuple_colors_in_call_and_imshow_paths(): + cmap = Cmap("viridis") + cmap.set_bad((1.0, 0.0, 0.0)) + cmap.set_under((0.0, 1.0, 0.0), alpha=0.5) + cmap.set_over(("red", 0.25)) + + rgba = cmap(np.array([np.nan, -1.0, 2.0])) + np.testing.assert_allclose(rgba[0], [1.0, 0.0, 0.0, 1.0]) + np.testing.assert_allclose(rgba[1], [0.0, 1.0, 0.0, 0.5]) + np.testing.assert_allclose(rgba[2], [1.0, 0.0, 0.0, 0.25]) + + _, ax = plt.subplots() + grid = np.array([[0.0, 1.0], [np.nan, 0.5]]) + image = ax.imshow(grid, cmap=cmap.with_extremes(bad=(0.0, 0.0, 1.0))) + assert image is not None + + +def test_set_cmap_validates_and_feeds_default_colormaps(): + with pytest.raises(ValueError, match="unsupported colormap"): + plt.set_cmap("notacmap") + plt.set_cmap("magma") + _, ax = plt.subplots() + ax.imshow(np.arange(9.0).reshape(3, 3)) + assert ax._entries[-1]["kwargs"]["colormap"] == "magma" + ax.scatter([1, 2, 3], [1, 2, 3], c=[0.1, 0.5, 0.9]) + assert ax._entries[-1]["kwargs"]["colormap"] == "magma" + + +def test_imsave_normalizes_before_quantizing(): + import io + + data = np.linspace(1000.0, 2000.0, 64).reshape(8, 8) + buffer = io.BytesIO() + plt.imsave(buffer, data, cmap="viridis") + buffer.seek(0) + pixels = plt.imread(buffer) + # quantize-then-normalize collapsed this ramp to one uniform color + assert len(np.unique(pixels.reshape(-1, pixels.shape[-1]), axis=0)) > 30 + + +def test_scatter_drops_rows_masked_in_x_y_or_s(): + _, ax = plt.subplots() + x = np.ma.masked_array([1.0, 2.0, 3.0, 4.0], mask=[False, True, False, False]) + y = np.ma.masked_array([1.0, 2.0, 3.0, 4.0], mask=[False, False, True, False]) + ax.scatter(x, y) + assert len(np.asarray(ax._entries[-1]["x"])) == 2 + sizes = np.ma.masked_array([10.0, 20.0, 30.0], mask=[False, True, False]) + ax.scatter([1, 2, 3], [1, 2, 3], s=sizes) + assert len(np.asarray(ax._entries[-1]["x"])) == 2 + + +def test_fill_between_interpolate_draws_single_point_regions(): + x = np.array([0.0, 1.0, 2.0]) + y1 = np.array([-3.0, 1.0, -1.0]) + _, ax = plt.subplots() + ax.fill_between(x, y1, 0.0, where=y1 > 0, interpolate=True) + visible = [ + entry for entry in ax._entries if not np.all(np.isnan(np.asarray(entry["y"], dtype=float))) + ] + assert len(visible) == 1 + # matplotlib's wedge for this input crosses zero at x=0.75 and x=1.5 + np.testing.assert_allclose(sorted(np.asarray(visible[0]["x"], dtype=float)), [0.75, 1.0, 1.5]) + + +def test_fill_betweenx_data_keys_resolve(): + _, ax = plt.subplots() + ax.fill_betweenx("a", "b", data={"a": [0.0, 1.0, 2.0], "b": [1.0, 2.0, 3.0]}) + assert len(ax._entries) == 1 + + +def test_savefig_svg_and_html_honor_facecolor_and_single_chart_suptitle(): + import io + + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + fig.suptitle("SoloTitle") + svg = io.BytesIO() + fig.savefig(svg, format="svg", facecolor="red") + assert b"SoloTitle" in svg.getvalue() + assert b'' in svg.getvalue() + html_out = io.BytesIO() + fig.savefig(html_out, format="html", facecolor="red") + assert b"background-color:red" in html_out.getvalue() + with pytest.raises(NotImplementedError): + fig.savefig(io.BytesIO(), format="html", metadata={"Title": "x"}) + with pytest.raises(ValueError, match="Latin-1"): + fig.savefig(io.BytesIO(), format="png", metadata={"键": "v"}) + + +def test_boxplot_component_styles_and_sym_are_honored_or_rejected(): + _, ax = plt.subplots() + with pytest.raises(NotImplementedError, match="linestyle"): + ax.boxplot([[1.0, 2.0, 3.0, 4.0]], medianprops={"linestyle": "--"}) + # empty sym suppresses fliers like matplotlib + data = [[1.0, 2.0, 3.0, 4.0, 50.0]] + result = ax.boxplot(data, sym="") + assert result["fliers"] == [] + # a fmt sym styles the fliers instead of vanishing + result = ax.boxplot(data, sym="r+") + assert result["fliers"] + flier_kwargs = result["fliers"][0]._entry["kwargs"] + assert flier_kwargs["color"] != result["boxes"][0]._entry["kwargs"].get("color") + # flierprops face color reaches the drawn dots + result = ax.boxplot(data, flierprops={"markerfacecolor": "#00ff00"}, notch=True) + assert result["fliers"][0]._entry["kwargs"]["color"] == "#00ff00" + + +def test_boxplot_usermedians_keep_data_derived_notches(): + values = np.asarray([1.0, 2.0, 2.5, 3.0, 4.0]) + _, ax = plt.subplots() + result = ax.boxplot([values], notch=True, usermedians=[10.0]) + outline = result["boxes"][0]._entry["args"] + ys = np.concatenate([np.asarray(outline[1]), np.asarray(outline[3])]) + q1, med, q3 = np.percentile(values, [25, 50, 75]) + delta = 1.57 * (q3 - q1) / np.sqrt(len(values)) + assert np.isclose(ys, med - delta).any() and np.isclose(ys, med + delta).any() + assert not np.isclose(ys, 10.0 - delta).any() + + +def test_violinplot_constant_data_draws_without_crashing(): + _, ax = plt.subplots() + result = ax.violinplot([[3.0, 3.0]], bw_method="scott") + assert len(result["bodies"]) == 1 + + +def test_secondary_axis_set_ticks_rejects_unknown_options(): + _, ax = plt.subplots() + ax.plot([0.0, 1.0], [0.0, 1.0]) + secondary = ax.secondary_xaxis("top") + with pytest.raises(TypeError): + secondary.set_ticks([0.0, 0.5], minor=True) + + +def test_logit_scale_masks_domain_edges_instead_of_inf(): + _, ax = plt.subplots() + ax.plot([0.0, 1.0, 2.0], [0.0, 0.5, 1.0]) + ax.set_yscale("logit") + values = np.asarray(ax._entries[0]["y"], dtype=float) + assert not np.isinf(values).any() + lo, hi = ax.get_ylim() + assert np.isfinite([lo, hi]).all() + + +def test_nonlinear_scale_ticks_follow_new_data(): + _, ax = plt.subplots() + ax.plot([0.0, 1.0], [-1.0, 1.0]) + ax.set_yscale("symlog") + ax.plot([0.0, 1.0], [-1000.0, 1000.0]) + ticks = ax.get_yticks() + assert ticks.min() <= -100.0 and ticks.max() >= 100.0 + # explicit ticks win and stay labeled in data units + ax.set_yticks([-100.0, 0.0, 100.0]) + assert ax._axis_props("y")["tick_labels"] == ["-100", "0", "100"] + # returning to linear drops the generated ticks + other = plt.figure().add_subplot(111) + other.plot([0.0, 1.0], [-1000.0, 1000.0]) + other.set_yscale("symlog") + other.set_yscale("linear") + assert "tick_values" not in other._axis_props("y") + + +def test_data_artists_reject_fraction_space_transforms(): + _, ax = plt.subplots() + with pytest.raises(NotImplementedError, match="transAxes"): + ax.plot([0.25, 0.75], [0.5, 0.5], transform=ax.transAxes) + + +def test_set_transform_rejects_singular_matrices_immediately(): + from xy.pyplot._transforms import Affine2D + + _, ax = plt.subplots() + (line,) = ax.plot([0.0, 1.0], [0.0, 1.0]) + with pytest.raises(ValueError, match="invertible"): + line.set_transform(Affine2D(np.zeros((3, 3)))) diff --git a/tests/pyplot/test_visible_style_contracts.py b/tests/pyplot/test_visible_style_contracts.py new file mode 100644 index 00000000..c9b56c2a --- /dev/null +++ b/tests/pyplot/test_visible_style_contracts.py @@ -0,0 +1,84 @@ +import pytest + +import xy.pyplot as plt + + +def teardown_function(): + plt.close("all") + + +def test_line_cap_and_gapcolor_mutations_fail_loudly(): + _, ax = plt.subplots() + line = ax.plot([0, 1], [0, 1])[0] + + with pytest.raises(NotImplementedError): + line.set_dash_capstyle("round") + with pytest.raises(NotImplementedError): + line.set_solid_capstyle("round") + with pytest.raises(NotImplementedError): + line.set_gapcolor("red") + + +def test_text_preserves_visible_font_alignment_and_rotation_style(): + _, ax = plt.subplots() + text = ax.text( + 0.2, + 0.3, + "styled", + ha="center", + va="top", + fontweight="bold", + fontfamily="serif", + rotation=45, + fontsize=12, + ) + + assert text._entry["kwargs"]["anchor"] == "middle" + assert text._entry["kwargs"]["style"] == { + "font_size": 12.0, + "vertical_align": "top", + "font_weight": "bold", + "font_family": "serif", + "rotation": 45.0, + } + + +def test_annotate_preserves_arrow_bbox_alignment_rotation_and_font_style(): + _, ax = plt.subplots() + note = ax.annotate( + "note", + (1, 2), + xytext=(5, 6), + arrowprops={"arrowstyle": "->", "color": "red"}, + bbox={"boxstyle": "round", "facecolor": "white"}, + ha="right", + va="bottom", + family="monospace", + weight="bold", + rotation=30, + fontsize=9, + ) + + kwargs = note._entry["kwargs"] + assert kwargs["arrowprops"] == {"arrowstyle": "->", "color": "red"} + assert kwargs["bbox"] == {"boxstyle": "round", "facecolor": "white"} + assert kwargs["anchor"] == "end" + assert kwargs["dx"] == 4.0 + assert kwargs["dy"] == 4.0 + assert kwargs["style"] == { + "font_size": 9.0, + "vertical_align": "bottom", + "font_weight": "bold", + "font_family": "monospace", + "rotation": 30.0, + } + + +def test_bar_align_edge_uses_edge_geometry_instead_of_center_approximation(): + _, ax = plt.subplots() + bars = ax.bar([1, 3], [2, 4], width=0.5, align="edge") + + assert list(bars._entry["x"]) == [1.25, 3.25] + assert bars._entry["kwargs"]["width"] == 0.5 + with pytest.raises(ValueError): + ax.bar(["a"], [1], align="edge") diff --git a/tests/test_verify_ci_workflow.py b/tests/test_verify_ci_workflow.py index 2fe51659..109b6df5 100644 --- a/tests/test_verify_ci_workflow.py +++ b/tests/test_verify_ci_workflow.py @@ -29,6 +29,17 @@ def test_ci_workflow_accepts_current_gates() -> None: assert verify_ci_workflow.validate_ci_workflow() == [] +def test_reference_gate_commands_must_be_in_the_named_step(tmp_path: Path) -> None: + workflow = Path(".github/workflows/ci.yml").read_text(encoding="utf-8") + command = " .venv/bin/pytest -q tests/pyplot/test_reference_semantics.py\n" + # Leaving the old verifier's needle elsewhere in the job must not satisfy + # the structural step-local check. + path = tmp_path / "ci.yml" + path.write_text(workflow.replace(command, "") + f"\n# {command.strip()}\n", encoding="utf-8") + errors = verify_ci_workflow.validate_ci_workflow(path) + assert any("reference test commands" in error for error in errors) + + def test_codspeed_workflow_accepts_current_gates() -> None: assert verify_ci_workflow.validate_codspeed_workflow() == [] From 5a81f87e4e5ebed14ba4de6db8cc3762b9099e22 Mon Sep 17 00:00:00 2001 From: Farhan Date: Tue, 14 Jul 2026 02:58:37 +0500 Subject: [PATCH 03/17] feat(pyplot): close the PDSH visual-parity majors (33 -> 8 measured) An image-level audit rendered every PDSH ch4 cell under both engines (per-cell seeded RNG, matplotlib 3.11 reference) and found that cells which *run* mostly did not *look* like matplotlib: 39 major divergences across 105 pairs. This lands the audit docs, the fix rounds, and the re-measured result: 34 match / 38 minor / 8 major over the 84 graded pairs (majors 33 -> 8 on the same notebooks). Legends render explicit label lists with line/marker samples and honor frameon; plot(x, y_2d) advances the color cycle; colorbars carry real count domains and nice ticks; every colormap stop table is resampled exactly from matplotlib 3.11 (plasma's padded tail merged discrete bands; RdBu was a coolwarm alias) in Python and the JS client; discrete N-band colormaps quantize marks and colorbars; contours dash negatives at matplotlib's level counts; insets keep their rect beside default axes in PNG; sharex/sharey accepts all/col/row with per-group domains and inner-label hiding via a new labels-only "off" strategy; annotate places text at xytext and draws a real arrow; tick labels keep locator precision; log axes label decades 10^k; a bounded TeX-subset converter turns $...$ into unicode across all shim text; scatter s= keeps matplotlib's absolute area semantics; errorbar color=/ecolor= resolve independently (regression caught by re-grading). The native rasterizer bakes greek/super-subscript/math glyphs, takes UTF-8 text, and rotates y-axis titles; the text wire changed, so ABI_VERSION bumps 32 -> 33 and the abi_smoke heatmap expectation moves to the corrected NaN-transparent/zero-opaque semantics. Explicitly out of scope, by direction: no runtime fallback onto real matplotlib, ever (seaborn-internal drawing paths fail loudly at import in a matplotlib-free environment and stay soft evidence only). Remaining boundaries are enumerated in the compat changelog. --- docs/matplotlib-compat-changelog.md | 107 ++ examples/pdsh/README.md | 26 + js/src/10_colormaps.js | 61 +- js/src/40_gl.js | 30 +- js/src/50_chartview.js | 5 +- python/xy/_native.py | 2 +- python/xy/_raster.py | 176 ++- python/xy/_svg.py | 544 ++++--- python/xy/_validate.py | 6 +- python/xy/channels.py | 1 + python/xy/components.py | 7 + python/xy/marks.py | 76 +- python/xy/pyplot/__init__.py | 18 +- python/xy/pyplot/_axes.py | 606 +++++--- python/xy/pyplot/_colors.py | 4 +- python/xy/pyplot/_mathtext.py | 150 ++ python/xy/pyplot/_mplfig.py | 97 +- python/xy/pyplot/_plot_types.py | 39 +- python/xy/pyplot/_translate.py | 8 +- python/xy/static/index.js | 96 +- python/xy/static/standalone.js | 96 +- scripts/abi_smoke.py | 4 +- scripts/gen_font.py | 22 +- src/font.rs | 1333 ++++++++++++----- src/kernels.rs | 12 +- src/lib.rs | 2 +- src/raster.rs | 169 ++- tests/pyplot/test_axes_layout.py | 2 +- tests/pyplot/test_color_pipeline_fixes.py | 215 +++ tests/pyplot/test_figure_state.py | 4 +- tests/pyplot/test_launch_compat.py | 6 +- tests/pyplot/test_layout_text_parity_fixes.py | 244 +++ tests/pyplot/test_pdsh_gap_features.py | 2 +- tests/pyplot/test_visible_style_contracts.py | 8 +- tests/pyplot/test_visual_parity_fixes.py | 223 +++ tests/test_figure.py | 2 +- tests/test_kernels.py | 7 +- 37 files changed, 3323 insertions(+), 1087 deletions(-) create mode 100644 python/xy/pyplot/_mathtext.py create mode 100644 tests/pyplot/test_color_pipeline_fixes.py create mode 100644 tests/pyplot/test_layout_text_parity_fixes.py create mode 100644 tests/pyplot/test_visual_parity_fixes.py diff --git a/docs/matplotlib-compat-changelog.md b/docs/matplotlib-compat-changelog.md index c2db9a6b..2c6f7a25 100644 --- a/docs/matplotlib-compat-changelog.md +++ b/docs/matplotlib-compat-changelog.md @@ -111,5 +111,112 @@ passing cells. scale returns to linear; explicit `set_*ticks` under a nonlinear scale label the original data values. +### Visual-parity audit — 2026-07-14 (PDSH ch4, image-level) + +The cell pass-rate benchmark (154/171 running) measures *execution*, not +*appearance*. This audit rendered every PDSH cell to PNG under both engines +with per-cell seeded RNG (identical data), and visually graded every image +pair (matplotlib 3.11 reference). Result over the 105 comparable non-3-D +pairs: **38 match (36%), 24 minor divergences (23%), 39 major (37%), +4 missing** — running is far ahead of looking right. The majors cluster +into a dozen root causes, recorded here as known defects until fixed: + +1. `legend(['a', 'b'], loc=...)` — an explicit label list renders no legend + at all (7 cells in the stylesheets notebook alone). +2. `plot(x, y_2d)` does not advance the color cycle; every column draws in + the first cycle color. +3. Count-based mappables (`hist2d`, `hexbin`, KDE rasters) label their + colorbar 0–1 instead of the count domain, colorbar tick labels print raw + floats (`6.01004e-07`) instead of nice steps, and zero-count bins render + empty where Matplotlib paints the 0-color field. +4. `imshow(..., cmap='RdBu')` + `clim` can leave the raster on the default + colormap while the attached colorbar correctly shows RdBu — image and + legend disagree. Discrete colormaps (`get_cmap(name, N)`-style) render + as continuous gradients. +5. Contours: no dashed-negative convention, auto level count runs 2–3× + Matplotlib's, `contourf` bands smooth into gradients. +6. **Mixed-engine measurement artifact (out of scope):** when seaborn — + which draws through real Matplotlib — owns the current figure, + module-level `plt.hist`/`axvline`/`axhline` draw onto xy's own figure, + so three seaborn-notebook cells count as passing while their plt-drawn + content lands elsewhere. This can only occur in a mixed environment: + xy replaces Matplotlib, and without Matplotlib installed seaborn fails + loudly at import. No runtime fallback onto Matplotlib will be added; + the benchmark's seaborn column is soft evidence only. +7. `annotate` draws no arrow and ignores the `xytext` offset. +8. A free-form `plt.axes([x, y, w, h])` inset next to a default axes + renders as an equal side-by-side panel even in PNG (contradicting the + documented free-form-rect exactness); the 04.08 `GridSpec` span pattern + and `sharex`/`sharey` inner tick-label suppression are also lost. +9. Mathtext (`$...$`) is never rendered; tick/axis labels show the raw + source text. +10. `MultipleLocator(π/2)` tick labels round to integers; log axes lack + `10^k` labels and draw gridlines at minor-tick positions. +11. Silent fallbacks: `'p'` pentagon markers render as squares, hexbin as + sparse dots, `errorbar(fmt='.k')` bars stay the default blue, and + bubble-chart `alpha` is dropped. + +Systematic (graded minor, one deliberate decision pending): open L-frame +instead of Matplotlib's boxed axes, gapped histogram bars, horizontal +top-left y-label instead of rotated, coarser default tick density with +trailing zeros dropped, legend swatches that do not reflect line/marker +style, `frameon=False` ignored. + +Method and per-cell ledger: dual-engine PNG dump with ordinal-paired +filenames, 14 independent image-comparison passes, every root cause +re-verified by direct image inspection before recording. + +### Visual-parity fix rounds — 2026-07-14 (Matplotlib 3.11.0 reference) + +Closed the bulk of the audit above, verified by re-rendering every notebook +and re-grading each image pair. Across the 12 comparable notebooks (84 +graded pairs), majors fell **33 → 8** and exact matches rose 25 → 34, with +the audit's biggest single offenders (stylesheet legends, color cycle, +colorbar domains) fully cleared. + +- Legends: explicit label lists (`legend(['a','b'], loc=...)`) render; + entries show line samples (color + dash) or the real marker glyph instead + of colored squares; `frameon=False` removes the box; handles+labels form + relabels without phantom traces. +- Color: `plot(x, y_2d)` advances the prop cycle per column. Every colormap + stop table is now sampled exactly from Matplotlib 3.11 (plasma/inferno/ + magma/rainbow/turbo/cividis/coolwarm had drifted up to 128 channel levels; + plasma's padded tail merged discrete bands), and `RdBu` is the true + ColorBrewer table instead of a coolwarm alias. Count-based mappables + (hist2d/hexbin) label colorbars with real count domains; colorbar ticks + use nice steps; hist2d zero-count bins paint the colormap floor + (NaN stays transparent — the ABI smoke expectation moved with it). + imshow honors reversed/`_r` colormaps and post-hoc `clim`. Discrete + N-level colormaps quantize both marks and colorbar into N bands. +- Contours: dashed-negative convention for single-color contours, + Matplotlib's auto level count, piecewise-constant `contourf` bands. +- Markers: real pentagon/hexagon/star SDFs in the native rasterizer + + SVG/GL equivalents; fmt-string markers keep their shape and fill. +- Errorbars: `fmt`/`color=` reach markers and bars independently of + `ecolor` (a first-round regression caught by the re-grade and fixed). +- Layout: free-form axes rects survive next to default axes in PNG (insets + render in place); `subplots(sharex=/sharey=)` accepts 'all'/'col'/'row', + unions domains per group, and hides inner tick labels via the new + `tick_label_strategy="off"` (labels only — grid, baselines and axis + titles stay; `"none"` keeps its silence-everything sparkline meaning). +- Text: tick labels keep locator-step precision (`MultipleLocator(π/2)` → + 1.57/3.14…); log axes label decades as 10ᵏ and grid majors only; a + bounded TeX-subset → unicode converter (`_mathtext.py`) feeds every shim + text path (labels, titles, ticks, legends, colorbar labels, annotations); + the native rasterizer's baked atlas gained greek/super-subscript/math + glyphs with a UTF-8 text wire (ABI 32 → 33) and rotated y-axis titles. +- Annotate: `xytext` places the text (data coords) and `arrowprops` draws a + real arrow (straight shaft + filled head, `shrink` honored) in PNG/SVG; + curved connectionstyles are approximated by the straight shaft. +- scatter `s=` keeps Matplotlib's absolute area semantics (size arrays no + longer compress into the engine's relative 2–18 px band). + +Remaining visual boundaries after this round (all loud or documented): +marker-variety fidelity for triangle/x variants, contour inline-label +placement density, hexbin's true tessellation, `subplots_adjust` spacing +(currently ignored — to be made loud or honored), `hist(orientation= +'horizontal')`, tick-density/trailing-zero formatting vs Matplotlib's +defaults, legend loc='best' avoidance, and the boxed-axes frame default. + Future entries must identify the Matplotlib release/revision, inventory additions or removals, and any compatibility-level changes. diff --git a/examples/pdsh/README.md b/examples/pdsh/README.md index e39946a1..a9b1d68c 100644 --- a/examples/pdsh/README.md +++ b/examples/pdsh/README.md @@ -50,6 +50,32 @@ a 2-D axes, so only this notebook's 2-D cells pass. ² Soft evidence: seaborn draws through real matplotlib internally, so only the cells calling `plt` directly exercise the shim. +## Visual parity (the honest asterisk on the scorecard) + +A cell "passing" means it *ran* and exported a non-empty PNG — not that +the PNG looks like Matplotlib's. A 2026-07-14 image-level audit rendered +every cell under both engines with per-cell seeded RNG (identical data) +and graded every comparable non-3-D image pair against matplotlib 3.11. +The first pass found **39 major divergences** across 105 pairs (legend +label-lists rendering nothing, `plot(x, y_2d)` not cycling colors, +colorbar count-domain defects, drifted colormap tables, contour +conventions, missing annotate arrows, mathtext, layout gaps). After the +fix rounds the same measurement over the 12 comparable notebooks (84 +graded pairs) stands at **34 match / 38 minor / 8 major** — majors down +from 33 on those notebooks, with the stylesheet-legend, color-cycle, +colorbar-domain, and colormap-fidelity classes fully cleared. The audit +findings, fixes, and remaining boundaries are recorded in +[docs/matplotlib-compat-changelog.md](../../docs/matplotlib-compat-changelog.md). + +One caveat inflates the scorecard above: with seaborn (which draws +through real matplotlib) holding the current figure, module-level +`plt.hist`/`axvline`/`axhline` draw onto xy's own figure — three 04.14 +cells count as passing while their plt-drawn content lands elsewhere. +This is a mixed-engine measurement artifact, not an xy code path: xy +replaces matplotlib, and without matplotlib installed seaborn fails +loudly at import, so 04.14 stays soft evidence only (no runtime +fallback onto matplotlib will ever be added). + ## Remaining failures All 17 are one of: 3-D projection cells (10, loud rejections by diff --git a/js/src/10_colormaps.js b/js/src/10_colormaps.js index f299297d..cc9dfe97 100644 --- a/js/src/10_colormaps.js +++ b/js/src/10_colormaps.js @@ -4,49 +4,26 @@ // --------------------------------------------------------------------------- const COLORMAP_STOPS = { - viridis: [ - [68, 1, 84], [72, 40, 120], [62, 74, 137], [49, 104, 142], [38, 130, 142], - [31, 158, 137], [53, 183, 121], [110, 206, 88], [181, 222, 43], [253, 231, 37], - ], - magma: [ - [0, 0, 4], [28, 16, 68], [79, 18, 123], [129, 37, 129], [181, 54, 122], - [229, 80, 100], [251, 135, 97], [254, 194, 135], [252, 253, 191], [252, 253, 191], - ], - plasma: [ - [13, 8, 135], [84, 2, 163], [139, 10, 165], [185, 50, 137], [219, 92, 104], - [244, 136, 73], [254, 188, 43], [240, 249, 33], [240, 249, 33], [240, 249, 33], - ], - inferno: [ - [0, 0, 4], [31, 12, 72], [85, 15, 109], [136, 34, 106], [186, 54, 85], - [227, 89, 51], [249, 140, 10], [249, 201, 50], [252, 255, 164], [252, 255, 164], - ], - cividis: [ - [0, 32, 76], [0, 42, 102], [39, 63, 108], [72, 85, 115], [106, 109, 120], - [143, 133, 118], [181, 159, 105], [223, 187, 82], [253, 217, 63], [255, 233, 69], - ], - gray: [ - [0, 0, 0], [28, 28, 28], [57, 57, 57], [85, 85, 85], [113, 113, 113], - [142, 142, 142], [170, 170, 170], [198, 198, 198], [227, 227, 227], [255, 255, 255], - ], - turbo: [ - [48, 18, 59], [70, 107, 227], [40, 187, 226], [61, 242, 148], [161, 253, 60], - [232, 216, 33], [253, 149, 35], [225, 66, 13], [153, 15, 4], [122, 4, 3], - ], - coolwarm: [ - [59, 76, 192], [87, 117, 211], [119, 154, 231], [157, 185, 243], [197, 209, 246], - [221, 220, 220], [242, 196, 174], [237, 158, 130], [214, 96, 77], [180, 4, 38], - ], - blues: [[247, 251, 255], [222, 235, 247], [198, 219, 239], [158, 202, 225], [107, 174, 214], [66, 146, 198], [33, 113, 181], [8, 81, 156], [8, 48, 107], [8, 48, 107]], - rdylgn: [[165, 0, 38], [215, 48, 39], [244, 109, 67], [253, 174, 97], [254, 224, 139], [217, 239, 139], [166, 217, 106], [102, 189, 99], [26, 152, 80], [0, 104, 55]], - rainbow: [[128, 0, 255], [57, 88, 255], [0, 180, 235], [0, 235, 176], [73, 255, 104], [176, 235, 38], [235, 180, 0], [255, 88, 0], [235, 0, 57], [255, 0, 0]], - spectral: [[158, 1, 66], [213, 62, 79], [244, 109, 67], [253, 174, 97], [254, 224, 139], [230, 245, 152], [171, 221, 164], [102, 194, 165], [50, 136, 189], [94, 79, 162]], - piyg: [[142, 1, 82], [197, 27, 125], [222, 119, 174], [241, 182, 218], [253, 224, 239], [247, 247, 247], [230, 245, 208], [184, 225, 134], [127, 188, 65], [77, 146, 33], [39, 100, 25]], - purples: [[252, 251, 253], [239, 237, 245], [218, 218, 235], [188, 189, 220], [158, 154, 200], [128, 125, 186], [106, 81, 163], [84, 39, 143], [63, 0, 125]], - pubu: [[255, 247, 251], [236, 231, 242], [208, 209, 230], [166, 189, 219], [116, 169, 207], [54, 144, 192], [5, 112, 176], [4, 90, 141], [2, 56, 88]], - prgn: [[64, 0, 75], [118, 42, 131], [153, 112, 171], [194, 165, 207], [231, 212, 232], [247, 247, 247], [217, 240, 211], [166, 219, 160], [90, 174, 97], [27, 120, 55], [0, 68, 27]], - rdgy: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [254, 254, 254], [224, 224, 224], [185, 185, 185], [135, 135, 135], [76, 76, 76], [26, 26, 26]], - jet: [[0, 0, 128], [0, 0, 241], [0, 76, 255], [0, 176, 255], [41, 255, 206], [125, 255, 122], [206, 255, 41], [255, 196, 0], [255, 104, 0], [241, 8, 0], [128, 0, 0]], binary: [[255, 255, 255], [0, 0, 0]], + gray: [[0, 0, 0], [25, 25, 25], [51, 51, 51], [76, 76, 76], [102, 102, 102], [128, 128, 128], [153, 153, 153], [179, 179, 179], [204, 204, 204], [230, 230, 230], [255, 255, 255]], + viridis: [[68, 1, 84], [72, 36, 117], [65, 68, 135], [53, 95, 141], [42, 120, 142], [33, 145, 140], [34, 168, 132], [68, 191, 112], [122, 209, 81], [189, 223, 38], [253, 231, 37]], + plasma: [[13, 8, 135], [65, 4, 157], [106, 0, 168], [143, 13, 164], [177, 42, 144], [204, 71, 120], [225, 100, 98], [242, 132, 75], [252, 166, 54], [252, 206, 37], [240, 249, 33]], + inferno: [[0, 0, 4], [22, 11, 57], [66, 10, 104], [106, 23, 110], [147, 38, 103], [188, 55, 84], [221, 81, 58], [243, 120, 25], [252, 165, 10], [246, 215, 70], [252, 255, 164]], + magma: [[0, 0, 4], [20, 14, 54], [59, 15, 112], [100, 26, 128], [140, 41, 129], [183, 55, 121], [222, 73, 104], [247, 112, 92], [254, 159, 109], [254, 207, 146], [252, 253, 191]], + cividis: [[0, 34, 78], [8, 51, 112], [53, 69, 108], [79, 87, 108], [102, 105, 112], [125, 124, 120], [148, 142, 119], [174, 163, 113], [200, 184, 102], [229, 207, 82], [254, 232, 56]], + coolwarm: [[59, 76, 192], [89, 119, 227], [123, 159, 249], [158, 190, 255], [192, 212, 245], [221, 220, 220], [242, 203, 183], [247, 172, 142], [238, 132, 104], [214, 82, 68], [180, 4, 38]], + turbo: [[48, 18, 59], [69, 89, 203], [62, 155, 254], [25, 213, 205], [70, 248, 132], [164, 252, 60], [225, 221, 55], [254, 164, 49], [240, 91, 18], [195, 37, 3], [122, 4, 3]], + rainbow: [[128, 0, 255], [78, 77, 252], [25, 150, 243], [24, 205, 228], [77, 243, 206], [128, 255, 180], [178, 243, 150], [230, 205, 115], [255, 150, 79], [255, 77, 39], [255, 0, 0]], + jet: [[0, 0, 128], [0, 0, 241], [0, 76, 255], [0, 176, 255], [41, 255, 206], [125, 255, 122], [206, 255, 41], [255, 196, 0], [255, 104, 0], [241, 8, 0], [128, 0, 0]], + rdgy: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [254, 254, 254], [224, 224, 224], [185, 185, 185], [135, 135, 135], [76, 76, 76], [26, 26, 26]], + rdbu: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [246, 247, 247], [209, 229, 240], [144, 196, 221], [67, 147, 195], [32, 101, 171], [5, 48, 97]], + blues: [[247, 251, 255], [227, 238, 249], [208, 225, 242], [183, 212, 234], [148, 196, 223], [106, 174, 214], [74, 152, 201], [46, 126, 188], [23, 100, 171], [8, 74, 145], [8, 48, 107]], + purples: [[252, 251, 253], [242, 240, 247], [226, 226, 239], [206, 207, 229], [182, 182, 216], [158, 154, 200], [134, 131, 189], [114, 98, 172], [97, 64, 155], [79, 31, 139], [63, 0, 125]], + pubu: [[255, 247, 251], [240, 234, 244], [219, 218, 235], [192, 201, 226], [156, 185, 217], [115, 169, 207], [66, 149, 195], [24, 124, 182], [5, 103, 162], [4, 83, 130], [2, 56, 88]], + piyg: [[142, 1, 82], [196, 26, 124], [222, 119, 174], [241, 181, 217], [253, 224, 239], [247, 247, 246], [230, 245, 208], [183, 224, 133], [127, 188, 65], [76, 145, 33], [39, 100, 25]], + prgn: [[64, 0, 75], [117, 41, 130], [153, 112, 171], [193, 164, 206], [231, 212, 232], [246, 247, 246], [217, 240, 211], [165, 218, 159], [90, 174, 97], [26, 119, 54], [0, 68, 27]], + rdylgn: [[165, 0, 38], [214, 47, 39], [244, 109, 67], [253, 173, 96], [254, 224, 139], [254, 255, 190], [217, 239, 139], [165, 216, 106], [102, 189, 99], [25, 151, 80], [0, 104, 55]], + spectral: [[158, 1, 66], [212, 61, 79], [244, 109, 67], [253, 173, 96], [254, 224, 139], [255, 255, 190], [230, 245, 152], [170, 220, 164], [102, 194, 165], [51, 135, 188], [94, 79, 162]], }; function colormapStops(name) { diff --git a/js/src/40_gl.js b/js/src/40_gl.js index 22072753..7430a0dd 100644 --- a/js/src/40_gl.js +++ b/js/src/40_gl.js @@ -124,10 +124,32 @@ float fcMarkerSdf(vec2 d, int shape) { vec2 a = abs(d); return min(max(a.x - 0.17, a.y - 0.5), max(a.x - 0.5, a.y - 0.17)); } - if (shape == 5) { // regular hexagon - const float k = 0.8660254; - vec2 p = abs(d); - return max(p.x - 0.5, p.y * 0.5 + p.x * k - 0.5); + if (shape == 5) { // regular hexagon (pointy top) + const vec3 k = vec3(-0.866025404, 0.5, 0.577350269); + vec2 p = abs(vec2(d.y, d.x)); + p -= 2.0 * min(dot(k.xy, p), 0.0) * k.xy; + p -= vec2(clamp(p.x, -k.z * 0.5, k.z * 0.5), 0.5); + return length(p) * sign(p.y); + } + if (shape == 6) { // regular pentagon (apex up) + const vec3 k = vec3(0.809016994, 0.587785252, 0.726542528); + vec2 p = vec2(abs(d.x), -d.y); + p -= 2.0 * min(dot(vec2(-k.x, k.y), p), 0.0) * vec2(-k.x, k.y); + p -= 2.0 * min(dot(vec2( k.x, k.y), p), 0.0) * vec2( k.x, k.y); + p -= vec2(clamp(p.x, -k.z * 0.5, k.z * 0.5), 0.5); + return length(p) * sign(p.y); + } + if (shape == 7) { // five-pointed star (apex up) + const float rf = 0.45; + const vec2 k1 = vec2(0.809016994, -0.587785252); + const vec2 k2 = vec2(-k1.x, k1.y); + vec2 p = vec2(abs(d.x), -d.y); + p -= 2.0 * max(dot(k1, p), 0.0) * k1; + p -= 2.0 * max(dot(k2, p), 0.0) * k2; + p = vec2(abs(p.x), p.y - 0.5); + vec2 ba = rf * vec2(-k1.y, k1.x) - vec2(0.0, 1.0); + float h = clamp(dot(p, ba) / dot(ba, ba), 0.0, 0.5); + return length(p - ba * h) * sign(p.y * ba.x - p.x * ba.y); } if (shape == 3) { // triangle (apex up) const float k = 1.7320508; diff --git a/js/src/50_chartview.js b/js/src/50_chartview.js index 47c607f7..435853f1 100644 --- a/js/src/50_chartview.js +++ b/js/src/50_chartview.js @@ -1096,7 +1096,7 @@ class ChartView { // no color borders in the mark color (matches the rect family). _pointMarkStyle(g, t) { const s = t.style || {}; - g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5 }[s.symbol] || 0; + g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5, pentagon: 6, star: 7 }[s.symbol] || 0; g.pointStrokeWidth = Number(s.stroke_width) || 0; const markOpaque = [g.color[0], g.color[1], g.color[2], 1]; g.pointStroke = s.stroke @@ -2292,7 +2292,7 @@ class ChartView { ? axis.tick_label_strategy : this._axisStyleValue(axis, "tick_label_strategy"); const value = String(raw || "auto").replace(/-/g, "_"); - return ["auto", "hide", "rotate", "stagger", "none"].includes(value) ? value : "auto"; + return ["auto", "hide", "rotate", "stagger", "none", "off"].includes(value) ? value : "auto"; } _axisTickLabelAngle(axis) { @@ -2363,6 +2363,7 @@ class ChartView { const withBase = labels.map((label) => ({ ...label, angle: baseAngle, row: 0 })); let strategy = this._axisTickLabelStrategy(axis); if (strategy === "none") return []; // hide every tick label (sparklines) + if (strategy === "off") return []; // labels only; grid/baselines stay (mpl shared axes) if (strategy === "auto") { if (!this._tickLabelsCollide(withBase, dim, fontSize, minGap)) return withBase; if (dim === "x" && axis.kind === "category" && labels.length <= 16) strategy = "rotate"; diff --git a/python/xy/_native.py b/python/xy/_native.py index 53691796..daab5d5e 100644 --- a/python/xy/_native.py +++ b/python/xy/_native.py @@ -24,7 +24,7 @@ from .config import MAX_CONTOUR_WORK, MAX_SCREEN_DIM -ABI_VERSION = 32 +ABI_VERSION = 33 # Rust reports invalid arguments (and, via the ffi_guard panic shield, any # internal panic) by returning `usize::MAX` from size-returning entry points. diff --git a/python/xy/_raster.py b/python/xy/_raster.py index 0142c5e2..f3098d57 100644 --- a/python/xy/_raster.py +++ b/python/xy/_raster.py @@ -22,8 +22,8 @@ _AXIS, _GRID, _TEXT, - COLORMAP_STOPS, DEFAULT_PALETTE, + _colormap_stops, _column, _corner_radii, _css, @@ -55,7 +55,16 @@ _AFFINE_CHANNEL_POINTS, _STROKED_TRIANGLES, ) = range(17) -_SYMBOLS = {"circle": 0, "square": 1, "diamond": 2, "triangle": 3, "cross": 4} +_SYMBOLS = { + "circle": 0, + "square": 1, + "diamond": 2, + "triangle": 3, + "cross": 4, + "hexagon": 5, + "pentagon": 6, + "star": 7, +} def _parse_color(css: str, opacity: float = 1.0) -> tuple[int, int, int, int]: @@ -285,10 +294,7 @@ def affine_channel_points( self._u32(int(meta.get("span", 0))) self._u64(int(meta["byte_offset"])) if encoded_color_mode == 1: - entries = ( - COLORMAP_STOPS.get(color_channel.get("colormap", "viridis")) - or COLORMAP_STOPS["viridis"] - ) + entries = _colormap_stops(color_channel.get("colormap", "viridis")) else: entries = [ _parse_color(entry)[:3] @@ -441,7 +447,7 @@ def heatmap_image( self.buf += stops.tobytes() def text(self, x, y, anchor, size, color, s) -> None: - data = str(s).encode("ascii", "replace") + data = str(s).encode("utf-8") self.buf.append(_TEXT_OP) self._f(x) self._f(y) @@ -550,8 +556,12 @@ def render_raster( # Chrome (unclipped): baselines, labels, title, legend. cmd.clip(0, 0, width, height) axis_c = _parse_color(_AXIS) + # "none" silences the whole axis chrome (sparklines); "off" hides only the + # label text and keeps baselines and the axis title (mpl shared axes). hide_x = xa.get("tick_label_strategy") == "none" hide_y = ya.get("tick_label_strategy") == "none" + hide_x_labels = hide_x or xa.get("tick_label_strategy") == "off" + hide_y_labels = hide_y or ya.get("tick_label_strategy") == "off" if not hide_y: cmd.stroke([(px0, py0), (px0, py1)], 1.0, axis_c) if not hide_x: @@ -559,11 +569,11 @@ def render_raster( cmd.stroke([(px0, x_axis_y), (px1, x_axis_y)], 1.0, axis_c) text_c = _parse_color(_TEXT) - if not hide_x: + if not hide_x_labels: for v in xlab: label_y = py0 - 7 if xa.get("side") == "top" else py1 + 15 cmd.text(float(sx(v)), label_y, 1, 11, text_c, _tick_text(xa, v, xstep)) - if not hide_y: + if not hide_y_labels: for v in ylab: cmd.text(px0 - 8, float(sy(v)) + 4, 2, 11, text_c, _tick_text(ya, v, ystep)) if spec.get("title"): @@ -571,8 +581,8 @@ def render_raster( if xa.get("label") and not hide_x: cmd.text(px0 + plot["w"] / 2, py1 + 33, 1, 12, text_c, str(xa["label"])) if ya.get("label") and not hide_y: - # No text rotation in the raster; place the y-label at top-left instead. - cmd.text(6, plot["y"] - 4, 0, 12, text_c, str(ya["label"])) + # Rotated 90° CCW alongside the axis, matching the SVG export. + cmd.text(14, plot["y"] + plot["h"] / 2, 1 | 0x80, 12, text_c, str(ya["label"])) named = [t for t in spec["traces"] if t.get("name")] if spec.get("show_legend", True) and named: @@ -652,6 +662,36 @@ def _emit_annotations(cmd, annotations, sx, sy, plot, width, height): _rect_pts(x0, y0, x1, y1), _rgba(style.get("color"), "#64748b", float(style.get("opacity", 0.14))), ) + elif ann.get("kind") == "arrow": + x0, y0 = float(sx(float(ann["x0"]))), float(sy(float(ann["y0"]))) + x1, y1 = float(sx(float(ann["x1"]))), float(sy(float(ann["y1"]))) + if all(np.isfinite(v) for v in (x0, y0, x1, y1)): + angle = np.arctan2(y1 - y0, x1 - x0) + head = max(7.0, float(style.get("head_size", 8.0))) + cmd.stroke( + [(x0, y0), (x1, y1)], + max(0.5, float(style.get("width", 1.5))), + color, + dash=( + [float(value) for value in style["dash"].split(",")] + if isinstance(style.get("dash"), str) + else style.get("dash") + ), + ) + cmd.fill( + [ + (x1, y1), + ( + x1 - head * float(np.cos(angle - np.pi / 6)), + y1 - head * float(np.sin(angle - np.pi / 6)), + ), + ( + x1 - head * float(np.cos(angle + np.pi / 6)), + y1 - head * float(np.sin(angle + np.pi / 6)), + ), + ], + color, + ) if ann.get("kind") == "text" and ann.get("text"): x, y = _annotation_point(ann, style, sx, sy, plot, width, height) anchor = {"start": 0, "middle": 1, "end": 2}.get(ann.get("anchor"), 0) @@ -790,6 +830,22 @@ def _emit_segments(cmd, t, blob, cols, sx, sy, style, color): else: colors[:] = _rgba(style.get("color"), color, opacity) width = float(style.get("width", 1.2)) + dash = style.get("dash") + if dash: + # The batched segments primitive cannot dash; fall back to one dashed + # stroke per segment (contour negative-level convention, few segments). + dash_pattern = ( + [float(value) for value in dash.split(",")] if isinstance(dash, str) else list(dash) + ) + px0, py0, px1, py1 = sx(x0), sy(y0), sx(x1), sy(y1) + for index in range(len(x0)): + cmd.stroke( + [(float(px0[index]), float(py0[index])), (float(px1[index]), float(py1[index]))], + width, + tuple(int(v) for v in colors[index]), + dash=dash_pattern, + ) + return cmd.segments(sx(x0), sy(y0), sx(x1), sy(y1), width, colors) @@ -949,10 +1005,7 @@ def _emit_grid(cmd, kind, g, blob, cols, sx, sy, style): cmd.image(dx, dy, dw, dh, w, h, rgba.tobytes(), nearest=True) return meta = cols[g["buf"]] - stops = np.asarray( - COLORMAP_STOPS.get(g.get("colormap", "viridis")) or COLORMAP_STOPS["viridis"], - dtype=np.uint8, - ) + stops = np.asarray(_colormap_stops(g.get("colormap", "viridis")), dtype=np.uint8) alpha = int(255 * float(style.get("opacity", 0.95))) xr, yr = g["x_range"], g["y_range"] dx, dy, dw, dh = _scene.grid_dest_rect(xr, yr, sx, sy) @@ -975,10 +1028,7 @@ def _emit_grid(cmd, kind, g, blob, cols, sx, sy, style): elif g.get("enc") == "log-u8": w, h = int(g["w"]), int(g["h"]) meta = cols[g["buf"]] - stops = np.asarray( - COLORMAP_STOPS.get(g.get("colormap", "viridis")) or COLORMAP_STOPS["viridis"], - dtype=np.uint8, - ) + stops = np.asarray(_colormap_stops(g.get("colormap", "viridis")), dtype=np.uint8) xr, yr = g["x_range"], g["y_range"] dx, dy, dw, dh = _scene.grid_dest_rect(xr, yr, sx, sy) cmd.density_image( @@ -1002,44 +1052,79 @@ def _emit_grid(cmd, kind, g, blob, cols, sx, sy, style): cmd.image(dx, dy, dw, dh, w, h, rgba.tobytes(), nearest=kind == "heatmap") +# Trace kinds whose legend entry is a short line sample (with dash) rather +# than a marker glyph or a filled patch. +_LEGEND_LINE_KINDS = frozenset({"line", "segments", "step", "stairs", "errorbar"}) + + def _emit_legend(cmd, named, plot, options): - pad, swatch, line_h = 8, 10, 16 + pad, handle, gap, line_h = 8, 20, 5, 16 ncols = min(len(named), max(1, int(options.get("ncols", 1)))) nrows = (len(named) + ncols - 1) // ncols - cell_w = max(len(str(t["name"])) for t in named) * 6.2 + swatch + 2 * pad + cell_w = max(len(str(t["name"])) for t in named) * 6.2 + handle + gap + 2 * pad box_w, box_h = ncols * cell_w + pad, nrows * line_h + pad loc = options.get("loc") or "upper right" x = plot["x"] + 6 if "left" in loc else plot["x"] + plot["w"] - box_w - 6 y = plot["y"] + plot["h"] - box_h - 6 if "lower" in loc else plot["y"] + 6 - cmd.fill(_rect_pts(x, y, x + box_w, y + box_h), (128, 128, 128, 20)) + # frameon=False (background transparent) drops the box entirely (§ mpl parity). + style_opts = options.get("style") or {} + if style_opts.get("background") != "transparent": + cmd.fill(_rect_pts(x, y, x + box_w, y + box_h), (128, 128, 128, 20)) for i, t in enumerate(named): style = t.get("style") or {} - c = _rgba( + color_str = _css( style.get("color") or (t.get("color") or {}).get("color"), DEFAULT_PALETTE[i % len(DEFAULT_PALETTE)], ) + c = _parse_color(color_str) col, row = i % ncols, i // ncols rx, ry = x + col * cell_w, y + pad / 2 + row * line_h - cmd.fill(_rect_pts(rx + pad, ry + 2, rx + pad + swatch, ry + 2 + swatch), c) - cmd.text(rx + pad + swatch + 5, ry + swatch, 0, 11, _parse_color(_TEXT), str(t["name"])) + hx0, hx1, cy = rx + pad, rx + pad + handle, ry + 7 + kind = t.get("kind") + if kind == "scatter": + sym = _SYMBOLS.get(style.get("symbol", "circle"), 0) + sw = float(style.get("stroke_width", 0.0)) + stroke = _rgba(style.get("stroke"), color_str) if sw > 0 else (0, 0, 0, 0) + cmd.point((hx0 + hx1) / 2, cy, 4.0, sym, c, sw, stroke) + elif kind in _LEGEND_LINE_KINDS: + cmd.stroke( + [(hx0, cy), (hx1, cy)], + float(style.get("width", 1.5)), + c, + dash=style.get("dash"), + ) + else: + cmd.fill(_rect_pts(hx0, cy - 4, hx1, cy + 4), c) + cmd.text(hx1 + gap, ry + 11, 0, 11, _parse_color(_TEXT), str(t["name"])) def _emit_colorbar(cmd, options, plot): - from ._svg import _lut + from ._svg import _linear_ticks, _lut orientation = options.get("orientation", "vertical") if orientation == "horizontal": x, y, width, height = plot["x"], plot["y"] + plot["h"] + 10, plot["w"], 8 else: x, y, width, height = plot["x"] + plot["w"] + 10, plot["y"], 8, plot["h"] - colors = _lut(options.get("colormap", "viridis"), np.linspace(0.0, 1.0, 64)) + # A discrete (resampled) colormap paints N solid bands; otherwise a smooth + # 64-step gradient approximates the continuous ramp. + levels = options.get("levels") + if levels and int(levels) >= 1: + n_seg = int(levels) + colors = _lut( + options.get("colormap", "viridis"), + np.linspace(0.0, 1.0, n_seg) if n_seg > 1 else np.array([0.0]), + ) + else: + n_seg = 64 + colors = _lut(options.get("colormap", "viridis"), np.linspace(0.0, 1.0, n_seg)) for index, color in enumerate(colors): if orientation == "horizontal": - x0, x1 = x + width * index / 64, x + width * (index + 1) / 64 + x0, x1 = x + width * index / n_seg, x + width * (index + 1) / n_seg cmd.fill(_rect_pts(x0, y, x1 + 0.5, y + height), (*map(int, color), 255)) else: - y0 = y + height * (63 - index) / 64 - y1 = y + height * (64 - index) / 64 + y0 = y + height * (n_seg - 1 - index) / n_seg + y1 = y + height * (n_seg - index) / n_seg cmd.fill(_rect_pts(x, y0, x + width, y1 + 0.5), (*map(int, color), 255)) domain = options.get("domain", [0.0, 1.0]) lo, hi = float(domain[0]), float(domain[1]) @@ -1061,21 +1146,20 @@ def _emit_colorbar(cmd, options, plot): pts = [(x, y + height), (x + width, y + height), (x + width / 2, y + height + 9)] cmd.fill(pts, color) if orientation == "horizontal": - if ticks is not None: - for value in ticks: - value = float(value) - if lo <= value <= hi: - cmd.text( - x + width * (value - lo) / span, - y + height + 13, - 1, - 10, - _parse_color(_TEXT), - f"{value:g}", - ) - else: - cmd.text(x, y + height + 13, 0, 10, _parse_color(_TEXT), f"{domain[0]:g}") - cmd.text(x + width, y + height + 13, 2, 10, _parse_color(_TEXT), f"{domain[1]:g}") + h_positions = ( + [float(value) for value in ticks if lo <= float(value) <= hi] + if ticks is not None + else (_linear_ticks(lo, hi)[0] or [lo, hi]) + ) + for value in h_positions: + cmd.text( + x + width * (value - lo) / span, + y + height + 13, + 1, + 10, + _parse_color(_TEXT), + f"{value:g}", + ) if options.get("label"): cmd.text( x + width / 2, @@ -1089,7 +1173,7 @@ def _emit_colorbar(cmd, options, plot): tick_positions = ( [float(value) for value in ticks if lo <= float(value) <= hi] if ticks is not None - else [lo + span * index / 4 for index in range(5)] + else (_linear_ticks(lo, hi)[0] or [lo, hi]) ) for value in tick_positions: cmd.text( diff --git a/python/xy/_svg.py b/python/xy/_svg.py index e7bdad1f..52f13554 100644 --- a/python/xy/_svg.py +++ b/python/xy/_svg.py @@ -29,225 +29,254 @@ # Mirrors js/src/10_colormaps.js COLORMAP_STOPS (§36) — test-guarded. COLORMAP_STOPS: dict[str, list[tuple[int, int, int]]] = { + "binary": [(255, 255, 255), (0, 0, 0)], + "gray": [ + (0, 0, 0), + (25, 25, 25), + (51, 51, 51), + (76, 76, 76), + (102, 102, 102), + (128, 128, 128), + (153, 153, 153), + (179, 179, 179), + (204, 204, 204), + (230, 230, 230), + (255, 255, 255), + ], "viridis": [ (68, 1, 84), - (72, 40, 120), - (62, 74, 137), - (49, 104, 142), - (38, 130, 142), - (31, 158, 137), - (53, 183, 121), - (110, 206, 88), - (181, 222, 43), + (72, 36, 117), + (65, 68, 135), + (53, 95, 141), + (42, 120, 142), + (33, 145, 140), + (34, 168, 132), + (68, 191, 112), + (122, 209, 81), + (189, 223, 38), (253, 231, 37), ], - "magma": [ - (0, 0, 4), - (28, 16, 68), - (79, 18, 123), - (129, 37, 129), - (181, 54, 122), - (229, 80, 100), - (251, 135, 97), - (254, 194, 135), - (252, 253, 191), - (252, 253, 191), - ], "plasma": [ (13, 8, 135), - (84, 2, 163), - (139, 10, 165), - (185, 50, 137), - (219, 92, 104), - (244, 136, 73), - (254, 188, 43), - (240, 249, 33), - (240, 249, 33), + (65, 4, 157), + (106, 0, 168), + (143, 13, 164), + (177, 42, 144), + (204, 71, 120), + (225, 100, 98), + (242, 132, 75), + (252, 166, 54), + (252, 206, 37), (240, 249, 33), ], "inferno": [ (0, 0, 4), - (31, 12, 72), - (85, 15, 109), - (136, 34, 106), - (186, 54, 85), - (227, 89, 51), - (249, 140, 10), - (249, 201, 50), - (252, 255, 164), + (22, 11, 57), + (66, 10, 104), + (106, 23, 110), + (147, 38, 103), + (188, 55, 84), + (221, 81, 58), + (243, 120, 25), + (252, 165, 10), + (246, 215, 70), (252, 255, 164), ], - "cividis": [ - (0, 32, 76), - (0, 42, 102), - (39, 63, 108), - (72, 85, 115), - (106, 109, 120), - (143, 133, 118), - (181, 159, 105), - (223, 187, 82), - (253, 217, 63), - (255, 233, 69), - ], - "gray": [ - (0, 0, 0), - (28, 28, 28), - (57, 57, 57), - (85, 85, 85), - (113, 113, 113), - (142, 142, 142), - (170, 170, 170), - (198, 198, 198), - (227, 227, 227), - (255, 255, 255), + "magma": [ + (0, 0, 4), + (20, 14, 54), + (59, 15, 112), + (100, 26, 128), + (140, 41, 129), + (183, 55, 121), + (222, 73, 104), + (247, 112, 92), + (254, 159, 109), + (254, 207, 146), + (252, 253, 191), ], - "turbo": [ - (48, 18, 59), - (70, 107, 227), - (40, 187, 226), - (61, 242, 148), - (161, 253, 60), - (232, 216, 33), - (253, 149, 35), - (225, 66, 13), - (153, 15, 4), - (122, 4, 3), + "cividis": [ + (0, 34, 78), + (8, 51, 112), + (53, 69, 108), + (79, 87, 108), + (102, 105, 112), + (125, 124, 120), + (148, 142, 119), + (174, 163, 113), + (200, 184, 102), + (229, 207, 82), + (254, 232, 56), ], "coolwarm": [ (59, 76, 192), - (87, 117, 211), - (119, 154, 231), - (157, 185, 243), - (197, 209, 246), + (89, 119, 227), + (123, 159, 249), + (158, 190, 255), + (192, 212, 245), (221, 220, 220), - (242, 196, 174), - (237, 158, 130), - (214, 96, 77), + (242, 203, 183), + (247, 172, 142), + (238, 132, 104), + (214, 82, 68), (180, 4, 38), ], - "blues": [ - (247, 251, 255), - (222, 235, 247), - (198, 219, 239), - (158, 202, 225), - (107, 174, 214), - (66, 146, 198), - (33, 113, 181), - (8, 81, 156), - (8, 48, 107), - (8, 48, 107), - ], - "rdylgn": [ - (165, 0, 38), - (215, 48, 39), - (244, 109, 67), - (253, 174, 97), - (254, 224, 139), - (217, 239, 139), - (166, 217, 106), - (102, 189, 99), - (26, 152, 80), - (0, 104, 55), + "turbo": [ + (48, 18, 59), + (69, 89, 203), + (62, 155, 254), + (25, 213, 205), + (70, 248, 132), + (164, 252, 60), + (225, 221, 55), + (254, 164, 49), + (240, 91, 18), + (195, 37, 3), + (122, 4, 3), ], "rainbow": [ (128, 0, 255), - (57, 88, 255), - (0, 180, 235), - (0, 235, 176), - (73, 255, 104), - (176, 235, 38), - (235, 180, 0), - (255, 88, 0), - (235, 0, 57), + (78, 77, 252), + (25, 150, 243), + (24, 205, 228), + (77, 243, 206), + (128, 255, 180), + (178, 243, 150), + (230, 205, 115), + (255, 150, 79), + (255, 77, 39), (255, 0, 0), ], - "spectral": [ - (158, 1, 66), - (213, 62, 79), - (244, 109, 67), - (253, 174, 97), - (254, 224, 139), - (230, 245, 152), - (171, 221, 164), - (102, 194, 165), - (50, 136, 189), - (94, 79, 162), + "jet": [ + (0, 0, 128), + (0, 0, 241), + (0, 76, 255), + (0, 176, 255), + (41, 255, 206), + (125, 255, 122), + (206, 255, 41), + (255, 196, 0), + (255, 104, 0), + (241, 8, 0), + (128, 0, 0), ], - "piyg": [ - (142, 1, 82), - (197, 27, 125), - (222, 119, 174), - (241, 182, 218), - (253, 224, 239), - (247, 247, 247), - (230, 245, 208), - (184, 225, 134), - (127, 188, 65), - (77, 146, 33), - (39, 100, 25), + "rdgy": [ + (103, 0, 31), + (177, 24, 43), + (214, 96, 77), + (243, 164, 129), + (253, 219, 199), + (254, 254, 254), + (224, 224, 224), + (185, 185, 185), + (135, 135, 135), + (76, 76, 76), + (26, 26, 26), + ], + "rdbu": [ + (103, 0, 31), + (177, 24, 43), + (214, 96, 77), + (243, 164, 129), + (253, 219, 199), + (246, 247, 247), + (209, 229, 240), + (144, 196, 221), + (67, 147, 195), + (32, 101, 171), + (5, 48, 97), + ], + "blues": [ + (247, 251, 255), + (227, 238, 249), + (208, 225, 242), + (183, 212, 234), + (148, 196, 223), + (106, 174, 214), + (74, 152, 201), + (46, 126, 188), + (23, 100, 171), + (8, 74, 145), + (8, 48, 107), ], "purples": [ (252, 251, 253), - (239, 237, 245), - (218, 218, 235), - (188, 189, 220), + (242, 240, 247), + (226, 226, 239), + (206, 207, 229), + (182, 182, 216), (158, 154, 200), - (128, 125, 186), - (106, 81, 163), - (84, 39, 143), + (134, 131, 189), + (114, 98, 172), + (97, 64, 155), + (79, 31, 139), (63, 0, 125), ], "pubu": [ (255, 247, 251), - (236, 231, 242), - (208, 209, 230), - (166, 189, 219), - (116, 169, 207), - (54, 144, 192), - (5, 112, 176), - (4, 90, 141), + (240, 234, 244), + (219, 218, 235), + (192, 201, 226), + (156, 185, 217), + (115, 169, 207), + (66, 149, 195), + (24, 124, 182), + (5, 103, 162), + (4, 83, 130), (2, 56, 88), ], + "piyg": [ + (142, 1, 82), + (196, 26, 124), + (222, 119, 174), + (241, 181, 217), + (253, 224, 239), + (247, 247, 246), + (230, 245, 208), + (183, 224, 133), + (127, 188, 65), + (76, 145, 33), + (39, 100, 25), + ], "prgn": [ (64, 0, 75), - (118, 42, 131), + (117, 41, 130), (153, 112, 171), - (194, 165, 207), + (193, 164, 206), (231, 212, 232), - (247, 247, 247), + (246, 247, 246), (217, 240, 211), - (166, 219, 160), + (165, 218, 159), (90, 174, 97), - (27, 120, 55), + (26, 119, 54), (0, 68, 27), ], - "rdgy": [ - (103, 0, 31), - (177, 24, 43), - (214, 96, 77), - (243, 164, 129), - (253, 219, 199), - (254, 254, 254), - (224, 224, 224), - (185, 185, 185), - (135, 135, 135), - (76, 76, 76), - (26, 26, 26), + "rdylgn": [ + (165, 0, 38), + (214, 47, 39), + (244, 109, 67), + (253, 173, 96), + (254, 224, 139), + (254, 255, 190), + (217, 239, 139), + (165, 216, 106), + (102, 189, 99), + (25, 151, 80), + (0, 104, 55), ], - "jet": [ - (0, 0, 128), - (0, 0, 241), - (0, 76, 255), - (0, 176, 255), - (41, 255, 206), - (125, 255, 122), - (206, 255, 41), - (255, 196, 0), - (255, 104, 0), - (241, 8, 0), - (128, 0, 0), + "spectral": [ + (158, 1, 66), + (212, 61, 79), + (244, 109, 67), + (253, 173, 96), + (254, 224, 139), + (255, 255, 190), + (230, 245, 152), + (170, 220, 164), + (102, 194, 165), + (51, 135, 188), + (94, 79, 162), ], - "binary": [(255, 255, 255), (0, 0, 0)], } # Light-theme chrome colors (the client derives these from currentColor). @@ -413,6 +442,10 @@ def _fmt_linear(v: float, step: float) -> str: if av >= 1e6 or av < 1e-4: return f"{v:.1e}".replace("e+0", "e").replace("e-0", "e-").replace("e+", "e") dec = max(0, -int(np.floor(np.log10(step))) + (1 if step < 1 else 0)) + # A non-nice step (pi/2, 0.3333…) needs enough decimals to keep adjacent + # ticks distinct; widen until the step itself round-trips at that precision. + while dec < 8 and abs(round(step, dec) - step) > step / 1000.0: + dec += 1 s = f"{v:.{min(dec, 8)}f}" if "." in s: s = s.rstrip("0").rstrip(".") @@ -690,9 +723,31 @@ def _step_arrays(xv: np.ndarray, yv: np.ndarray, where: str) -> tuple[np.ndarray f"H {_num(cx - 0.34 * r)} V {_num(cy + 0.34 * r)} H {_num(cx - r)} V {_num(cy - 0.34 * r)} " f'H {_num(cx - 0.34 * r)} Z"' ), + "pentagon": lambda cx, cy, r: _regular_polygon_path(cx, cy, r, 5, -90.0), + "hexagon": lambda cx, cy, r: _regular_polygon_path(cx, cy, r, 6, -90.0), + "star": lambda cx, cy, r: _star_path(cx, cy, r, 5, 0.45, -90.0), } +def _regular_polygon_path(cx: float, cy: float, r: float, n: int, start_deg: float) -> str: + pts = [] + for i in range(n): + theta = np.radians(start_deg + i * 360.0 / n) + pts.append((cx + r * np.cos(theta), cy + r * np.sin(theta))) + d = "M " + " L ".join(f"{_num(px)} {_num(py)}" for px, py in pts) + return f' str: + pts = [] + for i in range(points * 2): + radius = r if i % 2 == 0 else r * inner + theta = np.radians(start_deg + i * 180.0 / points) + pts.append((cx + radius * np.cos(theta), cy + radius * np.sin(theta))) + d = "M " + " L ".join(f"{_num(px)} {_num(py)}" for px, py in pts) + return f' str: dash = style.get("dash") if not dash: @@ -787,8 +842,12 @@ def ticks_for(axis: dict[str, Any], length_px: float) -> tuple[list[float], list grid_color = escape(_css(dom_style.get("--chart-grid"), _GRID)) grid: list[str] = [] labels: list[str] = [] + # "none" silences the whole axis chrome (sparklines); "off" hides only the + # label text and keeps grid, baselines and the axis title (mpl shared axes). hide_x = xa.get("tick_label_strategy") == "none" hide_y = ya.get("tick_label_strategy") == "none" + hide_x_labels = hide_x or xa.get("tick_label_strategy") == "off" + hide_y_labels = hide_y or ya.get("tick_label_strategy") == "off" for v in xt: if hide_x: break @@ -805,14 +864,14 @@ def ticks_for(axis: dict[str, Any], length_px: float) -> tuple[list[float], list f'' ) - if not hide_x: + if not hide_x_labels: for v in xlab: tick_y = plot["y"] - 7 if xa.get("side") == "top" else plot["y"] + plot["h"] + 16 labels.append( f'{escape(_tick_text(xa, v, xstep))}' ) - if not hide_y: + if not hide_y_labels: for v in ylab: labels.append( f'' ) + elif kind == "arrow": + x0, y0 = float(sx(float(ann["x0"]))), float(sy(float(ann["y0"]))) + x1, y1 = float(sx(float(ann["x1"]))), float(sy(float(ann["y1"]))) + if all(np.isfinite(v) for v in (x0, y0, x1, y1)): + angle = np.arctan2(y1 - y0, x1 - x0) + head = max(7.0, float(style.get("head_size", 8.0))) + wings = ( + ( + x1 - head * float(np.cos(angle - np.pi / 6)), + y1 - head * float(np.sin(angle - np.pi / 6)), + ), + ( + x1 - head * float(np.cos(angle + np.pi / 6)), + y1 - head * float(np.sin(angle + np.pi / 6)), + ), + ) + marks.append( + f'' + ) + marks.append( + f'' + ) if kind == "text" and ann.get("text"): x, y = float(ann.get("x", 0.0)), float(ann.get("y", 0.0)) space = style.get("coordinate_space") @@ -1054,8 +1140,10 @@ def _segment_marks( colors = [palette[code % len(palette)] for code in codes] else: colors = [color] * len(x0) - suffix = f'stroke-width="{_num(width)}" fill="none" stroke-linecap="round"' + ( - f' stroke-opacity="{_num(op)}"' if op < 1 else "" + suffix = ( + f'stroke-width="{_num(width)}" fill="none" stroke-linecap="round"' + + (f' stroke-opacity="{_num(op)}"' if op < 1 else "") + + _dash_attr(style) ) return "".join( f' str: rows = [] - pad, swatch, line_h = 8, 10, 16 + pad, handle, gap, line_h = 8, 20, 5, 16 ncols = min(len(named), max(1, int(options.get("ncols", 1)))) nrows = (len(named) + ncols - 1) // ncols - cell_w = max(len(str(t["name"])) for t in named) * 6.2 + swatch + 2 * pad + cell_w = max(len(str(t["name"])) for t in named) * 6.2 + handle + gap + 2 * pad box_w, box_h = ncols * cell_w + pad, nrows * line_h + pad loc = options.get("loc") or "upper right" x = plot["x"] + 6 if "left" in loc else plot["x"] + plot["w"] - box_w - 6 y = plot["y"] + plot["h"] - box_h - 6 if "lower" in loc else plot["y"] + 6 - rows.append( - f'' - ) + style_opts = options.get("style") or {} + if style_opts.get("background") != "transparent": + rows.append( + f'' + ) for i, t in enumerate(named): style = t.get("style") or {} color = _css( @@ -1323,12 +1418,39 @@ def _legend(named: list[dict], plot: dict, options: dict) -> str: ) col, row = i % ncols, i // ncols rx, ry = x + col * cell_w, y + pad / 2 + row * line_h + hx0, hx1, cy = rx + pad, rx + pad + handle, ry + 7 + kind = t.get("kind") + if kind == "scatter": + symbol = style.get("symbol", "circle") + builder = _SYMBOL_BUILDERS.get(symbol) + stroke_w = float(style.get("stroke_width", 0.0)) + stroke = _css(style.get("stroke"), color) if stroke_w else None + stroke_attr = ( + f' stroke="{escape(stroke)}" stroke-width="{_num(stroke_w)}"' if stroke else "" + ) + cxm = (hx0 + hx1) / 2 + if builder is None: + rows.append( + f'' + ) + else: + rows.append( + builder(float(cxm), float(cy), 4.0) + f' fill="{escape(color)}"{stroke_attr}/>' + ) + elif kind in _LEGEND_LINE_KINDS: + rows.append( + f'" + ) + else: + rows.append( + f'' + ) rows.append( - f'' - ) - rows.append( - f'{escape(str(t["name"]))}' ) return "".join(rows) @@ -1370,7 +1492,7 @@ def _colorbar(options: dict, plot: dict) -> str: tick_positions = ( [float(value) for value in ticks if lo <= float(value) <= hi] if ticks is not None - else [lo + span * i / 4 for i in range(5)] + else (_linear_ticks(lo, hi)[0] or [lo, hi]) ) tick_nodes = ( "".join( @@ -1385,7 +1507,6 @@ def _colorbar(options: dict, plot: dict) -> str: f'y="{_num(y + height + 12)}" text-anchor="middle" ' f'fill="{_TEXT}">{value:g}' for value in tick_positions - if ticks is not None ) ) extend = options.get("extend") @@ -1412,12 +1533,49 @@ def _colorbar(options: dict, plot: dict) -> str: return ( f'' f"{stop_nodes}" - f'' + f"{_colorbar_body(options, x, y, width, height, orientation, gradient_id)}" f"{extend_nodes}{tick_nodes}{label_node}" ) +def _colorbar_body( + options: dict, + x: float, + y: float, + width: float, + height: float, + orientation: str, + gradient_id: str, +) -> str: + """Colorbar bar fill: a smooth gradient, or N solid bands for a discrete + (resampled) colormap so it reads like Matplotlib's segmented colorbar.""" + levels = options.get("levels") + if not levels or int(levels) < 1: + return ( + f'' + ) + n = int(levels) + cmap = str(options.get("colormap", "viridis")) + positions = np.linspace(0.0, 1.0, n) if n > 1 else np.array([0.0]) + colors = _lut(cmap, positions) + rects = [] + for index, (r, g, b) in enumerate(colors): + if orientation == "horizontal": + bx0 = x + width * index / n + rects.append( + f'' + ) + else: + by0 = y + height * (n - 1 - index) / n + rects.append( + f'' + ) + return "".join(rects) + + def to_svg( fig: Any, path: Optional[str | PathLike[str]] = None, diff --git a/python/xy/_validate.py b/python/xy/_validate.py index 1420d00d..45630ca8 100644 --- a/python/xy/_validate.py +++ b/python/xy/_validate.py @@ -20,7 +20,7 @@ import numpy as np -_TICK_LABEL_STRATEGIES = frozenset({"auto", "hide", "rotate", "stagger", "none"}) +_TICK_LABEL_STRATEGIES = frozenset({"auto", "hide", "rotate", "stagger", "none", "off"}) _LABEL_POSITIONS = frozenset( {"start", "center", "end", "inside_start", "inside_center", "inside_end"} ) @@ -274,7 +274,9 @@ def curve(value: Any, label: str) -> str: return value -_POINT_SYMBOLS = frozenset({"circle", "square", "diamond", "triangle", "cross"}) +_POINT_SYMBOLS = frozenset( + {"circle", "square", "diamond", "triangle", "cross", "hexagon", "pentagon", "star"} +) def point_symbol(value: Any, label: str) -> str: diff --git a/python/xy/channels.py b/python/xy/channels.py index 44b38d03..cfc66185 100644 --- a/python/xy/channels.py +++ b/python/xy/channels.py @@ -42,6 +42,7 @@ "pubu", "prgn", "rdgy", + "rdbu", "jet", "binary", ) diff --git a/python/xy/components.py b/python/xy/components.py index f666bce3..507d2551 100644 --- a/python/xy/components.py +++ b/python/xy/components.py @@ -805,6 +805,7 @@ def contour( color: Optional[str] = None, width: float = 1.1, opacity: float = 0.9, + dash_negative: bool = False, class_name: Optional[str] = None, x_axis: str = "x", y_axis: str = "y", @@ -825,6 +826,7 @@ def contour( "color": color, "width": width, "opacity": opacity, + "dash_negative": dash_negative, "x_axis": x_axis, "y_axis": y_axis, }, @@ -1891,6 +1893,10 @@ def figure(self) -> Figure: node = legends[-1] _apply_chrome_node(fig, "legend", node.class_name, node.style) fig.legend_options = {"loc": node.loc, "ncols": node.ncols} + if node.style: + # Carry the frame/frameon styling into the static-export spec so + # the raster/SVG legend can honor frameon=False (transparent bg). + fig.legend_options["style"] = dict(node.style) if legend_shows and not legend_shows[-1]: fig.show_legend = False if modebars: @@ -2808,6 +2814,7 @@ def _apply_contour(fig: Figure, m: Mark, data: Any) -> None: color=m.props["color"], width=m.props["width"], opacity=m.props["opacity"], + dash_negative=m.props.get("dash_negative", False), ) diff --git a/python/xy/marks.py b/python/xy/marks.py index 9790832e..a0fd1b17 100644 --- a/python/xy/marks.py +++ b/python/xy/marks.py @@ -39,6 +39,7 @@ def _append_segment_trace( role: str, color_ch: Any = None, count: Optional[int] = None, + dash: Optional[str] = None, ) -> None: """Append a compact instanced line-segment trace. @@ -74,7 +75,13 @@ def _append_segment_trace( y0=y0c, y1=y1c, name=name, - style={"color": color, "opacity": opacity, "width": width, "role": role}, + style={ + "color": color, + "opacity": opacity, + "width": width, + "role": role, + **({"dash": dash} if dash else {}), + }, color_ch=color_ch, count=count, ) @@ -1483,8 +1490,14 @@ def contour( color: Optional[str] = None, width: float = 1.1, opacity: float = 0.9, + dash_negative: bool = False, ) -> "Figure": - """Add regular-grid contour isolines, optionally over a filled heatmap.""" + """Add regular-grid contour isolines, optionally over a filled heatmap. + + `dash_negative` renders negative-level isolines dashed for a single-color + contour (Matplotlib's monochrome convention); it is ignored when a colormap + drives per-level color. + """ arr = self._as_float_array(z, "contour z") if arr.ndim != 2 or min(arr.shape) < 2: raise ValueError( @@ -1527,7 +1540,28 @@ def contour( checkpoint = self._checkpoint() try: if filled: - self.heatmap(arr, x=x, y=y, name=None, colormap=colormap, opacity=min(opacity, 0.7)) + # Matplotlib's contourf paints piecewise-constant bands *between* + # consecutive levels, not a smooth ramp. Snap each cell to its + # band's midpoint so the heatmap renders flat bands; cells outside + # [levels[0], levels[-1]] stay unpainted (extend='neither'). + edges = np.asarray(level_values, dtype=np.float64) + if len(edges) >= 2 and edges[0] < edges[-1]: + band = np.searchsorted(edges, arr, side="right") - 1 + mids = (edges[:-1] + edges[1:]) * 0.5 + banded = np.full(arr.shape, np.nan, dtype=np.float64) + inside = np.isfinite(arr) & (band >= 0) & (band < len(edges) - 1) + banded[inside] = mids[np.clip(band, 0, len(edges) - 2)][inside] + self.heatmap( + banded, + x=x, + y=y, + name=None, + colormap=colormap, + domain=(float(edges[0]), float(edges[-1])), + opacity=min(opacity, 0.9), + ) + else: + self.heatmap(arr, x=x, y=y, name=None, colormap=colormap, opacity=min(opacity, 0.7)) x0, x1, y0, y1, level_values = _contour_segments(arr, xpos, ypos, level_values) if len(x0) == 0: raise ValueError("contour levels do not intersect the finite grid") @@ -1539,19 +1573,29 @@ def contour( if color is None else None ) - self._append_segment_trace( - "contour", - x0, - x1, - y0, - y1, - name=name, - color=color, - opacity=opacity, - width=width, - role="contour", - color_ch=color_ch, - ) + # Matplotlib dashes negative isolines for a single-color contour. Split + # the segment set by level sign so the negative group ships dashed; a + # colormapped contour keeps every level solid. + lv = np.asarray(level_values) + if dash_negative and color is not None and np.any(lv < 0) and np.any(lv >= 0): + groups = ((lv >= 0, None), (lv < 0, "6,4")) + else: + groups = ((np.ones(len(lv), dtype=bool), None),) + for mask, dash in groups: + self._append_segment_trace( + "contour", + x0[mask], + x1[mask], + y0[mask], + y1[mask], + name=name if dash is None else None, + color=color, + opacity=opacity, + width=width, + role="contour", + color_ch=color_ch, + dash=dash, + ) except Exception: self._rollback(checkpoint) raise diff --git a/python/xy/pyplot/__init__.py b/python/xy/pyplot/__init__.py index e6e63cdc..2dd6dd88 100644 --- a/python/xy/pyplot/__init__.py +++ b/python/xy/pyplot/__init__.py @@ -22,7 +22,7 @@ from ._axes import Axes from ._colors import LinearSegmentedColormap, ListedColormap -from ._mplfig import Figure, GridSpec, apply_sharing, make_axes_grid +from ._mplfig import Figure, GridSpec from ._rc import _PropCycle, rc, rc_context, rcdefaults, rcParams from ._state import all_figures, close, figlabels, fignum_exists, fignums, figure, gca, gcf, sca from ._ticker import ( @@ -218,18 +218,22 @@ def subplots( height_ratios = kwargs.pop("height_ratios", None) gridspec_kw = kwargs.pop("gridspec_kw", None) or {} subplot_kw = kwargs.pop("subplot_kw", None) or {} - width_ratios = gridspec_kw.get("width_ratios", width_ratios) - height_ratios = gridspec_kw.get("height_ratios", height_ratios) fig = figure(figsize=figsize, dpi=dpi) if fig._axes and any(ax._entries for ax in fig._axes): fig = figure(None, figsize=figsize, dpi=dpi) # fresh figure, mpl semantics - axes = make_axes_grid(fig, nrows, ncols, squeeze=squeeze) + axes = fig.subplots( + nrows, + ncols, + sharex=sharex, + sharey=sharey, + squeeze=squeeze, + width_ratios=width_ratios, + height_ratios=height_ratios, + gridspec_kw=gridspec_kw, + ) if subplot_kw: for ax in np.atleast_1d(np.asarray(axes, dtype=object)).ravel(): ax.set(**subplot_kw) - fig._width_ratios = None if width_ratios is None else tuple(map(float, width_ratios)) - fig._height_ratios = None if height_ratios is None else tuple(map(float, height_ratios)) - apply_sharing(fig, sharex, sharey) return fig, axes diff --git a/python/xy/pyplot/_axes.py b/python/xy/pyplot/_axes.py index 3c9f1532..6e92a751 100644 --- a/python/xy/pyplot/_axes.py +++ b/python/xy/pyplot/_axes.py @@ -23,6 +23,7 @@ from ._artists import Artist, AxesImage, BarContainer, Line2D, PathCollection, PolyCollection, Text from ._colors import PROP_CYCLE, resolve_cmap, resolve_color from ._fmt import parse_fmt +from ._mathtext import mathtext_to_unicode from ._plot_types import PlotTypeMixin from ._rc import rcParams from ._ticker import AutoLocator, NullLocator, ScalarFormatter, as_formatter @@ -759,63 +760,178 @@ def plot(self, *args: Any, **kwargs: Any) -> list[Line2D]: check_unsupported(kwargs, "plot()") handles: list[Line2D] = [] - for x, y, fmt in _iter_plot_groups(args): - x, y = np.atleast_1d(x), np.atleast_1d(y) - x, y = _convert_timedelta_axis(x), _convert_timedelta_axis(y) - if transform is not None: - x, y = self._transform_points(x, y, transform) - if x.ndim == 2 or y.ndim == 2: - if x.ndim == 1: - x = np.broadcast_to(x[:, None], np.asarray(y).shape) - if y.ndim == 1: - y = np.broadcast_to(y[:, None], np.asarray(x).shape) - if x.shape != y.shape: - raise ValueError("2-D plot x and y must have matching shapes") - separators = np.full((1, x.shape[1]), np.nan) - x = np.concatenate((x, separators)).T.reshape(-1) - y = np.concatenate((y, separators)).T.reshape(-1) - if np.ma.isMaskedArray(x): - x = np.ma.asarray(x).filled(np.nan) - if np.ma.isMaskedArray(y): - y = np.ma.asarray(y).filled(np.nan) - per = dict(base) - this_marker = marker - if fmt: - fcolor, fstyle, fmarker = parse_fmt(fmt) - if fcolor is not None and "color" not in per: - per["color"] = resolve_color(fcolor) - if fstyle is not None and "linestyle" not in per: - per["linestyle"] = fstyle - if fmarker is not None and this_marker is None: - this_marker = fmarker - # fmt with a marker and no linestyle means markers only. - if fmarker is not None and fstyle is None: - per.setdefault("linestyle", "none") - if "color" not in per: - per["color"] = self._next_color() - ls = per.pop("linestyle", "-") - dash = per.pop("dash", None) - if dash is None: - dash = LINESTYLE_TO_DASH.get(ls, None) if isinstance(ls, str) else None - - entry_kwargs = { - "color": per.get("color"), - "width": per.get("width", rcParams["lines.linewidth"]), - "opacity": per.get("opacity", 1.0), - "name": per.get("name"), - } - if dash == "none": + for gx, gy, fmt in _iter_plot_groups(args): + gx, gy = np.atleast_1d(gx), np.atleast_1d(gy) + gx, gy = _convert_timedelta_axis(gx), _convert_timedelta_axis(gy) + # A 2-D operand draws one line per column; each column advances the + # property cycle so column N gets color C{N}, matching matplotlib. + for x, y in _plot_series_columns(gx, gy): + handles.append( + self._plot_series( + x, + y, + fmt, + base, + marker, + markersize, + markerfacecolor, + markeredgecolor, + markeredgewidth, + markevery, + drawstyle, + transform, + ) + ) + return handles + + def _plot_series( + self, + x: Any, + y: Any, + fmt: Optional[str], + base: dict[str, Any], + marker: Any, + markersize: Any, + markerfacecolor: Any, + markeredgecolor: Any, + markeredgewidth: Any, + markevery: Any, + drawstyle: Any, + transform: Any, + ) -> "Line2D": + if transform is not None: + x, y = self._transform_points(x, y, transform) + if np.ma.isMaskedArray(x): + x = np.ma.asarray(x).filled(np.nan) + if np.ma.isMaskedArray(y): + y = np.ma.asarray(y).filled(np.nan) + per = dict(base) + this_marker = marker + if fmt: + fcolor, fstyle, fmarker = parse_fmt(fmt) + if fcolor is not None and "color" not in per: + per["color"] = resolve_color(fcolor) + if fstyle is not None and "linestyle" not in per: + per["linestyle"] = fstyle + if fmarker is not None and this_marker is None: + this_marker = fmarker + # fmt with a marker and no linestyle means markers only. + if fmarker is not None and fstyle is None: + per.setdefault("linestyle", "none") + if "color" not in per: + per["color"] = self._next_color() + ls = per.pop("linestyle", "-") + dash = per.pop("dash", None) + if dash is None: + dash = LINESTYLE_TO_DASH.get(ls, None) if isinstance(ls, str) else None + + entry_kwargs = { + "color": per.get("color"), + "width": per.get("width", rcParams["lines.linewidth"]), + "opacity": per.get("opacity", 1.0), + "name": per.get("name"), + } + if dash == "none": + entry = self._add( + "scatter", + { + "x": x, + "y": y, + "kwargs": { + **{k: v for k, v in entry_kwargs.items() if k != "width"}, + "symbol": _marker_symbol(this_marker or "o"), + "size": float( + rcParams["lines.markersize"] if markersize is None else markersize + ), + **( + {"color": resolve_color(markerfacecolor)} + if markerfacecolor not in (None, "auto") + else {} + ), + **( + { + "stroke": resolve_color(markeredgecolor), + "stroke_width": float( + 1.0 if markeredgewidth is None else markeredgewidth + ), + } + if markeredgecolor not in (None, "auto", "none") + else {} + ), + }, + }, + ) + else: + if dash is not None: + entry_kwargs["dash"] = dash + numeric_x = np.asarray(x) + try: + finite_pairs = np.isfinite(np.asarray(x, dtype=np.float64)) & np.isfinite( + np.asarray(y, dtype=np.float64) + ) + except (TypeError, ValueError): + finite_pairs = np.ones(len(x), dtype=bool) + has_gaps = not bool(np.all(finite_pairs)) + preserve_path = ( + numeric_x.ndim == 1 + and len(numeric_x) > 1 + and np.issubdtype(numeric_x.dtype, np.number) + and np.any(np.diff(numeric_x.astype(np.float64)) < 0) + ) + if preserve_path or has_gaps: + xv, yv = np.asarray(x), np.asarray(y) + keep = finite_pairs[:-1] & finite_pairs[1:] + segment_kwargs = { + key: value + for key, value in entry_kwargs.items() + if key in {"color", "width", "opacity", "name"} + } entry = self._add( - "scatter", + "@mark", + { + "factory": "segments", + "args": (xv[:-1][keep], yv[:-1][keep], xv[1:][keep], yv[1:][keep]), + "x": x, + "y": y, + "kwargs": segment_kwargs, + }, + ) + elif drawstyle and str(drawstyle).startswith("steps-"): + entry = self._add( + "@mark", { + "factory": "step", + "args": (x, y), "x": x, "y": y, "kwargs": { - **{k: v for k, v in entry_kwargs.items() if k != "width"}, - "symbol": _marker_symbol(this_marker or "o"), + "where": str(drawstyle).removeprefix("steps-"), + **entry_kwargs, + }, + }, + ) + else: + entry = self._add("line", {"x": x, "y": y, "kwargs": entry_kwargs}) + if this_marker is not None: + # line + markers: overlay a scatter with the same series color + marker_x, marker_y = _marked_values(x, y, markevery) + overlay = self._add( + "scatter", + { + "x": marker_x, + "y": marker_y, + "kwargs": { + "color": entry_kwargs["color"], + "opacity": entry_kwargs["opacity"], + "symbol": _marker_symbol(this_marker), + # Matplotlib marker sizes are points while the + # engine consumes CSS-pixel diameters. At the + # default 96 dpi, 6 pt is 8 px. "size": float( rcParams["lines.markersize"] if markersize is None else markersize - ), + ) + * (4.0 / 3.0), + "name": None, **( {"color": resolve_color(markerfacecolor)} if markerfacecolor not in (None, "auto") @@ -834,102 +950,13 @@ def plot(self, *args: Any, **kwargs: Any) -> list[Line2D]: }, }, ) - else: - if dash is not None: - entry_kwargs["dash"] = dash - numeric_x = np.asarray(x) - try: - finite_pairs = np.isfinite(np.asarray(x, dtype=np.float64)) & np.isfinite( - np.asarray(y, dtype=np.float64) - ) - except (TypeError, ValueError): - finite_pairs = np.ones(len(x), dtype=bool) - has_gaps = not bool(np.all(finite_pairs)) - preserve_path = ( - numeric_x.ndim == 1 - and len(numeric_x) > 1 - and np.issubdtype(numeric_x.dtype, np.number) - and np.any(np.diff(numeric_x.astype(np.float64)) < 0) - ) - if preserve_path or has_gaps: - xv, yv = np.asarray(x), np.asarray(y) - keep = finite_pairs[:-1] & finite_pairs[1:] - segment_kwargs = { - key: value - for key, value in entry_kwargs.items() - if key in {"color", "width", "opacity", "name"} - } - entry = self._add( - "@mark", - { - "factory": "segments", - "args": (xv[:-1][keep], yv[:-1][keep], xv[1:][keep], yv[1:][keep]), - "x": x, - "y": y, - "kwargs": segment_kwargs, - }, - ) - elif drawstyle and str(drawstyle).startswith("steps-"): - entry = self._add( - "@mark", - { - "factory": "step", - "args": (x, y), - "x": x, - "y": y, - "kwargs": { - "where": str(drawstyle).removeprefix("steps-"), - **entry_kwargs, - }, - }, - ) - else: - entry = self._add("line", {"x": x, "y": y, "kwargs": entry_kwargs}) - if this_marker is not None: - # line + markers: overlay a scatter with the same series color - marker_x, marker_y = _marked_values(x, y, markevery) - self._add( - "scatter", - { - "x": marker_x, - "y": marker_y, - "kwargs": { - "color": entry_kwargs["color"], - "opacity": entry_kwargs["opacity"], - "symbol": _marker_symbol(this_marker), - # Matplotlib marker sizes are points while the - # engine consumes CSS-pixel diameters. At the - # default 96 dpi, 6 pt is 8 px. - "size": float( - rcParams["lines.markersize"] - if markersize is None - else markersize - ) - * (4.0 / 3.0), - "name": None, - **( - {"color": resolve_color(markerfacecolor)} - if markerfacecolor not in (None, "auto") - else {} - ), - **( - { - "stroke": resolve_color(markeredgecolor), - "stroke_width": float( - 1.0 if markeredgewidth is None else markeredgewidth - ), - } - if markeredgecolor not in (None, "auto", "none") - else {} - ), - }, - }, - ) - handle = Line2D(self, entry) - if transform is not None: - handle._transform = transform - handles.append(handle) - return handles + # The overlay marks the same Line2D; it must not claim its own + # legend slot when labels are assigned positionally. + overlay["_legend_skip"] = True + handle = Line2D(self, entry) + if transform is not None: + handle._transform = transform + return handle def scatter( self, x: Any, y: Any, s: Any = None, c: Any = None, **kwargs: Any @@ -1001,12 +1028,22 @@ def scatter( s = np.asarray(s)[finite_color] x, y, c = xv, yv, cv + size_px = marker_size_to_scatter_size(s, default=8.0) entry_kwargs: dict[str, Any] = { - "size": marker_size_to_scatter_size(s, default=8.0), + "size": size_px, "opacity": float(alpha) if alpha is not None else 0.8, "name": str(label) if label is not None else None, "symbol": _marker_symbol(marker) if marker else "circle", } + if isinstance(size_px, np.ndarray) and size_px.size: + # matplotlib s= is absolute (points²); pin the engine's size range + # to the converted pixel values so normalization is the identity + # instead of compressing everything into the default 2-18 px band. + finite_sizes = size_px[np.isfinite(size_px)] + if finite_sizes.size: + lo_px, hi_px = float(finite_sizes.min()), float(finite_sizes.max()) + if hi_px > lo_px: + entry_kwargs["size_range"] = (lo_px, hi_px) if c is None: entry_kwargs["color"] = self._next_color() elif isinstance(c, str) or ( @@ -1041,6 +1078,10 @@ def scatter( entry = self._add("scatter", {"x": x, "y": y, "kwargs": entry_kwargs}) if source_color is not None: entry["source_array"] = source_color + if "colormap" in entry_kwargs: + levels = _discrete_levels(cmap) + if levels is not None: + entry["discrete_levels"] = levels artist = PathCollection(self, entry) if transform is not None: artist._transform = transform @@ -1234,7 +1275,13 @@ def hist( containers: list[BarContainer] = [] base = np.zeros(len(edges) - 1, dtype=np.float64) centers = (edges[:-1] + edges[1:]) * 0.5 - width = float(np.min(np.diff(edges))) * (1.0 if stacked else 0.8 / len(datasets)) + # matplotlib's bin filling: a single (or stacked) series spans the full + # bin so adjacent bars touch; only multiple side-by-side series shrink + # to 0.8 of the bin, split evenly. The shim previously applied the 0.8 + # factor to single-series hists too, leaving visible gaps. + binwidth = float(np.min(np.diff(edges))) + rel_width = 1.0 if (stacked or len(datasets) == 1) else 0.8 + width = binwidth * rel_width / (1 if stacked else len(datasets)) for index, values in enumerate(counts): positions = centers if stacked else centers + (index - (len(datasets) - 1) / 2) * width current_base = base.copy() if stacked else np.zeros_like(values) @@ -1515,6 +1562,21 @@ def imshow(self, z: Any, cmap: Any = None, **kwargs: Any) -> AxesImage: if clim is not None: vmin, vmax = clim has_extremes = any(hasattr(cmap, f"_{key}") for key in ("bad", "under", "over")) + # A resampled colormap (plt.get_cmap(name, N)) with no *customized* + # extremes must render N flat bands through the ordinary heatmap path so + # a later plt.clim() still applies; only genuine set_under/over/bad + # customization needs the Python-baked truecolor branch below. + imshow_levels = _discrete_levels(cmap) if not truecolor and norm is None else None + if imshow_levels is not None and has_extremes: + default_extremes = ( + getattr(cmap, "_under", None) is None + and getattr(cmap, "_over", None) is None + and getattr(cmap, "_bad", "transparent") in ("transparent", None) + ) + if default_extremes: + has_extremes = False + else: + imshow_levels = None if not truecolor and norm is not None and callable(norm) and not has_extremes: mapped = np.ma.asarray(norm(grid), dtype=np.float64) cmap_callable = cmap if callable(cmap) else None @@ -1683,6 +1745,8 @@ def extreme(name: str, default: tuple[float, float, float, float]) -> np.ndarray "extent": bounds, }, ) + if imshow_levels is not None: + entry["discrete_levels"] = imshow_levels image = AxesImage(self, entry) if clip_path is not None: image.set_clip_path(clip_path) @@ -1807,7 +1871,7 @@ def text( style["coordinate_space"] = "figure_fraction" if style: akw["style"] = style - return Text(self, self._add("@text", {"args": (x, y, str(s)), "kwargs": akw})) + return Text(self, self._add("@text", {"args": (x, y, _plain_text(s)), "kwargs": akw})) def annotate(self, text: str, xy: tuple, xytext: Optional[tuple] = None, **kwargs: Any) -> Text: arrowprops = kwargs.pop("arrowprops", None) @@ -1833,19 +1897,16 @@ def annotate(self, text: str, xy: tuple, xytext: Optional[tuple] = None, **kwarg akw["anchor"] = {"left": "start", "center": "middle", "right": "end"}.get( str(ha), "start" ) + text_xy = xy if xytext is not None: if textcoords in {"offset points", "offset pixels"}: scale = 4.0 / 3.0 if textcoords == "offset points" else 1.0 akw["dx"], akw["dy"] = float(xytext[0]) * scale, -float(xytext[1]) * scale + elif all(_is_number(v) for v in (*xytext, *xy)): + # matplotlib places the text AT xytext (data coordinates). + text_xy = (float(xytext[0]), float(xytext[1])) else: - akw["dx"] = ( - float(xytext[0] - xy[0]) if _is_number(xytext[0]) and _is_number(xy[0]) else 8.0 - ) - akw["dy"] = ( - float(xytext[1] - xy[1]) - if _is_number(xytext[1]) and _is_number(xy[1]) - else -8.0 - ) + akw["dx"], akw["dy"] = 8.0, -8.0 style: dict[str, Any] = {} if xycoords is self.transAxes or xycoords == "axes fraction": style["coordinate_space"] = "axes_fraction" @@ -1867,9 +1928,58 @@ def annotate(self, text: str, xy: tuple, xytext: Optional[tuple] = None, **kwarg style["rotation"] = 90.0 if rotation == "vertical" else float(rotation) if style: akw["style"] = style + if arrowprops is not None and text_xy != xy: + if style.get("coordinate_space"): + raise not_implemented( + "annotate(arrowprops=) outside data coordinates", + "data-coordinate annotations", + ) + if not all(_is_number(v) for v in (*text_xy, *xy)): + raise not_implemented( + "annotate(arrowprops=) with non-numeric coordinates", + "numeric data coordinates", + ) + # Straight arrow from the text toward the point; arrowstyle and + # connectionstyle curves are approximated by this straight shaft. + shrink = float(arrowprops.get("shrink", 0.0)) + sx0, sy0 = float(text_xy[0]), float(text_xy[1]) + ex0, ey0 = float(xy[0]), float(xy[1]) + if shrink: + dx_a, dy_a = ex0 - sx0, ey0 - sy0 + sx0, sy0 = sx0 + shrink * dx_a, sy0 + shrink * dy_a + ex0, ey0 = ex0 - shrink * dx_a, ey0 - shrink * dy_a + fancy = "arrowstyle" not in arrowprops + arrow_color = resolve_color( + arrowprops.get("color") + or arrowprops.get("facecolor") + or arrowprops.get("edgecolor") + or "black" + ) + self._add( + "@arrow", + { + "args": (sx0, sy0, ex0, ey0), + "kwargs": { + "color": arrow_color, + "width": float( + arrowprops.get( + "width", + arrowprops.get( + "lw", arrowprops.get("linewidth", 3.0 if fancy else 1.5) + ), + ) + ), + "style": { + "head_size": float(arrowprops.get("headwidth", 12.0 if fancy else 8.0)) + }, + }, + }, + ) return Text( self, - self._add("@text", {"args": (xy[0], xy[1], str(text)), "kwargs": akw}), + self._add( + "@text", {"args": (text_xy[0], text_xy[1], _plain_text(text)), "kwargs": akw} + ), ) # -- axis config ----------------------------------------------------------- @@ -1934,7 +2044,7 @@ def set(self, **kwargs: Any) -> "Axes": else: # matplotlib: xticklabels=[] hides labels but keeps the ticks. props.pop("tick_labels", None) - props["tick_label_strategy"] = "none" + props["tick_label_strategy"] = "off" self._invalidate() return self @@ -2807,7 +2917,7 @@ def tick_params(self, axis: str = "both", **kwargs: Any) -> None: raise ValueError("tick_params() direction must be 'in', 'out', or 'inout'") style["tick_direction"] = direction if label_visible is not None: - props["tick_label_strategy"] = None if label_visible else "none" + props["tick_label_strategy"] = None if label_visible else "off" self._invalidate() def set_xticks( @@ -2831,7 +2941,7 @@ def set_xticks( else: props.pop("tick_labels", None) if labels is not None: - props["tick_labels"] = [str(value) for value in labels] + props["tick_labels"] = [_plain_text(value) for value in labels] if len(props["tick_labels"]) != len(props.get("tick_values", [])): raise ValueError("labels must have the same length as ticks") # matplotlib: explicit labels install a FixedFormatter, displacing @@ -2863,7 +2973,7 @@ def set_yticks( else: props.pop("tick_labels", None) if labels is not None: - props["tick_labels"] = [str(value) for value in labels] + props["tick_labels"] = [_plain_text(value) for value in labels] if len(props["tick_labels"]) != len(props.get("tick_values", [])): raise ValueError("labels must have the same length as ticks") key = "y2" if self._y2_of is not None else "y" @@ -2963,22 +3073,19 @@ def twiny(self) -> "Axes": def legend(self, *args: Any, **kwargs: Any) -> None: host = self._y2_of or self if len(args) >= 2: - _handles, labels = args[:2] - for label in labels: - host._add( - "scatter", - { - "x": [np.nan], - "y": [np.nan], - "kwargs": { - "color": "#333333", - "size": 8.0, - "opacity": 1.0, - "name": _plain_text(label), - "symbol": "square", - }, - }, - ) + # legend(handles, labels): relabel the artists the caller passed. + handles, labels = args[0], args[1] + for handle, label in zip(handles, labels, strict=False): + entry = getattr(handle, "_entry", None) + if entry is not None: + entry.setdefault("kwargs", {})["name"] = _plain_text(label) + elif len(args) == 1: + # legend(labels): assign labels positionally to the plotted artists, + # skipping marker overlays that share their line's legend slot. + labels = args[0] + eligible = [entry for entry in host._entries if not entry.get("_legend_skip")] + for entry, label in zip(eligible, labels, strict=False): + entry.setdefault("kwargs", {})["name"] = _plain_text(label) host._legend = True loc = kwargs.pop("loc", rcParams["legend.loc"]) ncols = kwargs.pop("ncols", kwargs.pop("ncol", 1)) @@ -3114,10 +3221,14 @@ def _apply_tickers(self, key: str, props: dict[str, Any]) -> None: """Resolve a user locator/formatter into concrete tick props (in place).""" locator = self._tickers.get((key, "major_locator")) formatter = self._tickers.get((key, "major_formatter")) - if locator is None and formatter is None: + is_log = ( + props.get("type_") == "log" or (self._scale_specs.get(key) or {}).get("name") == "log" + ) + if locator is None and formatter is None and not is_log: return spec = self._scale_specs.get(key) or {"name": "linear"} lo, hi = self._ticker_view(key, props) + auto_log = False if locator is not None: ticks = np.asarray(locator.tick_values(lo, hi), dtype=float).reshape(-1) pad = (hi - lo) * 1e-9 @@ -3129,13 +3240,18 @@ def _apply_tickers(self, key: str, props: dict[str, Any]) -> None: else: from ._ticker import LogLocator - auto = LogLocator() if props.get("type_") == "log" else AutoLocator() + auto = LogLocator() if is_log else AutoLocator() ticks = np.asarray(auto.tick_values(lo, hi), dtype=float).reshape(-1) + auto_log = is_log props["tick_values"] = list(map(float, _scale_values(ticks, spec))) if formatter is not None: props["tick_labels"] = [ - formatter(float(value), position) for position, value in enumerate(ticks) + _plain_text(formatter(float(value), position)) + for position, value in enumerate(ticks) ] + elif auto_log: + # matplotlib's LogFormatter look: decades label as 10^k. + props["tick_labels"] = [_pow10_label(value) for value in ticks] elif spec.get("name") != "linear": props["tick_labels"] = [f"{value:g}" for value in ticks] else: @@ -3153,11 +3269,27 @@ def _chart_children(self) -> list[Any]: kind = e["kind"] axis_kw = {"y_axis": e["y_axis"]} if e["y_axis"] != "y" else {} kw = e.get("kwargs", {}) + name = kw.get("name") + if isinstance(name, str) and "$" in name: # legend text carries mathtext + kw["name"] = _plain_text(name) if kind == "line": children.append(fc.line(x=e["x"], y=e["y"], **kw, **axis_kw)) elif kind == "scatter": kw = dict(kw) domain = kw.pop("domain", None) # vmin/vmax → the color channel window + levels = e.get("discrete_levels") + if levels is not None and "colormap" in kw and not isinstance(kw.get("color"), str): + color_vals = np.asarray(kw.get("color"), dtype=np.float64) + dom = domain + if dom is None: + finite = color_vals[np.isfinite(color_vals)] + dom = ( + (float(finite.min()), float(finite.max())) + if finite.size + else (0.0, 1.0) + ) + kw["color"] = _quantize_to_levels(color_vals, dom, int(levels)) + domain = dom if domain is not None: kw["color_domain"] = (float(domain[0]), float(domain[1])) children.append(fc.scatter(x=e["x"], y=e["y"], **kw, **axis_kw)) @@ -3168,11 +3300,29 @@ def _chart_children(self) -> list[Any]: elif kind == "histogram": children.append(fc.histogram(values=e["values"], **kw, **axis_kw)) elif kind == "heatmap": - children.append(fc.heatmap(z=e["z"], **kw, **axis_kw)) + z = e["z"] + levels = e.get("discrete_levels") + if levels is not None: + zarr = np.asarray(z, dtype=np.float64) + if zarr.ndim == 2: + kw = dict(kw) + dom = kw.get("domain") + if dom is None: + finite = zarr[np.isfinite(zarr)] + dom = ( + (float(finite.min()), float(finite.max())) + if finite.size + else (0.0, 1.0) + ) + z = _quantize_to_levels(zarr, dom, int(levels)) + kw["domain"] = (float(dom[0]), float(dom[1])) + children.append(fc.heatmap(z=z, **kw, **axis_kw)) elif kind == "@mark": children.append(getattr(fc, e["factory"])(*e["args"], **kw, **axis_kw)) elif kind == "@hline": children.append(fc.hline(*e["args"], **kw)) + elif kind == "@arrow": + children.append(fc.arrow(*e["args"], **kw)) elif kind == "@vline": children.append(fc.vline(*e["args"], **kw)) elif kind == "@x_band": @@ -3271,10 +3421,62 @@ def _build_chart(self, width: int, height: int) -> Any: styles=self._chrome_styles, ) if self._colorbar is not None: - self._chart.figure().colorbar_options = dict(self._colorbar) + figure = self._chart.figure() + options = dict(self._colorbar) + if options.pop("_autoscale", False): + derived = _colorbar_figure_domain(figure) + if derived is not None: + options["domain"] = [derived[0], derived[1]] + figure.colorbar_options = options return self._chart +def _discrete_levels(cmap: Any) -> Optional[int]: + """Number of quantization bands for a resampled colormap (``get_cmap(n, N)``). + + Matplotlib's builtin continuous colormaps carry ``N == 256``; a smaller N + (e.g. ``plt.get_cmap('viridis', 6)``) requests N flat bands. Returns that + count only when it is a genuine down-sampling, else None (stay continuous). + """ + n = getattr(cmap, "N", None) + if isinstance(n, (int, np.integer)) and 1 <= int(n) < 256: + return int(n) + return None + + +def _quantize_to_levels(values: Any, domain: tuple[float, float], levels: int) -> np.ndarray: + """Snap values to the representative value of their band, so a later + linear colormap lookup over ``domain`` reproduces matplotlib's N discrete + bands. NaN is preserved (missing cells stay transparent).""" + v = np.asarray(values, dtype=np.float64) + lo, hi = float(domain[0]), float(domain[1]) + span = (hi - lo) or 1.0 + u = np.clip((v - lo) / span, 0.0, 1.0) + if levels <= 1: + q = np.zeros_like(u) + else: + q = np.minimum(np.floor(u * levels), levels - 1) / (levels - 1) + return lo + q * span + + +def _colorbar_figure_domain(figure: Any) -> Optional[tuple[float, float]]: + """Value domain of the last color-mapped trace on a compiled figure. + + Used to back a colorbar whose mappable computes its domain inside the mark + (e.g. hexbin counts), where it is not knowable when ``colorbar()`` runs. + """ + for trace in reversed(getattr(figure, "traces", []) or []): + style = getattr(trace, "style", None) or {} + if style.get("role") == "heatmap" and style.get("domain") is not None: + lo, hi = style["domain"] + return (float(lo), float(hi)) + color_ch = getattr(trace, "color_ch", None) + if color_ch is not None and color_ch.mode == "continuous" and color_ch.domain: + lo, hi = color_ch.domain + return (float(lo), float(hi)) + return None + + def _is_number(v: Any) -> bool: return isinstance(v, (int, float, np.integer, np.floating)) @@ -3425,8 +3627,26 @@ def _marker_symbol(marker: Any) -> str: return "circle" +_SUPERSCRIPT_DIGITS = str.maketrans("0123456789-", "⁰¹²³⁴⁵⁶⁷⁸⁹⁻") + + +def _pow10_label(value: float) -> str: + """Matplotlib's log-decade label: 10 with a unicode superscript exponent.""" + exponent = np.log10(value) if value > 0 else np.nan + if not np.isfinite(exponent) or abs(exponent - round(exponent)) > 1e-9: + return f"{value:g}" + return "10" + str(round(float(exponent))).translate(_SUPERSCRIPT_DIGITS) + + def _plain_text(value: Any) -> str: - text = str(value).replace("$", "") + text = str(value) + converted = mathtext_to_unicode(text) + if converted != text: + return converted + if "$" not in text and "\\" not in text: + return text + # ASCII fallback for TeX outside the unicode subset — approximate, never raw. + text = text.replace("$", "") replacements = { "\\Delta": "Delta", "\\mu": "mu", @@ -3494,6 +3714,24 @@ def _step_values(x: np.ndarray, y: np.ndarray, where: str) -> tuple[np.ndarray, raise ValueError("step must be 'pre', 'post', or 'mid'") +def _plot_series_columns(x: Any, y: Any) -> list[tuple[Any, Any]]: + """Split a plot() operand pair into per-column 1-D series. + + matplotlib draws one line per column of a 2-D operand (broadcasting a 1-D + partner), and each column consumes the next entry of the property cycle. + A purely 1-D pair yields a single series unchanged. + """ + if np.ndim(x) < 2 and np.ndim(y) < 2: + return [(x, y)] + if np.ndim(x) == 1: + x = np.broadcast_to(np.asarray(x)[:, None], np.shape(y)) + if np.ndim(y) == 1: + y = np.broadcast_to(np.asarray(y)[:, None], np.shape(x)) + if x.shape != y.shape: + raise ValueError("2-D plot x and y must have matching shapes") + return [(x[:, i], y[:, i]) for i in range(x.shape[1])] + + def _iter_plot_groups(args: tuple) -> list[tuple[Any, Any, Optional[str]]]: """matplotlib plot() arg grammar: repeated [x], y, [fmt] groups.""" groups: list[tuple[Any, Any, Optional[str]]] = [] diff --git a/python/xy/pyplot/_colors.py b/python/xy/pyplot/_colors.py index 5a4fb221..57b58585 100644 --- a/python/xy/pyplot/_colors.py +++ b/python/xy/pyplot/_colors.py @@ -65,8 +65,8 @@ "greys": "gray", "turbo": "turbo", "coolwarm": "coolwarm", - "rdbu": "coolwarm_r", - "rdbu_r": "coolwarm", + "rdbu": "rdbu", + "rdbu_r": "rdbu_r", "bwr": "coolwarm", "blues": "blues", "rdylgn": "rdylgn", diff --git a/python/xy/pyplot/_mathtext.py b/python/xy/pyplot/_mathtext.py new file mode 100644 index 00000000..50d618c1 --- /dev/null +++ b/python/xy/pyplot/_mathtext.py @@ -0,0 +1,150 @@ +"""Bounded TeX-subset → unicode text conversion for shim-rendered chrome. + +Matplotlib renders ``$...$`` spans with its own mathtext engine; xy's static +exporters draw plain glyph runs. This module converts the small TeX subset +that chart labels actually use (greek letters, super/subscripts, common +operators, ``\\frac``) into unicode so ``km$^2$`` reads km² instead of raw +TeX source. It is total: input that uses anything outside the subset is +returned unchanged rather than half-converted. +""" + +# ruff: noqa: RUF001 — the whole point of this module is unicode lookalikes. +from __future__ import annotations + +import re + +_COMMANDS = { + "alpha": "α", + "beta": "β", + "gamma": "γ", + "delta": "δ", + "epsilon": "ε", + "zeta": "ζ", + "eta": "η", + "theta": "θ", + "kappa": "κ", + "lambda": "λ", + "mu": "μ", + "nu": "ν", + "xi": "ξ", + "pi": "π", + "rho": "ρ", + "sigma": "σ", + "tau": "τ", + "phi": "φ", + "chi": "χ", + "psi": "ψ", + "omega": "ω", + "Gamma": "Γ", + "Delta": "Δ", + "Theta": "Θ", + "Lambda": "Λ", + "Xi": "Ξ", + "Pi": "Π", + "Sigma": "Σ", + "Phi": "Φ", + "Psi": "Ψ", + "Omega": "Ω", + "times": "×", + "cdot": "·", + "pm": "±", + "mp": "∓", + "leq": "≤", + "le": "≤", + "geq": "≥", + "ge": "≥", + "neq": "≠", + "ne": "≠", + "approx": "≈", + "sim": "~", + "infty": "∞", + "partial": "∂", + "nabla": "∇", + "sqrt": "√", + "circ": "°", + "degree": "°", + "rightarrow": "→", + "to": "→", + "leftarrow": "←", + "sum": "Σ", + "prod": "Π", + "int": "∫", + "propto": "∝", + "in": "∈", + "percent": "%", + "%": "%", + ",": " ", + ";": " ", + " ": " ", + "!": "", +} + +# Wrappers whose braces disappear and whose contents pass through. +_WRAPPERS = ("mathdefault", "mathrm", "mathit", "mathbf", "text", "textrm", "operatorname") + +_SUPERSCRIPTS = dict(zip("0123456789+-=()ni", "⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿⁱ", strict=True)) +_SUBSCRIPTS = dict( + zip("0123456789+-=()aehiklmnoprstuvx", "₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₕᵢₖₗₘₙₒₚᵣₛₜᵤᵥₓ", strict=True) +) + +_MATH_SPAN = re.compile(r"\$([^$]*)\$") +_FRAC = re.compile(r"\\frac\{([^{}]*)\}\{([^{}]*)\}") +_SCRIPT = re.compile(r"([\^_])(\{[^{}]*\}|[^\s{}])") +_COMMAND = re.compile(r"\\([A-Za-z]+|[%,;! ])") + + +def _convert_script(kind: str, body: str) -> str | None: + """Unicode super/subscript for a ^/_ argument; None when a char has none.""" + body = body[1:-1] if body.startswith("{") else body + table = _SUPERSCRIPTS if kind == "^" else _SUBSCRIPTS + if not body or any(ch not in table for ch in body): + return None + return "".join(table[ch] for ch in body) + + +def _convert_math(body: str) -> str | None: + """Convert one $...$ span; None when it needs more TeX than we speak.""" + out = body + for _ in range(4): # nested \frac + replaced = _FRAC.sub(lambda m: f"{m.group(1)}/{m.group(2)}", out) + if replaced == out: + break + out = replaced + + def script(match: re.Match[str]) -> str: + converted = _convert_script(match.group(1), match.group(2)) + return "\x00" if converted is None else converted + + # Scripts first: converting ^{3} removes the inner braces, so wrappers + # like \mathdefault{10^{3}} become flat and unwrap cleanly below. + out = _SCRIPT.sub(script, out) + if "\x00" in out: + return None + for name in _WRAPPERS: + out = re.sub(r"\\" + name + r"\{([^{}]*)\}", r"\1", out) + out = out.replace("\\left", "").replace("\\right", "") + + def command(match: re.Match[str]) -> str: + return _COMMANDS.get(match.group(1), "\x00") + + out = _COMMAND.sub(command, out) + if "\x00" in out or "\\" in out: + return None + return out.replace("{", "").replace("}", "") + + +def mathtext_to_unicode(text: str) -> str: + """Render ``$...$`` spans as unicode; unconvertible input passes through.""" + if "$" not in text: + return text + pieces: list[str] = [] + last = 0 + for match in _MATH_SPAN.finditer(text): + converted = _convert_math(match.group(1)) + if converted is None: + return text + pieces.append(text[last : match.start()]) + pieces.append(converted) + last = match.end() + pieces.append(text[last:]) + return "".join(pieces) diff --git a/python/xy/pyplot/_mplfig.py b/python/xy/pyplot/_mplfig.py index 20f0f11a..43e83c38 100644 --- a/python/xy/pyplot/_mplfig.py +++ b/python/xy/pyplot/_mplfig.py @@ -15,7 +15,7 @@ import numpy as np from ._artists import Text -from ._axes import Axes +from ._axes import Axes, _plain_text from ._rc import rc_figsize_px from ._transforms import CoordinateTransform from ._translate import check_unsupported, not_implemented @@ -166,10 +166,20 @@ def subplots( axes = make_axes_grid(self, int(nrows), int(ncols), squeeze=squeeze) self._width_ratios = None if width_ratios is None else tuple(map(float, width_ratios)) self._height_ratios = None if height_ratios is None else tuple(map(float, height_ratios)) - apply_sharing(self, bool(sharex), bool(sharey)) + apply_sharing(self, _share_mode(sharex, "sharex"), _share_mode(sharey, "sharey")) + self._hide_inner_tick_labels(int(nrows), int(ncols)) self._invalidate() return axes + def _hide_inner_tick_labels(self, nrows: int, ncols: int) -> None: + """Matplotlib's shared-axes rule: only edge panels keep tick labels.""" + for index, ax in enumerate(self._axes): + row, col = index // ncols, index % ncols + if self._sharex in ("all", "col") and row < nrows - 1: + ax._axis_props("x")["tick_label_strategy"] = "off" + if self._sharey in ("all", "row") and col > 0: + ax._axis_props("y")["tick_label_strategy"] = "off" + def add_gridspec(self, nrows: int = 1, ncols: int = 1, **kwargs: Any) -> "_GridSpec": """Return a lightweight GridSpec facade backed by the current grid. @@ -272,7 +282,7 @@ def suptitle(self, title: str, **kwargs: Any) -> None: va = kwargs.pop("va", kwargs.pop("verticalalignment", "top")) if kwargs: raise TypeError(f"suptitle() got unsupported keyword argument {next(iter(kwargs))!r}") - self._suptitle = str(title) + self._suptitle = _plain_text(title) self._suptitle_style = { "size": float(size), "weight": str(weight), @@ -437,9 +447,17 @@ def colorbar(self, mappable: Any = None, cax: Any = None, ax: Any = None, **kwar if explicit_domain is not None else ([float(finite.min()), float(finite.max())] if finite.size else [0.0, 1.0]) ), - "label": str(kwargs.pop("label", "")), + "label": _plain_text(kwargs.pop("label", "")), "orientation": str(kwargs.pop("orientation", "vertical")), } + # When the mappable's value domain is not knowable at colorbar() time + # (e.g. hexbin counts are binned inside the mark), defer to the compiled + # figure's color domain at render time instead of the 0..1 placeholder. + if explicit_domain is None and not finite.size: + options["_autoscale"] = True + levels = entry.get("discrete_levels") + if levels is not None: + options["levels"] = int(levels) ticks = kwargs.pop("ticks", None) if ticks is not None: options["ticks"] = [float(value) for value in np.asarray(ticks).reshape(-1)] @@ -468,7 +486,7 @@ def add_lines(self, *args: Any, **kwargs: Any) -> None: def set_label(self, label: str, **kwargs: Any) -> None: del kwargs - self._options["label"] = str(label) + self._options["label"] = _plain_text(label) self.ax._invalidate() def set_ticks(self, ticks: Any, labels: Any = None, **kwargs: Any) -> None: @@ -534,10 +552,23 @@ def _panel_px(self) -> tuple[int, int]: w, h = rc_figsize_px(self._figsize, self._dpi) return max(120, w // self._ncols), max(120, h // self._nrows) + def _effective_rects(self) -> Optional[list[tuple[float, float, float, float]]]: + """Per-axes figure rects when any axes is free-form, else None. + + Matplotlib places a rect-less axes at the SubplotParams default, so a + default axes mixed with an inset keeps its full-size position instead + of dragging every axes back onto the uniform grid. + """ + rects = [ax._figure_rect for ax in self._axes] + if not self._axes or not any(rect is not None for rect in rects): + return None + default = (0.125, 0.11, 0.775, 0.77) + return [rect if rect is not None else default for rect in rects] + def _charts(self) -> list[Any]: total_w, total_h = rc_figsize_px(self._figsize, self._dpi) - rects = [rect for ax in self._axes if (rect := ax._figure_rect) is not None] - if self._axes and len(rects) == len(self._axes): + rects = self._effective_rects() + if rects is not None: charts = [] for ax, rect in zip(self._axes, rects, strict=True): plot_w = max(1, round(total_w * rect[2])) @@ -567,16 +598,35 @@ def _charts(self) -> list[Any]: if not shared: continue linked.append(dim) - ranges = [ - figure.x_range() if dim == "x" else figure.y_range() for figure in figures - ] - domain = (min(min(pair) for pair in ranges), max(max(pair) for pair in ranges)) - for figure in figures: - figure._set_axis_domain(dim, domain) + for group in self._share_groups(shared, len(figures)): + members = [figures[i] for i in group] + ranges = [ + figure.x_range() if dim == "x" else figure.y_range() for figure in members + ] + domain = ( + min(min(pair) for pair in ranges), + max(max(pair) for pair in ranges), + ) + for figure in members: + figure._set_axis_domain(dim, domain) for figure in figures: figure.set_interaction(link_group=self._link_group, link_axes=tuple(linked)) return charts + def _share_groups(self, mode: Any, count: int) -> list[list[int]]: + """Panel-index groups whose data domains union under a share mode.""" + if mode == "col": + return [ + [r * self._ncols + c for r in range(self._nrows) if r * self._ncols + c < count] + for c in range(self._ncols) + ] + if mode == "row": + return [ + [r * self._ncols + c for c in range(self._ncols) if r * self._ncols + c < count] + for r in range(self._nrows) + ] + return [list(range(count))] + def _single(self) -> Optional[Any]: charts = self._charts() if ( @@ -699,7 +749,7 @@ def _to_png(self, *, bbox_tight: bool = False, pad_inches: float = 0.1) -> bytes from ._grid import stitch_png canvas_size = rc_figsize_px(self._figsize, self._dpi) - rects = [rect for ax in self._axes if (rect := ax._figure_rect) is not None] + rects = self._effective_rects() positions = ( [ ( @@ -714,7 +764,7 @@ def _to_png(self, *, bbox_tight: bool = False, pad_inches: float = 0.1) -> bytes ) for rect in rects ] - if self._axes and len(rects) == len(self._axes) + if rects is not None else None ) @@ -928,7 +978,18 @@ def make_axes_grid(fig: Figure, nrows: int, ncols: int, squeeze: bool = True) -> return axes -def apply_sharing(fig: Figure, sharex: bool, sharey: bool) -> None: +def _share_mode(value: Any, label: str) -> Any: + """Normalize matplotlib's sharex/sharey values to False | 'all' | 'row' | 'col'.""" + if value is None or value is False or value == "none": + return False + if value is True or value == "all": + return "all" + if value in ("row", "col"): + return value + raise ValueError(f"{label} must be one of True, False, 'all', 'none', 'row', 'col'") + + +def apply_sharing(fig: Figure, sharex: Any, sharey: Any) -> None: """Share static domains and live pan/zoom ranges across subplot panels.""" - fig._sharex = bool(sharex) - fig._sharey = bool(sharey) + fig._sharex = _share_mode(sharex, "sharex") + fig._sharey = _share_mode(sharey, "sharey") diff --git a/python/xy/pyplot/_plot_types.py b/python/xy/pyplot/_plot_types.py index b3d27d04..b4a9e627 100644 --- a/python/xy/pyplot/_plot_types.py +++ b/python/xy/pyplot/_plot_types.py @@ -1993,9 +1993,24 @@ def subset_limit(flag: Any) -> Any: xerr = _limit_error(xerr, xlolims, xuplims, len(np.asarray(x))) base = line_kwargs(kwargs) check_unsupported(kwargs, "errorbar()") - color = ( - resolve_color(ecolor) if ecolor is not None else base.get("color", self._next_color()) - ) + # When ecolor is omitted, the bars inherit the resolved data-series + # color, exactly as matplotlib does: an explicit color kwarg wins, then + # a color from the fmt string (e.g. ".k"), then the property cycle. + fmt_color = parse_fmt(fmt)[0] if fmt and fmt.lower() != "none" else None + # The marker/line color resolves independently of ecolor: an explicit + # ecolor recolors only the bars (fmt='o', color='black', + # ecolor='lightgray' keeps black markers). + line_color: Optional[str] = None + if "color" in base: + line_color = base["color"] + elif fmt_color is not None: + line_color = resolve_color(fmt_color) + if ecolor is not None: + color = resolve_color(ecolor) + else: + if line_color is None: + line_color = self._next_color() + color = line_color entry = self._add( "@mark", { @@ -2015,8 +2030,12 @@ def subset_limit(flag: Any) -> Any: data_line: Optional[Line2D] = None if fmt.lower() != "none": line_kwargs_for_plot: dict[str, Any] = {} - if "color" in base: - line_kwargs_for_plot["color"] = base["color"] + # Pass the already-resolved series color so plot() renders the data + # line/markers in the same color as the bars without re-advancing + # the property cycle (fmt still supplies the color when line_color + # is None, e.g. when ecolor was given explicitly). + if line_color is not None: + line_kwargs_for_plot["color"] = line_color if "width" in base: line_kwargs_for_plot["linewidth"] = base["width"] if "opacity" in base: @@ -2129,7 +2148,10 @@ def _contour(self, filled: bool, args: tuple[Any, ...], kwargs: dict[str, Any]) ) elif args: raise TypeError("contour() expects Z, [levels] or X, Y, Z, [levels]") - levels = kwargs.pop("levels", positional_levels if positional_levels is not None else 10) + # Matplotlib's auto-level path is MaxNLocator(N+1) with N=7; matching + # that default count keeps xy's contour density in step (it previously + # defaulted to 10, ~1.5x too many isolines). + levels = kwargs.pop("levels", positional_levels if positional_levels is not None else 7) cmap = kwargs.pop("cmap", None) colors = kwargs.pop("colors", None) linewidths = kwargs.pop("linewidths", None) @@ -2195,6 +2217,9 @@ def _contour(self, filled: bool, args: tuple[Any, ...], kwargs: dict[str, Any]) "opacity": 0.0 if transparent_fill else (0.9 if alpha is None else float(alpha)), + # Matplotlib dashes negative-level lines for a single-color + # contour; a colormapped contour keeps every level solid. + "dash_negative": color is not None, }, "source_z": za, "domain": (float(public_levels[0]), float(public_levels[-1])), @@ -2880,7 +2905,7 @@ def make_edges(spec: Any, bounds: tuple[float, float], label: str) -> np.ndarray mark_kwargs["domain"] = (float(vmin), float(vmax)) entry = self._add( "@mark", - {"factory": "heatmap", "args": (h.T,), "kwargs": mark_kwargs}, + {"factory": "heatmap", "args": (h.T,), "kwargs": mark_kwargs, "source_z": h.T}, ) return h, xedges, yedges, PolyCollection(self, entry) diff --git a/python/xy/pyplot/_translate.py b/python/xy/pyplot/_translate.py index 2c041dc7..adbaaaf1 100644 --- a/python/xy/pyplot/_translate.py +++ b/python/xy/pyplot/_translate.py @@ -43,11 +43,11 @@ "4": "triangle", "8": "circle", "s": "square", - "p": "square", + "p": "pentagon", "P": "cross", - "*": "diamond", - "h": "circle", - "H": "circle", + "*": "star", + "h": "hexagon", + "H": "hexagon", "+": "cross", "x": "cross", "X": "cross", diff --git a/python/xy/static/index.js b/python/xy/static/index.js index b9dc9360..b030fb04 100644 --- a/python/xy/static/index.js +++ b/python/xy/static/index.js @@ -2,49 +2,26 @@ "use strict"; const PROTOCOL = 3; const COLORMAP_STOPS = { -viridis: [ -[68, 1, 84], [72, 40, 120], [62, 74, 137], [49, 104, 142], [38, 130, 142], -[31, 158, 137], [53, 183, 121], [110, 206, 88], [181, 222, 43], [253, 231, 37], -], -magma: [ -[0, 0, 4], [28, 16, 68], [79, 18, 123], [129, 37, 129], [181, 54, 122], -[229, 80, 100], [251, 135, 97], [254, 194, 135], [252, 253, 191], [252, 253, 191], -], -plasma: [ -[13, 8, 135], [84, 2, 163], [139, 10, 165], [185, 50, 137], [219, 92, 104], -[244, 136, 73], [254, 188, 43], [240, 249, 33], [240, 249, 33], [240, 249, 33], -], -inferno: [ -[0, 0, 4], [31, 12, 72], [85, 15, 109], [136, 34, 106], [186, 54, 85], -[227, 89, 51], [249, 140, 10], [249, 201, 50], [252, 255, 164], [252, 255, 164], -], -cividis: [ -[0, 32, 76], [0, 42, 102], [39, 63, 108], [72, 85, 115], [106, 109, 120], -[143, 133, 118], [181, 159, 105], [223, 187, 82], [253, 217, 63], [255, 233, 69], -], -gray: [ -[0, 0, 0], [28, 28, 28], [57, 57, 57], [85, 85, 85], [113, 113, 113], -[142, 142, 142], [170, 170, 170], [198, 198, 198], [227, 227, 227], [255, 255, 255], -], -turbo: [ -[48, 18, 59], [70, 107, 227], [40, 187, 226], [61, 242, 148], [161, 253, 60], -[232, 216, 33], [253, 149, 35], [225, 66, 13], [153, 15, 4], [122, 4, 3], -], -coolwarm: [ -[59, 76, 192], [87, 117, 211], [119, 154, 231], [157, 185, 243], [197, 209, 246], -[221, 220, 220], [242, 196, 174], [237, 158, 130], [214, 96, 77], [180, 4, 38], -], -blues: [[247, 251, 255], [222, 235, 247], [198, 219, 239], [158, 202, 225], [107, 174, 214], [66, 146, 198], [33, 113, 181], [8, 81, 156], [8, 48, 107], [8, 48, 107]], -rdylgn: [[165, 0, 38], [215, 48, 39], [244, 109, 67], [253, 174, 97], [254, 224, 139], [217, 239, 139], [166, 217, 106], [102, 189, 99], [26, 152, 80], [0, 104, 55]], -rainbow: [[128, 0, 255], [57, 88, 255], [0, 180, 235], [0, 235, 176], [73, 255, 104], [176, 235, 38], [235, 180, 0], [255, 88, 0], [235, 0, 57], [255, 0, 0]], -spectral: [[158, 1, 66], [213, 62, 79], [244, 109, 67], [253, 174, 97], [254, 224, 139], [230, 245, 152], [171, 221, 164], [102, 194, 165], [50, 136, 189], [94, 79, 162]], -piyg: [[142, 1, 82], [197, 27, 125], [222, 119, 174], [241, 182, 218], [253, 224, 239], [247, 247, 247], [230, 245, 208], [184, 225, 134], [127, 188, 65], [77, 146, 33], [39, 100, 25]], -purples: [[252, 251, 253], [239, 237, 245], [218, 218, 235], [188, 189, 220], [158, 154, 200], [128, 125, 186], [106, 81, 163], [84, 39, 143], [63, 0, 125]], -pubu: [[255, 247, 251], [236, 231, 242], [208, 209, 230], [166, 189, 219], [116, 169, 207], [54, 144, 192], [5, 112, 176], [4, 90, 141], [2, 56, 88]], -prgn: [[64, 0, 75], [118, 42, 131], [153, 112, 171], [194, 165, 207], [231, 212, 232], [247, 247, 247], [217, 240, 211], [166, 219, 160], [90, 174, 97], [27, 120, 55], [0, 68, 27]], -rdgy: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [254, 254, 254], [224, 224, 224], [185, 185, 185], [135, 135, 135], [76, 76, 76], [26, 26, 26]], -jet: [[0, 0, 128], [0, 0, 241], [0, 76, 255], [0, 176, 255], [41, 255, 206], [125, 255, 122], [206, 255, 41], [255, 196, 0], [255, 104, 0], [241, 8, 0], [128, 0, 0]], binary: [[255, 255, 255], [0, 0, 0]], +gray: [[0, 0, 0], [25, 25, 25], [51, 51, 51], [76, 76, 76], [102, 102, 102], [128, 128, 128], [153, 153, 153], [179, 179, 179], [204, 204, 204], [230, 230, 230], [255, 255, 255]], +viridis: [[68, 1, 84], [72, 36, 117], [65, 68, 135], [53, 95, 141], [42, 120, 142], [33, 145, 140], [34, 168, 132], [68, 191, 112], [122, 209, 81], [189, 223, 38], [253, 231, 37]], +plasma: [[13, 8, 135], [65, 4, 157], [106, 0, 168], [143, 13, 164], [177, 42, 144], [204, 71, 120], [225, 100, 98], [242, 132, 75], [252, 166, 54], [252, 206, 37], [240, 249, 33]], +inferno: [[0, 0, 4], [22, 11, 57], [66, 10, 104], [106, 23, 110], [147, 38, 103], [188, 55, 84], [221, 81, 58], [243, 120, 25], [252, 165, 10], [246, 215, 70], [252, 255, 164]], +magma: [[0, 0, 4], [20, 14, 54], [59, 15, 112], [100, 26, 128], [140, 41, 129], [183, 55, 121], [222, 73, 104], [247, 112, 92], [254, 159, 109], [254, 207, 146], [252, 253, 191]], +cividis: [[0, 34, 78], [8, 51, 112], [53, 69, 108], [79, 87, 108], [102, 105, 112], [125, 124, 120], [148, 142, 119], [174, 163, 113], [200, 184, 102], [229, 207, 82], [254, 232, 56]], +coolwarm: [[59, 76, 192], [89, 119, 227], [123, 159, 249], [158, 190, 255], [192, 212, 245], [221, 220, 220], [242, 203, 183], [247, 172, 142], [238, 132, 104], [214, 82, 68], [180, 4, 38]], +turbo: [[48, 18, 59], [69, 89, 203], [62, 155, 254], [25, 213, 205], [70, 248, 132], [164, 252, 60], [225, 221, 55], [254, 164, 49], [240, 91, 18], [195, 37, 3], [122, 4, 3]], +rainbow: [[128, 0, 255], [78, 77, 252], [25, 150, 243], [24, 205, 228], [77, 243, 206], [128, 255, 180], [178, 243, 150], [230, 205, 115], [255, 150, 79], [255, 77, 39], [255, 0, 0]], +jet: [[0, 0, 128], [0, 0, 241], [0, 76, 255], [0, 176, 255], [41, 255, 206], [125, 255, 122], [206, 255, 41], [255, 196, 0], [255, 104, 0], [241, 8, 0], [128, 0, 0]], +rdgy: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [254, 254, 254], [224, 224, 224], [185, 185, 185], [135, 135, 135], [76, 76, 76], [26, 26, 26]], +rdbu: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [246, 247, 247], [209, 229, 240], [144, 196, 221], [67, 147, 195], [32, 101, 171], [5, 48, 97]], +blues: [[247, 251, 255], [227, 238, 249], [208, 225, 242], [183, 212, 234], [148, 196, 223], [106, 174, 214], [74, 152, 201], [46, 126, 188], [23, 100, 171], [8, 74, 145], [8, 48, 107]], +purples: [[252, 251, 253], [242, 240, 247], [226, 226, 239], [206, 207, 229], [182, 182, 216], [158, 154, 200], [134, 131, 189], [114, 98, 172], [97, 64, 155], [79, 31, 139], [63, 0, 125]], +pubu: [[255, 247, 251], [240, 234, 244], [219, 218, 235], [192, 201, 226], [156, 185, 217], [115, 169, 207], [66, 149, 195], [24, 124, 182], [5, 103, 162], [4, 83, 130], [2, 56, 88]], +piyg: [[142, 1, 82], [196, 26, 124], [222, 119, 174], [241, 181, 217], [253, 224, 239], [247, 247, 246], [230, 245, 208], [183, 224, 133], [127, 188, 65], [76, 145, 33], [39, 100, 25]], +prgn: [[64, 0, 75], [117, 41, 130], [153, 112, 171], [193, 164, 206], [231, 212, 232], [246, 247, 246], [217, 240, 211], [165, 218, 159], [90, 174, 97], [26, 119, 54], [0, 68, 27]], +rdylgn: [[165, 0, 38], [214, 47, 39], [244, 109, 67], [253, 173, 96], [254, 224, 139], [254, 255, 190], [217, 239, 139], [165, 216, 106], [102, 189, 99], [25, 151, 80], [0, 104, 55]], +spectral: [[158, 1, 66], [212, 61, 79], [244, 109, 67], [253, 173, 96], [254, 224, 139], [255, 255, 190], [230, 245, 152], [170, 220, 164], [102, 194, 165], [51, 135, 188], [94, 79, 162]], }; function colormapStops(name) { const reversed = typeof name === "string" && name.endsWith("_r"); @@ -449,10 +426,32 @@ float fcMarkerSdf(vec2 d, int shape) { vec2 a = abs(d); return min(max(a.x - 0.17, a.y - 0.5), max(a.x - 0.5, a.y - 0.17)); } - if (shape == 5) { // regular hexagon - const float k = 0.8660254; - vec2 p = abs(d); - return max(p.x - 0.5, p.y * 0.5 + p.x * k - 0.5); + if (shape == 5) { // regular hexagon (pointy top) + const vec3 k = vec3(-0.866025404, 0.5, 0.577350269); + vec2 p = abs(vec2(d.y, d.x)); + p -= 2.0 * min(dot(k.xy, p), 0.0) * k.xy; + p -= vec2(clamp(p.x, -k.z * 0.5, k.z * 0.5), 0.5); + return length(p) * sign(p.y); + } + if (shape == 6) { // regular pentagon (apex up) + const vec3 k = vec3(0.809016994, 0.587785252, 0.726542528); + vec2 p = vec2(abs(d.x), -d.y); + p -= 2.0 * min(dot(vec2(-k.x, k.y), p), 0.0) * vec2(-k.x, k.y); + p -= 2.0 * min(dot(vec2( k.x, k.y), p), 0.0) * vec2( k.x, k.y); + p -= vec2(clamp(p.x, -k.z * 0.5, k.z * 0.5), 0.5); + return length(p) * sign(p.y); + } + if (shape == 7) { // five-pointed star (apex up) + const float rf = 0.45; + const vec2 k1 = vec2(0.809016994, -0.587785252); + const vec2 k2 = vec2(-k1.x, k1.y); + vec2 p = vec2(abs(d.x), -d.y); + p -= 2.0 * max(dot(k1, p), 0.0) * k1; + p -= 2.0 * max(dot(k2, p), 0.0) * k2; + p = vec2(abs(p.x), p.y - 0.5); + vec2 ba = rf * vec2(-k1.y, k1.x) - vec2(0.0, 1.0); + float h = clamp(dot(p, ba) / dot(ba, ba), 0.0, 0.5); + return length(p - ba * h) * sign(p.y * ba.x - p.x * ba.y); } if (shape == 3) { // triangle (apex up) const float k = 1.7320508; @@ -2376,7 +2375,7 @@ this._pointMarkStyle(g, t); } _pointMarkStyle(g, t) { const s = t.style || {}; -g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5 }[s.symbol] || 0; +g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5, pentagon: 6, star: 7 }[s.symbol] || 0; g.pointStrokeWidth = Number(s.stroke_width) || 0; const markOpaque = [g.color[0], g.color[1], g.color[2], 1]; g.pointStroke = s.stroke @@ -3451,7 +3450,7 @@ const raw = axis && axis.tick_label_strategy !== undefined ? axis.tick_label_strategy : this._axisStyleValue(axis, "tick_label_strategy"); const value = String(raw || "auto").replace(/-/g, "_"); -return ["auto", "hide", "rotate", "stagger", "none"].includes(value) ? value : "auto"; +return ["auto", "hide", "rotate", "stagger", "none", "off"].includes(value) ? value : "auto"; } _axisTickLabelAngle(axis) { const raw = axis && axis.tick_label_angle !== undefined @@ -3515,6 +3514,7 @@ const baseAngle = explicitAngle === null ? 0 : explicitAngle; const withBase = labels.map((label) => ({ ...label, angle: baseAngle, row: 0 })); let strategy = this._axisTickLabelStrategy(axis); if (strategy === "none") return []; +if (strategy === "off") return []; if (strategy === "auto") { if (!this._tickLabelsCollide(withBase, dim, fontSize, minGap)) return withBase; if (dim === "x" && axis.kind === "category" && labels.length <= 16) strategy = "rotate"; diff --git a/python/xy/static/standalone.js b/python/xy/static/standalone.js index 085a40c5..a7bf9008 100644 --- a/python/xy/static/standalone.js +++ b/python/xy/static/standalone.js @@ -3,49 +3,26 @@ "use strict"; const PROTOCOL = 3; const COLORMAP_STOPS = { -viridis: [ -[68, 1, 84], [72, 40, 120], [62, 74, 137], [49, 104, 142], [38, 130, 142], -[31, 158, 137], [53, 183, 121], [110, 206, 88], [181, 222, 43], [253, 231, 37], -], -magma: [ -[0, 0, 4], [28, 16, 68], [79, 18, 123], [129, 37, 129], [181, 54, 122], -[229, 80, 100], [251, 135, 97], [254, 194, 135], [252, 253, 191], [252, 253, 191], -], -plasma: [ -[13, 8, 135], [84, 2, 163], [139, 10, 165], [185, 50, 137], [219, 92, 104], -[244, 136, 73], [254, 188, 43], [240, 249, 33], [240, 249, 33], [240, 249, 33], -], -inferno: [ -[0, 0, 4], [31, 12, 72], [85, 15, 109], [136, 34, 106], [186, 54, 85], -[227, 89, 51], [249, 140, 10], [249, 201, 50], [252, 255, 164], [252, 255, 164], -], -cividis: [ -[0, 32, 76], [0, 42, 102], [39, 63, 108], [72, 85, 115], [106, 109, 120], -[143, 133, 118], [181, 159, 105], [223, 187, 82], [253, 217, 63], [255, 233, 69], -], -gray: [ -[0, 0, 0], [28, 28, 28], [57, 57, 57], [85, 85, 85], [113, 113, 113], -[142, 142, 142], [170, 170, 170], [198, 198, 198], [227, 227, 227], [255, 255, 255], -], -turbo: [ -[48, 18, 59], [70, 107, 227], [40, 187, 226], [61, 242, 148], [161, 253, 60], -[232, 216, 33], [253, 149, 35], [225, 66, 13], [153, 15, 4], [122, 4, 3], -], -coolwarm: [ -[59, 76, 192], [87, 117, 211], [119, 154, 231], [157, 185, 243], [197, 209, 246], -[221, 220, 220], [242, 196, 174], [237, 158, 130], [214, 96, 77], [180, 4, 38], -], -blues: [[247, 251, 255], [222, 235, 247], [198, 219, 239], [158, 202, 225], [107, 174, 214], [66, 146, 198], [33, 113, 181], [8, 81, 156], [8, 48, 107], [8, 48, 107]], -rdylgn: [[165, 0, 38], [215, 48, 39], [244, 109, 67], [253, 174, 97], [254, 224, 139], [217, 239, 139], [166, 217, 106], [102, 189, 99], [26, 152, 80], [0, 104, 55]], -rainbow: [[128, 0, 255], [57, 88, 255], [0, 180, 235], [0, 235, 176], [73, 255, 104], [176, 235, 38], [235, 180, 0], [255, 88, 0], [235, 0, 57], [255, 0, 0]], -spectral: [[158, 1, 66], [213, 62, 79], [244, 109, 67], [253, 174, 97], [254, 224, 139], [230, 245, 152], [171, 221, 164], [102, 194, 165], [50, 136, 189], [94, 79, 162]], -piyg: [[142, 1, 82], [197, 27, 125], [222, 119, 174], [241, 182, 218], [253, 224, 239], [247, 247, 247], [230, 245, 208], [184, 225, 134], [127, 188, 65], [77, 146, 33], [39, 100, 25]], -purples: [[252, 251, 253], [239, 237, 245], [218, 218, 235], [188, 189, 220], [158, 154, 200], [128, 125, 186], [106, 81, 163], [84, 39, 143], [63, 0, 125]], -pubu: [[255, 247, 251], [236, 231, 242], [208, 209, 230], [166, 189, 219], [116, 169, 207], [54, 144, 192], [5, 112, 176], [4, 90, 141], [2, 56, 88]], -prgn: [[64, 0, 75], [118, 42, 131], [153, 112, 171], [194, 165, 207], [231, 212, 232], [247, 247, 247], [217, 240, 211], [166, 219, 160], [90, 174, 97], [27, 120, 55], [0, 68, 27]], -rdgy: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [254, 254, 254], [224, 224, 224], [185, 185, 185], [135, 135, 135], [76, 76, 76], [26, 26, 26]], -jet: [[0, 0, 128], [0, 0, 241], [0, 76, 255], [0, 176, 255], [41, 255, 206], [125, 255, 122], [206, 255, 41], [255, 196, 0], [255, 104, 0], [241, 8, 0], [128, 0, 0]], binary: [[255, 255, 255], [0, 0, 0]], +gray: [[0, 0, 0], [25, 25, 25], [51, 51, 51], [76, 76, 76], [102, 102, 102], [128, 128, 128], [153, 153, 153], [179, 179, 179], [204, 204, 204], [230, 230, 230], [255, 255, 255]], +viridis: [[68, 1, 84], [72, 36, 117], [65, 68, 135], [53, 95, 141], [42, 120, 142], [33, 145, 140], [34, 168, 132], [68, 191, 112], [122, 209, 81], [189, 223, 38], [253, 231, 37]], +plasma: [[13, 8, 135], [65, 4, 157], [106, 0, 168], [143, 13, 164], [177, 42, 144], [204, 71, 120], [225, 100, 98], [242, 132, 75], [252, 166, 54], [252, 206, 37], [240, 249, 33]], +inferno: [[0, 0, 4], [22, 11, 57], [66, 10, 104], [106, 23, 110], [147, 38, 103], [188, 55, 84], [221, 81, 58], [243, 120, 25], [252, 165, 10], [246, 215, 70], [252, 255, 164]], +magma: [[0, 0, 4], [20, 14, 54], [59, 15, 112], [100, 26, 128], [140, 41, 129], [183, 55, 121], [222, 73, 104], [247, 112, 92], [254, 159, 109], [254, 207, 146], [252, 253, 191]], +cividis: [[0, 34, 78], [8, 51, 112], [53, 69, 108], [79, 87, 108], [102, 105, 112], [125, 124, 120], [148, 142, 119], [174, 163, 113], [200, 184, 102], [229, 207, 82], [254, 232, 56]], +coolwarm: [[59, 76, 192], [89, 119, 227], [123, 159, 249], [158, 190, 255], [192, 212, 245], [221, 220, 220], [242, 203, 183], [247, 172, 142], [238, 132, 104], [214, 82, 68], [180, 4, 38]], +turbo: [[48, 18, 59], [69, 89, 203], [62, 155, 254], [25, 213, 205], [70, 248, 132], [164, 252, 60], [225, 221, 55], [254, 164, 49], [240, 91, 18], [195, 37, 3], [122, 4, 3]], +rainbow: [[128, 0, 255], [78, 77, 252], [25, 150, 243], [24, 205, 228], [77, 243, 206], [128, 255, 180], [178, 243, 150], [230, 205, 115], [255, 150, 79], [255, 77, 39], [255, 0, 0]], +jet: [[0, 0, 128], [0, 0, 241], [0, 76, 255], [0, 176, 255], [41, 255, 206], [125, 255, 122], [206, 255, 41], [255, 196, 0], [255, 104, 0], [241, 8, 0], [128, 0, 0]], +rdgy: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [254, 254, 254], [224, 224, 224], [185, 185, 185], [135, 135, 135], [76, 76, 76], [26, 26, 26]], +rdbu: [[103, 0, 31], [177, 24, 43], [214, 96, 77], [243, 164, 129], [253, 219, 199], [246, 247, 247], [209, 229, 240], [144, 196, 221], [67, 147, 195], [32, 101, 171], [5, 48, 97]], +blues: [[247, 251, 255], [227, 238, 249], [208, 225, 242], [183, 212, 234], [148, 196, 223], [106, 174, 214], [74, 152, 201], [46, 126, 188], [23, 100, 171], [8, 74, 145], [8, 48, 107]], +purples: [[252, 251, 253], [242, 240, 247], [226, 226, 239], [206, 207, 229], [182, 182, 216], [158, 154, 200], [134, 131, 189], [114, 98, 172], [97, 64, 155], [79, 31, 139], [63, 0, 125]], +pubu: [[255, 247, 251], [240, 234, 244], [219, 218, 235], [192, 201, 226], [156, 185, 217], [115, 169, 207], [66, 149, 195], [24, 124, 182], [5, 103, 162], [4, 83, 130], [2, 56, 88]], +piyg: [[142, 1, 82], [196, 26, 124], [222, 119, 174], [241, 181, 217], [253, 224, 239], [247, 247, 246], [230, 245, 208], [183, 224, 133], [127, 188, 65], [76, 145, 33], [39, 100, 25]], +prgn: [[64, 0, 75], [117, 41, 130], [153, 112, 171], [193, 164, 206], [231, 212, 232], [246, 247, 246], [217, 240, 211], [165, 218, 159], [90, 174, 97], [26, 119, 54], [0, 68, 27]], +rdylgn: [[165, 0, 38], [214, 47, 39], [244, 109, 67], [253, 173, 96], [254, 224, 139], [254, 255, 190], [217, 239, 139], [165, 216, 106], [102, 189, 99], [25, 151, 80], [0, 104, 55]], +spectral: [[158, 1, 66], [212, 61, 79], [244, 109, 67], [253, 173, 96], [254, 224, 139], [255, 255, 190], [230, 245, 152], [170, 220, 164], [102, 194, 165], [51, 135, 188], [94, 79, 162]], }; function colormapStops(name) { const reversed = typeof name === "string" && name.endsWith("_r"); @@ -450,10 +427,32 @@ float fcMarkerSdf(vec2 d, int shape) { vec2 a = abs(d); return min(max(a.x - 0.17, a.y - 0.5), max(a.x - 0.5, a.y - 0.17)); } - if (shape == 5) { // regular hexagon - const float k = 0.8660254; - vec2 p = abs(d); - return max(p.x - 0.5, p.y * 0.5 + p.x * k - 0.5); + if (shape == 5) { // regular hexagon (pointy top) + const vec3 k = vec3(-0.866025404, 0.5, 0.577350269); + vec2 p = abs(vec2(d.y, d.x)); + p -= 2.0 * min(dot(k.xy, p), 0.0) * k.xy; + p -= vec2(clamp(p.x, -k.z * 0.5, k.z * 0.5), 0.5); + return length(p) * sign(p.y); + } + if (shape == 6) { // regular pentagon (apex up) + const vec3 k = vec3(0.809016994, 0.587785252, 0.726542528); + vec2 p = vec2(abs(d.x), -d.y); + p -= 2.0 * min(dot(vec2(-k.x, k.y), p), 0.0) * vec2(-k.x, k.y); + p -= 2.0 * min(dot(vec2( k.x, k.y), p), 0.0) * vec2( k.x, k.y); + p -= vec2(clamp(p.x, -k.z * 0.5, k.z * 0.5), 0.5); + return length(p) * sign(p.y); + } + if (shape == 7) { // five-pointed star (apex up) + const float rf = 0.45; + const vec2 k1 = vec2(0.809016994, -0.587785252); + const vec2 k2 = vec2(-k1.x, k1.y); + vec2 p = vec2(abs(d.x), -d.y); + p -= 2.0 * max(dot(k1, p), 0.0) * k1; + p -= 2.0 * max(dot(k2, p), 0.0) * k2; + p = vec2(abs(p.x), p.y - 0.5); + vec2 ba = rf * vec2(-k1.y, k1.x) - vec2(0.0, 1.0); + float h = clamp(dot(p, ba) / dot(ba, ba), 0.0, 0.5); + return length(p - ba * h) * sign(p.y * ba.x - p.x * ba.y); } if (shape == 3) { // triangle (apex up) const float k = 1.7320508; @@ -2377,7 +2376,7 @@ this._pointMarkStyle(g, t); } _pointMarkStyle(g, t) { const s = t.style || {}; -g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5 }[s.symbol] || 0; +g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5, pentagon: 6, star: 7 }[s.symbol] || 0; g.pointStrokeWidth = Number(s.stroke_width) || 0; const markOpaque = [g.color[0], g.color[1], g.color[2], 1]; g.pointStroke = s.stroke @@ -3452,7 +3451,7 @@ const raw = axis && axis.tick_label_strategy !== undefined ? axis.tick_label_strategy : this._axisStyleValue(axis, "tick_label_strategy"); const value = String(raw || "auto").replace(/-/g, "_"); -return ["auto", "hide", "rotate", "stagger", "none"].includes(value) ? value : "auto"; +return ["auto", "hide", "rotate", "stagger", "none", "off"].includes(value) ? value : "auto"; } _axisTickLabelAngle(axis) { const raw = axis && axis.tick_label_angle !== undefined @@ -3516,6 +3515,7 @@ const baseAngle = explicitAngle === null ? 0 : explicitAngle; const withBase = labels.map((label) => ({ ...label, angle: baseAngle, row: 0 })); let strategy = this._axisTickLabelStrategy(axis); if (strategy === "none") return []; +if (strategy === "off") return []; if (strategy === "auto") { if (!this._tickLabelsCollide(withBase, dim, fontSize, minGap)) return withBase; if (dim === "x" && axis.kind === "category" && labels.length <= 16) strategy = "rotate"; diff --git a/scripts/abi_smoke.py b/scripts/abi_smoke.py index 3643efda..6ec7c8f0 100644 --- a/scripts/abi_smoke.py +++ b/scripts/abi_smoke.py @@ -1445,7 +1445,9 @@ def ok(cond: bool, msg: str) -> None: "multi-span raster-to-PNG emits a valid signature", ) - heat_values = array("d", [1.0 / 255.0, 128.0 / 255.0, 1.0, 0.0]) + # NaN marks a missing cell; a real 0.0 now paints the colormap floor + # (matplotlib semantics — see the visual-parity changelog entry). + heat_values = array("d", [1.0 / 255.0, 128.0 / 255.0, 1.0, float("nan")]) heat_stops = array("B", [0, 10, 20, 100, 110, 120]) heat_rgba = array("B", [0]) * 16 ok( diff --git a/scripts/gen_font.py b/scripts/gen_font.py index c512aa9b..9c29a2c6 100644 --- a/scripts/gen_font.py +++ b/scripts/gen_font.py @@ -12,14 +12,26 @@ `src/font.rs`. """ +# ruff: noqa: RUF001 — the EXTRA glyph list is deliberately unicode. from __future__ import annotations from pathlib import Path from PIL import Image, ImageFont -# Printable ASCII. Axis labels/titles/legends stay within this set. +# Printable ASCII. Axis labels/titles/legends mostly stay within this set. FIRST, LAST = 0x20, 0x7E +# Extra codepoints for the pyplot shim's TeX-subset → unicode conversion +# (greek, super/subscripts, math operators) plus typography mpl emits (−, µ). +EXTRA = sorted( + set( + "αβγδεζηθικλμνξοπρστυφχψω" + "ΓΔΘΛΞΠΣΥΦΨΩ" + "⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿⁱ" + "₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₕᵢₖₗₘₙₒₚᵣₛₜᵤᵥₓ" + "×·±∓≤≥≠≈∞√°→←∂∇∫∝∈−–—‘’“”…µ" + ) +) PX = 16 # render size; runtime scales this coverage bitmap to the requested size. ROOT = Path(__file__).resolve().parents[1] OUT = ROOT / "src" / "font.rs" @@ -38,7 +50,8 @@ def main() -> None: cell_h = ascent + descent glyphs = [] # (advance, w, h, left, top_from_baseline, coverage bytes) - for code in range(FIRST, LAST + 1): + codepoints = [*range(FIRST, LAST + 1), *(ord(ch) for ch in EXTRA)] + for code in codepoints: ch = chr(code) # Advance width (horizontal pen movement). advance = round(face.getlength(ch)) @@ -82,6 +95,11 @@ def _emit(cell_h: int, ascent: int, blob: bytearray, records: list) -> None: f"pub const CELL_H: i32 = {cell_h};", f"pub const ASCENT: i32 = {ascent};", "", + "/// Codepoints of the non-ASCII glyphs, sorted; GLYPHS row = 95 + index.", + f"pub static EXTRA_CODEPOINTS: [u32; {len(EXTRA)}] = [", + " " + ", ".join(str(ord(ch)) for ch in EXTRA) + ",", + "];", + "", "/// Per-glyph metrics at BASE_PX: (advance, w, h, left, top, cov_off, cov_len).", "/// `top` is the pixel offset of the glyph's top edge below the baseline", "/// (negative = above). Coverage bytes live in `COVERAGE[cov_off..][..cov_len]`,", diff --git a/src/font.rs b/src/font.rs index 1739cbc7..18d05564 100644 --- a/src/font.rs +++ b/src/font.rs @@ -9,11 +9,16 @@ pub const BASE_PX: i32 = 16; pub const CELL_H: i32 = 19; pub const ASCENT: i32 = 15; +/// Codepoints of the non-ASCII glyphs, sorted; GLYPHS row = 95 + index. +pub static EXTRA_CODEPOINTS: [u32; 110] = [ + 176, 177, 178, 179, 181, 183, 185, 215, 915, 916, 920, 923, 926, 928, 931, 933, 934, 936, 937, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 963, 964, 965, 966, 967, 968, 969, 7522, 7523, 7524, 7525, 8211, 8212, 8216, 8217, 8220, 8221, 8230, 8304, 8305, 8308, 8309, 8310, 8311, 8312, 8313, 8314, 8315, 8316, 8317, 8318, 8319, 8320, 8321, 8322, 8323, 8324, 8325, 8326, 8327, 8328, 8329, 8330, 8331, 8332, 8333, 8334, 8336, 8337, 8338, 8339, 8341, 8342, 8343, 8344, 8345, 8346, 8347, 8348, 8592, 8594, 8706, 8711, 8712, 8722, 8723, 8730, 8733, 8734, 8747, 8776, 8800, 8804, 8805, +]; + /// Per-glyph metrics at BASE_PX: (advance, w, h, left, top, cov_off, cov_len). /// `top` is the pixel offset of the glyph's top edge below the baseline /// (negative = above). Coverage bytes live in `COVERAGE[cov_off..][..cov_len]`, /// row-major w*h, 0..=255. -pub static GLYPHS: [(i32, i32, i32, i32, i32, u32, u32); 95] = [ +pub static GLYPHS: [(i32, i32, i32, i32, i32, u32, u32); 205] = [ (5, 0, 0, 0, 0, 0, 0), (6, 6, 12, 0, -12, 0, 72), (7, 7, 12, 0, -12, 72, 84), @@ -45,7 +50,7 @@ pub static GLYPHS: [(i32, i32, i32, i32, i32, u32, u32); 95] = [ (13, 13, 9, 0, -9, 2620, 117), (13, 13, 7, 0, -7, 2737, 91), (13, 13, 9, 0, -9, 2828, 117), - (9, 9, 12, 0, -12, 2945, 108), + (8, 9, 12, 0, -12, 2945, 108), (16, 16, 14, 0, -11, 3053, 224), (11, 11, 12, 0, -12, 3277, 132), (11, 11, 12, 0, -12, 3409, 132), @@ -57,7 +62,7 @@ pub static GLYPHS: [(i32, i32, i32, i32, i32, u32, u32); 95] = [ (12, 12, 12, 0, -12, 4189, 144), (5, 5, 12, 0, -12, 4333, 60), (5, 6, 15, -1, -12, 4393, 90), - (11, 11, 12, 0, -12, 4483, 132), + (10, 11, 12, 0, -12, 4483, 132), (9, 9, 12, 0, -12, 4615, 108), (14, 14, 12, 0, -12, 4723, 168), (12, 12, 12, 0, -12, 4891, 144), @@ -109,414 +114,926 @@ pub static GLYPHS: [(i32, i32, i32, i32, i32, u32, u32); 95] = [ (5, 5, 16, 0, -12, 9855, 80), (10, 10, 15, 0, -12, 9935, 150), (13, 13, 7, 0, -7, 10085, 91), + (8, 8, 12, 0, -12, 10176, 96), + (13, 13, 10, 0, -10, 10272, 130), + (6, 6, 12, 0, -12, 10402, 72), + (6, 6, 12, 0, -12, 10474, 72), + (10, 10, 12, 0, -9, 10546, 120), + (5, 5, 7, 0, -7, 10666, 35), + (6, 6, 12, 0, -12, 10701, 72), + (13, 13, 10, 0, -10, 10773, 130), + (9, 9, 12, 0, -12, 10903, 108), + (11, 11, 12, 0, -12, 11011, 132), + (13, 13, 12, 0, -12, 11143, 156), + (11, 11, 12, 0, -12, 11299, 132), + (10, 10, 12, 0, -12, 11431, 120), + (12, 12, 12, 0, -12, 11551, 144), + (10, 10, 12, 0, -12, 11695, 120), + (10, 11, 12, -1, -12, 11815, 132), + (13, 13, 12, 0, -12, 11947, 156), + (13, 13, 12, 0, -12, 12103, 156), + (12, 12, 12, 0, -12, 12259, 144), + (11, 11, 9, 0, -9, 12403, 99), + (10, 10, 16, 0, -13, 12502, 160), + (9, 9, 12, 0, -9, 12662, 108), + (10, 10, 12, 0, -12, 12770, 120), + (9, 9, 9, 0, -9, 12890, 81), + (9, 9, 15, 0, -12, 12971, 135), + (10, 10, 12, 0, -9, 13106, 120), + (10, 10, 12, 0, -12, 13226, 120), + (5, 5, 9, 0, -9, 13346, 45), + (9, 10, 9, 0, -9, 13391, 90), + (9, 9, 12, 0, -12, 13481, 108), + (10, 10, 12, 0, -9, 13589, 120), + (9, 9, 9, 0, -9, 13709, 81), + (9, 9, 15, 0, -12, 13790, 135), + (10, 10, 9, 0, -9, 13925, 90), + (10, 10, 9, 0, -9, 14015, 90), + (10, 10, 12, 0, -9, 14105, 120), + (10, 10, 9, 0, -9, 14225, 90), + (10, 10, 9, 0, -9, 14315, 90), + (9, 9, 9, 0, -9, 14405, 81), + (11, 11, 12, 0, -9, 14486, 132), + (9, 9, 12, 0, -9, 14618, 108), + (11, 11, 12, 0, -9, 14726, 132), + (13, 13, 9, 0, -9, 14858, 117), + (3, 3, 7, 0, -7, 14975, 21), + (4, 5, 6, 0, -6, 14996, 30), + (6, 6, 6, 0, -6, 15026, 36), + (7, 7, 6, 0, -6, 15062, 42), + (8, 8, 5, 0, -5, 15104, 40), + (16, 16, 5, 0, -5, 15144, 80), + (5, 5, 12, 0, -12, 15224, 60), + (5, 5, 12, 0, -12, 15284, 60), + (8, 8, 12, 0, -12, 15344, 96), + (8, 8, 12, 0, -12, 15440, 96), + (16, 16, 2, 0, -2, 15536, 32), + (6, 6, 12, 0, -12, 15568, 72), + (3, 3, 12, 0, -12, 15640, 36), + (6, 6, 12, 0, -12, 15676, 72), + (6, 6, 12, 0, -12, 15748, 72), + (6, 6, 12, 0, -12, 15820, 72), + (6, 6, 12, 0, -12, 15892, 72), + (6, 6, 12, 0, -12, 15964, 72), + (6, 6, 12, 0, -12, 16036, 72), + (8, 8, 11, 0, -11, 16108, 88), + (8, 8, 9, 0, -9, 16196, 72), + (8, 8, 10, 0, -10, 16268, 80), + (4, 4, 13, 0, -13, 16348, 52), + (4, 4, 13, 0, -13, 16400, 52), + (6, 6, 10, 0, -10, 16452, 60), + (6, 6, 7, 0, -7, 16512, 42), + (6, 6, 7, 0, -7, 16554, 42), + (6, 6, 7, 0, -7, 16596, 42), + (6, 6, 7, 0, -7, 16638, 42), + (6, 6, 7, 0, -7, 16680, 42), + (6, 6, 7, 0, -7, 16722, 42), + (6, 6, 7, 0, -7, 16764, 42), + (6, 6, 7, 0, -7, 16806, 42), + (6, 6, 7, 0, -7, 16848, 42), + (6, 6, 7, 0, -7, 16890, 42), + (8, 8, 6, 0, -6, 16932, 48), + (8, 8, 4, 0, -4, 16980, 32), + (8, 8, 5, 0, -5, 17012, 40), + (4, 4, 9, 0, -8, 17052, 36), + (4, 4, 9, 0, -8, 17088, 36), + (6, 6, 6, 0, -6, 17124, 36), + (7, 7, 6, 0, -6, 17160, 42), + (7, 7, 5, 0, -5, 17202, 35), + (7, 7, 6, 0, -6, 17237, 42), + (6, 6, 7, 0, -7, 17279, 42), + (7, 7, 8, 0, -8, 17321, 56), + (3, 3, 7, 0, -7, 17377, 21), + (10, 10, 5, 0, -5, 17398, 50), + (6, 6, 5, 0, -5, 17448, 30), + (7, 7, 8, 0, -6, 17478, 56), + (6, 6, 6, 0, -6, 17534, 36), + (5, 5, 7, 0, -7, 17570, 35), + (13, 13, 8, 0, -8, 17605, 104), + (13, 13, 8, 0, -8, 17709, 104), + (8, 8, 12, 0, -11, 17813, 96), + (11, 12, 12, -1, -12, 17909, 144), + (14, 14, 13, 0, -12, 18053, 182), + (13, 13, 5, 0, -5, 18235, 65), + (13, 13, 11, 0, -11, 18300, 143), + (10, 11, 13, 0, -13, 18443, 143), + (11, 11, 8, 0, -8, 18586, 88), + (13, 13, 8, 0, -8, 18674, 104), + (8, 8, 17, 0, -13, 18778, 136), + (13, 13, 9, 0, -9, 18914, 117), + (13, 13, 9, 0, -9, 19031, 117), + (13, 13, 9, 0, -9, 19148, 117), + (13, 13, 9, 0, -9, 19265, 117), ]; -pub static COVERAGE: [u8; 10176] = [ - 0, 0, 148, 255, 0, 0, 0, 0, 148, 255, 0, 0, 0, 0, 148, 255, 0, 0, 0, 0, 148, 255, 0, 0, 0, 0, - 148, 255, 0, 0, 0, 0, 143, 251, 0, 0, 0, 0, 130, 237, 0, 0, 0, 0, 115, 223, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 0, 0, 0, 0, 148, 255, 0, 0, 0, 116, 224, 0, 128, 212, - 0, 0, 116, 224, 0, 128, 212, 0, 0, 116, 224, 0, 128, 212, 0, 0, 116, 224, 0, 128, 212, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 224, 0, 0, - 215, 105, 0, 0, 0, 0, 0, 0, 0, 167, 155, 0, 28, 255, 37, 0, 0, 0, 0, 0, 0, 1, 233, 86, 0, 97, - 226, 0, 0, 0, 0, 0, 224, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 0, 0, 0, 121, 203, - 0, 0, 235, 84, 0, 0, 0, 0, 0, 0, 0, 177, 147, 0, 34, 255, 29, 0, 0, 0, 0, 0, 0, 0, 232, 91, 0, - 89, 231, 0, 0, 0, 0, 0, 196, 255, 255, 255, 255, 255, 255, 255, 255, 255, 72, 0, 0, 0, 0, 111, - 208, 0, 0, 229, 92, 0, 0, 0, 0, 0, 0, 0, 180, 139, 0, 43, 255, 24, 0, 0, 0, 0, 0, 0, 5, 243, - 70, 0, 110, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 104, 0, 0, 0, 0, 0, 0, 0, 0, 96, 104, 0, 0, 0, - 0, 0, 0, 83, 199, 246, 249, 197, 86, 1, 0, 0, 74, 254, 106, 108, 113, 60, 169, 34, 0, 0, 155, - 209, 0, 96, 104, 0, 0, 0, 0, 0, 138, 241, 31, 96, 104, 0, 0, 0, 0, 0, 24, 216, 246, 216, 174, - 70, 5, 0, 0, 0, 0, 4, 78, 178, 230, 251, 220, 40, 0, 0, 0, 0, 0, 96, 104, 32, 225, 180, 0, 0, - 0, 0, 0, 96, 104, 0, 166, 202, 0, 0, 144, 115, 42, 103, 113, 83, 244, 116, 0, 0, 26, 134, 210, - 248, 245, 202, 102, 1, 0, 0, 0, 0, 0, 96, 104, 0, 0, 0, 0, 0, 0, 0, 0, 96, 104, 0, 0, 0, 0, 0, - 35, 190, 243, 206, 55, 0, 0, 0, 0, 174, 145, 0, 0, 0, 0, 196, 158, 14, 125, 229, 9, 0, 0, 73, - 231, 15, 0, 0, 0, 10, 254, 48, 0, 9, 254, 53, 0, 8, 220, 90, 0, 0, 0, 0, 11, 255, 47, 0, 9, - 255, 52, 0, 129, 190, 0, 0, 0, 0, 0, 0, 198, 156, 14, 123, 230, 9, 36, 242, 41, 0, 0, 0, 0, 0, - 0, 37, 192, 244, 207, 58, 0, 184, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 225, 10, 28, - 183, 243, 211, 63, 0, 0, 0, 0, 0, 0, 12, 227, 80, 0, 183, 172, 15, 111, 236, 14, 0, 0, 0, 0, 0, - 140, 180, 0, 3, 249, 64, 0, 2, 249, 64, 0, 0, 0, 0, 43, 242, 33, 0, 3, 250, 63, 0, 2, 249, 64, - 0, 0, 0, 0, 193, 125, 0, 0, 0, 186, 172, 15, 111, 237, 14, 0, 0, 0, 94, 218, 7, 0, 0, 0, 31, - 186, 243, 212, 65, 0, 0, 0, 0, 87, 211, 244, 204, 88, 0, 0, 0, 0, 0, 0, 63, 254, 112, 11, 43, - 168, 27, 0, 0, 0, 0, 0, 146, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 248, 12, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 50, 254, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 253, 255, 122, 0, 0, 0, 0, 0, 0, 0, 92, - 255, 102, 149, 255, 119, 0, 0, 29, 255, 89, 0, 212, 187, 0, 0, 144, 255, 116, 0, 84, 253, 25, - 0, 244, 142, 0, 0, 0, 139, 255, 113, 196, 168, 0, 0, 201, 204, 2, 0, 0, 0, 134, 255, 245, 29, - 0, 0, 64, 249, 168, 40, 10, 48, 163, 251, 255, 118, 0, 0, 0, 52, 177, 238, 251, 225, 156, 39, - 125, 255, 122, 0, 116, 224, 0, 0, 116, 224, 0, 0, 116, 224, 0, 0, 116, 224, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, - 180, 0, 0, 0, 41, 251, 49, 0, 0, 0, 157, 198, 0, 0, 0, 12, 245, 108, 0, 0, 0, 74, 255, 48, 0, - 0, 0, 126, 255, 5, 0, 0, 0, 149, 241, 0, 0, 0, 0, 150, 240, 0, 0, 0, 0, 126, 255, 5, 0, 0, 0, - 74, 255, 50, 0, 0, 0, 13, 246, 111, 0, 0, 0, 0, 157, 200, 0, 0, 0, 0, 41, 251, 50, 0, 0, 0, 0, - 146, 181, 0, 0, 118, 206, 2, 0, 0, 0, 10, 232, 98, 0, 0, 0, 0, 138, 215, 0, 0, 0, 0, 48, 255, - 62, 0, 0, 0, 2, 241, 134, 0, 0, 0, 0, 197, 188, 0, 0, 0, 0, 177, 212, 0, 0, 0, 0, 178, 212, 0, - 0, 0, 0, 197, 188, 0, 0, 0, 1, 240, 135, 0, 0, 0, 47, 255, 63, 0, 0, 0, 137, 217, 1, 0, 0, 9, - 231, 99, 0, 0, 0, 117, 207, 2, 0, 0, 0, 0, 0, 112, 116, 0, 0, 0, 0, 0, 0, 112, 116, 0, 0, 0, - 65, 177, 45, 112, 116, 44, 175, 68, 0, 41, 159, 216, 216, 160, 42, 0, 0, 41, 158, 216, 216, - 158, 42, 0, 65, 178, 46, 112, 116, 44, 176, 69, 0, 0, 0, 112, 116, 0, 0, 0, 0, 0, 0, 112, 116, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 76, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 244, 92, 0, 0, 0, 0, 0, 0, 32, 255, 136, 0, 0, 43, 255, 112, 0, 0, 102, 238, 13, 0, 0, 165, - 126, 0, 0, 56, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 72, 255, 92, 0, 0, 72, 255, 92, 0, 0, 0, 0, 25, 254, 59, 0, 0, 0, 105, 233, - 2, 0, 0, 0, 185, 155, 0, 0, 0, 14, 249, 75, 0, 0, 0, 89, 243, 7, 0, 0, 0, 169, 171, 0, 0, 0, 6, - 241, 91, 0, 0, 0, 73, 250, 16, 0, 0, 0, 153, 187, 0, 0, 0, 1, 230, 107, 0, 0, 0, 57, 255, 28, - 0, 0, 0, 137, 203, 0, 0, 0, 0, 216, 123, 0, 0, 0, 0, 0, 0, 22, 163, 238, 244, 185, 43, 0, 0, 0, - 5, 208, 199, 32, 19, 166, 235, 25, 0, 0, 95, 255, 41, 0, 0, 12, 241, 142, 0, 0, 173, 220, 0, 0, - 0, 0, 175, 221, 0, 0, 216, 182, 0, 0, 0, 0, 136, 254, 9, 0, 234, 167, 0, 0, 0, 0, 120, 255, 25, - 0, 235, 167, 0, 0, 0, 0, 121, 255, 25, 0, 217, 182, 0, 0, 0, 0, 136, 254, 9, 0, 174, 220, 0, 0, - 0, 0, 175, 221, 0, 0, 96, 255, 41, 0, 0, 12, 241, 143, 0, 0, 5, 210, 199, 32, 19, 166, 236, 27, - 0, 0, 0, 23, 164, 239, 245, 186, 44, 0, 0, 0, 2, 67, 159, 242, 255, 32, 0, 0, 0, 0, 57, 189, - 98, 126, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, - 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, - 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, - 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 4, 255, 255, 255, 255, 255, 255, - 180, 0, 0, 30, 134, 211, 245, 220, 146, 19, 0, 0, 0, 192, 214, 71, 11, 53, 212, 217, 10, 0, 0, - 123, 11, 0, 0, 0, 60, 255, 94, 0, 0, 0, 0, 0, 0, 0, 26, 255, 125, 0, 0, 0, 0, 0, 0, 0, 98, 255, - 91, 0, 0, 0, 0, 0, 0, 20, 229, 226, 9, 0, 0, 0, 0, 0, 5, 189, 250, 64, 0, 0, 0, 0, 0, 1, 166, - 254, 90, 0, 0, 0, 0, 0, 0, 143, 255, 107, 0, 0, 0, 0, 0, 0, 121, 255, 123, 0, 0, 0, 0, 0, 0, - 97, 255, 140, 0, 0, 0, 0, 0, 0, 0, 212, 255, 255, 255, 255, 255, 255, 148, 0, 0, 10, 105, 200, - 243, 231, 179, 56, 0, 0, 0, 96, 138, 51, 11, 35, 172, 250, 53, 0, 0, 0, 0, 0, 0, 0, 13, 253, - 145, 0, 0, 0, 0, 0, 0, 0, 10, 252, 135, 0, 0, 0, 0, 0, 1, 30, 163, 231, 31, 0, 0, 0, 0, 196, - 255, 255, 231, 58, 0, 0, 0, 0, 0, 0, 3, 36, 165, 246, 63, 0, 0, 0, 0, 0, 0, 0, 4, 222, 192, 0, - 0, 0, 0, 0, 0, 0, 0, 183, 224, 0, 0, 0, 0, 0, 0, 0, 7, 227, 186, 0, 0, 158, 90, 30, 9, 46, 176, - 248, 59, 0, 0, 36, 152, 221, 247, 220, 163, 41, 0, 0, 0, 0, 0, 0, 0, 175, 255, 160, 0, 0, 0, 0, - 0, 0, 86, 238, 249, 160, 0, 0, 0, 0, 0, 19, 233, 104, 244, 160, 0, 0, 0, 0, 0, 163, 202, 2, - 244, 160, 0, 0, 0, 0, 73, 253, 52, 0, 244, 160, 0, 0, 0, 13, 226, 149, 0, 0, 244, 160, 0, 0, 0, - 151, 233, 16, 0, 0, 244, 160, 0, 0, 39, 254, 93, 0, 0, 0, 244, 160, 0, 0, 56, 255, 255, 255, - 255, 255, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 244, 160, 0, 0, 0, 0, 0, 0, 0, 0, 244, 160, 0, - 0, 0, 0, 0, 0, 0, 0, 244, 160, 0, 0, 0, 68, 255, 255, 255, 255, 255, 236, 0, 0, 0, 68, 255, 44, - 0, 0, 0, 0, 0, 0, 0, 68, 255, 44, 0, 0, 0, 0, 0, 0, 0, 68, 255, 44, 0, 0, 0, 0, 0, 0, 0, 68, - 255, 235, 251, 231, 159, 27, 0, 0, 0, 61, 131, 39, 13, 69, 219, 227, 21, 0, 0, 0, 0, 0, 0, 0, - 45, 254, 133, 0, 0, 0, 0, 0, 0, 0, 0, 225, 188, 0, 0, 0, 0, 0, 0, 0, 0, 225, 188, 0, 0, 0, 0, - 0, 0, 0, 44, 254, 134, 0, 0, 156, 92, 31, 12, 67, 217, 228, 22, 0, 0, 36, 153, 222, 250, 227, - 155, 26, 0, 0, 0, 0, 0, 74, 193, 243, 227, 138, 13, 0, 0, 0, 103, 241, 93, 14, 18, 114, 94, 0, - 0, 32, 247, 91, 0, 0, 0, 0, 0, 0, 0, 130, 240, 4, 0, 0, 0, 0, 0, 0, 0, 190, 196, 122, 224, 249, - 216, 106, 0, 0, 0, 216, 252, 218, 53, 16, 111, 255, 116, 0, 0, 218, 255, 71, 0, 0, 0, 179, 237, - 2, 0, 199, 255, 12, 0, 0, 0, 120, 255, 33, 0, 156, 255, 12, 0, 0, 0, 121, 255, 33, 0, 75, 255, - 70, 0, 0, 0, 178, 234, 2, 0, 1, 190, 217, 51, 15, 109, 254, 105, 0, 0, 0, 14, 148, 232, 249, - 211, 94, 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 203, 0, 0, 0, 0, 0, 0, 0, 67, 255, 130, 0, - 0, 0, 0, 0, 0, 0, 159, 255, 35, 0, 0, 0, 0, 0, 0, 9, 242, 194, 0, 0, 0, 0, 0, 0, 0, 89, 255, - 98, 0, 0, 0, 0, 0, 0, 0, 181, 245, 13, 0, 0, 0, 0, 0, 0, 22, 251, 162, 0, 0, 0, 0, 0, 0, 0, - 111, 255, 67, 0, 0, 0, 0, 0, 0, 0, 204, 225, 1, 0, 0, 0, 0, 0, 0, 40, 255, 131, 0, 0, 0, 0, 0, - 0, 0, 133, 255, 36, 0, 0, 0, 0, 0, 0, 1, 224, 195, 0, 0, 0, 0, 0, 0, 0, 66, 190, 236, 242, 203, - 92, 0, 0, 0, 55, 250, 158, 24, 15, 122, 255, 94, 0, 0, 142, 253, 10, 0, 0, 0, 215, 185, 0, 0, - 138, 252, 10, 0, 0, 0, 216, 181, 0, 0, 33, 232, 158, 23, 15, 121, 246, 63, 0, 0, 0, 55, 229, - 255, 255, 245, 87, 0, 0, 0, 57, 243, 150, 26, 17, 114, 252, 95, 0, 0, 187, 220, 2, 0, 0, 0, - 177, 235, 0, 0, 225, 181, 0, 0, 0, 0, 134, 255, 16, 0, 196, 220, 2, 0, 0, 0, 177, 241, 2, 0, - 85, 255, 147, 24, 17, 114, 255, 131, 0, 0, 0, 72, 193, 236, 241, 205, 99, 2, 0, 0, 0, 64, 195, - 246, 239, 169, 29, 0, 0, 0, 61, 251, 151, 22, 34, 190, 221, 14, 0, 0, 188, 223, 4, 0, 0, 28, - 251, 122, 0, 0, 241, 168, 0, 0, 0, 0, 220, 203, 0, 0, 242, 167, 0, 0, 0, 0, 221, 246, 0, 0, - 194, 222, 3, 0, 0, 27, 251, 255, 9, 0, 73, 254, 149, 21, 33, 187, 251, 255, 7, 0, 0, 79, 205, - 249, 235, 140, 156, 238, 0, 0, 0, 0, 0, 0, 0, 0, 199, 178, 0, 0, 0, 0, 0, 0, 0, 46, 254, 73, 0, - 0, 55, 138, 35, 8, 70, 223, 153, 0, 0, 0, 4, 112, 217, 247, 210, 106, 1, 0, 0, 0, 32, 255, 136, - 0, 0, 32, 255, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, - 136, 0, 0, 32, 255, 136, 0, 0, 32, 255, 136, 0, 0, 32, 255, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 136, 0, 0, 43, 255, 112, 0, 0, 102, 238, 13, 0, 0, - 165, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 136, 158, 0, 0, 0, 0, 0, 0, 0, 14, 100, 198, - 255, 237, 116, 0, 0, 0, 0, 1, 64, 161, 245, 252, 181, 85, 7, 0, 0, 0, 18, 125, 221, 255, 211, - 116, 24, 0, 0, 0, 0, 0, 0, 76, 255, 244, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 126, 222, 255, 204, - 110, 20, 0, 0, 0, 0, 0, 0, 0, 0, 1, 65, 162, 245, 250, 176, 81, 6, 0, 0, 0, 0, 0, 0, 0, 0, 14, - 101, 199, 255, 237, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 137, 158, 0, 0, 76, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 71, 178, 80, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 205, 255, 233, 141, 44, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 44, 141, 232, 255, 202, 105, 16, 0, 0, 0, 0, 0, 0, 0, 0, 4, 77, 173, 249, 247, 166, - 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 202, 255, 184, 0, 0, 0, 0, 0, 0, 2, 70, 165, 246, 247, 167, +pub static COVERAGE: [u8; 19382] = [ + 0, 0, 148, 255, 0, 0, 0, 0, 148, 255, 0, 0, 0, 0, 148, 255, 0, 0, 0, 0, 148, 255, 0, 0, + 0, 0, 148, 255, 0, 0, 0, 0, 143, 251, 0, 0, 0, 0, 130, 237, 0, 0, 0, 0, 115, 223, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 0, 0, 0, 0, 148, 255, 0, 0, + 0, 116, 224, 0, 128, 212, 0, 0, 116, 224, 0, 128, 212, 0, 0, 116, 224, 0, 128, 212, 0, 0, 116, 224, + 0, 128, 212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 224, 0, 0, 215, 105, 0, + 0, 0, 0, 0, 0, 0, 167, 155, 0, 28, 255, 37, 0, 0, 0, 0, 0, 0, 1, 233, 86, 0, 97, 226, + 0, 0, 0, 0, 0, 224, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 0, 0, 0, 121, 203, 0, 0, + 235, 84, 0, 0, 0, 0, 0, 0, 0, 177, 147, 0, 34, 255, 29, 0, 0, 0, 0, 0, 0, 0, 232, 91, + 0, 89, 231, 0, 0, 0, 0, 0, 196, 255, 255, 255, 255, 255, 255, 255, 255, 255, 72, 0, 0, 0, 0, 111, + 208, 0, 0, 229, 92, 0, 0, 0, 0, 0, 0, 0, 180, 139, 0, 43, 255, 24, 0, 0, 0, 0, 0, 0, + 5, 243, 70, 0, 110, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 104, 0, 0, 0, 0, 0, 0, 83, 199, 246, 249, 197, 86, 1, 0, 0, 74, 254, 106, 108, 113, 60, + 169, 34, 0, 0, 155, 209, 0, 96, 104, 0, 0, 0, 0, 0, 138, 241, 31, 96, 104, 0, 0, 0, 0, 0, + 24, 216, 246, 216, 174, 70, 5, 0, 0, 0, 0, 4, 78, 178, 230, 251, 220, 40, 0, 0, 0, 0, 0, 96, + 104, 32, 225, 180, 0, 0, 0, 0, 0, 96, 104, 0, 166, 202, 0, 0, 144, 115, 42, 103, 113, 83, 244, 116, + 0, 0, 26, 134, 210, 248, 245, 202, 102, 1, 0, 0, 0, 0, 0, 96, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 104, 0, 0, 0, 0, 0, 35, 190, 243, 206, 55, 0, 0, 0, 0, 174, 145, 0, 0, 0, 0, 196, + 158, 14, 125, 229, 9, 0, 0, 73, 231, 15, 0, 0, 0, 10, 254, 48, 0, 9, 254, 53, 0, 8, 220, 90, + 0, 0, 0, 0, 11, 255, 47, 0, 9, 255, 52, 0, 129, 190, 0, 0, 0, 0, 0, 0, 198, 156, 14, 123, + 230, 9, 36, 242, 41, 0, 0, 0, 0, 0, 0, 37, 192, 244, 207, 58, 0, 184, 135, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 225, 10, 28, 183, 243, 211, 63, 0, 0, 0, 0, 0, 0, 12, 227, 80, + 0, 183, 172, 15, 111, 236, 14, 0, 0, 0, 0, 0, 140, 180, 0, 3, 249, 64, 0, 2, 249, 64, 0, 0, + 0, 0, 43, 242, 33, 0, 3, 250, 63, 0, 2, 249, 64, 0, 0, 0, 0, 193, 125, 0, 0, 0, 186, 172, + 15, 111, 237, 14, 0, 0, 0, 94, 218, 7, 0, 0, 0, 31, 186, 243, 212, 65, 0, 0, 0, 0, 87, 211, + 244, 204, 88, 0, 0, 0, 0, 0, 0, 63, 254, 112, 11, 43, 168, 27, 0, 0, 0, 0, 0, 146, 227, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 248, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 254, 153, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 253, 255, 122, 0, 0, 0, 0, 0, 0, 0, 92, 255, 102, 149, + 255, 119, 0, 0, 29, 255, 89, 0, 212, 187, 0, 0, 144, 255, 116, 0, 84, 253, 25, 0, 244, 142, 0, 0, + 0, 139, 255, 113, 196, 168, 0, 0, 201, 204, 2, 0, 0, 0, 134, 255, 245, 29, 0, 0, 64, 249, 168, 40, + 10, 48, 163, 251, 255, 118, 0, 0, 0, 52, 177, 238, 251, 225, 156, 39, 125, 255, 122, 0, 116, 224, 0, 0, + 116, 224, 0, 0, 116, 224, 0, 0, 116, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 180, + 0, 0, 0, 41, 251, 49, 0, 0, 0, 157, 198, 0, 0, 0, 12, 245, 108, 0, 0, 0, 74, 255, 48, 0, + 0, 0, 126, 255, 5, 0, 0, 0, 149, 241, 0, 0, 0, 0, 150, 240, 0, 0, 0, 0, 126, 255, 5, 0, + 0, 0, 74, 255, 50, 0, 0, 0, 13, 246, 111, 0, 0, 0, 0, 157, 200, 0, 0, 0, 0, 41, 251, 50, + 0, 0, 0, 0, 146, 181, 0, 0, 118, 206, 2, 0, 0, 0, 10, 232, 98, 0, 0, 0, 0, 138, 215, 0, + 0, 0, 0, 48, 255, 62, 0, 0, 0, 2, 241, 134, 0, 0, 0, 0, 197, 188, 0, 0, 0, 0, 177, 212, + 0, 0, 0, 0, 178, 212, 0, 0, 0, 0, 197, 188, 0, 0, 0, 1, 240, 135, 0, 0, 0, 47, 255, 63, + 0, 0, 0, 137, 217, 1, 0, 0, 9, 231, 99, 0, 0, 0, 117, 207, 2, 0, 0, 0, 0, 0, 112, 116, + 0, 0, 0, 0, 0, 0, 112, 116, 0, 0, 0, 65, 177, 45, 112, 116, 44, 175, 68, 0, 41, 159, 216, 216, + 160, 42, 0, 0, 41, 158, 216, 216, 158, 42, 0, 65, 178, 46, 112, 116, 44, 176, 69, 0, 0, 0, 112, 116, + 0, 0, 0, 0, 0, 0, 112, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, + 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 32, 255, 136, 0, 0, 43, 255, + 112, 0, 0, 102, 238, 13, 0, 0, 165, 126, 0, 0, 56, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 92, 0, 0, + 72, 255, 92, 0, 0, 0, 0, 25, 254, 59, 0, 0, 0, 105, 233, 2, 0, 0, 0, 185, 155, 0, 0, 0, + 14, 249, 75, 0, 0, 0, 89, 243, 7, 0, 0, 0, 169, 171, 0, 0, 0, 6, 241, 91, 0, 0, 0, 73, + 250, 16, 0, 0, 0, 153, 187, 0, 0, 0, 1, 230, 107, 0, 0, 0, 57, 255, 28, 0, 0, 0, 137, 203, + 0, 0, 0, 0, 216, 123, 0, 0, 0, 0, 0, 0, 22, 163, 238, 244, 185, 43, 0, 0, 0, 5, 208, 199, + 32, 19, 166, 235, 25, 0, 0, 95, 255, 41, 0, 0, 12, 241, 142, 0, 0, 173, 220, 0, 0, 0, 0, 175, + 221, 0, 0, 216, 182, 0, 0, 0, 0, 136, 254, 9, 0, 234, 167, 0, 0, 0, 0, 120, 255, 25, 0, 235, + 167, 0, 0, 0, 0, 121, 255, 25, 0, 217, 182, 0, 0, 0, 0, 136, 254, 9, 0, 174, 220, 0, 0, 0, + 0, 175, 221, 0, 0, 96, 255, 41, 0, 0, 12, 241, 143, 0, 0, 5, 210, 199, 32, 19, 166, 236, 27, 0, + 0, 0, 23, 164, 239, 245, 186, 44, 0, 0, 0, 2, 67, 159, 242, 255, 32, 0, 0, 0, 0, 57, 189, 98, + 126, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, + 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, + 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, + 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, 0, 0, 0, 0, 112, 255, 32, 0, 0, 0, + 0, 4, 255, 255, 255, 255, 255, 255, 180, 0, 0, 30, 134, 211, 245, 220, 146, 19, 0, 0, 0, 192, 214, 71, + 11, 53, 212, 217, 10, 0, 0, 123, 11, 0, 0, 0, 60, 255, 94, 0, 0, 0, 0, 0, 0, 0, 26, 255, + 125, 0, 0, 0, 0, 0, 0, 0, 98, 255, 91, 0, 0, 0, 0, 0, 0, 20, 229, 226, 9, 0, 0, 0, + 0, 0, 5, 189, 250, 64, 0, 0, 0, 0, 0, 1, 166, 254, 90, 0, 0, 0, 0, 0, 0, 143, 255, 107, + 0, 0, 0, 0, 0, 0, 121, 255, 123, 0, 0, 0, 0, 0, 0, 97, 255, 140, 0, 0, 0, 0, 0, 0, + 0, 212, 255, 255, 255, 255, 255, 255, 148, 0, 0, 10, 105, 200, 243, 231, 179, 56, 0, 0, 0, 96, 138, 51, + 11, 35, 172, 250, 53, 0, 0, 0, 0, 0, 0, 0, 13, 253, 145, 0, 0, 0, 0, 0, 0, 0, 10, 252, + 135, 0, 0, 0, 0, 0, 1, 30, 163, 231, 31, 0, 0, 0, 0, 196, 255, 255, 231, 58, 0, 0, 0, 0, + 0, 0, 3, 36, 165, 246, 63, 0, 0, 0, 0, 0, 0, 0, 4, 222, 192, 0, 0, 0, 0, 0, 0, 0, + 0, 183, 224, 0, 0, 0, 0, 0, 0, 0, 7, 227, 186, 0, 0, 158, 90, 30, 9, 46, 176, 248, 59, 0, + 0, 36, 152, 221, 247, 220, 163, 41, 0, 0, 0, 0, 0, 0, 0, 175, 255, 160, 0, 0, 0, 0, 0, 0, + 86, 238, 249, 160, 0, 0, 0, 0, 0, 19, 233, 104, 244, 160, 0, 0, 0, 0, 0, 163, 202, 2, 244, 160, + 0, 0, 0, 0, 73, 253, 52, 0, 244, 160, 0, 0, 0, 13, 226, 149, 0, 0, 244, 160, 0, 0, 0, 151, + 233, 16, 0, 0, 244, 160, 0, 0, 39, 254, 93, 0, 0, 0, 244, 160, 0, 0, 56, 255, 255, 255, 255, 255, + 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 244, 160, 0, 0, 0, 0, 0, 0, 0, 0, 244, 160, 0, 0, + 0, 0, 0, 0, 0, 0, 244, 160, 0, 0, 0, 68, 255, 255, 255, 255, 255, 236, 0, 0, 0, 68, 255, 44, + 0, 0, 0, 0, 0, 0, 0, 68, 255, 44, 0, 0, 0, 0, 0, 0, 0, 68, 255, 44, 0, 0, 0, 0, + 0, 0, 0, 68, 255, 235, 251, 231, 159, 27, 0, 0, 0, 61, 131, 39, 13, 69, 219, 227, 21, 0, 0, 0, + 0, 0, 0, 0, 45, 254, 133, 0, 0, 0, 0, 0, 0, 0, 0, 225, 188, 0, 0, 0, 0, 0, 0, 0, + 0, 225, 188, 0, 0, 0, 0, 0, 0, 0, 44, 254, 134, 0, 0, 156, 92, 31, 12, 67, 217, 228, 22, 0, + 0, 36, 153, 222, 250, 227, 155, 26, 0, 0, 0, 0, 0, 74, 193, 243, 227, 138, 13, 0, 0, 0, 103, 241, + 93, 14, 18, 114, 94, 0, 0, 32, 247, 91, 0, 0, 0, 0, 0, 0, 0, 130, 240, 4, 0, 0, 0, 0, + 0, 0, 0, 190, 196, 122, 224, 249, 216, 106, 0, 0, 0, 216, 252, 218, 53, 16, 111, 255, 116, 0, 0, 218, + 255, 71, 0, 0, 0, 179, 237, 2, 0, 199, 255, 12, 0, 0, 0, 120, 255, 33, 0, 156, 255, 12, 0, 0, + 0, 121, 255, 33, 0, 75, 255, 70, 0, 0, 0, 178, 234, 2, 0, 1, 190, 217, 51, 15, 109, 254, 105, 0, + 0, 0, 14, 148, 232, 249, 211, 94, 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 203, 0, 0, 0, 0, 0, + 0, 0, 67, 255, 130, 0, 0, 0, 0, 0, 0, 0, 159, 255, 35, 0, 0, 0, 0, 0, 0, 9, 242, 194, + 0, 0, 0, 0, 0, 0, 0, 89, 255, 98, 0, 0, 0, 0, 0, 0, 0, 181, 245, 13, 0, 0, 0, 0, + 0, 0, 22, 251, 162, 0, 0, 0, 0, 0, 0, 0, 111, 255, 67, 0, 0, 0, 0, 0, 0, 0, 204, 225, + 1, 0, 0, 0, 0, 0, 0, 40, 255, 131, 0, 0, 0, 0, 0, 0, 0, 133, 255, 36, 0, 0, 0, 0, + 0, 0, 1, 224, 195, 0, 0, 0, 0, 0, 0, 0, 66, 190, 236, 242, 203, 92, 0, 0, 0, 55, 250, 158, + 24, 15, 122, 255, 94, 0, 0, 142, 253, 10, 0, 0, 0, 215, 185, 0, 0, 138, 252, 10, 0, 0, 0, 216, + 181, 0, 0, 33, 232, 158, 23, 15, 121, 246, 63, 0, 0, 0, 55, 229, 255, 255, 245, 87, 0, 0, 0, 57, + 243, 150, 26, 17, 114, 252, 95, 0, 0, 187, 220, 2, 0, 0, 0, 177, 235, 0, 0, 225, 181, 0, 0, 0, + 0, 134, 255, 16, 0, 196, 220, 2, 0, 0, 0, 177, 241, 2, 0, 85, 255, 147, 24, 17, 114, 255, 131, 0, + 0, 0, 72, 193, 236, 241, 205, 99, 2, 0, 0, 0, 64, 195, 246, 239, 169, 29, 0, 0, 0, 61, 251, 151, + 22, 34, 190, 221, 14, 0, 0, 188, 223, 4, 0, 0, 28, 251, 122, 0, 0, 241, 168, 0, 0, 0, 0, 220, + 203, 0, 0, 242, 167, 0, 0, 0, 0, 221, 246, 0, 0, 194, 222, 3, 0, 0, 27, 251, 255, 9, 0, 73, + 254, 149, 21, 33, 187, 251, 255, 7, 0, 0, 79, 205, 249, 235, 140, 156, 238, 0, 0, 0, 0, 0, 0, 0, + 0, 199, 178, 0, 0, 0, 0, 0, 0, 0, 46, 254, 73, 0, 0, 55, 138, 35, 8, 70, 223, 153, 0, 0, + 0, 4, 112, 217, 247, 210, 106, 1, 0, 0, 0, 32, 255, 136, 0, 0, 32, 255, 136, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 136, 0, 0, 32, 255, + 136, 0, 0, 32, 255, 136, 0, 0, 32, 255, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 136, 0, 0, 43, 255, 112, 0, 0, 102, 238, 13, 0, 0, + 165, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 136, 158, 0, 0, 0, 0, 0, 0, 0, 14, + 100, 198, 255, 237, 116, 0, 0, 0, 0, 1, 64, 161, 245, 252, 181, 85, 7, 0, 0, 0, 18, 125, 221, 255, + 211, 116, 24, 0, 0, 0, 0, 0, 0, 76, 255, 244, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 126, + 222, 255, 204, 110, 20, 0, 0, 0, 0, 0, 0, 0, 0, 1, 65, 162, 245, 250, 176, 81, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 14, 101, 199, 255, 237, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 137, 158, + 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 178, 80, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 205, 255, 233, 141, 44, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 44, 141, 232, 255, 202, 105, 16, 0, 0, 0, 0, 0, 0, 0, 0, 4, 77, 173, 249, 247, 166, 59, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 202, 255, 184, 0, 0, 0, 0, 0, 0, 2, 70, 165, 246, 247, 167, 60, 0, 0, 0, 0, 42, 137, 229, 255, 203, 106, 16, 0, 0, 0, 0, 42, 203, 255, 234, 142, 44, 0, 0, - 0, 0, 0, 0, 0, 71, 179, 81, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 167, 226, 243, 194, 55, 0, 0, 0, - 170, 86, 13, 26, 181, 240, 23, 0, 0, 0, 0, 0, 0, 67, 255, 81, 0, 0, 0, 0, 0, 0, 131, 255, 52, - 0, 0, 0, 0, 0, 84, 253, 146, 0, 0, 0, 0, 0, 53, 248, 163, 1, 0, 0, 0, 0, 0, 184, 216, 6, 0, 0, - 0, 0, 0, 0, 223, 155, 0, 0, 0, 0, 0, 0, 0, 228, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 240, 164, 0, 0, 0, 0, 0, 0, 0, 240, 164, 0, 0, 0, 0, 0, 0, 0, 0, 14, 119, 198, 238, 246, - 219, 149, 45, 0, 0, 0, 0, 0, 0, 0, 71, 233, 161, 65, 15, 8, 42, 127, 238, 125, 0, 0, 0, 0, 0, - 72, 237, 70, 0, 0, 0, 0, 0, 0, 31, 220, 123, 0, 0, 0, 14, 235, 77, 0, 16, 172, 247, 230, 130, - 202, 84, 40, 248, 44, 0, 0, 117, 183, 0, 0, 164, 202, 34, 31, 195, 255, 84, 0, 160, 147, 0, 0, - 193, 93, 0, 21, 253, 55, 0, 0, 44, 255, 84, 0, 96, 206, 0, 0, 228, 54, 0, 59, 253, 4, 0, 0, 0, - 246, 84, 0, 68, 222, 0, 0, 229, 51, 0, 60, 253, 4, 0, 0, 0, 246, 84, 0, 94, 195, 0, 0, 196, 90, - 0, 21, 253, 52, 0, 0, 41, 255, 84, 0, 185, 115, 0, 0, 124, 177, 0, 0, 164, 198, 33, 28, 190, - 255, 105, 144, 197, 7, 0, 0, 19, 240, 67, 0, 17, 173, 246, 221, 133, 195, 212, 124, 8, 0, 0, 0, - 0, 81, 235, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 236, 150, 57, 13, 12, 44, 107, - 211, 60, 0, 0, 0, 0, 0, 0, 0, 21, 131, 206, 242, 245, 218, 181, 98, 15, 0, 0, 0, 0, 0, 0, 0, - 156, 255, 142, 0, 0, 0, 0, 0, 0, 0, 9, 241, 247, 233, 4, 0, 0, 0, 0, 0, 0, 90, 255, 114, 255, - 76, 0, 0, 0, 0, 0, 0, 185, 218, 2, 231, 170, 0, 0, 0, 0, 0, 26, 253, 131, 0, 146, 248, 17, 0, - 0, 0, 0, 119, 255, 44, 0, 59, 255, 104, 0, 0, 0, 0, 213, 213, 0, 0, 1, 226, 198, 0, 0, 0, 53, - 255, 127, 0, 0, 0, 140, 255, 38, 0, 0, 148, 255, 255, 255, 255, 255, 255, 255, 132, 0, 6, 236, - 174, 0, 0, 0, 0, 0, 183, 225, 1, 82, 255, 83, 0, 0, 0, 0, 0, 92, 255, 65, 177, 241, 8, 0, 0, 0, - 0, 0, 12, 245, 160, 0, 108, 255, 255, 255, 253, 228, 167, 36, 0, 0, 0, 108, 255, 40, 0, 4, 45, - 205, 225, 13, 0, 0, 108, 255, 40, 0, 0, 0, 78, 255, 70, 0, 0, 108, 255, 40, 0, 0, 0, 78, 255, - 72, 0, 0, 108, 255, 40, 0, 3, 43, 202, 218, 8, 0, 0, 108, 255, 255, 255, 255, 255, 222, 46, 0, - 0, 0, 108, 255, 40, 0, 1, 30, 161, 238, 43, 0, 0, 108, 255, 40, 0, 0, 0, 8, 243, 166, 0, 0, - 108, 255, 40, 0, 0, 0, 0, 217, 207, 0, 0, 108, 255, 40, 0, 0, 0, 8, 243, 184, 0, 0, 108, 255, - 40, 0, 1, 30, 161, 254, 75, 0, 0, 108, 255, 255, 255, 255, 232, 186, 63, 0, 0, 0, 0, 0, 55, - 169, 229, 249, 226, 187, 89, 5, 0, 0, 108, 253, 147, 41, 7, 30, 123, 247, 80, 0, 57, 253, 130, - 0, 0, 0, 0, 0, 55, 66, 0, 175, 239, 8, 0, 0, 0, 0, 0, 0, 0, 0, 241, 179, 0, 0, 0, 0, 0, 0, 0, - 0, 15, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 15, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 179, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 175, 239, 8, 0, 0, 0, 0, 0, 0, 0, 0, 57, 253, 127, 0, 0, 0, 0, 0, 54, - 66, 0, 0, 109, 253, 145, 40, 6, 29, 122, 247, 80, 0, 0, 0, 56, 170, 230, 250, 227, 187, 90, 5, - 0, 108, 255, 255, 255, 245, 223, 174, 84, 1, 0, 0, 0, 108, 255, 40, 0, 8, 39, 119, 243, 185, 8, - 0, 0, 108, 255, 40, 0, 0, 0, 0, 57, 251, 145, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 172, 247, 10, - 0, 108, 255, 40, 0, 0, 0, 0, 0, 105, 255, 62, 0, 108, 255, 40, 0, 0, 0, 0, 0, 79, 255, 87, 0, - 108, 255, 40, 0, 0, 0, 0, 0, 80, 255, 86, 0, 108, 255, 40, 0, 0, 0, 0, 0, 106, 255, 61, 0, 108, - 255, 40, 0, 0, 0, 0, 0, 172, 247, 10, 0, 108, 255, 40, 0, 0, 0, 0, 56, 251, 145, 0, 0, 108, - 255, 40, 0, 7, 38, 118, 243, 185, 8, 0, 0, 108, 255, 255, 255, 246, 224, 174, 84, 1, 0, 0, 0, - 108, 255, 255, 255, 255, 255, 255, 244, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, - 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, - 255, 255, 255, 255, 255, 255, 180, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, - 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, - 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, 255, 255, 255, 255, 255, 24, 0, 108, 255, 255, 255, - 255, 255, 255, 72, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, - 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 255, 255, 255, 255, 200, 0, 0, - 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, - 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, - 0, 0, 52, 165, 226, 249, 232, 202, 129, 30, 0, 0, 0, 108, 252, 150, 46, 8, 20, 88, 217, 200, 0, - 0, 58, 253, 127, 0, 0, 0, 0, 0, 8, 124, 0, 0, 176, 238, 7, 0, 0, 0, 0, 0, 0, 0, 0, 1, 242, 178, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 150, 0, 0, 0, 12, - 255, 255, 255, 255, 24, 1, 242, 178, 0, 0, 0, 0, 0, 0, 120, 255, 24, 0, 176, 238, 7, 0, 0, 0, - 0, 0, 120, 255, 24, 0, 58, 253, 124, 0, 0, 0, 0, 0, 120, 255, 24, 0, 0, 108, 252, 148, 44, 7, - 16, 60, 192, 255, 23, 0, 0, 0, 53, 166, 227, 249, 235, 208, 151, 54, 0, 0, 108, 255, 40, 0, 0, - 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, - 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, - 28, 255, 120, 0, 0, 108, 255, 255, 255, 255, 255, 255, 255, 255, 120, 0, 0, 108, 255, 40, 0, 0, - 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, - 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, - 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 108, 255, - 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, - 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, - 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, - 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, - 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, - 114, 255, 35, 0, 0, 0, 143, 253, 11, 0, 2, 51, 232, 181, 0, 0, 211, 229, 160, 20, 0, 0, 0, 108, - 255, 40, 0, 0, 0, 50, 237, 209, 21, 0, 108, 255, 40, 0, 0, 55, 239, 203, 18, 0, 0, 108, 255, - 40, 0, 61, 242, 197, 15, 0, 0, 0, 108, 255, 40, 66, 244, 191, 12, 0, 0, 0, 0, 108, 255, 112, - 247, 184, 9, 0, 0, 0, 0, 0, 108, 255, 255, 220, 7, 0, 0, 0, 0, 0, 0, 108, 255, 180, 255, 128, - 0, 0, 0, 0, 0, 0, 108, 255, 41, 154, 255, 120, 0, 0, 0, 0, 0, 108, 255, 40, 1, 161, 255, 112, - 0, 0, 0, 0, 108, 255, 40, 0, 3, 168, 255, 104, 0, 0, 0, 108, 255, 40, 0, 0, 4, 175, 254, 97, 0, - 0, 108, 255, 40, 0, 0, 0, 6, 181, 253, 90, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, - 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, - 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, - 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, - 0, 0, 0, 0, 0, 108, 255, 255, 255, 255, 255, 255, 212, 0, 108, 255, 253, 27, 0, 0, 0, 0, 74, - 255, 255, 64, 0, 0, 108, 255, 245, 115, 0, 0, 0, 0, 164, 245, 255, 64, 0, 0, 108, 255, 167, - 206, 0, 0, 0, 10, 244, 166, 255, 64, 0, 0, 108, 255, 77, 255, 41, 0, 0, 90, 244, 87, 255, 64, - 0, 0, 108, 255, 28, 214, 131, 0, 0, 180, 165, 76, 255, 64, 0, 0, 108, 255, 28, 124, 221, 0, 20, - 250, 75, 76, 255, 64, 0, 0, 108, 255, 28, 34, 255, 57, 106, 235, 5, 76, 255, 64, 0, 0, 108, - 255, 28, 0, 199, 148, 197, 150, 0, 76, 255, 64, 0, 0, 108, 255, 28, 0, 108, 240, 254, 59, 0, - 76, 255, 64, 0, 0, 108, 255, 28, 0, 21, 251, 224, 1, 0, 76, 255, 64, 0, 0, 108, 255, 28, 0, 0, - 0, 0, 0, 0, 76, 255, 64, 0, 0, 108, 255, 28, 0, 0, 0, 0, 0, 0, 76, 255, 64, 0, 0, 108, 255, - 233, 11, 0, 0, 0, 32, 255, 104, 0, 0, 108, 255, 255, 121, 0, 0, 0, 32, 255, 104, 0, 0, 108, - 255, 186, 238, 15, 0, 0, 32, 255, 104, 0, 0, 108, 255, 60, 249, 129, 0, 0, 32, 255, 104, 0, 0, - 108, 255, 28, 150, 242, 19, 0, 32, 255, 104, 0, 0, 108, 255, 28, 26, 247, 137, 0, 32, 255, 104, - 0, 0, 108, 255, 28, 0, 142, 245, 23, 32, 255, 104, 0, 0, 108, 255, 28, 0, 21, 244, 145, 32, - 255, 104, 0, 0, 108, 255, 28, 0, 0, 134, 248, 60, 255, 104, 0, 0, 108, 255, 28, 0, 0, 17, 240, - 185, 255, 104, 0, 0, 108, 255, 28, 0, 0, 0, 126, 255, 255, 104, 0, 0, 108, 255, 28, 0, 0, 0, - 13, 236, 255, 104, 0, 0, 0, 0, 64, 180, 236, 249, 221, 144, 22, 0, 0, 0, 0, 0, 114, 253, 134, - 29, 7, 56, 199, 229, 39, 0, 0, 0, 58, 253, 128, 0, 0, 0, 0, 11, 224, 209, 2, 0, 0, 175, 240, 9, - 0, 0, 0, 0, 0, 108, 255, 75, 0, 1, 241, 180, 0, 0, 0, 0, 0, 0, 38, 255, 141, 0, 15, 255, 151, - 0, 0, 0, 0, 0, 0, 8, 254, 170, 0, 16, 255, 150, 0, 0, 0, 0, 0, 0, 8, 255, 170, 0, 1, 242, 180, - 0, 0, 0, 0, 0, 0, 38, 255, 141, 0, 0, 176, 240, 8, 0, 0, 0, 0, 0, 108, 255, 75, 0, 0, 60, 254, - 128, 0, 0, 0, 0, 11, 224, 210, 2, 0, 0, 0, 118, 253, 133, 28, 7, 55, 198, 231, 41, 0, 0, 0, 0, - 0, 67, 181, 237, 249, 222, 145, 24, 0, 0, 0, 0, 108, 255, 255, 255, 243, 209, 111, 4, 0, 0, - 108, 255, 40, 0, 16, 115, 255, 134, 0, 0, 108, 255, 40, 0, 0, 0, 191, 240, 0, 0, 108, 255, 40, - 0, 0, 0, 153, 255, 17, 0, 108, 255, 40, 0, 0, 0, 191, 241, 0, 0, 108, 255, 40, 0, 15, 112, 255, - 136, 0, 0, 108, 255, 255, 255, 244, 211, 115, 5, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, - 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, - 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 180, 236, 249, 221, 144, 22, 0, 0, 0, 0, 0, - 114, 253, 134, 29, 7, 56, 199, 229, 39, 0, 0, 0, 58, 253, 128, 0, 0, 0, 0, 11, 224, 208, 1, 0, - 0, 175, 240, 9, 0, 0, 0, 0, 0, 108, 255, 73, 0, 1, 241, 180, 0, 0, 0, 0, 0, 0, 38, 255, 140, 0, - 15, 255, 151, 0, 0, 0, 0, 0, 0, 8, 254, 169, 0, 16, 255, 150, 0, 0, 0, 0, 0, 0, 8, 255, 167, 0, - 1, 242, 180, 0, 0, 0, 0, 0, 0, 38, 255, 138, 0, 0, 176, 240, 8, 0, 0, 0, 0, 0, 108, 255, 70, 0, - 0, 60, 254, 128, 0, 0, 0, 0, 11, 224, 213, 2, 0, 0, 0, 117, 253, 133, 28, 7, 55, 198, 237, 40, - 0, 0, 0, 0, 0, 67, 181, 237, 255, 255, 207, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 251, 72, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 185, 243, 50, 0, 0, 0, 108, 255, 255, 255, 249, 218, 127, 5, 0, - 0, 0, 108, 255, 40, 0, 15, 104, 254, 135, 0, 0, 0, 108, 255, 40, 0, 0, 0, 186, 240, 0, 0, 0, - 108, 255, 40, 0, 0, 0, 152, 255, 18, 0, 0, 108, 255, 40, 0, 0, 0, 188, 246, 3, 0, 0, 108, 255, - 40, 0, 15, 106, 255, 138, 0, 0, 0, 108, 255, 255, 255, 255, 255, 151, 2, 0, 0, 0, 108, 255, 40, - 0, 22, 150, 249, 66, 0, 0, 0, 108, 255, 40, 0, 0, 1, 203, 223, 7, 0, 0, 108, 255, 40, 0, 0, 0, - 69, 255, 108, 0, 0, 108, 255, 40, 0, 0, 0, 0, 198, 227, 6, 0, 108, 255, 40, 0, 0, 0, 0, 71, - 255, 104, 0, 0, 73, 191, 233, 241, 202, 116, 17, 0, 0, 98, 255, 139, 28, 11, 83, 228, 144, 0, - 0, 212, 202, 0, 0, 0, 0, 23, 102, 0, 0, 228, 177, 0, 0, 0, 0, 0, 0, 0, 0, 172, 245, 62, 0, 0, - 0, 0, 0, 0, 0, 28, 197, 255, 216, 160, 97, 19, 0, 0, 0, 0, 0, 49, 117, 169, 242, 239, 78, 0, 0, - 0, 0, 0, 0, 0, 21, 209, 240, 13, 0, 0, 0, 0, 0, 0, 0, 110, 255, 55, 0, 134, 5, 0, 0, 0, 0, 136, - 255, 40, 0, 228, 207, 75, 11, 20, 97, 246, 186, 0, 0, 39, 139, 209, 244, 246, 211, 128, 10, 0, - 12, 255, 255, 255, 255, 255, 255, 255, 255, 255, 212, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 156, 252, 0, - 0, 0, 0, 0, 68, 255, 84, 0, 0, 156, 252, 0, 0, 0, 0, 0, 68, 255, 84, 0, 0, 156, 252, 0, 0, 0, - 0, 0, 68, 255, 84, 0, 0, 156, 252, 0, 0, 0, 0, 0, 68, 255, 84, 0, 0, 156, 252, 0, 0, 0, 0, 0, - 68, 255, 84, 0, 0, 156, 252, 0, 0, 0, 0, 0, 68, 255, 84, 0, 0, 156, 252, 0, 0, 0, 0, 0, 68, - 255, 84, 0, 0, 154, 253, 0, 0, 0, 0, 0, 70, 255, 81, 0, 0, 135, 255, 17, 0, 0, 0, 0, 90, 255, - 62, 0, 0, 75, 255, 82, 0, 0, 0, 0, 157, 244, 12, 0, 0, 2, 193, 229, 76, 15, 26, 117, 252, 120, - 0, 0, 0, 0, 10, 133, 218, 249, 244, 200, 92, 0, 0, 0, 177, 238, 7, 0, 0, 0, 0, 0, 13, 245, 160, - 82, 255, 83, 0, 0, 0, 0, 0, 97, 255, 65, 6, 236, 177, 0, 0, 0, 0, 0, 191, 225, 1, 0, 148, 250, - 20, 0, 0, 0, 30, 254, 132, 0, 0, 53, 255, 107, 0, 0, 0, 122, 255, 38, 0, 0, 0, 213, 201, 0, 0, - 0, 215, 198, 0, 0, 0, 0, 119, 255, 38, 0, 54, 255, 104, 0, 0, 0, 0, 27, 253, 132, 0, 148, 248, - 17, 0, 0, 0, 0, 0, 185, 224, 6, 235, 170, 0, 0, 0, 0, 0, 0, 90, 255, 141, 255, 76, 0, 0, 0, 0, - 0, 0, 9, 241, 254, 233, 4, 0, 0, 0, 0, 0, 0, 0, 156, 255, 143, 0, 0, 0, 0, 89, 255, 62, 0, 0, - 0, 23, 255, 233, 0, 0, 0, 0, 107, 255, 44, 26, 255, 124, 0, 0, 0, 85, 245, 255, 40, 0, 0, 0, - 169, 236, 1, 0, 221, 186, 0, 0, 0, 147, 179, 220, 102, 0, 0, 0, 230, 175, 0, 0, 158, 243, 4, 0, - 0, 209, 118, 160, 164, 0, 0, 37, 255, 113, 0, 0, 96, 255, 54, 0, 16, 253, 57, 99, 226, 0, 0, - 99, 255, 51, 0, 0, 34, 255, 116, 0, 77, 246, 6, 38, 255, 32, 0, 161, 241, 3, 0, 0, 0, 227, 178, - 0, 139, 191, 0, 0, 233, 94, 0, 223, 182, 0, 0, 0, 0, 165, 238, 1, 201, 130, 0, 0, 173, 156, 28, - 255, 120, 0, 0, 0, 0, 103, 255, 57, 251, 69, 0, 0, 112, 218, 91, 255, 57, 0, 0, 0, 0, 40, 255, - 177, 252, 12, 0, 0, 52, 255, 177, 245, 5, 0, 0, 0, 0, 0, 233, 254, 203, 0, 0, 0, 3, 243, 254, - 189, 0, 0, 0, 0, 0, 0, 171, 255, 142, 0, 0, 0, 0, 186, 255, 127, 0, 0, 0, 0, 170, 240, 26, 0, - 0, 0, 3, 202, 217, 9, 0, 22, 237, 175, 0, 0, 0, 120, 253, 57, 0, 0, 0, 94, 255, 85, 0, 42, 248, - 139, 0, 0, 0, 0, 0, 184, 232, 22, 202, 216, 8, 0, 0, 0, 0, 0, 31, 243, 226, 253, 56, 0, 0, 0, - 0, 0, 0, 0, 124, 255, 153, 0, 0, 0, 0, 0, 0, 0, 5, 207, 255, 223, 11, 0, 0, 0, 0, 0, 0, 128, - 252, 94, 248, 144, 0, 0, 0, 0, 0, 49, 250, 135, 0, 122, 253, 56, 0, 0, 0, 5, 209, 213, 7, 0, 4, - 208, 212, 6, 0, 0, 131, 252, 52, 0, 0, 0, 51, 252, 127, 0, 50, 251, 133, 0, 0, 0, 0, 0, 139, - 249, 43, 0, 180, 234, 20, 0, 0, 0, 0, 54, 252, 123, 0, 27, 240, 167, 0, 0, 0, 6, 212, 206, 4, - 0, 0, 100, 255, 78, 0, 0, 129, 251, 48, 0, 0, 0, 0, 187, 230, 16, 45, 250, 131, 0, 0, 0, 0, 0, - 32, 243, 161, 203, 212, 6, 0, 0, 0, 0, 0, 0, 108, 255, 252, 54, 0, 0, 0, 0, 0, 0, 0, 1, 237, - 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, - 0, 0, 0, 0, 24, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 74, 254, - 216, 5, 0, 0, 0, 0, 0, 0, 30, 237, 246, 45, 0, 0, 0, 0, 0, 0, 5, 200, 255, 97, 0, 0, 0, 0, 0, - 0, 0, 144, 255, 158, 0, 0, 0, 0, 0, 0, 0, 82, 255, 210, 9, 0, 0, 0, 0, 0, 0, 35, 240, 243, 39, - 0, 0, 0, 0, 0, 0, 7, 206, 255, 88, 0, 0, 0, 0, 0, 0, 0, 153, 255, 149, 0, 0, 0, 0, 0, 0, 0, 91, - 255, 204, 7, 0, 0, 0, 0, 0, 0, 33, 244, 239, 33, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 64, 0, 160, 255, 255, 176, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, - 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, - 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, - 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 255, 255, 176, 0, 216, 123, 0, 0, 0, 0, 137, 203, - 0, 0, 0, 0, 57, 255, 28, 0, 0, 0, 1, 230, 107, 0, 0, 0, 0, 153, 187, 0, 0, 0, 0, 73, 250, 16, - 0, 0, 0, 6, 241, 91, 0, 0, 0, 0, 169, 171, 0, 0, 0, 0, 89, 243, 7, 0, 0, 0, 14, 249, 75, 0, 0, - 0, 0, 185, 155, 0, 0, 0, 0, 105, 233, 2, 0, 0, 0, 26, 254, 59, 0, 112, 255, 255, 224, 0, 0, 0, - 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, - 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, - 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 112, 255, 255, 224, 0, 0, - 0, 0, 0, 1, 146, 255, 223, 35, 0, 0, 0, 0, 0, 0, 0, 3, 160, 245, 101, 200, 230, 43, 0, 0, 0, 0, - 0, 6, 173, 226, 49, 0, 8, 164, 236, 52, 0, 0, 0, 10, 185, 199, 23, 0, 0, 0, 0, 121, 239, 62, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 40, 255, 255, 255, 255, 255, 255, 255, 255, 40, 0, 68, 248, 64, 0, 0, 0, 0, - 0, 0, 102, 228, 20, 0, 0, 0, 0, 0, 0, 141, 180, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 100, 255, 255, 253, 225, 162, 29, 0, 0, 0, 0, 0, 0, 4, 43, 187, 209, 4, 0, 0, 0, 0, - 0, 0, 0, 38, 255, 53, 0, 0, 7, 121, 213, 243, 255, 255, 255, 87, 0, 0, 147, 236, 74, 15, 1, 27, - 255, 92, 0, 0, 235, 129, 0, 0, 0, 57, 255, 92, 0, 1, 245, 122, 0, 0, 0, 146, 255, 92, 0, 0, - 173, 225, 48, 16, 106, 171, 255, 92, 0, 0, 21, 168, 239, 237, 150, 27, 255, 92, 0, 0, 140, 232, - 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, - 140, 232, 26, 183, 245, 225, 113, 0, 0, 0, 140, 233, 173, 60, 16, 101, 251, 101, 0, 0, 140, - 255, 79, 0, 0, 0, 156, 229, 4, 0, 140, 254, 6, 0, 0, 0, 81, 255, 36, 0, 140, 239, 0, 0, 0, 0, - 59, 255, 63, 0, 140, 254, 6, 0, 0, 0, 81, 255, 36, 0, 140, 255, 78, 0, 0, 0, 155, 230, 4, 0, - 140, 234, 172, 59, 15, 99, 250, 103, 0, 0, 140, 232, 28, 185, 246, 227, 115, 0, 0, 0, 0, 45, - 174, 236, 242, 186, 51, 0, 0, 50, 245, 174, 41, 13, 68, 156, 0, 0, 189, 218, 4, 0, 0, 0, 0, 0, - 7, 251, 136, 0, 0, 0, 0, 0, 0, 23, 255, 112, 0, 0, 0, 0, 0, 0, 7, 251, 136, 0, 0, 0, 0, 0, 0, - 0, 189, 218, 4, 0, 0, 0, 0, 0, 0, 50, 245, 174, 40, 13, 67, 155, 0, 0, 0, 47, 178, 240, 240, - 184, 50, 0, 0, 0, 0, 0, 0, 0, 0, 188, 180, 0, 0, 0, 0, 0, 0, 0, 0, 188, 180, 0, 0, 0, 0, 0, 0, - 0, 0, 188, 180, 0, 0, 0, 84, 215, 247, 200, 49, 188, 180, 0, 0, 63, 254, 131, 20, 43, 180, 203, - 180, 0, 0, 192, 197, 0, 0, 0, 37, 254, 180, 0, 1, 248, 124, 0, 0, 0, 0, 217, 180, 0, 19, 255, - 102, 0, 0, 0, 0, 195, 180, 0, 1, 248, 123, 0, 0, 0, 0, 217, 180, 0, 0, 193, 195, 0, 0, 0, 37, - 253, 180, 0, 0, 65, 254, 129, 20, 41, 178, 203, 180, 0, 0, 0, 86, 216, 248, 203, 51, 188, 180, - 0, 0, 0, 45, 177, 239, 247, 198, 62, 0, 0, 0, 47, 244, 166, 34, 18, 126, 249, 51, 0, 0, 185, - 214, 4, 0, 0, 0, 193, 176, 0, 6, 250, 138, 0, 0, 0, 0, 130, 236, 0, 23, 255, 255, 255, 255, - 255, 255, 255, 254, 0, 8, 252, 126, 0, 0, 0, 0, 0, 0, 0, 0, 188, 205, 3, 0, 0, 0, 0, 0, 0, 0, - 47, 243, 168, 41, 9, 39, 112, 141, 0, 0, 0, 40, 168, 234, 247, 215, 140, 26, 0, 0, 0, 95, 218, - 250, 240, 0, 23, 251, 113, 4, 0, 0, 60, 255, 49, 0, 0, 160, 255, 255, 255, 255, 144, 0, 64, - 255, 48, 0, 0, 0, 64, 255, 48, 0, 0, 0, 64, 255, 48, 0, 0, 0, 64, 255, 48, 0, 0, 0, 64, 255, - 48, 0, 0, 0, 64, 255, 48, 0, 0, 0, 64, 255, 48, 0, 0, 0, 64, 255, 48, 0, 0, 0, 0, 86, 215, 247, - 200, 49, 188, 180, 0, 0, 65, 254, 128, 20, 41, 176, 203, 180, 0, 0, 193, 195, 0, 0, 0, 34, 253, - 180, 0, 1, 248, 123, 0, 0, 0, 0, 215, 180, 0, 19, 255, 102, 0, 0, 0, 0, 195, 180, 0, 1, 248, - 123, 0, 0, 0, 0, 215, 180, 0, 0, 193, 193, 0, 0, 0, 33, 253, 180, 0, 0, 65, 254, 126, 19, 40, - 174, 203, 180, 0, 0, 0, 87, 216, 248, 201, 49, 197, 172, 0, 0, 0, 0, 0, 0, 0, 11, 238, 134, 0, - 0, 15, 158, 46, 9, 42, 176, 245, 35, 0, 0, 0, 95, 204, 245, 240, 184, 55, 0, 0, 0, 140, 232, 0, - 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, - 232, 20, 175, 244, 228, 110, 0, 0, 0, 140, 233, 164, 61, 15, 118, 255, 57, 0, 0, 140, 255, 73, - 0, 0, 0, 222, 153, 0, 0, 140, 252, 4, 0, 0, 0, 181, 185, 0, 0, 140, 233, 0, 0, 0, 0, 168, 200, - 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, - 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, - 0, 0, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, - 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, - 0, 0, 0, 0, 0, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, - 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, - 0, 0, 133, 229, 0, 0, 17, 198, 178, 0, 76, 246, 194, 37, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, - 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 94, - 250, 124, 0, 0, 140, 232, 0, 0, 118, 251, 99, 0, 0, 0, 140, 232, 1, 143, 246, 78, 0, 0, 0, 0, - 140, 235, 167, 238, 60, 0, 0, 0, 0, 0, 140, 247, 244, 182, 4, 0, 0, 0, 0, 0, 140, 232, 62, 243, - 172, 4, 0, 0, 0, 0, 140, 232, 0, 60, 243, 173, 5, 0, 0, 0, 140, 232, 0, 0, 58, 242, 175, 5, 0, - 0, 140, 232, 0, 0, 0, 56, 241, 176, 6, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, - 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, - 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 140, 232, 23, 180, 244, 213, 61, 0, 75, 212, 247, - 190, 27, 0, 0, 0, 140, 233, 163, 54, 17, 153, 238, 67, 157, 27, 36, 218, 175, 0, 0, 0, 140, - 255, 68, 0, 0, 20, 254, 219, 6, 0, 0, 106, 253, 15, 0, 0, 140, 252, 3, 0, 0, 0, 236, 168, 0, 0, - 0, 68, 255, 45, 0, 0, 140, 233, 0, 0, 0, 0, 224, 145, 0, 0, 0, 56, 255, 60, 0, 0, 140, 232, 0, - 0, 0, 0, 224, 144, 0, 0, 0, 56, 255, 60, 0, 0, 140, 232, 0, 0, 0, 0, 224, 144, 0, 0, 0, 56, - 255, 60, 0, 0, 140, 232, 0, 0, 0, 0, 224, 144, 0, 0, 0, 56, 255, 60, 0, 0, 140, 232, 0, 0, 0, - 0, 224, 144, 0, 0, 0, 56, 255, 60, 0, 0, 140, 232, 20, 175, 244, 228, 110, 0, 0, 0, 140, 233, - 164, 61, 15, 118, 255, 57, 0, 0, 140, 255, 73, 0, 0, 0, 222, 153, 0, 0, 140, 252, 4, 0, 0, 0, - 181, 185, 0, 0, 140, 233, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, - 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, - 0, 168, 200, 0, 0, 0, 67, 196, 246, 241, 178, 40, 0, 0, 0, 67, 252, 145, 24, 36, 187, 237, 30, - 0, 0, 199, 209, 0, 0, 0, 19, 245, 148, 0, 8, 253, 133, 0, 0, 0, 0, 186, 212, 0, 24, 255, 111, - 0, 0, 0, 0, 163, 231, 0, 8, 253, 132, 0, 0, 0, 0, 185, 212, 0, 0, 199, 207, 0, 0, 0, 18, 244, - 149, 0, 0, 69, 252, 143, 23, 34, 185, 239, 31, 0, 0, 0, 69, 197, 247, 242, 179, 42, 0, 0, 0, - 140, 232, 26, 183, 245, 225, 113, 0, 0, 0, 140, 233, 173, 60, 16, 101, 251, 101, 0, 0, 140, - 255, 79, 0, 0, 0, 156, 229, 4, 0, 140, 254, 6, 0, 0, 0, 81, 255, 36, 0, 140, 239, 0, 0, 0, 0, - 59, 255, 63, 0, 140, 254, 6, 0, 0, 0, 81, 255, 36, 0, 140, 255, 78, 0, 0, 0, 155, 230, 4, 0, - 140, 234, 172, 59, 15, 99, 250, 103, 0, 0, 140, 232, 28, 185, 246, 227, 115, 0, 0, 0, 140, 232, - 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 215, 247, 200, 49, 188, 180, 0, 0, 63, 254, 131, 20, 43, 180, 203, 180, 0, 0, 192, 197, 0, + 0, 0, 0, 0, 0, 71, 179, 81, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 167, 226, 243, 194, 55, + 0, 0, 0, 170, 86, 13, 26, 181, 240, 23, 0, 0, 0, 0, 0, 0, 67, 255, 81, 0, 0, 0, 0, 0, + 0, 131, 255, 52, 0, 0, 0, 0, 0, 84, 253, 146, 0, 0, 0, 0, 0, 53, 248, 163, 1, 0, 0, 0, + 0, 0, 184, 216, 6, 0, 0, 0, 0, 0, 0, 223, 155, 0, 0, 0, 0, 0, 0, 0, 228, 152, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 164, 0, 0, 0, 0, 0, 0, 0, 240, + 164, 0, 0, 0, 0, 0, 0, 0, 0, 14, 119, 198, 238, 246, 219, 149, 45, 0, 0, 0, 0, 0, 0, 0, + 71, 233, 161, 65, 15, 8, 42, 127, 238, 125, 0, 0, 0, 0, 0, 72, 237, 70, 0, 0, 0, 0, 0, 0, + 31, 220, 123, 0, 0, 0, 14, 235, 77, 0, 16, 172, 247, 230, 130, 202, 84, 40, 248, 44, 0, 0, 117, 183, + 0, 0, 164, 202, 34, 31, 195, 255, 84, 0, 160, 147, 0, 0, 193, 93, 0, 21, 253, 55, 0, 0, 44, 255, + 84, 0, 96, 206, 0, 0, 228, 54, 0, 59, 253, 4, 0, 0, 0, 246, 84, 0, 68, 222, 0, 0, 229, 51, + 0, 60, 253, 4, 0, 0, 0, 246, 84, 0, 94, 195, 0, 0, 196, 90, 0, 21, 253, 52, 0, 0, 41, 255, + 84, 0, 185, 115, 0, 0, 124, 177, 0, 0, 164, 198, 33, 28, 190, 255, 105, 144, 197, 7, 0, 0, 19, 240, + 67, 0, 17, 173, 246, 221, 133, 195, 212, 124, 8, 0, 0, 0, 0, 81, 235, 57, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 236, 150, 57, 13, 12, 44, 107, 211, 60, 0, 0, 0, 0, 0, 0, + 0, 21, 131, 206, 242, 245, 218, 181, 98, 15, 0, 0, 0, 0, 0, 0, 0, 156, 255, 142, 0, 0, 0, 0, + 0, 0, 0, 9, 241, 247, 233, 4, 0, 0, 0, 0, 0, 0, 90, 255, 114, 255, 76, 0, 0, 0, 0, 0, + 0, 185, 218, 2, 231, 170, 0, 0, 0, 0, 0, 26, 253, 131, 0, 146, 248, 17, 0, 0, 0, 0, 119, 255, + 44, 0, 59, 255, 104, 0, 0, 0, 0, 213, 213, 0, 0, 1, 226, 198, 0, 0, 0, 53, 255, 127, 0, 0, + 0, 140, 255, 38, 0, 0, 148, 255, 255, 255, 255, 255, 255, 255, 132, 0, 6, 236, 174, 0, 0, 0, 0, 0, + 183, 225, 1, 82, 255, 83, 0, 0, 0, 0, 0, 92, 255, 65, 177, 241, 8, 0, 0, 0, 0, 0, 12, 245, + 160, 0, 108, 255, 255, 255, 253, 228, 167, 36, 0, 0, 0, 108, 255, 40, 0, 4, 45, 205, 225, 13, 0, 0, + 108, 255, 40, 0, 0, 0, 78, 255, 70, 0, 0, 108, 255, 40, 0, 0, 0, 78, 255, 72, 0, 0, 108, 255, + 40, 0, 3, 43, 202, 218, 8, 0, 0, 108, 255, 255, 255, 255, 255, 222, 46, 0, 0, 0, 108, 255, 40, 0, + 1, 30, 161, 238, 43, 0, 0, 108, 255, 40, 0, 0, 0, 8, 243, 166, 0, 0, 108, 255, 40, 0, 0, 0, + 0, 217, 207, 0, 0, 108, 255, 40, 0, 0, 0, 8, 243, 184, 0, 0, 108, 255, 40, 0, 1, 30, 161, 254, + 75, 0, 0, 108, 255, 255, 255, 255, 232, 186, 63, 0, 0, 0, 0, 0, 55, 169, 229, 249, 226, 187, 89, 5, + 0, 0, 108, 253, 147, 41, 7, 30, 123, 247, 80, 0, 57, 253, 130, 0, 0, 0, 0, 0, 55, 66, 0, 175, + 239, 8, 0, 0, 0, 0, 0, 0, 0, 0, 241, 179, 0, 0, 0, 0, 0, 0, 0, 0, 15, 255, 151, 0, + 0, 0, 0, 0, 0, 0, 0, 15, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 179, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 175, 239, 8, 0, 0, 0, 0, 0, 0, 0, 0, 57, 253, 127, 0, 0, 0, 0, + 0, 54, 66, 0, 0, 109, 253, 145, 40, 6, 29, 122, 247, 80, 0, 0, 0, 56, 170, 230, 250, 227, 187, 90, + 5, 0, 108, 255, 255, 255, 245, 223, 174, 84, 1, 0, 0, 0, 108, 255, 40, 0, 8, 39, 119, 243, 185, 8, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 57, 251, 145, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 172, 247, + 10, 0, 108, 255, 40, 0, 0, 0, 0, 0, 105, 255, 62, 0, 108, 255, 40, 0, 0, 0, 0, 0, 79, 255, + 87, 0, 108, 255, 40, 0, 0, 0, 0, 0, 80, 255, 86, 0, 108, 255, 40, 0, 0, 0, 0, 0, 106, 255, + 61, 0, 108, 255, 40, 0, 0, 0, 0, 0, 172, 247, 10, 0, 108, 255, 40, 0, 0, 0, 0, 56, 251, 145, + 0, 0, 108, 255, 40, 0, 7, 38, 118, 243, 185, 8, 0, 0, 108, 255, 255, 255, 246, 224, 174, 84, 1, 0, + 0, 0, 108, 255, 255, 255, 255, 255, 255, 244, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, + 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, + 0, 0, 0, 0, 108, 255, 255, 255, 255, 255, 255, 180, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, + 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, 255, 255, 255, 255, 255, + 24, 0, 108, 255, 255, 255, 255, 255, 255, 72, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, + 255, 255, 255, 255, 255, 200, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 52, 165, 226, 249, 232, 202, 129, 30, + 0, 0, 0, 108, 252, 150, 46, 8, 20, 88, 217, 200, 0, 0, 58, 253, 127, 0, 0, 0, 0, 0, 8, 124, + 0, 0, 176, 238, 7, 0, 0, 0, 0, 0, 0, 0, 0, 1, 242, 178, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 15, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 150, 0, 0, 0, 12, 255, 255, 255, 255, + 24, 1, 242, 178, 0, 0, 0, 0, 0, 0, 120, 255, 24, 0, 176, 238, 7, 0, 0, 0, 0, 0, 120, 255, + 24, 0, 58, 253, 124, 0, 0, 0, 0, 0, 120, 255, 24, 0, 0, 108, 252, 148, 44, 7, 16, 60, 192, 255, + 23, 0, 0, 0, 53, 166, 227, 249, 235, 208, 151, 54, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, + 0, 0, 108, 255, 255, 255, 255, 255, 255, 255, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, + 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, + 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, 0, 0, 108, 255, 40, + 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, + 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, + 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 108, 255, 40, 0, 0, 0, 114, 255, 35, + 0, 0, 0, 143, 253, 11, 0, 2, 51, 232, 181, 0, 0, 211, 229, 160, 20, 0, 0, 0, 108, 255, 40, 0, + 0, 0, 50, 237, 209, 21, 0, 108, 255, 40, 0, 0, 55, 239, 203, 18, 0, 0, 108, 255, 40, 0, 61, 242, + 197, 15, 0, 0, 0, 108, 255, 40, 66, 244, 191, 12, 0, 0, 0, 0, 108, 255, 112, 247, 184, 9, 0, 0, + 0, 0, 0, 108, 255, 255, 220, 7, 0, 0, 0, 0, 0, 0, 108, 255, 180, 255, 128, 0, 0, 0, 0, 0, + 0, 108, 255, 41, 154, 255, 120, 0, 0, 0, 0, 0, 108, 255, 40, 1, 161, 255, 112, 0, 0, 0, 0, 108, + 255, 40, 0, 3, 168, 255, 104, 0, 0, 0, 108, 255, 40, 0, 0, 4, 175, 254, 97, 0, 0, 108, 255, 40, + 0, 0, 0, 6, 181, 253, 90, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, + 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 255, 255, 255, 255, 255, 212, 0, 108, 255, 253, 27, + 0, 0, 0, 0, 74, 255, 255, 64, 0, 0, 108, 255, 245, 115, 0, 0, 0, 0, 164, 245, 255, 64, 0, 0, + 108, 255, 167, 206, 0, 0, 0, 10, 244, 166, 255, 64, 0, 0, 108, 255, 77, 255, 41, 0, 0, 90, 244, 87, + 255, 64, 0, 0, 108, 255, 28, 214, 131, 0, 0, 180, 165, 76, 255, 64, 0, 0, 108, 255, 28, 124, 221, 0, + 20, 250, 75, 76, 255, 64, 0, 0, 108, 255, 28, 34, 255, 57, 106, 235, 5, 76, 255, 64, 0, 0, 108, 255, + 28, 0, 199, 148, 197, 150, 0, 76, 255, 64, 0, 0, 108, 255, 28, 0, 108, 240, 254, 59, 0, 76, 255, 64, + 0, 0, 108, 255, 28, 0, 21, 251, 224, 1, 0, 76, 255, 64, 0, 0, 108, 255, 28, 0, 0, 0, 0, 0, + 0, 76, 255, 64, 0, 0, 108, 255, 28, 0, 0, 0, 0, 0, 0, 76, 255, 64, 0, 0, 108, 255, 233, 11, + 0, 0, 0, 32, 255, 104, 0, 0, 108, 255, 255, 121, 0, 0, 0, 32, 255, 104, 0, 0, 108, 255, 186, 238, + 15, 0, 0, 32, 255, 104, 0, 0, 108, 255, 60, 249, 129, 0, 0, 32, 255, 104, 0, 0, 108, 255, 28, 150, + 242, 19, 0, 32, 255, 104, 0, 0, 108, 255, 28, 26, 247, 137, 0, 32, 255, 104, 0, 0, 108, 255, 28, 0, + 142, 245, 23, 32, 255, 104, 0, 0, 108, 255, 28, 0, 21, 244, 145, 32, 255, 104, 0, 0, 108, 255, 28, 0, + 0, 134, 248, 60, 255, 104, 0, 0, 108, 255, 28, 0, 0, 17, 240, 185, 255, 104, 0, 0, 108, 255, 28, 0, + 0, 0, 126, 255, 255, 104, 0, 0, 108, 255, 28, 0, 0, 0, 13, 236, 255, 104, 0, 0, 0, 0, 64, 180, + 236, 249, 221, 144, 22, 0, 0, 0, 0, 0, 114, 253, 134, 29, 7, 56, 199, 229, 39, 0, 0, 0, 58, 253, + 128, 0, 0, 0, 0, 11, 224, 209, 2, 0, 0, 175, 240, 9, 0, 0, 0, 0, 0, 108, 255, 75, 0, 1, + 241, 180, 0, 0, 0, 0, 0, 0, 38, 255, 141, 0, 15, 255, 151, 0, 0, 0, 0, 0, 0, 8, 254, 170, + 0, 16, 255, 150, 0, 0, 0, 0, 0, 0, 8, 255, 170, 0, 1, 242, 180, 0, 0, 0, 0, 0, 0, 38, + 255, 141, 0, 0, 176, 240, 8, 0, 0, 0, 0, 0, 108, 255, 75, 0, 0, 60, 254, 128, 0, 0, 0, 0, + 11, 224, 210, 2, 0, 0, 0, 118, 253, 133, 28, 7, 55, 198, 231, 41, 0, 0, 0, 0, 0, 67, 181, 237, + 249, 222, 145, 24, 0, 0, 0, 0, 108, 255, 255, 255, 243, 209, 111, 4, 0, 0, 108, 255, 40, 0, 16, 115, + 255, 134, 0, 0, 108, 255, 40, 0, 0, 0, 191, 240, 0, 0, 108, 255, 40, 0, 0, 0, 153, 255, 17, 0, + 108, 255, 40, 0, 0, 0, 191, 241, 0, 0, 108, 255, 40, 0, 15, 112, 255, 136, 0, 0, 108, 255, 255, 255, + 244, 211, 115, 5, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 108, 255, + 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 180, 236, 249, 221, 144, 22, 0, 0, 0, 0, 0, 114, 253, + 134, 29, 7, 56, 199, 229, 39, 0, 0, 0, 58, 253, 128, 0, 0, 0, 0, 11, 224, 208, 1, 0, 0, 175, + 240, 9, 0, 0, 0, 0, 0, 108, 255, 73, 0, 1, 241, 180, 0, 0, 0, 0, 0, 0, 38, 255, 140, 0, + 15, 255, 151, 0, 0, 0, 0, 0, 0, 8, 254, 169, 0, 16, 255, 150, 0, 0, 0, 0, 0, 0, 8, 255, + 167, 0, 1, 242, 180, 0, 0, 0, 0, 0, 0, 38, 255, 138, 0, 0, 176, 240, 8, 0, 0, 0, 0, 0, + 108, 255, 70, 0, 0, 60, 254, 128, 0, 0, 0, 0, 11, 224, 213, 2, 0, 0, 0, 117, 253, 133, 28, 7, + 55, 198, 237, 40, 0, 0, 0, 0, 0, 67, 181, 237, 255, 255, 207, 23, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 154, 251, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 185, 243, 50, 0, 0, 0, 108, 255, + 255, 255, 249, 218, 127, 5, 0, 0, 0, 108, 255, 40, 0, 15, 104, 254, 135, 0, 0, 0, 108, 255, 40, 0, + 0, 0, 186, 240, 0, 0, 0, 108, 255, 40, 0, 0, 0, 152, 255, 18, 0, 0, 108, 255, 40, 0, 0, 0, + 188, 246, 3, 0, 0, 108, 255, 40, 0, 15, 106, 255, 138, 0, 0, 0, 108, 255, 255, 255, 255, 255, 151, 2, + 0, 0, 0, 108, 255, 40, 0, 22, 150, 249, 66, 0, 0, 0, 108, 255, 40, 0, 0, 1, 203, 223, 7, 0, + 0, 108, 255, 40, 0, 0, 0, 69, 255, 108, 0, 0, 108, 255, 40, 0, 0, 0, 0, 198, 227, 6, 0, 108, + 255, 40, 0, 0, 0, 0, 71, 255, 104, 0, 0, 73, 191, 233, 241, 202, 116, 17, 0, 0, 98, 255, 139, 28, + 11, 83, 228, 144, 0, 0, 212, 202, 0, 0, 0, 0, 23, 102, 0, 0, 228, 177, 0, 0, 0, 0, 0, 0, + 0, 0, 172, 245, 62, 0, 0, 0, 0, 0, 0, 0, 28, 197, 255, 216, 160, 97, 19, 0, 0, 0, 0, 0, + 49, 117, 169, 242, 239, 78, 0, 0, 0, 0, 0, 0, 0, 21, 209, 240, 13, 0, 0, 0, 0, 0, 0, 0, + 110, 255, 55, 0, 134, 5, 0, 0, 0, 0, 136, 255, 40, 0, 228, 207, 75, 11, 20, 97, 246, 186, 0, 0, + 39, 139, 209, 244, 246, 211, 128, 10, 0, 12, 255, 255, 255, 255, 255, 255, 255, 255, 255, 212, 0, 0, 0, 0, + 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, + 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 156, 252, + 0, 0, 0, 0, 0, 68, 255, 84, 0, 0, 156, 252, 0, 0, 0, 0, 0, 68, 255, 84, 0, 0, 156, 252, + 0, 0, 0, 0, 0, 68, 255, 84, 0, 0, 156, 252, 0, 0, 0, 0, 0, 68, 255, 84, 0, 0, 156, 252, + 0, 0, 0, 0, 0, 68, 255, 84, 0, 0, 156, 252, 0, 0, 0, 0, 0, 68, 255, 84, 0, 0, 156, 252, + 0, 0, 0, 0, 0, 68, 255, 84, 0, 0, 154, 253, 0, 0, 0, 0, 0, 70, 255, 81, 0, 0, 135, 255, + 17, 0, 0, 0, 0, 90, 255, 62, 0, 0, 75, 255, 82, 0, 0, 0, 0, 157, 244, 12, 0, 0, 2, 193, + 229, 76, 15, 26, 117, 252, 120, 0, 0, 0, 0, 10, 133, 218, 249, 244, 200, 92, 0, 0, 0, 177, 238, 7, + 0, 0, 0, 0, 0, 13, 245, 160, 82, 255, 83, 0, 0, 0, 0, 0, 97, 255, 65, 6, 236, 177, 0, 0, + 0, 0, 0, 191, 225, 1, 0, 148, 250, 20, 0, 0, 0, 30, 254, 132, 0, 0, 53, 255, 107, 0, 0, 0, + 122, 255, 38, 0, 0, 0, 213, 201, 0, 0, 0, 215, 198, 0, 0, 0, 0, 119, 255, 38, 0, 54, 255, 104, + 0, 0, 0, 0, 27, 253, 132, 0, 148, 248, 17, 0, 0, 0, 0, 0, 185, 224, 6, 235, 170, 0, 0, 0, + 0, 0, 0, 90, 255, 141, 255, 76, 0, 0, 0, 0, 0, 0, 9, 241, 254, 233, 4, 0, 0, 0, 0, 0, + 0, 0, 156, 255, 143, 0, 0, 0, 0, 89, 255, 62, 0, 0, 0, 23, 255, 233, 0, 0, 0, 0, 107, 255, + 44, 26, 255, 124, 0, 0, 0, 85, 245, 255, 40, 0, 0, 0, 169, 236, 1, 0, 221, 186, 0, 0, 0, 147, + 179, 220, 102, 0, 0, 0, 230, 175, 0, 0, 158, 243, 4, 0, 0, 209, 118, 160, 164, 0, 0, 37, 255, 113, + 0, 0, 96, 255, 54, 0, 16, 253, 57, 99, 226, 0, 0, 99, 255, 51, 0, 0, 34, 255, 116, 0, 77, 246, + 6, 38, 255, 32, 0, 161, 241, 3, 0, 0, 0, 227, 178, 0, 139, 191, 0, 0, 233, 94, 0, 223, 182, 0, + 0, 0, 0, 165, 238, 1, 201, 130, 0, 0, 173, 156, 28, 255, 120, 0, 0, 0, 0, 103, 255, 57, 251, 69, + 0, 0, 112, 218, 91, 255, 57, 0, 0, 0, 0, 40, 255, 177, 252, 12, 0, 0, 52, 255, 177, 245, 5, 0, + 0, 0, 0, 0, 233, 254, 203, 0, 0, 0, 3, 243, 254, 189, 0, 0, 0, 0, 0, 0, 171, 255, 142, 0, + 0, 0, 0, 186, 255, 127, 0, 0, 0, 0, 170, 240, 26, 0, 0, 0, 3, 202, 217, 9, 0, 22, 237, 175, + 0, 0, 0, 120, 253, 57, 0, 0, 0, 94, 255, 85, 0, 42, 248, 139, 0, 0, 0, 0, 0, 184, 232, 22, + 202, 216, 8, 0, 0, 0, 0, 0, 31, 243, 226, 253, 56, 0, 0, 0, 0, 0, 0, 0, 124, 255, 153, 0, + 0, 0, 0, 0, 0, 0, 5, 207, 255, 223, 11, 0, 0, 0, 0, 0, 0, 128, 252, 94, 248, 144, 0, 0, + 0, 0, 0, 49, 250, 135, 0, 122, 253, 56, 0, 0, 0, 5, 209, 213, 7, 0, 4, 208, 212, 6, 0, 0, + 131, 252, 52, 0, 0, 0, 51, 252, 127, 0, 50, 251, 133, 0, 0, 0, 0, 0, 139, 249, 43, 0, 180, 234, + 20, 0, 0, 0, 0, 54, 252, 123, 0, 27, 240, 167, 0, 0, 0, 6, 212, 206, 4, 0, 0, 100, 255, 78, + 0, 0, 129, 251, 48, 0, 0, 0, 0, 187, 230, 16, 45, 250, 131, 0, 0, 0, 0, 0, 32, 243, 161, 203, + 212, 6, 0, 0, 0, 0, 0, 0, 108, 255, 252, 54, 0, 0, 0, 0, 0, 0, 0, 1, 237, 182, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 232, 176, 0, 0, 0, 0, 24, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, + 0, 0, 0, 74, 254, 216, 5, 0, 0, 0, 0, 0, 0, 30, 237, 246, 45, 0, 0, 0, 0, 0, 0, 5, + 200, 255, 97, 0, 0, 0, 0, 0, 0, 0, 144, 255, 158, 0, 0, 0, 0, 0, 0, 0, 82, 255, 210, 9, + 0, 0, 0, 0, 0, 0, 35, 240, 243, 39, 0, 0, 0, 0, 0, 0, 7, 206, 255, 88, 0, 0, 0, 0, + 0, 0, 0, 153, 255, 149, 0, 0, 0, 0, 0, 0, 0, 91, 255, 204, 7, 0, 0, 0, 0, 0, 0, 33, + 244, 239, 33, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 0, 160, 255, + 255, 176, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, + 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, + 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, 0, 0, 0, 0, 160, 208, + 0, 0, 0, 0, 160, 255, 255, 176, 0, 216, 123, 0, 0, 0, 0, 137, 203, 0, 0, 0, 0, 57, 255, 28, + 0, 0, 0, 1, 230, 107, 0, 0, 0, 0, 153, 187, 0, 0, 0, 0, 73, 250, 16, 0, 0, 0, 6, 241, + 91, 0, 0, 0, 0, 169, 171, 0, 0, 0, 0, 89, 243, 7, 0, 0, 0, 14, 249, 75, 0, 0, 0, 0, + 185, 155, 0, 0, 0, 0, 105, 233, 2, 0, 0, 0, 26, 254, 59, 0, 112, 255, 255, 224, 0, 0, 0, 0, + 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, + 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, + 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 0, 0, 148, 224, 0, 0, 112, 255, + 255, 224, 0, 0, 0, 0, 0, 1, 146, 255, 223, 35, 0, 0, 0, 0, 0, 0, 0, 3, 160, 245, 101, 200, + 230, 43, 0, 0, 0, 0, 0, 6, 173, 226, 49, 0, 8, 164, 236, 52, 0, 0, 0, 10, 185, 199, 23, 0, + 0, 0, 0, 121, 239, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, + 255, 255, 255, 255, 255, 255, 40, 0, 68, 248, 64, 0, 0, 0, 0, 0, 0, 102, 228, 20, 0, 0, 0, 0, + 0, 0, 141, 180, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 255, 253, 225, 162, 29, 0, + 0, 0, 0, 0, 0, 4, 43, 187, 209, 4, 0, 0, 0, 0, 0, 0, 0, 38, 255, 53, 0, 0, 7, 121, + 213, 243, 255, 255, 255, 87, 0, 0, 147, 236, 74, 15, 1, 27, 255, 92, 0, 0, 235, 129, 0, 0, 0, 57, + 255, 92, 0, 1, 245, 122, 0, 0, 0, 146, 255, 92, 0, 0, 173, 225, 48, 16, 106, 171, 255, 92, 0, 0, + 21, 168, 239, 237, 150, 27, 255, 92, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, + 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 26, 183, 245, 225, 113, 0, + 0, 0, 140, 233, 173, 60, 16, 101, 251, 101, 0, 0, 140, 255, 79, 0, 0, 0, 156, 229, 4, 0, 140, 254, + 6, 0, 0, 0, 81, 255, 36, 0, 140, 239, 0, 0, 0, 0, 59, 255, 63, 0, 140, 254, 6, 0, 0, 0, + 81, 255, 36, 0, 140, 255, 78, 0, 0, 0, 155, 230, 4, 0, 140, 234, 172, 59, 15, 99, 250, 103, 0, 0, + 140, 232, 28, 185, 246, 227, 115, 0, 0, 0, 0, 45, 174, 236, 242, 186, 51, 0, 0, 50, 245, 174, 41, 13, + 68, 156, 0, 0, 189, 218, 4, 0, 0, 0, 0, 0, 7, 251, 136, 0, 0, 0, 0, 0, 0, 23, 255, 112, + 0, 0, 0, 0, 0, 0, 7, 251, 136, 0, 0, 0, 0, 0, 0, 0, 189, 218, 4, 0, 0, 0, 0, 0, + 0, 50, 245, 174, 40, 13, 67, 155, 0, 0, 0, 47, 178, 240, 240, 184, 50, 0, 0, 0, 0, 0, 0, 0, + 0, 188, 180, 0, 0, 0, 0, 0, 0, 0, 0, 188, 180, 0, 0, 0, 0, 0, 0, 0, 0, 188, 180, 0, + 0, 0, 84, 215, 247, 200, 49, 188, 180, 0, 0, 63, 254, 131, 20, 43, 180, 203, 180, 0, 0, 192, 197, 0, 0, 0, 37, 254, 180, 0, 1, 248, 124, 0, 0, 0, 0, 217, 180, 0, 19, 255, 102, 0, 0, 0, 0, 195, 180, 0, 1, 248, 123, 0, 0, 0, 0, 217, 180, 0, 0, 193, 195, 0, 0, 0, 37, 253, 180, 0, 0, 65, - 254, 129, 20, 41, 178, 203, 180, 0, 0, 0, 86, 216, 248, 203, 51, 188, 180, 0, 0, 0, 0, 0, 0, 0, - 0, 188, 180, 0, 0, 0, 0, 0, 0, 0, 0, 188, 180, 0, 0, 0, 0, 0, 0, 0, 0, 188, 180, 0, 0, 140, - 232, 24, 177, 243, 148, 0, 140, 233, 171, 61, 5, 0, 0, 140, 255, 79, 0, 0, 0, 0, 140, 254, 7, - 0, 0, 0, 0, 140, 237, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 140, - 232, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 29, 166, 231, 243, 202, 87, 0, 0, 199, 188, 35, 9, - 50, 166, 23, 5, 254, 97, 0, 0, 0, 0, 0, 0, 215, 218, 87, 16, 0, 0, 0, 0, 39, 180, 249, 253, - 199, 88, 0, 0, 0, 0, 10, 70, 172, 255, 75, 0, 0, 0, 0, 0, 2, 248, 130, 30, 178, 68, 19, 13, - 104, 254, 65, 0, 75, 184, 236, 241, 199, 82, 0, 0, 132, 240, 0, 0, 0, 0, 132, 240, 0, 0, 0, - 144, 255, 255, 255, 255, 228, 0, 132, 240, 0, 0, 0, 0, 132, 240, 0, 0, 0, 0, 132, 240, 0, 0, 0, - 0, 132, 240, 0, 0, 0, 0, 132, 240, 0, 0, 0, 0, 122, 246, 0, 0, 0, 0, 81, 255, 65, 2, 0, 0, 2, - 157, 238, 255, 228, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, - 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, - 0, 0, 193, 176, 0, 0, 150, 216, 0, 0, 0, 0, 218, 176, 0, 0, 117, 246, 11, 0, 0, 36, 252, 176, - 0, 0, 28, 250, 146, 16, 45, 172, 204, 176, 0, 0, 0, 83, 219, 248, 194, 39, 192, 176, 0, 86, - 255, 46, 0, 0, 0, 0, 183, 208, 8, 240, 138, 0, 0, 0, 23, 251, 115, 0, 155, 228, 2, 0, 0, 112, - 252, 25, 0, 62, 255, 68, 0, 0, 205, 184, 0, 0, 1, 223, 161, 0, 41, 255, 91, 0, 0, 0, 131, 243, - 10, 134, 243, 10, 0, 0, 0, 38, 255, 92, 225, 160, 0, 0, 0, 0, 0, 200, 228, 255, 67, 0, 0, 0, 0, - 0, 107, 255, 228, 2, 0, 0, 52, 255, 60, 0, 0, 109, 255, 132, 0, 0, 37, 255, 74, 2, 239, 126, 0, - 0, 175, 246, 198, 0, 0, 103, 252, 13, 0, 177, 191, 0, 2, 238, 141, 251, 12, 0, 168, 200, 0, 0, - 112, 248, 8, 50, 254, 23, 244, 73, 0, 232, 134, 0, 0, 46, 255, 66, 115, 207, 0, 184, 138, 43, - 255, 69, 0, 0, 1, 235, 132, 181, 141, 0, 118, 204, 109, 249, 10, 0, 0, 0, 172, 201, 242, 75, 0, - 52, 253, 190, 194, 0, 0, 0, 0, 106, 255, 252, 14, 0, 2, 239, 255, 129, 0, 0, 0, 0, 41, 255, - 200, 0, 0, 0, 177, 255, 64, 0, 0, 11, 216, 202, 5, 0, 0, 70, 254, 107, 0, 49, 249, 136, 0, 22, - 232, 178, 0, 0, 0, 113, 254, 68, 184, 229, 20, 0, 0, 0, 1, 182, 248, 254, 67, 0, 0, 0, 0, 0, - 89, 255, 207, 0, 0, 0, 0, 0, 24, 233, 205, 254, 111, 0, 0, 0, 1, 185, 227, 18, 142, 248, 46, 0, - 0, 115, 253, 63, 0, 6, 206, 213, 9, 49, 249, 132, 0, 0, 0, 39, 245, 151, 81, 255, 49, 0, 0, 0, - 0, 186, 204, 4, 230, 148, 0, 0, 0, 31, 253, 103, 0, 132, 239, 8, 0, 0, 129, 243, 13, 0, 31, - 253, 91, 0, 2, 225, 155, 0, 0, 0, 183, 190, 0, 71, 255, 54, 0, 0, 0, 80, 254, 35, 171, 208, 0, - 0, 0, 0, 4, 229, 153, 249, 106, 0, 0, 0, 0, 0, 131, 255, 245, 15, 0, 0, 0, 0, 0, 32, 255, 159, - 0, 0, 0, 0, 0, 0, 65, 255, 56, 0, 0, 0, 0, 0, 19, 199, 193, 0, 0, 0, 0, 0, 188, 250, 198, 34, - 0, 0, 0, 0, 28, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 81, 254, 143, 0, 0, 0, 0, 45, - 243, 198, 6, 0, 0, 0, 19, 222, 227, 24, 0, 0, 0, 4, 190, 246, 51, 0, 0, 0, 0, 148, 255, 90, 0, - 0, 0, 0, 100, 255, 136, 0, 0, 0, 0, 45, 249, 182, 2, 0, 0, 0, 0, 80, 255, 255, 255, 255, 255, - 255, 184, 0, 0, 0, 0, 9, 161, 233, 253, 48, 0, 0, 0, 0, 0, 100, 254, 66, 5, 0, 0, 0, 0, 0, 0, - 139, 232, 0, 0, 0, 0, 0, 0, 0, 0, 144, 224, 0, 0, 0, 0, 0, 0, 0, 0, 144, 223, 0, 0, 0, 0, 0, 0, - 0, 0, 162, 211, 0, 0, 0, 0, 0, 0, 4, 51, 236, 147, 0, 0, 0, 0, 0, 0, 255, 255, 206, 14, 0, 0, - 0, 0, 0, 0, 4, 49, 232, 145, 0, 0, 0, 0, 0, 0, 0, 0, 157, 211, 0, 0, 0, 0, 0, 0, 0, 0, 144, - 223, 0, 0, 0, 0, 0, 0, 0, 0, 144, 224, 0, 0, 0, 0, 0, 0, 0, 0, 139, 232, 0, 0, 0, 0, 0, 0, 0, - 0, 101, 254, 66, 5, 0, 0, 0, 0, 0, 0, 10, 164, 234, 253, 48, 0, 0, 0, 248, 92, 0, 0, 0, 248, - 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, - 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, - 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 254, 239, 183, 28, - 0, 0, 0, 0, 0, 0, 2, 40, 237, 147, 0, 0, 0, 0, 0, 0, 0, 0, 185, 186, 0, 0, 0, 0, 0, 0, 0, 0, - 176, 192, 0, 0, 0, 0, 0, 0, 0, 0, 175, 192, 0, 0, 0, 0, 0, 0, 0, 0, 164, 210, 0, 0, 0, 0, 0, 0, - 0, 0, 103, 254, 79, 7, 0, 0, 0, 0, 0, 0, 1, 173, 255, 255, 48, 0, 0, 0, 0, 0, 101, 252, 73, 7, - 0, 0, 0, 0, 0, 0, 164, 205, 0, 0, 0, 0, 0, 0, 0, 0, 175, 192, 0, 0, 0, 0, 0, 0, 0, 0, 176, 192, - 0, 0, 0, 0, 0, 0, 0, 0, 184, 186, 0, 0, 0, 0, 0, 0, 2, 39, 236, 148, 0, 0, 0, 0, 0, 0, 254, - 241, 186, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 6, 112, 205, 244, 227, 157, - 74, 14, 20, 107, 165, 0, 0, 76, 175, 49, 8, 43, 121, 202, 246, 225, 162, 38, 0, 0, 10, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 254, 129, 20, 41, 178, 203, 180, 0, 0, 0, 86, 216, 248, 203, 51, 188, 180, 0, 0, 0, 45, 177, 239, 247, + 198, 62, 0, 0, 0, 47, 244, 166, 34, 18, 126, 249, 51, 0, 0, 185, 214, 4, 0, 0, 0, 193, 176, 0, + 6, 250, 138, 0, 0, 0, 0, 130, 236, 0, 23, 255, 255, 255, 255, 255, 255, 255, 254, 0, 8, 252, 126, 0, + 0, 0, 0, 0, 0, 0, 0, 188, 205, 3, 0, 0, 0, 0, 0, 0, 0, 47, 243, 168, 41, 9, 39, 112, + 141, 0, 0, 0, 40, 168, 234, 247, 215, 140, 26, 0, 0, 0, 95, 218, 250, 240, 0, 23, 251, 113, 4, 0, + 0, 60, 255, 49, 0, 0, 160, 255, 255, 255, 255, 144, 0, 64, 255, 48, 0, 0, 0, 64, 255, 48, 0, 0, + 0, 64, 255, 48, 0, 0, 0, 64, 255, 48, 0, 0, 0, 64, 255, 48, 0, 0, 0, 64, 255, 48, 0, 0, + 0, 64, 255, 48, 0, 0, 0, 64, 255, 48, 0, 0, 0, 0, 86, 215, 247, 200, 49, 188, 180, 0, 0, 65, + 254, 128, 20, 41, 176, 203, 180, 0, 0, 193, 195, 0, 0, 0, 34, 253, 180, 0, 1, 248, 123, 0, 0, 0, + 0, 215, 180, 0, 19, 255, 102, 0, 0, 0, 0, 195, 180, 0, 1, 248, 123, 0, 0, 0, 0, 215, 180, 0, + 0, 193, 193, 0, 0, 0, 33, 253, 180, 0, 0, 65, 254, 126, 19, 40, 174, 203, 180, 0, 0, 0, 87, 216, + 248, 201, 49, 197, 172, 0, 0, 0, 0, 0, 0, 0, 11, 238, 134, 0, 0, 15, 158, 46, 9, 42, 176, 245, + 35, 0, 0, 0, 95, 204, 245, 240, 184, 55, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, + 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 20, 175, 244, + 228, 110, 0, 0, 0, 140, 233, 164, 61, 15, 118, 255, 57, 0, 0, 140, 255, 73, 0, 0, 0, 222, 153, 0, + 0, 140, 252, 4, 0, 0, 0, 181, 185, 0, 0, 140, 233, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, + 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, + 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 0, 0, 0, + 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, + 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, + 0, 0, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, + 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, 0, 0, 124, 244, 0, + 0, 0, 133, 229, 0, 0, 17, 198, 178, 0, 76, 246, 194, 37, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, + 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, + 0, 0, 0, 94, 250, 124, 0, 0, 140, 232, 0, 0, 118, 251, 99, 0, 0, 0, 140, 232, 1, 143, 246, 78, + 0, 0, 0, 0, 140, 235, 167, 238, 60, 0, 0, 0, 0, 0, 140, 247, 244, 182, 4, 0, 0, 0, 0, 0, + 140, 232, 62, 243, 172, 4, 0, 0, 0, 0, 140, 232, 0, 60, 243, 173, 5, 0, 0, 0, 140, 232, 0, 0, + 58, 242, 175, 5, 0, 0, 140, 232, 0, 0, 0, 56, 241, 176, 6, 0, 124, 244, 0, 0, 124, 244, 0, 0, + 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, + 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 124, 244, 0, 0, 140, 232, 23, 180, 244, 213, 61, 0, + 75, 212, 247, 190, 27, 0, 0, 0, 140, 233, 163, 54, 17, 153, 238, 67, 157, 27, 36, 218, 175, 0, 0, 0, + 140, 255, 68, 0, 0, 20, 254, 219, 6, 0, 0, 106, 253, 15, 0, 0, 140, 252, 3, 0, 0, 0, 236, 168, + 0, 0, 0, 68, 255, 45, 0, 0, 140, 233, 0, 0, 0, 0, 224, 145, 0, 0, 0, 56, 255, 60, 0, 0, + 140, 232, 0, 0, 0, 0, 224, 144, 0, 0, 0, 56, 255, 60, 0, 0, 140, 232, 0, 0, 0, 0, 224, 144, + 0, 0, 0, 56, 255, 60, 0, 0, 140, 232, 0, 0, 0, 0, 224, 144, 0, 0, 0, 56, 255, 60, 0, 0, + 140, 232, 0, 0, 0, 0, 224, 144, 0, 0, 0, 56, 255, 60, 0, 0, 140, 232, 20, 175, 244, 228, 110, 0, + 0, 0, 140, 233, 164, 61, 15, 118, 255, 57, 0, 0, 140, 255, 73, 0, 0, 0, 222, 153, 0, 0, 140, 252, + 4, 0, 0, 0, 181, 185, 0, 0, 140, 233, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, + 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, + 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 0, 67, 196, 246, 241, 178, 40, 0, 0, 0, 67, 252, 145, 24, + 36, 187, 237, 30, 0, 0, 199, 209, 0, 0, 0, 19, 245, 148, 0, 8, 253, 133, 0, 0, 0, 0, 186, 212, + 0, 24, 255, 111, 0, 0, 0, 0, 163, 231, 0, 8, 253, 132, 0, 0, 0, 0, 185, 212, 0, 0, 199, 207, + 0, 0, 0, 18, 244, 149, 0, 0, 69, 252, 143, 23, 34, 185, 239, 31, 0, 0, 0, 69, 197, 247, 242, 179, + 42, 0, 0, 0, 140, 232, 26, 183, 245, 225, 113, 0, 0, 0, 140, 233, 173, 60, 16, 101, 251, 101, 0, 0, + 140, 255, 79, 0, 0, 0, 156, 229, 4, 0, 140, 254, 6, 0, 0, 0, 81, 255, 36, 0, 140, 239, 0, 0, + 0, 0, 59, 255, 63, 0, 140, 254, 6, 0, 0, 0, 81, 255, 36, 0, 140, 255, 78, 0, 0, 0, 155, 230, + 4, 0, 140, 234, 172, 59, 15, 99, 250, 103, 0, 0, 140, 232, 28, 185, 246, 227, 115, 0, 0, 0, 140, 232, + 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 215, 247, 200, 49, 188, 180, 0, 0, 63, 254, 131, 20, 43, 180, 203, 180, 0, 0, + 192, 197, 0, 0, 0, 37, 254, 180, 0, 1, 248, 124, 0, 0, 0, 0, 217, 180, 0, 19, 255, 102, 0, 0, + 0, 0, 195, 180, 0, 1, 248, 123, 0, 0, 0, 0, 217, 180, 0, 0, 193, 195, 0, 0, 0, 37, 253, 180, + 0, 0, 65, 254, 129, 20, 41, 178, 203, 180, 0, 0, 0, 86, 216, 248, 203, 51, 188, 180, 0, 0, 0, 0, + 0, 0, 0, 0, 188, 180, 0, 0, 0, 0, 0, 0, 0, 0, 188, 180, 0, 0, 0, 0, 0, 0, 0, 0, + 188, 180, 0, 0, 140, 232, 24, 177, 243, 148, 0, 140, 233, 171, 61, 5, 0, 0, 140, 255, 79, 0, 0, 0, + 0, 140, 254, 7, 0, 0, 0, 0, 140, 237, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 140, 232, + 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 29, 166, 231, 243, 202, + 87, 0, 0, 199, 188, 35, 9, 50, 166, 23, 5, 254, 97, 0, 0, 0, 0, 0, 0, 215, 218, 87, 16, 0, + 0, 0, 0, 39, 180, 249, 253, 199, 88, 0, 0, 0, 0, 10, 70, 172, 255, 75, 0, 0, 0, 0, 0, 2, + 248, 130, 30, 178, 68, 19, 13, 104, 254, 65, 0, 75, 184, 236, 241, 199, 82, 0, 0, 132, 240, 0, 0, 0, + 0, 132, 240, 0, 0, 0, 144, 255, 255, 255, 255, 228, 0, 132, 240, 0, 0, 0, 0, 132, 240, 0, 0, 0, + 0, 132, 240, 0, 0, 0, 0, 132, 240, 0, 0, 0, 0, 132, 240, 0, 0, 0, 0, 122, 246, 0, 0, 0, + 0, 81, 255, 65, 2, 0, 0, 2, 157, 238, 255, 228, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, + 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, + 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, 0, 193, 176, 0, 0, 150, 216, 0, 0, 0, 0, 218, 176, 0, + 0, 117, 246, 11, 0, 0, 36, 252, 176, 0, 0, 28, 250, 146, 16, 45, 172, 204, 176, 0, 0, 0, 83, 219, + 248, 194, 39, 192, 176, 0, 86, 255, 46, 0, 0, 0, 0, 183, 208, 8, 240, 138, 0, 0, 0, 23, 251, 115, + 0, 155, 228, 2, 0, 0, 112, 252, 25, 0, 62, 255, 68, 0, 0, 205, 184, 0, 0, 1, 223, 161, 0, 41, + 255, 91, 0, 0, 0, 131, 243, 10, 134, 243, 10, 0, 0, 0, 38, 255, 92, 225, 160, 0, 0, 0, 0, 0, + 200, 228, 255, 67, 0, 0, 0, 0, 0, 107, 255, 228, 2, 0, 0, 52, 255, 60, 0, 0, 109, 255, 132, 0, + 0, 37, 255, 74, 2, 239, 126, 0, 0, 175, 246, 198, 0, 0, 103, 252, 13, 0, 177, 191, 0, 2, 238, 141, + 251, 12, 0, 168, 200, 0, 0, 112, 248, 8, 50, 254, 23, 244, 73, 0, 232, 134, 0, 0, 46, 255, 66, 115, + 207, 0, 184, 138, 43, 255, 69, 0, 0, 1, 235, 132, 181, 141, 0, 118, 204, 109, 249, 10, 0, 0, 0, 172, + 201, 242, 75, 0, 52, 253, 190, 194, 0, 0, 0, 0, 106, 255, 252, 14, 0, 2, 239, 255, 129, 0, 0, 0, + 0, 41, 255, 200, 0, 0, 0, 177, 255, 64, 0, 0, 11, 216, 202, 5, 0, 0, 70, 254, 107, 0, 49, 249, + 136, 0, 22, 232, 178, 0, 0, 0, 113, 254, 68, 184, 229, 20, 0, 0, 0, 1, 182, 248, 254, 67, 0, 0, + 0, 0, 0, 89, 255, 207, 0, 0, 0, 0, 0, 24, 233, 205, 254, 111, 0, 0, 0, 1, 185, 227, 18, 142, + 248, 46, 0, 0, 115, 253, 63, 0, 6, 206, 213, 9, 49, 249, 132, 0, 0, 0, 39, 245, 151, 81, 255, 49, + 0, 0, 0, 0, 186, 204, 4, 230, 148, 0, 0, 0, 31, 253, 103, 0, 132, 239, 8, 0, 0, 129, 243, 13, + 0, 31, 253, 91, 0, 2, 225, 155, 0, 0, 0, 183, 190, 0, 71, 255, 54, 0, 0, 0, 80, 254, 35, 171, + 208, 0, 0, 0, 0, 4, 229, 153, 249, 106, 0, 0, 0, 0, 0, 131, 255, 245, 15, 0, 0, 0, 0, 0, + 32, 255, 159, 0, 0, 0, 0, 0, 0, 65, 255, 56, 0, 0, 0, 0, 0, 19, 199, 193, 0, 0, 0, 0, + 0, 188, 250, 198, 34, 0, 0, 0, 0, 28, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 81, 254, + 143, 0, 0, 0, 0, 45, 243, 198, 6, 0, 0, 0, 19, 222, 227, 24, 0, 0, 0, 4, 190, 246, 51, 0, + 0, 0, 0, 148, 255, 90, 0, 0, 0, 0, 100, 255, 136, 0, 0, 0, 0, 45, 249, 182, 2, 0, 0, 0, + 0, 80, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 9, 161, 233, 253, 48, 0, 0, 0, 0, 0, 100, + 254, 66, 5, 0, 0, 0, 0, 0, 0, 139, 232, 0, 0, 0, 0, 0, 0, 0, 0, 144, 224, 0, 0, 0, + 0, 0, 0, 0, 0, 144, 223, 0, 0, 0, 0, 0, 0, 0, 0, 162, 211, 0, 0, 0, 0, 0, 0, 4, + 51, 236, 147, 0, 0, 0, 0, 0, 0, 255, 255, 206, 14, 0, 0, 0, 0, 0, 0, 4, 49, 232, 145, 0, + 0, 0, 0, 0, 0, 0, 0, 157, 211, 0, 0, 0, 0, 0, 0, 0, 0, 144, 223, 0, 0, 0, 0, 0, + 0, 0, 0, 144, 224, 0, 0, 0, 0, 0, 0, 0, 0, 139, 232, 0, 0, 0, 0, 0, 0, 0, 0, 101, + 254, 66, 5, 0, 0, 0, 0, 0, 0, 10, 164, 234, 253, 48, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, + 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, + 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, + 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, 0, 248, 92, 0, 0, + 0, 254, 239, 183, 28, 0, 0, 0, 0, 0, 0, 2, 40, 237, 147, 0, 0, 0, 0, 0, 0, 0, 0, 185, + 186, 0, 0, 0, 0, 0, 0, 0, 0, 176, 192, 0, 0, 0, 0, 0, 0, 0, 0, 175, 192, 0, 0, 0, + 0, 0, 0, 0, 0, 164, 210, 0, 0, 0, 0, 0, 0, 0, 0, 103, 254, 79, 7, 0, 0, 0, 0, 0, + 0, 1, 173, 255, 255, 48, 0, 0, 0, 0, 0, 101, 252, 73, 7, 0, 0, 0, 0, 0, 0, 164, 205, 0, + 0, 0, 0, 0, 0, 0, 0, 175, 192, 0, 0, 0, 0, 0, 0, 0, 0, 176, 192, 0, 0, 0, 0, 0, + 0, 0, 0, 184, 186, 0, 0, 0, 0, 0, 0, 2, 39, 236, 148, 0, 0, 0, 0, 0, 0, 254, 241, 186, + 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 6, 112, 205, 244, 227, + 157, 74, 14, 20, 107, 165, 0, 0, 76, 175, 49, 8, 43, 121, 202, 246, 225, 162, 38, 0, 0, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 106, 230, 229, 105, 0, 0, 0, 54, 219, 35, 37, 222, 55, 0, 0, 108, 143, 0, 0, 144, 111, 0, + 0, 59, 218, 33, 34, 220, 60, 0, 0, 0, 115, 232, 232, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 184, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, + 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 56, 255, 255, 233, 129, 0, 0, 0, 1, 55, 245, 64, 0, 0, + 0, 9, 235, 48, 0, 0, 1, 163, 150, 0, 0, 2, 152, 174, 4, 0, 6, 167, 162, 4, 0, 0, 67, 255, + 255, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 239, 155, 5, 0, 0, 0, 33, 227, 89, 0, 0, + 0, 33, 228, 67, 0, 0, 248, 255, 170, 3, 0, 0, 1, 29, 204, 116, 0, 0, 1, 32, 206, 128, 60, 255, + 255, 236, 164, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, + 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, 0, 192, + 176, 0, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, 0, 196, 176, 0, 0, 164, + 230, 0, 0, 0, 3, 232, 176, 0, 0, 164, 255, 127, 16, 22, 144, 255, 199, 3, 0, 164, 209, 148, 232, 235, + 160, 103, 240, 174, 0, 164, 204, 0, 0, 0, 0, 0, 0, 0, 0, 164, 204, 0, 0, 0, 0, 0, 0, 0, + 0, 164, 204, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 92, 0, 0, 72, 255, 92, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 236, 255, + 240, 0, 0, 0, 0, 48, 240, 0, 0, 0, 0, 48, 240, 0, 0, 0, 0, 48, 240, 0, 0, 0, 0, 48, + 240, 0, 0, 0, 0, 48, 240, 0, 0, 0, 200, 255, 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, + 155, 2, 0, 0, 0, 0, 64, 179, 9, 0, 0, 0, 69, 247, 159, 2, 0, 0, 65, 245, 164, 3, 0, 0, + 0, 0, 69, 247, 160, 2, 67, 246, 164, 3, 0, 0, 0, 0, 0, 0, 69, 247, 183, 246, 164, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 124, 255, 225, 6, 0, 0, 0, 0, 0, 0, 0, 0, 69, 246, 183, 246, 164, 2, + 0, 0, 0, 0, 0, 0, 69, 246, 160, 2, 67, 246, 164, 2, 0, 0, 0, 0, 69, 246, 159, 2, 0, 0, + 66, 245, 164, 2, 0, 0, 0, 95, 156, 2, 0, 0, 0, 0, 64, 180, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, 255, 255, 255, 255, 212, 0, 108, 255, 40, 0, 0, 0, 0, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, + 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, + 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 108, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, + 255, 142, 0, 0, 0, 0, 0, 0, 0, 9, 241, 173, 233, 4, 0, 0, 0, 0, 0, 0, 90, 243, 26, 251, + 76, 0, 0, 0, 0, 0, 0, 185, 170, 0, 186, 170, 0, 0, 0, 0, 0, 26, 253, 88, 0, 104, 248, 17, + 0, 0, 0, 0, 119, 248, 13, 0, 23, 253, 104, 0, 0, 0, 0, 213, 180, 0, 0, 0, 195, 198, 0, 0, + 0, 53, 255, 98, 0, 0, 0, 113, 255, 38, 0, 0, 148, 252, 20, 0, 0, 0, 31, 255, 132, 0, 6, 236, + 190, 0, 0, 0, 0, 0, 204, 225, 1, 82, 255, 108, 0, 0, 0, 0, 0, 121, 255, 65, 177, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 160, 0, 0, 0, 65, 181, 237, 249, 221, 144, 22, 0, 0, 0, 0, 0, 114, 253, + 134, 29, 7, 56, 199, 229, 39, 0, 0, 0, 57, 253, 128, 0, 0, 0, 0, 11, 224, 209, 2, 0, 0, 174, + 240, 9, 0, 0, 0, 0, 0, 108, 255, 75, 0, 1, 241, 180, 0, 0, 0, 0, 0, 0, 38, 255, 141, 0, + 15, 255, 151, 116, 255, 255, 255, 255, 255, 24, 254, 170, 0, 16, 255, 150, 0, 0, 0, 0, 0, 0, 8, 255, + 170, 0, 1, 241, 180, 0, 0, 0, 0, 0, 0, 38, 255, 141, 0, 0, 175, 240, 8, 0, 0, 0, 0, 0, + 108, 255, 75, 0, 0, 58, 253, 128, 0, 0, 0, 0, 11, 224, 209, 2, 0, 0, 0, 114, 253, 133, 28, 7, + 55, 198, 230, 40, 0, 0, 0, 0, 0, 66, 181, 237, 249, 222, 145, 23, 0, 0, 0, 0, 0, 0, 0, 156, + 255, 142, 0, 0, 0, 0, 0, 0, 0, 9, 241, 170, 233, 4, 0, 0, 0, 0, 0, 0, 90, 240, 21, 248, + 76, 0, 0, 0, 0, 0, 0, 185, 162, 0, 177, 170, 0, 0, 0, 0, 0, 26, 253, 76, 0, 91, 248, 17, + 0, 0, 0, 0, 119, 240, 6, 0, 13, 247, 104, 0, 0, 0, 0, 213, 161, 0, 0, 0, 174, 198, 0, 0, + 0, 53, 255, 76, 0, 0, 0, 88, 255, 38, 0, 0, 148, 240, 6, 0, 0, 0, 11, 246, 132, 0, 6, 236, + 161, 0, 0, 0, 0, 0, 172, 225, 1, 82, 255, 75, 0, 0, 0, 0, 0, 86, 255, 65, 177, 240, 6, 0, + 0, 0, 0, 0, 10, 244, 160, 0, 108, 255, 255, 255, 255, 255, 255, 196, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 255, 244, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 255, + 255, 255, 255, 255, 255, 196, 0, 0, 108, 255, 255, 255, 255, 255, 255, 255, 255, 120, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 40, 0, + 0, 0, 0, 28, 255, 120, 0, 0, 108, 255, 255, 255, 255, 255, 255, 244, 0, 0, 28, 231, 197, 6, 0, 0, + 0, 0, 0, 0, 0, 58, 249, 158, 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 113, 0, 0, 0, 0, 0, + 0, 0, 0, 148, 252, 70, 0, 0, 0, 0, 0, 0, 0, 4, 222, 232, 5, 0, 0, 0, 0, 0, 0, 80, + 255, 120, 0, 0, 0, 0, 0, 0, 22, 234, 198, 3, 0, 0, 0, 0, 0, 0, 177, 245, 36, 0, 0, 0, + 0, 0, 0, 99, 255, 106, 0, 0, 0, 0, 0, 0, 32, 242, 186, 1, 0, 0, 0, 0, 0, 0, 108, 255, + 255, 255, 255, 255, 255, 255, 24, 0, 180, 234, 20, 0, 0, 0, 0, 54, 252, 123, 0, 27, 240, 167, 0, 0, + 0, 6, 212, 206, 4, 0, 0, 100, 255, 78, 0, 0, 129, 251, 48, 0, 0, 0, 0, 187, 230, 16, 45, 250, + 131, 0, 0, 0, 0, 0, 32, 243, 161, 203, 212, 6, 0, 0, 0, 0, 0, 0, 108, 255, 252, 54, 0, 0, + 0, 0, 0, 0, 0, 1, 237, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 124, 255, 24, 0, 0, 0, 0, 0, 0, 0, 1, 98, 191, 247, 255, 230, 166, 42, 0, 0, 0, 0, 6, 182, + 241, 98, 135, 255, 59, 160, 249, 94, 0, 0, 0, 126, 255, 64, 0, 124, 255, 24, 0, 178, 249, 32, 0, 0, + 226, 200, 0, 0, 124, 255, 24, 0, 55, 255, 126, 0, 13, 255, 153, 0, 0, 124, 255, 24, 0, 12, 255, 168, + 0, 14, 255, 152, 0, 0, 124, 255, 24, 0, 12, 255, 168, 0, 0, 227, 200, 0, 0, 124, 255, 24, 0, 55, + 255, 126, 0, 0, 127, 255, 63, 0, 124, 255, 24, 0, 178, 249, 33, 0, 0, 6, 182, 241, 98, 135, 255, 58, + 160, 249, 94, 0, 0, 0, 0, 1, 98, 192, 247, 255, 230, 165, 42, 0, 0, 0, 0, 0, 0, 0, 0, 124, + 255, 24, 0, 0, 0, 0, 0, 24, 255, 144, 0, 0, 124, 255, 24, 0, 0, 244, 184, 0, 24, 255, 144, 0, + 0, 124, 255, 24, 0, 0, 244, 184, 0, 24, 255, 144, 0, 0, 124, 255, 24, 0, 0, 244, 184, 0, 21, 255, + 144, 0, 0, 124, 255, 24, 0, 0, 245, 180, 0, 5, 255, 155, 0, 0, 124, 255, 24, 0, 2, 254, 162, 0, + 0, 220, 190, 0, 0, 124, 255, 24, 0, 35, 255, 120, 0, 0, 147, 247, 14, 0, 124, 255, 24, 0, 107, 255, + 47, 0, 0, 35, 246, 134, 0, 124, 255, 24, 11, 223, 180, 0, 0, 0, 0, 91, 251, 112, 132, 255, 56, 187, + 220, 24, 0, 0, 0, 0, 0, 70, 204, 251, 255, 241, 162, 21, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 255, 24, 0, 0, 0, 0, 0, 0, 0, 0, 87, 193, + 241, 246, 208, 116, 7, 0, 0, 0, 0, 152, 240, 99, 18, 10, 74, 218, 196, 11, 0, 0, 99, 253, 62, 0, + 0, 0, 0, 25, 235, 155, 0, 0, 221, 181, 0, 0, 0, 0, 0, 0, 126, 252, 26, 33, 255, 116, 0, 0, + 0, 0, 0, 0, 61, 255, 90, 60, 255, 97, 0, 0, 0, 0, 0, 0, 41, 255, 117, 52, 255, 119, 0, 0, + 0, 0, 0, 0, 64, 255, 107, 17, 252, 169, 0, 0, 0, 0, 0, 0, 113, 255, 69, 0, 185, 247, 20, 0, + 0, 0, 0, 1, 210, 236, 6, 0, 47, 250, 173, 0, 0, 0, 0, 118, 255, 100, 0, 0, 0, 91, 254, 161, + 8, 0, 114, 252, 149, 0, 0, 100, 255, 255, 255, 255, 76, 16, 255, 255, 255, 255, 160, 0, 0, 82, 211, 248, + 214, 77, 6, 241, 77, 0, 0, 83, 254, 106, 13, 98, 247, 101, 242, 7, 0, 0, 209, 198, 0, 0, 0, 208, + 240, 166, 0, 0, 10, 254, 131, 0, 0, 0, 149, 255, 82, 0, 0, 22, 255, 110, 0, 0, 0, 116, 245, 9, + 0, 0, 2, 251, 127, 0, 0, 0, 178, 248, 4, 0, 0, 0, 206, 187, 0, 0, 14, 246, 255, 45, 0, 0, + 0, 90, 254, 107, 15, 151, 199, 194, 147, 0, 0, 0, 0, 112, 222, 250, 216, 38, 48, 221, 200, 0, 0, 0, + 52, 193, 241, 231, 170, 30, 0, 0, 0, 15, 237, 174, 23, 24, 184, 201, 0, 0, 0, 89, 255, 31, 0, 0, + 67, 255, 26, 0, 0, 121, 249, 0, 0, 0, 64, 255, 48, 0, 0, 128, 244, 0, 0, 0, 124, 254, 19, 0, + 0, 128, 244, 0, 19, 94, 243, 176, 0, 0, 0, 128, 244, 4, 255, 255, 253, 87, 0, 0, 0, 128, 244, 0, + 7, 26, 113, 251, 118, 0, 0, 128, 244, 0, 0, 0, 0, 146, 241, 0, 0, 128, 244, 0, 0, 0, 0, 110, + 253, 6, 0, 128, 249, 16, 0, 0, 0, 151, 224, 0, 0, 128, 255, 194, 43, 10, 80, 245, 116, 0, 0, 128, + 247, 152, 230, 245, 212, 111, 3, 0, 0, 128, 244, 0, 0, 0, 0, 0, 0, 0, 0, 128, 244, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 244, 0, 0, 0, 0, 0, 0, 0, 191, 223, 66, 0, 0, 0, 0, 188, 205, 54, + 194, 219, 0, 0, 0, 36, 254, 104, 0, 54, 255, 57, 0, 0, 138, 245, 14, 0, 0, 221, 147, 0, 6, 234, + 158, 0, 0, 0, 134, 233, 4, 89, 255, 57, 0, 0, 0, 47, 255, 72, 192, 212, 0, 0, 0, 0, 0, 215, + 197, 255, 111, 0, 0, 0, 0, 0, 128, 255, 248, 18, 0, 0, 0, 0, 0, 40, 255, 166, 0, 0, 0, 0, + 0, 0, 0, 252, 116, 0, 0, 0, 0, 0, 0, 0, 252, 116, 0, 0, 0, 0, 0, 0, 0, 252, 116, 0, + 0, 0, 0, 0, 95, 205, 241, 241, 195, 60, 0, 0, 0, 37, 255, 126, 15, 6, 47, 138, 0, 0, 0, 33, + 251, 160, 29, 0, 0, 0, 0, 0, 0, 0, 105, 254, 247, 216, 135, 16, 0, 0, 0, 47, 242, 83, 3, 61, + 209, 226, 27, 0, 0, 187, 190, 0, 0, 0, 25, 246, 149, 0, 9, 252, 129, 0, 0, 0, 0, 186, 214, 0, + 23, 255, 111, 0, 0, 0, 0, 163, 231, 0, 5, 249, 134, 0, 0, 0, 0, 187, 211, 0, 0, 187, 210, 0, + 0, 0, 20, 246, 146, 0, 0, 54, 248, 145, 23, 36, 189, 236, 28, 0, 0, 0, 59, 193, 246, 242, 177, 39, + 0, 0, 0, 5, 137, 223, 251, 255, 255, 68, 0, 0, 128, 247, 82, 8, 0, 0, 0, 0, 0, 169, 190, 0, + 0, 0, 0, 0, 0, 0, 56, 224, 77, 6, 0, 0, 0, 0, 0, 27, 169, 255, 255, 220, 0, 0, 0, 0, + 192, 221, 55, 6, 0, 0, 0, 0, 0, 228, 132, 0, 0, 0, 0, 0, 0, 0, 151, 228, 69, 11, 0, 0, + 0, 0, 0, 6, 125, 214, 244, 255, 255, 144, 0, 0, 220, 255, 255, 255, 255, 255, 240, 0, 0, 0, 0, 1, + 119, 248, 207, 63, 0, 0, 0, 6, 173, 254, 130, 4, 0, 0, 0, 0, 166, 252, 87, 0, 0, 0, 0, 0, + 85, 255, 112, 0, 0, 0, 0, 0, 0, 201, 210, 1, 0, 0, 0, 0, 0, 14, 254, 117, 0, 0, 0, 0, + 0, 0, 35, 255, 79, 0, 0, 0, 0, 0, 0, 20, 255, 99, 0, 0, 0, 0, 0, 0, 0, 213, 177, 0, + 0, 0, 0, 0, 0, 0, 75, 253, 150, 39, 5, 0, 0, 0, 0, 0, 59, 178, 234, 249, 208, 51, 0, 0, + 0, 0, 0, 0, 4, 182, 200, 0, 0, 0, 0, 0, 0, 6, 174, 197, 0, 0, 0, 0, 0, 0, 145, 203, + 49, 0, 0, 140, 232, 20, 175, 244, 222, 116, 0, 0, 0, 140, 233, 151, 53, 12, 97, 255, 80, 0, 0, 140, + 255, 50, 0, 0, 0, 203, 165, 0, 0, 140, 245, 0, 0, 0, 0, 171, 194, 0, 0, 140, 232, 0, 0, 0, + 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, + 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 140, 232, 0, 0, 0, 0, 168, 200, 0, 0, 0, 0, 0, + 0, 0, 0, 168, 200, 0, 0, 0, 0, 0, 0, 0, 0, 168, 200, 0, 0, 0, 0, 0, 0, 0, 0, 168, + 200, 0, 0, 0, 31, 177, 243, 237, 153, 14, 0, 0, 0, 14, 222, 159, 18, 33, 201, 191, 1, 0, 0, 125, + 238, 7, 0, 0, 39, 255, 79, 0, 0, 210, 166, 0, 0, 0, 0, 222, 163, 0, 5, 252, 126, 0, 0, 0, + 0, 180, 209, 0, 22, 255, 255, 255, 255, 255, 255, 255, 229, 0, 22, 255, 112, 0, 0, 0, 0, 165, 229, 0, + 5, 252, 136, 0, 0, 0, 0, 188, 209, 0, 0, 210, 179, 0, 0, 0, 0, 232, 162, 0, 0, 124, 244, 16, + 0, 0, 60, 255, 78, 0, 0, 14, 221, 175, 21, 36, 211, 189, 1, 0, 0, 0, 31, 177, 244, 238, 153, 14, + 0, 0, 0, 180, 200, 0, 0, 0, 179, 200, 0, 0, 0, 178, 200, 0, 0, 0, 178, 200, 0, 0, 0, 177, + 200, 0, 0, 0, 175, 200, 0, 0, 0, 155, 207, 0, 0, 0, 98, 246, 40, 0, 0, 6, 153, 236, 220, 0, + 128, 252, 0, 0, 10, 186, 222, 31, 0, 0, 128, 252, 0, 13, 193, 220, 29, 0, 0, 0, 128, 252, 16, 200, + 219, 28, 0, 0, 0, 0, 128, 253, 206, 255, 69, 0, 0, 0, 0, 0, 128, 255, 226, 223, 209, 7, 0, 0, + 0, 0, 128, 253, 30, 58, 252, 143, 0, 0, 0, 0, 128, 252, 0, 0, 127, 254, 70, 0, 0, 0, 128, 252, + 0, 0, 3, 196, 230, 20, 0, 0, 128, 252, 0, 0, 0, 31, 240, 176, 0, 0, 55, 251, 220, 83, 0, 0, + 0, 0, 0, 0, 9, 156, 242, 13, 0, 0, 0, 0, 0, 0, 23, 251, 100, 0, 0, 0, 0, 0, 0, 0, + 202, 197, 0, 0, 0, 0, 0, 0, 33, 252, 255, 39, 0, 0, 0, 0, 0, 147, 246, 241, 135, 0, 0, 0, + 0, 18, 244, 148, 154, 229, 3, 0, 0, 0, 123, 252, 32, 57, 255, 74, 0, 0, 7, 231, 165, 0, 0, 215, + 171, 0, 0, 99, 255, 47, 0, 0, 119, 249, 19, 1, 213, 182, 0, 0, 0, 25, 252, 109, 74, 255, 63, 0, + 0, 0, 0, 181, 207, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, 0, 192, 176, + 0, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, + 0, 0, 0, 0, 192, 176, 0, 0, 164, 204, 0, 0, 0, 0, 196, 176, 0, 0, 164, 230, 0, 0, 0, 3, + 232, 176, 0, 0, 164, 255, 127, 16, 22, 144, 255, 199, 3, 0, 164, 209, 148, 232, 235, 160, 103, 240, 174, 0, + 164, 204, 0, 0, 0, 0, 0, 0, 0, 0, 164, 204, 0, 0, 0, 0, 0, 0, 0, 0, 164, 204, 0, 0, + 0, 0, 0, 0, 0, 71, 255, 70, 0, 0, 44, 248, 70, 0, 6, 243, 149, 0, 0, 0, 157, 194, 0, 0, + 175, 227, 0, 0, 0, 86, 254, 19, 0, 99, 255, 51, 0, 0, 86, 255, 40, 0, 24, 254, 130, 0, 0, 165, + 246, 8, 0, 0, 203, 209, 0, 54, 252, 147, 0, 0, 0, 127, 255, 48, 215, 218, 14, 0, 0, 0, 51, 255, + 238, 243, 43, 0, 0, 0, 0, 1, 229, 249, 68, 0, 0, 0, 0, 168, 255, 255, 255, 255, 255, 132, 0, 0, + 24, 193, 202, 92, 36, 10, 1, 0, 0, 155, 211, 6, 0, 0, 0, 0, 0, 0, 178, 166, 0, 0, 0, 0, + 0, 0, 0, 90, 249, 134, 53, 17, 2, 0, 0, 0, 0, 88, 239, 255, 255, 184, 0, 0, 0, 88, 246, 136, + 42, 7, 0, 0, 0, 4, 239, 138, 0, 0, 0, 0, 0, 0, 31, 255, 80, 0, 0, 0, 0, 0, 0, 12, + 249, 133, 0, 0, 0, 0, 0, 0, 0, 140, 251, 126, 38, 7, 0, 0, 0, 0, 1, 96, 190, 237, 251, 218, + 71, 0, 0, 0, 0, 0, 0, 2, 152, 232, 0, 0, 0, 0, 0, 0, 3, 145, 229, 0, 0, 0, 0, 0, + 0, 116, 214, 68, 0, 0, 0, 67, 196, 246, 241, 178, 40, 0, 0, 0, 67, 252, 145, 24, 36, 187, 237, 30, + 0, 0, 199, 209, 0, 0, 0, 19, 245, 148, 0, 8, 253, 133, 0, 0, 0, 0, 186, 212, 0, 24, 255, 111, + 0, 0, 0, 0, 163, 231, 0, 8, 253, 132, 0, 0, 0, 0, 185, 212, 0, 0, 199, 207, 0, 0, 0, 18, + 244, 149, 0, 0, 69, 252, 143, 23, 34, 185, 239, 31, 0, 0, 0, 69, 197, 247, 242, 179, 42, 0, 0, 108, + 255, 255, 255, 255, 255, 255, 255, 248, 0, 0, 76, 255, 44, 0, 0, 156, 220, 0, 0, 0, 76, 255, 44, 0, + 0, 156, 220, 0, 0, 0, 76, 255, 44, 0, 0, 156, 220, 0, 0, 0, 76, 255, 44, 0, 0, 156, 220, 0, + 0, 0, 76, 255, 44, 0, 0, 156, 220, 0, 0, 0, 76, 255, 44, 0, 0, 153, 220, 0, 0, 0, 76, 255, + 44, 0, 0, 133, 242, 18, 1, 0, 76, 255, 44, 0, 0, 46, 227, 246, 40, 0, 0, 4, 129, 230, 250, 225, + 113, 0, 0, 0, 0, 160, 226, 60, 16, 101, 251, 101, 0, 0, 43, 255, 79, 0, 0, 0, 156, 229, 4, 0, + 110, 254, 6, 0, 0, 0, 81, 255, 36, 0, 135, 239, 0, 0, 0, 0, 59, 255, 63, 0, 140, 254, 6, 0, + 0, 0, 81, 255, 36, 0, 140, 255, 78, 0, 0, 0, 155, 230, 4, 0, 140, 244, 208, 59, 15, 99, 250, 103, + 0, 0, 140, 232, 55, 201, 248, 227, 115, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, + 0, 0, 0, 0, 0, 0, 0, 0, 140, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 211, 248, 255, 255, + 255, 255, 172, 0, 95, 255, 140, 23, 33, 178, 255, 73, 0, 0, 212, 206, 0, 0, 0, 16, 244, 175, 0, 11, + 255, 132, 0, 0, 0, 0, 185, 215, 0, 23, 255, 111, 0, 0, 0, 0, 163, 229, 0, 7, 251, 133, 0, 0, + 0, 0, 187, 202, 0, 0, 192, 210, 0, 0, 0, 19, 246, 132, 0, 0, 59, 250, 145, 23, 35, 187, 229, 20, + 0, 0, 0, 63, 195, 247, 241, 173, 34, 0, 0, 56, 255, 255, 255, 255, 255, 255, 255, 220, 0, 0, 0, 0, + 0, 240, 136, 0, 0, 0, 0, 0, 0, 0, 0, 240, 136, 0, 0, 0, 0, 0, 0, 0, 0, 240, 136, 0, + 0, 0, 0, 0, 0, 0, 0, 240, 136, 0, 0, 0, 0, 0, 0, 0, 0, 237, 136, 0, 0, 0, 0, 0, + 0, 0, 0, 215, 146, 0, 0, 0, 0, 0, 0, 0, 0, 152, 209, 20, 0, 0, 0, 0, 0, 0, 0, 23, + 180, 244, 160, 0, 0, 0, 212, 164, 0, 0, 23, 237, 108, 0, 0, 212, 164, 0, 0, 0, 126, 230, 3, 0, + 212, 164, 0, 0, 0, 55, 255, 50, 0, 212, 164, 0, 0, 0, 33, 255, 77, 0, 212, 164, 0, 0, 0, 57, + 255, 58, 0, 209, 164, 0, 0, 0, 109, 253, 20, 0, 180, 183, 0, 0, 2, 207, 170, 0, 0, 93, 248, 63, + 14, 147, 239, 37, 0, 0, 1, 122, 226, 244, 183, 42, 0, 0, 0, 0, 90, 212, 14, 180, 244, 188, 32, 0, + 0, 0, 93, 255, 104, 82, 254, 26, 189, 221, 10, 0, 0, 215, 198, 0, 113, 248, 0, 53, 255, 94, 0, 14, + 255, 127, 0, 116, 248, 0, 1, 249, 146, 0, 27, 255, 106, 0, 116, 248, 0, 0, 229, 159, 0, 10, 253, 129, + 0, 116, 248, 0, 0, 243, 138, 0, 0, 201, 201, 0, 116, 248, 0, 54, 255, 73, 0, 0, 71, 253, 121, 126, + 248, 45, 216, 194, 2, 0, 0, 0, 78, 210, 252, 255, 240, 160, 16, 0, 0, 0, 0, 0, 0, 116, 248, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 116, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 248, 0, 0, 0, + 0, 0, 134, 237, 165, 15, 0, 0, 48, 254, 129, 0, 35, 218, 143, 0, 0, 188, 232, 12, 0, 0, 98, 237, + 7, 75, 255, 103, 0, 0, 0, 12, 244, 89, 212, 214, 3, 0, 0, 0, 0, 160, 239, 255, 77, 0, 0, 0, + 0, 0, 67, 255, 192, 0, 0, 0, 0, 0, 0, 129, 255, 130, 0, 0, 0, 0, 0, 25, 244, 240, 222, 1, + 0, 0, 0, 0, 155, 249, 60, 252, 64, 0, 0, 0, 43, 252, 140, 0, 182, 162, 0, 0, 0, 181, 238, 17, + 0, 80, 249, 67, 2, 65, 255, 114, 0, 0, 0, 130, 228, 197, 32, 255, 84, 0, 116, 248, 0, 0, 208, 164, + 0, 32, 255, 84, 0, 116, 248, 0, 0, 208, 164, 0, 32, 255, 84, 0, 116, 248, 0, 0, 208, 164, 0, 32, + 255, 84, 0, 116, 248, 0, 0, 208, 164, 0, 32, 255, 84, 0, 116, 248, 0, 0, 208, 164, 0, 24, 255, 93, + 0, 116, 248, 0, 0, 218, 156, 0, 1, 236, 140, 0, 116, 248, 0, 18, 249, 115, 0, 0, 115, 246, 90, 125, + 248, 33, 178, 228, 20, 0, 0, 0, 78, 186, 246, 255, 220, 142, 20, 0, 0, 0, 0, 0, 0, 116, 248, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 116, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 248, 0, 0, 0, + 0, 0, 0, 8, 235, 157, 0, 0, 0, 0, 0, 66, 255, 79, 0, 0, 91, 255, 64, 0, 0, 0, 0, 0, + 1, 226, 184, 0, 0, 170, 244, 4, 0, 0, 0, 0, 0, 0, 154, 251, 13, 0, 220, 201, 0, 0, 0, 0, + 0, 0, 0, 106, 255, 59, 0, 236, 184, 0, 0, 4, 255, 94, 0, 0, 88, 255, 76, 0, 221, 197, 0, 0, + 19, 255, 110, 0, 0, 102, 255, 61, 0, 175, 238, 4, 0, 67, 248, 159, 0, 0, 147, 252, 18, 0, 76, 255, + 123, 21, 184, 141, 239, 48, 51, 238, 173, 0, 0, 0, 110, 227, 245, 172, 8, 103, 232, 245, 171, 18, 0, 12, + 220, 0, 0, 0, 0, 12, 220, 0, 12, 220, 0, 12, 220, 0, 12, 220, 0, 12, 220, 0, 0, 0, 0, 3, + 6, 20, 224, 176, 238, 29, 20, 253, 76, 3, 0, 20, 218, 0, 0, 0, 20, 212, 0, 0, 0, 20, 212, 0, + 0, 0, 4, 24, 0, 0, 13, 15, 36, 196, 0, 0, 108, 124, 36, 196, 0, 0, 108, 124, 35, 196, 0, 0, + 110, 124, 13, 226, 7, 3, 179, 124, 0, 111, 193, 163, 125, 96, 21, 9, 0, 0, 10, 20, 0, 113, 134, 0, + 0, 143, 104, 0, 15, 222, 7, 10, 224, 11, 0, 0, 152, 92, 101, 145, 0, 0, 0, 44, 199, 206, 38, 0, + 0, 0, 0, 158, 154, 0, 0, 0, 56, 255, 255, 255, 255, 255, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 56, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 135, 0, 0, 31, 250, 72, 0, 0, 141, 255, 14, 0, 0, + 164, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 176, + 0, 0, 9, 253, 145, 0, 0, 65, 252, 33, 0, 0, 126, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 135, 0, 104, 187, 0, 0, 31, 250, 72, 5, 224, 124, 0, + 0, 141, 255, 14, 89, 255, 66, 0, 0, 164, 255, 4, 112, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 168, 0, 204, 220, 0, 0, 11, 255, 144, 0, 215, 196, 0, + 0, 70, 251, 33, 19, 254, 80, 0, 0, 133, 158, 0, 81, 209, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 132, 0, 0, 0, 212, 212, 0, 0, 0, 128, 255, 40, 0, + 0, 40, 255, 132, 0, 0, 0, 212, 212, 0, 0, 0, 128, 255, 40, 0, 0, 43, 174, 179, 131, 3, 4, 221, + 43, 0, 157, 108, 51, 204, 0, 0, 62, 190, 73, 185, 0, 0, 41, 212, 51, 204, 0, 0, 61, 190, 4, 222, + 41, 0, 154, 111, 0, 48, 178, 180, 137, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 220, 0, 0, 0, 0, 12, 220, + 0, 12, 220, 0, 12, 220, 0, 12, 220, 0, 12, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 191, 220, 0, 0, 0, 97, 194, 220, 0, 0, 21, 197, 75, 220, 0, 0, 162, + 64, 68, 220, 0, 66, 159, 0, 68, 220, 0, 128, 255, 255, 255, 255, 232, 0, 0, 0, 68, 220, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 171, 192, 192, 192, 18, 0, 228, 12, 0, 0, 0, 0, 228, 133, 119, 43, 0, 0, 88, + 60, 88, 223, 66, 0, 0, 0, 0, 107, 153, 11, 14, 0, 7, 175, 112, 32, 189, 190, 192, 127, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 142, 188, 191, 72, 0, 180, 104, 0, 1, 13, 32, 224, 73, 128, 85, 2, 63, 248, + 124, 47, 164, 140, 41, 246, 0, 0, 43, 218, 2, 214, 47, 0, 98, 178, 0, 39, 172, 176, 166, 26, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 192, 192, 192, 192, 120, 0, 0, 0, 0, 197, 72, 0, 0, 0, 54, 214, 1, 0, 0, + 0, 168, 102, 0, 0, 0, 29, 232, 9, 0, 0, 0, 139, 132, 0, 0, 0, 3, 183, 25, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 86, 184, 177, 164, 23, 13, 242, 20, 0, 123, 150, 5, 214, 60, 18, 157, 110, 0, 108, + 210, 182, 201, 33, 48, 215, 3, 0, 78, 188, 43, 222, 10, 0, 96, 183, 0, 101, 186, 177, 173, 36, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 85, 182, 176, 122, 2, 38, 224, 16, 1, 162, 99, 75, 183, 0, 0, 103, 181, 19, 225, + 78, 50, 198, 203, 0, 29, 121, 122, 111, 173, 0, 11, 0, 15, 197, 67, 0, 158, 188, 184, 79, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 40, 138, 0, 0, 0, 0, 0, 0, 48, 164, 0, 0, 0, 0, 95, 104, 132, + 201, 104, 104, 39, 0, 81, 88, 119, 195, 88, 88, 33, 0, 0, 0, 48, 164, 0, 0, 0, 0, 0, 0, 37, + 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 104, 104, + 104, 104, 104, 39, 0, 81, 88, 88, 88, 88, 88, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 76, 76, + 76, 76, 76, 28, 0, 103, 112, 112, 112, 112, 112, 42, 0, 118, 128, 128, 128, 128, 128, 48, 0, 55, 60, 60, + 60, 60, 60, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 0, 0, 18, 186, 2, 0, 137, 85, 0, 0, 224, 9, 0, 21, 221, 0, 0, + 19, 222, 0, 0, 0, 221, 11, 0, 0, 129, 92, 0, 0, 14, 180, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 7, 189, 11, 0, 0, 104, 121, 0, 0, 23, 213, 0, + 0, 0, 237, 5, 0, 0, 237, 4, 0, 26, 210, 0, 0, 111, 114, 0, 7, 182, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 226, 188, 244, 199, 21, 20, 252, 73, 10, 164, 114, + 20, 216, 0, 0, 97, 135, 20, 212, 0, 0, 96, 136, 20, 212, 0, 0, 96, 136, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 43, 174, 179, 131, 3, 4, 221, 43, 0, 157, 108, 51, 204, 0, 0, 62, 190, 73, 185, 0, 0, 41, 212, + 51, 204, 0, 0, 61, 190, 4, 222, 41, 0, 154, 111, 0, 48, 178, 180, 137, 5, 0, 236, 255, 240, 0, 0, + 0, 0, 48, 240, 0, 0, 0, 0, 48, 240, 0, 0, 0, 0, 48, 240, 0, 0, 0, 0, 48, 240, 0, 0, + 0, 0, 48, 240, 0, 0, 0, 200, 255, 255, 255, 140, 56, 255, 255, 233, 129, 0, 0, 0, 1, 55, 245, 64, + 0, 0, 0, 9, 235, 48, 0, 0, 1, 163, 150, 0, 0, 2, 152, 174, 4, 0, 6, 167, 162, 4, 0, 0, + 67, 255, 255, 255, 255, 104, 0, 255, 255, 239, 155, 5, 0, 0, 0, 33, 227, 89, 0, 0, 0, 33, 228, 67, + 0, 0, 248, 255, 170, 3, 0, 0, 1, 29, 204, 116, 0, 0, 1, 32, 206, 128, 60, 255, 255, 236, 164, 13, + 0, 0, 0, 191, 220, 0, 0, 0, 97, 194, 220, 0, 0, 21, 197, 75, 220, 0, 0, 162, 64, 68, 220, 0, + 66, 159, 0, 68, 220, 0, 128, 255, 255, 255, 255, 232, 0, 0, 0, 68, 220, 0, 0, 171, 192, 192, 192, 18, + 0, 228, 12, 0, 0, 0, 0, 228, 133, 119, 43, 0, 0, 88, 60, 88, 223, 66, 0, 0, 0, 0, 107, 153, + 11, 14, 0, 7, 175, 112, 32, 189, 190, 192, 127, 3, 0, 12, 142, 188, 191, 72, 0, 180, 104, 0, 1, 13, + 32, 224, 73, 128, 85, 2, 63, 248, 124, 47, 164, 140, 41, 246, 0, 0, 43, 218, 2, 214, 47, 0, 98, 178, + 0, 39, 172, 176, 166, 26, 30, 192, 192, 192, 192, 120, 0, 0, 0, 0, 197, 72, 0, 0, 0, 54, 214, 1, + 0, 0, 0, 168, 102, 0, 0, 0, 29, 232, 9, 0, 0, 0, 139, 132, 0, 0, 0, 3, 183, 25, 0, 0, + 0, 86, 184, 177, 164, 23, 13, 242, 20, 0, 123, 150, 5, 214, 60, 18, 157, 110, 0, 108, 210, 182, 201, 33, + 48, 215, 3, 0, 78, 188, 43, 222, 10, 0, 96, 183, 0, 101, 186, 177, 173, 36, 0, 85, 182, 176, 122, 2, + 38, 224, 16, 1, 162, 99, 75, 183, 0, 0, 103, 181, 19, 225, 78, 50, 198, 203, 0, 29, 121, 122, 111, 173, + 0, 11, 0, 15, 197, 67, 0, 158, 188, 184, 79, 0, 0, 0, 0, 40, 138, 0, 0, 0, 0, 0, 0, 48, + 164, 0, 0, 0, 0, 95, 104, 132, 201, 104, 104, 39, 0, 81, 88, 119, 195, 88, 88, 33, 0, 0, 0, 48, + 164, 0, 0, 0, 0, 0, 0, 37, 128, 0, 0, 0, 0, 95, 104, 104, 104, 104, 104, 39, 0, 81, 88, 88, + 88, 88, 88, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 76, 76, + 76, 76, 76, 28, 0, 103, 112, 112, 112, 112, 112, 42, 0, 118, 128, 128, 128, 128, 128, 48, 0, 55, 60, 60, + 60, 60, 60, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 18, 186, 2, 0, 137, 85, 0, + 0, 224, 9, 0, 21, 221, 0, 0, 19, 222, 0, 0, 0, 221, 11, 0, 0, 129, 92, 0, 0, 14, 180, 3, + 0, 2, 0, 0, 7, 189, 11, 0, 0, 104, 121, 0, 0, 23, 213, 0, 0, 0, 237, 5, 0, 0, 237, 4, + 0, 26, 210, 0, 0, 111, 114, 0, 7, 182, 8, 0, 0, 12, 50, 39, 0, 0, 0, 165, 122, 148, 182, 2, + 0, 12, 64, 79, 203, 49, 33, 203, 102, 81, 198, 68, 86, 154, 0, 19, 229, 68, 11, 171, 181, 150, 145, 53, + 0, 0, 27, 47, 7, 0, 0, 0, 157, 158, 125, 198, 33, 0, 68, 182, 28, 28, 106, 143, 0, 99, 201, 132, + 132, 132, 88, 0, 41, 209, 15, 0, 1, 17, 0, 0, 75, 181, 176, 178, 69, 0, 0, 120, 231, 238, 155, 5, + 0, 58, 216, 34, 20, 187, 105, 0, 102, 142, 0, 0, 99, 149, 0, 58, 215, 33, 20, 186, 106, 0, 0, 123, + 232, 240, 157, 5, 0, 0, 27, 7, 0, 0, 30, 3, 0, 98, 174, 1, 110, 162, 0, 0, 0, 139, 175, 195, + 7, 0, 0, 0, 55, 250, 108, 0, 0, 0, 27, 210, 47, 203, 57, 0, 0, 149, 62, 0, 32, 171, 10, 20, + 212, 0, 0, 0, 0, 20, 212, 0, 0, 0, 0, 20, 220, 165, 242, 199, 21, 20, 252, 73, 10, 164, 114, 20, + 216, 0, 0, 97, 135, 20, 212, 0, 0, 96, 136, 20, 212, 0, 0, 96, 136, 0, 6, 0, 0, 0, 0, 0, + 20, 212, 0, 0, 0, 0, 0, 20, 212, 0, 0, 17, 19, 0, 20, 212, 0, 82, 186, 28, 0, 20, 215, 137, + 151, 6, 0, 0, 20, 232, 212, 58, 0, 0, 0, 20, 212, 23, 192, 88, 0, 0, 15, 165, 0, 9, 156, 72, + 0, 12, 220, 0, 12, 220, 0, 12, 220, 0, 12, 220, 0, 12, 220, 0, 12, 220, 0, 12, 220, 0, 20, 226, + 193, 246, 185, 77, 224, 240, 97, 0, 20, 251, 69, 12, 189, 190, 21, 53, 226, 0, 20, 216, 0, 0, 133, 104, + 0, 0, 233, 0, 20, 212, 0, 0, 132, 100, 0, 0, 232, 0, 20, 212, 0, 0, 132, 100, 0, 0, 232, 0, + 20, 226, 188, 244, 199, 21, 20, 252, 73, 10, 164, 114, 20, 216, 0, 0, 97, 135, 20, 212, 0, 0, 96, 136, + 20, 212, 0, 0, 96, 136, 2, 26, 7, 46, 14, 0, 0, 20, 231, 160, 125, 206, 54, 0, 20, 242, 10, 0, + 65, 179, 0, 20, 221, 0, 0, 33, 205, 0, 20, 252, 43, 0, 107, 150, 0, 20, 217, 139, 172, 167, 18, 0, + 20, 212, 0, 0, 0, 0, 0, 12, 135, 0, 0, 0, 0, 0, 0, 11, 78, 86, 43, 0, 0, 203, 105, 88, + 109, 0, 2, 224, 94, 29, 0, 0, 0, 27, 111, 178, 185, 2, 6, 40, 0, 7, 228, 15, 9, 154, 176, 170, + 81, 0, 8, 111, 0, 0, 0, 37, 221, 32, 23, 0, 100, 236, 128, 92, 0, 16, 216, 0, 0, 0, 16, 216, + 0, 0, 0, 6, 227, 1, 0, 0, 0, 111, 174, 123, 0, 0, 0, 1, 153, 84, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 144, 254, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 133, 255, 109, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 145, 254, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 154, 85, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 17, 185, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 217, 223, 27, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 31, 229, 215, 21, 20, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 229, 215, 21, 0, 0, 0, 0, 0, 0, 0, 0, 21, 217, + 223, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 186, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 58, 129, 139, 76, 0, 0, 0, 0, 127, 169, 188, 254, 140, 0, 0, 0, 0, + 0, 0, 113, 254, 41, 0, 0, 0, 0, 0, 8, 250, 109, 0, 0, 50, 115, 77, 1, 232, 129, 0, 82, 251, + 172, 197, 158, 246, 112, 2, 218, 146, 0, 0, 175, 255, 82, 36, 255, 56, 0, 0, 96, 253, 22, 53, 255, 26, + 0, 0, 192, 185, 0, 14, 246, 79, 0, 107, 250, 51, 0, 0, 98, 241, 214, 235, 76, 0, 0, 0, 0, 18, + 47, 7, 0, 0, 0, 0, 124, 132, 132, 132, 132, 132, 132, 132, 132, 132, 85, 0, 166, 255, 227, 224, 224, 224, + 224, 224, 237, 255, 89, 0, 65, 255, 81, 0, 0, 0, 0, 0, 162, 237, 7, 0, 1, 219, 180, 0, 0, 0, + 0, 14, 245, 145, 0, 0, 0, 120, 252, 26, 0, 0, 0, 103, 255, 45, 0, 0, 0, 24, 251, 121, 0, 0, + 0, 202, 200, 0, 0, 0, 0, 0, 174, 219, 0, 0, 44, 255, 99, 0, 0, 0, 0, 0, 73, 255, 62, 0, + 143, 243, 12, 0, 0, 0, 0, 0, 2, 226, 161, 6, 235, 155, 0, 0, 0, 0, 0, 0, 0, 128, 245, 100, + 255, 54, 0, 0, 0, 0, 0, 0, 0, 30, 253, 244, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 182, 255, + 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 70, 91, 92, 92, 92, 92, 53, 0, 0, 0, 0, 26, 173, + 252, 245, 224, 224, 224, 224, 224, 129, 0, 0, 0, 37, 233, 198, 61, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 204, 187, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 251, 30, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 131, 203, 40, 40, 40, 40, 40, 40, 40, 40, 40, 23, 0, 0, 151, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 148, 0, 0, 123, 210, 24, 24, 24, 24, 24, 24, 24, 24, 24, 13, 0, 0, 46, 255, + 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 215, 23, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 18, 205, 231, 113, 44, 24, 24, 24, 24, 24, 13, 0, 0, 0, 0, 7, 125, 227, 255, 255, 255, + 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 19, 39, 40, 40, 40, 40, 23, 0, 0, 76, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 8, 8, 8, 8, 8, 8, 8, 8, 5, + 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 22, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 54, 0, 0, 0, 0, 0, 0, 0, 91, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 27, 92, 92, 92, 92, + 248, 150, 92, 92, 92, 66, 0, 0, 73, 248, 248, 248, 248, 255, 250, 248, 248, 248, 178, 0, 0, 0, 0, 0, + 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 244, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 255, 52, 0, 0, + 0, 0, 0, 0, 0, 0, 210, 28, 0, 0, 0, 0, 0, 0, 0, 0, 52, 182, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 146, 83, 0, 0, 0, 0, 0, 0, 0, 0, 5, 213, 6, 0, 0, 0, 20, 72, 0, 0, 0, + 79, 141, 0, 0, 0, 72, 184, 249, 18, 0, 0, 172, 43, 0, 0, 0, 0, 9, 240, 105, 0, 17, 192, 0, + 0, 0, 0, 0, 0, 152, 200, 0, 105, 100, 0, 0, 0, 0, 0, 0, 53, 255, 38, 188, 13, 0, 0, 0, + 0, 0, 0, 0, 210, 170, 158, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 59, 0, 0, 0, 0, 0, 0, + 0, 0, 20, 210, 0, 0, 0, 0, 0, 0, 0, 0, 29, 163, 184, 80, 0, 40, 166, 140, 0, 0, 2, 202, + 108, 92, 234, 89, 226, 128, 47, 0, 0, 45, 165, 0, 0, 69, 254, 134, 0, 0, 0, 0, 59, 143, 0, 0, + 8, 239, 94, 0, 0, 0, 0, 16, 209, 18, 3, 150, 185, 232, 38, 0, 0, 0, 0, 95, 238, 235, 194, 10, + 116, 245, 153, 0, 0, 0, 0, 13, 35, 1, 0, 0, 17, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 163, 184, 80, 0, 40, 166, 175, 69, 0, 0, 0, 2, 202, 108, 92, 234, 89, 226, 127, + 82, 213, 42, 0, 0, 45, 165, 0, 0, 69, 254, 132, 0, 0, 78, 128, 0, 0, 59, 143, 0, 0, 8, 239, + 94, 0, 0, 56, 142, 0, 0, 16, 209, 18, 3, 150, 185, 232, 38, 0, 154, 88, 0, 0, 0, 95, 238, 235, + 194, 10, 116, 245, 229, 177, 2, 0, 0, 0, 0, 13, 35, 1, 0, 0, 17, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 133, 254, + 197, 5, 0, 0, 0, 22, 255, 83, 225, 75, 0, 0, 0, 58, 255, 19, 7, 0, 0, 0, 0, 72, 255, 6, + 0, 0, 0, 0, 0, 84, 250, 0, 0, 0, 0, 0, 0, 97, 237, 0, 0, 0, 0, 0, 0, 109, 225, 0, + 0, 0, 0, 0, 0, 122, 212, 0, 0, 0, 0, 0, 0, 134, 200, 0, 0, 0, 0, 0, 0, 147, 187, 0, + 0, 0, 0, 0, 0, 159, 175, 0, 0, 0, 0, 0, 0, 171, 163, 0, 0, 0, 0, 0, 0, 183, 150, 0, + 0, 0, 3, 181, 33, 212, 122, 0, 0, 0, 0, 174, 228, 245, 39, 0, 0, 0, 0, 9, 88, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 6, 112, 205, 244, 227, 157, 74, 14, + 20, 107, 166, 0, 0, 76, 176, 49, 8, 43, 121, 202, 246, 225, 162, 38, 0, 0, 11, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 13, 0, 0, 6, 112, 205, 244, 227, 157, 74, 14, 21, 113, 171, 0, 0, 76, 172, 47, 7, + 43, 121, 202, 246, 225, 162, 38, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9, 180, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 186, 201, 15, 0, + 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 2, 186, 216, 13, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 127, 245, 44, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 184, 0, 0, 0, 0, 121, 241, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 204, 55, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 23, 94, 167, 165, 0, 0, 0, 0, 0, 3, 59, 132, 205, 254, 245, 181, 85, 0, 0, 14, + 97, 170, 239, 255, 217, 144, 71, 8, 0, 0, 0, 0, 76, 255, 255, 211, 69, 0, 0, 0, 0, 0, 0, 0, + 0, 15, 98, 171, 239, 255, 218, 145, 73, 9, 0, 0, 0, 0, 0, 0, 0, 3, 60, 133, 206, 254, 247, 184, + 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 95, 168, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 72, 197, 125, 52, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 30, 150, 223, 255, 233, 162, 90, 20, 0, 0, 0, 0, 0, 0, 0, 0, 40, + 113, 187, 248, 253, 200, 128, 47, 0, 0, 0, 0, 0, 0, 0, 0, 22, 154, 253, 255, 184, 0, 0, 0, 0, + 0, 42, 115, 187, 249, 253, 201, 129, 47, 0, 0, 31, 153, 226, 255, 234, 163, 91, 20, 0, 0, 0, 0, 0, + 72, 198, 126, 53, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, ]; + diff --git a/src/kernels.rs b/src/kernels.rs index 997d2b32..b27425e4 100644 --- a/src/kernels.rs +++ b/src/kernels.rs @@ -2752,12 +2752,14 @@ pub fn heatmap_rgba_into( /// drift in rounding, missing-value, or alpha behavior. pub(crate) fn heatmap_color(value: f64, stops: &[[u8; 3]], alpha: u8) -> [u8; 4] { debug_assert!(!stops.is_empty()); - let t = ((value * 255.0 - 1.0) / 254.0).clamp(0.0, 1.0); - let mut color = colormap_color(t, stops, alpha); - if value <= 0.0 { - color[3] = 0; + // Only genuinely missing cells (NaN, e.g. masked/cmin-clipped bins) are + // transparent. A real in-domain value of 0 must paint the colormap's floor + // color, matching Matplotlib's hist2d/imshow which fill the whole extent. + if value.is_nan() { + return [0, 0, 0, 0]; } - color + let t = ((value * 255.0 - 1.0) / 254.0).clamp(0.0, 1.0); + colormap_color(t, stops, alpha) } /// Evenly spaced color-stop interpolation for a normalized scalar. Shared by diff --git a/src/lib.rs b/src/lib.rs index 73c5c2fa..f1f7671c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ unsafe fn borrowed_byte_spans<'a>( /// ABI version — bumped on any signature change. The Python wrapper checks this /// at load time and refuses a mismatched library loudly (§33 comm-versioning /// rule, applied to the in-process boundary). -pub const ABI_VERSION: u32 = 32; +pub const ABI_VERSION: u32 = 33; const FACTORIZE_CAPACITY_EXCEEDED: usize = usize::MAX - 1; #[no_mangle] diff --git a/src/raster.rs b/src/raster.rs index 177e8412..dc60a402 100644 --- a/src/raster.rs +++ b/src/raster.rs @@ -715,6 +715,47 @@ fn symbol_sdf(px: f32, py: f32, r: f32, sym: u8) -> f32 { let (ax, ay) = (px.abs(), py.abs()); (ax - 0.34 * r).max(ay - r).min((ax - r).max(ay - 0.34 * r)) } + 5 => { + // regular hexagon, pointy top (IQ SDF, x/y swapped for a top vertex) + let (k0, k1, k2) = (-0.866_025_4_f32, 0.5_f32, 0.577_350_3_f32); + let mut p = (py.abs(), px.abs()); + let m = (k0 * p.0 + k1 * p.1).min(0.0); + p = (p.0 - 2.0 * m * k0, p.1 - 2.0 * m * k1); + p = (p.0 - p.0.clamp(-k2 * r, k2 * r), p.1 - r); + (p.0 * p.0 + p.1 * p.1).sqrt() * p.1.signum() + } + 6 => { + // regular pentagon, apex up (IQ SDF) + let (k0, k1, k2) = (0.809_017_f32, 0.587_785_25_f32, 0.726_542_5_f32); + let mut p = (px.abs(), -py); // flip y so the apex points up + let d1 = (-k0) * p.0 + k1 * p.1; + let m1 = d1.min(0.0); + p = (p.0 - 2.0 * m1 * (-k0), p.1 - 2.0 * m1 * k1); + let d2 = k0 * p.0 + k1 * p.1; + let m2 = d2.min(0.0); + p = (p.0 - 2.0 * m2 * k0, p.1 - 2.0 * m2 * k1); + p = (p.0 - p.0.clamp(-r * k2, r * k2), p.1 - r); + (p.0 * p.0 + p.1 * p.1).sqrt() * p.1.signum() + } + 7 => { + // five-pointed star, apex up (IQ SDF) + let rf = 0.45_f32; + let (k1x, k1y) = (0.809_017_f32, -0.587_785_25_f32); + let (k2x, k2y) = (-k1x, k1y); + let mut p = (px.abs(), -py); // flip y so a point faces up + let d1 = k1x * p.0 + k1y * p.1; + let m1 = d1.max(0.0); + p = (p.0 - 2.0 * m1 * k1x, p.1 - 2.0 * m1 * k1y); + let d2 = k2x * p.0 + k2y * p.1; + let m2 = d2.max(0.0); + p = (p.0 - 2.0 * m2 * k2x, p.1 - 2.0 * m2 * k2y); + p = (p.0.abs(), p.1 - r); + let ba = (rf * -k1y - 0.0, rf * k1x - 1.0); + let h = (p.0 * ba.0 + p.1 * ba.1) / (ba.0 * ba.0 + ba.1 * ba.1); + let h = h.clamp(0.0, r); + let q = (p.0 - ba.0 * h, p.1 - ba.1 * h); + (q.0 * q.0 + q.1 * q.1).sqrt() * (p.1 * ba.0 - p.0 * ba.1).signum() + } _ => (px * px + py * py).sqrt() - r, // circle } } @@ -766,7 +807,7 @@ fn point_u8_at( let stroke_alpha = stroke[3] as f32 / 255.0; let ext = r + sw + 1.0; let (bx0, by0, bx1, by1) = cv.bbox(cx - ext, cy - ext, cx + ext, cy + ext); - if sw <= 0.0 && !(1..=4).contains(&sym) { + if sw <= 0.0 && sym == 0 { // Stroke-free circle — the default mark and the overwhelming batch // case. Classify each pixel by squared distance first: fully outside // and fully covered pixels never pay the sqrt or the float coverage @@ -1069,55 +1110,111 @@ fn blit_heatmap( // ---- text (baked glyph atlas) ----------------------------------------------- +/// Atlas row for a char: the contiguous ASCII block, then the sorted extras. +fn glyph_index(ch: char) -> Option { + let code = ch as u32; + if (font::FIRST as u32..=font::LAST as u32).contains(&code) { + return Some((code - font::FIRST as u32) as usize); + } + let ascii = (font::LAST - font::FIRST + 1) as usize; + font::EXTRA_CODEPOINTS + .binary_search(&code) + .ok() + .map(|i| ascii + i) +} + +/// High bit of the anchor byte requests 90°-CCW text (bottom-up y-axis titles). +pub const TEXT_ROTATED: u8 = 0x80; + fn text(cv: &mut Canvas, x: f32, y: f32, anchor: u8, size: f32, rgba: [f32; 4], s: &[u8]) { + let rotated = anchor & TEXT_ROTATED != 0; + let anchor = anchor & !TEXT_ROTATED; let scale = size / font::BASE_PX as f32; + let text = String::from_utf8_lossy(s); // Total advance for anchoring. let mut adv = 0.0f32; - for &b in s { - if (font::FIRST..=font::LAST).contains(&b) { - adv += font::GLYPHS[(b - font::FIRST) as usize].0 as f32; + for ch in text.chars() { + if let Some(i) = glyph_index(ch) { + adv += font::GLYPHS[i].0 as f32; } } - let mut penx = match anchor { - 1 => x - adv * scale * 0.5, - 2 => x - adv * scale, - _ => x, + // The pen walks +x for horizontal text, -y (upward) when rotated. + let (mut penx, mut peny) = if rotated { + ( + x, + match anchor { + 1 => y + adv * scale * 0.5, + 2 => y + adv * scale, + _ => y, + }, + ) + } else { + ( + match anchor { + 1 => x - adv * scale * 0.5, + 2 => x - adv * scale, + _ => x, + }, + y, + ) }; - for &b in s { - if !(font::FIRST..=font::LAST).contains(&b) { + for ch in text.chars() { + let Some(i) = glyph_index(ch) else { continue; - } - let (advance, gw, gh, left, top, off, len) = font::GLYPHS[(b - font::FIRST) as usize]; + }; + let (advance, gw, gh, left, top, off, len) = font::GLYPHS[i]; if gw > 0 && gh > 0 { let cov = &font::COVERAGE[off as usize..(off + len) as usize]; - let gx = penx + left as f32 * scale; - let gy = y + top as f32 * scale; - let (dw, dh) = (gw as f32 * scale, gh as f32 * scale); - let (bx0, by0, bx1, by1) = cv.bbox(gx, gy, gx + dw, gy + dh); - for py in by0..by1 { - for px in bx0..bx1 { - let u = - ((px as f32 + 0.5 - gx) / dw * gw as f32 - 0.5).clamp(0.0, gw as f32 - 1.0); - let vv = - ((py as f32 + 0.5 - gy) / dh * gh as f32 - 0.5).clamp(0.0, gh as f32 - 1.0); - let (x0, y0c) = (u.floor() as usize, vv.floor() as usize); - let (x1, y1c) = ( - (x0 + 1).min(gw as usize - 1), - (y0c + 1).min(gh as usize - 1), - ); - let (fx, fy) = (u - x0 as f32, vv - y0c as f32); - let sample = |sx: usize, sy: usize| cov[sy * gw as usize + sx] as f32 / 255.0; - let c = sample(x0, y0c) * (1.0 - fx) * (1.0 - fy) - + sample(x1, y0c) * fx * (1.0 - fy) - + sample(x0, y1c) * (1.0 - fx) * fy - + sample(x1, y1c) * fx * fy; - if c > 0.0 { - cv.blend(px, py, rgba, c); + let sample = |sx: usize, sy: usize| cov[sy * gw as usize + sx] as f32 / 255.0; + let bilinear = |u: f32, vv: f32| { + let u = u.clamp(0.0, gw as f32 - 1.0); + let vv = vv.clamp(0.0, gh as f32 - 1.0); + let (x0, y0c) = (u.floor() as usize, vv.floor() as usize); + let (x1, y1c) = ((x0 + 1).min(gw as usize - 1), (y0c + 1).min(gh as usize - 1)); + let (fx, fy) = (u - x0 as f32, vv - y0c as f32); + sample(x0, y0c) * (1.0 - fx) * (1.0 - fy) + + sample(x1, y0c) * fx * (1.0 - fy) + + sample(x0, y1c) * (1.0 - fx) * fy + + sample(x1, y1c) * fx * fy + }; + if rotated { + // CCW: glyph +u (right) points up (-y), +v (down) points +x. + let gx = penx + top as f32 * scale; + let gy = peny - (left + gw) as f32 * scale; + let (dw, dh) = (gh as f32 * scale, gw as f32 * scale); + let (bx0, by0, bx1, by1) = cv.bbox(gx, gy, gx + dw, gy + dh); + for py in by0..by1 { + for px in bx0..bx1 { + let u = (gy + dh - (py as f32 + 0.5)) / dh * gw as f32 - 0.5; + let vv = (px as f32 + 0.5 - gx) / dw * gh as f32 - 0.5; + let c = bilinear(u, vv); + if c > 0.0 { + cv.blend(px, py, rgba, c); + } + } + } + } else { + let gx = penx + left as f32 * scale; + let gy = y + top as f32 * scale; + let (dw, dh) = (gw as f32 * scale, gh as f32 * scale); + let (bx0, by0, bx1, by1) = cv.bbox(gx, gy, gx + dw, gy + dh); + for py in by0..by1 { + for px in bx0..bx1 { + let u = (px as f32 + 0.5 - gx) / dw * gw as f32 - 0.5; + let vv = (py as f32 + 0.5 - gy) / dh * gh as f32 - 0.5; + let c = bilinear(u, vv); + if c > 0.0 { + cv.blend(px, py, rgba, c); + } } } } } - penx += advance as f32 * scale; + if rotated { + peny -= advance as f32 * scale; + } else { + penx += advance as f32 * scale; + } } } diff --git a/tests/pyplot/test_axes_layout.py b/tests/pyplot/test_axes_layout.py index bb885281..3f3e54b6 100644 --- a/tests/pyplot/test_axes_layout.py +++ b/tests/pyplot/test_axes_layout.py @@ -90,7 +90,7 @@ def test_tick_params_records_supported_style_and_rejects_unknown() -> None: x_axis = _axis_child(ax, "x") assert x_axis.tick_label_angle == 45.0 - assert x_axis.tick_label_strategy == "none" + assert x_axis.tick_label_strategy == "off" # labels hidden, ticks/baselines kept assert x_axis.style == { "tick_color": "#d62728", "tick_label_color": "#d62728", diff --git a/tests/pyplot/test_color_pipeline_fixes.py b/tests/pyplot/test_color_pipeline_fixes.py new file mode 100644 index 00000000..504c0d45 --- /dev/null +++ b/tests/pyplot/test_color_pipeline_fixes.py @@ -0,0 +1,215 @@ +"""Regression tests for the colorbar/colormap visual-parity fixes. + +Each mirrors a defect confirmed by pixel comparison against Matplotlib 3.11 +(PDSH ch.4 histograms/colorbars/contour notebooks): count-mappable colorbar +domains, nice colorbar ticks, opaque zero-count heatmap cells, reversed + +clamped imshow colormaps, discrete (resampled) colormaps, and the contour +auto-level count / dashed-negative / filled-band conventions. +""" + +import io + +import numpy as np +import pytest + +import xy.pyplot as plt + + +@pytest.fixture(autouse=True) +def _clean_state(): + yield + plt.close("all") + plt.rcdefaults() + + +def _svg(): + buffer = io.BytesIO() + plt.savefig(buffer, format="svg") + return buffer.getvalue().decode() + + +def _png(): + buffer = io.BytesIO() + plt.savefig(buffer, format="png") + data = buffer.getvalue() + assert data[:4] == b"\x89PNG" + return data + + +def _compiled_traces(): + ax = plt.gca() + ax._chart = None + return ax._build_chart(800, 600).figure().traces + + +def _blob(name: str) -> np.ndarray: + """Compiled colorbar options for the current axes (after build).""" + ax = plt.gca() + ax._chart = None + figure = ax._build_chart(800, 600).figure() + return getattr(figure, name) + + +# -- defect 1/4: count-mappable colorbar domains ------------------------------ + + +def test_hist2d_colorbar_domain_reflects_counts_not_unit_interval(): + np.random.seed(3) + x, y = np.random.randn(2000), np.random.randn(2000) + h, _xe, _ye, _im = plt.hist2d(x, y, bins=20) + plt.colorbar() + lo, hi = plt.gca()._colorbar["domain"] + assert lo == 0.0 + assert hi == pytest.approx(float(h.max())) + assert hi > 1.0 # regression: used to clamp to the 0..1 placeholder + + +def test_hexbin_colorbar_domain_autoscales_to_counts_at_build(): + np.random.seed(4) + x, y = np.random.randn(3000), np.random.randn(3000) + plt.hexbin(x, y, gridsize=20) + plt.colorbar() + # colorbar() cannot know binned counts yet, so it defers via _autoscale. + assert plt.gca()._colorbar.get("_autoscale") is True + options = _blob("colorbar_options") + assert "_autoscale" not in options + assert options["domain"][1] > 1.0 + + +# -- defect 2: nice default colorbar ticks ------------------------------------ + + +def test_default_colorbar_ticks_are_round_numbers(): + np.random.seed(5) + x, y = np.random.randn(4000), np.random.randn(4000) + plt.hist2d(x, y, bins=25) + plt.colorbar() + svg = _svg() + # A count domain of ~[0, 40+] must tick on round multiples produced by the + # nice-tick locator (0, 10, 20, ...), not the raw min/max endpoints only. + assert ">10<" in svg + assert ">20<" in svg + + +def test_explicit_colorbar_ticks_still_honored(): + np.random.seed(6) + x, y = np.random.randn(2000), np.random.randn(2000) + plt.hist2d(x, y, bins=20) + plt.colorbar(ticks=[0, 7, 14]) + svg = _svg() + assert ">7<" in svg and ">14<" in svg + + +# -- defect 3: zero-count heatmap cells paint the colormap floor -------------- + + +def test_zero_value_heatmap_cell_is_opaque_colormap_floor(): + from xy import kernels + from xy._svg import _colormap_stops + + stops = np.asarray(_colormap_stops("viridis"), dtype=np.uint8) + # value 0 -> opaque floor color; NaN (masked/missing) -> transparent. + rgba = kernels.heatmap_rgba(np.array([[0.0, np.nan]]), 2, 1, stops, 255) + assert rgba[0, 0, 3] == 255 # zero is painted, not a white hole + assert tuple(rgba[0, 0, :3]) == tuple(int(v) for v in stops[0]) + assert rgba[0, 1, 3] == 0 # only genuine NaN stays transparent + + +# -- defect 5: imshow cmap (reversed) + clim reach the raster ----------------- + + +def test_imshow_reversed_cmap_and_post_hoc_clim_reach_the_heatmap(): + from xy._svg import _colormap_stops + + grid = np.linspace(-3, 3, 100).reshape(10, 10) + plt.imshow(grid, cmap="RdBu") + plt.colorbar() + plt.clim(-1, 1) + entry = plt.gca()._entries[-1] + assert entry["kwargs"]["colormap"] == "rdbu" # true ColorBrewer table, not a coolwarm alias + assert entry["kwargs"]["domain"] == (-1.0, 1.0) + # The reversed lookup table must differ from the viridis fallback. + assert _colormap_stops("coolwarm_r") == list(reversed(_colormap_stops("coolwarm"))) + _png() + + +# -- defect 6: discrete (resampled) colormaps --------------------------------- + + +def test_discrete_scatter_colormap_quantizes_into_n_bands(): + np.random.seed(7) + x, y = np.random.randn(400), np.random.randn(400) + plt.scatter(x, y, c=x, cmap=plt.get_cmap("viridis", 6)) + plt.colorbar() + assert plt.gca()._colorbar["levels"] == 6 + scatter = next(t for t in _compiled_traces() if t.kind == "scatter") + distinct = np.unique(scatter.color_ch.values) + assert distinct.size <= 6 # quantized to <= N band representatives + + +def test_discrete_imshow_colormap_quantizes_and_survives_clim(): + grid = np.linspace(-3, 3, 100).reshape(10, 10) + plt.imshow(grid, cmap=plt.get_cmap("Blues", 6)) + plt.colorbar(extend="both") + plt.clim(-1, 1) + assert plt.gca()._colorbar["levels"] == 6 + heatmap = next(t for t in _compiled_traces() if t.kind == "heatmap") + finite = heatmap.grid.values[np.isfinite(heatmap.grid.values)] + assert np.unique(finite).size <= 6 + + +def test_discrete_colorbar_renders_solid_bands(): + np.random.seed(8) + x, y = np.random.randn(300), np.random.randn(300) + plt.scatter(x, y, c=y, cmap=plt.get_cmap("plasma", 4)) + plt.colorbar() + assert _blob("colorbar_options")["levels"] == 4 + _svg() + _png() + + +# -- defect 7: contour conventions -------------------------------------------- + + +def _wiggle(): + def f(a, b): + return np.sin(a) ** 10 + np.cos(10 + b * a) * np.cos(a) + + xg = np.linspace(0, 5, 50) + yg = np.linspace(0, 5, 40) + xx, yy = np.meshgrid(xg, yg) + return xx, yy, f(xx, yy) + + +def test_auto_contour_level_count_matches_maxnlocator(): + xx, yy, zz = _wiggle() + cs = plt.contour(xx, yy, zz) + # MaxNLocator(N+1=8) over [-0.997, 1.05] -> 9 levels at 0.3 spacing. + assert list(np.round(cs.levels, 3)) == [-1.2, -0.9, -0.6, -0.3, 0.0, 0.3, 0.6, 0.9, 1.2] + + +def test_monochrome_contour_dashes_negative_levels(): + xx, yy, zz = _wiggle() + plt.contour(xx, yy, zz, colors="black") + contours = [t for t in _compiled_traces() if t.kind == "contour"] + assert len(contours) == 2 + dashes = sorted(str(t.style.get("dash")) for t in contours) + assert dashes == ["6,4", "None"] # negatives dashed, non-negatives solid + + +def test_colormapped_contour_stays_solid(): + xx, yy, zz = _wiggle() + plt.contour(xx, yy, zz, cmap="RdGy") + contours = [t for t in _compiled_traces() if t.kind == "contour"] + assert len(contours) == 1 + assert contours[0].style.get("dash") is None + + +def test_contourf_fills_discrete_bands_not_a_smooth_gradient(): + xx, yy, zz = _wiggle() + plt.contourf(xx, yy, zz, 20, cmap="RdGy") + heatmap = next(t for t in _compiled_traces() if t.kind == "heatmap") + finite = heatmap.grid.values[np.isfinite(heatmap.grid.values)] + # Piecewise-constant bands: far fewer distinct fill values than grid cells. + assert np.unique(finite).size <= 21 + assert np.unique(finite).size < finite.size diff --git a/tests/pyplot/test_figure_state.py b/tests/pyplot/test_figure_state.py index dfd454a2..f3b7e764 100644 --- a/tests/pyplot/test_figure_state.py +++ b/tests/pyplot/test_figure_state.py @@ -106,8 +106,8 @@ def test_figure_subplots_sharing_ratios_and_squeeze() -> None: assert axes.shape == (2, 2) assert fig.axes == list(axes.ravel()) assert fig.gca() is axes[-1, -1] - assert fig._sharex is True - assert fig._sharey is True + assert fig._sharex == "all" + assert fig._sharey == "all" assert fig._width_ratios == (2.0, 1.0) assert fig._height_ratios == (1.0, 3.0) diff --git a/tests/pyplot/test_launch_compat.py b/tests/pyplot/test_launch_compat.py index 5362b301..5f27147b 100644 --- a/tests/pyplot/test_launch_compat.py +++ b/tests/pyplot/test_launch_compat.py @@ -56,7 +56,11 @@ def test_pyplot_legend_location_and_columns_reach_render_spec() -> None: ax.bar([0, 1], [10, 20], label="values") ax.legend(loc="upper left", ncols=3) spec, _ = ax._build_chart(640, 480).figure().build_payload() - assert spec["legend"] == {"loc": "upper left", "ncols": 3} + assert spec["legend"]["loc"] == "upper left" + assert spec["legend"]["ncols"] == 3 + # Frame styling now rides along so the static-export legend can honor + # frameon/facecolor/edgecolor (previously only the DOM legend saw it). + assert "style" in spec["legend"] def test_filled_stairs_use_seamless_bins_and_hatches_are_not_dropped() -> None: diff --git a/tests/pyplot/test_layout_text_parity_fixes.py b/tests/pyplot/test_layout_text_parity_fixes.py new file mode 100644 index 00000000..a2d95b59 --- /dev/null +++ b/tests/pyplot/test_layout_text_parity_fixes.py @@ -0,0 +1,244 @@ +"""Regression tests for the layout/annotate/tick-text visual-parity fixes. + +Each test pins a defect confirmed by the PDSH image-level audit: inset rects +in mixed figures, shared-axes label suppression, annotate arrows at xytext, +locator label precision, log-axis decade labels, mathtext-to-unicode, and the +rotated raster y-label. +""" + +from __future__ import annotations + +import io +import re + +import numpy as np +import pytest + +import xy.pyplot as plt +from xy.pyplot._mathtext import mathtext_to_unicode + + +@pytest.fixture(autouse=True) +def _reset(): + plt.close("all") + plt.rcdefaults() + yield + plt.close("all") + plt.rcdefaults() + + +def _svg(fig) -> str: + out = io.BytesIO() + fig.savefig(out, format="svg") + return out.getvalue().decode() + + +def _png_pixels(fig) -> np.ndarray: + out = io.BytesIO() + fig.savefig(out, format="png") + out.seek(0) + return np.asarray(plt.imread(out)) + + +def test_inset_rect_survives_next_to_a_default_axes() -> None: + ax1 = plt.axes() + ax2 = plt.axes([0.65, 0.65, 0.2, 0.2]) + fig = plt.gcf() + rects = fig._effective_rects() + assert rects is not None + assert rects[0] == (0.125, 0.11, 0.775, 0.77) # matplotlib SubplotParams + assert rects[1] == (0.65, 0.65, 0.2, 0.2) + ax1.plot([0, 1], [0, 1]) + ax2.plot([0, 1], [1, 0]) + out = io.BytesIO() + fig.savefig(out, format="png") + assert out.getvalue()[:4] == b"\x89PNG" + + +def test_shared_axes_hide_inner_tick_labels_only() -> None: + fig, ax = plt.subplots(2, 3, sharex="col", sharey="row") + strategies = [ + (a._axis["x"].get("tick_label_strategy"), a._axis["y"].get("tick_label_strategy")) + for a in fig.axes + ] + # Top row hides x labels; columns 1-2 hide y labels; edges keep labels. + assert strategies == [ + ("off", None), + ("off", "off"), + ("off", "off"), + (None, None), + (None, "off"), + (None, "off"), + ] + # 'col' unions x-domains per column, not globally. + assert fig._share_groups("col", 6) == [[0, 3], [1, 4], [2, 5]] + assert fig._share_groups("row", 6) == [[0, 1, 2], [3, 4, 5]] + assert fig._share_groups("all", 6) == [[0, 1, 2, 3, 4, 5]] + with pytest.raises(ValueError, match="sharex"): + plt.subplots(2, 2, sharex="diagonal") + + +def test_off_strategy_keeps_baselines_and_hides_labels_in_svg() -> None: + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + ax._axis_props("x")["tick_label_strategy"] = "off" + svg = _svg(fig) + assert 'text-anchor="middle">0.5<' not in svg # x tick labels hidden + assert 'text-anchor="end">' in svg # y tick labels still present + # The x baseline survives (two axis baselines drawn). + assert svg.count('stroke="#c8cdd4"') >= 2 or "baseline" not in svg + + +def test_annotate_draws_arrow_at_xytext() -> None: + fig, ax = plt.subplots() + x = np.linspace(0, 20, 50) + ax.plot(x, np.cos(x)) + ax.annotate( + "peak", xy=(6.28, 1.0), xytext=(10.0, 4.0), arrowprops=dict(facecolor="black", shrink=0.05) + ) + entry = next(e for e in ax._entries if e["kind"] == "@text") + assert entry["args"][:2] == (10.0, 4.0) + arrow = next(e for e in ax._entries if e["kind"] == "@arrow") + sx0, sy0, ex0, ey0 = arrow["args"] + # shrink=0.05 pulls both ends 5% toward each other along the segment + np.testing.assert_allclose((sx0, sy0), (10 + 0.05 * (6.28 - 10), 4 + 0.05 * (1 - 4))) + np.testing.assert_allclose((ex0, ey0), (6.28 - 0.05 * (6.28 - 10), 1 - 0.05 * (1 - 4))) + svg = _svg(fig) + assert ""), + ) + + +def test_multiple_locator_labels_keep_step_precision() -> None: + fig, ax = plt.subplots() + x = np.linspace(0, 10, 100) + ax.plot(x, np.sin(x)) + ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2)) + svg = _svg(fig) + for label in ("1.57", "3.14", "4.71"): + assert f">{label}<" in svg + assert ">2" not in svg.split('text-anchor="middle"')[1][:400] + + +def test_log_axes_label_decades_and_grid_majors_only() -> None: + fig, ax = plt.subplots() + x = np.linspace(1, 2000, 100) + ax.loglog(x, x**2) + chart = ax._build_chart(640, 480).figure() + x_opts = chart.axis_options["x"] + ticks = x_opts["tick_values"] + # decades only — no 2x/5x minor positions frozen into the spec + assert all(abs(t - round(t)) < 1e-9 for t in np.log10(ticks).tolist()) + assert x_opts["tick_labels"][0].startswith("10") + assert "10⁰" in x_opts["tick_labels"] or "10¹" in x_opts["tick_labels"] + + +def test_mathtext_subset_converts_and_unknown_tex_passes_through() -> None: + assert mathtext_to_unicode(r"$\pi/2$") == "π/2" + assert mathtext_to_unicode(r"$3\pi/2$") == "3π/2" + assert mathtext_to_unicode("km$^2$") == "km²" + assert mathtext_to_unicode("log$_{10}$(population)") == "log₁₀(population)" + assert mathtext_to_unicode(r"$\mathdefault{10^{3}}$") == "10³" + assert mathtext_to_unicode(r"$\frac{1}{2}\pi$") == "1/2π" + assert mathtext_to_unicode(r"$\unknowncmd{x}$") == r"$\unknowncmd{x}$" + assert mathtext_to_unicode(r"$x^q$") == r"$x^q$" # no unicode superscript q + + +def test_mathtext_reaches_labels_ticks_and_legend() -> None: + fig, ax = plt.subplots() + x = np.linspace(0, 2 * np.pi, 30) + ax.plot(x, np.sin(x), label=r"$\sigma^2$") + ax.set_ylabel("area km$^2$") + ax.set_xticks([0, np.pi, 2 * np.pi], [r"$0$", r"$\pi$", r"$2\pi$"]) + ax.legend() + svg = _svg(fig) + assert "km²" in svg + assert ">π<" in svg and ">2π<" in svg + assert "σ²" in svg + + +def test_funcformatter_mathtext_tick_labels_render_unicode() -> None: + fig, ax = plt.subplots() + x = np.linspace(0, 3 * np.pi, 100) + ax.plot(x, np.sin(x)) + ax.set_xlim(0, 3 * np.pi) + ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2)) + ax.xaxis.set_major_formatter(plt.FuncFormatter(lambda v, _n: rf"${v / np.pi:g}\pi$")) + svg = _svg(fig) + assert re.search(r">[\d.]+π<", svg) + + +def test_unicode_glyphs_produce_ink_in_png() -> None: + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + ax.set_title("π σ² 10³") + with_title = _png_pixels(fig) + plt.close("all") + fig2, ax2 = plt.subplots() + ax2.plot([0, 1], [0, 1]) + without_title = _png_pixels(fig2) + band = slice(0, with_title.shape[0] // 8) + assert (with_title[band] != without_title[band]).any() + + +def test_ylabel_renders_rotated_in_png_left_margin() -> None: + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + ax.set_ylabel("amplitude of the signal") + pixels = _png_pixels(fig) + height, width = pixels.shape[:2] + # Ink confined to a tall, narrow left-margin band — rotated text; the old + # horizontal top-left placement would put ink in the top band instead. + left_band = pixels[height // 4 : 3 * height // 4, : width // 24, :3] + assert (left_band < 200).any() + top_band = pixels[: height // 40, : width // 24, :3] + assert not (top_band < 200).any() + + +def test_errorbar_color_and_ecolor_resolve_independently() -> None: + fig, ax = plt.subplots() + ax.errorbar([0, 1], [0, 1], yerr=0.1, fmt="o", color="black", ecolor="lightgray") + svg = _svg(fig) + assert "black" in svg or "#000000" in svg # markers keep the explicit color + assert "lightgray" in svg or "#d3d3d3" in svg # bars take ecolor + assert "#1f77b4" not in svg # nobody falls back to the cycle + + +def test_rdbu_is_colorbrewer_not_coolwarm() -> None: + from xy._svg import COLORMAP_STOPS + + assert COLORMAP_STOPS["rdbu"][0] == (103, 0, 31) + assert COLORMAP_STOPS["rdbu"][-1] == (5, 48, 97) + + +def test_colormap_stop_tables_track_matplotlib_bands() -> None: + # plasma's top-of-ramp anchors were once padded duplicates, merging the + # last discrete bands; the tail must stay distinct and orange-then-yellow. + from xy._svg import COLORMAP_STOPS + + tail = COLORMAP_STOPS["plasma"][-3:] + assert len({tuple(anchor) for anchor in tail}) == 3 + assert tail[-1][0] > 200 and tail[-1][1] > 200 # yellow + assert tail[0][1] < 200 # still orange two anchors down + + +def test_scatter_size_arrays_keep_absolute_scale() -> None: + fig, ax = plt.subplots() + sizes = np.array([36.0, 900.0]) + ax.scatter([0, 1], [0, 1], s=sizes) + entry = ax._entries[-1] + lo, hi = entry["kwargs"]["size_range"] + np.testing.assert_allclose((lo, hi), (np.sqrt(36) * 4 / 3, np.sqrt(900) * 4 / 3)) + + +def test_colorbar_label_converts_mathtext() -> None: + fig, ax = plt.subplots() + handle = ax.scatter([0, 1], [0, 1], c=[0.0, 1.0]) + fig.colorbar(handle, label="log$_{10}$(population)") + assert "log₁₀(population)" in _svg(fig) diff --git a/tests/pyplot/test_pdsh_gap_features.py b/tests/pyplot/test_pdsh_gap_features.py index d8f69cd5..059e9506 100644 --- a/tests/pyplot/test_pdsh_gap_features.py +++ b/tests/pyplot/test_pdsh_gap_features.py @@ -404,7 +404,7 @@ def test_add_subplot_xticklabels_empty_hides_labels(): fig = plt.figure() grid = plt.GridSpec(2, 2) ax = fig.add_subplot(grid[0, 0], xticklabels=[]) - assert ax._axis["x"]["tick_label_strategy"] == "none" + assert ax._axis["x"]["tick_label_strategy"] == "off" # labels hidden, ticks kept # -- pandas period interop --------------------------------------------------------- diff --git a/tests/pyplot/test_visible_style_contracts.py b/tests/pyplot/test_visible_style_contracts.py index c9b56c2a..c8aa77fb 100644 --- a/tests/pyplot/test_visible_style_contracts.py +++ b/tests/pyplot/test_visible_style_contracts.py @@ -63,8 +63,12 @@ def test_annotate_preserves_arrow_bbox_alignment_rotation_and_font_style(): assert kwargs["arrowprops"] == {"arrowstyle": "->", "color": "red"} assert kwargs["bbox"] == {"boxstyle": "round", "facecolor": "white"} assert kwargs["anchor"] == "end" - assert kwargs["dx"] == 4.0 - assert kwargs["dy"] == 4.0 + # matplotlib semantics: the text sits AT xytext (data coords), and the + # arrowprops materialize an @arrow entry pointing back at xy. + assert note._entry["args"][:2] == (5.0, 6.0) + arrow = next(e for e in ax._entries if e["kind"] == "@arrow") + assert arrow["args"] == (5.0, 6.0, 1.0, 2.0) + assert arrow["kwargs"]["color"] == "red" assert kwargs["style"] == { "font_size": 9.0, "vertical_align": "bottom", diff --git a/tests/pyplot/test_visual_parity_fixes.py b/tests/pyplot/test_visual_parity_fixes.py new file mode 100644 index 00000000..e942e880 --- /dev/null +++ b/tests/pyplot/test_visual_parity_fixes.py @@ -0,0 +1,223 @@ +"""Regression tests for confirmed matplotlib visual-parity defects. + +Each test pins a bug that was verified by pixel comparison against +matplotlib 3.11 (legend handling, marker shapes, prop-cycle advancement, +scatter alpha, errorbar/hist styling). A regression here means one of those +static-export defects came back. +""" + +import io + +import numpy as np +import pytest + +import xy.pyplot as plt +from xy.pyplot._axes import _marker_symbol +from xy.pyplot._translate import MARKER_TO_SYMBOL + + +@pytest.fixture(autouse=True) +def _clean_state(): + yield + plt.close("all") + plt.rcdefaults() + + +def _png(fig=None): + buffer = io.BytesIO() + (fig or plt.gcf()).savefig(buffer, format="png") + data = buffer.getvalue() + assert data[:4] == b"\x89PNG" + return data + + +def _svg(fig=None): + buffer = io.BytesIO() + (fig or plt.gcf()).savefig(buffer, format="svg") + return buffer.getvalue().decode() + + +# -- defect 1: legend with an explicit label list --------------------------- + + +def test_legend_label_list_labels_existing_lines(): + x = np.linspace(0, 10, 20) + fig, ax = plt.subplots() + ax.plot(x, np.column_stack([np.sin(x), np.cos(x), np.sin(x) * 0.5])) + ax.legend(["a", "b", "c"], loc="lower left") + svg = _svg(fig) + for label in ("a", "b", "c"): + assert f">{label}" in svg + _png(fig) + + +def test_legend_handles_labels_form_relabels_handles(): + x = np.linspace(0, 10, 20) + fig, ax = plt.subplots() + lines = ax.plot(x, np.sin(x)) + lines += ax.plot(x, np.cos(x)) + ax.legend(lines[:2], ["first", "second"]) + svg = _svg(fig) + assert ">first" in svg + assert ">second" in svg + + +# -- defect 2: legend swatches reflect the artist style --------------------- + + +def test_legend_line_entries_render_line_samples_with_dash(): + x = np.linspace(0, 10, 20) + fig, ax = plt.subplots() + ax.plot(x, np.sin(x), label="solid") + ax.plot(x, np.cos(x), "--", label="dashed") + svg = _svg(fig) + # Line legend entries are drawn as samples, and the dashed line + # carries a dash array rather than a filled square. + assert svg.count("= 2 + assert "stroke-dasharray" in svg + + +def test_legend_scatter_entry_renders_marker_glyph(): + x = np.arange(10) + fig, ax = plt.subplots() + ax.scatter(x, np.sin(x), marker="^", label="pts") + svg = _svg(fig) + assert ">pts" in svg + # A triangle marker glyph is a path, not a filled rectangle swatch. + assert "s" in no_frame + + +# -- defect 4: prop cycle advances per column of a 2-D operand -------------- + + +def test_plot_2d_y_advances_prop_cycle_per_column(): + x = np.linspace(0, 10, 20) + y = np.column_stack([np.sin(x), np.cos(x), np.sin(x) + 1, np.cos(x) - 1]) + fig, ax = plt.subplots() + ax.plot(x, y) + colors = [e["kwargs"].get("color") for e in ax._entries if e["kind"] == "line"] + assert colors == ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728"] + + +# -- defect 5: real marker shapes, no silent square substitution ------------ + + +def test_pentagon_and_polygon_markers_map_to_real_shapes(): + assert MARKER_TO_SYMBOL["p"] == "pentagon" + assert MARKER_TO_SYMBOL["h"] == "hexagon" + assert MARKER_TO_SYMBOL["H"] == "hexagon" + assert MARKER_TO_SYMBOL["*"] == "star" + assert _marker_symbol("p") == "pentagon" + + +def test_pentagon_marker_reaches_render_without_square_fallback(): + x = np.linspace(0, 10, 30) + fig, ax = plt.subplots() + ax.plot( + x, + np.sin(x), + "-p", + markersize=15, + markerfacecolor="white", + markeredgecolor="gray", + color="gray", + ) + overlay = [e for e in ax._entries if e["kind"] == "scatter"][-1] + assert overlay["kwargs"]["symbol"] == "pentagon" + _png(fig) + assert " None: assert style2["stroke_width"] == 1.0 with pytest.raises(ValueError, match="scatter symbol"): - Figure().scatter([0.0], [0.0], symbol="star") + Figure().scatter([0.0], [0.0], symbol="octagon") with pytest.raises(ValueError, match="scatter stroke_width"): Figure().scatter([0.0], [0.0], stroke_width=-1) diff --git a/tests/test_kernels.py b/tests/test_kernels.py index 68efc4a3..025a3f1d 100644 --- a/tests/test_kernels.py +++ b/tests/test_kernels.py @@ -1116,12 +1116,15 @@ def test_heatmap_rgba_maps_stops_and_flips_rows(): rgba = k.heatmap_rgba(raw, 2, 2, stops, 200) + # Only genuinely missing (NaN) cells are transparent; a real in-domain 0 + # paints the colormap's floor color opaquely (Matplotlib hist2d/imshow fill + # the whole extent — empty bins are the 0-color, not holes). np.testing.assert_array_equal( rgba, np.array( [ - [[100, 110, 120, 200], [0, 0, 0, 200]], - [[0, 10, 20, 0], [50, 60, 70, 200]], + [[100, 110, 120, 200], [0, 0, 0, 0]], + [[0, 10, 20, 200], [50, 60, 70, 200]], ], dtype=np.uint8, ), From eaaca02143ec54a1b74b636e008a971be7b70b29 Mon Sep 17 00:00:00 2001 From: Farhan Date: Tue, 14 Jul 2026 03:16:44 +0500 Subject: [PATCH 04/17] test(pyplot): add future annotations import for the 3.11 floor gate CI's check_python_floor.py requires every annotated file to import `from __future__ import annotations`; the new color-pipeline regression tests were missing it. --- tests/pyplot/test_color_pipeline_fixes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/pyplot/test_color_pipeline_fixes.py b/tests/pyplot/test_color_pipeline_fixes.py index 504c0d45..f50d647f 100644 --- a/tests/pyplot/test_color_pipeline_fixes.py +++ b/tests/pyplot/test_color_pipeline_fixes.py @@ -7,6 +7,8 @@ auto-level count / dashed-negative / filled-band conventions. """ +from __future__ import annotations + import io import numpy as np From 03e35133010f0c78fe0561323de332fceb2034e8 Mon Sep 17 00:00:00 2001 From: Farhan Date: Tue, 14 Jul 2026 16:24:57 +0500 Subject: [PATCH 05/17] feat(pyplot): close PDSH visual-fidelity follow-ups; make notebooks dual-engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Visual parity round on the shim and renderers: - Directional triangles and diagonal x get distinct glyphs in SVG, native raster, and WebGL instead of collapsing into up-triangle/plus. - clabel places bounded, separated labels along each contour level. - hexbin uses true offset-grid tessellation (nearest-center metric, complete zero-count lattice, six data-space triangles per cell) so bin membership agrees across browser, PNG, and SVG. - Horizontal bar/step/stepfilled histograms put counts on x with touching horizontal bins. - Legend borderpad/labelspacing/fancybox/framealpha/shadow/title reach all renderers; subplots_adjust now raises NotImplementedError for material values, removing the last known silent option discard. - Automatic ticks add the 2.5 nice step with shared fixed precision, loc='best' picks the least-occupied corner, and a four-sided frame with independently controllable spines ships to browser/PNG/SVG. Each PDSH notebook now interleaves a Matplotlib 3.11 reference cell before every xy cell so outputs compare side by side in one kernel run. sync_dual_engine_notebooks.py regenerates the mirrored cells and a test gates freshness. The notebooks are excluded from ruff format (the two plotting aliases differ in length, so near-limit lines wrap in only one copy — the formatter and the sync check cannot both pass) and F811 is ignored there since paired cells redefine names by design. --- docs/matplotlib-compat-changelog.md | 36 +- docs/matplotlib-compat.md | 16 +- docs/matplotlib-shim-todo.md | 5 +- examples/pdsh/README.md | 45 +- ...dsh_04_00_introduction_to_matplotlib.ipynb | 316 ++++++- .../pdsh/pdsh_04_01_simple_line_plots.ipynb | 515 +++++++++-- .../pdsh_04_02_simple_scatter_plots.ipynb | 292 +++++- examples/pdsh/pdsh_04_03_errorbars.ipynb | 188 +++- ...pdsh_04_04_density_and_contour_plots.ipynb | 249 +++++- .../pdsh_04_05_histograms_and_binnings.ipynb | 341 ++++++- .../pdsh/pdsh_04_06_customizing_legends.ipynb | 392 ++++++-- .../pdsh_04_07_customizing_colorbars.ipynb | 460 ++++++++-- .../pdsh/pdsh_04_08_multiple_subplots.ipynb | 347 +++++++- .../pdsh/pdsh_04_09_text_and_annotation.ipynb | 425 ++++++++- .../pdsh/pdsh_04_10_customizing_ticks.ipynb | 388 +++++++- .../pdsh_04_11_settings_and_stylesheets.ipynb | 524 +++++++++-- ...dsh_04_12_three_dimensional_plotting.ipynb | 526 +++++++++-- ...dsh_04_14_visualization_with_seaborn.ipynb | 837 ++++++++++++++++-- examples/pdsh/sync_dual_engine_notebooks.py | 151 ++++ js/src/30_ticks.js | 12 +- js/src/40_gl.js | 13 +- js/src/50_chartview.js | 20 +- js/src/55_marks.js | 10 +- pyproject.toml | 14 +- python/xy/_figure.py | 3 + python/xy/_hosts.py | 1 + python/xy/_payload.py | 48 +- python/xy/_raster.py | 49 +- python/xy/_svg.py | 100 ++- python/xy/_validate.py | 15 +- python/xy/columns.py | 8 +- python/xy/components.py | 5 + python/xy/marks.py | 58 +- python/xy/pyplot/__init__.py | 24 + python/xy/pyplot/_axes.py | 176 +++- python/xy/pyplot/_mplfig.py | 10 +- python/xy/pyplot/_plot_types.py | 38 +- python/xy/pyplot/_rc.py | 14 +- python/xy/pyplot/_translate.py | 16 +- python/xy/static/index.js | 55 +- python/xy/static/standalone.js | 55 +- src/raster.rs | 17 +- tests/pyplot/test_axes_charts.py | 3 +- tests/pyplot/test_grid_legend_contracts.py | 10 +- tests/pyplot/test_launch_compat.py | 11 + tests/pyplot/test_layout_noops.py | 14 +- tests/pyplot/test_layout_text_parity_fixes.py | 9 + tests/pyplot/test_marker_fidelity.py | 31 + tests/pyplot/test_pdsh_gap_features.py | 7 +- tests/pyplot/test_pyplot_state_management.py | 23 + tests/pyplot/test_rc_chrome_contracts.py | 21 +- tests/pyplot/test_silent_drop_regressions.py | 8 +- tests/pyplot/test_visual_parity_fixes.py | 17 + tests/test_pdsh_dual_engine_notebooks.py | 50 ++ tests/test_plot_families.py | 8 +- 55 files changed, 6079 insertions(+), 947 deletions(-) create mode 100644 examples/pdsh/sync_dual_engine_notebooks.py create mode 100644 tests/pyplot/test_marker_fidelity.py create mode 100644 tests/test_pdsh_dual_engine_notebooks.py diff --git a/docs/matplotlib-compat-changelog.md b/docs/matplotlib-compat-changelog.md index 2c6f7a25..925b5f67 100644 --- a/docs/matplotlib-compat-changelog.md +++ b/docs/matplotlib-compat-changelog.md @@ -211,12 +211,36 @@ colorbar domains) fully cleared. - scatter `s=` keeps Matplotlib's absolute area semantics (size arrays no longer compress into the engine's relative 2–18 px band). -Remaining visual boundaries after this round (all loud or documented): -marker-variety fidelity for triangle/x variants, contour inline-label -placement density, hexbin's true tessellation, `subplots_adjust` spacing -(currently ignored — to be made loud or honored), `hist(orientation= -'horizontal')`, tick-density/trailing-zero formatting vs Matplotlib's -defaults, legend loc='best' avoidance, and the boxed-axes frame default. +### Visual-fidelity follow-up — 2026-07-14 + +- Directional triangle markers and diagonal `x` now have distinct SVG, + native-raster, and WebGL glyphs rather than collapsing into up-triangle and + plus. +- `clabel` places a bounded set of separated labels along every contour level + instead of one label per level. +- Hexbin now uses the two offset center grids and nearest-center hex metric, + emits complete zero-count lattices by default, and renders each occupied + cell as six data-space triangles. Bin membership and visible tessellation + now agree across browser, PNG, and SVG. +- Horizontal bar/step/stepfilled histograms put counts on x; the filled form + uses touching horizontal bins rather than a vertical area primitive. +- `borderpad`, `labelspacing`, `fancybox`, `framealpha`, `shadow`, and legend + titles reach browser and static renderers, clearing the two loud PDSH + legend-style cells. +- `subplots_adjust` now raises `NotImplementedError` when given material + values; use `GridSpec` for supported spacing. This removes the last known + silent Matplotlib-shim option discard. +- Direct Matplotlib 3.11/xy PNG comparisons of the six affected PDSH cells + verified usable marker, contour, lattice, legend, and horizontal-histogram + output. +- The final systematic minors are now closed: automatic ticks include the 2.5 + nice step and keep a shared fixed precision, `loc='best'` chooses the least + occupied corner from bounded data samples, and pyplot transports an explicit + four-sided frame with independently controllable spines to browser, PNG, and + SVG. A follow-up Matplotlib 3.11 comparison verified all three together. +- The non-gating `ty` diagnostics on zone-map min/max folds were removed by + using typed list reductions; the complete shippable Python package now type + checks cleanly. Future entries must identify the Matplotlib release/revision, inventory additions or removals, and any compatibility-level changes. diff --git a/docs/matplotlib-compat.md b/docs/matplotlib-compat.md index 32c76401..d07bb468 100644 --- a/docs/matplotlib-compat.md +++ b/docs/matplotlib-compat.md @@ -50,30 +50,30 @@ dependency-free `triangles=` shorthand into Matplotlib's equivalent | matplotlib | notes | |---|---| -| `plt.plot` / `ax.plot` | format strings (`'r--o'`), multiple series per call, implicit x, `label=`, `lw=`, `ls=`, `alpha=`, marker face/edge styling, `markevery`, and dependency-free affine *data* transforms (`Affine2D + ax.transData`); axes/figure-fraction transforms on data artists, partial fill styles, and cap/join policies fail loudly | +| `plt.plot` / `ax.plot` | format strings (`'r--o'`), multiple series per call, implicit x, `label=`, `lw=`, `ls=`, `alpha=`, marker face/edge styling, directional `^`/`v`/`<`/`>` triangles and distinct `+`/`x` glyphs, `markevery`, and dependency-free affine *data* transforms (`Affine2D + ax.transData`); axes/figure-fraction transforms on data artists, partial fill styles, and cap/join policies fail loudly | | `scatter(x, y, s=, c=, cmap=, vmin=, vmax=, alpha=, marker=, edgecolors=, plotnonfinite=)` | `s` (pt², area) maps to pixel diameter; array `c` becomes a color encoding and explicit paired color bounds are retained; custom norms/marker paths fail loudly | | `bar`, `barh`, `grouped_bar`, `bar_label` | string categories, stacking bases, Matplotlib 3.11 grouped-bar containers and labels | -| `hist(bins=, range=, density=, cumulative=, weights=, orientation=, stacked=)` | Returns computed counts/edges and supports bar/step histogram families | -| `hist2d`, `hexbin`, `ecdf` | 2D uniform binning uses the native Rust kernel; hexbin supports `C`, arbitrary scalar reducers, and `mincnt` without retaining source points. Hexbin aggregates over a rectangular grid with staggered display centers — a visual approximation of Matplotlib's true hexagonal binning, so per-bin values differ from Matplotlib's | +| `hist(bins=, range=, density=, cumulative=, weights=, orientation=, stacked=)` | Returns computed counts/edges; bar, step, and stepfilled families render in both vertical and horizontal orientations | +| `hist2d`, `hexbin`, `ecdf` | 2D uniform binning uses the native Rust kernel; hexbin uses Matplotlib's two-offset-grid nearest-center assignment and six-triangle data-space cells, supports `C`, arbitrary scalar reducers, and `mincnt`, and retains only the bounded lattice rather than source points | | `boxplot`, `violinplot`, `bxp`, `violin`, `errorbar` | Boxplots support notches, bootstrap/user confidence intervals, median overrides (drawn median only; notch CIs stay data-derived like Matplotlib), percentile/custom whiskers, cap widths, `sym`, and component colors/widths/alpha — dashed component linestyles fail loudly. Violins support Scott/Silverman/scalar/callable Gaussian-KDE bandwidths, quantiles, and low/high sides; the default (bw_method omitted) uses the native histogram violin mark, whose shape differs from the explicit KDE path | | `fill_between(x, y1, y2, where=, step=)` / `fill_betweenx` | Masks are split into finite contiguous polygons; step geometry is expanded exactly | | `stackplot` | All four baselines are computed by the native stacked-bounds kernel | | `imshow` / `pcolormesh` (`cmap=`, `vmin=`/`vmax=`, `origin=`) | `imshow` defaults to `rcParams['image.origin']`; nearest stays cell-exact and Matplotlib's smoothing mode names all collapse to the shim's single bounded gradient upsampling (a visual approximation, not per-mode kernels) and apply to scalar data only — RGB(A) truecolor arrays render unresampled — while unsupported stages/transforms fail loudly. Uniform meshes retain the texture fast path; nonuniform and curvilinear grids use native quad-to-triangle expansion | | `step`, `stairs`, `stem`, `eventplot` | Compact step/stem/segment marks; no Python-side vertex expansion | -| `contour` / `contourf` / `clabel` | Native marching squares over rectilinear grids; warped grids route through native Delaunay/marching-triangle kernels | +| `contour` / `contourf` / `clabel` | Native marching squares over rectilinear grids; warped grids route through native Delaunay/marching-triangle kernels; automatic labels repeat at bounded, separated positions along each level (line knockout for `inline=True` remains a visual approximation) | | `quiver`, `barbs`, `streamplot` | Native vector endpoint/arrowhead and bounded streamline kernels feeding one instanced segment mark. Barbs are a visual approximation: magnitude maps to a bounded tick count, not WMO 50/10/5 increments. Streamplot always uses the shim's own bounded fixed-step integrator (identical output with or without Matplotlib installed, but paths approximate Matplotlib's adaptive ones); `start_points`, `integration_direction`, array widths/colors and `num_arrows` are honored, and remaining non-default integration options fail loudly | | `tripcolor`, `triplot`, `tricontour`, `tricontourf` | Explicit topology or native dependency-free Delaunay triangulation; indexed geometry and isolines stay in Rust | | `pie` / `pie_label` | Native pie/donut tessellation and the Matplotlib 3.11 `PieContainer` (`values`, `fracs`, grouped text labels) | | `axhline` / `axvline` / `axhspan` / `axvspan`, `text`, `annotate`, `table` | Fractional span bounds plus data/axes/figure text coordinates are supported; `arrowprops` is approximated as callout text | | `xlabel` / `ylabel` / `title` / `suptitle` | Suptitles are retained in HTML and multi-panel PNG/SVG | -| `legend()` | `loc`/`fontsize` accepted; placement is the chart's own | +| `legend()` | `loc`, columns, title/font size/colors, frame styling, `borderpad`, `labelspacing`, `fancybox`, `framealpha`, and `shadow` are retained across browser and static output. `loc='best'` chooses the least occupied corner from bounded samples of the current data | | `grid(True/False)` | toggles the grid via the theme | -| `xlim` / `ylim`, axis scales, `invert_xaxis/yaxis` | linear/log are native; symlog/logit/asinh use dependency-free monotone data transforms with inverse limit/tick semantics. Tick locations come from xy's own generators (refreshed as data arrives), not Matplotlib's locators; artist `get_data()` reflects the transformed space; logit masks values at/outside (0, 1) | +| `xlim` / `ylim`, axis scales, `invert_xaxis/yaxis` | linear/log are native; symlog/logit/asinh use dependency-free monotone data transforms with inverse limit/tick semantics. Automatic linear ticks include Matplotlib's 2.5 step and use uniform decimal padding across a tick set; locations refresh as data arrives. Artist `get_data()` reflects the transformed space; logit masks values at/outside (0, 1) | | `set_major_locator` / `set_major_formatter`, `plt.NullLocator/FixedLocator/MultipleLocator/MaxNLocator/LinearLocator/LogLocator`, `plt.NullFormatter/FixedFormatter/FuncFormatter/FormatStrFormatter/StrMethodFormatter/ScalarFormatter` | xy-owned re-implementations resolved at build time against live data limits (Null/Fixed/Multiple are position-exact; MaxN is a nice-step heuristic, not Matplotlib's edge-extension algorithm). Third-party locator objects work if they implement `tick_values(vmin, vmax)`; minor locators/formatters are retained for round-tripping but minor ticks do not render | | datetime, timedelta, and string coordinates | datetime inputs use the engine's automatic date ticks, timedeltas are bounded to elapsed seconds, and common strings use categorical ticks; the general Matplotlib units registry is intentionally out of scope | | `xticks(positions, labels, rotation=)` / `tick_params(labelrotation=)` | Exact positions and strings render in browser, PNG, and SVG | | `twinx()`, `secondary_xaxis()`, `secondary_yaxis()` | second data axes and linked tick-only secondary axes with callable forward/inverse conversions. Secondary-axis ticks are evenly spaced conversions of the primary domain (not Matplotlib's secondary-unit locators) and currently reach the interactive HTML client only — PNG/SVG export does not draw them yet | -| `fig, ax = plt.subplots()`; `plt.subplots(n, m, figsize=, dpi=, squeeze=, sharex=, sharey=)` | Grid renders as CSS-grid HTML and stitched PNG/SVG; shared axes use common domains and live linked pan/zoom | +| `fig, ax = plt.subplots()`; `plt.subplots(n, m, figsize=, dpi=, squeeze=, sharex=, sharey=)` | Grid renders as CSS-grid HTML and stitched PNG/SVG; shared axes use common domains and live linked pan/zoom. `Figure.subplots_adjust(...)` is rejected because those frame/spacing values are not representable by this renderer; use `GridSpec(..., wspace=, hspace=)` for supported explicit layout | | `fig.add_subplot(2, 2, 1)` / `add_subplot(221)` | | | `gca` / `gcf` / `sca` / `figure(num)` / `close(...)` | matplotlib's implicit-state semantics | | `savefig('x.png' / '.svg' / '.html', dpi=)` | Browser-free PNG/SVG supports both single and multi-panel figures; file-like targets require an explicit `format=` and unsupported metadata/layout/export formats fail loudly | @@ -83,7 +83,7 @@ dependency-free `triangles=` shorthand into Matplotlib's equivalent | `plt.cm.*` / `plt.colormaps[...]` / `cmap=` names | viridis, plasma, inferno, magma, cividis, gray, turbo, coolwarm, Blues, RdYlGn, RdGy, jet, rainbow, Spectral, aliases, and true `*_r` reversal (RdGy/jet render from 11-stop anchor tables sampled from Matplotlib 3.11, linearly interpolated) | | `LinearSegmentedColormap.from_list` / `ListedColormap` | Python-side callables (`cmap(np.arange(cmap.N))` → RGBA) for scripts that colormap values themselves; they cannot be passed as `cmap=` to plotting calls (no engine table), which fails loudly | | `plt.colorbar()` / `fig.colorbar()` / `plt.clim()` / `plt.gci()` | Returns a live handle (`set_label`, `set_ticks`); with no mappable it uses the current image the way pyplot does. `ticks=`/`extend=` render in PNG and SVG (the HTML colorbar stays a minimal gradient without tick text); `clim` retargets the mappable's color window and any colorbar derived from it | -| `rcParams` | Figure size/DPI, line width/marker size, image cmap/origin, and the axes color cycle affect every exporter. The chrome keys (axes face/edge/label/title styles, font family/size, tick colors/sizes, legend defaults, figure facecolor) reach the HTML renderer and multi-panel PNG stitching; single-chart PNG and SVG export currently render their own fixed chrome and ignore them. Unknown keys warn once | +| `rcParams` | Figure size/DPI, line width/marker size, image cmap/origin, axes color cycle, and all four `axes.spines.*` switches affect every exporter. Pyplot axes default to Matplotlib's four-sided box and each spine can be hidden independently. The chrome keys (axes face/edge/label/title styles, font family/size, tick colors/sizes, legend defaults, figure facecolor) reach the HTML renderer and multi-panel PNG stitching; single-chart PNG and SVG export currently render their own fixed chrome and ignore them. Unknown keys warn once | | `plt.style.use(...)` / `plt.style.context(...)` | `"default"`, `"xy"`, bounded rcParam dictionaries, ordered lists, and the stock sheets fivethirtyeight, ggplot, bmh, dark_background, grayscale, and seaborn-v0_8-white(grid) — reduced to the supported rcParams subset (colors, grid, cycle, line width, font size; per-sheet keys outside that subset are not carried). `context()` snapshots and restores. Unknown sheet names fail precisely | | `plt.GridSpec(r, c, wspace=, hspace=, width_ratios=)` + slice specs | Spans (`grid[0, 1:]`, `grid[:-1, 0]`) and custom spacing resolve to explicit figure rectangles using Matplotlib's SubplotParams frame; default-geometry single cells keep the uniform grid. Spanning layouts position exactly in PNG; HTML/SVG sequence free-form panels rather than overlaying the true rectangles | | `add_subplot(spec, sharex=, sharey=, xticklabels=[], ...)` | per-axes sharing aliases the axis-property store (static domains, as `twiny` does), not Matplotlib's live Grouper; `get_shared_x_axes()` reflects it | diff --git a/docs/matplotlib-shim-todo.md b/docs/matplotlib-shim-todo.md index 65206edf..7e5b6dae 100644 --- a/docs/matplotlib-shim-todo.md +++ b/docs/matplotlib-shim-todo.md @@ -629,8 +629,9 @@ streamplot quiver/barbs (plot/scatter/hist/bar still reject `data=`), unknown colormap names raise ValueError at every entry point including `set_cmap` (which now also feeds imshow/scatter defaults via `rcParams["image.cmap"]`), legend - layout options (borderpad/labelspacing/handlelength/handletextpad/ - title_fontsize) raise NotImplementedError instead of vanishing. + layout options `borderpad`/`labelspacing` now map to renderer spacing; + handlelength/handletextpad/title_fontsize still raise NotImplementedError + instead of vanishing. - Newer tranche (validation in progress, see matrix/compat doc): boxplot notch/bootstrap/user statistics, violin bandwidths/quantiles/sides, hexbin `C`/reducers/`mincnt`, symlog/logit/asinh scales, secondary axes, affine diff --git a/examples/pdsh/README.md b/examples/pdsh/README.md index a9b1d68c..b6735f83 100644 --- a/examples/pdsh/README.md +++ b/examples/pdsh/README.md @@ -6,6 +6,31 @@ with one systematic change: `import matplotlib.pyplot as plt` became `import xy.pyplot as plt`. They exist to answer, on popular real-world code, "can I just change the import?" +## Run the comparison in one notebook + +Every checked-in notebook contains both runs in the same file. Each Matplotlib +code cell is immediately followed by the matching `xy.pyplot` code cell, so the +two outputs appear together instead of in separate sections or notebooks. The +paired cells use distinct plotting aliases and are labeled in their first line. +`xy.pyplot` provides the Jupyter end-of-cell figure flush itself, so the example +cells do not need display workarounds. + +Open any original notebook, choose **Restart Kernel and Run All Cells**, and +compare each adjacent output pair in place. For example: + +```bash +cd examples/pdsh +jupyter lab pdsh_04_02_simple_scatter_plots.ipynb +``` + +The notebooks remain beside `data/`, so their existing relative data paths work +for both sections. There are no separate Matplotlib-reference notebooks. After +editing an xy example, regenerate its mirrored reference section with: + +```bash +.venv/bin/python examples/pdsh/sync_dual_engine_notebooks.py +``` + Only the MIT-licensed code cells are included (the book's prose is CC-BY-NC-ND and is omitted; section headings are kept for navigation). Besides the import swap, the code carries the same modernizations the @@ -30,11 +55,11 @@ signal. A "cell ok" also implies a non-empty `savefig` PNG export. | 04.03 Errorbars | 5/5 | 5/5 | | 04.04 Density and Contour Plots | 8/8 | 8/8 | | 04.05 Histograms and Binnings | 10/10 | 10/10 | -| 04.06 Customizing Legends | 11/11 | 8/11 | +| 04.06 Customizing Legends | 11/11 | 10/11 | | 04.07 Customizing Colorbars | 13/13 | 13/13 | -| 04.08 Multiple Subplots | 10/10 | 10/10 | +| 04.08 Multiple Subplots | 10/10 | 9/10 | | 04.09 Text and Annotation | 9/9 | 6/9 | -| 04.10 Customizing Ticks | 11/11 | 11/11 | +| 04.10 Customizing Ticks | 11/11 | 10/11 | | 04.11 Settings and Stylesheets | 15/15 | 15/15 | | 04.12 Three-Dimensional Plotting¹ | 17/17 | 7/17 | | 04.14 Visualization with Seaborn² | 31/31 | 30/31 | @@ -79,12 +104,22 @@ fallback onto matplotlib will ever be added). ## Remaining failures All 17 are one of: 3-D projection cells (10, loud rejections by -design), legend layout options `borderpad`/`labelspacing` and the -`Legend` class (3, documented loud rejections), pandas +design), the second-legend `Legend` class (1, a documented loud rejection), +`subplots_adjust` calls whose values the renderer cannot honor (2, now loud +rather than silently discarded), pandas `Series.plot(ax=ax)` datetime interop (3, a real gap — a dtype error inside the pandas plotting path), and `axhline(marker=)` via seaborn (1, loud rejection). +The 2026-07-14 fidelity follow-up also compared the affected PDSH cells +directly against Matplotlib 3.11: directional markers and `x`/`+` remain +distinct, contour labels repeat along levels, hexbins form a complete true +lattice, horizontal filled histograms use the correct value axis, and both +formerly rejected legend-style cells render. The final follow-up adds the 2.5 +automatic tick step with uniform decimal padding, occupancy-based +`loc='best'`, and Matplotlib's four-sided boxed-axes default across browser and +static exporters. + Data files under `data/` come from the handbook's repository (`births.csv`, `california_cities.csv`) and [jakevdp/marathon-data](https://github.com/jakevdp/marathon-data) diff --git a/examples/pdsh/pdsh_04_00_introduction_to_matplotlib.ipynb b/examples/pdsh/pdsh_04_00_introduction_to_matplotlib.ipynb index b32d420b..f859b8e5 100644 --- a/examples/pdsh/pdsh_04_00_introduction_to_matplotlib.ipynb +++ b/examples/pdsh/pdsh_04_00_introduction_to_matplotlib.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Introduction To Matplotlib — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Visualization with Matplotlib" ] @@ -22,7 +42,11 @@ { "cell_type": "markdown", "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "source": [ "# General Matplotlib Tips" ] @@ -30,7 +54,11 @@ { "cell_type": "markdown", "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "source": [ "## Importing Matplotlib" ] @@ -38,18 +66,41 @@ { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ - "import xy.pyplot as mpl\n", - "import xy.pyplot as plt" + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_mpl\n", + "import matplotlib.pyplot as mpl_plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "import xy.pyplot as xy_mpl\n", + "import xy.pyplot as xy_plt" ] }, { "cell_type": "markdown", "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "source": [ "## Setting Styles" ] @@ -57,17 +108,39 @@ { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.style.use(\"default\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "outputs": [], "source": [ - "plt.style.use(\"default\")" + "# xy.pyplot\n", + "xy_plt.style.use(\"default\")" ] }, { "cell_type": "markdown", "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "source": [ "## show or No show? How to Display Your Plots" ] @@ -75,7 +148,11 @@ { "cell_type": "markdown", "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "source": [ "### Plotting from a Script" ] @@ -83,7 +160,11 @@ { "cell_type": "markdown", "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "source": [ "### Plotting from an IPython Shell" ] @@ -91,7 +172,11 @@ { "cell_type": "markdown", "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, "source": [ "### Plotting from a Jupyter Notebook" ] @@ -99,23 +184,51 @@ { "cell_type": "code", "execution_count": null, - "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "id": "mpl-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import numpy as np\n", + "\n", + "x = np.linspace(0, 10, 100)\n", + "\n", + "fig = mpl_plt.figure()\n", + "mpl_plt.plot(x, np.sin(x), \"-\")\n", + "mpl_plt.plot(x, np.cos(x), \"--\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "import numpy as np\n", "\n", "x = np.linspace(0, 10, 100)\n", "\n", - "fig = plt.figure()\n", - "plt.plot(x, np.sin(x), \"-\")\n", - "plt.plot(x, np.cos(x), \"--\");" + "fig = xy_plt.figure()\n", + "xy_plt.plot(x, np.sin(x), \"-\")\n", + "xy_plt.plot(x, np.cos(x), \"--\");" ] }, { "cell_type": "markdown", "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "source": [ "### Saving Figures to File" ] @@ -123,20 +236,42 @@ { "cell_type": "code", "execution_count": null, - "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "id": "mpl-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "fig.savefig(\"my_figure.png\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, + "id": "xy-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "fig.savefig(\"my_figure.png\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", "from IPython.display import Image\n", "\n", "Image(\"my_figure.png\")" @@ -145,17 +280,55 @@ { "cell_type": "code", "execution_count": null, - "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, + "id": "xy-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "from IPython.display import Image\n", + "\n", + "Image(\"my_figure.png\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "fig.canvas.get_supported_filetypes()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "fig.canvas.get_supported_filetypes()" ] }, { "cell_type": "markdown", "id": "c3933fab20d04ec698c2621248eb3be0", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "c3933fab20d04ec698c2621248eb3be0" + }, "source": [ "### Two Interfaces for the Price of One" ] @@ -163,7 +336,11 @@ { "cell_type": "markdown", "id": "4dd4641cc4064e0191573fe9c69df29b", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, "source": [ "#### MATLAB-style Interface" ] @@ -171,25 +348,55 @@ { "cell_type": "code", "execution_count": null, - "id": "8309879909854d7188b41380fd92a7c3", - "metadata": {}, + "id": "mpl-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, "outputs": [], "source": [ - "plt.figure() # create a plot figure\n", + "# Matplotlib reference\n", + "mpl_plt.figure() # create a plot figure\n", "\n", "# create the first of two panels and set current axis\n", - "plt.subplot(2, 1, 1) # (rows, columns, panel number)\n", - "plt.plot(x, np.sin(x))\n", + "mpl_plt.subplot(2, 1, 1) # (rows, columns, panel number)\n", + "mpl_plt.plot(x, np.sin(x))\n", "\n", "# create the second panel and set current axis\n", - "plt.subplot(2, 1, 2)\n", - "plt.plot(x, np.cos(x));" + "mpl_plt.subplot(2, 1, 2)\n", + "mpl_plt.plot(x, np.cos(x));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.figure() # create a plot figure\n", + "\n", + "# create the first of two panels and set current axis\n", + "xy_plt.subplot(2, 1, 1) # (rows, columns, panel number)\n", + "xy_plt.plot(x, np.sin(x))\n", + "\n", + "# create the second panel and set current axis\n", + "xy_plt.subplot(2, 1, 2)\n", + "xy_plt.plot(x, np.cos(x));" ] }, { "cell_type": "markdown", "id": "3ed186c9a28b402fb0bc4494df01f08d", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "3ed186c9a28b402fb0bc4494df01f08d" + }, "source": [ "#### Object-oriented interface" ] @@ -197,13 +404,37 @@ { "cell_type": "code", "execution_count": null, - "id": "cb1e1581032b452c9409d6c6813c49d1", - "metadata": {}, + "id": "mpl-cb1e1581032b452c9409d6c6813c49d1", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "cb1e1581032b452c9409d6c6813c49d1" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "# First create a grid of plots\n", + "# ax will be an array of two Axes objects\n", + "fig, ax = mpl_plt.subplots(2)\n", + "\n", + "# Call plot() method on the appropriate object\n", + "ax[0].plot(x, np.sin(x))\n", + "ax[1].plot(x, np.cos(x));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-cb1e1581032b452c9409d6c6813c49d1", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "cb1e1581032b452c9409d6c6813c49d1" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "# First create a grid of plots\n", "# ax will be an array of two Axes objects\n", - "fig, ax = plt.subplots(2)\n", + "fig, ax = xy_plt.subplots(2)\n", "\n", "# Call plot() method on the appropriate object\n", "ax[0].plot(x, np.sin(x))\n", @@ -219,7 +450,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_01_simple_line_plots.ipynb b/examples/pdsh/pdsh_04_01_simple_line_plots.ipynb index 1e5c7949..16a3d8f2 100644 --- a/examples/pdsh/pdsh_04_01_simple_line_plots.ipynb +++ b/examples/pdsh/pdsh_04_01_simple_line_plots.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Simple Line Plots — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Simple Line Plots" ] @@ -22,36 +42,98 @@ { "cell_type": "code", "execution_count": null, - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "id": "mpl-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", + "\n", + "mpl_plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", "\n", - "plt.style.use(\"default\")\n", + "xy_plt.style.use(\"default\")\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, - "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "id": "mpl-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "outputs": [], "source": [ - "fig = plt.figure()\n", - "ax = plt.axes()" + "# Matplotlib reference\n", + "fig = mpl_plt.figure()\n", + "ax = mpl_plt.axes()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "fig = xy_plt.figure()\n", + "ax = xy_plt.axes()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "fig = mpl_plt.figure()\n", + "ax = mpl_plt.axes()\n", + "\n", + "x = np.linspace(0, 10, 1000)\n", + "ax.plot(x, np.sin(x));" ] }, { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ - "fig = plt.figure()\n", - "ax = plt.axes()\n", + "# xy.pyplot\n", + "fig = xy_plt.figure()\n", + "ax = xy_plt.axes()\n", "\n", "x = np.linspace(0, 10, 1000)\n", "ax.plot(x, np.sin(x));" @@ -60,28 +142,69 @@ { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.plot(x, np.sin(x));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ - "plt.plot(x, np.sin(x));" + "# xy.pyplot\n", + "xy_plt.plot(x, np.sin(x));" ] }, { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "outputs": [], "source": [ - "plt.plot(x, np.sin(x))\n", - "plt.plot(x, np.cos(x));" + "# Matplotlib reference\n", + "mpl_plt.plot(x, np.sin(x))\n", + "mpl_plt.plot(x, np.cos(x));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.plot(x, np.sin(x))\n", + "xy_plt.plot(x, np.cos(x));" ] }, { "cell_type": "markdown", "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "source": [ "## Adjusting the Plot: Line Colors and Styles" ] @@ -89,53 +212,127 @@ { "cell_type": "code", "execution_count": null, - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "id": "mpl-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "outputs": [], "source": [ - "plt.plot(x, np.sin(x - 0), color=\"blue\") # specify color by name\n", - "plt.plot(x, np.sin(x - 1), color=\"g\") # short color code (rgbcmyk)\n", - "plt.plot(x, np.sin(x - 2), color=\"0.75\") # grayscale between 0 and 1\n", - "plt.plot(x, np.sin(x - 3), color=\"#FFDD44\") # hex code (RRGGBB, 00 to FF)\n", - "plt.plot(x, np.sin(x - 4), color=(1.0, 0.2, 0.3)) # RGB tuple, values 0 to 1\n", - "plt.plot(x, np.sin(x - 5), color=\"chartreuse\"); # HTML color names supported" + "# Matplotlib reference\n", + "mpl_plt.plot(x, np.sin(x - 0), color=\"blue\") # specify color by name\n", + "mpl_plt.plot(x, np.sin(x - 1), color=\"g\") # short color code (rgbcmyk)\n", + "mpl_plt.plot(x, np.sin(x - 2), color=\"0.75\") # grayscale between 0 and 1\n", + "mpl_plt.plot(x, np.sin(x - 3), color=\"#FFDD44\") # hex code (RRGGBB, 00 to FF)\n", + "mpl_plt.plot(x, np.sin(x - 4), color=(1.0, 0.2, 0.3)) # RGB tuple, values 0 to 1\n", + "mpl_plt.plot(x, np.sin(x - 5), color=\"chartreuse\"); # HTML color names supported" ] }, { "cell_type": "code", "execution_count": null, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "id": "xy-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "outputs": [], "source": [ - "plt.plot(x, x + 0, linestyle=\"solid\")\n", - "plt.plot(x, x + 1, linestyle=\"dashed\")\n", - "plt.plot(x, x + 2, linestyle=\"dashdot\")\n", - "plt.plot(x, x + 3, linestyle=\"dotted\")\n", + "# xy.pyplot\n", + "xy_plt.plot(x, np.sin(x - 0), color=\"blue\") # specify color by name\n", + "xy_plt.plot(x, np.sin(x - 1), color=\"g\") # short color code (rgbcmyk)\n", + "xy_plt.plot(x, np.sin(x - 2), color=\"0.75\") # grayscale between 0 and 1\n", + "xy_plt.plot(x, np.sin(x - 3), color=\"#FFDD44\") # hex code (RRGGBB, 00 to FF)\n", + "xy_plt.plot(x, np.sin(x - 4), color=(1.0, 0.2, 0.3)) # RGB tuple, values 0 to 1\n", + "xy_plt.plot(x, np.sin(x - 5), color=\"chartreuse\"); # HTML color names supported" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.plot(x, x + 0, linestyle=\"solid\")\n", + "mpl_plt.plot(x, x + 1, linestyle=\"dashed\")\n", + "mpl_plt.plot(x, x + 2, linestyle=\"dashdot\")\n", + "mpl_plt.plot(x, x + 3, linestyle=\"dotted\")\n", "# For short, you can use the following codes:\n", - "plt.plot(x, x + 4, linestyle=\"-\") # solid\n", - "plt.plot(x, x + 5, linestyle=\"--\") # dashed\n", - "plt.plot(x, x + 6, linestyle=\"-.\") # dashdot\n", - "plt.plot(x, x + 7, linestyle=\":\"); # dotted" + "mpl_plt.plot(x, x + 4, linestyle=\"-\") # solid\n", + "mpl_plt.plot(x, x + 5, linestyle=\"--\") # dashed\n", + "mpl_plt.plot(x, x + 6, linestyle=\"-.\") # dashdot\n", + "mpl_plt.plot(x, x + 7, linestyle=\":\"); # dotted" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.plot(x, x + 0, linestyle=\"solid\")\n", + "xy_plt.plot(x, x + 1, linestyle=\"dashed\")\n", + "xy_plt.plot(x, x + 2, linestyle=\"dashdot\")\n", + "xy_plt.plot(x, x + 3, linestyle=\"dotted\")\n", + "# For short, you can use the following codes:\n", + "xy_plt.plot(x, x + 4, linestyle=\"-\") # solid\n", + "xy_plt.plot(x, x + 5, linestyle=\"--\") # dashed\n", + "xy_plt.plot(x, x + 6, linestyle=\"-.\") # dashdot\n", + "xy_plt.plot(x, x + 7, linestyle=\":\"); # dotted" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.plot(x, x + 0, \"-g\") # solid green\n", + "mpl_plt.plot(x, x + 1, \"--c\") # dashed cyan\n", + "mpl_plt.plot(x, x + 2, \"-.k\") # dashdot black\n", + "mpl_plt.plot(x, x + 3, \":r\"); # dotted red" ] }, { "cell_type": "code", "execution_count": null, - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "id": "xy-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, "outputs": [], "source": [ - "plt.plot(x, x + 0, \"-g\") # solid green\n", - "plt.plot(x, x + 1, \"--c\") # dashed cyan\n", - "plt.plot(x, x + 2, \"-.k\") # dashdot black\n", - "plt.plot(x, x + 3, \":r\"); # dotted red" + "# xy.pyplot\n", + "xy_plt.plot(x, x + 0, \"-g\") # solid green\n", + "xy_plt.plot(x, x + 1, \"--c\") # dashed cyan\n", + "xy_plt.plot(x, x + 2, \"-.k\") # dashdot black\n", + "xy_plt.plot(x, x + 3, \":r\"); # dotted red" ] }, { "cell_type": "markdown", "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "source": [ "## Adjusting the Plot: Axes Limits" ] @@ -143,55 +340,139 @@ { "cell_type": "code", "execution_count": null, - "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "id": "mpl-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.plot(x, np.sin(x))\n", + "\n", + "mpl_plt.xlim(-1, 11)\n", + "mpl_plt.ylim(-1.5, 1.5);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "outputs": [], "source": [ - "plt.plot(x, np.sin(x))\n", + "# xy.pyplot\n", + "xy_plt.plot(x, np.sin(x))\n", "\n", - "plt.xlim(-1, 11)\n", - "plt.ylim(-1.5, 1.5);" + "xy_plt.xlim(-1, 11)\n", + "xy_plt.ylim(-1.5, 1.5);" ] }, { "cell_type": "code", "execution_count": null, - "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "id": "mpl-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "outputs": [], "source": [ - "plt.plot(x, np.sin(x))\n", + "# Matplotlib reference\n", + "mpl_plt.plot(x, np.sin(x))\n", "\n", - "plt.xlim(10, 0)\n", - "plt.ylim(1.2, -1.2);" + "mpl_plt.xlim(10, 0)\n", + "mpl_plt.ylim(1.2, -1.2);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.plot(x, np.sin(x))\n", + "\n", + "xy_plt.xlim(10, 0)\n", + "xy_plt.ylim(1.2, -1.2);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.plot(x, np.sin(x))\n", + "mpl_plt.axis(\"tight\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.plot(x, np.sin(x))\n", + "xy_plt.axis(\"tight\");" ] }, { "cell_type": "code", "execution_count": null, - "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, + "id": "mpl-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, "outputs": [], "source": [ - "plt.plot(x, np.sin(x))\n", - "plt.axis(\"tight\");" + "# Matplotlib reference\n", + "mpl_plt.plot(x, np.sin(x))\n", + "mpl_plt.axis(\"equal\");" ] }, { "cell_type": "code", "execution_count": null, - "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, + "id": "xy-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, "outputs": [], "source": [ - "plt.plot(x, np.sin(x))\n", - "plt.axis(\"equal\");" + "# xy.pyplot\n", + "xy_plt.plot(x, np.sin(x))\n", + "xy_plt.axis(\"equal\");" ] }, { "cell_type": "markdown", "id": "c3933fab20d04ec698c2621248eb3be0", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "c3933fab20d04ec698c2621248eb3be0" + }, "source": [ "## Labeling Plots" ] @@ -199,34 +480,81 @@ { "cell_type": "code", "execution_count": null, - "id": "4dd4641cc4064e0191573fe9c69df29b", - "metadata": {}, + "id": "mpl-4dd4641cc4064e0191573fe9c69df29b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.plot(x, np.sin(x))\n", + "mpl_plt.title(\"A Sine Curve\")\n", + "mpl_plt.xlabel(\"x\")\n", + "mpl_plt.ylabel(\"sin(x)\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-4dd4641cc4064e0191573fe9c69df29b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, "outputs": [], "source": [ - "plt.plot(x, np.sin(x))\n", - "plt.title(\"A Sine Curve\")\n", - "plt.xlabel(\"x\")\n", - "plt.ylabel(\"sin(x)\");" + "# xy.pyplot\n", + "xy_plt.plot(x, np.sin(x))\n", + "xy_plt.title(\"A Sine Curve\")\n", + "xy_plt.xlabel(\"x\")\n", + "xy_plt.ylabel(\"sin(x)\");" ] }, { "cell_type": "code", "execution_count": null, - "id": "8309879909854d7188b41380fd92a7c3", - "metadata": {}, + "id": "mpl-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, "outputs": [], "source": [ - "plt.plot(x, np.sin(x), \"-g\", label=\"sin(x)\")\n", - "plt.plot(x, np.cos(x), \":b\", label=\"cos(x)\")\n", - "plt.axis(\"equal\")\n", + "# Matplotlib reference\n", + "mpl_plt.plot(x, np.sin(x), \"-g\", label=\"sin(x)\")\n", + "mpl_plt.plot(x, np.cos(x), \":b\", label=\"cos(x)\")\n", + "mpl_plt.axis(\"equal\")\n", "\n", - "plt.legend();" + "mpl_plt.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.plot(x, np.sin(x), \"-g\", label=\"sin(x)\")\n", + "xy_plt.plot(x, np.cos(x), \":b\", label=\"cos(x)\")\n", + "xy_plt.axis(\"equal\")\n", + "\n", + "xy_plt.legend();" ] }, { "cell_type": "markdown", "id": "3ed186c9a28b402fb0bc4494df01f08d", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "3ed186c9a28b402fb0bc4494df01f08d" + }, "source": [ "## Matplotlib Gotchas" ] @@ -234,11 +562,31 @@ { "cell_type": "code", "execution_count": null, - "id": "cb1e1581032b452c9409d6c6813c49d1", - "metadata": {}, + "id": "mpl-cb1e1581032b452c9409d6c6813c49d1", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "cb1e1581032b452c9409d6c6813c49d1" + }, "outputs": [], "source": [ - "ax = plt.axes()\n", + "# Matplotlib reference\n", + "ax = mpl_plt.axes()\n", + "ax.plot(x, np.sin(x))\n", + "ax.set(xlim=(0, 10), ylim=(-2, 2), xlabel=\"x\", ylabel=\"sin(x)\", title=\"A Simple Plot\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-cb1e1581032b452c9409d6c6813c49d1", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "cb1e1581032b452c9409d6c6813c49d1" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "ax = xy_plt.axes()\n", "ax.plot(x, np.sin(x))\n", "ax.set(xlim=(0, 10), ylim=(-2, 2), xlabel=\"x\", ylabel=\"sin(x)\", title=\"A Simple Plot\");" ] @@ -252,7 +600,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_02_simple_scatter_plots.ipynb b/examples/pdsh/pdsh_04_02_simple_scatter_plots.ipynb index 39d1e3a1..6bb558b3 100644 --- a/examples/pdsh/pdsh_04_02_simple_scatter_plots.ipynb +++ b/examples/pdsh/pdsh_04_02_simple_scatter_plots.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Simple Scatter Plots — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Simple Scatter Plots" ] @@ -22,20 +42,45 @@ { "cell_type": "code", "execution_count": null, - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "id": "mpl-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", + "\n", + "mpl_plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", "\n", - "plt.style.use(\"default\")\n", + "xy_plt.style.use(\"default\")\n", "import numpy as np" ] }, { "cell_type": "markdown", "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "source": [ "## Scatter Plots with plt.plot" ] @@ -43,50 +88,142 @@ { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "x = np.linspace(0, 10, 30)\n", + "y = np.sin(x)\n", + "\n", + "mpl_plt.plot(x, y, \"o\", color=\"black\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "x = np.linspace(0, 10, 30)\n", "y = np.sin(x)\n", "\n", - "plt.plot(x, y, \"o\", color=\"black\");" + "xy_plt.plot(x, y, \"o\", color=\"black\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "rng = np.random.default_rng(0)\n", + "for marker in [\"o\", \".\", \",\", \"x\", \"+\", \"v\", \"^\", \"<\", \">\", \"s\", \"d\"]:\n", + " mpl_plt.plot(\n", + " rng.random(2), rng.random(2), marker, color=\"black\", label=\"marker='{0}'\".format(marker)\n", + " )\n", + "mpl_plt.legend(numpoints=1, fontsize=13)\n", + "mpl_plt.xlim(0, 1.8);" ] }, { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "rng = np.random.default_rng(0)\n", "for marker in [\"o\", \".\", \",\", \"x\", \"+\", \"v\", \"^\", \"<\", \">\", \"s\", \"d\"]:\n", - " plt.plot(\n", + " xy_plt.plot(\n", " rng.random(2), rng.random(2), marker, color=\"black\", label=\"marker='{0}'\".format(marker)\n", " )\n", - "plt.legend(numpoints=1, fontsize=13)\n", - "plt.xlim(0, 1.8);" + "xy_plt.legend(numpoints=1, fontsize=13)\n", + "xy_plt.xlim(0, 1.8);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.plot(x, y, \"-ok\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.plot(x, y, \"-ok\");" ] }, { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "mpl-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "outputs": [], "source": [ - "plt.plot(x, y, \"-ok\");" + "# Matplotlib reference\n", + "mpl_plt.plot(\n", + " x,\n", + " y,\n", + " \"-p\",\n", + " color=\"gray\",\n", + " markersize=15,\n", + " linewidth=4,\n", + " markerfacecolor=\"white\",\n", + " markeredgecolor=\"gray\",\n", + " markeredgewidth=2,\n", + ")\n", + "mpl_plt.ylim(-1.2, 1.2);" ] }, { "cell_type": "code", "execution_count": null, - "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "id": "xy-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "outputs": [], "source": [ - "plt.plot(\n", + "# xy.pyplot\n", + "xy_plt.plot(\n", " x,\n", " y,\n", " \"-p\",\n", @@ -97,13 +234,17 @@ " markeredgecolor=\"gray\",\n", " markeredgewidth=2,\n", ")\n", - "plt.ylim(-1.2, 1.2);" + "xy_plt.ylim(-1.2, 1.2);" ] }, { "cell_type": "markdown", "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "source": [ "## Scatter Plots with plt.scatter" ] @@ -111,51 +252,127 @@ { "cell_type": "code", "execution_count": null, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "id": "mpl-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.scatter(x, y, marker=\"o\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.scatter(x, y, marker=\"o\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, "outputs": [], "source": [ - "plt.scatter(x, y, marker=\"o\");" + "# Matplotlib reference\n", + "rng = np.random.default_rng(0)\n", + "x = rng.normal(size=100)\n", + "y = rng.normal(size=100)\n", + "colors = rng.random(100)\n", + "sizes = 1000 * rng.random(100)\n", + "\n", + "mpl_plt.scatter(x, y, c=colors, s=sizes, alpha=0.3)\n", + "mpl_plt.colorbar(); # show color scale" ] }, { "cell_type": "code", "execution_count": null, - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "id": "xy-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "rng = np.random.default_rng(0)\n", "x = rng.normal(size=100)\n", "y = rng.normal(size=100)\n", "colors = rng.random(100)\n", "sizes = 1000 * rng.random(100)\n", "\n", - "plt.scatter(x, y, c=colors, s=sizes, alpha=0.3)\n", - "plt.colorbar(); # show color scale" + "xy_plt.scatter(x, y, c=colors, s=sizes, alpha=0.3)\n", + "xy_plt.colorbar(); # show color scale" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "from sklearn.datasets import load_iris\n", + "\n", + "iris = load_iris()\n", + "features = iris.data.T\n", + "\n", + "mpl_plt.scatter(\n", + " features[0], features[1], alpha=0.4, s=100 * features[3], c=iris.target, cmap=\"viridis\"\n", + ")\n", + "mpl_plt.xlabel(iris.feature_names[0])\n", + "mpl_plt.ylabel(iris.feature_names[1]);" ] }, { "cell_type": "code", "execution_count": null, - "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "id": "xy-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "from sklearn.datasets import load_iris\n", "\n", "iris = load_iris()\n", "features = iris.data.T\n", "\n", - "plt.scatter(features[0], features[1], alpha=0.4, s=100 * features[3], c=iris.target, cmap=\"viridis\")\n", - "plt.xlabel(iris.feature_names[0])\n", - "plt.ylabel(iris.feature_names[1]);" + "xy_plt.scatter(\n", + " features[0], features[1], alpha=0.4, s=100 * features[3], c=iris.target, cmap=\"viridis\"\n", + ")\n", + "xy_plt.xlabel(iris.feature_names[0])\n", + "xy_plt.ylabel(iris.feature_names[1]);" ] }, { "cell_type": "markdown", "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "source": [ "## plot Versus scatter: A Note on Efficiency" ] @@ -169,7 +386,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_03_errorbars.ipynb b/examples/pdsh/pdsh_04_03_errorbars.ipynb index aa40514d..b637bf4e 100644 --- a/examples/pdsh/pdsh_04_03_errorbars.ipynb +++ b/examples/pdsh/pdsh_04_03_errorbars.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Errorbars — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Visualizing Uncertainties" ] @@ -22,7 +42,11 @@ { "cell_type": "markdown", "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "source": [ "## Basic Errorbars" ] @@ -30,44 +54,109 @@ { "cell_type": "code", "execution_count": null, - "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "id": "mpl-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", + "\n", + "mpl_plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", "\n", - "plt.style.use(\"default\")\n", + "xy_plt.style.use(\"default\")\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "x = np.linspace(0, 10, 50)\n", + "dy = 0.8\n", + "y = np.sin(x) + dy * np.random.randn(50)\n", + "\n", + "mpl_plt.errorbar(x, y, yerr=dy, fmt=\".k\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "x = np.linspace(0, 10, 50)\n", "dy = 0.8\n", "y = np.sin(x) + dy * np.random.randn(50)\n", "\n", - "plt.errorbar(x, y, yerr=dy, fmt=\".k\");" + "xy_plt.errorbar(x, y, yerr=dy, fmt=\".k\");" ] }, { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ - "plt.errorbar(x, y, yerr=dy, fmt=\"o\", color=\"black\", ecolor=\"lightgray\", elinewidth=3, capsize=0);" + "# Matplotlib reference\n", + "mpl_plt.errorbar(x, y, yerr=dy, fmt=\"o\", color=\"black\", ecolor=\"lightgray\", elinewidth=3, capsize=0);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.errorbar(x, y, yerr=dy, fmt=\"o\", color=\"black\", ecolor=\"lightgray\", elinewidth=3, capsize=0);" ] }, { "cell_type": "markdown", "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "source": [ "## Continuous Errors" ] @@ -75,10 +164,14 @@ { "cell_type": "code", "execution_count": null, - "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "id": "mpl-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "from sklearn.gaussian_process import GaussianProcessRegressor\n", "\n", "# define the model and draw some data\n", @@ -97,15 +190,63 @@ { "cell_type": "code", "execution_count": null, - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "id": "xy-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "from sklearn.gaussian_process import GaussianProcessRegressor\n", + "\n", + "# define the model and draw some data\n", + "model = lambda x: x * np.sin(x)\n", + "xdata = np.array([1, 3, 5, 6, 8])\n", + "ydata = model(xdata)\n", + "\n", + "# Compute the Gaussian process fit\n", + "gp = GaussianProcessRegressor()\n", + "gp.fit(xdata[:, np.newaxis], ydata)\n", + "\n", + "xfit = np.linspace(0, 10, 1000)\n", + "yfit, dyfit = gp.predict(xfit[:, np.newaxis], return_std=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", "# Visualize the result\n", - "plt.plot(xdata, ydata, \"or\")\n", - "plt.plot(xfit, yfit, \"-\", color=\"gray\")\n", - "plt.fill_between(xfit, yfit - dyfit, yfit + dyfit, color=\"gray\", alpha=0.2)\n", - "plt.xlim(0, 10);" + "mpl_plt.plot(xdata, ydata, \"or\")\n", + "mpl_plt.plot(xfit, yfit, \"-\", color=\"gray\")\n", + "mpl_plt.fill_between(xfit, yfit - dyfit, yfit + dyfit, color=\"gray\", alpha=0.2)\n", + "mpl_plt.xlim(0, 10);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "# Visualize the result\n", + "xy_plt.plot(xdata, ydata, \"or\")\n", + "xy_plt.plot(xfit, yfit, \"-\", color=\"gray\")\n", + "xy_plt.fill_between(xfit, yfit - dyfit, yfit + dyfit, color=\"gray\", alpha=0.2)\n", + "xy_plt.xlim(0, 10);" ] } ], @@ -117,7 +258,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_04_density_and_contour_plots.ipynb b/examples/pdsh/pdsh_04_04_density_and_contour_plots.ipynb index 05ef6d45..c9904cb9 100644 --- a/examples/pdsh/pdsh_04_04_density_and_contour_plots.ipynb +++ b/examples/pdsh/pdsh_04_04_density_and_contour_plots.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Density and Contour Plots — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Density and Contour Plots" ] @@ -22,20 +42,45 @@ { "cell_type": "code", "execution_count": null, - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "id": "mpl-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", "\n", - "plt.style.use(\"default\")\n", + "mpl_plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", + "\n", + "xy_plt.style.use(\"default\")\n", "import numpy as np" ] }, { "cell_type": "markdown", "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "source": [ "## Visualizing a Three-Dimensional Function" ] @@ -43,10 +88,29 @@ { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "def f(x, y):\n", + " return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "def f(x, y):\n", " return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)" ] @@ -54,10 +118,14 @@ { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "x = np.linspace(0, 5, 50)\n", "y = np.linspace(0, 5, 40)\n", "\n", @@ -68,59 +136,175 @@ { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "x = np.linspace(0, 5, 50)\n", + "y = np.linspace(0, 5, 40)\n", + "\n", + "X, Y = np.meshgrid(x, y)\n", + "Z = f(X, Y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.contour(X, Y, Z, colors=\"black\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.contour(X, Y, Z, colors=\"black\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.contour(X, Y, Z, 20, cmap=\"RdGy\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "outputs": [], "source": [ - "plt.contour(X, Y, Z, colors=\"black\");" + "# xy.pyplot\n", + "xy_plt.contour(X, Y, Z, 20, cmap=\"RdGy\");" ] }, { "cell_type": "code", "execution_count": null, - "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "id": "mpl-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "outputs": [], "source": [ - "plt.contour(X, Y, Z, 20, cmap=\"RdGy\");" + "# Matplotlib reference\n", + "mpl_plt.contourf(X, Y, Z, 20, cmap=\"RdGy\")\n", + "mpl_plt.colorbar();" ] }, { "cell_type": "code", "execution_count": null, - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "id": "xy-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "outputs": [], "source": [ - "plt.contourf(X, Y, Z, 20, cmap=\"RdGy\")\n", - "plt.colorbar();" + "# xy.pyplot\n", + "xy_plt.contourf(X, Y, Z, 20, cmap=\"RdGy\")\n", + "xy_plt.colorbar();" ] }, { "cell_type": "code", "execution_count": null, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "id": "mpl-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ - "plt.imshow(\n", + "# Matplotlib reference\n", + "mpl_plt.imshow(\n", " Z, extent=[0, 5, 0, 5], origin=\"lower\", cmap=\"RdGy\", interpolation=\"gaussian\", aspect=\"equal\"\n", ")\n", - "plt.colorbar();" + "mpl_plt.colorbar();" ] }, { "cell_type": "code", "execution_count": null, - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "id": "xy-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ - "contours = plt.contour(X, Y, Z, 3, colors=\"black\")\n", - "plt.clabel(contours, inline=True, fontsize=8)\n", + "# xy.pyplot\n", + "xy_plt.imshow(\n", + " Z, extent=[0, 5, 0, 5], origin=\"lower\", cmap=\"RdGy\", interpolation=\"gaussian\", aspect=\"equal\"\n", + ")\n", + "xy_plt.colorbar();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "contours = mpl_plt.contour(X, Y, Z, 3, colors=\"black\")\n", + "mpl_plt.clabel(contours, inline=True, fontsize=8)\n", "\n", - "plt.imshow(Z, extent=[0, 5, 0, 5], origin=\"lower\", cmap=\"RdGy\", alpha=0.5)\n", - "plt.colorbar();" + "mpl_plt.imshow(Z, extent=[0, 5, 0, 5], origin=\"lower\", cmap=\"RdGy\", alpha=0.5)\n", + "mpl_plt.colorbar();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "contours = xy_plt.contour(X, Y, Z, 3, colors=\"black\")\n", + "xy_plt.clabel(contours, inline=True, fontsize=8)\n", + "\n", + "xy_plt.imshow(Z, extent=[0, 5, 0, 5], origin=\"lower\", cmap=\"RdGy\", alpha=0.5)\n", + "xy_plt.colorbar();" ] } ], @@ -132,7 +316,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_05_histograms_and_binnings.ipynb b/examples/pdsh/pdsh_04_05_histograms_and_binnings.ipynb index 10dfbf07..412b956a 100644 --- a/examples/pdsh/pdsh_04_05_histograms_and_binnings.ipynb +++ b/examples/pdsh/pdsh_04_05_histograms_and_binnings.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Histograms and Binnings — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Histograms, Binnings, and Density" ] @@ -22,14 +42,38 @@ { "cell_type": "code", "execution_count": null, - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "id": "mpl-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import numpy as np\n", + "import matplotlib.pyplot as mpl_plt\n", + "\n", + "mpl_plt.style.use(\"default\")\n", + "\n", + "rng = np.random.default_rng(1701)\n", + "data = rng.normal(size=1000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "import numpy as np\n", - "import xy.pyplot as plt\n", + "import xy.pyplot as xy_plt\n", "\n", - "plt.style.use(\"default\")\n", + "xy_plt.style.use(\"default\")\n", "\n", "rng = np.random.default_rng(1701)\n", "data = rng.normal(size=1000)" @@ -38,21 +82,65 @@ { "cell_type": "code", "execution_count": null, - "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "id": "mpl-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.hist(data);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.hist(data);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ - "plt.hist(data);" + "# Matplotlib reference\n", + "mpl_plt.hist(\n", + " data,\n", + " bins=30,\n", + " density=True,\n", + " alpha=0.5,\n", + " histtype=\"stepfilled\",\n", + " color=\"steelblue\",\n", + " edgecolor=\"none\",\n", + ");" ] }, { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ - "plt.hist(\n", + "# xy.pyplot\n", + "xy_plt.hist(\n", " data,\n", " bins=30,\n", " density=True,\n", @@ -66,28 +154,73 @@ { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "x1 = rng.normal(0, 0.8, 1000)\n", + "x2 = rng.normal(-2, 1, 1000)\n", + "x3 = rng.normal(3, 2, 1000)\n", + "\n", + "kwargs = dict(histtype=\"stepfilled\", alpha=0.3, density=True, bins=40)\n", + "\n", + "mpl_plt.hist(x1, **kwargs)\n", + "mpl_plt.hist(x2, **kwargs)\n", + "mpl_plt.hist(x3, **kwargs);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "x1 = rng.normal(0, 0.8, 1000)\n", "x2 = rng.normal(-2, 1, 1000)\n", "x3 = rng.normal(3, 2, 1000)\n", "\n", "kwargs = dict(histtype=\"stepfilled\", alpha=0.3, density=True, bins=40)\n", "\n", - "plt.hist(x1, **kwargs)\n", - "plt.hist(x2, **kwargs)\n", - "plt.hist(x3, **kwargs);" + "xy_plt.hist(x1, **kwargs)\n", + "xy_plt.hist(x2, **kwargs)\n", + "xy_plt.hist(x3, **kwargs);" ] }, { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", + "counts, bin_edges = np.histogram(data, bins=5)\n", + "print(counts)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "counts, bin_edges = np.histogram(data, bins=5)\n", "print(counts)" ] @@ -95,7 +228,11 @@ { "cell_type": "markdown", "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "source": [ "## Two-Dimensional Histograms and Binnings" ] @@ -103,10 +240,30 @@ { "cell_type": "code", "execution_count": null, - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "id": "mpl-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", + "mean = [0, 0]\n", + "cov = [[1, 1], [1, 2]]\n", + "x, y = rng.multivariate_normal(mean, cov, 10000).T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "mean = [0, 0]\n", "cov = [[1, 1], [1, 2]]\n", "x, y = rng.multivariate_normal(mean, cov, 10000).T" @@ -115,7 +272,11 @@ { "cell_type": "markdown", "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "source": [ "### plt.hist2d: Two-dimensional histogram" ] @@ -123,22 +284,61 @@ { "cell_type": "code", "execution_count": null, - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "id": "mpl-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.hist2d(x, y, bins=30)\n", + "cb = mpl_plt.colorbar()\n", + "cb.set_label(\"counts in bin\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, "outputs": [], "source": [ - "plt.hist2d(x, y, bins=30)\n", - "cb = plt.colorbar()\n", + "# xy.pyplot\n", + "xy_plt.hist2d(x, y, bins=30)\n", + "cb = xy_plt.colorbar()\n", "cb.set_label(\"counts in bin\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "id": "mpl-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", + "counts, xedges, yedges = np.histogram2d(x, y, bins=30)\n", + "print(counts.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "counts, xedges, yedges = np.histogram2d(x, y, bins=30)\n", "print(counts.shape)" ] @@ -146,7 +346,11 @@ { "cell_type": "markdown", "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "source": [ "### plt.hexbin: Hexagonal binnings" ] @@ -154,18 +358,41 @@ { "cell_type": "code", "execution_count": null, - "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "id": "mpl-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "outputs": [], "source": [ - "plt.hexbin(x, y, gridsize=30)\n", - "cb = plt.colorbar(label=\"count in bin\")" + "# Matplotlib reference\n", + "mpl_plt.hexbin(x, y, gridsize=30)\n", + "cb = mpl_plt.colorbar(label=\"count in bin\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.hexbin(x, y, gridsize=30)\n", + "cb = xy_plt.colorbar(label=\"count in bin\")" ] }, { "cell_type": "markdown", "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, "source": [ "### Kernel density estimation" ] @@ -173,10 +400,14 @@ { "cell_type": "code", "execution_count": null, - "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, + "id": "mpl-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "from scipy.stats import gaussian_kde\n", "\n", "# fit an array of size [Ndim, Nsamples]\n", @@ -190,8 +421,37 @@ "Z = kde.evaluate(np.vstack([Xgrid.ravel(), Ygrid.ravel()]))\n", "\n", "# Plot the result as an image\n", - "plt.imshow(Z.reshape(Xgrid.shape), origin=\"lower\", aspect=\"auto\", extent=[-3.5, 3.5, -6, 6])\n", - "cb = plt.colorbar()\n", + "mpl_plt.imshow(Z.reshape(Xgrid.shape), origin=\"lower\", aspect=\"auto\", extent=[-3.5, 3.5, -6, 6])\n", + "cb = mpl_plt.colorbar()\n", + "cb.set_label(\"density\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "from scipy.stats import gaussian_kde\n", + "\n", + "# fit an array of size [Ndim, Nsamples]\n", + "data = np.vstack([x, y])\n", + "kde = gaussian_kde(data)\n", + "\n", + "# evaluate on a regular grid\n", + "xgrid = np.linspace(-3.5, 3.5, 40)\n", + "ygrid = np.linspace(-6, 6, 40)\n", + "Xgrid, Ygrid = np.meshgrid(xgrid, ygrid)\n", + "Z = kde.evaluate(np.vstack([Xgrid.ravel(), Ygrid.ravel()]))\n", + "\n", + "# Plot the result as an image\n", + "xy_plt.imshow(Z.reshape(Xgrid.shape), origin=\"lower\", aspect=\"auto\", extent=[-3.5, 3.5, -6, 6])\n", + "cb = xy_plt.colorbar()\n", "cb.set_label(\"density\")" ] } @@ -204,7 +464,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_06_customizing_legends.ipynb b/examples/pdsh/pdsh_04_06_customizing_legends.ipynb index 9d2c13f2..dcc2346b 100644 --- a/examples/pdsh/pdsh_04_06_customizing_legends.ipynb +++ b/examples/pdsh/pdsh_04_06_customizing_legends.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Customizing Legends — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Customizing Plot Legends" ] @@ -22,34 +42,95 @@ { "cell_type": "code", "execution_count": null, - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "id": "mpl-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", "\n", - "plt.style.use(\"default\")" + "mpl_plt.style.use(\"default\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", + "\n", + "xy_plt.style.use(\"default\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, - "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "id": "xy-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "x = np.linspace(0, 10, 1000)\n", + "fig, ax = mpl_plt.subplots()\n", + "ax.plot(x, np.sin(x), \"-b\", label=\"Sine\")\n", + "ax.plot(x, np.cos(x), \"--r\", label=\"Cosine\")\n", + "ax.axis(\"equal\")\n", + "leg = ax.legend()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "x = np.linspace(0, 10, 1000)\n", - "fig, ax = plt.subplots()\n", + "fig, ax = xy_plt.subplots()\n", "ax.plot(x, np.sin(x), \"-b\", label=\"Sine\")\n", "ax.plot(x, np.cos(x), \"--r\", label=\"Cosine\")\n", "ax.axis(\"equal\")\n", @@ -59,10 +140,14 @@ { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "ax.legend(loc=\"upper left\", frameon=True)\n", "fig" ] @@ -70,10 +155,44 @@ { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "ax.legend(loc=\"upper left\", frameon=True)\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "ax.legend(loc=\"lower center\", ncol=2)\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "ax.legend(loc=\"lower center\", ncol=2)\n", "fig" ] @@ -81,10 +200,29 @@ { "cell_type": "code", "execution_count": null, - "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "id": "mpl-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", + "ax.legend(frameon=True, fancybox=True, framealpha=1, shadow=True, borderpad=1)\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "ax.legend(frameon=True, fancybox=True, framealpha=1, shadow=True, borderpad=1)\n", "fig" ] @@ -92,7 +230,11 @@ { "cell_type": "markdown", "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "source": [ "## Choosing Elements for the Legend" ] @@ -100,34 +242,81 @@ { "cell_type": "code", "execution_count": null, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "id": "mpl-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "y = np.sin(x[:, np.newaxis] + np.pi * np.arange(0, 2, 0.5))\n", - "lines = plt.plot(x, y)\n", + "lines = mpl_plt.plot(x, y)\n", "\n", - "# lines is a list of plt.Line2D instances\n", - "plt.legend(lines[:2], [\"first\", \"second\"], frameon=True);" + "# lines is a list of mpl_plt.Line2D instances\n", + "mpl_plt.legend(lines[:2], [\"first\", \"second\"], frameon=True);" ] }, { "cell_type": "code", "execution_count": null, - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "id": "xy-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ - "plt.plot(x, y[:, 0], label=\"first\")\n", - "plt.plot(x, y[:, 1], label=\"second\")\n", - "plt.plot(x, y[:, 2:])\n", - "plt.legend(frameon=True);" + "# xy.pyplot\n", + "y = np.sin(x[:, np.newaxis] + np.pi * np.arange(0, 2, 0.5))\n", + "lines = xy_plt.plot(x, y)\n", + "\n", + "# lines is a list of xy_plt.Line2D instances\n", + "xy_plt.legend(lines[:2], [\"first\", \"second\"], frameon=True);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.plot(x, y[:, 0], label=\"first\")\n", + "mpl_plt.plot(x, y[:, 1], label=\"second\")\n", + "mpl_plt.plot(x, y[:, 2:])\n", + "mpl_plt.legend(frameon=True);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.plot(x, y[:, 0], label=\"first\")\n", + "xy_plt.plot(x, y[:, 1], label=\"second\")\n", + "xy_plt.plot(x, y[:, 2:])\n", + "xy_plt.legend(frameon=True);" ] }, { "cell_type": "markdown", "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "source": [ "## Legend for Size of Points" ] @@ -135,10 +324,31 @@ { "cell_type": "code", "execution_count": null, - "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "id": "mpl-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "# Uncomment to download the data\n", + "# url = ('https://raw.githubusercontent.com/jakevdp/PythonDataScienceHandbook/'\n", + "# 'master/notebooks/data/california_cities.csv')\n", + "# !cd data && curl -O {url}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "# Uncomment to download the data\n", "# url = ('https://raw.githubusercontent.com/jakevdp/PythonDataScienceHandbook/'\n", "# 'master/notebooks/data/california_cities.csv')\n", @@ -148,10 +358,14 @@ { "cell_type": "code", "execution_count": null, - "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "id": "mpl-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "import pandas as pd\n", "\n", "cities = pd.read_csv(\"data/california_cities.csv\")\n", @@ -161,28 +375,70 @@ "population, area = cities[\"population_total\"], cities[\"area_total_km2\"]\n", "\n", "# Scatter the points, using size and color but no label\n", - "plt.scatter(\n", + "mpl_plt.scatter(\n", " lon, lat, label=None, c=np.log10(population), cmap=\"viridis\", s=area, linewidth=0, alpha=0.5\n", ")\n", - "plt.axis(\"equal\")\n", - "plt.xlabel(\"longitude\")\n", - "plt.ylabel(\"latitude\")\n", - "plt.colorbar(label=\"log$_{10}$(population)\")\n", - "plt.clim(3, 7)\n", + "mpl_plt.axis(\"equal\")\n", + "mpl_plt.xlabel(\"longitude\")\n", + "mpl_plt.ylabel(\"latitude\")\n", + "mpl_plt.colorbar(label=\"log$_{10}$(population)\")\n", + "mpl_plt.clim(3, 7)\n", "\n", "# Here we create a legend:\n", "# we'll plot empty lists with the desired size and label\n", "for area in [100, 300, 500]:\n", - " plt.scatter([], [], c=\"k\", alpha=0.3, s=area, label=str(area) + \" km$^2$\")\n", - "plt.legend(scatterpoints=1, frameon=False, labelspacing=1, title=\"City Area\")\n", + " mpl_plt.scatter([], [], c=\"k\", alpha=0.3, s=area, label=str(area) + \" km$^2$\")\n", + "mpl_plt.legend(scatterpoints=1, frameon=False, labelspacing=1, title=\"City Area\")\n", "\n", - "plt.title(\"California Cities: Area and Population\");" + "mpl_plt.title(\"California Cities: Area and Population\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "import pandas as pd\n", + "\n", + "cities = pd.read_csv(\"data/california_cities.csv\")\n", + "\n", + "# Extract the data we're interested in\n", + "lat, lon = cities[\"latd\"], cities[\"longd\"]\n", + "population, area = cities[\"population_total\"], cities[\"area_total_km2\"]\n", + "\n", + "# Scatter the points, using size and color but no label\n", + "xy_plt.scatter(\n", + " lon, lat, label=None, c=np.log10(population), cmap=\"viridis\", s=area, linewidth=0, alpha=0.5\n", + ")\n", + "xy_plt.axis(\"equal\")\n", + "xy_plt.xlabel(\"longitude\")\n", + "xy_plt.ylabel(\"latitude\")\n", + "xy_plt.colorbar(label=\"log$_{10}$(population)\")\n", + "xy_plt.clim(3, 7)\n", + "\n", + "# Here we create a legend:\n", + "# we'll plot empty lists with the desired size and label\n", + "for area in [100, 300, 500]:\n", + " xy_plt.scatter([], [], c=\"k\", alpha=0.3, s=area, label=str(area) + \" km$^2$\")\n", + "xy_plt.legend(scatterpoints=1, frameon=False, labelspacing=1, title=\"City Area\")\n", + "\n", + "xy_plt.title(\"California Cities: Area and Population\");" ] }, { "cell_type": "markdown", "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, "source": [ "## Multiple Legends" ] @@ -190,11 +446,15 @@ { "cell_type": "code", "execution_count": null, - "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, + "id": "mpl-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, "outputs": [], "source": [ - "fig, ax = plt.subplots()\n", + "# Matplotlib reference\n", + "fig, ax = mpl_plt.subplots()\n", "\n", "lines = []\n", "styles = [\"-\", \"--\", \"-.\", \":\"]\n", @@ -208,9 +468,40 @@ "ax.legend(lines[:2], [\"line A\", \"line B\"], loc=\"upper right\")\n", "\n", "# Create the second legend and add the artist manually\n", - "from xy.pyplot import Legend # was matplotlib.legend\n", + "from matplotlib.legend import Legend as mpl_Legend # was matplotlib.legend\n", "\n", - "leg = Legend(ax, lines[2:], [\"line C\", \"line D\"], loc=\"lower right\")\n", + "leg = mpl_Legend(ax, lines[2:], [\"line C\", \"line D\"], loc=\"lower right\")\n", + "ax.add_artist(leg);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "fig, ax = xy_plt.subplots()\n", + "\n", + "lines = []\n", + "styles = [\"-\", \"--\", \"-.\", \":\"]\n", + "x = np.linspace(0, 10, 1000)\n", + "\n", + "for i in range(4):\n", + " lines += ax.plot(x, np.sin(x - i * np.pi / 2), styles[i], color=\"black\")\n", + "ax.axis(\"equal\")\n", + "\n", + "# Specify the lines and labels of the first legend\n", + "ax.legend(lines[:2], [\"line A\", \"line B\"], loc=\"upper right\")\n", + "\n", + "# Create the second legend and add the artist manually\n", + "from xy.pyplot import Legend as xy_Legend # was matplotlib.legend\n", + "\n", + "leg = xy_Legend(ax, lines[2:], [\"line C\", \"line D\"], loc=\"lower right\")\n", "ax.add_artist(leg);" ] } @@ -223,7 +514,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_07_customizing_colorbars.ipynb b/examples/pdsh/pdsh_04_07_customizing_colorbars.ipynb index 02c07d80..805b1d6a 100644 --- a/examples/pdsh/pdsh_04_07_customizing_colorbars.ipynb +++ b/examples/pdsh/pdsh_04_07_customizing_colorbars.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Customizing Colorbars — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Customizing Colorbars" ] @@ -22,43 +42,107 @@ { "cell_type": "code", "execution_count": null, - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "id": "mpl-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", "\n", - "plt.style.use(\"default\")" + "mpl_plt.style.use(\"default\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", + "\n", + "xy_plt.style.use(\"default\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, - "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "id": "xy-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "x = np.linspace(0, 10, 1000)\n", "I = np.sin(x) * np.cos(x[:, np.newaxis])\n", "\n", - "plt.imshow(I)\n", - "plt.colorbar();" + "mpl_plt.imshow(I)\n", + "mpl_plt.colorbar();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "x = np.linspace(0, 10, 1000)\n", + "I = np.sin(x) * np.cos(x[:, np.newaxis])\n", + "\n", + "xy_plt.imshow(I)\n", + "xy_plt.colorbar();" ] }, { "cell_type": "markdown", "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "source": [ "## Customizing Colorbars" ] @@ -66,17 +150,39 @@ { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.imshow(I, cmap=\"Blues\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "outputs": [], "source": [ - "plt.imshow(I, cmap=\"Blues\");" + "# xy.pyplot\n", + "xy_plt.imshow(I, cmap=\"Blues\");" ] }, { "cell_type": "markdown", "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "source": [ "### Choosing the Colormap" ] @@ -84,16 +190,61 @@ { "cell_type": "code", "execution_count": null, - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "id": "mpl-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "from matplotlib.colors import LinearSegmentedColormap as mpl_LinearSegmentedColormap # was matplotlib.colors\n", + "\n", + "\n", + "def grayscale_cmap(cmap):\n", + " \"\"\"Return a grayscale version of the given colormap\"\"\"\n", + " cmap = mpl_plt.get_cmap(cmap)\n", + " colors = cmap(np.arange(cmap.N))\n", + "\n", + " # Convert RGBA to perceived grayscale luminance\n", + " # cf. http://alienryderflex.com/hsp.html\n", + " RGB_weight = [0.299, 0.587, 0.114]\n", + " luminance = np.sqrt(np.dot(colors[:, :3] ** 2, RGB_weight))\n", + " colors[:, :3] = luminance[:, np.newaxis]\n", + "\n", + " return mpl_LinearSegmentedColormap.from_list(cmap.name + \"_gray\", colors, cmap.N)\n", + "\n", + "\n", + "def view_colormap(cmap):\n", + " \"\"\"Plot a colormap with its grayscale equivalent\"\"\"\n", + " cmap = mpl_plt.get_cmap(cmap)\n", + " colors = cmap(np.arange(cmap.N))\n", + "\n", + " cmap = grayscale_cmap(cmap)\n", + " grayscale = cmap(np.arange(cmap.N))\n", + "\n", + " fig, ax = mpl_plt.subplots(2, figsize=(6, 2), subplot_kw=dict(xticks=[], yticks=[]))\n", + " ax[0].imshow([colors], extent=[0, 10, 0, 1])\n", + " ax[1].imshow([grayscale], extent=[0, 10, 0, 1])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "outputs": [], "source": [ - "from xy.pyplot import LinearSegmentedColormap # was matplotlib.colors\n", + "# xy.pyplot\n", + "from xy.pyplot import LinearSegmentedColormap as xy_LinearSegmentedColormap # was matplotlib.colors\n", "\n", "\n", "def grayscale_cmap(cmap):\n", " \"\"\"Return a grayscale version of the given colormap\"\"\"\n", - " cmap = plt.get_cmap(cmap)\n", + " cmap = xy_plt.get_cmap(cmap)\n", " colors = cmap(np.arange(cmap.N))\n", "\n", " # Convert RGBA to perceived grayscale luminance\n", @@ -102,18 +253,18 @@ " luminance = np.sqrt(np.dot(colors[:, :3] ** 2, RGB_weight))\n", " colors[:, :3] = luminance[:, np.newaxis]\n", "\n", - " return LinearSegmentedColormap.from_list(cmap.name + \"_gray\", colors, cmap.N)\n", + " return xy_LinearSegmentedColormap.from_list(cmap.name + \"_gray\", colors, cmap.N)\n", "\n", "\n", "def view_colormap(cmap):\n", " \"\"\"Plot a colormap with its grayscale equivalent\"\"\"\n", - " cmap = plt.get_cmap(cmap)\n", + " cmap = xy_plt.get_cmap(cmap)\n", " colors = cmap(np.arange(cmap.N))\n", "\n", " cmap = grayscale_cmap(cmap)\n", " grayscale = cmap(np.arange(cmap.N))\n", "\n", - " fig, ax = plt.subplots(2, figsize=(6, 2), subplot_kw=dict(xticks=[], yticks=[]))\n", + " fig, ax = xy_plt.subplots(2, figsize=(6, 2), subplot_kw=dict(xticks=[], yticks=[]))\n", " ax[0].imshow([colors], extent=[0, 10, 0, 1])\n", " ax[1].imshow([grayscale], extent=[0, 10, 0, 1])" ] @@ -121,37 +272,95 @@ { "cell_type": "code", "execution_count": null, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "id": "mpl-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "view_colormap(\"jet\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "id": "xy-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "view_colormap(\"jet\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "view_colormap(\"viridis\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "view_colormap(\"viridis\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "id": "mpl-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", + "view_colormap(\"RdBu\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "view_colormap(\"RdBu\")" ] }, { "cell_type": "markdown", "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "source": [ "### Color Limits and Extensions" ] @@ -159,30 +368,65 @@ { "cell_type": "code", "execution_count": null, - "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "id": "mpl-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "# make noise in 1% of the image pixels\n", + "speckles = np.random.random(I.shape) < 0.01\n", + "I[speckles] = np.random.normal(0, 3, np.count_nonzero(speckles))\n", + "\n", + "mpl_plt.figure(figsize=(10, 3.5))\n", + "\n", + "mpl_plt.subplot(1, 2, 1)\n", + "mpl_plt.imshow(I, cmap=\"RdBu\")\n", + "mpl_plt.colorbar()\n", + "\n", + "mpl_plt.subplot(1, 2, 2)\n", + "mpl_plt.imshow(I, cmap=\"RdBu\")\n", + "mpl_plt.colorbar(extend=\"both\")\n", + "mpl_plt.clim(-1, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "# make noise in 1% of the image pixels\n", "speckles = np.random.random(I.shape) < 0.01\n", "I[speckles] = np.random.normal(0, 3, np.count_nonzero(speckles))\n", "\n", - "plt.figure(figsize=(10, 3.5))\n", + "xy_plt.figure(figsize=(10, 3.5))\n", "\n", - "plt.subplot(1, 2, 1)\n", - "plt.imshow(I, cmap=\"RdBu\")\n", - "plt.colorbar()\n", + "xy_plt.subplot(1, 2, 1)\n", + "xy_plt.imshow(I, cmap=\"RdBu\")\n", + "xy_plt.colorbar()\n", "\n", - "plt.subplot(1, 2, 2)\n", - "plt.imshow(I, cmap=\"RdBu\")\n", - "plt.colorbar(extend=\"both\")\n", - "plt.clim(-1, 1)" + "xy_plt.subplot(1, 2, 2)\n", + "xy_plt.imshow(I, cmap=\"RdBu\")\n", + "xy_plt.colorbar(extend=\"both\")\n", + "xy_plt.clim(-1, 1)" ] }, { "cell_type": "markdown", "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, "source": [ "### Discrete Colorbars" ] @@ -190,19 +434,43 @@ { "cell_type": "code", "execution_count": null, - "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, + "id": "mpl-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, "outputs": [], "source": [ - "plt.imshow(I, cmap=plt.get_cmap(\"Blues\", 6))\n", - "plt.colorbar(extend=\"both\")\n", - "plt.clim(-1, 1);" + "# Matplotlib reference\n", + "mpl_plt.imshow(I, cmap=mpl_plt.get_cmap(\"Blues\", 6))\n", + "mpl_plt.colorbar(extend=\"both\")\n", + "mpl_plt.clim(-1, 1);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "xy_plt.imshow(I, cmap=xy_plt.get_cmap(\"Blues\", 6))\n", + "xy_plt.colorbar(extend=\"both\")\n", + "xy_plt.clim(-1, 1);" ] }, { "cell_type": "markdown", "id": "c3933fab20d04ec698c2621248eb3be0", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "c3933fab20d04ec698c2621248eb3be0" + }, "source": [ "## Example: Handwritten Digits" ] @@ -210,16 +478,20 @@ { "cell_type": "code", "execution_count": null, - "id": "4dd4641cc4064e0191573fe9c69df29b", - "metadata": {}, + "id": "mpl-4dd4641cc4064e0191573fe9c69df29b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "# load images of the digits 0 through 5 and visualize several of them\n", "from sklearn.datasets import load_digits\n", "\n", "digits = load_digits(n_class=6)\n", "\n", - "fig, ax = plt.subplots(8, 8, figsize=(6, 6))\n", + "fig, ax = mpl_plt.subplots(8, 8, figsize=(6, 6))\n", "for i, axi in enumerate(ax.flat):\n", " axi.imshow(digits.images[i], cmap=\"binary\")\n", " axi.set(xticks=[], yticks=[])" @@ -228,10 +500,36 @@ { "cell_type": "code", "execution_count": null, - "id": "8309879909854d7188b41380fd92a7c3", - "metadata": {}, + "id": "xy-4dd4641cc4064e0191573fe9c69df29b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "# load images of the digits 0 through 5 and visualize several of them\n", + "from sklearn.datasets import load_digits\n", + "\n", + "digits = load_digits(n_class=6)\n", + "\n", + "fig, ax = xy_plt.subplots(8, 8, figsize=(6, 6))\n", + "for i, axi in enumerate(ax.flat):\n", + " axi.imshow(digits.images[i], cmap=\"binary\")\n", + " axi.set(xticks=[], yticks=[])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", "# project the digits into 2 dimensions using Isomap\n", "from sklearn.manifold import Isomap\n", "\n", @@ -242,16 +540,57 @@ { "cell_type": "code", "execution_count": null, - "id": "3ed186c9a28b402fb0bc4494df01f08d", - "metadata": {}, + "id": "xy-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "# project the digits into 2 dimensions using Isomap\n", + "from sklearn.manifold import Isomap\n", + "\n", + "iso = Isomap(n_components=2, n_neighbors=15)\n", + "projection = iso.fit_transform(digits.data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-3ed186c9a28b402fb0bc4494df01f08d", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "3ed186c9a28b402fb0bc4494df01f08d" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "# plot the results\n", + "mpl_plt.scatter(\n", + " projection[:, 0], projection[:, 1], lw=0.1, c=digits.target, cmap=mpl_plt.get_cmap(\"plasma\", 6)\n", + ")\n", + "mpl_plt.colorbar(ticks=range(6), label=\"digit value\")\n", + "mpl_plt.clim(-0.5, 5.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-3ed186c9a28b402fb0bc4494df01f08d", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "3ed186c9a28b402fb0bc4494df01f08d" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "# plot the results\n", - "plt.scatter(\n", - " projection[:, 0], projection[:, 1], lw=0.1, c=digits.target, cmap=plt.get_cmap(\"plasma\", 6)\n", + "xy_plt.scatter(\n", + " projection[:, 0], projection[:, 1], lw=0.1, c=digits.target, cmap=xy_plt.get_cmap(\"plasma\", 6)\n", ")\n", - "plt.colorbar(ticks=range(6), label=\"digit value\")\n", - "plt.clim(-0.5, 5.5)" + "xy_plt.colorbar(ticks=range(6), label=\"digit value\")\n", + "xy_plt.clim(-0.5, 5.5)" ] } ], @@ -263,7 +602,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_08_multiple_subplots.ipynb b/examples/pdsh/pdsh_04_08_multiple_subplots.ipynb index 8c0a647a..60480278 100644 --- a/examples/pdsh/pdsh_04_08_multiple_subplots.ipynb +++ b/examples/pdsh/pdsh_04_08_multiple_subplots.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Multiple Subplots — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Multiple Subplots" ] @@ -22,20 +42,45 @@ { "cell_type": "code", "execution_count": null, - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "id": "mpl-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", + "\n", + "mpl_plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", "\n", - "plt.style.use(\"default\")\n", + "xy_plt.style.use(\"default\")\n", "import numpy as np" ] }, { "cell_type": "markdown", "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "source": [ "## plt.axes: Subplots by Hand" ] @@ -43,22 +88,65 @@ { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "ax1 = mpl_plt.axes() # standard axes\n", + "ax2 = mpl_plt.axes([0.65, 0.65, 0.2, 0.2])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "ax1 = xy_plt.axes() # standard axes\n", + "ax2 = xy_plt.axes([0.65, 0.65, 0.2, 0.2])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ - "ax1 = plt.axes() # standard axes\n", - "ax2 = plt.axes([0.65, 0.65, 0.2, 0.2])" + "# Matplotlib reference\n", + "fig = mpl_plt.figure()\n", + "ax1 = fig.add_axes([0.1, 0.5, 0.8, 0.4], xticklabels=[], ylim=(-1.2, 1.2))\n", + "ax2 = fig.add_axes([0.1, 0.1, 0.8, 0.4], ylim=(-1.2, 1.2))\n", + "\n", + "x = np.linspace(0, 10)\n", + "ax1.plot(np.sin(x))\n", + "ax2.plot(np.cos(x));" ] }, { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ - "fig = plt.figure()\n", + "# xy.pyplot\n", + "fig = xy_plt.figure()\n", "ax1 = fig.add_axes([0.1, 0.5, 0.8, 0.4], xticklabels=[], ylim=(-1.2, 1.2))\n", "ax2 = fig.add_axes([0.1, 0.1, 0.8, 0.4], ylim=(-1.2, 1.2))\n", "\n", @@ -70,7 +158,11 @@ { "cell_type": "markdown", "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "source": [ "## plt.subplot: Simple Grids of Subplots" ] @@ -78,23 +170,65 @@ { "cell_type": "code", "execution_count": null, - "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "id": "mpl-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "for i in range(1, 7):\n", - " plt.subplot(2, 3, i)\n", - " plt.text(0.5, 0.5, str((2, 3, i)), fontsize=18, ha=\"center\")" + " mpl_plt.subplot(2, 3, i)\n", + " mpl_plt.text(0.5, 0.5, str((2, 3, i)), fontsize=18, ha=\"center\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "id": "xy-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "outputs": [], "source": [ - "fig = plt.figure()\n", + "# xy.pyplot\n", + "for i in range(1, 7):\n", + " xy_plt.subplot(2, 3, i)\n", + " xy_plt.text(0.5, 0.5, str((2, 3, i)), fontsize=18, ha=\"center\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "fig = mpl_plt.figure()\n", + "fig.subplots_adjust(hspace=0.4, wspace=0.4)\n", + "for i in range(1, 7):\n", + " ax = fig.add_subplot(2, 3, i)\n", + " ax.text(0.5, 0.5, str((2, 3, i)), fontsize=18, ha=\"center\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "fig = xy_plt.figure()\n", "fig.subplots_adjust(hspace=0.4, wspace=0.4)\n", "for i in range(1, 7):\n", " ax = fig.add_subplot(2, 3, i)\n", @@ -104,7 +238,11 @@ { "cell_type": "markdown", "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "source": [ "## plt.subplots: The Whole Grid in One Go" ] @@ -112,20 +250,60 @@ { "cell_type": "code", "execution_count": null, - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "id": "mpl-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "fig, ax = mpl_plt.subplots(2, 3, sharex=\"col\", sharey=\"row\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, "outputs": [], "source": [ - "fig, ax = plt.subplots(2, 3, sharex=\"col\", sharey=\"row\")" + "# xy.pyplot\n", + "fig, ax = xy_plt.subplots(2, 3, sharex=\"col\", sharey=\"row\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "# axes are in a two-dimensional array, indexed by [row, col]\n", + "for i in range(2):\n", + " for j in range(3):\n", + " ax[i, j].text(0.5, 0.5, str((i, j)), fontsize=18, ha=\"center\")\n", + "fig" ] }, { "cell_type": "code", "execution_count": null, - "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "id": "xy-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "# axes are in a two-dimensional array, indexed by [row, col]\n", "for i in range(2):\n", " for j in range(3):\n", @@ -136,7 +314,11 @@ { "cell_type": "markdown", "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "source": [ "## plt.GridSpec: More Complicated Arrangements" ] @@ -144,33 +326,76 @@ { "cell_type": "code", "execution_count": null, - "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "id": "mpl-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "grid = mpl_plt.GridSpec(2, 3, wspace=0.4, hspace=0.3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "grid = xy_plt.GridSpec(2, 3, wspace=0.4, hspace=0.3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, "outputs": [], "source": [ - "grid = plt.GridSpec(2, 3, wspace=0.4, hspace=0.3)" + "# Matplotlib reference\n", + "mpl_plt.subplot(grid[0, 0])\n", + "mpl_plt.subplot(grid[0, 1:])\n", + "mpl_plt.subplot(grid[1, :2])\n", + "mpl_plt.subplot(grid[1, 2]);" ] }, { "cell_type": "code", "execution_count": null, - "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, + "id": "xy-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, "outputs": [], "source": [ - "plt.subplot(grid[0, 0])\n", - "plt.subplot(grid[0, 1:])\n", - "plt.subplot(grid[1, :2])\n", - "plt.subplot(grid[1, 2]);" + "# xy.pyplot\n", + "xy_plt.subplot(grid[0, 0])\n", + "xy_plt.subplot(grid[0, 1:])\n", + "xy_plt.subplot(grid[1, :2])\n", + "xy_plt.subplot(grid[1, 2]);" ] }, { "cell_type": "code", "execution_count": null, - "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, + "id": "mpl-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "# Create some normally distributed data\n", "mean = [0, 0]\n", "cov = [[1, 1], [1, 2]]\n", @@ -178,8 +403,43 @@ "x, y = rng.multivariate_normal(mean, cov, 3000).T\n", "\n", "# Set up the axes with GridSpec\n", - "fig = plt.figure(figsize=(6, 6))\n", - "grid = plt.GridSpec(4, 4, hspace=0.2, wspace=0.2)\n", + "fig = mpl_plt.figure(figsize=(6, 6))\n", + "grid = mpl_plt.GridSpec(4, 4, hspace=0.2, wspace=0.2)\n", + "main_ax = fig.add_subplot(grid[:-1, 1:])\n", + "y_hist = fig.add_subplot(grid[:-1, 0], xticklabels=[], sharey=main_ax)\n", + "x_hist = fig.add_subplot(grid[-1, 1:], yticklabels=[], sharex=main_ax)\n", + "\n", + "# Scatter points on the main axes\n", + "main_ax.plot(x, y, \"ok\", markersize=3, alpha=0.2)\n", + "\n", + "# Histogram on the attached axes\n", + "x_hist.hist(x, 40, histtype=\"stepfilled\", orientation=\"vertical\", color=\"gray\")\n", + "x_hist.invert_yaxis()\n", + "\n", + "y_hist.hist(y, 40, histtype=\"stepfilled\", orientation=\"horizontal\", color=\"gray\")\n", + "y_hist.invert_xaxis()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "# Create some normally distributed data\n", + "mean = [0, 0]\n", + "cov = [[1, 1], [1, 2]]\n", + "rng = np.random.default_rng(1701)\n", + "x, y = rng.multivariate_normal(mean, cov, 3000).T\n", + "\n", + "# Set up the axes with GridSpec\n", + "fig = xy_plt.figure(figsize=(6, 6))\n", + "grid = xy_plt.GridSpec(4, 4, hspace=0.2, wspace=0.2)\n", "main_ax = fig.add_subplot(grid[:-1, 1:])\n", "y_hist = fig.add_subplot(grid[:-1, 0], xticklabels=[], sharey=main_ax)\n", "x_hist = fig.add_subplot(grid[-1, 1:], yticklabels=[], sharex=main_ax)\n", @@ -204,7 +464,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_09_text_and_annotation.ipynb b/examples/pdsh/pdsh_04_09_text_and_annotation.ipynb index be961706..5ecfba3e 100644 --- a/examples/pdsh/pdsh_04_09_text_and_annotation.ipynb +++ b/examples/pdsh/pdsh_04_09_text_and_annotation.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Text and Annotation — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Text and Annotation" ] @@ -22,14 +42,37 @@ { "cell_type": "code", "execution_count": null, - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "id": "mpl-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", - "import xy.pyplot as mpl\n", + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", + "import matplotlib.pyplot as mpl_mpl\n", "\n", - "plt.style.use(\"default\")\n", + "mpl_plt.style.use(\"default\")\n", + "import numpy as np\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", + "import xy.pyplot as xy_mpl\n", + "\n", + "xy_plt.style.use(\"default\")\n", "import numpy as np\n", "import pandas as pd" ] @@ -37,7 +80,11 @@ { "cell_type": "markdown", "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "source": [ "## Example: Effect of Holidays on US Births" ] @@ -45,10 +92,14 @@ { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "# shell command to download the data:\n", "# !cd data && curl -O \\\n", "# https://raw.githubusercontent.com/jakevdp/data-CDCbirths/master/births.csv" @@ -57,10 +108,58 @@ { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "# shell command to download the data:\n", + "# !cd data && curl -O \\\n", + "# https://raw.githubusercontent.com/jakevdp/data-CDCbirths/master/births.csv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "from datetime import datetime\n", + "\n", + "births = pd.read_csv(\"data/births.csv\")\n", + "\n", + "quartiles = np.percentile(births[\"births\"], [25, 50, 75])\n", + "mu, sig = quartiles[1], 0.74 * (quartiles[2] - quartiles[0])\n", + "births = births.query(\"(births > @mu - 5 * @sig) & (births < @mu + 5 * @sig)\")\n", + "\n", + "births[\"day\"] = births[\"day\"].astype(int)\n", + "\n", + "births.index = pd.to_datetime(\n", + " 10000 * births.year + 100 * births.month + births.day, format=\"%Y%m%d\"\n", + ")\n", + "births_by_date = births.pivot_table(\"births\", [births.index.month, births.index.day])\n", + "births_by_date.index = [datetime(2012, month, day) for (month, day) in births_by_date.index]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "from datetime import datetime\n", "\n", "births = pd.read_csv(\"data/births.csv\")\n", @@ -81,22 +180,45 @@ { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "outputs": [], "source": [ - "fig, ax = plt.subplots(figsize=(12, 4))\n", + "# Matplotlib reference\n", + "fig, ax = mpl_plt.subplots(figsize=(12, 4))\n", "births_by_date.plot(ax=ax);" ] }, { "cell_type": "code", "execution_count": null, - "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "outputs": [], "source": [ - "fig, ax = plt.subplots(figsize=(12, 4))\n", + "# xy.pyplot\n", + "fig, ax = xy_plt.subplots(figsize=(12, 4))\n", + "births_by_date.plot(ax=ax);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "fig, ax = mpl_plt.subplots(figsize=(12, 4))\n", "births_by_date.plot(ax=ax)\n", "\n", "# Add labels to the plot\n", @@ -113,16 +235,54 @@ "ax.set(title=\"USA births by day of year (1969-1988)\", ylabel=\"average daily births\")\n", "\n", "# Format the x-axis with centered month labels\n", - "ax.xaxis.set_major_locator(mpl.dates.MonthLocator())\n", - "ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))\n", - "ax.xaxis.set_major_formatter(plt.NullFormatter())\n", - "ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter(\"%h\"));" + "ax.xaxis.set_major_locator(mpl_mpl.dates.MonthLocator())\n", + "ax.xaxis.set_minor_locator(mpl_mpl.dates.MonthLocator(bymonthday=15))\n", + "ax.xaxis.set_major_formatter(mpl_plt.NullFormatter())\n", + "ax.xaxis.set_minor_formatter(mpl_mpl.dates.DateFormatter(\"%h\"));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "fig, ax = xy_plt.subplots(figsize=(12, 4))\n", + "births_by_date.plot(ax=ax)\n", + "\n", + "# Add labels to the plot\n", + "style = dict(size=10, color=\"gray\")\n", + "\n", + "ax.text(\"2012-1-1\", 3950, \"New Year's Day\", **style)\n", + "ax.text(\"2012-7-4\", 4250, \"Independence Day\", ha=\"center\", **style)\n", + "ax.text(\"2012-9-4\", 4850, \"Labor Day\", ha=\"center\", **style)\n", + "ax.text(\"2012-10-31\", 4600, \"Halloween\", ha=\"right\", **style)\n", + "ax.text(\"2012-11-25\", 4450, \"Thanksgiving\", ha=\"center\", **style)\n", + "ax.text(\"2012-12-25\", 3850, \"Christmas \", ha=\"right\", **style)\n", + "\n", + "# Label the axes\n", + "ax.set(title=\"USA births by day of year (1969-1988)\", ylabel=\"average daily births\")\n", + "\n", + "# Format the x-axis with centered month labels\n", + "ax.xaxis.set_major_locator(xy_mpl.dates.MonthLocator())\n", + "ax.xaxis.set_minor_locator(xy_mpl.dates.MonthLocator(bymonthday=15))\n", + "ax.xaxis.set_major_formatter(xy_plt.NullFormatter())\n", + "ax.xaxis.set_minor_formatter(xy_mpl.dates.DateFormatter(\"%h\"));" ] }, { "cell_type": "markdown", "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "source": [ "## Transforms and Text Position" ] @@ -130,11 +290,15 @@ { "cell_type": "code", "execution_count": null, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "id": "mpl-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ - "fig, ax = plt.subplots(facecolor=\"lightgray\")\n", + "# Matplotlib reference\n", + "fig, ax = mpl_plt.subplots(facecolor=\"lightgray\")\n", "ax.axis([0, 10, 0, 10])\n", "\n", "# transform=ax.transData is the default, but we'll specify it anyway\n", @@ -146,10 +310,50 @@ { "cell_type": "code", "execution_count": null, - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "id": "xy-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "fig, ax = xy_plt.subplots(facecolor=\"lightgray\")\n", + "ax.axis([0, 10, 0, 10])\n", + "\n", + "# transform=ax.transData is the default, but we'll specify it anyway\n", + "ax.text(1, 5, \". Data: (1, 5)\", transform=ax.transData)\n", + "ax.text(0.5, 0.1, \". Axes: (0.5, 0.1)\", transform=ax.transAxes)\n", + "ax.text(0.2, 0.2, \". Figure: (0.2, 0.2)\", transform=fig.transFigure);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "ax.set_xlim(0, 2)\n", + "ax.set_ylim(-6, 6)\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "ax.set_xlim(0, 2)\n", "ax.set_ylim(-6, 6)\n", "fig" @@ -158,7 +362,11 @@ { "cell_type": "markdown", "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "source": [ "## Arrows and Annotation" ] @@ -166,11 +374,44 @@ { "cell_type": "code", "execution_count": null, - "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "id": "mpl-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "fig, ax = mpl_plt.subplots()\n", + "\n", + "x = np.linspace(0, 20, 1000)\n", + "ax.plot(x, np.cos(x))\n", + "ax.axis(\"equal\")\n", + "\n", + "ax.annotate(\n", + " \"local maximum\", xy=(6.28, 1), xytext=(10, 4), arrowprops=dict(facecolor=\"black\", shrink=0.05)\n", + ")\n", + "\n", + "ax.annotate(\n", + " \"local minimum\",\n", + " xy=(5 * np.pi, -1),\n", + " xytext=(2, -6),\n", + " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"angle3,angleA=0,angleB=-90\"),\n", + ");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "outputs": [], "source": [ - "fig, ax = plt.subplots()\n", + "# xy.pyplot\n", + "fig, ax = xy_plt.subplots()\n", "\n", "x = np.linspace(0, 20, 1000)\n", "ax.plot(x, np.cos(x))\n", @@ -191,11 +432,15 @@ { "cell_type": "code", "execution_count": null, - "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "id": "mpl-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "outputs": [], "source": [ - "fig, ax = plt.subplots(figsize=(12, 4))\n", + "# Matplotlib reference\n", + "fig, ax = mpl_plt.subplots(figsize=(12, 4))\n", "births_by_date.plot(ax=ax)\n", "\n", "# Add labels to the plot\n", @@ -276,10 +521,109 @@ "ax.set(title=\"USA births by day of year (1969-1988)\", ylabel=\"average daily births\")\n", "\n", "# Format the x-axis with centered month labels\n", - "ax.xaxis.set_major_locator(mpl.dates.MonthLocator())\n", - "ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))\n", - "ax.xaxis.set_major_formatter(plt.NullFormatter())\n", - "ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter(\"%h\"))\n", + "ax.xaxis.set_major_locator(mpl_mpl.dates.MonthLocator())\n", + "ax.xaxis.set_minor_locator(mpl_mpl.dates.MonthLocator(bymonthday=15))\n", + "ax.xaxis.set_major_formatter(mpl_plt.NullFormatter())\n", + "ax.xaxis.set_minor_formatter(mpl_mpl.dates.DateFormatter(\"%h\"))\n", + "ax.set_ylim(3600, 5400);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "fig, ax = xy_plt.subplots(figsize=(12, 4))\n", + "births_by_date.plot(ax=ax)\n", + "\n", + "# Add labels to the plot\n", + "ax.annotate(\n", + " \"New Year's Day\",\n", + " xy=(\"2012-1-1\", 4100),\n", + " xycoords=\"data\",\n", + " xytext=(50, -30),\n", + " textcoords=\"offset points\",\n", + " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3,rad=-0.2\"),\n", + ")\n", + "\n", + "ax.annotate(\n", + " \"Independence Day\",\n", + " xy=(\"2012-7-4\", 4250),\n", + " xycoords=\"data\",\n", + " bbox=dict(boxstyle=\"round\", fc=\"none\", ec=\"gray\"),\n", + " xytext=(10, -40),\n", + " textcoords=\"offset points\",\n", + " ha=\"center\",\n", + " arrowprops=dict(arrowstyle=\"->\"),\n", + ")\n", + "\n", + "ax.annotate(\n", + " \"Labor Day Weekend\",\n", + " xy=(\"2012-9-4\", 4850),\n", + " xycoords=\"data\",\n", + " ha=\"center\",\n", + " xytext=(0, -20),\n", + " textcoords=\"offset points\",\n", + ")\n", + "ax.annotate(\n", + " \"\",\n", + " xy=(\"2012-9-1\", 4850),\n", + " xytext=(\"2012-9-7\", 4850),\n", + " xycoords=\"data\",\n", + " textcoords=\"data\",\n", + " arrowprops={\n", + " \"arrowstyle\": \"|-|,widthA=0.2,widthB=0.2\",\n", + " },\n", + ")\n", + "\n", + "ax.annotate(\n", + " \"Halloween\",\n", + " xy=(\"2012-10-31\", 4600),\n", + " xycoords=\"data\",\n", + " xytext=(-80, -40),\n", + " textcoords=\"offset points\",\n", + " arrowprops=dict(\n", + " arrowstyle=\"fancy\", fc=\"0.6\", ec=\"none\", connectionstyle=\"angle3,angleA=0,angleB=-90\"\n", + " ),\n", + ")\n", + "\n", + "ax.annotate(\n", + " \"Thanksgiving\",\n", + " xy=(\"2012-11-25\", 4500),\n", + " xycoords=\"data\",\n", + " xytext=(-120, -60),\n", + " textcoords=\"offset points\",\n", + " bbox=dict(boxstyle=\"round4,pad=.5\", fc=\"0.9\"),\n", + " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"angle,angleA=0,angleB=80,rad=20\"),\n", + ")\n", + "\n", + "\n", + "ax.annotate(\n", + " \"Christmas\",\n", + " xy=(\"2012-12-25\", 3850),\n", + " xycoords=\"data\",\n", + " xytext=(-30, 0),\n", + " textcoords=\"offset points\",\n", + " size=13,\n", + " ha=\"right\",\n", + " va=\"center\",\n", + " bbox=dict(boxstyle=\"round\", alpha=0.1),\n", + " arrowprops=dict(arrowstyle=\"wedge,tail_width=0.5\", alpha=0.1),\n", + ")\n", + "# Label the axes\n", + "ax.set(title=\"USA births by day of year (1969-1988)\", ylabel=\"average daily births\")\n", + "\n", + "# Format the x-axis with centered month labels\n", + "ax.xaxis.set_major_locator(xy_mpl.dates.MonthLocator())\n", + "ax.xaxis.set_minor_locator(xy_mpl.dates.MonthLocator(bymonthday=15))\n", + "ax.xaxis.set_major_formatter(xy_plt.NullFormatter())\n", + "ax.xaxis.set_minor_formatter(xy_mpl.dates.DateFormatter(\"%h\"))\n", "ax.set_ylim(3600, 5400);" ] } @@ -292,7 +636,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_10_customizing_ticks.ipynb b/examples/pdsh/pdsh_04_10_customizing_ticks.ipynb index 680ee5b4..2374fcb3 100644 --- a/examples/pdsh/pdsh_04_10_customizing_ticks.ipynb +++ b/examples/pdsh/pdsh_04_10_customizing_ticks.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Customizing Ticks — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Customizing Ticks" ] @@ -22,7 +42,11 @@ { "cell_type": "markdown", "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "source": [ "## Major and Minor Ticks" ] @@ -30,24 +54,49 @@ { "cell_type": "code", "execution_count": null, - "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "id": "mpl-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", + "\n", + "mpl_plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", "\n", - "plt.style.use(\"default\")\n", + "xy_plt.style.use(\"default\")\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ - "ax = plt.axes(xscale=\"log\", yscale=\"log\")\n", + "# Matplotlib reference\n", + "ax = mpl_plt.axes(xscale=\"log\", yscale=\"log\")\n", "ax.set(xlim=(1, 1e3), ylim=(1, 1e3))\n", "ax.grid(True);" ] @@ -55,10 +104,30 @@ { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "ax = xy_plt.axes(xscale=\"log\", yscale=\"log\")\n", + "ax.set(xlim=(1, 1e3), ylim=(1, 1e3))\n", + "ax.grid(True);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", "print(ax.xaxis.get_major_locator())\n", "print(ax.xaxis.get_minor_locator())" ] @@ -66,10 +135,44 @@ { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "print(ax.xaxis.get_major_locator())\n", + "print(ax.xaxis.get_minor_locator())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "print(ax.xaxis.get_major_formatter())\n", + "print(ax.xaxis.get_minor_formatter())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "print(ax.xaxis.get_major_formatter())\n", "print(ax.xaxis.get_minor_formatter())" ] @@ -77,7 +180,11 @@ { "cell_type": "markdown", "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "source": [ "## Hiding Ticks or Labels" ] @@ -85,27 +192,55 @@ { "cell_type": "code", "execution_count": null, - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "id": "mpl-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "ax = mpl_plt.axes()\n", + "rng = np.random.default_rng(1701)\n", + "ax.plot(rng.random(50))\n", + "ax.grid()\n", + "\n", + "ax.yaxis.set_major_locator(mpl_plt.NullLocator())\n", + "ax.xaxis.set_major_formatter(mpl_plt.NullFormatter())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "outputs": [], "source": [ - "ax = plt.axes()\n", + "# xy.pyplot\n", + "ax = xy_plt.axes()\n", "rng = np.random.default_rng(1701)\n", "ax.plot(rng.random(50))\n", "ax.grid()\n", "\n", - "ax.yaxis.set_major_locator(plt.NullLocator())\n", - "ax.xaxis.set_major_formatter(plt.NullFormatter())" + "ax.yaxis.set_major_locator(xy_plt.NullLocator())\n", + "ax.xaxis.set_major_formatter(xy_plt.NullFormatter())" ] }, { "cell_type": "code", "execution_count": null, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "id": "mpl-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ - "fig, ax = plt.subplots(5, 5, figsize=(5, 5))\n", + "# Matplotlib reference\n", + "fig, ax = mpl_plt.subplots(5, 5, figsize=(5, 5))\n", "fig.subplots_adjust(hspace=0, wspace=0)\n", "\n", "# Get some face data from Scikit-Learn\n", @@ -115,15 +250,45 @@ "\n", "for i in range(5):\n", " for j in range(5):\n", - " ax[i, j].xaxis.set_major_locator(plt.NullLocator())\n", - " ax[i, j].yaxis.set_major_locator(plt.NullLocator())\n", + " ax[i, j].xaxis.set_major_locator(mpl_plt.NullLocator())\n", + " ax[i, j].yaxis.set_major_locator(mpl_plt.NullLocator())\n", + " ax[i, j].imshow(faces[10 * i + j], cmap=\"binary_r\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "fig, ax = xy_plt.subplots(5, 5, figsize=(5, 5))\n", + "fig.subplots_adjust(hspace=0, wspace=0)\n", + "\n", + "# Get some face data from Scikit-Learn\n", + "from sklearn.datasets import fetch_olivetti_faces\n", + "\n", + "faces = fetch_olivetti_faces().images\n", + "\n", + "for i in range(5):\n", + " for j in range(5):\n", + " ax[i, j].xaxis.set_major_locator(xy_plt.NullLocator())\n", + " ax[i, j].yaxis.set_major_locator(xy_plt.NullLocator())\n", " ax[i, j].imshow(faces[10 * i + j], cmap=\"binary_r\")" ] }, { "cell_type": "markdown", "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, "source": [ "## Reducing or Increasing the Number of Ticks" ] @@ -131,31 +296,75 @@ { "cell_type": "code", "execution_count": null, - "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "id": "mpl-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "outputs": [], "source": [ - "fig, ax = plt.subplots(4, 4, sharex=True, sharey=True)" + "# Matplotlib reference\n", + "fig, ax = mpl_plt.subplots(4, 4, sharex=True, sharey=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "fig, ax = xy_plt.subplots(4, 4, sharex=True, sharey=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "# For every axis, set the x and y major locator\n", + "for axi in ax.flat:\n", + " axi.xaxis.set_major_locator(mpl_plt.MaxNLocator(3))\n", + " axi.yaxis.set_major_locator(mpl_plt.MaxNLocator(3))\n", + "fig" ] }, { "cell_type": "code", "execution_count": null, - "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "id": "xy-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "# For every axis, set the x and y major locator\n", "for axi in ax.flat:\n", - " axi.xaxis.set_major_locator(plt.MaxNLocator(3))\n", - " axi.yaxis.set_major_locator(plt.MaxNLocator(3))\n", + " axi.xaxis.set_major_locator(xy_plt.MaxNLocator(3))\n", + " axi.yaxis.set_major_locator(xy_plt.MaxNLocator(3))\n", "fig" ] }, { "cell_type": "markdown", "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "source": [ "## Fancy Tick Formats" ] @@ -163,12 +372,40 @@ { "cell_type": "code", "execution_count": null, - "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, + "id": "mpl-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "# Plot a sine and cosine curve\n", + "fig, ax = mpl_plt.subplots()\n", + "x = np.linspace(0, 3 * np.pi, 1000)\n", + "ax.plot(x, np.sin(x), lw=3, label=\"Sine\")\n", + "ax.plot(x, np.cos(x), lw=3, label=\"Cosine\")\n", + "\n", + "# Set up grid, legend, and limits\n", + "ax.grid(True)\n", + "ax.legend(frameon=False)\n", + "ax.axis(\"equal\")\n", + "ax.set_xlim(0, 3 * np.pi);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "# Plot a sine and cosine curve\n", - "fig, ax = plt.subplots()\n", + "fig, ax = xy_plt.subplots()\n", "x = np.linspace(0, 3 * np.pi, 1000)\n", "ax.plot(x, np.sin(x), lw=3, label=\"Sine\")\n", "ax.plot(x, np.cos(x), lw=3, label=\"Cosine\")\n", @@ -183,22 +420,76 @@ { "cell_type": "code", "execution_count": null, - "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, + "id": "mpl-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "ax.xaxis.set_major_locator(mpl_plt.MultipleLocator(np.pi / 2))\n", + "ax.xaxis.set_minor_locator(mpl_plt.MultipleLocator(np.pi / 4))\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "ax.xaxis.set_major_locator(xy_plt.MultipleLocator(np.pi / 2))\n", + "ax.xaxis.set_minor_locator(xy_plt.MultipleLocator(np.pi / 4))\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-c3933fab20d04ec698c2621248eb3be0", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "c3933fab20d04ec698c2621248eb3be0" + }, "outputs": [], "source": [ - "ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2))\n", - "ax.xaxis.set_minor_locator(plt.MultipleLocator(np.pi / 4))\n", + "# Matplotlib reference\n", + "def format_func(value, tick_number):\n", + " # find number of multiples of pi/2\n", + " N = int(np.round(2 * value / np.pi))\n", + " if N == 0:\n", + " return \"0\"\n", + " elif N == 1:\n", + " return r\"$\\pi/2$\"\n", + " elif N == 2:\n", + " return r\"$\\pi$\"\n", + " elif N % 2 > 0:\n", + " return rf\"${N}\\pi/2$\"\n", + " else:\n", + " return rf\"${N // 2}\\pi$\"\n", + "\n", + "\n", + "ax.xaxis.set_major_formatter(mpl_plt.FuncFormatter(format_func))\n", "fig" ] }, { "cell_type": "code", "execution_count": null, - "id": "c3933fab20d04ec698c2621248eb3be0", - "metadata": {}, + "id": "xy-c3933fab20d04ec698c2621248eb3be0", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "c3933fab20d04ec698c2621248eb3be0" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "def format_func(value, tick_number):\n", " # find number of multiples of pi/2\n", " N = int(np.round(2 * value / np.pi))\n", @@ -214,14 +505,18 @@ " return rf\"${N // 2}\\pi$\"\n", "\n", "\n", - "ax.xaxis.set_major_formatter(plt.FuncFormatter(format_func))\n", + "ax.xaxis.set_major_formatter(xy_plt.FuncFormatter(format_func))\n", "fig" ] }, { "cell_type": "markdown", "id": "4dd4641cc4064e0191573fe9c69df29b", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, "source": [ "## Summary of Formatters and Locators" ] @@ -235,7 +530,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_11_settings_and_stylesheets.ipynb b/examples/pdsh/pdsh_04_11_settings_and_stylesheets.ipynb index d1683359..bd6dd0d1 100644 --- a/examples/pdsh/pdsh_04_11_settings_and_stylesheets.ipynb +++ b/examples/pdsh/pdsh_04_11_settings_and_stylesheets.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Settings and Stylesheets — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Customizing Matplotlib: Configurations and Stylesheets" ] @@ -22,7 +42,11 @@ { "cell_type": "markdown", "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "source": [ "## Plot Customization by Hand" ] @@ -30,41 +54,123 @@ { "cell_type": "code", "execution_count": null, - "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "id": "mpl-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", + "\n", + "mpl_plt.style.use(\"default\")\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", "\n", - "plt.style.use(\"default\")\n", + "xy_plt.style.use(\"default\")\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "x = np.random.randn(1000)\n", - "plt.hist(x);" + "mpl_plt.hist(x);" ] }, { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "x = np.random.randn(1000)\n", + "xy_plt.hist(x);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", "# use a gray background\n", - "fig = plt.figure(facecolor=\"white\")\n", - "ax = plt.axes(facecolor=\"#E6E6E6\")\n", + "fig = mpl_plt.figure(facecolor=\"white\")\n", + "ax = mpl_plt.axes(facecolor=\"#E6E6E6\")\n", "ax.set_axisbelow(True)\n", "\n", "# draw solid white gridlines\n", - "plt.grid(color=\"w\", linestyle=\"solid\")\n", + "mpl_plt.grid(color=\"w\", linestyle=\"solid\")\n", + "\n", + "# hide axis spines\n", + "for spine in ax.spines.values():\n", + " spine.set_visible(False)\n", + "\n", + "# hide top and right ticks\n", + "ax.xaxis.tick_bottom()\n", + "ax.yaxis.tick_left()\n", + "\n", + "# lighten ticks and labels\n", + "ax.tick_params(colors=\"gray\", direction=\"out\")\n", + "for tick in ax.get_xticklabels():\n", + " tick.set_color(\"gray\")\n", + "for tick in ax.get_yticklabels():\n", + " tick.set_color(\"gray\")\n", + "\n", + "# control face and edge color of histogram\n", + "ax.hist(x, edgecolor=\"#E6E6E6\", color=\"#EE6666\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "# use a gray background\n", + "fig = xy_plt.figure(facecolor=\"white\")\n", + "ax = xy_plt.axes(facecolor=\"#E6E6E6\")\n", + "ax.set_axisbelow(True)\n", + "\n", + "# draw solid white gridlines\n", + "xy_plt.grid(color=\"w\", linestyle=\"solid\")\n", "\n", "# hide axis spines\n", "for spine in ax.spines.values():\n", @@ -88,7 +194,11 @@ { "cell_type": "markdown", "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "source": [ "## Changing the Defaults: rcParams" ] @@ -96,47 +206,119 @@ { "cell_type": "code", "execution_count": null, - "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "id": "mpl-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "from matplotlib import cycler as mpl_cycler\n", + "\n", + "colors = mpl_cycler(\"color\", [\"#EE6666\", \"#3388BB\", \"#9988DD\", \"#EECC55\", \"#88BB44\", \"#FFBBBB\"])\n", + "mpl_plt.rc(\"figure\", facecolor=\"white\")\n", + "mpl_plt.rc(\n", + " \"axes\", facecolor=\"#E6E6E6\", edgecolor=\"none\", axisbelow=True, grid=True, prop_cycle=colors\n", + ")\n", + "mpl_plt.rc(\"grid\", color=\"w\", linestyle=\"solid\")\n", + "mpl_plt.rc(\"xtick\", direction=\"out\", color=\"gray\")\n", + "mpl_plt.rc(\"ytick\", direction=\"out\", color=\"gray\")\n", + "mpl_plt.rc(\"patch\", edgecolor=\"#E6E6E6\")\n", + "mpl_plt.rc(\"lines\", linewidth=2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "outputs": [], "source": [ - "from xy.pyplot import cycler\n", + "# xy.pyplot\n", + "from xy.pyplot import cycler as xy_cycler\n", "\n", - "colors = cycler(\"color\", [\"#EE6666\", \"#3388BB\", \"#9988DD\", \"#EECC55\", \"#88BB44\", \"#FFBBBB\"])\n", - "plt.rc(\"figure\", facecolor=\"white\")\n", - "plt.rc(\"axes\", facecolor=\"#E6E6E6\", edgecolor=\"none\", axisbelow=True, grid=True, prop_cycle=colors)\n", - "plt.rc(\"grid\", color=\"w\", linestyle=\"solid\")\n", - "plt.rc(\"xtick\", direction=\"out\", color=\"gray\")\n", - "plt.rc(\"ytick\", direction=\"out\", color=\"gray\")\n", - "plt.rc(\"patch\", edgecolor=\"#E6E6E6\")\n", - "plt.rc(\"lines\", linewidth=2)" + "colors = xy_cycler(\"color\", [\"#EE6666\", \"#3388BB\", \"#9988DD\", \"#EECC55\", \"#88BB44\", \"#FFBBBB\"])\n", + "xy_plt.rc(\"figure\", facecolor=\"white\")\n", + "xy_plt.rc(\n", + " \"axes\", facecolor=\"#E6E6E6\", edgecolor=\"none\", axisbelow=True, grid=True, prop_cycle=colors\n", + ")\n", + "xy_plt.rc(\"grid\", color=\"w\", linestyle=\"solid\")\n", + "xy_plt.rc(\"xtick\", direction=\"out\", color=\"gray\")\n", + "xy_plt.rc(\"ytick\", direction=\"out\", color=\"gray\")\n", + "xy_plt.rc(\"patch\", edgecolor=\"#E6E6E6\")\n", + "xy_plt.rc(\"lines\", linewidth=2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "mpl_plt.hist(x);" ] }, { "cell_type": "code", "execution_count": null, - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "id": "xy-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "outputs": [], "source": [ - "plt.hist(x);" + "# xy.pyplot\n", + "xy_plt.hist(x);" ] }, { "cell_type": "code", "execution_count": null, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "id": "mpl-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "for i in range(4):\n", - " plt.plot(np.random.rand(10))" + " mpl_plt.plot(np.random.rand(10))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "for i in range(4):\n", + " xy_plt.plot(np.random.rand(10))" ] }, { "cell_type": "markdown", "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, "source": [ "## Stylesheets" ] @@ -144,23 +326,65 @@ { "cell_type": "code", "execution_count": null, - "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "id": "mpl-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "outputs": [], "source": [ - "plt.style.available[:5]" + "# Matplotlib reference\n", + "mpl_plt.style.available[:5]" ] }, { "cell_type": "code", "execution_count": null, - "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "id": "xy-938c804e27f84196a10c8828c723f798", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "xy_plt.style.available[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", "def hist_and_lines():\n", " np.random.seed(0)\n", - " fig, ax = plt.subplots(1, 2, figsize=(11, 4))\n", + " fig, ax = mpl_plt.subplots(1, 2, figsize=(11, 4))\n", + " ax[0].hist(np.random.randn(1000))\n", + " for i in range(3):\n", + " ax[1].plot(np.random.rand(10))\n", + " ax[1].legend([\"a\", \"b\", \"c\"], loc=\"lower left\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "def hist_and_lines():\n", + " np.random.seed(0)\n", + " fig, ax = xy_plt.subplots(1, 2, figsize=(11, 4))\n", " ax[0].hist(np.random.randn(1000))\n", " for i in range(3):\n", " ax[1].plot(np.random.rand(10))\n", @@ -170,7 +394,11 @@ { "cell_type": "markdown", "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "source": [ "### Default Style" ] @@ -178,18 +406,41 @@ { "cell_type": "code", "execution_count": null, - "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, + "id": "mpl-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "with mpl_plt.style.context(\"default\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, "outputs": [], "source": [ - "with plt.style.context(\"default\"):\n", + "# xy.pyplot\n", + "with xy_plt.style.context(\"default\"):\n", " hist_and_lines()" ] }, { "cell_type": "markdown", "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, "source": [ "### FiveThiryEight Style" ] @@ -197,18 +448,41 @@ { "cell_type": "code", "execution_count": null, - "id": "c3933fab20d04ec698c2621248eb3be0", - "metadata": {}, + "id": "mpl-c3933fab20d04ec698c2621248eb3be0", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "c3933fab20d04ec698c2621248eb3be0" + }, "outputs": [], "source": [ - "with plt.style.context(\"fivethirtyeight\"):\n", + "# Matplotlib reference\n", + "with mpl_plt.style.context(\"fivethirtyeight\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-c3933fab20d04ec698c2621248eb3be0", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "c3933fab20d04ec698c2621248eb3be0" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "with xy_plt.style.context(\"fivethirtyeight\"):\n", " hist_and_lines()" ] }, { "cell_type": "markdown", "id": "4dd4641cc4064e0191573fe9c69df29b", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, "source": [ "### ggplot Style" ] @@ -216,18 +490,41 @@ { "cell_type": "code", "execution_count": null, - "id": "8309879909854d7188b41380fd92a7c3", - "metadata": {}, + "id": "mpl-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "with mpl_plt.style.context(\"ggplot\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, "outputs": [], "source": [ - "with plt.style.context(\"ggplot\"):\n", + "# xy.pyplot\n", + "with xy_plt.style.context(\"ggplot\"):\n", " hist_and_lines()" ] }, { "cell_type": "markdown", "id": "3ed186c9a28b402fb0bc4494df01f08d", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "3ed186c9a28b402fb0bc4494df01f08d" + }, "source": [ "### Bayesian Methods for Hackers Style" ] @@ -235,18 +532,41 @@ { "cell_type": "code", "execution_count": null, - "id": "cb1e1581032b452c9409d6c6813c49d1", - "metadata": {}, + "id": "mpl-cb1e1581032b452c9409d6c6813c49d1", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "cb1e1581032b452c9409d6c6813c49d1" + }, "outputs": [], "source": [ - "with plt.style.context(\"bmh\"):\n", + "# Matplotlib reference\n", + "with mpl_plt.style.context(\"bmh\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-cb1e1581032b452c9409d6c6813c49d1", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "cb1e1581032b452c9409d6c6813c49d1" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "with xy_plt.style.context(\"bmh\"):\n", " hist_and_lines()" ] }, { "cell_type": "markdown", "id": "379cbbc1e968416e875cc15c1202d7eb", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "379cbbc1e968416e875cc15c1202d7eb" + }, "source": [ "### Dark Background Style" ] @@ -254,18 +574,41 @@ { "cell_type": "code", "execution_count": null, - "id": "277c27b1587741f2af2001be3712ef0d", - "metadata": {}, + "id": "mpl-277c27b1587741f2af2001be3712ef0d", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "277c27b1587741f2af2001be3712ef0d" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "with mpl_plt.style.context(\"dark_background\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-277c27b1587741f2af2001be3712ef0d", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "277c27b1587741f2af2001be3712ef0d" + }, "outputs": [], "source": [ - "with plt.style.context(\"dark_background\"):\n", + "# xy.pyplot\n", + "with xy_plt.style.context(\"dark_background\"):\n", " hist_and_lines()" ] }, { "cell_type": "markdown", "id": "db7b79bc585a40fcaf58bf750017e135", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "db7b79bc585a40fcaf58bf750017e135" + }, "source": [ "### Grayscale Style" ] @@ -273,18 +616,41 @@ { "cell_type": "code", "execution_count": null, - "id": "916684f9a58a4a2aa5f864670399430d", - "metadata": {}, + "id": "mpl-916684f9a58a4a2aa5f864670399430d", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "916684f9a58a4a2aa5f864670399430d" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "with mpl_plt.style.context(\"grayscale\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-916684f9a58a4a2aa5f864670399430d", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "916684f9a58a4a2aa5f864670399430d" + }, "outputs": [], "source": [ - "with plt.style.context(\"grayscale\"):\n", + "# xy.pyplot\n", + "with xy_plt.style.context(\"grayscale\"):\n", " hist_and_lines()" ] }, { "cell_type": "markdown", "id": "1671c31a24314836a5b85d7ef7fbf015", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "1671c31a24314836a5b85d7ef7fbf015" + }, "source": [ "### Seaborn Style" ] @@ -292,11 +658,30 @@ { "cell_type": "code", "execution_count": null, - "id": "33b0902fd34d4ace834912fa1002cf8e", - "metadata": {}, + "id": "mpl-33b0902fd34d4ace834912fa1002cf8e", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "33b0902fd34d4ace834912fa1002cf8e" + }, "outputs": [], "source": [ - "with plt.style.context(\"default\"):\n", + "# Matplotlib reference\n", + "with mpl_plt.style.context(\"default\"):\n", + " hist_and_lines()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-33b0902fd34d4ace834912fa1002cf8e", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "33b0902fd34d4ace834912fa1002cf8e" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "with xy_plt.style.context(\"default\"):\n", " hist_and_lines()" ] } @@ -309,7 +694,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_12_three_dimensional_plotting.ipynb b/examples/pdsh/pdsh_04_12_three_dimensional_plotting.ipynb index 7c1ebaf7..d5170a80 100644 --- a/examples/pdsh/pdsh_04_12_three_dimensional_plotting.ipynb +++ b/examples/pdsh/pdsh_04_12_three_dimensional_plotting.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Three Dimensional Plotting — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Three-Dimensional Plotting in Matplotlib" ] @@ -22,39 +42,99 @@ { "cell_type": "code", "execution_count": null, - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "id": "mpl-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "from mpl_toolkits import mplot3d" ] }, { "cell_type": "code", "execution_count": null, - "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "id": "xy-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "from mpl_toolkits import mplot3d" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import numpy as np\n", + "import matplotlib.pyplot as mpl_plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8dd0d8092fe74a7c96281538738b07e2", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "import numpy as np\n", - "import xy.pyplot as plt" + "import xy.pyplot as xy_plt" ] }, { "cell_type": "code", "execution_count": null, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "id": "mpl-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "outputs": [], "source": [ - "fig = plt.figure()\n", - "ax = plt.axes(projection=\"3d\")" + "# Matplotlib reference\n", + "fig = mpl_plt.figure()\n", + "ax = mpl_plt.axes(projection=\"3d\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-72eea5119410473aa328ad9291626812", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "fig = xy_plt.figure()\n", + "ax = xy_plt.axes(projection=\"3d\")" ] }, { "cell_type": "markdown", "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "source": [ "## Three-Dimensional Points and Lines" ] @@ -62,11 +142,41 @@ { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "ax = mpl_plt.axes(projection=\"3d\")\n", + "\n", + "# Data for a three-dimensional line\n", + "zline = np.linspace(0, 15, 1000)\n", + "xline = np.sin(zline)\n", + "yline = np.cos(zline)\n", + "ax.plot3D(xline, yline, zline, \"gray\")\n", + "\n", + "# Data for three-dimensional scattered points\n", + "zdata = 15 * np.random.random(100)\n", + "xdata = np.sin(zdata) + 0.1 * np.random.randn(100)\n", + "ydata = np.cos(zdata) + 0.1 * np.random.randn(100)\n", + "ax.scatter3D(xdata, ydata, zdata, c=zdata, cmap=\"Greens\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "outputs": [], "source": [ - "ax = plt.axes(projection=\"3d\")\n", + "# xy.pyplot\n", + "ax = xy_plt.axes(projection=\"3d\")\n", "\n", "# Data for a three-dimensional line\n", "zline = np.linspace(0, 15, 1000)\n", @@ -84,7 +194,11 @@ { "cell_type": "markdown", "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "source": [ "## Three-Dimensional Contour Plots" ] @@ -92,10 +206,14 @@ { "cell_type": "code", "execution_count": null, - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "id": "mpl-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "def f(x, y):\n", " return np.sin(np.sqrt(x**2 + y**2))\n", "\n", @@ -110,12 +228,57 @@ { "cell_type": "code", "execution_count": null, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "id": "xy-7623eae2785240b9bd12b16a66d81610", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "outputs": [], "source": [ - "fig = plt.figure()\n", - "ax = plt.axes(projection=\"3d\")\n", + "# xy.pyplot\n", + "def f(x, y):\n", + " return np.sin(np.sqrt(x**2 + y**2))\n", + "\n", + "\n", + "x = np.linspace(-6, 6, 30)\n", + "y = np.linspace(-6, 6, 30)\n", + "\n", + "X, Y = np.meshgrid(x, y)\n", + "Z = f(X, Y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "fig = mpl_plt.figure()\n", + "ax = mpl_plt.axes(projection=\"3d\")\n", + "ax.contour3D(X, Y, Z, 40, cmap=\"binary\")\n", + "ax.set_xlabel(\"x\")\n", + "ax.set_ylabel(\"y\")\n", + "ax.set_zlabel(\"z\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "fig = xy_plt.figure()\n", + "ax = xy_plt.axes(projection=\"3d\")\n", "ax.contour3D(X, Y, Z, 40, cmap=\"binary\")\n", "ax.set_xlabel(\"x\")\n", "ax.set_ylabel(\"y\")\n", @@ -125,10 +288,29 @@ { "cell_type": "code", "execution_count": null, - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "id": "mpl-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "ax.view_init(60, 35)\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "ax.view_init(60, 35)\n", "fig" ] @@ -136,7 +318,11 @@ { "cell_type": "markdown", "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "source": [ "## Wireframes and Surface Plots" ] @@ -144,12 +330,33 @@ { "cell_type": "code", "execution_count": null, - "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "id": "mpl-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "fig = mpl_plt.figure()\n", + "ax = mpl_plt.axes(projection=\"3d\")\n", + "ax.plot_wireframe(X, Y, Z)\n", + "ax.set_title(\"wireframe\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "outputs": [], "source": [ - "fig = plt.figure()\n", - "ax = plt.axes(projection=\"3d\")\n", + "# xy.pyplot\n", + "fig = xy_plt.figure()\n", + "ax = xy_plt.axes(projection=\"3d\")\n", "ax.plot_wireframe(X, Y, Z)\n", "ax.set_title(\"wireframe\");" ] @@ -157,11 +364,31 @@ { "cell_type": "code", "execution_count": null, - "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "id": "mpl-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "ax = mpl_plt.axes(projection=\"3d\")\n", + "ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=\"viridis\", edgecolor=\"none\")\n", + "ax.set_title(\"surface\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "outputs": [], "source": [ - "ax = plt.axes(projection=\"3d\")\n", + "# xy.pyplot\n", + "ax = xy_plt.axes(projection=\"3d\")\n", "ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=\"viridis\", edgecolor=\"none\")\n", "ax.set_title(\"surface\");" ] @@ -169,10 +396,14 @@ { "cell_type": "code", "execution_count": null, - "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, + "id": "mpl-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "r = np.linspace(0, 6, 20)\n", "theta = np.linspace(-0.9 * np.pi, 0.8 * np.pi, 40)\n", "r, theta = np.meshgrid(r, theta)\n", @@ -181,14 +412,41 @@ "Y = r * np.cos(theta)\n", "Z = f(X, Y)\n", "\n", - "ax = plt.axes(projection=\"3d\")\n", + "ax = mpl_plt.axes(projection=\"3d\")\n", + "ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=\"viridis\", edgecolor=\"none\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b43b363d81ae4b689946ece5c682cd59", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "r = np.linspace(0, 6, 20)\n", + "theta = np.linspace(-0.9 * np.pi, 0.8 * np.pi, 40)\n", + "r, theta = np.meshgrid(r, theta)\n", + "\n", + "X = r * np.sin(theta)\n", + "Y = r * np.cos(theta)\n", + "Z = f(X, Y)\n", + "\n", + "ax = xy_plt.axes(projection=\"3d\")\n", "ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=\"viridis\", edgecolor=\"none\");" ] }, { "cell_type": "markdown", "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, "source": [ "## Surface Triangulations" ] @@ -196,10 +454,14 @@ { "cell_type": "code", "execution_count": null, - "id": "c3933fab20d04ec698c2621248eb3be0", - "metadata": {}, + "id": "mpl-c3933fab20d04ec698c2621248eb3be0", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "c3933fab20d04ec698c2621248eb3be0" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "theta = 2 * np.pi * np.random.random(1000)\n", "r = 6 * np.random.random(1000)\n", "x = np.ravel(r * np.sin(theta))\n", @@ -210,29 +472,89 @@ { "cell_type": "code", "execution_count": null, - "id": "4dd4641cc4064e0191573fe9c69df29b", - "metadata": {}, + "id": "xy-c3933fab20d04ec698c2621248eb3be0", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "c3933fab20d04ec698c2621248eb3be0" + }, "outputs": [], "source": [ - "ax = plt.axes(projection=\"3d\")\n", + "# xy.pyplot\n", + "theta = 2 * np.pi * np.random.random(1000)\n", + "r = 6 * np.random.random(1000)\n", + "x = np.ravel(r * np.sin(theta))\n", + "y = np.ravel(r * np.cos(theta))\n", + "z = f(x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-4dd4641cc4064e0191573fe9c69df29b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "ax = mpl_plt.axes(projection=\"3d\")\n", + "ax.scatter(x, y, z, c=z, cmap=\"viridis\", linewidth=0.5);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-4dd4641cc4064e0191573fe9c69df29b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "ax = xy_plt.axes(projection=\"3d\")\n", "ax.scatter(x, y, z, c=z, cmap=\"viridis\", linewidth=0.5);" ] }, { "cell_type": "code", "execution_count": null, - "id": "8309879909854d7188b41380fd92a7c3", - "metadata": {}, + "id": "mpl-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "ax = mpl_plt.axes(projection=\"3d\")\n", + "ax.plot_trisurf(x, y, z, cmap=\"viridis\", edgecolor=\"none\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, "outputs": [], "source": [ - "ax = plt.axes(projection=\"3d\")\n", + "# xy.pyplot\n", + "ax = xy_plt.axes(projection=\"3d\")\n", "ax.plot_trisurf(x, y, z, cmap=\"viridis\", edgecolor=\"none\");" ] }, { "cell_type": "markdown", "id": "3ed186c9a28b402fb0bc4494df01f08d", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "3ed186c9a28b402fb0bc4494df01f08d" + }, "source": [ "## Example: Visualizing a Möbius Strip" ] @@ -240,10 +562,14 @@ { "cell_type": "code", "execution_count": null, - "id": "cb1e1581032b452c9409d6c6813c49d1", - "metadata": {}, + "id": "mpl-cb1e1581032b452c9409d6c6813c49d1", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "cb1e1581032b452c9409d6c6813c49d1" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "theta = np.linspace(0, 2 * np.pi, 30)\n", "w = np.linspace(-0.25, 0.25, 8)\n", "w, theta = np.meshgrid(w, theta)" @@ -252,20 +578,77 @@ { "cell_type": "code", "execution_count": null, - "id": "379cbbc1e968416e875cc15c1202d7eb", - "metadata": {}, + "id": "xy-cb1e1581032b452c9409d6c6813c49d1", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "cb1e1581032b452c9409d6c6813c49d1" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "theta = np.linspace(0, 2 * np.pi, 30)\n", + "w = np.linspace(-0.25, 0.25, 8)\n", + "w, theta = np.meshgrid(w, theta)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-379cbbc1e968416e875cc15c1202d7eb", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "379cbbc1e968416e875cc15c1202d7eb" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "phi = 0.5 * theta" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-379cbbc1e968416e875cc15c1202d7eb", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "379cbbc1e968416e875cc15c1202d7eb" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "phi = 0.5 * theta" ] }, { "cell_type": "code", "execution_count": null, - "id": "277c27b1587741f2af2001be3712ef0d", - "metadata": {}, + "id": "mpl-277c27b1587741f2af2001be3712ef0d", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "277c27b1587741f2af2001be3712ef0d" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "# radius in x-y plane\n", + "r = 1 + w * np.cos(phi)\n", + "\n", + "x = np.ravel(r * np.cos(theta))\n", + "y = np.ravel(r * np.sin(theta))\n", + "z = np.ravel(w * np.sin(phi))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-277c27b1587741f2af2001be3712ef0d", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "277c27b1587741f2af2001be3712ef0d" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "# radius in x-y plane\n", "r = 1 + w * np.cos(phi)\n", "\n", @@ -277,16 +660,44 @@ { "cell_type": "code", "execution_count": null, - "id": "db7b79bc585a40fcaf58bf750017e135", - "metadata": {}, + "id": "mpl-db7b79bc585a40fcaf58bf750017e135", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "db7b79bc585a40fcaf58bf750017e135" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "# triangulate in the underlying parametrization\n", + "from matplotlib.tri import Triangulation as mpl_Triangulation # was matplotlib.tri\n", + "\n", + "tri = mpl_Triangulation(np.ravel(w), np.ravel(theta))\n", + "\n", + "ax = mpl_plt.axes(projection=\"3d\")\n", + "ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=\"Greys\", linewidths=0.2)\n", + "ax.set_xlim(-1, 1)\n", + "ax.set_ylim(-1, 1)\n", + "ax.set_zlim(-1, 1)\n", + "ax.axis(\"off\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-db7b79bc585a40fcaf58bf750017e135", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "db7b79bc585a40fcaf58bf750017e135" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "# triangulate in the underlying parametrization\n", - "from xy.pyplot import Triangulation # was matplotlib.tri\n", + "from xy.pyplot import Triangulation as xy_Triangulation # was matplotlib.tri\n", "\n", - "tri = Triangulation(np.ravel(w), np.ravel(theta))\n", + "tri = xy_Triangulation(np.ravel(w), np.ravel(theta))\n", "\n", - "ax = plt.axes(projection=\"3d\")\n", + "ax = xy_plt.axes(projection=\"3d\")\n", "ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=\"Greys\", linewidths=0.2)\n", "ax.set_xlim(-1, 1)\n", "ax.set_ylim(-1, 1)\n", @@ -303,7 +714,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/pdsh_04_14_visualization_with_seaborn.ipynb b/examples/pdsh/pdsh_04_14_visualization_with_seaborn.ipynb index 01d2fb72..047c5b82 100644 --- a/examples/pdsh/pdsh_04_14_visualization_with_seaborn.ipynb +++ b/examples/pdsh/pdsh_04_14_visualization_with_seaborn.ipynb @@ -1,9 +1,25 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "xy-pdsh-dual-engine-intro", + "metadata": { + "xy_pdsh_engine": "shared" + }, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed by its matching xy.pyplot example, using distinct plotting aliases so the two engines do not replace one another. xy.pyplot itself flushes open figures at the end of each Jupyter cell.\n" + ] + }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7fb27b941602401d91542211134fc71a" + }, "source": [ "# Visualization With Seaborn — with `xy.pyplot`\n", "\n", @@ -14,7 +30,11 @@ { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "acae54e37e7d407bbb7b55eff062a284" + }, "source": [ "# Visualization with Seaborn" ] @@ -22,11 +42,34 @@ { "cell_type": "code", "execution_count": null, - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, + "id": "mpl-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import matplotlib.pyplot as mpl_plt\n", + "import seaborn as sns\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "sns.set() # seaborn's method to set its chart style" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "9a63283cbaf04dbcab1f6479b197f3a8" + }, "outputs": [], "source": [ - "import xy.pyplot as plt\n", + "# xy.pyplot\n", + "import xy.pyplot as xy_plt\n", "import seaborn as sns\n", "import numpy as np\n", "import pandas as pd\n", @@ -37,7 +80,11 @@ { "cell_type": "markdown", "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "8dd0d8092fe74a7c96281538738b07e2" + }, "source": [ "## Exploring Seaborn Plots" ] @@ -45,7 +92,11 @@ { "cell_type": "markdown", "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "72eea5119410473aa328ad9291626812" + }, "source": [ "### Histograms, KDE, and Densities" ] @@ -53,41 +104,103 @@ { "cell_type": "code", "execution_count": null, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, + "id": "mpl-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "data = np.random.multivariate_normal([0, 0], [[5, 2], [2, 2]], size=2000)\n", + "data = pd.DataFrame(data, columns=[\"x\", \"y\"])\n", + "\n", + "for col in \"xy\":\n", + " mpl_plt.hist(data[col], density=True, alpha=0.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8edb47106e1a46a883d545849b8ab81b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8edb47106e1a46a883d545849b8ab81b" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "data = np.random.multivariate_normal([0, 0], [[5, 2], [2, 2]], size=2000)\n", "data = pd.DataFrame(data, columns=[\"x\", \"y\"])\n", "\n", "for col in \"xy\":\n", - " plt.hist(data[col], density=True, alpha=0.5)" + " xy_plt.hist(data[col], density=True, alpha=0.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "sns.kdeplot(data=data, shade=True);" ] }, { "cell_type": "code", "execution_count": null, - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, + "id": "xy-10185d26023b46108eb7d9f57d49d2b3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "10185d26023b46108eb7d9f57d49d2b3" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "sns.kdeplot(data=data, shade=True);" ] }, { "cell_type": "code", "execution_count": null, - "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, + "id": "mpl-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", + "sns.kdeplot(data=data, x=\"x\", y=\"y\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8763a12b2bbd4a93a75aff182afb95dc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8763a12b2bbd4a93a75aff182afb95dc" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "sns.kdeplot(data=data, x=\"x\", y=\"y\");" ] }, { "cell_type": "markdown", "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "7623eae2785240b9bd12b16a66d81610" + }, "source": [ "### Pair Plots" ] @@ -95,10 +208,29 @@ { "cell_type": "code", "execution_count": null, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, + "id": "mpl-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "iris = sns.load_dataset(\"iris\")\n", + "iris.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-7cdc8c89c7104fffa095e18ddfef8986", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "7cdc8c89c7104fffa095e18ddfef8986" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "iris = sns.load_dataset(\"iris\")\n", "iris.head()" ] @@ -106,17 +238,39 @@ { "cell_type": "code", "execution_count": null, - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, + "id": "mpl-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", + "sns.pairplot(iris, hue=\"species\", height=2.5);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-b118ea5561624da68c537baed56e602f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "b118ea5561624da68c537baed56e602f" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "sns.pairplot(iris, hue=\"species\", height=2.5);" ] }, { "cell_type": "markdown", "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "938c804e27f84196a10c8828c723f798" + }, "source": [ "### Faceted Histograms" ] @@ -124,10 +278,14 @@ { "cell_type": "code", "execution_count": null, - "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, + "id": "mpl-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "tips = sns.load_dataset(\"tips\")\n", "tips.head()" ] @@ -135,20 +293,60 @@ { "cell_type": "code", "execution_count": null, - "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, + "id": "xy-504fb2a444614c0babb325280ed9130a", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "504fb2a444614c0babb325280ed9130a" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "tips = sns.load_dataset(\"tips\")\n", + "tips.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "tips[\"tip_pct\"] = 100 * tips[\"tip\"] / tips[\"total_bill\"]\n", + "\n", + "grid = sns.FacetGrid(tips, row=\"sex\", col=\"time\", margin_titles=True)\n", + "grid.map(mpl_plt.hist, \"tip_pct\", bins=np.linspace(0, 40, 15));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-59bbdb311c014d738909a11f9e486628", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "59bbdb311c014d738909a11f9e486628" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "tips[\"tip_pct\"] = 100 * tips[\"tip\"] / tips[\"total_bill\"]\n", "\n", "grid = sns.FacetGrid(tips, row=\"sex\", col=\"time\", margin_titles=True)\n", - "grid.map(plt.hist, \"tip_pct\", bins=np.linspace(0, 40, 15));" + "grid.map(xy_plt.hist, \"tip_pct\", bins=np.linspace(0, 40, 15));" ] }, { "cell_type": "markdown", "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "b43b363d81ae4b689946ece5c682cd59" + }, "source": [ "### Categorical Plots" ] @@ -156,10 +354,30 @@ { "cell_type": "code", "execution_count": null, - "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, + "id": "mpl-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "with sns.axes_style(style=\"ticks\"):\n", + " g = sns.catplot(x=\"day\", y=\"total_bill\", hue=\"sex\", data=tips, kind=\"box\")\n", + " g.set_axis_labels(\"Day\", \"Total Bill\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8a65eabff63a45729fe45fb5ade58bdc", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8a65eabff63a45729fe45fb5ade58bdc" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "with sns.axes_style(style=\"ticks\"):\n", " g = sns.catplot(x=\"day\", y=\"total_bill\", hue=\"sex\", data=tips, kind=\"box\")\n", " g.set_axis_labels(\"Day\", \"Total Bill\")" @@ -168,7 +386,11 @@ { "cell_type": "markdown", "id": "c3933fab20d04ec698c2621248eb3be0", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "c3933fab20d04ec698c2621248eb3be0" + }, "source": [ "### Joint Distributions" ] @@ -176,10 +398,29 @@ { "cell_type": "code", "execution_count": null, - "id": "4dd4641cc4064e0191573fe9c69df29b", - "metadata": {}, + "id": "mpl-4dd4641cc4064e0191573fe9c69df29b", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "with sns.axes_style(\"white\"):\n", + " sns.jointplot(x=\"total_bill\", y=\"tip\", data=tips, kind=\"hex\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-4dd4641cc4064e0191573fe9c69df29b", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "4dd4641cc4064e0191573fe9c69df29b" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "with sns.axes_style(\"white\"):\n", " sns.jointplot(x=\"total_bill\", y=\"tip\", data=tips, kind=\"hex\")" ] @@ -187,17 +428,39 @@ { "cell_type": "code", "execution_count": null, - "id": "8309879909854d7188b41380fd92a7c3", - "metadata": {}, + "id": "mpl-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "sns.jointplot(x=\"total_bill\", y=\"tip\", data=tips, kind=\"reg\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-8309879909854d7188b41380fd92a7c3", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "8309879909854d7188b41380fd92a7c3" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "sns.jointplot(x=\"total_bill\", y=\"tip\", data=tips, kind=\"reg\");" ] }, { "cell_type": "markdown", "id": "3ed186c9a28b402fb0bc4494df01f08d", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "3ed186c9a28b402fb0bc4494df01f08d" + }, "source": [ "### Bar Plots" ] @@ -205,10 +468,29 @@ { "cell_type": "code", "execution_count": null, - "id": "cb1e1581032b452c9409d6c6813c49d1", - "metadata": {}, + "id": "mpl-cb1e1581032b452c9409d6c6813c49d1", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "cb1e1581032b452c9409d6c6813c49d1" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "planets = sns.load_dataset(\"planets\")\n", + "planets.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-cb1e1581032b452c9409d6c6813c49d1", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "cb1e1581032b452c9409d6c6813c49d1" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "planets = sns.load_dataset(\"planets\")\n", "planets.head()" ] @@ -216,10 +498,14 @@ { "cell_type": "code", "execution_count": null, - "id": "379cbbc1e968416e875cc15c1202d7eb", - "metadata": {}, + "id": "mpl-379cbbc1e968416e875cc15c1202d7eb", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "379cbbc1e968416e875cc15c1202d7eb" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "with sns.axes_style(\"white\"):\n", " g = sns.catplot(x=\"year\", data=planets, aspect=2, kind=\"count\", color=\"steelblue\")\n", " g.set_xticklabels(step=5)" @@ -228,10 +514,48 @@ { "cell_type": "code", "execution_count": null, - "id": "277c27b1587741f2af2001be3712ef0d", - "metadata": {}, + "id": "xy-379cbbc1e968416e875cc15c1202d7eb", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "379cbbc1e968416e875cc15c1202d7eb" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "with sns.axes_style(\"white\"):\n", + " g = sns.catplot(x=\"year\", data=planets, aspect=2, kind=\"count\", color=\"steelblue\")\n", + " g.set_xticklabels(step=5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-277c27b1587741f2af2001be3712ef0d", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "277c27b1587741f2af2001be3712ef0d" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "with sns.axes_style(\"white\"):\n", + " g = sns.catplot(\n", + " x=\"year\", data=planets, aspect=4.0, kind=\"count\", hue=\"method\", order=range(2001, 2015)\n", + " )\n", + " g.set_ylabels(\"Number of Planets Discovered\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-277c27b1587741f2af2001be3712ef0d", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "277c27b1587741f2af2001be3712ef0d" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "with sns.axes_style(\"white\"):\n", " g = sns.catplot(\n", " x=\"year\", data=planets, aspect=4.0, kind=\"count\", hue=\"method\", order=range(2001, 2015)\n", @@ -242,7 +566,11 @@ { "cell_type": "markdown", "id": "db7b79bc585a40fcaf58bf750017e135", - "metadata": {}, + "metadata": { + "xy_pdsh_engine": "shared", + "xy_pdsh_source": true, + "xy_pdsh_source_id": "db7b79bc585a40fcaf58bf750017e135" + }, "source": [ "## Example: Exploring Marathon Finishing Times" ] @@ -250,10 +578,14 @@ { "cell_type": "code", "execution_count": null, - "id": "916684f9a58a4a2aa5f864670399430d", - "metadata": {}, + "id": "mpl-916684f9a58a4a2aa5f864670399430d", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "916684f9a58a4a2aa5f864670399430d" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "# url = ('https://raw.githubusercontent.com/jakevdp/'\n", "# 'marathon-data/master/marathon-data.csv')\n", "# !cd data && curl -O {url}" @@ -262,10 +594,45 @@ { "cell_type": "code", "execution_count": null, - "id": "1671c31a24314836a5b85d7ef7fbf015", - "metadata": {}, + "id": "xy-916684f9a58a4a2aa5f864670399430d", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "916684f9a58a4a2aa5f864670399430d" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "# url = ('https://raw.githubusercontent.com/jakevdp/'\n", + "# 'marathon-data/master/marathon-data.csv')\n", + "# !cd data && curl -O {url}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-1671c31a24314836a5b85d7ef7fbf015", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "1671c31a24314836a5b85d7ef7fbf015" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "data = pd.read_csv(\"data/marathon-data.csv\")\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-1671c31a24314836a5b85d7ef7fbf015", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "1671c31a24314836a5b85d7ef7fbf015" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "data = pd.read_csv(\"data/marathon-data.csv\")\n", "data.head()" ] @@ -273,20 +640,67 @@ { "cell_type": "code", "execution_count": null, - "id": "33b0902fd34d4ace834912fa1002cf8e", - "metadata": {}, + "id": "mpl-33b0902fd34d4ace834912fa1002cf8e", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "33b0902fd34d4ace834912fa1002cf8e" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "data.dtypes" ] }, { "cell_type": "code", "execution_count": null, - "id": "f6fa52606d8c4a75a9b52967216f8f3f", - "metadata": {}, + "id": "xy-33b0902fd34d4ace834912fa1002cf8e", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "33b0902fd34d4ace834912fa1002cf8e" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "data.dtypes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-f6fa52606d8c4a75a9b52967216f8f3f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "f6fa52606d8c4a75a9b52967216f8f3f" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "import datetime\n", + "\n", + "\n", + "def convert_time(s):\n", + " h, m, s = map(int, s.split(\":\"))\n", + " return datetime.timedelta(hours=h, minutes=m, seconds=s)\n", + "\n", + "\n", + "data = pd.read_csv(\n", + " \"data/marathon-data.csv\", converters={\"split\": convert_time, \"final\": convert_time}\n", + ")\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-f6fa52606d8c4a75a9b52967216f8f3f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "f6fa52606d8c4a75a9b52967216f8f3f" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "import datetime\n", "\n", "\n", @@ -304,20 +718,42 @@ { "cell_type": "code", "execution_count": null, - "id": "f5a1fa73e5044315a093ec459c9be902", - "metadata": {}, + "id": "mpl-f5a1fa73e5044315a093ec459c9be902", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "f5a1fa73e5044315a093ec459c9be902" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "data.dtypes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-f5a1fa73e5044315a093ec459c9be902", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "f5a1fa73e5044315a093ec459c9be902" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "data.dtypes" ] }, { "cell_type": "code", "execution_count": null, - "id": "cdf66aed5cc84ca1b48e60bad68798a8", - "metadata": {}, + "id": "mpl-cdf66aed5cc84ca1b48e60bad68798a8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "cdf66aed5cc84ca1b48e60bad68798a8" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "data[\"split_sec\"] = data[\"split\"].astype(\"int64\") / 1e9\n", "data[\"final_sec\"] = data[\"final\"].astype(\"int64\") / 1e9\n", "data.head()" @@ -326,10 +762,46 @@ { "cell_type": "code", "execution_count": null, - "id": "28d3efd5258a48a79c179ea5c6759f01", - "metadata": {}, + "id": "xy-cdf66aed5cc84ca1b48e60bad68798a8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "cdf66aed5cc84ca1b48e60bad68798a8" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "data[\"split_sec\"] = data[\"split\"].astype(\"int64\") / 1e9\n", + "data[\"final_sec\"] = data[\"final\"].astype(\"int64\") / 1e9\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-28d3efd5258a48a79c179ea5c6759f01", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "28d3efd5258a48a79c179ea5c6759f01" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "with sns.axes_style(\"white\"):\n", + " g = sns.jointplot(x=\"split_sec\", y=\"final_sec\", data=data, kind=\"hex\")\n", + " g.ax_joint.plot(np.linspace(4000, 16000), np.linspace(8000, 32000), \":k\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-28d3efd5258a48a79c179ea5c6759f01", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "28d3efd5258a48a79c179ea5c6759f01" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "with sns.axes_style(\"white\"):\n", " g = sns.jointplot(x=\"split_sec\", y=\"final_sec\", data=data, kind=\"hex\")\n", " g.ax_joint.plot(np.linspace(4000, 16000), np.linspace(8000, 32000), \":k\")" @@ -338,10 +810,14 @@ { "cell_type": "code", "execution_count": null, - "id": "3f9bc0b9dd2c44919cc8dcca39b469f8", - "metadata": {}, + "id": "mpl-3f9bc0b9dd2c44919cc8dcca39b469f8", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "3f9bc0b9dd2c44919cc8dcca39b469f8" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "data[\"split_frac\"] = 1 - 2 * data[\"split_sec\"] / data[\"final_sec\"]\n", "data.head()" ] @@ -349,67 +825,183 @@ { "cell_type": "code", "execution_count": null, - "id": "0e382214b5f147d187d36a2058b9c724", - "metadata": {}, + "id": "xy-3f9bc0b9dd2c44919cc8dcca39b469f8", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "3f9bc0b9dd2c44919cc8dcca39b469f8" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "data[\"split_frac\"] = 1 - 2 * data[\"split_sec\"] / data[\"final_sec\"]\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-0e382214b5f147d187d36a2058b9c724", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "0e382214b5f147d187d36a2058b9c724" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", "sns.displot(data[\"split_frac\"], kde=False)\n", - "plt.axvline(0, color=\"k\", linestyle=\"--\");" + "mpl_plt.axvline(0, color=\"k\", linestyle=\"--\");" ] }, { "cell_type": "code", "execution_count": null, - "id": "5b09d5ef5b5e4bb6ab9b829b10b6a29f", - "metadata": {}, + "id": "xy-0e382214b5f147d187d36a2058b9c724", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "0e382214b5f147d187d36a2058b9c724" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "sns.displot(data[\"split_frac\"], kde=False)\n", + "xy_plt.axvline(0, color=\"k\", linestyle=\"--\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-5b09d5ef5b5e4bb6ab9b829b10b6a29f", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "5b09d5ef5b5e4bb6ab9b829b10b6a29f" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", "sum(data.split_frac < 0)" ] }, { "cell_type": "code", "execution_count": null, - "id": "a50416e276a0479cbe66534ed1713a40", - "metadata": {}, + "id": "xy-5b09d5ef5b5e4bb6ab9b829b10b6a29f", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "5b09d5ef5b5e4bb6ab9b829b10b6a29f" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "sum(data.split_frac < 0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-a50416e276a0479cbe66534ed1713a40", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "a50416e276a0479cbe66534ed1713a40" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "g = sns.PairGrid(\n", + " data, vars=[\"age\", \"split_sec\", \"final_sec\", \"split_frac\"], hue=\"gender\", palette=\"RdBu_r\"\n", + ")\n", + "g.map(mpl_plt.scatter, alpha=0.8)\n", + "g.add_legend();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-a50416e276a0479cbe66534ed1713a40", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "a50416e276a0479cbe66534ed1713a40" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "g = sns.PairGrid(\n", " data, vars=[\"age\", \"split_sec\", \"final_sec\", \"split_frac\"], hue=\"gender\", palette=\"RdBu_r\"\n", ")\n", - "g.map(plt.scatter, alpha=0.8)\n", + "g.map(xy_plt.scatter, alpha=0.8)\n", "g.add_legend();" ] }, { "cell_type": "code", "execution_count": null, - "id": "46a27a456b804aa2a380d5edf15a5daf", - "metadata": {}, + "id": "mpl-46a27a456b804aa2a380d5edf15a5daf", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "46a27a456b804aa2a380d5edf15a5daf" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "sns.kdeplot(data.split_frac[data.gender == \"M\"], label=\"men\", shade=True)\n", "sns.kdeplot(data.split_frac[data.gender == \"W\"], label=\"women\", shade=True)\n", - "plt.xlabel(\"split_frac\");" + "mpl_plt.xlabel(\"split_frac\");" ] }, { "cell_type": "code", "execution_count": null, - "id": "1944c39560714e6e80c856f20744a8e5", - "metadata": {}, + "id": "xy-46a27a456b804aa2a380d5edf15a5daf", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "46a27a456b804aa2a380d5edf15a5daf" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "sns.kdeplot(data.split_frac[data.gender == \"M\"], label=\"men\", shade=True)\n", + "sns.kdeplot(data.split_frac[data.gender == \"W\"], label=\"women\", shade=True)\n", + "xy_plt.xlabel(\"split_frac\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-1944c39560714e6e80c856f20744a8e5", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "1944c39560714e6e80c856f20744a8e5" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "sns.violinplot(x=\"gender\", y=\"split_frac\", data=data, palette=[\"lightblue\", \"lightpink\"]);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-1944c39560714e6e80c856f20744a8e5", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "1944c39560714e6e80c856f20744a8e5" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", "sns.violinplot(x=\"gender\", y=\"split_frac\", data=data, palette=[\"lightblue\", \"lightpink\"]);" ] }, { "cell_type": "code", "execution_count": null, - "id": "d6ca27006b894b04b6fc8b79396e2797", - "metadata": {}, + "id": "mpl-d6ca27006b894b04b6fc8b79396e2797", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "d6ca27006b894b04b6fc8b79396e2797" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "data[\"age_dec\"] = data.age.map(lambda age: 10 * (age // 10))\n", "data.head()" ] @@ -417,10 +1009,55 @@ { "cell_type": "code", "execution_count": null, - "id": "f61877af4e7f4313ad8234302950b331", - "metadata": {}, + "id": "xy-d6ca27006b894b04b6fc8b79396e2797", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "d6ca27006b894b04b6fc8b79396e2797" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "data[\"age_dec\"] = data.age.map(lambda age: 10 * (age // 10))\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-f61877af4e7f4313ad8234302950b331", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "f61877af4e7f4313ad8234302950b331" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", + "men = data.gender == \"M\"\n", + "women = data.gender == \"W\"\n", + "\n", + "with sns.axes_style(style=None):\n", + " sns.violinplot(\n", + " x=\"age_dec\",\n", + " y=\"split_frac\",\n", + " hue=\"gender\",\n", + " data=data,\n", + " split=True,\n", + " inner=\"quartile\",\n", + " palette=[\"lightblue\", \"lightpink\"],\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-f61877af4e7f4313ad8234302950b331", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "f61877af4e7f4313ad8234302950b331" + }, "outputs": [], "source": [ + "# xy.pyplot\n", "men = data.gender == \"M\"\n", "women = data.gender == \"W\"\n", "\n", @@ -439,24 +1076,63 @@ { "cell_type": "code", "execution_count": null, - "id": "84d5ab97d17b4c38ab41a2b065bbd0c0", - "metadata": {}, + "id": "mpl-84d5ab97d17b4c38ab41a2b065bbd0c0", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "84d5ab97d17b4c38ab41a2b065bbd0c0" + }, "outputs": [], "source": [ + "# Matplotlib reference\n", "(data.age > 80).sum()" ] }, { "cell_type": "code", "execution_count": null, - "id": "35ffc1ce1c7b4df9ace1bc936b8b1dc2", - "metadata": {}, + "id": "xy-84d5ab97d17b4c38ab41a2b065bbd0c0", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "84d5ab97d17b4c38ab41a2b065bbd0c0" + }, "outputs": [], "source": [ + "# xy.pyplot\n", + "(data.age > 80).sum()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mpl-35ffc1ce1c7b4df9ace1bc936b8b1dc2", + "metadata": { + "xy_pdsh_engine": "matplotlib", + "xy_pdsh_source_id": "35ffc1ce1c7b4df9ace1bc936b8b1dc2" + }, + "outputs": [], + "source": [ + "# Matplotlib reference\n", "g = sns.lmplot(\n", " x=\"final_sec\", y=\"split_frac\", col=\"gender\", data=data, markers=\".\", scatter_kws=dict(color=\"c\")\n", ")\n", - "g.map(plt.axhline, y=0.0, color=\"k\", ls=\":\");" + "g.map(mpl_plt.axhline, y=0.0, color=\"k\", ls=\":\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "xy-35ffc1ce1c7b4df9ace1bc936b8b1dc2", + "metadata": { + "xy_pdsh_engine": "xy", + "xy_pdsh_source_id": "35ffc1ce1c7b4df9ace1bc936b8b1dc2" + }, + "outputs": [], + "source": [ + "# xy.pyplot\n", + "g = sns.lmplot(\n", + " x=\"final_sec\", y=\"split_frac\", col=\"gender\", data=data, markers=\".\", scatter_kws=dict(color=\"c\")\n", + ")\n", + "g.map(xy_plt.axhline, y=0.0, color=\"k\", ls=\":\");" ] } ], @@ -468,7 +1144,8 @@ }, "language_info": { "name": "python" - } + }, + "xy_pdsh_dual_engine": true }, "nbformat": 4, "nbformat_minor": 5 diff --git a/examples/pdsh/sync_dual_engine_notebooks.py b/examples/pdsh/sync_dual_engine_notebooks.py new file mode 100644 index 00000000..3cbc0db9 --- /dev/null +++ b/examples/pdsh/sync_dual_engine_notebooks.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +"""Keep the Matplotlib and xy sections inside each PDSH notebook synchronized.""" + +from __future__ import annotations + +import argparse +import copy +import json +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parent +SHARED_ID = "xy-pdsh-dual-engine-intro" +IMPORT_REPLACEMENTS = { + "import xy.pyplot as ": "import matplotlib.pyplot as ", + "from xy.pyplot import LinearSegmentedColormap": ( + "from matplotlib.colors import LinearSegmentedColormap" + ), + "from xy.pyplot import Legend": "from matplotlib.legend import Legend", + "from xy.pyplot import Triangulation": "from matplotlib.tri import Triangulation", + "from xy.pyplot import cycler": "from matplotlib import cycler", +} +ENGINE_NAMES = ( + "plt", + "mpl", + "LinearSegmentedColormap", + "Legend", + "Triangulation", + "cycler", +) + + +def notebooks() -> list[Path]: + return sorted(ROOT.glob("pdsh_*.ipynb")) + + +def _xy_cells(notebook: dict) -> list[dict]: + marked = [ + cell + for cell in notebook["cells"] + if cell.get("metadata", {}).get("xy_pdsh_engine") == "xy" + or cell.get("metadata", {}).get("xy_pdsh_source") is True + ] + if marked: + canonical = copy.deepcopy(marked) + for cell in canonical: + metadata = cell.setdefault("metadata", {}) + cell["id"] = metadata.get("xy_pdsh_source_id", cell["id"]) + if cell.get("cell_type") == "code": + if cell.get("source", [None])[0] == "# xy.pyplot\n": + cell["source"] = cell["source"][1:] + if metadata.get("xy_pdsh_auto_show") is True: + cell["source"] = cell["source"][:-3] + for index, line in enumerate(cell.get("source", [])): + for name in ENGINE_NAMES: + line = re.sub(rf"\bxy_{re.escape(name)}\b", name, line) + for name in ENGINE_NAMES[2:]: + line = line.replace(f" import {name} as {name}", f" import {name}") + cell["source"][index] = line + for key in ( + "xy_pdsh_engine", + "xy_pdsh_source", + "xy_pdsh_source_id", + "xy_pdsh_auto_show", + ): + metadata.pop(key, None) + return canonical + return notebook["cells"] + + +def _engine_cell(cell: dict, engine: str) -> dict: + made = copy.deepcopy(cell) + made.setdefault("metadata", {})["xy_pdsh_engine"] = engine + made["metadata"]["xy_pdsh_source_id"] = cell["id"] + prefix = "mpl" if engine == "matplotlib" else "xy" + made["id"] = f"{prefix}-{made['id']}" + replaced = [] + for line in made.get("source", []): + if engine == "matplotlib": + for old, new in IMPORT_REPLACEMENTS.items(): + line = line.replace(old, new) + for name in ENGINE_NAMES: + line = re.sub(rf"\b{re.escape(name)}\b", f"{prefix}_{name}", line) + for name in ENGINE_NAMES[2:]: + line = line.replace(f" import {prefix}_{name}", f" import {name} as {prefix}_{name}") + replaced.append(line) + title = "Matplotlib reference" if engine == "matplotlib" else "xy.pyplot" + made["source"] = [f"# {title}\n", *replaced] + made["execution_count"] = None + made["outputs"] = [] + return made + + +def build(notebook: dict) -> dict: + xy_cells = copy.deepcopy(_xy_cells(notebook)) + intro = { + "cell_type": "markdown", + "id": SHARED_ID, + "metadata": {"xy_pdsh_engine": "shared"}, + "source": [ + "# Cell-by-cell Matplotlib ↔ xy comparison\n", + "\n", + "Run all cells from the top. Every Matplotlib example is immediately followed " + "by its matching xy.pyplot example, using distinct plotting aliases so the two " + "engines do not replace one another. xy.pyplot itself flushes open figures at " + "the end of each Jupyter cell.\n", + ], + } + paired = [] + for cell in xy_cells: + if cell.get("cell_type") == "markdown": + shared = copy.deepcopy(cell) + shared.setdefault("metadata", {})["xy_pdsh_engine"] = "shared" + shared["metadata"]["xy_pdsh_source"] = True + shared["metadata"]["xy_pdsh_source_id"] = cell["id"] + paired.append(shared) + continue + paired.extend([_engine_cell(cell, "matplotlib"), _engine_cell(cell, "xy")]) + result = copy.deepcopy(notebook) + result["cells"] = [intro, *paired] + result.setdefault("metadata", {})["xy_pdsh_dual_engine"] = True + return result + + +def serialized(path: Path) -> str: + notebook = json.loads(path.read_text(encoding="utf-8")) + return json.dumps(build(notebook), indent=1, ensure_ascii=False) + "\n" + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--check", action="store_true") + args = parser.parse_args() + stale = [] + for path in notebooks(): + expected = serialized(path) + if args.check: + if path.read_text(encoding="utf-8") != expected: + stale.append(path.name) + else: + path.write_text(expected, encoding="utf-8") + print(f"updated {path.name}") + if stale: + parser.error("stale dual-engine notebook(s): " + ", ".join(stale)) + if args.check: + print(f"{len(notebooks())} dual-engine notebooks are fresh") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/js/src/30_ticks.js b/js/src/30_ticks.js index f97f1e0b..376a4899 100644 --- a/js/src/30_ticks.js +++ b/js/src/30_ticks.js @@ -6,7 +6,7 @@ function niceStep(rough) { rough = Math.abs(rough); if (!Number.isFinite(rough) || rough <= 0) return 1; const mag = Math.pow(10, Math.floor(Math.log10(rough))); - for (const m of [1, 2, 5, 10]) { + for (const m of [1, 2, 2.5, 5, 10]) { if (rough <= m * mag * (1 + 1e-12)) return m * mag; } return 10 * mag; @@ -127,13 +127,11 @@ function fmtTime(ms, step) { } function fmtLinear(v, step) { - if (v === 0) return "0"; const av = Math.abs(v); - if (av >= 1e6 || av < 1e-4) return v.toExponential(1).replace("e+", "e"); - const dec = Math.max(0, -Math.floor(Math.log10(step)) + (step < 1 ? 1 : 0)); - let s = v.toFixed(Math.min(dec, 8)); - if (s.includes(".")) s = s.replace(/0+$/, "").replace(/\.$/, ""); - return s; + if (av >= 1e6 || (av !== 0 && av < 1e-4)) return v.toExponential(1).replace("e+", "e"); + let dec = step ? Math.max(0, Math.ceil(-Math.log10(Math.abs(step)))) : 0; + while (dec < 8 && Math.abs(Number(step.toFixed(dec)) - step) > Math.abs(step) / 1000) dec++; + return v.toFixed(Math.min(dec, 8)); } function fmtCategory(v, categories) { diff --git a/js/src/40_gl.js b/js/src/40_gl.js index 7430a0dd..add4d95a 100644 --- a/js/src/40_gl.js +++ b/js/src/40_gl.js @@ -151,16 +151,25 @@ float fcMarkerSdf(vec2 d, int shape) { float h = clamp(dot(p, ba) / dot(ba, ba), 0.0, 0.5); return length(p - ba * h) * sign(p.y * ba.x - p.x * ba.y); } - if (shape == 3) { // triangle (apex up) + if (shape == 3 || shape == 8 || shape == 9 || shape == 10) { // directional triangle const float k = 1.7320508; float r = 0.62; - vec2 p = vec2(d.x, -d.y); // flip so the apex points up + vec2 q = d; + if (shape == 8) q = -d; + if (shape == 9) q = vec2(d.y, -d.x); + if (shape == 10) q = vec2(-d.y, d.x); + vec2 p = vec2(q.x, -q.y); // flip so the canonical apex points up p.x = abs(p.x) - r; p.y = p.y + r / k; if (p.x + k * p.y > 0.0) p = vec2(p.x - k * p.y, -k * p.x - p.y) / 2.0; p.x -= clamp(p.x, -2.0 * r, 0.0); return -length(p) * sign(p.y); } + if (shape == 11) { // diagonal x + vec2 q = vec2(d.x + d.y, d.y - d.x) * 0.707106781; + vec2 a = abs(q); + return min(max(a.x - 0.17, a.y - 0.5), max(a.x - 0.5, a.y - 0.17)); + } return length(d) - 0.5; // circle }`; diff --git a/js/src/50_chartview.js b/js/src/50_chartview.js index 435853f1..bd20da99 100644 --- a/js/src/50_chartview.js +++ b/js/src/50_chartview.js @@ -865,6 +865,13 @@ class ChartView { `display:grid;grid-template-columns:repeat(${horizontal ? ncols : 1},max-content);` + "overflow:auto;" + `max-height:${this.plot.h - 12}px;`; this._applySlot(lg, "legend"); + if (options.title) { + const title = document.createElement("div"); + title.textContent = String(options.title); + title.style.fontWeight = "600"; + title.style.gridColumn = `1 / span ${horizontal ? ncols : 1}`; + lg.appendChild(title); + } for (const it of items) { const row = document.createElement("div"); this._applySlot(row, "legend_item"); @@ -1096,7 +1103,7 @@ class ChartView { // no color borders in the mark color (matches the rect family). _pointMarkStyle(g, t) { const s = t.style || {}; - g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5, pentagon: 6, star: 7 }[s.symbol] || 0; + g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5, pentagon: 6, star: 7, triangle_down: 8, triangle_left: 9, triangle_right: 10, x: 11 }[s.symbol] || 0; g.pointStrokeWidth = Number(s.stroke_width) || 0; const markOpaque = [g.color[0], g.color[1], g.color[2], 1]; g.pointStroke = s.stroke @@ -2511,15 +2518,18 @@ class ChartView { "pointer-events:none;"; this.labels.appendChild(d); }; + const frameSides = Array.isArray(s.frame_sides) + ? s.frame_sides + : [xAxis.side || "bottom", yAxis.side || "left"]; if (!hideY) { const yWidth = Math.max(1, Math.round(this._axisStyleNumber(yAxis, "axis_width", 1))); - const yAxisX = yAxis.side === "right" ? p.x + p.w - yWidth : p.x; - rule(yAxis, yAxisX, p.y, yWidth, p.h); + if (frameSides.includes("left")) rule(yAxis, p.x, p.y, yWidth, p.h); + if (frameSides.includes("right")) rule(yAxis, p.x + p.w - yWidth, p.y, yWidth, p.h); } if (!hideX) { const xHeight = Math.max(1, Math.round(this._axisStyleNumber(xAxis, "axis_width", 1))); - const xTop = xAxis.side === "top" ? p.y : p.y + p.h - xHeight; - rule(xAxis, p.x, xTop, p.w, xHeight); + if (frameSides.includes("top")) rule(xAxis, p.x, p.y, p.w, xHeight); + if (frameSides.includes("bottom")) rule(xAxis, p.x, p.y + p.h - xHeight, p.w, xHeight); } for (const axis of Object.values(this.axes)) { if (!axis || axis.id === "y" || !String(axis.id || "").startsWith("y")) continue; diff --git a/js/src/55_marks.js b/js/src/55_marks.js index 6baa7397..f83e4164 100644 --- a/js/src/55_marks.js +++ b/js/src/55_marks.js @@ -154,13 +154,17 @@ const MARK_KINDS = { triangle_mesh: MESH_MARK, error_band: AREA_MARK, hexbin: { - build: (view, g, t, buffer) => view._buildScatterMark(g, t, buffer), + build: (view, g, t, buffer) => view._buildMeshMark(g, t, buffer), draw: (view, g) => { const [x0, x1] = view._axisRange(g.xAxis); const [y0, y1] = view._axisRange(g.yAxis); - view._drawPoints(g, view._map(g.xMeta, x0, x1, g.xAxis), view._map(g.yMeta, y0, y1, g.yAxis)); + view._drawMesh(g, view._map(g.x0Meta, x0, x1, g.xAxis), view._map(g.y0Meta, y0, y1, g.yAxis)); + }, + refreshColor: (view, g) => { + if (g.colorMode === 0 && g.trace.color) g.color = parseColor(view.root, g.trace.color.color, g.color); + const style = g.trace.style || {}; + g.meshStroke = parseColor(view.root, style.stroke || "transparent", [0, 0, 0, 0]); }, - refreshColor: (view, g) => view._pointMarkStyle(g, g.trace), }, bar: BAR_MARK, column: BAR_MARK, diff --git a/pyproject.toml b/pyproject.toml index 028eb5dd..50834388 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -122,10 +122,20 @@ ignore = [ "RUF100", # unused noqa (kept for editors running broader rule sets) ] +[tool.ruff.format] +# The dual-engine PDSH notebooks mirror each cell verbatim across the two +# plotting aliases (mpl_plt/xy_plt). The aliases differ in length, so a line +# near the width limit wraps in one copy and not the other — the formatter and +# sync_dual_engine_notebooks.py --check can never both pass. The sync script +# owns these files' layout. +exclude = ["examples/pdsh/*.ipynb"] + [tool.ruff.lint.per-file-ignores] # Third-party example code (PDSH notebooks): kept faithful to upstream, so -# upstream's import placement and naming style is not ours to lint. -"examples/pdsh/*.ipynb" = ["E402", "E731", "E741", "F401", "I001", "UP030", "UP032", "B007"] +# upstream's import placement and naming style is not ours to lint. F811: the +# dual-engine layout mirrors every cell (matplotlib then xy), so imports and +# helper defs repeat by design. +"examples/pdsh/*.ipynb" = ["E402", "E731", "E741", "F401", "F811", "I001", "UP030", "UP032", "B007"] [tool.ruff.lint.isort] known-first-party = ["fastcharts", "xy"] diff --git a/python/xy/_figure.py b/python/xy/_figure.py index 0a30c615..a6f15f02 100644 --- a/python/xy/_figure.py +++ b/python/xy/_figure.py @@ -105,6 +105,9 @@ def __init__( self.traces: list[Trace] = [] self.show_legend = True self.legend_options: dict[str, Any] = {} + # None keeps the declarative engine's two-axis baseline convention; + # pyplot sets an explicit Matplotlib-style spine list. + self.frame_sides: Optional[list[str]] = None self.colorbar_options: Optional[dict[str, Any]] = None self.show_modebar = True self.show_tooltip = True diff --git a/python/xy/_hosts.py b/python/xy/_hosts.py index 7f7859b8..55053240 100644 --- a/python/xy/_hosts.py +++ b/python/xy/_hosts.py @@ -32,6 +32,7 @@ class FigureHost(Protocol): tooltip: Optional[dict[str, Any]] show_legend: bool legend_options: dict[str, Any] + frame_sides: Optional[list[str]] colorbar_options: Optional[dict[str, Any]] show_modebar: bool show_tooltip: bool diff --git a/python/xy/_payload.py b/python/xy/_payload.py index a260c00a..5885de34 100644 --- a/python/xy/_payload.py +++ b/python/xy/_payload.py @@ -189,6 +189,8 @@ def axis_range(axis_id: str) -> tuple[float, float]: } if self.legend_options: spec["legend"] = self.legend_options + if self.frame_sides is not None: + spec["frame_sides"] = list(self.frame_sides) if self.colorbar_options: spec["colorbar"] = self.colorbar_options if self.show_modebar is False: @@ -358,8 +360,50 @@ def _emit_hexbin( sel = self._finite_sel(t, xv, yv) if sel is not None: xv, yv = xv[sel], yv[sel] - entry = self._base_entry(t, pw, xv, yv, "direct", self._default_styled(t)) - entry["color"], entry["size"] = self._ship_channels(t, sel, pw.ship_scalar, pw.ship_u8) + style = self._default_styled(t) + dx = float(style.pop("hex_dx")) + dy = float(style.pop("hex_dy")) + # Six center-fan triangles per occupied cell preserve a true data-space + # tessellation at every aspect ratio and in every renderer. + offsets = np.asarray( + [ + (0.0, -dy / 3.0), + (dx / 2.0, -dy / 6.0), + (dx / 2.0, dy / 6.0), + (0.0, dy / 3.0), + (-dx / 2.0, dy / 6.0), + (-dx / 2.0, -dy / 6.0), + (0.0, -dy / 3.0), + ], + dtype=np.float64, + ) + cx = np.repeat(xv, 6) + cy = np.repeat(yv, 6) + x1 = (xv[:, None] + offsets[:-1, 0]).reshape(-1) + y1 = (yv[:, None] + offsets[:-1, 1]).reshape(-1) + x2 = (xv[:, None] + offsets[1:, 0]).reshape(-1) + y2 = (yv[:, None] + offsets[1:, 1]).reshape(-1) + entry = { + "id": t.id, + "kind": t.kind, + "name": t.name, + "style": style, + "tier": "direct", + "n_points": t.n_points, + "n_marks": int(len(xv)), + "x_axis": t.x_axis, + "y_axis": t.y_axis, + "x0": pw.ship_values(cx), + "y0": pw.ship_values(cy), + "x1": pw.ship_values(x1), + "y1": pw.ship_values(y1), + "x2": pw.ship_values(x2), + "y2": pw.ship_values(y2), + } + triangle_sel = np.repeat( + np.arange(len(t.x), dtype=np.intp) if sel is None else np.asarray(sel), 6 + ) + entry["color"], _size = self._ship_channels(t, triangle_sel, pw.ship_scalar, pw.ship_u8) return entry def _emit_histogram( diff --git a/python/xy/_raster.py b/python/xy/_raster.py index f3098d57..3c1d3436 100644 --- a/python/xy/_raster.py +++ b/python/xy/_raster.py @@ -64,6 +64,10 @@ "hexagon": 5, "pentagon": 6, "star": 7, + "triangle_down": 8, + "triangle_left": 9, + "triangle_right": 10, + "x": 11, } @@ -538,8 +542,10 @@ def render_raster( _emit_line(cmd, t, blob, cols, sx, sy, style, color) elif kind in ("area", "error_band"): _emit_area(cmd, t, blob, cols, sx, sy, style, color, plot) - elif kind in ("scatter", "hexbin"): + elif kind == "scatter": _emit_scatter(cmd, t, blob, cols, sx, sy, style, color) + elif kind == "hexbin": + _emit_triangle_mesh(cmd, t, blob, cols, sx, sy, style, color) elif kind in {"errorbar", "stem", "box_whisker", "box_median", "contour", "segments"}: _emit_segments(cmd, t, blob, cols, sx, sy, style, color) elif kind in ("bar", "column") and t.get("bar"): @@ -562,11 +568,19 @@ def render_raster( hide_y = ya.get("tick_label_strategy") == "none" hide_x_labels = hide_x or xa.get("tick_label_strategy") == "off" hide_y_labels = hide_y or ya.get("tick_label_strategy") == "off" + frame_sides = spec.get("frame_sides") + if frame_sides is None: + frame_sides = [xa.get("side", "bottom"), ya.get("side", "left")] if not hide_y: - cmd.stroke([(px0, py0), (px0, py1)], 1.0, axis_c) + if "left" in frame_sides: + cmd.stroke([(px0, py0), (px0, py1)], 1.0, axis_c) + if "right" in frame_sides: + cmd.stroke([(px1, py0), (px1, py1)], 1.0, axis_c) if not hide_x: - x_axis_y = py0 if xa.get("side") == "top" else py1 - cmd.stroke([(px0, x_axis_y), (px1, x_axis_y)], 1.0, axis_c) + if "top" in frame_sides: + cmd.stroke([(px0, py0), (px1, py0)], 1.0, axis_c) + if "bottom" in frame_sides: + cmd.stroke([(px0, py1), (px1, py1)], 1.0, axis_c) text_c = _parse_color(_TEXT) if not hide_x_labels: @@ -1058,18 +1072,35 @@ def _emit_grid(cmd, kind, g, blob, cols, sx, sy, style): def _emit_legend(cmd, named, plot, options): - pad, handle, gap, line_h = 8, 20, 5, 16 + style_opts = options.get("style") or {} + pad, handle, gap, line_h = 8.0, 20, 5, 16.0 + if str(style_opts.get("padding", "")).endswith("em"): + pad = 11.0 * float(str(style_opts["padding"])[:-2]) + if str(style_opts.get("rowGap", "")).endswith("em"): + line_h = 11.0 * (1.0 + float(str(style_opts["rowGap"])[:-2])) ncols = min(len(named), max(1, int(options.get("ncols", 1)))) nrows = (len(named) + ncols - 1) // ncols + title = options.get("title") + title_h = 16 if title else 0 cell_w = max(len(str(t["name"])) for t in named) * 6.2 + handle + gap + 2 * pad - box_w, box_h = ncols * cell_w + pad, nrows * line_h + pad + box_w, box_h = ncols * cell_w + pad, nrows * line_h + pad + title_h loc = options.get("loc") or "upper right" x = plot["x"] + 6 if "left" in loc else plot["x"] + plot["w"] - box_w - 6 y = plot["y"] + plot["h"] - box_h - 6 if "lower" in loc else plot["y"] + 6 # frameon=False (background transparent) drops the box entirely (§ mpl parity). - style_opts = options.get("style") or {} if style_opts.get("background") != "transparent": - cmd.fill(_rect_pts(x, y, x + box_w, y + box_h), (128, 128, 128, 20)) + if style_opts.get("boxShadow"): + cmd.fill(_rect_pts(x + 2, y + 2, x + box_w + 2, y + box_h + 2), (0, 0, 0, 55)) + alpha = float(style_opts.get("--xy-legend-frame-alpha", 0.08)) + background = style_opts.get("background") + frame = ( + _rgba(background, "#808080", alpha) + if background + else (128, 128, 128, round(255 * alpha)) + ) + cmd.fill(_rect_pts(x, y, x + box_w, y + box_h), frame) + if title: + cmd.text(x + pad, y + pad / 2 + 11, 0, 11, _parse_color(_TEXT), str(title)) for i, t in enumerate(named): style = t.get("style") or {} color_str = _css( @@ -1078,7 +1109,7 @@ def _emit_legend(cmd, named, plot, options): ) c = _parse_color(color_str) col, row = i % ncols, i // ncols - rx, ry = x + col * cell_w, y + pad / 2 + row * line_h + rx, ry = x + col * cell_w, y + pad / 2 + title_h + row * line_h hx0, hx1, cy = rx + pad, rx + pad + handle, ry + 7 kind = t.get("kind") if kind == "scatter": diff --git a/python/xy/_svg.py b/python/xy/_svg.py index 52f13554..a25811f7 100644 --- a/python/xy/_svg.py +++ b/python/xy/_svg.py @@ -297,7 +297,7 @@ def _nice_step(rough: float) -> float: if not np.isfinite(rough) or rough <= 0: return 1.0 mag = 10.0 ** np.floor(np.log10(rough)) - for m in (1, 2, 5, 10): + for m in (1, 2, 2.5, 5, 10): if rough <= m * mag * (1 + 1e-12): return m * mag return 10 * mag @@ -436,20 +436,18 @@ def _fmt_time(ms: float, step: float) -> str: def _fmt_linear(v: float, step: float) -> str: - if v == 0: - return "0" av = abs(v) - if av >= 1e6 or av < 1e-4: + if av >= 1e6 or (av != 0 and av < 1e-4): return f"{v:.1e}".replace("e+0", "e").replace("e-0", "e-").replace("e+", "e") - dec = max(0, -int(np.floor(np.log10(step))) + (1 if step < 1 else 0)) + dec = max(0, int(np.ceil(-np.log10(abs(step))))) if step else 0 # A non-nice step (pi/2, 0.3333…) needs enough decimals to keep adjacent # ticks distinct; widen until the step itself round-trips at that precision. - while dec < 8 and abs(round(step, dec) - step) > step / 1000.0: + while dec < 8 and abs(round(step, dec) - step) > abs(step) / 1000.0: dec += 1 - s = f"{v:.{min(dec, 8)}f}" - if "." in s: - s = s.rstrip("0").rstrip(".") - return s + # ScalarFormatter uses one precision for the whole tick set. Retaining + # those zeros (0.00 beside ±0.25) makes magnitude and spacing legible and + # matches Matplotlib's default formatter. + return f"{v:.{min(dec, 8)}f}" def _fmt_axis(axis: dict[str, Any], v: float, step: float) -> str: @@ -717,12 +715,29 @@ def _step_arrays(xv: np.ndarray, yv: np.ndarray, where: str) -> tuple[np.ndarray "triangle": lambda cx, cy, r: ( f' str: + "/>" ) - elif kind in ("scatter", "hexbin"): + elif kind == "scatter": marks.append(_scatter_marks(t, blob, cols, sx, sy, style, color)) + elif kind == "hexbin": + marks.append(_triangle_mesh_marks(t, blob, cols, sx, sy, style, color)) + elif kind in {"errorbar", "stem", "box_whisker", "box_median", "contour", "segments"}: marks.append(_segment_marks(t, blob, cols, sx, sy, style, color)) @@ -991,18 +1009,24 @@ def line_attrs(style: dict[str, Any], color: str) -> str: # baselines above the marks, matching the client's overlay rules baselines = "" + frame_sides = spec.get("frame_sides") + if frame_sides is None: + frame_sides = [xa.get("side", "bottom"), ya.get("side", "left")] if not hide_y: - baselines += ( - f'' - ) + for side, x in (("left", plot["x"]), ("right", plot["x"] + plot["w"])): + if side in frame_sides: + baselines += ( + f'' + ) if not hide_x: - x_axis_y = plot["y"] if xa.get("side") == "top" else plot["y"] + plot["h"] - baselines += ( - f'' - ) + for side, y in (("top", plot["y"]), ("bottom", plot["y"] + plot["h"])): + if side in frame_sides: + baselines += ( + f'' + ) clip_id = svg.uid("clip") svg.defs.append( @@ -1396,19 +1420,43 @@ def _heatmap_image(hm: dict, blob: bytes, cols: list, sx: _Scale, sy: _Scale, st def _legend(named: list[dict], plot: dict, options: dict) -> str: rows = [] - pad, handle, gap, line_h = 8, 20, 5, 16 + style_opts = options.get("style") or {} + pad, handle, gap, line_h = 8.0, 20, 5, 16.0 + if str(style_opts.get("padding", "")).endswith("em"): + pad = 11.0 * float(str(style_opts["padding"])[:-2]) + if str(style_opts.get("rowGap", "")).endswith("em"): + line_h = 11.0 * (1.0 + float(str(style_opts["rowGap"])[:-2])) ncols = min(len(named), max(1, int(options.get("ncols", 1)))) nrows = (len(named) + ncols - 1) // ncols + title = options.get("title") + title_h = 16 if title else 0 cell_w = max(len(str(t["name"])) for t in named) * 6.2 + handle + gap + 2 * pad - box_w, box_h = ncols * cell_w + pad, nrows * line_h + pad + box_w, box_h = ncols * cell_w + pad, nrows * line_h + pad + title_h loc = options.get("loc") or "upper right" x = plot["x"] + 6 if "left" in loc else plot["x"] + plot["w"] - box_w - 6 y = plot["y"] + plot["h"] - box_h - 6 if "lower" in loc else plot["y"] + 6 - style_opts = options.get("style") or {} if style_opts.get("background") != "transparent": + if style_opts.get("boxShadow"): + rows.append( + f'' + ) + alpha = float(style_opts.get("--xy-legend-frame-alpha", 0.08)) + radius = "4" if style_opts.get("borderRadius") else "0" + background_value = style_opts.get("background") + if background_value is None and alpha == 0.08: + fill_attrs = 'fill="rgba(128,128,128,0.08)"' + else: + background = _css(background_value, "#808080") + fill_attrs = f'fill="{escape(background)}" fill-opacity="{_num(alpha)}"' rows.append( f'' + f'rx="{radius}" {fill_attrs}/>' + ) + if title: + rows.append( + f'{escape(str(title))}' ) for i, t in enumerate(named): style = t.get("style") or {} @@ -1417,7 +1465,7 @@ def _legend(named: list[dict], plot: dict, options: dict) -> str: DEFAULT_PALETTE[i % len(DEFAULT_PALETTE)], ) col, row = i % ncols, i // ncols - rx, ry = x + col * cell_w, y + pad / 2 + row * line_h + rx, ry = x + col * cell_w, y + pad / 2 + title_h + row * line_h hx0, hx1, cy = rx + pad, rx + pad + handle, ry + 7 kind = t.get("kind") if kind == "scatter": diff --git a/python/xy/_validate.py b/python/xy/_validate.py index 45630ca8..d773839d 100644 --- a/python/xy/_validate.py +++ b/python/xy/_validate.py @@ -275,7 +275,20 @@ def curve(value: Any, label: str) -> str: _POINT_SYMBOLS = frozenset( - {"circle", "square", "diamond", "triangle", "cross", "hexagon", "pentagon", "star"} + { + "circle", + "square", + "diamond", + "triangle", + "triangle_down", + "triangle_left", + "triangle_right", + "cross", + "x", + "hexagon", + "pentagon", + "star", + } ) diff --git a/python/xy/columns.py b/python/xy/columns.py index ceb03a9e..c710a4d4 100644 --- a/python/xy/columns.py +++ b/python/xy/columns.py @@ -56,22 +56,22 @@ class ZoneMaps: @cached_property def min(self) -> float: valid = self.mins[self.counts > 0] - return float(valid.min()) if len(valid) else float("nan") + return min(valid.tolist()) if len(valid) else float("nan") @cached_property def max(self) -> float: valid = self.maxs[self.counts > 0] - return float(valid.max()) if len(valid) else float("nan") + return max(valid.tolist()) if len(valid) else float("nan") @cached_property def positive_min(self) -> float: valid = self.positive_mins[np.isfinite(self.positive_mins)] - return float(valid.min()) if len(valid) else float("nan") + return min(valid.tolist()) if len(valid) else float("nan") @cached_property def positive_max(self) -> float: valid = self.positive_maxs[np.isfinite(self.positive_maxs)] - return float(valid.max()) if len(valid) else float("nan") + return max(valid.tolist()) if len(valid) else float("nan") @cached_property def count(self) -> int: diff --git a/python/xy/components.py b/python/xy/components.py index 507d2551..b1eaebf3 100644 --- a/python/xy/components.py +++ b/python/xy/components.py @@ -194,6 +194,7 @@ class Legend(Component): show: bool = True loc: Optional[str] = None ncols: int = 1 + title: Optional[str] = None class_name: Optional[str] = None style: dict[str, StyleValue] = field(default_factory=dict) render: Any = None @@ -1468,6 +1469,7 @@ def legend( show: bool = True, loc: Optional[str] = None, ncols: int = 1, + title: Optional[str] = None, render: Any = None, class_name: Optional[str] = None, style: Optional[dict[str, StyleValue]] = None, @@ -1477,6 +1479,7 @@ def legend( show=_strict_bool(show, "legend show"), loc=_optional_string(loc, "legend loc"), ncols=_optional_positive_int(ncols, "legend ncols") or 1, + title=_optional_string(title, "legend title"), class_name=_optional_string(class_name, "legend class_name"), style=_style_dict(style, "legend style"), render=render, @@ -1893,6 +1896,8 @@ def figure(self) -> Figure: node = legends[-1] _apply_chrome_node(fig, "legend", node.class_name, node.style) fig.legend_options = {"loc": node.loc, "ncols": node.ncols} + if node.title is not None: + fig.legend_options["title"] = node.title if node.style: # Carry the frame/frameon styling into the static-export spec so # the raster/SVG legend can honor frameon=False (transparent bg). diff --git a/python/xy/marks.py b/python/xy/marks.py index a0fd1b17..af168131 100644 --- a/python/xy/marks.py +++ b/python/xy/marks.py @@ -1368,7 +1368,8 @@ def hexbin( shipped as centers plus one scalar count/color channel. """ if isinstance(gridsize, (int, np.integer)) and not isinstance(gridsize, (bool, np.bool_)): - w = h = int(gridsize) + w = int(gridsize) + h = max(2, int(w / np.sqrt(3.0))) elif isinstance(gridsize, (tuple, list)) and len(gridsize) == 2: if any( isinstance(value, (bool, np.bool_)) or not isinstance(value, (int, np.integer)) @@ -1417,33 +1418,49 @@ def hexbin( raise ValueError("hexbin range must be ((x0, x1), (y0, y1))") xr = self._finite_increasing_pair(range[0], "hexbin x range") yr = self._finite_increasing_pair(range[1], "hexbin y range") - threshold = 1 if mincnt is None else int(mincnt) + # Matplotlib displays zero-count cells when C is absent and mincnt is not + # specified, producing the full rectangular honeycomb. Reducer hexbins + # cannot reduce an empty group and therefore default to one observation. + threshold = (0 if cv is None else 1) if mincnt is None else int(mincnt) if threshold < 0: raise ValueError("hexbin mincnt must be nonnegative") - if cv is None: - grid = kernels.bin_2d(xv, yv, xr[0], xr[1], yr[0], yr[1], w, h) - grid2d = grid.reshape(h, w) - rows, cols = np.nonzero(grid2d >= threshold) - else: - # count with the same clip membership the reducer uses below, so - # mincnt filtering and aggregation always agree on bin contents - x_index = np.clip(((xv - xr[0]) / (xr[1] - xr[0]) * w).astype(int), 0, w - 1) - y_index = np.clip(((yv - yr[0]) / (yr[1] - yr[0]) * h).astype(int), 0, h - 1) - grid2d = np.zeros((h, w), dtype=np.float64) - np.add.at(grid2d, (y_index, x_index), 1.0) - rows, cols = np.nonzero(grid2d >= max(1, threshold)) - counts = grid2d[rows, cols] + # Matplotlib's hex lattice is the union of an integer grid and a half-cell + # offset grid. Assign each point to the nearer center in the hex metric; + # rectangular binning plus staggered display centers leaves overlaps and + # gaps and, more importantly, puts values in the wrong cells. + fx = (xv - xr[0]) * w / (xr[1] - xr[0]) + fy = (yv - yr[0]) * h / (yr[1] - yr[0]) + ix1 = np.rint(fx).astype(np.int64) + iy1 = np.rint(fy).astype(np.int64) + ix2 = np.floor(fx).astype(np.int64) + iy2 = np.floor(fy).astype(np.int64) + use_first = (fx - ix1) ** 2 + 3.0 * (fy - iy1) ** 2 < ( + (fx - ix2 - 0.5) ** 2 + 3.0 * (fy - iy2 - 0.5) ** 2 + ) + valid_first = use_first & (ix1 >= 0) & (ix1 <= w) & (iy1 >= 0) & (iy1 <= h) + valid_second = ~use_first & (ix2 >= 0) & (ix2 < w) & (iy2 >= 0) & (iy2 < h) + if not np.any(valid_first | valid_second): + raise ValueError("hexbin range contains no finite points") + flat1 = iy1 * (w + 1) + ix1 + flat2 = iy2 * w + ix2 + count1 = np.bincount(flat1[valid_first], minlength=(w + 1) * (h + 1)).astype(float) + count2 = np.bincount(flat2[valid_second], minlength=w * h).astype(float) + keep1 = np.flatnonzero(count1 >= threshold) + keep2 = np.flatnonzero(count2 >= threshold) + counts = np.concatenate((count1[keep1], count2[keep2])) if len(counts) == 0: raise ValueError("hexbin range contains no finite points") dx, dy = (xr[1] - xr[0]) / w, (yr[1] - yr[0]) / h - centers_x = xr[0] + (cols + 0.5 + 0.5 * (rows & 1)) * dx - centers_y = yr[0] + (rows + 0.5) * dy + centers_x = np.concatenate((xr[0] + (keep1 % (w + 1)) * dx, xr[0] + (keep2 % w + 0.5) * dx)) + centers_y = np.concatenate((yr[0] + (keep1 // (w + 1)) * dy, yr[0] + (keep2 // w + 0.5) * dy)) if cv is None: metric = np.log1p(counts) if bins == "log" else counts else: reduced: list[float] = [] - for row, col in zip(rows, cols, strict=True): - values = cv[(x_index == col) & (y_index == row)] + memberships = [cv[valid_first & (flat1 == flat)] for flat in keep1] + [ + cv[valid_second & (flat2 == flat)] for flat in keep2 + ] + for values in memberships: made = np.asarray(reduce_C_function(values)) if made.ndim != 0 or not np.isfinite(made): raise ValueError("hexbin reduce_C_function must return one finite scalar per bin") @@ -1464,7 +1481,8 @@ def hexbin( style={ "color": DEFAULT_PALETTE[len(self.traces) % len(DEFAULT_PALETTE)], "opacity": opacity, - "symbol": "hexagon", + "hex_dx": dx, + "hex_dy": dy, }, color_ch=color_ch, size_ch=channels.SizeChannel(mode="constant", constant=8.0), diff --git a/python/xy/pyplot/__init__.py b/python/xy/pyplot/__init__.py index 2dd6dd88..80892d08 100644 --- a/python/xy/pyplot/__init__.py +++ b/python/xy/pyplot/__init__.py @@ -731,6 +731,27 @@ def show(*args: Any, **kwargs: Any) -> None: fig.show() +def _flush_inline_figures() -> None: + """Display pyplot figures at the end of an IPython cell, like `%matplotlib inline`.""" + if fignums(): + show() + + +def _install_ipython_display_hook() -> None: + """Install one optional end-of-cell flush hook on the active IPython shell.""" + import sys # noqa: PLC0415 + + ipython = sys.modules.get("IPython") + if ipython is None: + return + shell = ipython.get_ipython() + events = getattr(shell, "events", None) + if events is None or getattr(shell, "_xy_pyplot_inline_hook", None) is not None: + return + events.register("post_execute", _flush_inline_figures) + shell._xy_pyplot_inline_hook = _flush_inline_figures + + # -- namespaces scripts poke at --------------------------------------------------- @@ -975,3 +996,6 @@ def cycler(*args: Any, **kwargs: Any) -> Any: def np_asarray_passthrough(x: Any) -> Any: # pragma: no cover - numpy re-export shim return np.asarray(x) + + +_install_ipython_display_hook() diff --git a/python/xy/pyplot/_axes.py b/python/xy/pyplot/_axes.py index 6e92a751..a3eb7eb5 100644 --- a/python/xy/pyplot/_axes.py +++ b/python/xy/pyplot/_axes.py @@ -387,14 +387,8 @@ def __iter__(self): def set_visible(self, visible: bool) -> None: for name in self.names: if bool(visible): - if name in {"top", "right"}: - raise NotImplementedError( - f"xy.pyplot cannot show the {name} spine independently" - ) self.axes._hidden_spines.discard(name) - elif name in {"left", "bottom"}: - # Hiding is deferred: both gone → transparent axis lines; one - # gone is inexpressible and fails loudly at build time. + else: self.axes._hidden_spines.add(name) self.axes._invalidate() @@ -515,6 +509,11 @@ def _load_rc_chrome(self) -> None: ), }, } + self._hidden_spines = { + side + for side in ("left", "bottom", "top", "right") + if not bool(rcParams[f"axes.spines.{side}"]) + } def _invalidate(self) -> None: host = self._y2_of or self @@ -1285,7 +1284,48 @@ def hist( for index, values in enumerate(counts): positions = centers if stacked else centers + (index - (len(datasets) - 1) / 2) * width current_base = base.copy() if stacked else np.zeros_like(values) - if histtype == "stepfilled": + resolved_color = ( + resolve_color(colors[index]) if colors[index] is not None else self._next_color() + ) + if orientation == "horizontal" and histtype == "stepfilled": + # The core area primitive fills along y. Horizontal filled + # steps are equivalently represented by touching horizontal + # bars, preserving the exact bins/counts without rotating a + # rasterized approximation. + entry = self._add( + "bar", + { + "x": positions, + "y": values, + "kwargs": { + "base": current_base, + "width": width, + "orientation": "horizontal", + "color": resolved_color, + "opacity": 1.0 if alpha is None else float(alpha), + "name": None if labels[index] is None else str(labels[index]), + "stroke": resolve_color(edgecolor) if edgecolor is not None else None, + }, + }, + ) + elif orientation == "horizontal" and histtype.startswith("step"): + step_values = values + current_base + path_x = np.repeat(step_values, 2) + path_y = np.repeat(edges, 2)[1:-1] + entry = self._add( + "@mark", + { + "factory": "segments", + "args": (path_x[:-1], path_y[:-1], path_x[1:], path_y[1:]), + "kwargs": { + "color": resolved_color, + "width": 1.2, + "name": None if labels[index] is None else str(labels[index]), + "opacity": 1.0 if alpha is None else float(alpha), + }, + }, + ) + elif histtype == "stepfilled": # matplotlib fills the step polygon down to the baseline; the # area mark takes the pre-expanded step vertices verbatim. tops = values + current_base @@ -1296,9 +1336,7 @@ def hist( "args": (np.repeat(edges, 2)[1:-1], np.repeat(tops, 2)), "kwargs": { "base": np.repeat(current_base, 2), - "color": resolve_color(colors[index]) - if colors[index] is not None - else self._next_color(), + "color": resolved_color, "name": None if labels[index] is None else str(labels[index]), "opacity": 1.0 if alpha is None else float(alpha), }, @@ -1312,9 +1350,7 @@ def hist( "factory": "stairs", "args": (step_values, edges), "kwargs": { - "color": resolve_color(colors[index]) - if colors[index] is not None - else self._next_color(), + "color": resolved_color, "name": None if labels[index] is None else str(labels[index]), "opacity": 1.0 if alpha is None else float(alpha), }, @@ -1330,9 +1366,7 @@ def hist( "base": current_base, "width": width, "orientation": orientation, - "color": resolve_color(colors[index]) - if colors[index] is not None - else self._next_color(), + "color": resolved_color, "opacity": 1.0 if alpha is None else float(alpha), "name": None if labels[index] is None else str(labels[index]), "stroke": resolve_color(edgecolor) if edgecolor is not None else None, @@ -3109,14 +3143,18 @@ def legend(self, *args: Any, **kwargs: Any) -> None: frameon = kwargs.pop("frameon", rcParams["legend.frameon"]) facecolor = kwargs.pop("facecolor", rcParams["legend.facecolor"]) edgecolor = kwargs.pop("edgecolor", rcParams["legend.edgecolor"]) - # These affect legend geometry which the current component does not - # expose. Reject them instead of accepting and silently discarding. + framealpha = kwargs.pop("framealpha", None) + fancybox = kwargs.pop("fancybox", False) + shadow = kwargs.pop("shadow", False) + borderpad = kwargs.pop("borderpad", None) + labelspacing = kwargs.pop("labelspacing", None) + # Remaining handle/title geometry is not expressible yet and stays + # loud; the frame and row-layout options above map directly to CSS and + # the static exporters. layout_options = { key: kwargs.pop(key) for key in ( "title_fontsize", - "borderpad", - "labelspacing", "handlelength", "handletextpad", ) @@ -3151,9 +3189,28 @@ def legend(self, *args: Any, **kwargs: Any) -> None: if edgecolor is not None: style["borderColor"] = resolve_color(edgecolor) style["borderStyle"] = "solid" + if framealpha is not None: + alpha_value = float(framealpha) + if not 0.0 <= alpha_value <= 1.0: + raise ValueError("legend framealpha must be between 0 and 1") + style["--xy-legend-frame-alpha"] = alpha_value + if bool(fancybox): + style["borderRadius"] = "4px" + if bool(shadow): + style["boxShadow"] = "2px 2px 4px rgba(0,0,0,0.3)" + if borderpad is not None: + padding = float(borderpad) + if padding < 0: + raise ValueError("legend borderpad must be non-negative") + style["padding"] = f"{padding:g}em" + if labelspacing is not None: + spacing = float(labelspacing) + if spacing < 0: + raise ValueError("legend labelspacing must be non-negative") + style["rowGap"] = f"{spacing:g}em" options: dict[str, Any] = {"loc": loc, "ncols": max(1, int(ncols))} if title is not None: - options["class_name"] = f"legend-title:{_plain_text(title)}" + options["title"] = _plain_text(title) if style: options["style"] = style host._legend_options = options @@ -3343,6 +3400,62 @@ def _chart_children(self) -> list[Any]: children.append(fc.text(*e["args"], **text_kw)) return children + def _best_legend_loc(self) -> str: + """Choose the least occupied corner using bounded data-space samples. + + Matplotlib tests artist extents against several candidate boxes. The + shim has no Artist layout graph, but its canonical entry arrays are + enough to make the same useful decision without renderer-specific + guesses: count sampled marks in each corner and preserve Matplotlib's + candidate order for ties. + """ + try: + xlo, xhi = sorted(map(float, self._axis["x"].get("domain", self._auto_domain("x")))) + ylo, yhi = sorted(map(float, self._axis["y"].get("domain", self._auto_domain("y")))) + except (TypeError, ValueError): + return "upper right" + if xhi <= xlo or yhi <= ylo: + return "upper right" + scores = {name: 0.0 for name in ("upper right", "upper left", "lower left", "lower right")} + for entry in self._entries: + x_values, y_values = entry.get("x"), entry.get("y") + if x_values is None or y_values is None: + args: Any = entry.get("args") + if args is not None and len(args) >= 2: + x_values, y_values = args[0], args[1] + if x_values is None or y_values is None: + continue + try: + xv, yv = np.broadcast_arrays( + np.asarray(x_values, dtype=np.float64), + np.asarray(y_values, dtype=np.float64), + ) + except (TypeError, ValueError): + continue + xv, yv = xv.reshape(-1), yv.reshape(-1) + finite = np.flatnonzero(np.isfinite(xv) & np.isfinite(yv)) + if len(finite) > 512: + finite = finite[np.linspace(0, len(finite) - 1, 512, dtype=np.intp)] + if not len(finite): + continue + xn = np.clip((xv[finite] - xlo) / (xhi - xlo), 0.0, 1.0) + yn = np.clip((yv[finite] - ylo) / (yhi - ylo), 0.0, 1.0) + if self._axis["x"].get("reverse"): + xn = 1.0 - xn + if self._axis["y"].get("reverse"): + yn = 1.0 - yn + # Smooth corner weights notice line crossings near a candidate box + # without letting a long series dominate solely by row count. + weights = { + "upper right": xn * yn, + "upper left": (1.0 - xn) * yn, + "lower left": (1.0 - xn) * (1.0 - yn), + "lower right": xn * (1.0 - yn), + } + for name, values in weights.items(): + scores[name] += float(np.mean(values**4)) + return min(scores, key=scores.__getitem__) + def _build_chart(self, width: int, height: int) -> Any: if self._y2_of is not None: return self._y2_of._build_chart(width, height) @@ -3391,16 +3504,11 @@ def _build_chart(self, width: int, height: int) -> Any: self._apply_tickers("y2", y2_props) children.append(fc.y_axis(id="y2", side="right", **y2_props)) if self._legend: - children.append(fc.legend(**self._legend_options)) - hidden = self._hidden_spines & {"left", "bottom"} + legend_options = dict(self._legend_options) + if legend_options.get("loc") in (None, "best"): + legend_options["loc"] = self._best_legend_loc() + children.append(fc.legend(**legend_options)) theme_tokens = self._theme_tokens - if hidden == {"left", "bottom"}: - theme_tokens = dict(theme_tokens) - theme_tokens["axis_color"] = "transparent" - elif hidden: - raise not_implemented( - f"hiding only the {next(iter(hidden))} spine", "hiding both left and bottom" - ) if _MPL_THEME_TOKENS: if self._grid_axis != "both": tokens = dict(theme_tokens) @@ -3420,8 +3528,12 @@ def _build_chart(self, width: int, height: int) -> Any: padding=self._padding, styles=self._chrome_styles, ) + core_figure = self._chart.figure() + core_figure.frame_sides = [ + side for side in ("left", "bottom", "top", "right") if side not in self._hidden_spines + ] if self._colorbar is not None: - figure = self._chart.figure() + figure = core_figure options = dict(self._colorbar) if options.pop("_autoscale", False): derived = _colorbar_figure_domain(figure) diff --git a/python/xy/pyplot/_mplfig.py b/python/xy/pyplot/_mplfig.py index 43e83c38..3fac0bf9 100644 --- a/python/xy/pyplot/_mplfig.py +++ b/python/xy/pyplot/_mplfig.py @@ -358,10 +358,12 @@ def subplots_adjust(self, **kwargs: Any) -> None: raise TypeError( f"subplots_adjust() got unsupported keyword argument {sorted(unsupported)[0]!r}" ) - for key, value in kwargs.items(): - if value is not None: - self._subplot_adjust[key] = float(value) - self._invalidate() + material = {key: value for key, value in kwargs.items() if value is not None} + if material: + raise NotImplementedError( + "xy.pyplot does not implement Figure.subplots_adjust(); subplot frame and " + "spacing values cannot be represented by the current multi-panel renderer" + ) def autofmt_xdate(self, **kwargs: Any) -> None: rotation = float(kwargs.pop("rotation", 30)) diff --git a/python/xy/pyplot/_plot_types.py b/python/xy/pyplot/_plot_types.py index b4a9e627..e699668b 100644 --- a/python/xy/pyplot/_plot_types.py +++ b/python/xy/pyplot/_plot_types.py @@ -2357,7 +2357,10 @@ def clabel( ) # compat-noop: deterministic shim contour-label placement and styling chosen = np.asarray(CS.levels if levels is None else levels, dtype=np.float64).reshape(-1) if isinstance(manual, (list, tuple, np.ndarray)) and len(manual): - locations = list(manual) + label_specs = [ + (index, level, tuple(manual[index % len(manual)])) + for index, level in enumerate(chosen) + ] else: source = CS._entry grid = np.asarray(source["args"][0], dtype=np.float64) @@ -2379,31 +2382,40 @@ def clabel( x0, x1, y0, y1, segment_levels = kernels.marching_squares( grid, x_values, y_values, chosen ) - locations = [] + label_specs = [] + x_span = max(float(np.ptp(x_values)), np.finfo(float).eps) + y_span = max(float(np.ptp(y_values)), np.finfo(float).eps) for index, level in enumerate(chosen): candidates = np.flatnonzero(np.isclose(segment_levels, level)) if len(candidates): - selected = candidates[(index * 37 + len(candidates) // 3) % len(candidates)] - locations.append( - ( + target = min(6, max(3, int(np.ceil(len(candidates) / 18)))) + probes = np.linspace(0, len(candidates) - 1, target, dtype=int) + accepted: list[tuple[float, float]] = [] + for probe in probes: + selected = candidates[(probe + index * 7) % len(candidates)] + location = ( float((x0[selected] + x1[selected]) * 0.5), float((y0[selected] + y1[selected]) * 0.5), ) - ) - else: - locations.append(None) + if all( + np.hypot( + (location[0] - prior[0]) / x_span, + (location[1] - prior[1]) / y_span, + ) + >= 0.12 + for prior in accepted + ): + accepted.append(location) + label_specs.extend((index, level, location) for location in accepted) except (ValueError, RuntimeError): - locations = [(0.5, 0.5)] * len(chosen) + label_specs = [(index, level, (0.5, 0.5)) for index, level in enumerate(chosen)] color_values = [colors] * len(chosen) if isinstance(colors, str) else colors if color_values is None: color_values = [None] * len(chosen) elif not isinstance(color_values, list): color_values = list(color_values) result: list[Text] = [] - for index, level in enumerate(chosen): - location = locations[index % len(locations)] - if location is None: - continue + for index, level, location in label_specs: if callable(fmt): label = str(fmt(level)) elif isinstance(fmt, dict): diff --git a/python/xy/pyplot/_rc.py b/python/xy/pyplot/_rc.py index c0d17d9a..3585427f 100644 --- a/python/xy/pyplot/_rc.py +++ b/python/xy/pyplot/_rc.py @@ -38,8 +38,8 @@ def by_key(self) -> dict[str, list[str]]: "axes.titlecolor": "auto", "axes.spines.left": True, "axes.spines.bottom": True, - "axes.spines.top": False, - "axes.spines.right": False, + "axes.spines.top": True, + "axes.spines.right": True, "xtick.color": "black", "ytick.color": "black", "xtick.labelcolor": "inherit", @@ -70,14 +70,8 @@ def __setitem__(self, key: str, value: Any) -> None: f"xy.pyplot ignores rcParams[{key!r}] — see {COMPAT_URL}", stacklevel=2, ) - if key in {"axes.spines.left", "axes.spines.bottom"} and value is not True: - raise NotImplementedError( - f"xy.pyplot cannot hide {key.removeprefix('axes.spines.')} spines independently" - ) - if key in {"axes.spines.top", "axes.spines.right"} and value is not False: - raise NotImplementedError( - f"xy.pyplot does not render {key.removeprefix('axes.spines.')} spines" - ) + if key.startswith("axes.spines.") and not isinstance(value, bool): + raise ValueError(f"{key} must be boolean") if key == "axes.prop_cycle": by_key = getattr(value, "by_key", None) colors = by_key().get("color") if by_key is not None else None diff --git a/python/xy/pyplot/_translate.py b/python/xy/pyplot/_translate.py index adbaaaf1..10eb34a7 100644 --- a/python/xy/pyplot/_translate.py +++ b/python/xy/pyplot/_translate.py @@ -33,14 +33,14 @@ ".": "circle", ",": "circle", "o": "circle", - "v": "triangle", + "v": "triangle_down", "^": "triangle", - "<": "triangle", - ">": "triangle", - "1": "triangle", + "<": "triangle_left", + ">": "triangle_right", + "1": "triangle_down", "2": "triangle", - "3": "triangle", - "4": "triangle", + "3": "triangle_left", + "4": "triangle_right", "8": "circle", "s": "square", "p": "pentagon", @@ -49,8 +49,8 @@ "h": "hexagon", "H": "hexagon", "+": "cross", - "x": "cross", - "X": "cross", + "x": "x", + "X": "x", "D": "diamond", "d": "diamond", "|": "cross", diff --git a/python/xy/static/index.js b/python/xy/static/index.js index b030fb04..26551236 100644 --- a/python/xy/static/index.js +++ b/python/xy/static/index.js @@ -142,7 +142,7 @@ function niceStep(rough) { rough = Math.abs(rough); if (!Number.isFinite(rough) || rough <= 0) return 1; const mag = Math.pow(10, Math.floor(Math.log10(rough))); -for (const m of [1, 2, 5, 10]) { +for (const m of [1, 2, 2.5, 5, 10]) { if (rough <= m * mag * (1 + 1e-12)) return m * mag; } return 10 * mag; @@ -255,13 +255,11 @@ if (step >= MS.s) return `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pa return `${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}.${pad(d.getUTCMilliseconds(), 3)}`; } function fmtLinear(v, step) { -if (v === 0) return "0"; const av = Math.abs(v); -if (av >= 1e6 || av < 1e-4) return v.toExponential(1).replace("e+", "e"); -const dec = Math.max(0, -Math.floor(Math.log10(step)) + (step < 1 ? 1 : 0)); -let s = v.toFixed(Math.min(dec, 8)); -if (s.includes(".")) s = s.replace(/0+$/, "").replace(/\.$/, ""); -return s; +if (av >= 1e6 || (av !== 0 && av < 1e-4)) return v.toExponential(1).replace("e+", "e"); +let dec = step ? Math.max(0, Math.ceil(-Math.log10(Math.abs(step)))) : 0; +while (dec < 8 && Math.abs(Number(step.toFixed(dec)) - step) > Math.abs(step) / 1000) dec++; +return v.toFixed(Math.min(dec, 8)); } function fmtCategory(v, categories) { const i = Math.round(v); @@ -453,16 +451,25 @@ float fcMarkerSdf(vec2 d, int shape) { float h = clamp(dot(p, ba) / dot(ba, ba), 0.0, 0.5); return length(p - ba * h) * sign(p.y * ba.x - p.x * ba.y); } - if (shape == 3) { // triangle (apex up) + if (shape == 3 || shape == 8 || shape == 9 || shape == 10) { // directional triangle const float k = 1.7320508; float r = 0.62; - vec2 p = vec2(d.x, -d.y); // flip so the apex points up + vec2 q = d; + if (shape == 8) q = -d; + if (shape == 9) q = vec2(d.y, -d.x); + if (shape == 10) q = vec2(-d.y, d.x); + vec2 p = vec2(q.x, -q.y); // flip so the canonical apex points up p.x = abs(p.x) - r; p.y = p.y + r / k; if (p.x + k * p.y > 0.0) p = vec2(p.x - k * p.y, -k * p.x - p.y) / 2.0; p.x -= clamp(p.x, -2.0 * r, 0.0); return -length(p) * sign(p.y); } + if (shape == 11) { // diagonal x + vec2 q = vec2(d.x + d.y, d.y - d.x) * 0.707106781; + vec2 a = abs(q); + return min(max(a.x - 0.17, a.y - 0.5), max(a.x - 0.5, a.y - 0.17)); + } return length(d) - 0.5; // circle }`; const POINT_FS = `#version 300 es @@ -2177,6 +2184,13 @@ lg.style.cssText = `position:absolute;${xPos}${yPos}` + `display:grid;grid-template-columns:repeat(${horizontal ? ncols : 1},max-content);` + "overflow:auto;" + `max-height:${this.plot.h - 12}px;`; this._applySlot(lg, "legend"); +if (options.title) { +const title = document.createElement("div"); +title.textContent = String(options.title); +title.style.fontWeight = "600"; +title.style.gridColumn = `1 / span ${horizontal ? ncols : 1}`; +lg.appendChild(title); +} for (const it of items) { const row = document.createElement("div"); this._applySlot(row, "legend_item"); @@ -2375,7 +2389,7 @@ this._pointMarkStyle(g, t); } _pointMarkStyle(g, t) { const s = t.style || {}; -g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5, pentagon: 6, star: 7 }[s.symbol] || 0; +g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5, pentagon: 6, star: 7, triangle_down: 8, triangle_left: 9, triangle_right: 10, x: 11 }[s.symbol] || 0; g.pointStrokeWidth = Number(s.stroke_width) || 0; const markOpaque = [g.color[0], g.color[1], g.color[2], 1]; g.pointStroke = s.stroke @@ -3638,15 +3652,18 @@ d.style.cssText = "pointer-events:none;"; this.labels.appendChild(d); }; +const frameSides = Array.isArray(s.frame_sides) +? s.frame_sides +: [xAxis.side || "bottom", yAxis.side || "left"]; if (!hideY) { const yWidth = Math.max(1, Math.round(this._axisStyleNumber(yAxis, "axis_width", 1))); -const yAxisX = yAxis.side === "right" ? p.x + p.w - yWidth : p.x; -rule(yAxis, yAxisX, p.y, yWidth, p.h); +if (frameSides.includes("left")) rule(yAxis, p.x, p.y, yWidth, p.h); +if (frameSides.includes("right")) rule(yAxis, p.x + p.w - yWidth, p.y, yWidth, p.h); } if (!hideX) { const xHeight = Math.max(1, Math.round(this._axisStyleNumber(xAxis, "axis_width", 1))); -const xTop = xAxis.side === "top" ? p.y : p.y + p.h - xHeight; -rule(xAxis, p.x, xTop, p.w, xHeight); +if (frameSides.includes("top")) rule(xAxis, p.x, p.y, p.w, xHeight); +if (frameSides.includes("bottom")) rule(xAxis, p.x, p.y + p.h - xHeight, p.w, xHeight); } for (const axis of Object.values(this.axes)) { if (!axis || axis.id === "y" || !String(axis.id || "").startsWith("y")) continue; @@ -5458,13 +5475,17 @@ segments: SEGMENT_MARK, triangle_mesh: MESH_MARK, error_band: AREA_MARK, hexbin: { -build: (view, g, t, buffer) => view._buildScatterMark(g, t, buffer), +build: (view, g, t, buffer) => view._buildMeshMark(g, t, buffer), draw: (view, g) => { const [x0, x1] = view._axisRange(g.xAxis); const [y0, y1] = view._axisRange(g.yAxis); -view._drawPoints(g, view._map(g.xMeta, x0, x1, g.xAxis), view._map(g.yMeta, y0, y1, g.yAxis)); +view._drawMesh(g, view._map(g.x0Meta, x0, x1, g.xAxis), view._map(g.y0Meta, y0, y1, g.yAxis)); +}, +refreshColor: (view, g) => { +if (g.colorMode === 0 && g.trace.color) g.color = parseColor(view.root, g.trace.color.color, g.color); +const style = g.trace.style || {}; +g.meshStroke = parseColor(view.root, style.stroke || "transparent", [0, 0, 0, 0]); }, -refreshColor: (view, g) => view._pointMarkStyle(g, g.trace), }, bar: BAR_MARK, column: BAR_MARK, diff --git a/python/xy/static/standalone.js b/python/xy/static/standalone.js index a7bf9008..c79488a1 100644 --- a/python/xy/static/standalone.js +++ b/python/xy/static/standalone.js @@ -143,7 +143,7 @@ function niceStep(rough) { rough = Math.abs(rough); if (!Number.isFinite(rough) || rough <= 0) return 1; const mag = Math.pow(10, Math.floor(Math.log10(rough))); -for (const m of [1, 2, 5, 10]) { +for (const m of [1, 2, 2.5, 5, 10]) { if (rough <= m * mag * (1 + 1e-12)) return m * mag; } return 10 * mag; @@ -256,13 +256,11 @@ if (step >= MS.s) return `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pa return `${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}.${pad(d.getUTCMilliseconds(), 3)}`; } function fmtLinear(v, step) { -if (v === 0) return "0"; const av = Math.abs(v); -if (av >= 1e6 || av < 1e-4) return v.toExponential(1).replace("e+", "e"); -const dec = Math.max(0, -Math.floor(Math.log10(step)) + (step < 1 ? 1 : 0)); -let s = v.toFixed(Math.min(dec, 8)); -if (s.includes(".")) s = s.replace(/0+$/, "").replace(/\.$/, ""); -return s; +if (av >= 1e6 || (av !== 0 && av < 1e-4)) return v.toExponential(1).replace("e+", "e"); +let dec = step ? Math.max(0, Math.ceil(-Math.log10(Math.abs(step)))) : 0; +while (dec < 8 && Math.abs(Number(step.toFixed(dec)) - step) > Math.abs(step) / 1000) dec++; +return v.toFixed(Math.min(dec, 8)); } function fmtCategory(v, categories) { const i = Math.round(v); @@ -454,16 +452,25 @@ float fcMarkerSdf(vec2 d, int shape) { float h = clamp(dot(p, ba) / dot(ba, ba), 0.0, 0.5); return length(p - ba * h) * sign(p.y * ba.x - p.x * ba.y); } - if (shape == 3) { // triangle (apex up) + if (shape == 3 || shape == 8 || shape == 9 || shape == 10) { // directional triangle const float k = 1.7320508; float r = 0.62; - vec2 p = vec2(d.x, -d.y); // flip so the apex points up + vec2 q = d; + if (shape == 8) q = -d; + if (shape == 9) q = vec2(d.y, -d.x); + if (shape == 10) q = vec2(-d.y, d.x); + vec2 p = vec2(q.x, -q.y); // flip so the canonical apex points up p.x = abs(p.x) - r; p.y = p.y + r / k; if (p.x + k * p.y > 0.0) p = vec2(p.x - k * p.y, -k * p.x - p.y) / 2.0; p.x -= clamp(p.x, -2.0 * r, 0.0); return -length(p) * sign(p.y); } + if (shape == 11) { // diagonal x + vec2 q = vec2(d.x + d.y, d.y - d.x) * 0.707106781; + vec2 a = abs(q); + return min(max(a.x - 0.17, a.y - 0.5), max(a.x - 0.5, a.y - 0.17)); + } return length(d) - 0.5; // circle }`; const POINT_FS = `#version 300 es @@ -2178,6 +2185,13 @@ lg.style.cssText = `position:absolute;${xPos}${yPos}` + `display:grid;grid-template-columns:repeat(${horizontal ? ncols : 1},max-content);` + "overflow:auto;" + `max-height:${this.plot.h - 12}px;`; this._applySlot(lg, "legend"); +if (options.title) { +const title = document.createElement("div"); +title.textContent = String(options.title); +title.style.fontWeight = "600"; +title.style.gridColumn = `1 / span ${horizontal ? ncols : 1}`; +lg.appendChild(title); +} for (const it of items) { const row = document.createElement("div"); this._applySlot(row, "legend_item"); @@ -2376,7 +2390,7 @@ this._pointMarkStyle(g, t); } _pointMarkStyle(g, t) { const s = t.style || {}; -g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5, pentagon: 6, star: 7 }[s.symbol] || 0; +g.symbol = { circle: 0, square: 1, diamond: 2, triangle: 3, cross: 4, hexagon: 5, pentagon: 6, star: 7, triangle_down: 8, triangle_left: 9, triangle_right: 10, x: 11 }[s.symbol] || 0; g.pointStrokeWidth = Number(s.stroke_width) || 0; const markOpaque = [g.color[0], g.color[1], g.color[2], 1]; g.pointStroke = s.stroke @@ -3639,15 +3653,18 @@ d.style.cssText = "pointer-events:none;"; this.labels.appendChild(d); }; +const frameSides = Array.isArray(s.frame_sides) +? s.frame_sides +: [xAxis.side || "bottom", yAxis.side || "left"]; if (!hideY) { const yWidth = Math.max(1, Math.round(this._axisStyleNumber(yAxis, "axis_width", 1))); -const yAxisX = yAxis.side === "right" ? p.x + p.w - yWidth : p.x; -rule(yAxis, yAxisX, p.y, yWidth, p.h); +if (frameSides.includes("left")) rule(yAxis, p.x, p.y, yWidth, p.h); +if (frameSides.includes("right")) rule(yAxis, p.x + p.w - yWidth, p.y, yWidth, p.h); } if (!hideX) { const xHeight = Math.max(1, Math.round(this._axisStyleNumber(xAxis, "axis_width", 1))); -const xTop = xAxis.side === "top" ? p.y : p.y + p.h - xHeight; -rule(xAxis, p.x, xTop, p.w, xHeight); +if (frameSides.includes("top")) rule(xAxis, p.x, p.y, p.w, xHeight); +if (frameSides.includes("bottom")) rule(xAxis, p.x, p.y + p.h - xHeight, p.w, xHeight); } for (const axis of Object.values(this.axes)) { if (!axis || axis.id === "y" || !String(axis.id || "").startsWith("y")) continue; @@ -5459,13 +5476,17 @@ segments: SEGMENT_MARK, triangle_mesh: MESH_MARK, error_band: AREA_MARK, hexbin: { -build: (view, g, t, buffer) => view._buildScatterMark(g, t, buffer), +build: (view, g, t, buffer) => view._buildMeshMark(g, t, buffer), draw: (view, g) => { const [x0, x1] = view._axisRange(g.xAxis); const [y0, y1] = view._axisRange(g.yAxis); -view._drawPoints(g, view._map(g.xMeta, x0, x1, g.xAxis), view._map(g.yMeta, y0, y1, g.yAxis)); +view._drawMesh(g, view._map(g.x0Meta, x0, x1, g.xAxis), view._map(g.y0Meta, y0, y1, g.yAxis)); +}, +refreshColor: (view, g) => { +if (g.colorMode === 0 && g.trace.color) g.color = parseColor(view.root, g.trace.color.color, g.color); +const style = g.trace.style || {}; +g.meshStroke = parseColor(view.root, style.stroke || "transparent", [0, 0, 0, 0]); }, -refreshColor: (view, g) => view._pointMarkStyle(g, g.trace), }, bar: BAR_MARK, column: BAR_MARK, diff --git a/src/raster.rs b/src/raster.rs index dc60a402..ad6350fd 100644 --- a/src/raster.rs +++ b/src/raster.rs @@ -697,11 +697,17 @@ fn symbol_sdf(px: f32, py: f32, r: f32, sym: u8) -> f32 { match sym { 1 => px.abs().max(py.abs()) - r, // square 2 => (px.abs() + py.abs()) - r, // diamond - 3 => { + 3 | 8 | 9 | 10 => { // equilateral triangle, apex up (IQ SDF), matching the GL shader let k = 1.732_050_8_f32; let rr = r * 1.24; - let mut p = (px, -py); + let d = match sym { + 8 => (-px, -py), // down + 9 => (py, -px), // left + 10 => (-py, px), // right + _ => (px, py), + }; + let mut p = (d.0, -d.1); p.0 = p.0.abs() - rr; p.1 += rr / k; if p.0 + k * p.1 > 0.0 { @@ -715,6 +721,13 @@ fn symbol_sdf(px: f32, py: f32, r: f32, sym: u8) -> f32 { let (ax, ay) = (px.abs(), py.abs()); (ax - 0.34 * r).max(ay - r).min((ax - r).max(ay - 0.34 * r)) } + 11 => { + // diagonal cross (matplotlib's x/X), distinct from the plus glyph + let qx = (px + py) * std::f32::consts::FRAC_1_SQRT_2; + let qy = (py - px) * std::f32::consts::FRAC_1_SQRT_2; + let (ax, ay) = (qx.abs(), qy.abs()); + (ax - 0.34 * r).max(ay - r).min((ax - r).max(ay - 0.34 * r)) + } 5 => { // regular hexagon, pointy top (IQ SDF, x/y swapped for a top vertex) let (k0, k1, k2) = (-0.866_025_4_f32, 0.5_f32, 0.577_350_3_f32); diff --git a/tests/pyplot/test_axes_charts.py b/tests/pyplot/test_axes_charts.py index 0c4e2a56..059e7afd 100644 --- a/tests/pyplot/test_axes_charts.py +++ b/tests/pyplot/test_axes_charts.py @@ -502,7 +502,8 @@ def test_clabel_table_and_quiverkey_complete_annotation_families() -> None: ) quiver = ax.quiver([0, 1], [0, 1], [1, 1], [1, 0], [0.2, 0.8], cmap="plasma") key = ax.quiverkey(quiver, 0.5, 0.5, 1.0, "1 m/s") - assert [label.get_text() for label in contour_labels] == ["L=4", "L=8"] + assert {label.get_text() for label in contour_labels} == {"L=4", "L=8"} + assert len(contour_labels) > 2 assert len(table.get_celld()) == 9 assert key is not None assert {trace.kind for trace in _traces(ax)} >= {"contour", "triangle_mesh", "segments"} diff --git a/tests/pyplot/test_grid_legend_contracts.py b/tests/pyplot/test_grid_legend_contracts.py index 4d3b7d24..e5b8dd9e 100644 --- a/tests/pyplot/test_grid_legend_contracts.py +++ b/tests/pyplot/test_grid_legend_contracts.py @@ -48,7 +48,7 @@ def test_legend_maps_supported_style_and_rejects_unknown_options(): assert ax._legend is True assert ax._legend_options["loc"] == "upper right" assert ax._legend_options["ncols"] == 2 - assert ax._legend_options["class_name"] == "legend-title:Legend" + assert ax._legend_options["title"] == "Legend" assert ax._legend_options["style"] == { "fontSize": "13px", "color": "green", @@ -57,8 +57,12 @@ def test_legend_maps_supported_style_and_rejects_unknown_options(): "borderStyle": "solid", } - with pytest.raises(TypeError): - ax.legend(shadow=True) + ax.legend(shadow=True, fancybox=True, framealpha=0.8, borderpad=1, labelspacing=0.7) + style = ax._legend_options["style"] + assert style["boxShadow"] + assert style["borderRadius"] == "4px" + assert style["padding"] == "1em" + assert style["rowGap"] == "0.7em" def test_legend_frameoff_maps_to_transparent_style(): diff --git a/tests/pyplot/test_launch_compat.py b/tests/pyplot/test_launch_compat.py index 5f27147b..858bb984 100644 --- a/tests/pyplot/test_launch_compat.py +++ b/tests/pyplot/test_launch_compat.py @@ -63,6 +63,17 @@ def test_pyplot_legend_location_and_columns_reach_render_spec() -> None: assert "style" in spec["legend"] +def test_legend_best_avoids_the_busy_corner_and_default_axes_are_boxed() -> None: + _fig, ax = plt.subplots() + ax.scatter(np.linspace(0.72, 0.98, 100), np.linspace(0.7, 0.98, 100), label="busy") + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.legend() + spec, _ = ax._build_chart(640, 480).figure().build_payload() + assert spec["legend"]["loc"] in {"upper left", "lower left", "lower right"} + assert spec["frame_sides"] == ["left", "bottom", "top", "right"] + + def test_filled_stairs_use_seamless_bins_and_hatches_are_not_dropped() -> None: _fig, ax = plt.subplots() ax.stairs([1, 2], [0, 1, 2], fill=True) diff --git a/tests/pyplot/test_layout_noops.py b/tests/pyplot/test_layout_noops.py index eac8e69e..4d82799f 100644 --- a/tests/pyplot/test_layout_noops.py +++ b/tests/pyplot/test_layout_noops.py @@ -22,18 +22,10 @@ def test_tight_layout_records_validated_noop_contract(): fig.tight_layout(foo=1) -def test_subplots_adjust_records_supported_spacing_values(): +def test_subplots_adjust_rejects_spacing_it_cannot_render(): fig, _ = plt.subplots(2, 2) - fig.subplots_adjust(left=0.1, right=0.9, top=0.8, bottom=0.2, wspace=0.3, hspace=0.4) - - assert fig._subplot_adjust == { - "left": 0.1, - "right": 0.9, - "top": 0.8, - "bottom": 0.2, - "wspace": 0.3, - "hspace": 0.4, - } + with pytest.raises(NotImplementedError, match="subplots_adjust"): + fig.subplots_adjust(left=0.1, wspace=0.3) with pytest.raises(TypeError): fig.subplots_adjust(foo=1) diff --git a/tests/pyplot/test_layout_text_parity_fixes.py b/tests/pyplot/test_layout_text_parity_fixes.py index a2d95b59..757217db 100644 --- a/tests/pyplot/test_layout_text_parity_fixes.py +++ b/tests/pyplot/test_layout_text_parity_fixes.py @@ -126,6 +126,15 @@ def test_multiple_locator_labels_keep_step_precision() -> None: assert ">2" not in svg.split('text-anchor="middle"')[1][:400] +def test_default_ticks_use_matplotlib_density_and_uniform_decimal_padding() -> None: + fig, ax = plt.subplots() + ax.plot([0, 1], [-1, 1]) + ax.set_ylim(-1, 1) + svg = _svg(fig) + for label in ("-1.00", "-0.75", "0.00", "0.75", "1.00"): + assert f">{label}<" in svg + + def test_log_axes_label_decades_and_grid_majors_only() -> None: fig, ax = plt.subplots() x = np.linspace(1, 2000, 100) diff --git a/tests/pyplot/test_marker_fidelity.py b/tests/pyplot/test_marker_fidelity.py new file mode 100644 index 00000000..bd6917a7 --- /dev/null +++ b/tests/pyplot/test_marker_fidelity.py @@ -0,0 +1,31 @@ +import io + +import xy.pyplot as plt + + +def teardown_function(): + plt.close("all") + + +def test_directional_triangles_and_x_keep_distinct_symbols_in_payload(): + fig, ax = plt.subplots() + for index, marker in enumerate(("^", "v", "<", ">", "+", "x")): + ax.plot([index], [index], marker=marker, linestyle="none") + + payload, _blob = ax._build_chart(640, 480).figure().build_payload() + symbols = [ + trace["style"]["symbol"] for trace in payload["traces"] if trace["kind"] == "scatter" + ] + assert symbols == [ + "triangle", + "triangle_down", + "triangle_left", + "triangle_right", + "cross", + "x", + ] + + for format in ("png", "svg"): + output = io.BytesIO() + fig.savefig(output, format=format) + assert output.tell() > 100 diff --git a/tests/pyplot/test_pdsh_gap_features.py b/tests/pyplot/test_pdsh_gap_features.py index 059e9506..7e278160 100644 --- a/tests/pyplot/test_pdsh_gap_features.py +++ b/tests/pyplot/test_pdsh_gap_features.py @@ -298,12 +298,13 @@ def test_spines_values_hide_all_renders_transparent_axis(): _png() -def test_hiding_a_single_left_spine_fails_at_build(): +def test_hiding_a_single_left_spine_is_renderable(): fig, ax = plt.subplots() ax.plot([0, 1], [0, 1]) ax.spines["left"].set_visible(False) - with pytest.raises(NotImplementedError): - _png() + _png() + spec, _ = ax._build_chart(640, 480).figure().build_payload() + assert "left" not in spec["frame_sides"] def test_tick_label_handles_recolor_axis_labels(): diff --git a/tests/pyplot/test_pyplot_state_management.py b/tests/pyplot/test_pyplot_state_management.py index 3d8899b1..adca7f82 100644 --- a/tests/pyplot/test_pyplot_state_management.py +++ b/tests/pyplot/test_pyplot_state_management.py @@ -1,3 +1,7 @@ +import sys +from types import ModuleType, SimpleNamespace +from unittest.mock import Mock + import xy.pyplot as plt @@ -51,3 +55,22 @@ def test_pyplot_twiny_creates_current_axes_on_same_figure(): assert plt.gca() is twin assert twin in fig.axes assert twin is not ax + + +def test_pyplot_installs_one_ipython_end_of_cell_display_hook(monkeypatch): + callbacks = [] + events = SimpleNamespace(register=lambda event, callback: callbacks.append((event, callback))) + shell = SimpleNamespace(events=events) + ipython = ModuleType("IPython") + ipython.get_ipython = lambda: shell + monkeypatch.setitem(sys.modules, "IPython", ipython) + + plt._install_ipython_display_hook() + plt._install_ipython_display_hook() + + assert callbacks == [("post_execute", plt._flush_inline_figures)] + plt.figure() + show = Mock() + monkeypatch.setattr(plt, "show", show) + callbacks[0][1]() + show.assert_called_once_with() diff --git a/tests/pyplot/test_rc_chrome_contracts.py b/tests/pyplot/test_rc_chrome_contracts.py index 7d5292c9..0032eeed 100644 --- a/tests/pyplot/test_rc_chrome_contracts.py +++ b/tests/pyplot/test_rc_chrome_contracts.py @@ -1,7 +1,5 @@ from __future__ import annotations -import io - import pytest import xy.pyplot as plt @@ -76,23 +74,14 @@ def test_legend_rc_defaults_reach_legend_component() -> None: } -def test_spine_and_invalid_cycle_boundaries_fail_loudly() -> None: - with pytest.raises(NotImplementedError, match="cannot hide left"): - plt.rcParams["axes.spines.left"] = False - with pytest.raises(NotImplementedError, match="does not render top"): - plt.rcParams["axes.spines.top"] = True +def test_spine_controls_and_invalid_cycle_boundaries() -> None: + plt.rcParams["axes.spines.left"] = False + plt.rcParams["axes.spines.top"] = True with pytest.raises(ValueError, match="non-empty color cycle"): plt.rcParams["axes.prop_cycle"] = object() _fig, ax = plt.subplots() ax.plot([0, 1], [0, 1]) ax.spines[["top", "right"]].set_visible(False) - # Hiding only one of left/bottom is inexpressible and fails at build time; - # hiding both renders with transparent axis lines. - ax.spines["left"].set_visible(False) - with pytest.raises(NotImplementedError, match="hiding only the left spine"): - _fig.savefig(io.BytesIO(), format="png") - ax.spines["bottom"].set_visible(False) - buffer = io.BytesIO() - _fig.savefig(buffer, format="png") - assert buffer.getvalue()[:4] == b"\x89PNG" + spec, _ = ax._build_chart(640, 480).figure().build_payload() + assert spec["frame_sides"] == ["bottom"] diff --git a/tests/pyplot/test_silent_drop_regressions.py b/tests/pyplot/test_silent_drop_regressions.py index 289de43f..ce97a5d2 100644 --- a/tests/pyplot/test_silent_drop_regressions.py +++ b/tests/pyplot/test_silent_drop_regressions.py @@ -181,10 +181,10 @@ def test_savefig_without_extension_defaults_to_png(tmp_path): def test_rc_update_paths_enforce_the_same_validation_as_setitem(): - with pytest.raises(NotImplementedError), plt.rc_context({"axes.spines.left": False}): - pass - with pytest.raises(NotImplementedError): - plt.style.use({"axes.spines.top": True}) + with plt.rc_context({"axes.spines.left": False, "axes.spines.top": True}): + assert plt.rcParams["axes.spines.left"] is False + with pytest.raises(ValueError, match="boolean"): + plt.style.use({"axes.spines.top": "yes"}) plt.style.use({"font.size": "14"}) assert plt.rcParams["font.size"] == 14.0 diff --git a/tests/pyplot/test_visual_parity_fixes.py b/tests/pyplot/test_visual_parity_fixes.py index e942e880..7ae83dba 100644 --- a/tests/pyplot/test_visual_parity_fixes.py +++ b/tests/pyplot/test_visual_parity_fixes.py @@ -211,6 +211,23 @@ def test_hist_single_series_bars_span_full_bin_width(): _png(fig) +@pytest.mark.parametrize("histtype", ["bar", "step", "stepfilled"]) +def test_horizontal_histogram_geometry_uses_counts_on_x(histtype): + _fig, ax = plt.subplots() + counts, edges, _ = ax.hist( + [0, 0, 1, 2, 2, 2], bins=3, orientation="horizontal", histtype=histtype + ) + entry = ax._entries[-1] + if histtype == "step": + assert entry["factory"] == "segments" + assert max(entry["args"][0]) == pytest.approx(max(counts)) + else: + assert entry["kind"] == "bar" + assert entry["kwargs"]["orientation"] == "horizontal" + np.testing.assert_allclose(entry["x"], (edges[:-1] + edges[1:]) * 0.5) + np.testing.assert_allclose(entry["y"], counts) + + def test_hist_multiple_series_stay_side_by_side(): rng = np.random.default_rng(2) fig, ax = plt.subplots() diff --git a/tests/test_pdsh_dual_engine_notebooks.py b/tests/test_pdsh_dual_engine_notebooks.py new file mode 100644 index 00000000..73436a2d --- /dev/null +++ b/tests/test_pdsh_dual_engine_notebooks.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +PDSH = ROOT / "examples" / "pdsh" + + +def test_pdsh_notebooks_contain_fresh_matplotlib_and_xy_sections() -> None: + subprocess.run( + [sys.executable, str(PDSH / "sync_dual_engine_notebooks.py"), "--check"], + cwd=ROOT, + check=True, + ) + paths = sorted(PDSH.glob("pdsh_*.ipynb")) + assert len(paths) == 14 + for path in paths: + notebook = json.loads(path.read_text(encoding="utf-8")) + code_cells = [cell for cell in notebook["cells"] if cell.get("cell_type") == "code"] + engines = [cell.get("metadata", {}).get("xy_pdsh_engine") for cell in code_cells] + assert "matplotlib" in engines and "xy" in engines + assert len(code_cells) % 2 == 0 + assert all( + engines[index : index + 2] == ["matplotlib", "xy"] + for index in range(0, len(engines), 2) + ) + assert all( + code_cells[index]["metadata"]["xy_pdsh_source_id"] + == code_cells[index + 1]["metadata"]["xy_pdsh_source_id"] + for index in range(0, len(code_cells), 2) + ) + mpl_code = "\n".join( + "".join(cell.get("source", [])) + for cell in notebook["cells"] + if cell.get("metadata", {}).get("xy_pdsh_engine") == "matplotlib" + and cell.get("cell_type") == "code" + ) + xy_code = "\n".join( + "".join(cell.get("source", [])) + for cell in notebook["cells"] + if cell.get("metadata", {}).get("xy_pdsh_engine") == "xy" + and cell.get("cell_type") == "code" + ) + assert "xy.pyplot" not in mpl_code + assert "matplotlib" in mpl_code + assert "xy.pyplot" in xy_code + assert "xy_plt.show()" not in xy_code diff --git a/tests/test_plot_families.py b/tests/test_plot_families.py index 97f79e82..b52c9d1c 100644 --- a/tests/test_plot_families.py +++ b/tests/test_plot_families.py @@ -58,7 +58,10 @@ def test_hexbin_is_screen_bounded_and_contour_emits_isolines() -> None: Figure().contour(np.arange(64, dtype=float).reshape(8, 8), levels=5).build_payload() ) assert hex_spec["traces"][0]["kind"] == "hexbin" - assert hex_spec["traces"][0]["n_marks"] <= 32 * 32 + ny = int(32 / np.sqrt(3.0)) + assert hex_spec["traces"][0]["n_marks"] <= (32 + 1) * (ny + 1) + 32 * ny + assert all(key in hex_spec["traces"][0] for key in ("x0", "y0", "x1", "y1", "x2", "y2")) + assert "x" not in hex_spec["traces"][0] assert contour_spec["traces"][0]["kind"] == "contour" assert contour_spec["traces"][0]["n_marks"] > 0 @@ -266,7 +269,8 @@ def test_hexbin_does_not_retain_raw_points_in_the_store() -> None: fig = Figure().hexbin(rng.normal(size=n), rng.normal(size=n), gridsize=32) total_elements = sum(len(col) for col in fig.store.columns) # Only occupied bin centers may be resident, never the raw points. - assert total_elements <= 2 * 32 * 32 + ny = int(32 / np.sqrt(3.0)) + assert total_elements <= 2 * ((32 + 1) * (ny + 1) + 32 * ny) assert fig.traces[0].count == n failing = Figure() with pytest.raises(ValueError, match="no finite points"): From 34f5b6c9e60a533063295ae60b0c98c2fdf99e13 Mon Sep 17 00:00:00 2001 From: Farhan Date: Tue, 14 Jul 2026 16:38:18 +0500 Subject: [PATCH 06/17] fix(export): isolate notebook HTML reprs in a sandboxed srcdoc iframe Notebook frontends treat rich-HTML payloads as fragments even when they contain a full document, so the standalone export's global CSS (html,body resets, font-family) could restyle the surrounding notebook. notebook_iframe() wraps the document in a sandboxed srcdoc iframe sized from the figure, giving the repr a real document boundary while keeping the self-contained offline display path. Both Figure._repr_html_ surfaces (engine and pyplot shim) and pyplot.show() in IPython now route through it. --- python/xy/_figure.py | 4 +-- python/xy/export.py | 38 ++++++++++++++++++++ python/xy/pyplot/__init__.py | 2 +- python/xy/pyplot/_mplfig.py | 5 ++- tests/pyplot/test_boundaries.py | 2 +- tests/pyplot/test_corpus.py | 2 +- tests/pyplot/test_pyplot_state_management.py | 20 +++++++++++ tests/pyplot/test_state_and_figs.py | 16 ++++++++- tests/test_components.py | 2 +- tests/test_figure.py | 12 +++++-- tests/test_import.py | 2 +- 11 files changed, 93 insertions(+), 12 deletions(-) diff --git a/python/xy/_figure.py b/python/xy/_figure.py index a6f15f02..b2cd3879 100644 --- a/python/xy/_figure.py +++ b/python/xy/_figure.py @@ -1152,8 +1152,8 @@ def html( return self.to_html(path, custom_css=custom_css) def _repr_html_(self) -> str: - """Notebook HTML repr fallback using the standalone export path.""" - return self.to_html() + """Notebook HTML repr isolated from the host document's styles.""" + return export.notebook_iframe(self.to_html(), width=self.width, height=self.height) def to_svg( self, diff --git a/python/xy/export.py b/python/xy/export.py index e61b2eeb..5e54b107 100644 --- a/python/xy/export.py +++ b/python/xy/export.py @@ -218,6 +218,44 @@ def to_html( return doc +_NOTEBOOK_DIMENSION_RE = _re.compile(r"^[0-9]+(?:\.[0-9]+)?(?:px|%|vw|vh|rem|em)?$") + + +def _notebook_dimension(value: object, fallback: int) -> tuple[str, bool]: + if isinstance(value, (int, float)) and not isinstance(value, bool): + return f"{max(1, int(value))}px", True + text = str(value).strip() + if _NOTEBOOK_DIMENSION_RE.fullmatch(text): + if text.replace(".", "", 1).isdigit(): + return f"{max(1, int(float(text)))}px", True + return text, text.endswith("px") + return f"{fallback}px", True + + +def notebook_iframe(doc: str, *, width: object, height: object) -> str: + """Embed a standalone document without leaking its CSS into a notebook. + + Rich HTML display payloads are fragments, even when their value happens to + contain ````/```` tags. Consequently a notebook frontend may + apply the standalone document's global selectors to the notebook itself. + ``srcdoc`` gives the export a real document boundary while preserving the + self-contained, offline display path. + """ + width_css, fixed_width = _notebook_dimension(width, 900) + height_css, _ = _notebook_dimension(height, 420) + width_attr = width_css.removesuffix("px") + height_attr = height_css.removesuffix("px") + source = _html.escape(doc, quote=True) + width_style = f"width:100%;max-width:{width_css}" if fixed_width else f"width:{width_css}" + return ( + '' + ) + + def find_chromium(explicit: Optional[str] = None) -> Optional[str]: """Locate a headless-capable Chromium/Chrome, or None.""" for cand in (explicit, os.environ.get(_CHROMIUM_ENV)): diff --git a/python/xy/pyplot/__init__.py b/python/xy/pyplot/__init__.py index 80892d08..09dcbf8e 100644 --- a/python/xy/pyplot/__init__.py +++ b/python/xy/pyplot/__init__.py @@ -724,7 +724,7 @@ def show(*args: Any, **kwargs: Any) -> None: from IPython.display import HTML, display # noqa: PLC0415 for fig in all_figures(): - display(HTML(fig._to_html())) + display(HTML(fig._repr_html_())) close("all") return for fig in all_figures(): diff --git a/python/xy/pyplot/_mplfig.py b/python/xy/pyplot/_mplfig.py index 3fac0bf9..6d06524a 100644 --- a/python/xy/pyplot/_mplfig.py +++ b/python/xy/pyplot/_mplfig.py @@ -802,7 +802,10 @@ def _to_html(self) -> str: return self._html_cache def _repr_html_(self) -> str: - return self._to_html() + from xy import export + + width, height = rc_figsize_px(self._figsize, self._dpi) + return export.notebook_iframe(self._to_html(), width=width, height=height) def show(self, *args: Any, **kwargs: Any) -> None: import tempfile diff --git a/tests/pyplot/test_boundaries.py b/tests/pyplot/test_boundaries.py index 06890b22..1f7cb43e 100644 --- a/tests/pyplot/test_boundaries.py +++ b/tests/pyplot/test_boundaries.py @@ -104,7 +104,7 @@ def blocked_import(name, *args, **kwargs): for path in sorted(pathlib.Path({str(corpus)!r}).glob("[0-9][0-9]_*.py")): runpy.run_path(path, run_name="__main__") for figure in tuple(__import__("xy.pyplot._state", fromlist=["all_figures"]).all_figures()): - assert figure._repr_html_().startswith("") + assert figure._repr_html_().startswith('' ) diff --git a/python/xy/pyplot/__init__.py b/python/xy/pyplot/__init__.py index 09dcbf8e..28ac66b6 100644 --- a/python/xy/pyplot/__init__.py +++ b/python/xy/pyplot/__init__.py @@ -721,10 +721,10 @@ def show(*args: Any, **kwargs: Any) -> None: ipython = sys.modules.get("IPython") shell = ipython.get_ipython() if ipython is not None else None if shell is not None: - from IPython.display import HTML, display # noqa: PLC0415 + from IPython.display import display # noqa: PLC0415 for fig in all_figures(): - display(HTML(fig._repr_html_())) + display(fig) close("all") return for fig in all_figures(): diff --git a/python/xy/pyplot/_artists.py b/python/xy/pyplot/_artists.py index 8d24ec3d..82f2f76f 100644 --- a/python/xy/pyplot/_artists.py +++ b/python/xy/pyplot/_artists.py @@ -14,6 +14,7 @@ import numpy as np from ._colors import resolve_color +from ._rc import rcParams from ._transforms import Bbox, IdentityTransform @@ -236,11 +237,18 @@ def set_markerfacecolor(self, color: Any) -> None: def set_markeredgecolor(self, color: Any) -> None: for entry in self._marker_entries(): if isinstance(color, str) and color.lower() == "none": + old_width = float(entry["kwargs"].get("stroke_width", 0.0)) entry["kwargs"].pop("stroke", None) entry["kwargs"].pop("stroke_width", None) + entry["kwargs"]["size"] = max( + 0.0, float(entry["kwargs"].get("size", 0.0)) - old_width + ) else: + if "stroke_width" not in entry["kwargs"]: + width = float(rcParams["lines.markeredgewidth"]) * (4.0 / 3.0) + entry["kwargs"]["stroke_width"] = width + entry["kwargs"]["size"] = float(entry["kwargs"].get("size", 0.0)) + width entry["kwargs"]["stroke"] = resolve_color(color) - entry["kwargs"].setdefault("stroke_width", 1.0) self._touch() set_mec = set_markeredgecolor @@ -248,9 +256,9 @@ def set_markeredgecolor(self, color: Any) -> None: def set_markersize(self, size: Any) -> None: # Matplotlib specifies Line2D marker size in points; xy's scatter mark # consumes CSS-pixel diameters. 96 dpi maps one point to 4/3 pixels. - diameter = float(size) * (4.0 / 3.0) for entry in self._marker_entries(): - entry["kwargs"]["size"] = diameter + stroke_width = float(entry["kwargs"].get("stroke_width", 0.0)) + entry["kwargs"]["size"] = float(size) * (4.0 / 3.0) + stroke_width self._touch() set_ms = set_markersize diff --git a/python/xy/pyplot/_axes.py b/python/xy/pyplot/_axes.py index a3eb7eb5..d8679710 100644 --- a/python/xy/pyplot/_axes.py +++ b/python/xy/pyplot/_axes.py @@ -435,6 +435,7 @@ def __init__(self, figure: Any, *, y2_of: Optional["Axes"] = None) -> None: self._padding: Optional[list[float]] = None self._xmargin = 0.0 self._ymargin = 0.0 + self._margin_overrides: set[str] = set() self._explicit_domains: set[str] = set() self._secondary_axes: list[SecondaryAxis] = [] self._scale_specs: dict[str, dict[str, Any]] = { @@ -486,9 +487,11 @@ def _load_rc_chrome(self) -> None: } family = rcParams["font.family"] family = family if isinstance(family, str) else ", ".join(map(str, family)) + if family == "sans-serif": + family = "DejaVu Sans, sans-serif" self._theme_style = { "font-family": family, - "font-size": f"{float(rcParams['font.size']):g}px", + "font-size": f"{_font_size(rcParams['font.size'], rcParams['font.size']):g}px", } title_color = self._theme_tokens["text_color"] self._chrome_styles = { @@ -830,6 +833,29 @@ def _plot_series( "opacity": per.get("opacity", 1.0), "name": per.get("name"), } + marker_size_pt = float(rcParams["lines.markersize"] if markersize is None else markersize) + marker_edge_visible = not ( + isinstance(markeredgecolor, str) and markeredgecolor.lower() == "none" + ) + marker_edge_px = ( + float(rcParams["lines.markeredgewidth"] if markeredgewidth is None else markeredgewidth) + * (4.0 / 3.0) + if marker_edge_visible + else 0.0 + ) + marker_size_px = marker_size_pt * (4.0 / 3.0) + marker_edge_px + marker_edge_style = ( + { + "stroke": ( + entry_kwargs["color"] + if markeredgecolor in (None, "auto") + else resolve_color(markeredgecolor) + ), + "stroke_width": marker_edge_px, + } + if marker_edge_visible + else {} + ) if dash == "none": entry = self._add( "scatter", @@ -839,24 +865,13 @@ def _plot_series( "kwargs": { **{k: v for k, v in entry_kwargs.items() if k != "width"}, "symbol": _marker_symbol(this_marker or "o"), - "size": float( - rcParams["lines.markersize"] if markersize is None else markersize - ), + "size": marker_size_px, + **marker_edge_style, **( {"color": resolve_color(markerfacecolor)} if markerfacecolor not in (None, "auto") else {} ), - **( - { - "stroke": resolve_color(markeredgecolor), - "stroke_width": float( - 1.0 if markeredgewidth is None else markeredgewidth - ), - } - if markeredgecolor not in (None, "auto", "none") - else {} - ), }, }, ) @@ -926,26 +941,14 @@ def _plot_series( # Matplotlib marker sizes are points while the # engine consumes CSS-pixel diameters. At the # default 96 dpi, 6 pt is 8 px. - "size": float( - rcParams["lines.markersize"] if markersize is None else markersize - ) - * (4.0 / 3.0), + "size": marker_size_px, "name": None, + **marker_edge_style, **( {"color": resolve_color(markerfacecolor)} if markerfacecolor not in (None, "auto") else {} ), - **( - { - "stroke": resolve_color(markeredgecolor), - "stroke_width": float( - 1.0 if markeredgewidth is None else markeredgewidth - ), - } - if markeredgecolor not in (None, "auto", "none") - else {} - ), }, }, ) @@ -2188,25 +2191,70 @@ def _auto_domain(self, axis: str) -> tuple[float, float]: return lo - pad, hi + pad def axis(self, arg: Any = None, **kwargs: Any) -> tuple[float, float, float, float]: - del kwargs - if isinstance(arg, (tuple, list)) and len(arg) == 4: + kwargs.pop("emit", None) # compat-noop: callback emission is not exposed + if isinstance(arg, bool): + arg = "on" if arg else "off" + if isinstance(arg, str): + arg = arg.lower() + + if isinstance(arg, (tuple, list)): + if len(arg) != 4: + raise TypeError("the first argument to axis() must be [xmin, xmax, ymin, ymax]") self.set_xlim(arg[0], arg[1]) self.set_ylim(arg[2], arg[3]) elif arg == "off": + self._materialize_axis_view_domains() self.set_axis_off() elif arg == "on": + self._materialize_axis_view_domains() self.xaxis.set_visible(True) self.yaxis.set_visible(True) - elif arg in ("equal", "scaled"): - self._set_aspect_equal_from_current() - elif arg == "tight": + elif arg in {"auto", "equal", "scaled", "image", "square"}: + # All five Matplotlib modes begin with autoscale_view(tight=False), + # whose limits include the configured x/y margins. + self._aspect_equal = False + self._aspect_bounds = None self._set_tight_domains() - elif arg == "auto": + if arg in {"equal", "scaled", "image", "square"}: + self._set_aspect_equal_from_current() + if arg in {"scaled", "image"}: + x0, x1 = self.get_xlim() + y0, y1 = self.get_ylim() + self._set_box_aspect_ratio(abs(x1 - x0) / max(abs(y1 - y0), 1e-12)) + if arg == "square": + x0, x1 = self.get_xlim() + y0, y1 = self.get_ylim() + edge = max(abs(x1 - x0), abs(y1 - y0)) + self.set_xlim(x0, x0 + edge) + self.set_ylim(y0, y0 + edge) + self._aspect_bounds = (x0, x0 + edge, y0, y0 + edge) + self._set_box_aspect_ratio(1.0) + elif arg == "tight": self._aspect_equal = False self._aspect_bounds = None - self._invalidate() + self._set_tight_domains() elif arg is not None: raise ValueError(f"unsupported axis() argument {arg!r}") + + if arg is None: + self._materialize_axis_view_domains() + limits = {} + for axis_name in ("x", "y"): + lower = kwargs.pop(f"{axis_name}min", None) + upper = kwargs.pop(f"{axis_name}max", None) + if lower is not None or upper is not None: + limits[axis_name] = (lower, upper) + if "x" in limits: + x0, x1 = self.get_xlim() + lower, upper = limits["x"] + self.set_xlim(x0 if lower is None else lower, x1 if upper is None else upper) + if "y" in limits: + y0, y1 = self.get_ylim() + lower, upper = limits["y"] + self.set_ylim(y0 if lower is None else lower, y1 if upper is None else upper) + + if kwargs: + raise TypeError(f"axis() got an unexpected keyword argument {next(iter(kwargs))!r}") x0, x1 = self.get_xlim() y0, y1 = self.get_ylim() return float(x0), float(x1), float(y0), float(y1) @@ -2237,10 +2285,12 @@ def margins(self, *args: Any, **kwargs: Any) -> None: return if x is not None: self._xmargin = _validate_margin(x, "x") + self._margin_overrides.add("x") if "x" not in self._explicit_domains: self._axis_props("x").pop("domain", None) if y is not None: self._ymargin = _validate_margin(y, "y") + self._margin_overrides.add("y") if "y" not in self._explicit_domains: self._axis_props("y").pop("domain", None) self._invalidate() @@ -2459,11 +2509,43 @@ def secondary_yaxis( return made def _set_tight_domains(self) -> None: - self._axis_props("x")["domain"] = self._entry_extent("x") - self._axis_props("y")["domain"] = self._entry_extent("y") + # Matplotlib's axis("tight") disables further autoscaling after an + # autoscale_view(tight=True), but that view still includes the current + # axes.xmargin/axes.ymargin (5% by default). "Tight" suppresses tick + # locator expansion; it does not mean raw data extrema. + for axis in ("x", "y"): + margin = ( + (self._xmargin if axis == "x" else self._ymargin) + if axis in self._margin_overrides + else float(rcParams[f"axes.{axis}margin"]) + ) + lo, hi = self._entry_extent(axis) + span = hi - lo + pad = span * margin if span > 0 else abs(lo) * margin or margin + self._axis_props(axis)["domain"] = (lo - pad, hi + pad) self._explicit_domains.update({"x", "y"}) self._invalidate() + def _materialize_axis_view_domains(self) -> None: + """Expose Matplotlib-like auto limits for axis query/decorative forms.""" + if not self._entries: + return + changed = False + for axis in ("x", "y"): + if "domain" in self._axis_props(axis): + continue + margin = ( + (self._xmargin if axis == "x" else self._ymargin) + if axis in self._margin_overrides + else float(rcParams[f"axes.{axis}margin"]) + ) + lo, hi = self._entry_extent(axis) + pad = (hi - lo) * margin + self._axis_props(axis)["domain"] = (lo - pad, hi + pad) + changed = True + if changed: + self._invalidate() + def _set_aspect_equal_from_current(self) -> None: x0, x1 = self._axis_props("x").get("domain", self._auto_domain("x")) y0, y1 = self._axis_props("y").get("domain", self._auto_domain("y")) @@ -2471,6 +2553,28 @@ def _set_aspect_equal_from_current(self) -> None: self._aspect_bounds = (float(x0), float(x1), float(y0), float(y1)) self._invalidate() + def _set_box_aspect_ratio(self, ratio: float) -> None: + """Center a Matplotlib-style adjustable box for one ordinary axes.""" + if self.figure is None or len(self.figure.axes) != 1: + # Multi-panel box placement belongs to the grid compositor. Keep + # equal-unit domain rendering there rather than turning one subplot + # into a free-form figure rectangle that overlaps its neighbors. + return + figure_width, figure_height = self.figure._panel_px() + x0, y0, width, height = self.get_position().bounds + current = (width * figure_width) / max(height * figure_height, 1e-12) + if current > ratio: + new_width = height * figure_height * ratio / figure_width + x0 += (width - new_width) * 0.5 + width = new_width + elif current < ratio: + new_height = width * figure_width / ratio / figure_height + y0 += (height - new_height) * 0.5 + height = new_height + self._figure_rect = (x0, y0, width, height) + self._absolute_plot_ratio = ratio + self._invalidate() + def set_axis_off(self) -> None: self.xaxis.set_visible(False) self.yaxis.set_visible(False) @@ -2943,9 +3047,9 @@ def tick_params(self, axis: str = "both", **kwargs: Any) -> None: if labelcolor is not None: style["tick_label_color"] = resolve_color(labelcolor) if length is not None: - style["tick_length"] = float(length) + style["tick_length"] = float(length) * (4.0 / 3.0) if width is not None: - style["tick_width"] = float(width) + style["tick_width"] = float(width) * (4.0 / 3.0) if direction is not None: if direction not in {"in", "out", "inout"}: raise ValueError("tick_params() direction must be 'in', 'out', or 'inout'") @@ -3330,6 +3434,8 @@ def _chart_children(self) -> list[Any]: if isinstance(name, str) and "$" in name: # legend text carries mathtext kw["name"] = _plain_text(name) if kind == "line": + kw = dict(kw) + kw["width"] = float(kw.get("width", rcParams["lines.linewidth"])) * (4.0 / 3.0) children.append(fc.line(x=e["x"], y=e["y"], **kw, **axis_kw)) elif kind == "scatter": kw = dict(kw) @@ -3395,6 +3501,11 @@ def _chart_children(self) -> list[Any]: for key, value in kw.items() if key in {"dx", "dy", "color", "anchor", "class_name", "style"} } + if "font_size" in (text_kw.get("style") or {}): + text_kw["style"] = dict(text_kw["style"]) + text_kw["style"]["font_size"] = float(text_kw["style"]["font_size"]) * ( + 4.0 / 3.0 + ) if opacity is not None and float(opacity) < 1.0: text_kw["style"] = {**(text_kw.get("style") or {}), "opacity": float(opacity)} children.append(fc.text(*e["args"], **text_kw)) @@ -3473,7 +3584,7 @@ def _build_chart(self, width: int, height: int) -> Any: data_ratio = (x1 - x0) / max(y1 - y0, np.finfo(float).eps) panel_ratio = ( self._absolute_plot_ratio - if self._figure_rect is not None and self._absolute_plot_ratio is not None + if self._absolute_plot_ratio is not None else max(1.0, width - 80) / max(1.0, height - 60) ) if data_ratio < panel_ratio: @@ -3495,6 +3606,7 @@ def _build_chart(self, width: int, height: int) -> Any: y_props = {k: v for k, v in self._axis["y"].items() if v is not None} self._apply_tickers("x", x_props) self._apply_tickers("y", y_props) + self._apply_auto_tick_density(x_props, y_props, width, height) children.append(_cached_axis("x", x_props)) children.append(_cached_axis("y", y_props)) for index, secondary in enumerate(self._secondary_axes, 1): @@ -3542,6 +3654,42 @@ def _build_chart(self, width: int, height: int) -> Any: figure.colorbar_options = options return self._chart + def _apply_auto_tick_density( + self, + x_props: dict[str, Any], + y_props: dict[str, Any], + width: int, + height: int, + ) -> None: + """Match Matplotlib AutoLocator's axes-size tick-space heuristic.""" + compact = width < 520 + if self._padding is None: + left, right = (46.0, 8.0) if compact else (62.0, 14.0) + top, bottom = (6.0, 36.0) if compact else (10.0, 42.0) + else: + top, right, bottom, left = map(float, self._padding) + if self._title: + top += 26.0 if compact else 30.0 + if x_props.get("side") == "top": + top += 26.0 if compact else 32.0 + plot_width = max(40.0, float(width) - left - right) + plot_height = max(40.0, float(height) - top - bottom) + dpi = float(self.figure._dpi if self.figure._dpi is not None else rcParams["figure.dpi"]) + base = float(rcParams["font.size"]) + x_font = _font_size_points(rcParams["xtick.labelsize"], base) + y_font = _font_size_points(rcParams["ytick.labelsize"], base) + counts = { + "x": max(1, min(9, int(np.floor(plot_width * 72.0 / dpi / (x_font * 3.0))))), + "y": max(1, min(9, int(np.floor(plot_height * 72.0 / dpi / (y_font * 2.0))))), + } + for axis, props in (("x", x_props), ("y", y_props)): + if ( + "tick_count" not in props + and "tick_values" not in props + and (axis, "major_locator") not in self._tickers + ): + props["tick_count"] = counts[axis] + def _discrete_levels(cmap: Any) -> Optional[int]: """Number of quantization bands for a resampled colormap (``get_cmap(n, N)``). @@ -3593,7 +3741,7 @@ def _is_number(v: Any) -> bool: return isinstance(v, (int, float, np.integer, np.floating)) -def _font_size(value: Any, base: Any) -> float: +def _font_size_points(value: Any, base: Any) -> float: relative = { "xx-small": 0.6, "x-small": 0.75, @@ -3613,11 +3761,18 @@ def _font_size(value: Any, base: Any) -> float: return result +def _font_size(value: Any, base: Any) -> float: + return _font_size_points(value, base) * (4.0 / 3.0) + + def _rc_axis_style(axis: str) -> dict[str, Any]: prefix = "xtick" if axis == "x" else "ytick" tick_color = rcParams[f"{prefix}.color"] label_color = rcParams[f"{prefix}.labelcolor"] result: dict[str, Any] = {} + result["axis_width"] = float(rcParams["axes.linewidth"]) * (4.0 / 3.0) + result["tick_length"] = float(rcParams[f"{prefix}.major.size"]) * (4.0 / 3.0) + result["tick_width"] = float(rcParams[f"{prefix}.major.width"]) * (4.0 / 3.0) if tick_color != "black": result["tick_color"] = resolve_color(tick_color) if label_color != "inherit" or tick_color != "black": diff --git a/python/xy/pyplot/_mplfig.py b/python/xy/pyplot/_mplfig.py index 6d06524a..b0de0654 100644 --- a/python/xy/pyplot/_mplfig.py +++ b/python/xy/pyplot/_mplfig.py @@ -801,11 +801,40 @@ def _to_html(self) -> str: ) return self._html_cache + def _to_notebook_html(self) -> tuple[str, int, int]: + """Notebook-only tight layout matching Matplotlib's inline backend.""" + width, height = rc_figsize_px(self._figsize, self._dpi) + dpi = float(self._dpi if self._dpi is not None else 100.0) + if ( + self._nrows == self._ncols == 1 + and len(self._axes) == 1 + and self._axes[0]._figure_rect is None + and self._suptitle is None + ): + # Matplotlib's inline backend displays figures with + # bbox_inches="tight" and pad_inches=.1. For the ordinary default + # axes this retains the 0.775×0.77 plot box and its label ink while + # trimming the unused figure canvas. Build directly at that tight + # footprint so fonts/strokes remain unscaled and interactive. + tight_width = max(120, round(width * 0.775 + dpi * 0.62)) + tight_height = max(120, round(height * 0.77 + dpi * 0.48)) + ax = self._axes[0] + old_chart, old_padding = ax._chart, ax._padding + try: + ax._chart = None + ax._padding = [dpi * 0.15, dpi * 0.20, dpi * 0.34, dpi * 0.41] + doc = ax._build_chart(tight_width, tight_height).to_html() + finally: + ax._chart = old_chart + ax._padding = old_padding + return doc, tight_width, tight_height + return self._to_html(), width, height + def _repr_html_(self) -> str: from xy import export - width, height = rc_figsize_px(self._figsize, self._dpi) - return export.notebook_iframe(self._to_html(), width=width, height=height) + doc, width, height = self._to_notebook_html() + return export.notebook_iframe(doc, width=width, height=height) def show(self, *args: Any, **kwargs: Any) -> None: import tempfile diff --git a/python/xy/pyplot/_rc.py b/python/xy/pyplot/_rc.py index 3585427f..5bb250c1 100644 --- a/python/xy/pyplot/_rc.py +++ b/python/xy/pyplot/_rc.py @@ -26,6 +26,7 @@ def by_key(self) -> dict[str, list[str]]: "figure.facecolor": "white", "lines.linewidth": 1.5, "lines.markersize": 6.0, + "lines.markeredgewidth": 1.0, "font.size": 10.0, "font.family": ["sans-serif"], "axes.grid": False, @@ -36,6 +37,9 @@ def by_key(self) -> dict[str, list[str]]: "axes.labelsize": "medium", "axes.titlesize": "large", "axes.titlecolor": "auto", + "axes.linewidth": 0.8, + "axes.xmargin": 0.05, + "axes.ymargin": 0.05, "axes.spines.left": True, "axes.spines.bottom": True, "axes.spines.top": True, @@ -46,6 +50,10 @@ def by_key(self) -> dict[str, list[str]]: "ytick.labelcolor": "inherit", "xtick.labelsize": "medium", "ytick.labelsize": "medium", + "xtick.major.size": 3.5, + "ytick.major.size": 3.5, + "xtick.major.width": 0.8, + "ytick.major.width": 0.8, "legend.loc": "best", "legend.fontsize": "medium", "legend.facecolor": "inherit", @@ -81,6 +89,19 @@ def __setitem__(self, key: str, value: Any) -> None: value = float(value) if value <= 0: raise ValueError(f"{key} must be positive") + if key in { + "axes.xmargin", + "axes.ymargin", + "axes.linewidth", + "lines.markeredgewidth", + "xtick.major.size", + "ytick.major.size", + "xtick.major.width", + "ytick.major.width", + }: + value = float(value) + if value < 0: + raise ValueError(f"{key} must be non-negative") if isinstance(value, list): value = list(value) # never share list defaults; rcdefaults() must stay pristine super().__setitem__(key, value) diff --git a/python/xy/static/index.js b/python/xy/static/index.js index 26551236..fc3b1561 100644 --- a/python/xy/static/index.js +++ b/python/xy/static/index.js @@ -3656,21 +3656,51 @@ const frameSides = Array.isArray(s.frame_sides) ? s.frame_sides : [xAxis.side || "bottom", yAxis.side || "left"]; if (!hideY) { -const yWidth = Math.max(1, Math.round(this._axisStyleNumber(yAxis, "axis_width", 1))); +const yWidth = Math.max(1, this._axisStyleNumber(yAxis, "axis_width", 1)); if (frameSides.includes("left")) rule(yAxis, p.x, p.y, yWidth, p.h); if (frameSides.includes("right")) rule(yAxis, p.x + p.w - yWidth, p.y, yWidth, p.h); } if (!hideX) { -const xHeight = Math.max(1, Math.round(this._axisStyleNumber(xAxis, "axis_width", 1))); +const xHeight = Math.max(1, this._axisStyleNumber(xAxis, "axis_width", 1)); if (frameSides.includes("top")) rule(xAxis, p.x, p.y, p.w, xHeight); if (frameSides.includes("bottom")) rule(xAxis, p.x, p.y + p.h - xHeight, p.w, xHeight); } for (const axis of Object.values(this.axes)) { if (!axis || axis.id === "y" || !String(axis.id || "").startsWith("y")) continue; -const w = Math.max(1, Math.round(this._axisStyleNumber(axis, "axis_width", 1))); +const w = Math.max(1, this._axisStyleNumber(axis, "axis_width", 1)); const x = axis.side === "left" ? p.x : p.x + p.w - w; rule(axis, x, p.y, w, p.h); } +const tickParts = (axis) => { +const length = Math.max(0, this._axisStyleNumber(axis, "tick_length", 0)); +const width = Math.max(0.5, this._axisStyleNumber(axis, "tick_width", 1)); +const direction = String(this._axisStyleValue(axis, "tick_direction") || "out"); +if (direction === "in") return { inward: length, outward: 0, width }; +if (direction === "inout") return { inward: length / 2, outward: length / 2, width }; +return { inward: 0, outward: length, width }; +}; +if (!hideX) { +const tick = tickParts(xAxis); +const side = xAxis.side || "bottom"; +const edge = side === "top" ? p.y : p.y + p.h; +for (const value of xt.ticks) { +const x = this._dataPx("x", value); +if (!Number.isFinite(x) || x < p.x - 1 || x > p.x + p.w + 1) continue; +const top = side === "top" ? edge - tick.outward : edge - tick.inward; +rule(xAxis, x - tick.width / 2, top, tick.width, tick.inward + tick.outward); +} +} +if (!hideY) { +const tick = tickParts(yAxis); +const side = yAxis.side || "left"; +const edge = side === "right" ? p.x + p.w : p.x; +for (const value of yt.ticks) { +const y = this._dataPx("y", value); +if (!Number.isFinite(y) || y < p.y - 1 || y > p.y + p.h + 1) continue; +const left = side === "right" ? edge - tick.inward : edge - tick.outward; +rule(yAxis, left, y - tick.width / 2, tick.inward + tick.outward, tick.width); +} +} } const label = (text, css, axis, kind = "tick", extraStyle = null) => { if (!updateLabels) return; diff --git a/python/xy/static/standalone.js b/python/xy/static/standalone.js index c79488a1..1c0917a8 100644 --- a/python/xy/static/standalone.js +++ b/python/xy/static/standalone.js @@ -3657,21 +3657,51 @@ const frameSides = Array.isArray(s.frame_sides) ? s.frame_sides : [xAxis.side || "bottom", yAxis.side || "left"]; if (!hideY) { -const yWidth = Math.max(1, Math.round(this._axisStyleNumber(yAxis, "axis_width", 1))); +const yWidth = Math.max(1, this._axisStyleNumber(yAxis, "axis_width", 1)); if (frameSides.includes("left")) rule(yAxis, p.x, p.y, yWidth, p.h); if (frameSides.includes("right")) rule(yAxis, p.x + p.w - yWidth, p.y, yWidth, p.h); } if (!hideX) { -const xHeight = Math.max(1, Math.round(this._axisStyleNumber(xAxis, "axis_width", 1))); +const xHeight = Math.max(1, this._axisStyleNumber(xAxis, "axis_width", 1)); if (frameSides.includes("top")) rule(xAxis, p.x, p.y, p.w, xHeight); if (frameSides.includes("bottom")) rule(xAxis, p.x, p.y + p.h - xHeight, p.w, xHeight); } for (const axis of Object.values(this.axes)) { if (!axis || axis.id === "y" || !String(axis.id || "").startsWith("y")) continue; -const w = Math.max(1, Math.round(this._axisStyleNumber(axis, "axis_width", 1))); +const w = Math.max(1, this._axisStyleNumber(axis, "axis_width", 1)); const x = axis.side === "left" ? p.x : p.x + p.w - w; rule(axis, x, p.y, w, p.h); } +const tickParts = (axis) => { +const length = Math.max(0, this._axisStyleNumber(axis, "tick_length", 0)); +const width = Math.max(0.5, this._axisStyleNumber(axis, "tick_width", 1)); +const direction = String(this._axisStyleValue(axis, "tick_direction") || "out"); +if (direction === "in") return { inward: length, outward: 0, width }; +if (direction === "inout") return { inward: length / 2, outward: length / 2, width }; +return { inward: 0, outward: length, width }; +}; +if (!hideX) { +const tick = tickParts(xAxis); +const side = xAxis.side || "bottom"; +const edge = side === "top" ? p.y : p.y + p.h; +for (const value of xt.ticks) { +const x = this._dataPx("x", value); +if (!Number.isFinite(x) || x < p.x - 1 || x > p.x + p.w + 1) continue; +const top = side === "top" ? edge - tick.outward : edge - tick.inward; +rule(xAxis, x - tick.width / 2, top, tick.width, tick.inward + tick.outward); +} +} +if (!hideY) { +const tick = tickParts(yAxis); +const side = yAxis.side || "left"; +const edge = side === "right" ? p.x + p.w : p.x; +for (const value of yt.ticks) { +const y = this._dataPx("y", value); +if (!Number.isFinite(y) || y < p.y - 1 || y > p.y + p.h + 1) continue; +const left = side === "right" ? edge - tick.inward : edge - tick.outward; +rule(yAxis, left, y - tick.width / 2, tick.inward + tick.outward, tick.width); +} +} } const label = (text, css, axis, kind = "tick", extraStyle = null) => { if (!updateLabels) return; diff --git a/tests/pyplot/test_artist_mutations.py b/tests/pyplot/test_artist_mutations.py index cff831ae..5f262adc 100644 --- a/tests/pyplot/test_artist_mutations.py +++ b/tests/pyplot/test_artist_mutations.py @@ -32,7 +32,7 @@ def test_line2d_marker_style_setters_mutate_marker_overlay() -> None: assert [trace.kind for trace in traces] == ["line", "scatter"] assert traces[1].color_ch.constant == "#d62728" assert traces[1].style["stroke"] == "#000000" - assert traces[1].size_ch.constant == 12.0 + assert traces[1].size_ch.constant == pytest.approx(40.0 / 3.0) def test_marker_only_plot_handle_supports_marker_style_setters() -> None: @@ -50,6 +50,18 @@ def test_marker_only_plot_handle_supports_marker_style_setters() -> None: assert trace.size_ch.constant == 8.0 +def test_plot_marker_diameters_convert_points_to_pixels_in_every_path() -> None: + _fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1], "o") + ax.plot([0, 1], [1, 0], "o-") + ax.plot([0, 1], [0.5, 0.5], "o", markersize=9) + + traces = ax._build_chart(640, 480).figure().traces + marker_sizes = [trace.size_ch.constant for trace in traces if trace.kind == "scatter"] + + assert marker_sizes == pytest.approx([28.0 / 3.0, 28.0 / 3.0, 40.0 / 3.0]) + + def test_segment_backed_line2d_set_ydata_rebuilds_retained_logical_data() -> None: _fig, ax = plt.subplots() (line,) = ax.plot([0, 2, 1], [1, 2, 3]) diff --git a/tests/pyplot/test_axes_layout.py b/tests/pyplot/test_axes_layout.py index 3f3e54b6..7849cc04 100644 --- a/tests/pyplot/test_axes_layout.py +++ b/tests/pyplot/test_axes_layout.py @@ -62,17 +62,96 @@ def test_axis_tight_sets_data_domains_and_equal_expands_to_panel_ratio() -> None _fig, ax = plt.subplots() ax.plot([0.0, 2.0], [0.0, 1.0]) - assert ax.axis("tight") == (0.0, 2.0, 0.0, 1.0) - assert ax._axis["x"]["domain"] == (0.0, 2.0) - assert ax._axis["y"]["domain"] == (0.0, 1.0) + assert ax.axis("tight") == pytest.approx((-0.1, 2.1, -0.05, 1.05)) + assert ax._axis["x"]["domain"] == pytest.approx((-0.1, 2.1)) + assert ax._axis["y"]["domain"] == pytest.approx((-0.05, 1.05)) ax.axis("equal") x_axis = _axis_child(ax, "x") y_axis = _axis_child(ax, "y") - assert x_axis.domain == (0.0, 2.0) - assert y_axis.domain[0] < 0.0 - assert y_axis.domain[1] > 1.0 + assert x_axis.domain == pytest.approx((-0.1, 2.1)) + assert y_axis.domain[0] < -0.05 + assert y_axis.domain[1] > 1.05 + + +def test_axis_tight_honors_configured_margins() -> None: + _fig, ax = plt.subplots() + ax.plot([0.0, 10.0], [-1.0, 1.0]) + ax.margins(x=0.1, y=0.25) + + assert ax.axis("tight") == pytest.approx((-1.0, 11.0, -1.5, 1.5)) + + +@pytest.mark.parametrize("mode", ["auto", "equal", "scaled", "image"]) +def test_axis_autoscale_and_aspect_modes_start_from_padded_limits(mode: str) -> None: + _fig, ax = plt.subplots() + ax.plot([0.0, 2.0], [0.0, 1.0]) + + assert ax.axis(mode) == pytest.approx((-0.1, 2.1, -0.05, 1.05)) + assert ax._aspect_equal is (mode != "auto") + + +def test_axis_square_matches_matplotlib_limit_contract() -> None: + _fig, ax = plt.subplots() + ax.plot([0.0, 2.0], [0.0, 1.0]) + + assert ax.axis("square") == pytest.approx((-0.1, 2.1, -0.05, 2.15)) + assert ax._aspect_equal + assert ax._absolute_plot_ratio == 1.0 + assert ax.get_position().bounds == pytest.approx((0.22375, 0.11, 0.5775, 0.77)) + + +@pytest.mark.parametrize("mode", ["scaled", "image"]) +def test_axis_adjustable_box_modes_match_matplotlib_position(mode: str) -> None: + fig, ax = plt.subplots(figsize=(6.4, 4.8), dpi=100) + ax.plot([0.0, 2.0], [0.0, 1.0]) + + ax.axis(mode) + ax._build_chart(*fig._panel_px()) + + assert ax.get_xlim() == pytest.approx((-0.1, 2.1)) + assert ax.get_ylim() == pytest.approx((-0.05, 1.05)) + assert ax.get_position().bounds == pytest.approx((0.125, 0.2366666667, 0.775, 0.5166666667)) + + +def test_axis_boolean_case_insensitive_and_keyword_forms() -> None: + _fig, ax = plt.subplots() + ax.plot([0.0, 2.0], [0.0, 1.0]) + + ax.axis(False) + assert ax._axis_props("x")["tick_label_strategy"] == "none" + ax.axis("ON") + assert ax._axis_props("x")["tick_label_strategy"] is None + assert ax.axis(xmin=-3.0, ymax=4.0) == pytest.approx((-3.0, 2.1, -0.05, 4.0)) + + with pytest.raises(TypeError, match="unexpected keyword"): + ax.axis(zmin=0.0) + with pytest.raises(TypeError, match="xmin, xmax, ymin, ymax"): + ax.axis([0.0, 1.0]) + + +def test_default_auto_tick_density_matches_matplotlib_tick_space() -> None: + fig, ax = plt.subplots(figsize=(6.4, 4.8), dpi=100) + ax.plot([0.0, 10.0], [-1.0, 1.0]) + + chart = ax._build_chart(558, 418) + axes = {child.which: child for child in chart.children if hasattr(child, "which")} + + assert axes["x"].tick_count == 9 + assert axes["y"].tick_count == 9 + + +def test_auto_tick_density_reduces_for_shorter_axes() -> None: + fig, ax = plt.subplots(figsize=(4.0, 3.0), dpi=100) + ax.plot([0.0, 10.0], [-1.0, 1.0]) + ax._padding = [14.0, 19.0, 34.0, 43.0] + + chart = ax._build_chart(372, 279) + axes = {child.which: child for child in chart.children if hasattr(child, "which")} + + assert axes["x"].tick_count == 7 + assert axes["y"].tick_count == 8 def test_tick_params_records_supported_style_and_rejects_unknown() -> None: @@ -92,10 +171,11 @@ def test_tick_params_records_supported_style_and_rejects_unknown() -> None: assert x_axis.tick_label_angle == 45.0 assert x_axis.tick_label_strategy == "off" # labels hidden, ticks/baselines kept assert x_axis.style == { + "axis_width": pytest.approx(0.8 * 4.0 / 3.0), "tick_color": "#d62728", "tick_label_color": "#d62728", - "tick_length": 7.0, - "tick_width": 2.0, + "tick_length": pytest.approx(7.0 * 4.0 / 3.0), + "tick_width": pytest.approx(2.0 * 4.0 / 3.0), "tick_direction": "in", } diff --git a/tests/pyplot/test_corpus.py b/tests/pyplot/test_corpus.py index 800cd82a..6a63d01b 100644 --- a/tests/pyplot/test_corpus.py +++ b/tests/pyplot/test_corpus.py @@ -29,14 +29,15 @@ XFAILS: dict[str, str] = {} PNG_MAGIC = b"\x89PNG\r\n\x1a\n" -HTML_MAGIC = '