diff --git a/src/components/annotations/draw.js b/src/components/annotations/draw.js
index 3bd4875456b..1548a2400a6 100644
--- a/src/components/annotations/draw.js
+++ b/src/components/annotations/draw.js
@@ -194,7 +194,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
var isSizeConstrained = options.width || options.height;
- var annTextClip = fullLayout._defs.select('.clips')
+ var annTextClip = fullLayout._topclips
.selectAll('#' + annClipID)
.data(isSizeConstrained ? [0] : []);
diff --git a/src/components/fx/layout_defaults.js b/src/components/fx/layout_defaults.js
index a4800d094f0..f0939835d49 100644
--- a/src/components/fx/layout_defaults.js
+++ b/src/components/fx/layout_defaults.js
@@ -29,11 +29,17 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
coerce('hovermode', hovermodeDflt);
- // if only mapbox subplots is present on graph,
+ // if only mapbox or geo subplots is present on graph,
// reset 'zoom' dragmode to 'pan' until 'zoom' is implemented,
// so that the correct modebar button is active
- if(layoutOut._has('mapbox') && layoutOut._basePlotModules.length === 1 &&
- layoutOut.dragmode === 'zoom') {
+ var hasMapbox = layoutOut._has('mapbox');
+ var hasGeo = layoutOut._has('geo');
+ var len = layoutOut._basePlotModules.length;
+
+ if(layoutOut.dragmode === 'zoom' && (
+ ((hasMapbox || hasGeo) && len === 1) ||
+ (hasMapbox && hasGeo && len === 2)
+ )) {
layoutOut.dragmode = 'pan';
}
};
diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js
index 2fa852e70de..3f2875c55b7 100644
--- a/src/components/modebar/buttons.js
+++ b/src/components/modebar/buttons.js
@@ -446,23 +446,24 @@ modeBarButtons.hoverClosestGeo = {
};
function handleGeo(gd, ev) {
- var button = ev.currentTarget,
- attr = button.getAttribute('data-attr'),
- val = button.getAttribute('data-val') || true,
- fullLayout = gd._fullLayout,
- geoIds = Plots.getSubplotIds(fullLayout, 'geo');
+ var button = ev.currentTarget;
+ var attr = button.getAttribute('data-attr');
+ var val = button.getAttribute('data-val') || true;
+ var fullLayout = gd._fullLayout;
+ var geoIds = Plots.getSubplotIds(fullLayout, 'geo');
for(var i = 0; i < geoIds.length; i++) {
- var geo = fullLayout[geoIds[i]]._subplot;
+ var id = geoIds[i];
+ var geoLayout = fullLayout[id];
if(attr === 'zoom') {
- var scale = geo.projection.scale();
+ var scale = geoLayout.projection.scale;
var newScale = (val === 'in') ? 2 * scale : 0.5 * scale;
- geo.projection.scale(newScale);
- geo.zoom.scale(newScale);
- geo.render();
+
+ Plotly.relayout(gd, id + '.projection.scale', newScale);
+ } else if(attr === 'reset') {
+ resetView(gd, 'geo');
}
- else if(attr === 'reset') geo.zoomReset();
}
}
@@ -535,8 +536,8 @@ modeBarButtons.resetViews = {
button.setAttribute('data-attr', 'resetLastSave');
handleCamera3d(gd, ev);
- // N.B handleCamera3d also triggers a replot for
- // geo subplots.
+ resetView(gd, 'geo');
+ resetView(gd, 'mapbox');
}
};
@@ -581,22 +582,26 @@ modeBarButtons.resetViewMapbox = {
attr: 'reset',
icon: Icons.home,
click: function(gd) {
- var fullLayout = gd._fullLayout;
- var subplotIds = Plots.getSubplotIds(fullLayout, 'mapbox');
- var aObj = {};
-
- for(var i = 0; i < subplotIds.length; i++) {
- var id = subplotIds[i];
- var subplotObj = fullLayout[id]._subplot;
- var viewInitial = subplotObj.viewInitial;
- var viewKeys = Object.keys(viewInitial);
-
- for(var j = 0; j < viewKeys.length; j++) {
- var key = viewKeys[j];
- aObj[id + '.' + key] = viewInitial[key];
- }
- }
-
- Plotly.relayout(gd, aObj);
+ resetView(gd, 'mapbox');
}
};
+
+function resetView(gd, subplotType) {
+ var fullLayout = gd._fullLayout;
+ var subplotIds = Plots.getSubplotIds(fullLayout, subplotType);
+ var aObj = {};
+
+ for(var i = 0; i < subplotIds.length; i++) {
+ var id = subplotIds[i];
+ var subplotObj = fullLayout[id]._subplot;
+ var viewInitial = subplotObj.viewInitial;
+ var viewKeys = Object.keys(viewInitial);
+
+ for(var j = 0; j < viewKeys.length; j++) {
+ var key = viewKeys[j];
+ aObj[id + '.' + key] = viewInitial[key];
+ }
+ }
+
+ Plotly.relayout(gd, aObj);
+}
diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js
index 7bc6c3b8b34..8feee485140 100644
--- a/src/components/modebar/manage.js
+++ b/src/components/modebar/manage.js
@@ -71,16 +71,16 @@ module.exports = function manageModeBar(gd) {
// logic behind which buttons are displayed by default
function getButtonGroups(gd, buttonsToRemove, buttonsToAdd) {
- var fullLayout = gd._fullLayout,
- fullData = gd._fullData;
+ var fullLayout = gd._fullLayout;
+ var fullData = gd._fullData;
- var hasCartesian = fullLayout._has('cartesian'),
- hasGL3D = fullLayout._has('gl3d'),
- hasGeo = fullLayout._has('geo'),
- hasPie = fullLayout._has('pie'),
- hasGL2D = fullLayout._has('gl2d'),
- hasTernary = fullLayout._has('ternary'),
- hasMapbox = fullLayout._has('mapbox');
+ var hasCartesian = fullLayout._has('cartesian');
+ var hasGL3D = fullLayout._has('gl3d');
+ var hasGeo = fullLayout._has('geo');
+ var hasPie = fullLayout._has('pie');
+ var hasGL2D = fullLayout._has('gl2d');
+ var hasTernary = fullLayout._has('ternary');
+ var hasMapbox = fullLayout._has('mapbox');
var groups = [];
@@ -112,18 +112,13 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd) {
addGroup(['hoverClosest3d']);
}
- if(hasGeo) {
- addGroup(['zoomInGeo', 'zoomOutGeo', 'resetGeo']);
- addGroup(['hoverClosestGeo']);
- }
-
var allAxesFixed = areAllAxesFixed(fullLayout),
dragModeGroup = [];
if(((hasCartesian || hasGL2D) && !allAxesFixed) || hasTernary) {
dragModeGroup = ['zoom2d', 'pan2d'];
}
- if(hasMapbox) {
+ if(hasMapbox || hasGeo) {
dragModeGroup = ['pan2d'];
}
if(isSelectable(fullData)) {
@@ -138,18 +133,17 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd) {
if(hasCartesian && hasPie) {
addGroup(['toggleHover']);
- }
- else if(hasGL2D) {
+ } else if(hasGL2D) {
addGroup(['hoverClosestGl2d']);
- }
- else if(hasCartesian) {
+ } else if(hasCartesian) {
addGroup(['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian']);
- }
- else if(hasPie) {
+ } else if(hasPie) {
addGroup(['hoverClosestPie']);
- }
- else if(hasMapbox) {
+ } else if(hasMapbox) {
addGroup(['resetViewMapbox', 'toggleHover']);
+ } else if(hasGeo) {
+ addGroup(['zoomInGeo', 'zoomOutGeo', 'resetGeo']);
+ addGroup(['hoverClosestGeo']);
}
return appendButtonsToGroups(groups, buttonsToAdd);
diff --git a/src/lib/array_to_calc_item.js b/src/lib/array_to_calc_item.js
deleted file mode 100644
index 4a968234f0a..00000000000
--- a/src/lib/array_to_calc_item.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
-* Copyright 2012-2017, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
-
-
-'use strict';
-
-// similar to Lib.mergeArray, but using inside a loop
-module.exports = function arrayToCalcItem(traceAttr, calcItem, calcAttr, i) {
- if(Array.isArray(traceAttr)) calcItem[calcAttr] = traceAttr[i];
-};
diff --git a/src/lib/geojson_utils.js b/src/lib/geojson_utils.js
index b123c1c68ba..66dea69f192 100644
--- a/src/lib/geojson_utils.js
+++ b/src/lib/geojson_utils.js
@@ -54,32 +54,22 @@ exports.calcTraceToLineCoords = function(calcTrace) {
*
* @param {array} coords
* results form calcTraceToLineCoords
- * @param {object} trace
- * (optional) full trace object to be added on to output
- *
* @return {object} out
* GeoJSON object
*
*/
-exports.makeLine = function(coords, trace) {
- var out = {};
-
+exports.makeLine = function(coords) {
if(coords.length === 1) {
- out = {
+ return {
type: 'LineString',
coordinates: coords[0]
};
- }
- else {
- out = {
+ } else {
+ return {
type: 'MultiLineString',
coordinates: coords
};
}
-
- if(trace) out.trace = trace;
-
- return out;
};
/**
@@ -87,37 +77,27 @@ exports.makeLine = function(coords, trace) {
*
* @param {array} coords
* results form calcTraceToLineCoords
- * @param {object} trace
- * (optional) full trace object to be added on to output
- *
* @return {object} out
* GeoJSON object
*/
-exports.makePolygon = function(coords, trace) {
- var out = {};
-
+exports.makePolygon = function(coords) {
if(coords.length === 1) {
- out = {
+ return {
type: 'Polygon',
coordinates: coords
};
- }
- else {
+ } else {
var _coords = new Array(coords.length);
for(var i = 0; i < coords.length; i++) {
_coords[i] = [coords[i]];
}
- out = {
+ return {
type: 'MultiPolygon',
coordinates: _coords
};
}
-
- if(trace) out.trace = trace;
-
- return out;
};
/**
diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index 58244368e0e..486aea85e45 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2748,8 +2748,8 @@ Plotly.purge = function purge(gd) {
// makePlotFramework: Create the plot container and axes
// -------------------------------------------------------
function makePlotFramework(gd) {
- var gd3 = d3.select(gd),
- fullLayout = gd._fullLayout;
+ var gd3 = d3.select(gd);
+ var fullLayout = gd._fullLayout;
// Plot container
fullLayout._container = gd3.selectAll('.plot-container').data([0]);
@@ -2795,9 +2795,15 @@ function makePlotFramework(gd) {
fullLayout._defs = fullLayout._paper.append('defs')
.attr('id', 'defs-' + fullLayout._uid);
+ fullLayout._clips = fullLayout._defs.append('g')
+ .classed('clips', true);
+
fullLayout._topdefs = fullLayout._toppaper.append('defs')
.attr('id', 'topdefs-' + fullLayout._uid);
+ fullLayout._topclips = fullLayout._topdefs.append('g')
+ .classed('clips', true);
+
fullLayout._bgLayer = fullLayout._paper.append('g')
.classed('bglayer', true);
diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js
index 4e907028c32..f59434c9963 100644
--- a/src/plot_api/subroutines.js
+++ b/src/plot_api/subroutines.js
@@ -151,8 +151,7 @@ exports.lsInner = function(gd) {
// Clip so that data only shows up on the plot area.
plotinfo.clipId = 'clip' + fullLayout._uid + subplot + 'plot';
- var plotClip = fullLayout._defs.selectAll('g.clips')
- .selectAll('#' + plotinfo.clipId)
+ var plotClip = fullLayout._clips.selectAll('#' + plotinfo.clipId)
.data([0]);
plotClip.enter().append('clipPath')
@@ -490,28 +489,15 @@ exports.doTicksRelayout = function(gd) {
exports.doModeBar = function(gd) {
var fullLayout = gd._fullLayout;
- var subplotIds, subplotObj, i;
ModeBar.manage(gd);
initInteractions(gd);
- subplotIds = Plots.getSubplotIds(fullLayout, 'gl3d');
- for(i = 0; i < subplotIds.length; i++) {
- subplotObj = fullLayout[subplotIds[i]]._scene;
- subplotObj.updateFx(fullLayout.dragmode, fullLayout.hovermode);
+ for(var i = 0; i < fullLayout._basePlotModules.length; i++) {
+ var updateFx = fullLayout._basePlotModules[i].updateFx;
+ if(updateFx) updateFx(fullLayout);
}
- subplotIds = Plots.getSubplotIds(fullLayout, 'gl2d');
- for(i = 0; i < subplotIds.length; i++) {
- subplotObj = fullLayout._plots[subplotIds[i]]._scene2d;
- subplotObj.updateFx(fullLayout.dragmode);
- }
-
- subplotIds = Plots.getSubplotIds(fullLayout, 'mapbox');
- for(i = 0; i < subplotIds.length; i++) {
- subplotObj = fullLayout[subplotIds[i]]._subplot;
- subplotObj.updateFx(fullLayout);
- }
return Plots.previousPromises(gd);
};
diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 7d18cc8fd8a..890f5350e16 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -1602,15 +1602,13 @@ axes.findSubplotsWithAxis = function(subplots, ax) {
// makeClipPaths: prepare clipPaths for all single axes and all possible xy pairings
axes.makeClipPaths = function(gd) {
- var fullLayout = gd._fullLayout,
- defs = fullLayout._defs,
- fullWidth = {_offset: 0, _length: fullLayout.width, _id: ''},
- fullHeight = {_offset: 0, _length: fullLayout.height, _id: ''},
- xaList = axes.list(gd, 'x', true),
- yaList = axes.list(gd, 'y', true),
- clipList = [],
- i,
- j;
+ var fullLayout = gd._fullLayout;
+ var fullWidth = {_offset: 0, _length: fullLayout.width, _id: ''};
+ var fullHeight = {_offset: 0, _length: fullLayout.height, _id: ''};
+ var xaList = axes.list(gd, 'x', true);
+ var yaList = axes.list(gd, 'y', true);
+ var clipList = [];
+ var i, j;
for(i = 0; i < xaList.length; i++) {
clipList.push({x: xaList[i], y: fullHeight});
@@ -1620,16 +1618,10 @@ axes.makeClipPaths = function(gd) {
}
}
- var defGroup = defs.selectAll('g.clips')
- .data([0]);
-
- defGroup.enter().append('g')
- .classed('clips', true);
-
// selectors don't work right with camelCase tags,
// have to use class instead
// https://groups.google.com/forum/#!topic/d3-js/6EpAzQ2gU9I
- var axClips = defGroup.selectAll('.axesclip')
+ var axClips = fullLayout._clips.selectAll('.axesclip')
.data(clipList, function(d) { return d.x._id + d.y._id; });
axClips.enter().append('clipPath')
@@ -1649,7 +1641,6 @@ axes.makeClipPaths = function(gd) {
});
};
-
// doTicks: draw ticks, grids, and tick labels
// axid: 'x', 'y', 'x2' etc,
// blank to do all,
diff --git a/src/plots/cartesian/select.js b/src/plots/cartesian/select.js
index d8542f5c090..50095155a99 100644
--- a/src/plots/cartesian/select.js
+++ b/src/plots/cartesian/select.js
@@ -79,16 +79,18 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
if(!trace._module || !trace._module.selectPoints) continue;
if(dragOptions.subplot) {
- if(trace.subplot !== dragOptions.subplot) continue;
-
- searchTraces.push({
- selectPoints: trace._module.selectPoints,
- cd: cd,
- xaxis: dragOptions.xaxes[0],
- yaxis: dragOptions.yaxes[0]
- });
- }
- else {
+ if(
+ trace.subplot === dragOptions.subplot ||
+ trace.geo === dragOptions.subplot
+ ) {
+ searchTraces.push({
+ selectPoints: trace._module.selectPoints,
+ cd: cd,
+ xaxis: dragOptions.xaxes[0],
+ yaxis: dragOptions.yaxes[0]
+ });
+ }
+ } else {
if(xAxisIds.indexOf(trace.xaxis) === -1) continue;
if(yAxisIds.indexOf(trace.yaxis) === -1) continue;
diff --git a/src/plots/geo/constants.js b/src/plots/geo/constants.js
index fde13b5ef2f..384803057c3 100644
--- a/src/plots/geo/constants.js
+++ b/src/plots/geo/constants.js
@@ -6,13 +6,10 @@
* LICENSE file in the root directory of this source tree.
*/
-
'use strict';
-var params = module.exports = {};
-
// projection names to d3 function name
-params.projNames = {
+exports.projNames = {
// d3.geo.projection
'equirectangular': 'equirectangular',
'mercator': 'mercator',
@@ -39,10 +36,10 @@ params.projNames = {
};
// name of the axes
-params.axesNames = ['lonaxis', 'lataxis'];
+exports.axesNames = ['lonaxis', 'lataxis'];
// max longitudinal angular span (EXPERIMENTAL)
-params.lonaxisSpan = {
+exports.lonaxisSpan = {
'orthographic': 180,
'azimuthal equal area': 360,
'azimuthal equidistant': 360,
@@ -54,14 +51,14 @@ params.lonaxisSpan = {
};
// max latitudinal angular span (EXPERIMENTAL)
-params.lataxisSpan = {
+exports.lataxisSpan = {
'conic conformal': 150,
'stereographic': 179.5,
'*': 180
};
// defaults for each scope
-params.scopeDefaults = {
+exports.scopeDefaults = {
world: {
lonaxisRange: [-180, 180],
lataxisRange: [-90, 90],
@@ -75,7 +72,7 @@ params.scopeDefaults = {
},
europe: {
lonaxisRange: [-30, 60],
- lataxisRange: [30, 80],
+ lataxisRange: [30, 85],
projType: 'conic conformal',
projRotate: [15, 0, 0],
projParallels: [0, 60]
@@ -108,42 +105,63 @@ params.scopeDefaults = {
};
// angular pad to avoid rounding error around clip angles
-params.clipPad = 1e-3;
+exports.clipPad = 1e-3;
// map projection precision
-params.precision = 0.1;
+exports.precision = 0.1;
// default land and water fill colors
-params.landColor = '#F0DC82';
-params.waterColor = '#3399FF';
+exports.landColor = '#F0DC82';
+exports.waterColor = '#3399FF';
// locationmode to layer name
-params.locationmodeToLayer = {
+exports.locationmodeToLayer = {
'ISO-3': 'countries',
'USA-states': 'subunits',
'country names': 'countries'
};
// SVG element for a sphere (use to frame maps)
-params.sphereSVG = {type: 'Sphere'};
+exports.sphereSVG = {type: 'Sphere'};
// N.B. base layer names must be the same as in the topojson files
// base layer with a fill color
-params.fillLayers = ['ocean', 'land', 'lakes'];
+exports.fillLayers = {
+ ocean: 1,
+ land: 1,
+ lakes: 1
+};
// base layer with a only a line color
-params.lineLayers = ['subunits', 'countries', 'coastlines', 'rivers', 'frame'];
+exports.lineLayers = {
+ subunits: 1,
+ countries: 1,
+ coastlines: 1,
+ rivers: 1,
+ frame: 1
+};
-// all base layers - in order
-params.baseLayers = [
+exports.layers = [
+ 'bg',
'ocean', 'land', 'lakes',
'subunits', 'countries', 'coastlines', 'rivers',
- 'lataxis', 'lonaxis',
- 'frame'
+ 'lataxis', 'lonaxis', 'frame',
+ 'backplot',
+ 'frontplot'
];
-params.layerNameToAdjective = {
+exports.layersForChoropleth = [
+ 'bg',
+ 'ocean', 'land',
+ 'subunits', 'countries', 'coastlines',
+ 'lataxis', 'lonaxis', 'frame',
+ 'backplot',
+ 'rivers', 'lakes',
+ 'frontplot'
+];
+
+exports.layerNameToAdjective = {
ocean: 'ocean',
land: 'land',
lakes: 'lake',
@@ -153,6 +171,3 @@ params.layerNameToAdjective = {
rivers: 'river',
frame: 'frame'
};
-
-// base layers drawn over choropleth
-params.baseLayersOverChoropleth = ['rivers', 'lakes'];
diff --git a/src/plots/geo/geo.js b/src/plots/geo/geo.js
index 2909a3cc6da..282befb3e92 100644
--- a/src/plots/geo/geo.js
+++ b/src/plots/geo/geo.js
@@ -6,466 +6,683 @@
* LICENSE file in the root directory of this source tree.
*/
-
'use strict';
/* global PlotlyGeoAssets:false */
var d3 = require('d3');
+var Plotly = require('../../plotly');
+var Lib = require('../../lib');
var Color = require('../../components/color');
var Drawing = require('../../components/drawing');
var Fx = require('../../components/fx');
var Plots = require('../plots');
var Axes = require('../cartesian/axes');
+var dragElement = require('../../components/dragelement');
+var prepSelect = require('../cartesian/select');
-var addProjectionsToD3 = require('./projections');
-var createGeoScale = require('./set_scale');
var createGeoZoom = require('./zoom');
-var createGeoZoomReset = require('./zoom_reset');
var constants = require('./constants');
var topojsonUtils = require('../../lib/topojson_utils');
var topojsonFeature = require('topojson-client').feature;
-// add a few projection types to d3.geo
-addProjectionsToD3(d3);
-
+require('./projections')(d3);
-function Geo(options) {
- this.id = options.id;
- this.graphDiv = options.graphDiv;
- this.container = options.container;
- this.topojsonURL = options.topojsonURL;
+function Geo(opts) {
+ this.id = opts.id;
+ this.graphDiv = opts.graphDiv;
+ this.container = opts.container;
+ this.topojsonURL = opts.topojsonURL;
+ this.isStatic = opts.staticPlot;
this.topojsonName = null;
this.topojson = null;
- this.projectionType = null;
this.projection = null;
+ this.viewInitial = null;
+ this.fitScale = null;
+ this.bounds = null;
+ this.midPt = null;
- this.clipAngle = null;
- this.setScale = null;
- this.path = null;
+ this.hasChoropleth = false;
+ this.traceHash = {};
- this.zoom = null;
- this.zoomReset = null;
+ this.layers = {};
+ this.basePaths = {};
+ this.dataPaths = {};
+ this.dataPoints = {};
- this.makeFramework();
+ this.clipDef = null;
+ this.clipRect = null;
+ this.bgRect = null;
- this.traceHash = {};
+ this.makeFramework();
}
-module.exports = Geo;
-
var proto = Geo.prototype;
-proto.plot = function(geoCalcData, fullLayout, promises) {
- var _this = this,
- geoLayout = fullLayout[_this.id],
- graphSize = fullLayout._size;
+module.exports = function createGeo(opts) {
+ return new Geo(opts);
+};
- var topojsonNameNew, topojsonPath;
+proto.plot = function(geoCalcData, fullLayout, promises) {
+ var _this = this;
+ var geoLayout = fullLayout[this.id];
+ var topojsonNameNew = topojsonUtils.getTopojsonName(geoLayout);
- // N.B. 'geoLayout' is unambiguous, no need for 'user' geo layout here
+ if(_this.topojson === null || topojsonNameNew !== _this.topojsonName) {
+ _this.topojsonName = topojsonNameNew;
- // TODO don't reset projection on all graph edits
- _this.projection = null;
+ if(PlotlyGeoAssets.topojson[_this.topojsonName] === undefined) {
+ promises.push(_this.fetchTopojson().then(function(topojson) {
+ PlotlyGeoAssets.topojson[_this.topojsonName] = topojson;
+ _this.topojson = topojson;
+ _this.update(geoCalcData, fullLayout);
+ }));
+ } else {
+ _this.topojson = PlotlyGeoAssets.topojson[_this.topojsonName];
+ _this.update(geoCalcData, fullLayout);
+ }
+ } else {
+ _this.update(geoCalcData, fullLayout);
+ }
+};
- _this.setScale = createGeoScale(geoLayout, graphSize);
- _this.makeProjection(geoLayout);
- _this.makePath();
- _this.adjustLayout(geoLayout, graphSize);
+proto.fetchTopojson = function() {
+ var topojsonPath = topojsonUtils.getTopojsonPath(
+ this.topojsonURL,
+ this.topojsonName
+ );
+ return new Promise(function(resolve, reject) {
+ d3.json(topojsonPath, function(err, topojson) {
+ if(err) {
+ if(err.status === 404) {
+ return reject(new Error([
+ 'plotly.js could not find topojson file at',
+ topojsonPath, '.',
+ 'Make sure the *topojsonURL* plot config option',
+ 'is set properly.'
+ ].join(' ')));
+ } else {
+ return reject(new Error([
+ 'unexpected error while fetching topojson file at',
+ topojsonPath
+ ].join(' ')));
+ }
+ }
+ resolve(topojson);
+ });
+ });
+};
- _this.zoom = createGeoZoom(_this, geoLayout);
- _this.zoomReset = createGeoZoomReset(_this, geoLayout);
- _this.mockAxis = createMockAxis(fullLayout);
+proto.update = function(geoCalcData, fullLayout) {
+ var geoLayout = fullLayout[this.id];
- _this.framework
- .call(_this.zoom)
- .on('dblclick.zoom', _this.zoomReset);
+ var hasInvalidBounds = this.updateProjection(fullLayout, geoLayout);
+ if(hasInvalidBounds) return;
- _this.framework.on('mousemove', function() {
- var mouse = d3.mouse(this),
- lonlat = _this.projection.invert(mouse);
+ // important: maps with choropleth traces have a different layer order
+ this.hasChoropleth = false;
+ for(var i = 0; i < geoCalcData.length; i++) {
+ if(geoCalcData[i][0].trace.type === 'choropleth') {
+ this.hasChoropleth = true;
+ break;
+ }
+ }
- if(!lonlat || isNaN(lonlat[0]) || isNaN(lonlat[1])) return;
+ if(!this.viewInitial) {
+ this.saveViewInitial(geoLayout);
+ }
- var evt = d3.event;
- evt.xpx = mouse[0];
- evt.ypx = mouse[1];
+ this.updateBaseLayers(fullLayout, geoLayout);
+ this.updateDims(fullLayout, geoLayout);
+ this.updateFx(fullLayout, geoLayout);
- _this.xaxis.c2p = function() { return mouse[0]; };
- _this.xaxis.p2c = function() { return lonlat[0]; };
- _this.yaxis.c2p = function() { return mouse[1]; };
- _this.yaxis.p2c = function() { return lonlat[1]; };
+ Plots.generalUpdatePerTraceModule(this, geoCalcData, geoLayout);
- Fx.hover(_this.graphDiv, evt, _this.id);
- });
+ var scatterLayer = this.layers.frontplot.select('.scatterlayer');
+ this.dataPoints.point = scatterLayer.selectAll('.point');
+ this.dataPoints.text = scatterLayer.selectAll('text');
+ this.dataPaths.line = scatterLayer.selectAll('.js-line');
- _this.framework.on('mouseout', function() {
- Fx.loneUnhover(fullLayout._toppaper);
- });
+ var choroplethLayer = this.layers.backplot.select('.choroplethlayer');
+ this.dataPaths.choropleth = choroplethLayer.selectAll('path');
- _this.framework.on('click', function() {
- Fx.click(_this.graphDiv, d3.event);
- });
+ this.render();
+};
- topojsonNameNew = topojsonUtils.getTopojsonName(geoLayout);
+proto.updateProjection = function(fullLayout, geoLayout) {
+ var gs = fullLayout._size;
+ var domain = geoLayout.domain;
+ var projLayout = geoLayout.projection;
+ var rotation = projLayout.rotation || {};
+ var center = geoLayout.center || {};
- if(_this.topojson === null || topojsonNameNew !== _this.topojsonName) {
- _this.topojsonName = topojsonNameNew;
+ var projection = this.projection = getProjection(geoLayout);
- if(PlotlyGeoAssets.topojson[_this.topojsonName] !== undefined) {
- _this.topojson = PlotlyGeoAssets.topojson[_this.topojsonName];
- _this.onceTopojsonIsLoaded(geoCalcData, geoLayout);
- }
- else {
- topojsonPath = topojsonUtils.getTopojsonPath(
- _this.topojsonURL,
- _this.topojsonName
- );
-
- promises.push(new Promise(function(resolve, reject) {
- d3.json(topojsonPath, function(error, topojson) {
- if(error) {
- if(error.status === 404) {
- reject(new Error([
- 'plotly.js could not find topojson file at',
- topojsonPath, '.',
- 'Make sure the *topojsonURL* plot config option',
- 'is set properly.'
- ].join(' ')));
- }
- else {
- reject(new Error([
- 'unexpected error while fetching topojson file at',
- topojsonPath
- ].join(' ')));
- }
- return;
- }
-
- _this.topojson = topojson;
- PlotlyGeoAssets.topojson[_this.topojsonName] = topojson;
-
- _this.onceTopojsonIsLoaded(geoCalcData, geoLayout);
- resolve();
- });
- }));
+ // set 'pre-fit' projection
+ projection
+ .center([center.lon - rotation.lon, center.lat - rotation.lat])
+ .rotate([-rotation.lon, -rotation.lat, rotation.roll])
+ .parallels(projLayout.parallels);
+
+ // setup subplot extent [[x0,y0], [x1,y1]]
+ var extent = [[
+ gs.l + gs.w * domain.x[0],
+ gs.t + gs.h * (1 - domain.y[1])
+ ], [
+ gs.l + gs.w * domain.x[1],
+ gs.t + gs.h * (1 - domain.y[0])
+ ]];
+
+ var lonaxis = geoLayout.lonaxis;
+ var lataxis = geoLayout.lataxis;
+ var rangeBox = makeRangeBox(lonaxis.range, lataxis.range);
+
+ // fit projection 'scale' and 'translate' to set lon/lat ranges
+ projection.fitExtent(extent, rangeBox);
+
+ var b = this.bounds = projection.getBounds(rangeBox);
+ var s = this.fitScale = projection.scale();
+ var t = projection.translate();
+
+ if(
+ !isFinite(b[0][0]) || !isFinite(b[0][1]) ||
+ !isFinite(b[1][0]) || !isFinite(b[1][1]) ||
+ isNaN(t[0]) || isNaN(t[0])
+ ) {
+ var gd = this.graphDiv;
+ var attrToUnset = ['projection.rotation', 'center', 'lonaxis.range', 'lataxis.range'];
+ var msg = 'Invalid geo settings, relayout\'ing to default view.';
+ var updateObj = {};
+
+ // clear all attribute that could cause invalid bounds,
+ // clear viewInitial to update reset-view behavior
+
+ for(var i = 0; i < attrToUnset.length; i++) {
+ updateObj[this.id + '.' + attrToUnset[i]] = null;
}
- }
- else _this.onceTopojsonIsLoaded(geoCalcData, geoLayout);
- // TODO handle topojson-is-loading case
- // to avoid making multiple request while streaming
-};
+ this.viewInitial = null;
-proto.onceTopojsonIsLoaded = function(geoCalcData, geoLayout) {
- this.drawLayout(geoLayout);
+ Lib.warn(msg);
+ gd._promises.push(Plotly.relayout(gd, updateObj));
+ return msg;
+ }
- Plots.generalUpdatePerTraceModule(this, geoCalcData, geoLayout);
+ // px coordinates of view mid-point,
+ // useful to update `geo.center` after interactions
+ var midPt = this.midPt = [
+ (b[0][0] + b[1][0]) / 2,
+ (b[0][1] + b[1][1]) / 2
+ ];
- this.render();
+ // adjust projection to user setting
+ projection
+ .scale(projLayout.scale * s)
+ .translate([t[0] + (midPt[0] - t[0]), t[1] + (midPt[1] - t[1])])
+ .clipExtent(b);
+
+ // the 'albers usa' projection does not expose a 'center' method
+ // so here's this hack to make it respond to 'geoLayout.center'
+ if(geoLayout._isAlbersUsa) {
+ var centerPx = projection([center.lon, center.lat]);
+ var tt = projection.translate();
+
+ projection.translate([
+ tt[0] - (centerPx[0] - tt[0]),
+ tt[1] - (centerPx[1] - tt[1])
+ ]);
+ }
};
-proto.makeProjection = function(geoLayout) {
- var projLayout = geoLayout.projection,
- projType = projLayout.type,
- isNew = this.projection === null || projType !== this.projectionType,
- projection;
+proto.updateBaseLayers = function(fullLayout, geoLayout) {
+ var _this = this;
+ var topojson = _this.topojson;
+ var layers = _this.layers;
+ var basePaths = _this.basePaths;
- if(isNew) {
- this.projectionType = projType;
- projection = this.projection = d3.geo[constants.projNames[projType]]();
+ function isAxisLayer(d) {
+ return (d === 'lonaxis' || d === 'lataxis');
}
- else projection = this.projection;
- projection
- .translate(projLayout._translate0)
- .precision(constants.precision);
-
- if(!geoLayout._isAlbersUsa) {
- projection
- .rotate(projLayout._rotate)
- .center(projLayout._center);
+ function isLineLayer(d) {
+ return Boolean(constants.lineLayers[d]);
}
- if(geoLayout._clipAngle) {
- this.clipAngle = geoLayout._clipAngle; // needed in proto.render
- projection
- .clipAngle(geoLayout._clipAngle - constants.clipPad);
+ function isFillLayer(d) {
+ return Boolean(constants.fillLayers[d]);
}
- else this.clipAngle = null; // for graph edits
- if(projLayout.parallels) {
- projection
- .parallels(projLayout.parallels);
- }
-
- if(isNew) this.setScale(projection);
-
- projection
- .translate(projLayout._translate)
- .scale(projLayout._scale);
-};
+ var allLayers = this.hasChoropleth ?
+ constants.layersForChoropleth :
+ constants.layers;
-proto.makePath = function() {
- this.path = d3.geo.path().projection(this.projection);
-};
+ var layerData = allLayers.filter(function(d) {
+ return (isLineLayer(d) || isFillLayer(d)) ? geoLayout['show' + d] :
+ isAxisLayer(d) ? geoLayout[d].showgrid :
+ true;
+ });
-proto.makeFramework = function() {
- var fullLayout = this.graphDiv._fullLayout;
- var clipId = 'clip' + fullLayout._uid + this.id;
+ var join = _this.framework.selectAll('.layer')
+ .data(layerData, String);
- var defGroup = fullLayout._defs.selectAll('g.clips')
- .data([0]);
- defGroup.enter().append('g')
- .classed('clips', true);
+ join.exit().each(function(d) {
+ delete layers[d];
+ delete basePaths[d];
+ d3.select(this).remove();
+ });
- var clipDef = this.clipDef = defGroup.selectAll('#' + clipId)
- .data([0]);
+ join.enter().append('g')
+ .attr('class', function(d) { return 'layer ' + d; })
+ .each(function(d) {
+ var layer = layers[d] = d3.select(this);
+
+ if(d === 'bg') {
+ _this.bgRect = layer.append('rect')
+ .style('pointer-events', 'all');
+ } else if(isAxisLayer(d)) {
+ basePaths[d] = layer.append('path')
+ .style('fill', 'none');
+ } else if(d === 'backplot') {
+ layer.append('g')
+ .classed('choroplethlayer', true);
+ } else if(d === 'frontplot') {
+ layer.append('g')
+ .classed('scatterlayer', true);
+ } else if(isLineLayer(d)) {
+ basePaths[d] = layer.append('path')
+ .style('fill', 'none')
+ .style('stroke-miterlimit', 2);
+ } else if(isFillLayer(d)) {
+ basePaths[d] = layer.append('path')
+ .style('stroke', 'none');
+ }
+ });
+
+ join.order();
+
+ join.each(function(d) {
+ var path = basePaths[d];
+ var adj = constants.layerNameToAdjective[d];
+
+ if(d === 'frame') {
+ path.datum(constants.sphereSVG);
+ } else if(isLineLayer(d) || isFillLayer(d)) {
+ path.datum(topojsonFeature(topojson, topojson.objects[d]));
+ } else if(isAxisLayer(d)) {
+ path.datum(makeGraticule(d, geoLayout))
+ .call(Color.stroke, geoLayout[d].gridcolor)
+ .call(Drawing.dashLine, '', geoLayout[d].gridwidth);
+ }
- clipDef.enter().append('clipPath').attr('id', clipId)
- .append('rect');
+ if(isLineLayer(d)) {
+ path.call(Color.stroke, geoLayout[adj + 'color'])
+ .call(Drawing.dashLine, '', geoLayout[adj + 'width']);
+ } else if(isFillLayer(d)) {
+ path.call(Color.fill, geoLayout[adj + 'color']);
+ }
+ });
+};
- var framework = this.framework = d3.select(this.container).append('g');
+proto.updateDims = function(fullLayout, geoLayout) {
+ var b = this.bounds;
+ var hFrameWidth = (geoLayout.framewidth || 0) / 2;
- framework
- .attr('class', 'geo ' + this.id)
- .style('pointer-events', 'all')
- .call(Drawing.setClipUrl, clipId);
+ var l = b[0][0] - hFrameWidth;
+ var t = b[0][1] - hFrameWidth;
+ var w = b[1][0] - l + hFrameWidth;
+ var h = b[1][1] - t + hFrameWidth;
- framework.append('g')
- .attr('class', 'bglayer')
- .append('rect');
+ Drawing.setRect(this.clipRect, l, t, w, h);
- framework.append('g').attr('class', 'baselayer');
- framework.append('g').attr('class', 'choroplethlayer');
- framework.append('g').attr('class', 'baselayeroverchoropleth');
- framework.append('g').attr('class', 'scattergeolayer');
+ this.bgRect
+ .call(Drawing.setRect, l, t, w, h)
+ .call(Color.fill, geoLayout.bgcolor);
- // N.B. disable dblclick zoom default
- framework.on('dblclick.zoom', null);
+ this.xaxis._offset = l;
+ this.xaxis._length = w;
- this.xaxis = { _id: 'x' };
- this.yaxis = { _id: 'y' };
+ this.yaxis._offset = t;
+ this.yaxis._length = h;
};
-proto.adjustLayout = function(geoLayout, graphSize) {
- var domain = geoLayout.domain;
+proto.updateFx = function(fullLayout, geoLayout) {
+ var _this = this;
+ var gd = _this.graphDiv;
+ var bgRect = _this.bgRect;
+ var dragMode = fullLayout.dragmode;
- var left = graphSize.l + graphSize.w * domain.x[0] + geoLayout._marginX,
- top = graphSize.t + graphSize.h * (1 - domain.y[1]) + geoLayout._marginY;
+ if(_this.isStatic) return;
- Drawing.setTranslate(this.framework, left, top);
+ function zoomReset() {
+ var viewInitial = _this.viewInitial;
+ var updateObj = {};
- var dimsAttrs = {
- x: 0,
- y: 0,
- width: geoLayout._width,
- height: geoLayout._height
- };
+ for(var k in viewInitial) {
+ updateObj[_this.id + '.' + k] = viewInitial[k];
+ }
- this.clipDef.select('rect')
- .attr(dimsAttrs);
+ Plotly.relayout(gd, updateObj);
+ gd.emit('plotly_doubleclick', null);
+ }
- this.framework.select('.bglayer').select('rect')
- .attr(dimsAttrs)
- .call(Color.fill, geoLayout.bgcolor);
+ function invert(lonlat) {
+ return _this.projection.invert([
+ lonlat[0] + _this.xaxis._offset,
+ lonlat[1] + _this.yaxis._offset
+ ]);
+ }
- this.xaxis._offset = left;
- this.xaxis._length = geoLayout._width;
+ if(dragMode === 'pan') {
+ bgRect.node().onmousedown = null;
+ bgRect.call(createGeoZoom(_this, geoLayout));
+ bgRect.on('dblclick.zoom', zoomReset);
+ }
+ else if(dragMode === 'select' || dragMode === 'lasso') {
+ bgRect.on('.zoom', null);
+
+ var fillRangeItems;
+
+ if(dragMode === 'select') {
+ fillRangeItems = function(eventData, poly) {
+ var ranges = eventData.range = {};
+ ranges[_this.id] = [
+ invert([poly.xmin, poly.ymin]),
+ invert([poly.xmax, poly.ymax])
+ ];
+ };
+ } else if(dragMode === 'lasso') {
+ fillRangeItems = function(eventData, poly, pts) {
+ var dataPts = eventData.lassoPoints = {};
+ dataPts[_this.id] = pts.filtered.map(invert);
+ };
+ }
- this.yaxis._offset = top;
- this.yaxis._length = geoLayout._height;
-};
+ var dragOptions = {
+ element: _this.bgRect.node(),
+ gd: gd,
+ plotinfo: {
+ xaxis: _this.xaxis,
+ yaxis: _this.yaxis,
+ fillRangeItems: fillRangeItems
+ },
+ xaxes: [_this.xaxis],
+ yaxes: [_this.yaxis],
+ subplot: _this.id
+ };
+
+ dragOptions.prepFn = function(e, startX, startY) {
+ prepSelect(e, startX, startY, dragOptions, dragMode);
+ };
+
+ dragOptions.doneFn = function(dragged, numClicks) {
+ if(numClicks === 2) {
+ fullLayout._zoomlayer.selectAll('.select-outline').remove();
+ }
+ };
+
+ dragElement.init(dragOptions);
+ }
-proto.drawTopo = function(selection, layerName, geoLayout) {
- if(geoLayout['show' + layerName] !== true) return;
+ bgRect.on('mousemove', function() {
+ var lonlat = _this.projection.invert(d3.mouse(this));
- var topojson = this.topojson,
- datum = layerName === 'frame' ?
- constants.sphereSVG :
- topojsonFeature(topojson, topojson.objects[layerName]);
+ if(!lonlat || isNaN(lonlat[0]) || isNaN(lonlat[1])) {
+ return dragElement.unhover(gd, d3.event);
+ }
- selection.append('g')
- .datum(datum)
- .attr('class', layerName)
- .append('path')
- .attr('class', 'basepath');
-};
+ _this.xaxis.p2c = function() { return lonlat[0]; };
+ _this.yaxis.p2c = function() { return lonlat[1]; };
-function makeGraticule(lonaxisRange, lataxisRange, step) {
- return d3.geo.graticule()
- .extent([
- [lonaxisRange[0], lataxisRange[0]],
- [lonaxisRange[1], lataxisRange[1]]
- ])
- .step(step);
-}
+ Fx.hover(gd, d3.event, _this.id);
+ });
-proto.drawGraticule = function(selection, axisName, geoLayout) {
- var axisLayout = geoLayout[axisName];
+ bgRect.on('mouseout', function() {
+ dragElement.unhover(gd, d3.event);
+ });
- if(axisLayout.showgrid !== true) return;
-
- var scopeDefaults = constants.scopeDefaults[geoLayout.scope],
- lonaxisRange = scopeDefaults.lonaxisRange,
- lataxisRange = scopeDefaults.lataxisRange,
- step = axisName === 'lonaxis' ?
- [axisLayout.dtick] :
- [0, axisLayout.dtick],
- graticule = makeGraticule(lonaxisRange, lataxisRange, step);
-
- selection.append('g')
- .datum(graticule)
- .attr('class', axisName + 'graticule')
- .append('path')
- .attr('class', 'graticulepath');
+ bgRect.on('click', function() {
+ Fx.click(gd, d3.event);
+ });
};
-proto.drawLayout = function(geoLayout) {
- var gBaseLayer = this.framework.select('g.baselayer'),
- baseLayers = constants.baseLayers,
- axesNames = constants.axesNames,
- layerName;
+proto.makeFramework = function() {
+ var _this = this;
+ var fullLayout = _this.graphDiv._fullLayout;
+ var clipId = 'clip' + fullLayout._uid + _this.id;
- // TODO move to more d3-idiomatic pattern (that's work on replot)
- // N.B. html('') does not work in IE11
- gBaseLayer.selectAll('*').remove();
+ _this.clipDef = fullLayout._clips.append('clipPath')
+ .attr('id', clipId);
- for(var i = 0; i < baseLayers.length; i++) {
- layerName = baseLayers[i];
+ _this.clipRect = _this.clipDef.append('rect');
- if(axesNames.indexOf(layerName) !== -1) {
- this.drawGraticule(gBaseLayer, layerName, geoLayout);
- }
- else this.drawTopo(gBaseLayer, layerName, geoLayout);
- }
+ _this.framework = d3.select(_this.container).append('g')
+ .attr('class', 'geo ' + _this.id)
+ .call(Drawing.setClipUrl, clipId);
- this.styleLayout(geoLayout);
-};
+ // sane lonlat to px
+ _this.project = function(v) {
+ var px = _this.projection(v);
+ return px ?
+ [px[0] - _this.xaxis._offset, px[1] - _this.yaxis._offset] :
+ [null, null];
+ };
-function styleFillLayer(selection, layerName, geoLayout) {
- var layerAdj = constants.layerNameToAdjective[layerName];
+ _this.xaxis = {
+ _id: 'x',
+ c2p: function(v) { return _this.project(v)[0]; }
+ };
- selection.select('.' + layerName)
- .selectAll('path')
- .attr('stroke', 'none')
- .call(Color.fill, geoLayout[layerAdj + 'color']);
-}
+ _this.yaxis = {
+ _id: 'y',
+ c2p: function(v) { return _this.project(v)[1]; }
+ };
-function styleLineLayer(selection, layerName, geoLayout) {
- var layerAdj = constants.layerNameToAdjective[layerName];
+ // mock axis for hover formatting
+ _this.mockAxis = {
+ type: 'linear',
+ showexponent: 'all',
+ exponentformat: 'B'
+ };
+ Axes.setConvert(_this.mockAxis, fullLayout);
+};
- selection.select('.' + layerName)
- .selectAll('path')
- .attr('fill', 'none')
- .call(Color.stroke, geoLayout[layerAdj + 'color'])
- .call(Drawing.dashLine, '', geoLayout[layerAdj + 'width']);
-}
+proto.saveViewInitial = function(geoLayout) {
+ var center = geoLayout.center || {};
+ var projLayout = geoLayout.projection;
+ var rotation = projLayout.rotation || {};
+
+ if(geoLayout._isScoped) {
+ this.viewInitial = {
+ 'center.lon': center.lon,
+ 'center.lat': center.lat,
+ 'projection.scale': projLayout.scale
+ };
+ } else if(geoLayout._isClipped) {
+ this.viewInitial = {
+ 'projection.scale': projLayout.scale,
+ 'projection.rotation.lon': rotation.lon,
+ 'projection.rotation.lat': rotation.lat
+ };
+ } else {
+ this.viewInitial = {
+ 'center.lon': center.lon,
+ 'center.lat': center.lat,
+ 'projection.scale': projLayout.scale,
+ 'projection.rotation.lon': rotation.lon
+ };
+ }
+};
-function styleGraticule(selection, axisName, geoLayout) {
- selection.select('.' + axisName + 'graticule')
- .selectAll('path')
- .attr('fill', 'none')
- .call(Color.stroke, geoLayout[axisName].gridcolor)
- .call(Drawing.dashLine, '', geoLayout[axisName].gridwidth);
-}
+// [hot code path] (re)draw all paths which depend on the projection
+proto.render = function() {
+ var projection = this.projection;
+ var pathFn = projection.getPath();
+ var k;
-proto.styleLayer = function(selection, layerName, geoLayout) {
- var fillLayers = constants.fillLayers,
- lineLayers = constants.lineLayers;
+ function translatePoints(d) {
+ var lonlatPx = projection(d.lonlat);
+ return lonlatPx ?
+ 'translate(' + lonlatPx[0] + ',' + lonlatPx[1] + ')' :
+ null;
+ }
- if(fillLayers.indexOf(layerName) !== -1) {
- styleFillLayer(selection, layerName, geoLayout);
+ function hideShowPoints(d) {
+ return projection.isLonLatOverEdges(d.lonlat) ? 'none' : null;
}
- else if(lineLayers.indexOf(layerName) !== -1) {
- styleLineLayer(selection, layerName, geoLayout);
+
+ for(k in this.basePaths) {
+ this.basePaths[k].attr('d', pathFn);
}
-};
-proto.styleLayout = function(geoLayout) {
- var gBaseLayer = this.framework.select('g.baselayer'),
- baseLayers = constants.baseLayers,
- axesNames = constants.axesNames,
- layerName;
+ for(k in this.dataPaths) {
+ this.dataPaths[k].attr('d', function(d) { return pathFn(d.geojson); });
+ }
- for(var i = 0; i < baseLayers.length; i++) {
- layerName = baseLayers[i];
+ for(k in this.dataPoints) {
+ this.dataPoints[k]
+ .attr('display', hideShowPoints)
+ .attr('transform', translatePoints);
+ }
+};
- if(axesNames.indexOf(layerName) !== -1) {
- styleGraticule(gBaseLayer, layerName, geoLayout);
+// Helper that wraps d3.geo[/* projection name /*]() which:
+//
+// - adds 'fitExtent' (available in d3 v4)
+// - adds 'getPath', 'getBounds' convenience methods
+// - scopes logic related to 'clipAngle'
+// - adds 'isLonLatOverEdges' method
+// - sets projection precision
+// - sets methods that aren't always defined depending
+// on the projection type to a dummy 'd3-esque' function,
+//
+// This wrapper alleviates subsequent code of (many) annoying if-statements.
+function getProjection(geoLayout) {
+ var projLayout = geoLayout.projection;
+ var projType = projLayout.type;
+
+ var projection = d3.geo[constants.projNames[projType]]();
+
+ var clipAngle = geoLayout._isClipped ?
+ constants.lonaxisSpan[projType] / 2 :
+ null;
+
+ var methods = ['center', 'rotate', 'parallels', 'clipExtent'];
+ var dummyFn = function(_) { return _ ? projection : []; };
+
+ for(var i = 0; i < methods.length; i++) {
+ var m = methods[i];
+ if(typeof projection[m] !== 'function') {
+ projection[m] = dummyFn;
}
- else this.styleLayer(gBaseLayer, layerName, geoLayout);
}
-};
-proto.isLonLatOverEdges = function(lonlat) {
- var clipAngle = this.clipAngle;
+ projection.isLonLatOverEdges = function(lonlat) {
+ if(projection(lonlat) === null) {
+ return true;
+ }
- if(clipAngle === null) return false;
+ if(clipAngle) {
+ var r = projection.rotate();
+ var angle = d3.geo.distance(lonlat, [-r[0], -r[1]]);
+ var maxAngle = clipAngle * Math.PI / 180;
+ return angle > maxAngle;
+ } else {
+ return false;
+ }
+ };
- var p = this.projection.rotate(),
- angle = d3.geo.distance(lonlat, [-p[0], -p[1]]),
- maxAngle = clipAngle * Math.PI / 180;
+ projection.getPath = function() {
+ return d3.geo.path().projection(projection);
+ };
- return angle > maxAngle;
-};
+ projection.getBounds = function(object) {
+ return projection.getPath().bounds(object);
+ };
-// [hot code path] (re)draw all paths which depend on the projection
-proto.render = function() {
- var _this = this,
- framework = _this.framework,
- gChoropleth = framework.select('g.choroplethlayer'),
- gScatterGeo = framework.select('g.scattergeolayer'),
- path = _this.path;
+ // adapted from d3 v4:
+ // https://github.com/d3/d3-geo/blob/master/src/projection/fit.js
+ projection.fitExtent = function(extent, object) {
+ var w = extent[1][0] - extent[0][0];
+ var h = extent[1][1] - extent[0][1];
+ var clip = projection.clipExtent && projection.clipExtent();
- function translatePoints(d) {
- var lonlatPx = _this.projection(d.lonlat);
- if(!lonlatPx) return null;
+ projection
+ .scale(150)
+ .translate([0, 0]);
- return 'translate(' + lonlatPx[0] + ',' + lonlatPx[1] + ')';
- }
+ if(clip) projection.clipExtent(null);
- // hide paths over edges of clipped projections
- function hideShowPoints(d) {
- return _this.isLonLatOverEdges(d.lonlat) ? '0' : '1.0';
- }
+ var b = projection.getBounds(object);
+ var k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1]));
+ var x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2;
+ var y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
- framework.selectAll('path.basepath').attr('d', path);
- framework.selectAll('path.graticulepath').attr('d', path);
+ if(clip) projection.clipExtent(clip);
- gChoropleth.selectAll('path.choroplethlocation').attr('d', path);
- gChoropleth.selectAll('path.basepath').attr('d', path);
+ return projection
+ .scale(k * 150)
+ .translate([x, y]);
+ };
- gScatterGeo.selectAll('path.js-line').attr('d', path);
+ projection.precision(constants.precision);
- if(_this.clipAngle !== null) {
- gScatterGeo.selectAll('path.point')
- .style('opacity', hideShowPoints)
- .attr('transform', translatePoints);
- gScatterGeo.selectAll('text')
- .style('opacity', hideShowPoints)
- .attr('transform', translatePoints);
+ if(clipAngle) {
+ projection.clipAngle(clipAngle - constants.clipPad);
}
- else {
- gScatterGeo.selectAll('path.point')
- .attr('transform', translatePoints);
- gScatterGeo.selectAll('text')
- .attr('transform', translatePoints);
- }
-};
-// create a mock axis used to format hover text
-function createMockAxis(fullLayout) {
- var mockAxis = {
- type: 'linear',
- showexponent: 'all',
- exponentformat: Axes.layoutAttributes.exponentformat.dflt
- };
+ return projection;
+}
+
+function makeGraticule(axisName, geoLayout) {
+ var axisLayout = geoLayout[axisName];
+ var dtick = axisLayout.dtick;
+ var scopeDefaults = constants.scopeDefaults[geoLayout.scope];
+ var lonaxisRange = scopeDefaults.lonaxisRange;
+ var lataxisRange = scopeDefaults.lataxisRange;
+ var step = axisName === 'lonaxis' ? [dtick] : [0, dtick];
- Axes.setConvert(mockAxis, fullLayout);
- return mockAxis;
+ return d3.geo.graticule()
+ .extent([
+ [lonaxisRange[0], lataxisRange[0]],
+ [lonaxisRange[1], lataxisRange[1]]
+ ])
+ .step(step);
+}
+
+// Returns polygon GeoJSON corresponding to lon/lat range box
+// with well-defined direction
+//
+// Note that clipPad padding is added around range to avoid aliasing.
+function makeRangeBox(lon, lat) {
+ var clipPad = constants.clipPad;
+ var lon0 = lon[0] + clipPad;
+ var lon1 = lon[1] - clipPad;
+ var lat0 = lat[0] + clipPad;
+ var lat1 = lat[1] - clipPad;
+
+ // to cross antimeridian w/o ambiguity
+ if(lon0 > 0 && lon1 < 0) lon1 += 360;
+
+ var dlon4 = (lon1 - lon0) / 4;
+
+ return {
+ type: 'Polygon',
+ coordinates: [[
+ [lon0, lat0],
+ [lon0, lat1],
+ [lon0 + dlon4, lat1],
+ [lon0 + 2 * dlon4, lat1],
+ [lon0 + 3 * dlon4, lat1],
+ [lon1, lat1],
+ [lon1, lat0],
+ [lon1 - dlon4, lat0],
+ [lon1 - 2 * dlon4, lat0],
+ [lon1 - 3 * dlon4, lat0],
+ [lon0, lat0]
+ ]]
+ };
}
diff --git a/src/plots/geo/index.js b/src/plots/geo/index.js
index c56651f8a6e..076411c0d82 100644
--- a/src/plots/geo/index.js
+++ b/src/plots/geo/index.js
@@ -9,14 +9,12 @@
'use strict';
-var Geo = require('./geo');
-
+var createGeo = require('./geo');
var Plots = require('../../plots/plots');
var counterRegex = require('../../lib').counterRegex;
var GEO = 'geo';
-
exports.name = GEO;
exports.attr = GEO;
@@ -32,29 +30,31 @@ exports.layoutAttributes = require('./layout/layout_attributes');
exports.supplyLayoutDefaults = require('./layout/defaults');
exports.plot = function plotGeo(gd) {
- var fullLayout = gd._fullLayout,
- calcData = gd.calcdata,
- geoIds = Plots.getSubplotIds(fullLayout, GEO);
+ var fullLayout = gd._fullLayout;
+ var calcData = gd.calcdata;
+ var geoIds = Plots.getSubplotIds(fullLayout, GEO);
/**
* If 'plotly-geo-assets.js' is not included,
* initialize object to keep reference to every loaded topojson
*/
if(window.PlotlyGeoAssets === undefined) {
- window.PlotlyGeoAssets = { topojson: {} };
+ window.PlotlyGeoAssets = {topojson: {}};
}
for(var i = 0; i < geoIds.length; i++) {
- var geoId = geoIds[i],
- geoCalcData = Plots.getSubplotCalcData(calcData, GEO, geoId),
- geo = fullLayout[geoId]._subplot;
+ var geoId = geoIds[i];
+ var geoCalcData = Plots.getSubplotCalcData(calcData, GEO, geoId);
+ var geoLayout = fullLayout[geoId];
+ var geo = geoLayout._subplot;
if(!geo) {
- geo = new Geo({
+ geo = createGeo({
id: geoId,
graphDiv: gd,
container: fullLayout._geolayer.node(),
- topojsonURL: gd._context.topojsonURL
+ topojsonURL: gd._context.topojsonURL,
+ staticPlot: gd._context.staticPlot
});
fullLayout[geoId]._subplot = geo;
@@ -77,3 +77,13 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
}
}
};
+
+exports.updateFx = function(fullLayout) {
+ var subplotIds = Plots.getSubplotIds(fullLayout, GEO);
+
+ for(var i = 0; i < subplotIds.length; i++) {
+ var subplotLayout = fullLayout[subplotIds[i]];
+ var subplotObj = subplotLayout._subplot;
+ subplotObj.updateFx(fullLayout, subplotLayout);
+ }
+};
diff --git a/src/plots/geo/layout/axis_attributes.js b/src/plots/geo/layout/axis_attributes.js
deleted file mode 100644
index a03ea496f05..00000000000
--- a/src/plots/geo/layout/axis_attributes.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
-* Copyright 2012-2017, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
-
-'use strict';
-
-var colorAttrs = require('../../../components/color/attributes');
-
-
-module.exports = {
- range: {
- valType: 'info_array',
- role: 'info',
- items: [
- {valType: 'number'},
- {valType: 'number'}
- ],
- description: 'Sets the range of this axis (in degrees).'
- },
- showgrid: {
- valType: 'boolean',
- role: 'info',
- dflt: false,
- description: 'Sets whether or not graticule are shown on the map.'
- },
- tick0: {
- valType: 'number',
- role: 'info',
- description: [
- 'Sets the graticule\'s starting tick longitude/latitude.'
- ].join(' ')
- },
- dtick: {
- valType: 'number',
- role: 'info',
- description: [
- 'Sets the graticule\'s longitude/latitude tick step.'
- ].join(' ')
- },
- gridcolor: {
- valType: 'color',
- role: 'style',
- dflt: colorAttrs.lightLine,
- description: [
- 'Sets the graticule\'s stroke color.'
- ].join(' ')
- },
- gridwidth: {
- valType: 'number',
- role: 'style',
- min: 0,
- dflt: 1,
- description: [
- 'Sets the graticule\'s stroke width (in px).'
- ].join(' ')
- }
-};
diff --git a/src/plots/geo/layout/axis_defaults.js b/src/plots/geo/layout/axis_defaults.js
deleted file mode 100644
index f3ccf86f885..00000000000
--- a/src/plots/geo/layout/axis_defaults.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
-* Copyright 2012-2017, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
-
-
-'use strict';
-
-var Lib = require('../../../lib');
-var constants = require('../constants');
-var axisAttributes = require('./axis_attributes');
-
-
-module.exports = function supplyGeoAxisLayoutDefaults(geoLayoutIn, geoLayoutOut) {
- var axesNames = constants.axesNames;
-
- var axisIn, axisOut;
-
- function coerce(attr, dflt) {
- return Lib.coerce(axisIn, axisOut, axisAttributes, attr, dflt);
- }
-
- function getRangeDflt(axisName) {
- var scope = geoLayoutOut.scope;
-
- var projLayout, projType, projRotation, rotateAngle, dfltSpans, halfSpan;
-
- if(scope === 'world') {
- projLayout = geoLayoutOut.projection;
- projType = projLayout.type;
- projRotation = projLayout.rotation;
- dfltSpans = constants[axisName + 'Span'];
-
- halfSpan = dfltSpans[projType] !== undefined ?
- dfltSpans[projType] / 2 :
- dfltSpans['*'] / 2;
- rotateAngle = axisName === 'lonaxis' ?
- projRotation.lon :
- projRotation.lat;
-
- return [rotateAngle - halfSpan, rotateAngle + halfSpan];
- }
- else return constants.scopeDefaults[scope][axisName + 'Range'];
- }
-
- for(var i = 0; i < axesNames.length; i++) {
- var axisName = axesNames[i];
- axisIn = geoLayoutIn[axisName] || {};
- axisOut = {};
-
- var rangeDflt = getRangeDflt(axisName);
-
- var range = coerce('range', rangeDflt);
-
- Lib.noneOrAll(axisIn.range, axisOut.range, [0, 1]);
-
- coerce('tick0', range[0]);
- coerce('dtick', axisName === 'lonaxis' ? 30 : 10);
-
- var show = coerce('showgrid');
- if(show) {
- coerce('gridcolor');
- coerce('gridwidth');
- }
-
- geoLayoutOut[axisName] = axisOut;
- geoLayoutOut[axisName]._fullRange = rangeDflt;
- }
-};
diff --git a/src/plots/geo/layout/defaults.js b/src/plots/geo/layout/defaults.js
index 8fa7af85365..dd4eb737e02 100644
--- a/src/plots/geo/layout/defaults.js
+++ b/src/plots/geo/layout/defaults.js
@@ -12,8 +12,8 @@
var handleSubplotDefaults = require('../../subplot_defaults');
var constants = require('../constants');
var layoutAttributes = require('./layout_attributes');
-var supplyGeoAxisLayoutDefaults = require('./axis_defaults');
+var axesNames = constants.axesNames;
module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
handleSubplotDefaults(layoutIn, layoutOut, fullData, {
@@ -27,24 +27,64 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce) {
var show;
+ var resolution = coerce('resolution');
var scope = coerce('scope');
- var isScoped = (scope !== 'world');
var scopeParams = constants.scopeDefaults[scope];
- var resolution = coerce('resolution');
-
var projType = coerce('projection.type', scopeParams.projType);
- var isAlbersUsa = projType === 'albers usa';
- var isConic = projType.indexOf('conic') !== -1;
+ var isAlbersUsa = geoLayoutOut._isAlbersUsa = projType === 'albers usa';
+
+ // no other scopes are allowed for 'albers usa' projection
+ if(isAlbersUsa) scope = geoLayoutOut.scope = 'usa';
+
+ var isScoped = geoLayoutOut._isScoped = (scope !== 'world');
+ var isConic = geoLayoutOut._isConic = projType.indexOf('conic') !== -1;
+ geoLayoutOut._isClipped = !!constants.lonaxisSpan[projType];
+
+ for(var i = 0; i < axesNames.length; i++) {
+ var axisName = axesNames[i];
+ var dtickDflt = [30, 10][i];
+ var rangeDflt;
+
+ if(isScoped) {
+ rangeDflt = scopeParams[axisName + 'Range'];
+ } else {
+ var dfltSpans = constants[axisName + 'Span'];
+ var hSpan = (dfltSpans[projType] || dfltSpans['*']) / 2;
+ var rot = coerce(
+ 'projection.rotation.' + axisName.substr(0, 3),
+ scopeParams.projRotate[i]
+ );
+ rangeDflt = [rot - hSpan, rot + hSpan];
+ }
- if(isConic) {
- var dfltProjParallels = scopeParams.projParallels || [0, 60];
- coerce('projection.parallels', dfltProjParallels);
+ var range = coerce(axisName + '.range', rangeDflt);
+
+ coerce(axisName + '.tick0', range[0]);
+ coerce(axisName + '.dtick', dtickDflt);
+
+ show = coerce(axisName + '.showgrid');
+ if(show) {
+ coerce(axisName + '.gridcolor');
+ coerce(axisName + '.gridwidth');
+ }
}
+ var lonRange = geoLayoutOut.lonaxis.range;
+ var latRange = geoLayoutOut.lataxis.range;
+
+ // to cross antimeridian w/o ambiguity
+ var lon0 = lonRange[0];
+ var lon1 = lonRange[1];
+ if(lon0 > 0 && lon1 < 0) lon1 += 360;
+
+ var centerLon = (lon0 + lon1) / 2;
+ var projLon;
+
if(!isAlbersUsa) {
- var dfltProjRotate = scopeParams.projRotate || [0, 0, 0];
- coerce('projection.rotation.lon', dfltProjRotate[0]);
+ var dfltProjRotate = isScoped ? scopeParams.projRotate : [centerLon, 0, 0];
+
+ projLon = coerce('projection.rotation.lon', dfltProjRotate[0]);
coerce('projection.rotation.lat', dfltProjRotate[1]);
coerce('projection.rotation.roll', dfltProjRotate[2]);
@@ -57,7 +97,28 @@ function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce) {
show = coerce('showocean');
if(show) coerce('oceancolor');
}
- else geoLayoutOut.scope = 'usa';
+
+ var centerLonDflt;
+ var centerLatDflt;
+
+ if(isAlbersUsa) {
+ // 'albers usa' does not have a 'center',
+ // these values were found using via:
+ // projection.invert([geoLayout.center.lon, geoLayoutIn.center.lat])
+ centerLonDflt = -96.6;
+ centerLatDflt = 38.7;
+ } else {
+ centerLonDflt = isScoped ? centerLon : projLon;
+ centerLatDflt = (latRange[0] + latRange[1]) / 2;
+ }
+
+ coerce('center.lon', centerLonDflt);
+ coerce('center.lat', centerLatDflt);
+
+ if(isConic) {
+ var dfltProjParallels = scopeParams.projParallels || [0, 60];
+ coerce('projection.parallels', dfltProjParallels);
+ }
coerce('projection.scale');
@@ -98,20 +159,4 @@ function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce) {
}
coerce('bgcolor');
-
- supplyGeoAxisLayoutDefaults(geoLayoutIn, geoLayoutOut);
-
- // bind a few helper variables
- geoLayoutOut._isHighRes = resolution === 50;
- geoLayoutOut._clipAngle = constants.lonaxisSpan[projType] / 2;
- geoLayoutOut._isAlbersUsa = isAlbersUsa;
- geoLayoutOut._isConic = isConic;
- geoLayoutOut._isScoped = isScoped;
-
- var rotation = geoLayoutOut.projection.rotation || {};
- geoLayoutOut.projection._rotate = [
- -rotation.lon || 0,
- -rotation.lat || 0,
- rotation.roll || 0
- ];
}
diff --git a/src/plots/geo/layout/layout_attributes.js b/src/plots/geo/layout/layout_attributes.js
index 98a5043dcfa..a6da089854e 100644
--- a/src/plots/geo/layout/layout_attributes.js
+++ b/src/plots/geo/layout/layout_attributes.js
@@ -10,9 +10,59 @@
var colorAttrs = require('../../../components/color/attributes');
var constants = require('../constants');
-var geoAxesAttrs = require('./axis_attributes');
var overrideAll = require('../../../plot_api/edit_types').overrideAll;
+var geoAxesAttrs = {
+ range: {
+ valType: 'info_array',
+ role: 'info',
+ items: [
+ {valType: 'number'},
+ {valType: 'number'}
+ ],
+ description: [
+ 'Sets the range of this axis (in degrees),',
+ 'sets the map\'s clipped coordinates.'
+ ].join(' ')
+ },
+ showgrid: {
+ valType: 'boolean',
+ role: 'info',
+ dflt: false,
+ description: 'Sets whether or not graticule are shown on the map.'
+ },
+ tick0: {
+ valType: 'number',
+ role: 'info',
+ description: [
+ 'Sets the graticule\'s starting tick longitude/latitude.'
+ ].join(' ')
+ },
+ dtick: {
+ valType: 'number',
+ role: 'info',
+ description: [
+ 'Sets the graticule\'s longitude/latitude tick step.'
+ ].join(' ')
+ },
+ gridcolor: {
+ valType: 'color',
+ role: 'style',
+ dflt: colorAttrs.lightLine,
+ description: [
+ 'Sets the graticule\'s stroke color.'
+ ].join(' ')
+ },
+ gridwidth: {
+ valType: 'number',
+ role: 'style',
+ min: 0,
+ dflt: 1,
+ description: [
+ 'Sets the graticule\'s stroke width (in px).'
+ ].join(' ')
+ }
+};
module.exports = overrideAll({
domain: {
@@ -25,8 +75,11 @@ module.exports = overrideAll({
],
dflt: [0, 1],
description: [
- 'Sets the horizontal domain of this map',
- '(in plot fraction).'
+ 'Sets the maximum horizontal domain of this map',
+ '(in plot fraction).',
+ 'Note that geo subplots are constrained by domain.',
+ 'In general, when `projection.scale` is set to 1.',
+ 'a map will fit either its x or y domain, but not both. '
].join(' ')
},
y: {
@@ -38,8 +91,11 @@ module.exports = overrideAll({
],
dflt: [0, 1],
description: [
- 'Sets the vertical domain of this map',
- '(in plot fraction).'
+ 'Sets the maximum vertical domain of this map',
+ '(in plot fraction).',
+ 'Note that geo subplots are constrained by domain.',
+ 'In general, when `projection.scale` is set to 1.',
+ 'a map will fit either its x or y domain, but not both. '
].join(' ')
}
},
@@ -75,7 +131,8 @@ module.exports = overrideAll({
role: 'info',
description: [
'Rotates the map along parallels',
- '(in degrees East).'
+ '(in degrees East).',
+ 'Defaults to the center of the `lonaxis.range` values.'
].join(' ')
},
lat: {
@@ -112,9 +169,32 @@ module.exports = overrideAll({
valType: 'number',
role: 'info',
min: 0,
- max: 10,
dflt: 1,
- description: 'Zooms in or out on the map view.'
+ description: [
+ 'Zooms in or out on the map view.',
+ 'A scale of *1* corresponds to the largest zoom level',
+ 'that fits the map\'s lon and lat ranges. '
+ ].join(' ')
+ },
+ },
+ center: {
+ lon: {
+ valType: 'number',
+ role: 'info',
+ description: [
+ 'Sets the longitude of the map\'s center.',
+ 'By default, the map\'s longitude center lies at the middle of the longitude range',
+ 'for scoped projection and above `projection.rotation.lon` otherwise.'
+ ].join(' ')
+ },
+ lat: {
+ valType: 'number',
+ role: 'info',
+ description: [
+ 'Sets the latitude of the map\'s center.',
+ 'For all projection types, the map\'s latitude center lies',
+ 'at the middle of the latitude range by default.'
+ ].join(' ')
}
},
showcoastlines: {
diff --git a/src/plots/geo/set_scale.js b/src/plots/geo/set_scale.js
deleted file mode 100644
index 66ef39f0983..00000000000
--- a/src/plots/geo/set_scale.js
+++ /dev/null
@@ -1,149 +0,0 @@
-/**
-* Copyright 2012-2017, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
-
-
-'use strict';
-
-var d3 = require('d3');
-
-var clipPad = require('./constants').clipPad;
-
-function createGeoScale(geoLayout, graphSize) {
- var projLayout = geoLayout.projection,
- lonaxisLayout = geoLayout.lonaxis,
- lataxisLayout = geoLayout.lataxis,
- geoDomain = geoLayout.domain,
- frameWidth = geoLayout.framewidth || 0;
-
- // width & height the geo div
- var geoWidth = graphSize.w * (geoDomain.x[1] - geoDomain.x[0]),
- geoHeight = graphSize.h * (geoDomain.y[1] - geoDomain.y[0]);
-
- // add padding around range to avoid aliasing
- var lon0 = lonaxisLayout.range[0] + clipPad,
- lon1 = lonaxisLayout.range[1] - clipPad,
- lat0 = lataxisLayout.range[0] + clipPad,
- lat1 = lataxisLayout.range[1] - clipPad,
- lonfull0 = lonaxisLayout._fullRange[0] + clipPad,
- lonfull1 = lonaxisLayout._fullRange[1] - clipPad,
- latfull0 = lataxisLayout._fullRange[0] + clipPad,
- latfull1 = lataxisLayout._fullRange[1] - clipPad;
-
- // initial translation (makes the math easier)
- projLayout._translate0 = [
- graphSize.l + geoWidth / 2, graphSize.t + geoHeight / 2
- ];
-
-
- // center of the projection is given by
- // the lon/lat ranges and the rotate angle
- var dlon = lon1 - lon0,
- dlat = lat1 - lat0,
- c0 = [lon0 + dlon / 2, lat0 + dlat / 2],
- r = projLayout._rotate;
-
- projLayout._center = [c0[0] + r[0], c0[1] + r[1]];
-
- // needs a initial projection; it is called from makeProjection
- var setScale = function(projection) {
- var scale0 = projection.scale(),
- translate0 = projLayout._translate0,
- rangeBox = makeRangeBox(lon0, lat0, lon1, lat1),
- fullRangeBox = makeRangeBox(lonfull0, latfull0, lonfull1, latfull1);
-
- var scale, translate, bounds, fullBounds;
-
- // Inspired by: http://stackoverflow.com/a/14654988/4068492
- // using the path determine the bounds of the current map and use
- // these to determine better values for the scale and translation
-
- function getScale(bounds) {
- return Math.min(
- scale0 * geoWidth / (bounds[1][0] - bounds[0][0]),
- scale0 * geoHeight / (bounds[1][1] - bounds[0][1])
- );
- }
-
- // scale projection given how range box get deformed
- // by the projection
- bounds = getBounds(projection, rangeBox);
- scale = getScale(bounds);
-
- // similarly, get scale at full range
- fullBounds = getBounds(projection, fullRangeBox);
- projLayout._fullScale = getScale(fullBounds);
-
- projection.scale(scale);
-
- // translate the projection so that the top-left corner
- // of the range box is at the top-left corner of the viewbox
- bounds = getBounds(projection, rangeBox);
- translate = [
- translate0[0] - bounds[0][0] + frameWidth,
- translate0[1] - bounds[0][1] + frameWidth
- ];
- projLayout._translate = translate;
- projection.translate(translate);
-
- // clip regions out of the range box
- // (these are clipping along horizontal/vertical lines)
- bounds = getBounds(projection, rangeBox);
- if(!geoLayout._isAlbersUsa) projection.clipExtent(bounds);
-
- // adjust scale one more time with the 'scale' attribute
- scale = projLayout.scale * scale;
-
- // set projection scale and save it
- projLayout._scale = scale;
-
- // save the effective width & height of the geo framework
- geoLayout._width = Math.round(bounds[1][0]) + frameWidth;
- geoLayout._height = Math.round(bounds[1][1]) + frameWidth;
-
- // save the margin length induced by the map scaling
- geoLayout._marginX = (geoWidth - Math.round(bounds[1][0])) / 2;
- geoLayout._marginY = (geoHeight - Math.round(bounds[1][1])) / 2;
- };
-
- return setScale;
-}
-
-module.exports = createGeoScale;
-
-// polygon GeoJSON corresponding to lon/lat range box
-// with well-defined direction
-function makeRangeBox(lon0, lat0, lon1, lat1) {
- var dlon4 = (lon1 - lon0) / 4;
-
- // TODO is this enough to handle ALL cases?
- // -- this makes scaling less precise than using d3.geo.graticule
- // as great circles can overshoot the boundary
- // (that's not a big deal I think)
- return {
- type: 'Polygon',
- coordinates: [
- [ [lon0, lat0],
- [lon0, lat1],
- [lon0 + dlon4, lat1],
- [lon0 + 2 * dlon4, lat1],
- [lon0 + 3 * dlon4, lat1],
- [lon1, lat1],
- [lon1, lat0],
- [lon1 - dlon4, lat0],
- [lon1 - 2 * dlon4, lat0],
- [lon1 - 3 * dlon4, lat0],
- [lon0, lat0] ]
- ]
- };
-}
-
-// bounds array [[top, left], [bottom, right]]
-// of the lon/lat range box
-function getBounds(projection, rangeBox) {
- return d3.geo.path().projection(projection).bounds(rangeBox);
-}
diff --git a/src/plots/geo/zoom.js b/src/plots/geo/zoom.js
index dd26c33bae8..6330445c543 100644
--- a/src/plots/geo/zoom.js
+++ b/src/plots/geo/zoom.js
@@ -10,41 +10,66 @@
'use strict';
var d3 = require('d3');
+var Lib = require('../../lib');
-var radians = Math.PI / 180,
- degrees = 180 / Math.PI,
- zoomstartStyle = { cursor: 'pointer' },
- zoomendStyle = { cursor: 'auto' };
-
+var radians = Math.PI / 180;
+var degrees = 180 / Math.PI;
+var zoomstartStyle = {cursor: 'pointer'};
+var zoomendStyle = {cursor: 'auto'};
function createGeoZoom(geo, geoLayout) {
+ var projection = geo.projection;
var zoomConstructor;
- if(geoLayout._isScoped) zoomConstructor = zoomScoped;
- else if(geoLayout._clipAngle) zoomConstructor = zoomClipped;
- else zoomConstructor = zoomNonClipped;
+ if(geoLayout._isScoped) {
+ zoomConstructor = zoomScoped;
+ } else if(geoLayout._isClipped) {
+ zoomConstructor = zoomClipped;
+ } else {
+ zoomConstructor = zoomNonClipped;
+ }
// TODO add a conic-specific zoom
- return zoomConstructor(geo, geoLayout.projection);
+ return zoomConstructor(geo, projection);
}
module.exports = createGeoZoom;
// common to all zoom types
-function initZoom(projection, projLayout) {
- var fullScale = projLayout._fullScale;
-
+function initZoom(geo, projection) {
return d3.behavior.zoom()
.translate(projection.translate())
- .scale(projection.scale())
- .scaleExtent([0.5 * fullScale, 100 * fullScale]);
+ .scale(projection.scale());
+}
+
+// sync zoom updates with user & full layout
+function sync(geo, projection, cb) {
+ var id = geo.id;
+ var gd = geo.graphDiv;
+ var userOpts = gd.layout[id];
+ var fullOpts = gd._fullLayout[id];
+
+ var eventData = {};
+
+ function set(propStr, val) {
+ var fullNp = Lib.nestedProperty(fullOpts, propStr);
+
+ if(fullNp.get() !== val) {
+ fullNp.set(val);
+ Lib.nestedProperty(userOpts, propStr).set(val);
+ eventData[id + '.' + propStr] = val;
+ }
+ }
+
+ cb(set);
+ set('projection.scale', projection.scale() / geo.fitScale);
+ gd.emit('plotly_relayout', eventData);
}
// zoom for scoped projections
-function zoomScoped(geo, projLayout) {
- var projection = geo.projection,
- zoom = initZoom(projection, projLayout);
+function zoomScoped(geo, projection) {
+ var zoom = initZoom(geo, projection);
function handleZoomstart() {
d3.select(this).style(zoomstartStyle);
@@ -54,12 +79,19 @@ function zoomScoped(geo, projLayout) {
projection
.scale(d3.event.scale)
.translate(d3.event.translate);
-
geo.render();
}
+ function syncCb(set) {
+ var center = projection.invert(geo.midPt);
+
+ set('center.lon', center[0]);
+ set('center.lat', center[1]);
+ }
+
function handleZoomend() {
d3.select(this).style(zoomendStyle);
+ sync(geo, projection, syncCb);
}
zoom
@@ -71,9 +103,8 @@ function zoomScoped(geo, projLayout) {
}
// zoom for non-clipped projections
-function zoomNonClipped(geo, projLayout) {
- var projection = geo.projection,
- zoom = initZoom(projection, projLayout);
+function zoomNonClipped(geo, projection) {
+ var zoom = initZoom(geo, projection);
var INSIDETOLORANCEPXS = 2;
@@ -108,7 +139,6 @@ function zoomNonClipped(geo, projLayout) {
}
projection.scale(d3.event.scale);
-
projection.translate([translate0[0], d3.event.translate[1]]);
if(!zoomPoint) {
@@ -127,10 +157,16 @@ function zoomNonClipped(geo, projLayout) {
function handleZoomend() {
d3.select(this).style(zoomendStyle);
+ sync(geo, projection, syncCb);
+ }
+
+ function syncCb(set) {
+ var rotate = projection.rotate();
+ var center = projection.invert(geo.midPt);
- // or something like
- // http://www.jasondavies.com/maps/gilbert/
- // ... a little harder with multiple base layers
+ set('projection.rotation.lon', -rotate[0]);
+ set('center.lon', center[0]);
+ set('center.lat', center[1]);
}
zoom
@@ -143,10 +179,9 @@ function zoomNonClipped(geo, projLayout) {
// zoom for clipped projections
// inspired by https://www.jasondavies.com/maps/d3.geo.zoom.js
-function zoomClipped(geo, projLayout) {
- var projection = geo.projection,
- view = {r: projection.rotate(), k: projection.scale()},
- zoom = initZoom(projection, projLayout),
+function zoomClipped(geo, projection) {
+ var view = {r: projection.rotate(), k: projection.scale()},
+ zoom = initZoom(geo, projection),
event = d3_eventDispatch(zoom, 'zoomstart', 'zoom', 'zoomend'),
zooming = 0,
zoomOn = zoom.on;
@@ -179,7 +214,6 @@ function zoomClipped(geo, projLayout) {
// if not, don't do anything new but scale
// if it is, then we can assume between will exist below
// so we don't need the 'bank' function, whatever that is.
- // TODO: is this right?
else if(position(projection, mouse1)) {
// go back to original projection temporarily
// except for scale... that's kind of independent?
@@ -212,6 +246,7 @@ function zoomClipped(geo, projLayout) {
d3.select(this).style(zoomendStyle);
zoomOn.call(zoom, 'zoom', null);
zoomended(event.of(this, arguments));
+ sync(geo, projection, syncCb);
})
.on('zoom.redraw', function() {
geo.render();
@@ -229,6 +264,12 @@ function zoomClipped(geo, projLayout) {
if(!--zooming) dispatch({type: 'zoomend'});
}
+ function syncCb(set) {
+ var _rotate = projection.rotate();
+ set('projection.rotation.lon', -_rotate[0]);
+ set('projection.rotation.lat', -_rotate[1]);
+ }
+
return d3.rebind(zoom, event, 'on');
}
diff --git a/src/plots/geo/zoom_reset.js b/src/plots/geo/zoom_reset.js
deleted file mode 100644
index dc9a543d09f..00000000000
--- a/src/plots/geo/zoom_reset.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
-* Copyright 2012-2017, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
-
-
-'use strict';
-
-module.exports = function createGeoZoomReset(geo, geoLayout) {
- var projection = geo.projection,
- zoom = geo.zoom;
-
- var zoomReset = function() {
- geo.makeProjection(geoLayout);
- geo.makePath();
-
- zoom.scale(projection.scale());
- zoom.translate(projection.translate());
-
- geo.render();
- };
-
- return zoomReset;
-};
diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js
index 3618c491212..177de42ffab 100644
--- a/src/plots/gl2d/index.js
+++ b/src/plots/gl2d/index.js
@@ -130,3 +130,12 @@ exports.toSVG = function(gd) {
scene.destroy();
}
};
+
+exports.updateFx = function(fullLayout) {
+ var subplotIds = Plots.getSubplotIds(fullLayout, 'gl2d');
+
+ for(var i = 0; i < subplotIds.length; i++) {
+ var subplotObj = fullLayout._plots[subplotIds[i]]._scene2d;
+ subplotObj.updateFx(fullLayout.dragmode);
+ }
+};
diff --git a/src/plots/gl3d/index.js b/src/plots/gl3d/index.js
index 3c003165a8a..e3e2e9094ca 100644
--- a/src/plots/gl3d/index.js
+++ b/src/plots/gl3d/index.js
@@ -128,3 +128,12 @@ exports.cleanId = function cleanId(id) {
return SCENE + sceneNum;
};
+
+exports.updateFx = function(fullLayout) {
+ var subplotIds = Plots.getSubplotIds(fullLayout, GL3D);
+
+ for(var i = 0; i < subplotIds.length; i++) {
+ var subplotObj = fullLayout[subplotIds[i]]._scene;
+ subplotObj.updateFx(fullLayout.dragmode, fullLayout.hovermode);
+ }
+};
diff --git a/src/plots/mapbox/index.js b/src/plots/mapbox/index.js
index a63393fe6dd..b417cb59729 100644
--- a/src/plots/mapbox/index.js
+++ b/src/plots/mapbox/index.js
@@ -155,3 +155,12 @@ function findAccessToken(gd, mapboxIds) {
return accessToken;
}
+
+exports.updateFx = function(fullLayout) {
+ var subplotIds = Plots.getSubplotIds(fullLayout, MAPBOX);
+
+ for(var i = 0; i < subplotIds.length; i++) {
+ var subplotObj = fullLayout[subplotIds[i]]._subplot;
+ subplotObj.updateFx(fullLayout);
+ }
+};
diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js
index 719b89168a7..a42f8461f53 100644
--- a/src/plots/ternary/ternary.js
+++ b/src/plots/ternary/ternary.js
@@ -70,22 +70,17 @@ proto.plot = function(ternaryCalcData, fullLayout) {
proto.makeFramework = function(fullLayout) {
var _this = this;
var ternaryLayout = fullLayout[_this.id];
-
- var defGroup = _this.defs.selectAll('g.clips')
- .data([0]);
- defGroup.enter().append('g')
- .classed('clips', true);
+ var clipId = _this.clipId = 'clip' + _this.layoutId + _this.id;
// clippath for this ternary subplot
- var clipId = _this.clipId = 'clip' + _this.layoutId + _this.id;
- _this.clipDef = defGroup.selectAll('#' + clipId)
+ _this.clipDef = fullLayout._clips.selectAll('#' + clipId)
.data([0]);
_this.clipDef.enter().append('clipPath').attr('id', clipId)
.append('path').attr('d', 'M0,0Z');
// 'relative' clippath (i.e. no translation) for this ternary subplot
var clipIdRelative = _this.clipIdRelative = 'clip-relative' + _this.layoutId + _this.id;
- _this.clipDefRelative = defGroup.selectAll('#' + clipIdRelative)
+ _this.clipDefRelative = fullLayout._clips.selectAll('#' + clipIdRelative)
.data([0]);
_this.clipDefRelative.enter().append('clipPath').attr('id', clipIdRelative)
.append('path').attr('d', 'M0,0Z');
diff --git a/src/traces/carpet/plot.js b/src/traces/carpet/plot.js
index 8c469851e90..f83731592ad 100644
--- a/src/traces/carpet/plot.js
+++ b/src/traces/carpet/plot.js
@@ -38,7 +38,7 @@ function plotOne(gd, plotinfo, cd) {
fullLayout = gd._fullLayout;
var gridLayer = plotinfo.plot.selectAll('.carpetlayer');
- var clipLayer = makeg(fullLayout._defs, 'g', 'clips');
+ var clipLayer = fullLayout._clips;
var axisLayer = makeg(gridLayer, 'g', 'carpet' + trace.uid).classed('trace', true);
var minorLayer = makeg(axisLayer, 'g', 'minorlayer');
diff --git a/src/traces/choropleth/calc.js b/src/traces/choropleth/calc.js
index 5a3eacb14a4..f4e479a98f7 100644
--- a/src/traces/choropleth/calc.js
+++ b/src/traces/choropleth/calc.js
@@ -9,9 +9,27 @@
'use strict';
-var colorscaleCalc = require('../../components/colorscale/calc');
+var isNumeric = require('fast-isnumeric');
+var BADNUM = require('../../constants/numerical').BADNUM;
+var colorscaleCalc = require('../../components/colorscale/calc');
+var arraysToCalcdata = require('../scatter/arrays_to_calcdata');
module.exports = function calc(gd, trace) {
+ var len = trace.locations.length;
+ var calcTrace = new Array(len);
+
+ for(var i = 0; i < len; i++) {
+ var calcPt = calcTrace[i] = {};
+ var loc = trace.locations[i];
+ var z = trace.z[i];
+
+ calcPt.loc = typeof loc === 'string' ? loc : null;
+ calcPt.z = isNumeric(z) ? z : BADNUM;
+ }
+
+ arraysToCalcdata(calcTrace, trace);
colorscaleCalc(trace, trace.z, '', 'z');
+
+ return calcTrace;
};
diff --git a/src/traces/choropleth/hover.js b/src/traces/choropleth/hover.js
index b9b4962102e..71799f22385 100644
--- a/src/traces/choropleth/hover.js
+++ b/src/traces/choropleth/hover.js
@@ -12,23 +12,39 @@
var Axes = require('../../plots/cartesian/axes');
var attributes = require('./attributes');
-module.exports = function hoverPoints(pointData) {
+module.exports = function hoverPoints(pointData, xval, yval) {
var cd = pointData.cd;
var trace = cd[0].trace;
var geo = pointData.subplot;
- // set on choropleth paths 'mouseover'
- var pt = geo.choroplethHoverPt;
-
- if(!pt) return;
+ var pt, i, j, isInside;
+
+ for(i = 0; i < cd.length; i++) {
+ pt = cd[i];
+ isInside = false;
+
+ if(pt._polygons) {
+ for(j = 0; j < pt._polygons.length; j++) {
+ if(pt._polygons[j].contains([xval, yval])) {
+ isInside = !isInside;
+ }
+ // for polygons that cross antimeridian as xval is in [-180, 180]
+ if(pt._polygons[j].contains([xval + 360, yval])) {
+ isInside = !isInside;
+ }
+ }
+
+ if(isInside) break;
+ }
+ }
- var centroid = geo.projection(pt.properties.ct);
+ if(!isInside || !pt) return;
- pointData.x0 = pointData.x1 = centroid[0];
- pointData.y0 = pointData.y1 = centroid[1];
+ pointData.x0 = pointData.x1 = pointData.xa.c2p(pt.ct);
+ pointData.y0 = pointData.y1 = pointData.ya.c2p(pt.ct);
pointData.index = pt.index;
- pointData.location = pt.id;
+ pointData.location = pt.loc;
pointData.z = pt.z;
makeHoverInfo(pointData, trace, pt, geo.mockAxis);
@@ -55,10 +71,11 @@ function makeHoverInfo(pointData, trace, pt, axis) {
return Axes.tickText(axis, axis.c2l(val), 'hover').text;
}
- if(hasIdAsNameLabel) pointData.nameOverride = pt.id;
- else {
+ if(hasIdAsNameLabel) {
+ pointData.nameOverride = pt.loc;
+ } else {
if(hasName) pointData.nameOverride = trace.name;
- if(hasLocation) text.push(pt.id);
+ if(hasLocation) text.push(pt.loc);
}
if(hasZ) text.push(formatter(pt.z));
diff --git a/src/traces/choropleth/index.js b/src/traces/choropleth/index.js
index f358369cfd3..a5940e41cd6 100644
--- a/src/traces/choropleth/index.js
+++ b/src/traces/choropleth/index.js
@@ -18,6 +18,7 @@ Choropleth.calc = require('./calc');
Choropleth.plot = require('./plot');
Choropleth.hoverPoints = require('./hover');
Choropleth.eventData = require('./event_data');
+Choropleth.selectPoints = require('./select');
Choropleth.moduleType = 'trace';
Choropleth.name = 'choropleth';
diff --git a/src/traces/choropleth/plot.js b/src/traces/choropleth/plot.js
index 9f4ec41e87c..1d2c30d4234 100644
--- a/src/traces/choropleth/plot.js
+++ b/src/traces/choropleth/plot.js
@@ -11,108 +11,179 @@
var d3 = require('d3');
+var Lib = require('../../lib');
var Color = require('../../components/color');
var Drawing = require('../../components/drawing');
var Colorscale = require('../../components/colorscale');
+var polygon = require('../../lib/polygon');
var getTopojsonFeatures = require('../../lib/topojson_utils').getTopojsonFeatures;
var locationToFeature = require('../../lib/geo_location_utils').locationToFeature;
-var arrayToCalcItem = require('../../lib/array_to_calc_item');
-var constants = require('../../plots/geo/constants');
-
-module.exports = function plot(geo, calcData, geoLayout) {
+module.exports = function plot(geo, calcData) {
+ for(var i = 0; i < calcData.length; i++) {
+ calcGeoJSON(calcData[i], geo.topojson);
+ }
function keyFunc(d) { return d[0].trace.uid; }
- var framework = geo.framework,
- gChoropleth = framework.select('g.choroplethlayer'),
- gBaseLayer = framework.select('g.baselayer'),
- gBaseLayerOverChoropleth = framework.select('g.baselayeroverchoropleth'),
- baseLayersOverChoropleth = constants.baseLayersOverChoropleth,
- layerName;
-
- var gChoroplethTraces = gChoropleth
+ var gTraces = geo.layers.backplot.select('.choroplethlayer')
.selectAll('g.trace.choropleth')
.data(calcData, keyFunc);
- gChoroplethTraces.enter().append('g')
+ gTraces.enter().append('g')
.attr('class', 'trace choropleth');
- gChoroplethTraces.exit().remove();
+ gTraces.exit().remove();
- gChoroplethTraces.each(function(calcTrace) {
- var trace = calcTrace[0].trace,
- cdi = calcGeoJSON(trace, geo.topojson);
+ gTraces.each(function(calcTrace) {
+ var sel = calcTrace[0].node3 = d3.select(this);
- var paths = d3.select(this)
- .selectAll('path.choroplethlocation')
- .data(cdi);
+ var paths = sel.selectAll('path.choroplethlocation')
+ .data(Lib.identity);
paths.enter().append('path')
- .classed('choroplethlocation', true)
- .on('mouseover', function(pt) {
- geo.choroplethHoverPt = pt;
- })
- .on('mouseout', function() {
- geo.choroplethHoverPt = null;
- });
+ .classed('choroplethlocation', true);
paths.exit().remove();
});
- // some baselayers are drawn over choropleth
- gBaseLayerOverChoropleth.selectAll('*').remove();
-
- for(var i = 0; i < baseLayersOverChoropleth.length; i++) {
- layerName = baseLayersOverChoropleth[i];
- gBaseLayer.select('g.' + layerName).remove();
- geo.drawTopo(gBaseLayerOverChoropleth, layerName, geoLayout);
- geo.styleLayer(gBaseLayerOverChoropleth, layerName, geoLayout);
- }
-
style(geo);
};
-function calcGeoJSON(trace, topojson) {
- var cdi = [],
- locations = trace.locations,
- len = locations.length,
- features = getTopojsonFeatures(trace, topojson),
- markerLine = (trace.marker || {}).line || {};
-
- var feature;
+function calcGeoJSON(calcTrace, topojson) {
+ var trace = calcTrace[0].trace;
+ var len = calcTrace.length;
+ var features = getTopojsonFeatures(trace, topojson);
for(var i = 0; i < len; i++) {
- feature = locationToFeature(trace.locationmode, locations[i], features);
+ var calcPt = calcTrace[i];
+ var feature = locationToFeature(trace.locationmode, calcPt.loc, features);
- if(!feature) continue; // filter the blank features here
+ if(!feature) {
+ calcPt.geojson = null;
+ continue;
+ }
- // 'data_array' attributes
- feature.z = trace.z[i];
- if(trace.text !== undefined) feature.tx = trace.text[i];
- // 'arrayOk' attributes
- arrayToCalcItem(markerLine.color, feature, 'mlc', i);
- arrayToCalcItem(markerLine.width, feature, 'mlw', i);
+ calcPt.geojson = feature;
+ calcPt.ct = feature.properties.ct;
+ calcPt.index = i;
+ calcPt._polygons = feature2polygons(feature);
+ }
+}
- // for event data
- feature.index = i;
+function feature2polygons(feature) {
+ var geometry = feature.geometry;
+ var coords = geometry.coordinates;
+ var loc = feature.id;
- cdi.push(feature);
+ var polygons = [];
+ var appendPolygon, j, k, m;
+
+ function doesCrossAntiMerdian(pts) {
+ for(var l = 0; l < pts.length - 1; l++) {
+ if(pts[l][0] > 0 && pts[l + 1][0] < 0) return l;
+ }
+ return null;
}
- if(cdi.length > 0) cdi[0].trace = trace;
+ if(loc === 'RUS' || loc === 'FJI') {
+ // Russia and Fiji have landmasses that cross the antimeridian,
+ // we need to add +360 to their longitude coordinates, so that
+ // polygon 'contains' doesn't get confused when crossing the antimeridian.
+ //
+ // Note that other countries have polygons on either side of the antimeridian
+ // (e.g. some Aleutian island for the USA), but those don't confuse
+ // the 'contains' method; these are skipped here.
+ appendPolygon = function(_pts) {
+ var pts;
+
+ if(doesCrossAntiMerdian(_pts) === null) {
+ pts = _pts;
+ } else {
+ pts = new Array(_pts.length);
+ for(m = 0; m < _pts.length; m++) {
+ // do nut mutate calcdata[i][j].geojson !!
+ pts[m] = [
+ _pts[m][0] < 0 ? _pts[m][0] + 360 : _pts[m][0],
+ _pts[m][1]
+ ];
+ }
+ }
+
+ polygons.push(polygon.tester(pts));
+ };
+ } else if(loc === 'ATA') {
+ // Antarctica has a landmass that wraps around every longitudes which
+ // confuses the 'contains' methods.
+ appendPolygon = function(pts) {
+ var crossAntiMeridianIndex = doesCrossAntiMerdian(pts);
+
+ // polygon that do not cross anti-meridian need no special handling
+ if(crossAntiMeridianIndex === null) {
+ return polygons.push(polygon.tester(pts));
+ }
+
+ // stitch polygon by adding pt over South Pole,
+ // so that it covers the projected region covers all latitudes
+ //
+ // Note that the algorithm below only works for polygons that
+ // start and end on longitude -180 (like the ones built by
+ // https://github.com/etpinard/sane-topojson).
+ var stitch = new Array(pts.length + 1);
+ var si = 0;
+
+ for(m = 0; m < pts.length; m++) {
+ if(m > crossAntiMeridianIndex) {
+ stitch[si++] = [pts[m][0] + 360, pts[m][1]];
+ } else if(m === crossAntiMeridianIndex) {
+ stitch[si++] = pts[m];
+ stitch[si++] = [pts[m][0], -90];
+ } else {
+ stitch[si++] = pts[m];
+ }
+ }
+
+ // polygon.tester by default appends pt[0] to the points list,
+ // we must remove it here, to avoid a jump in longitude from 180 to -180,
+ // that would confuse the 'contains' method
+ var tester = polygon.tester(stitch);
+ tester.pts.pop();
+ polygons.push(tester);
+ };
+ } else {
+ // otherwise using same array ref is fine
+ appendPolygon = function(pts) {
+ polygons.push(polygon.tester(pts));
+ };
+ }
- return cdi;
+ switch(geometry.type) {
+ case 'MultiPolygon':
+ for(j = 0; j < coords.length; j++) {
+ for(k = 0; k < coords[j].length; k++) {
+ appendPolygon(coords[j][k]);
+ }
+ }
+ break;
+ case 'Polygon':
+ for(j = 0; j < coords.length; j++) {
+ appendPolygon(coords[j]);
+ }
+ break;
+ }
+
+ return polygons;
}
function style(geo) {
- geo.framework.selectAll('g.trace.choropleth').each(function(calcTrace) {
- var trace = calcTrace[0].trace,
- s = d3.select(this),
- marker = trace.marker || {},
- markerLine = marker.line || {};
+ var gTraces = geo.layers.backplot.selectAll('.trace.choropleth');
+
+ gTraces.each(function(calcTrace) {
+ var trace = calcTrace[0].trace;
+ var marker = trace.marker || {};
+ var markerLine = marker.line || {};
var sclFunc = Colorscale.makeColorScaleFunc(
Colorscale.extractScale(
@@ -122,11 +193,11 @@ function style(geo) {
)
);
- s.selectAll('path.choroplethlocation').each(function(pt) {
+ d3.select(this).selectAll('.choroplethlocation').each(function(d) {
d3.select(this)
- .attr('fill', function(pt) { return sclFunc(pt.z); })
- .call(Color.stroke, pt.mlc || markerLine.color)
- .call(Drawing.dashLine, '', pt.mlw || markerLine.width || 0);
+ .attr('fill', sclFunc(d.z))
+ .call(Color.stroke, d.mlc || markerLine.color)
+ .call(Drawing.dashLine, '', d.mlw || markerLine.width || 0);
});
});
}
diff --git a/src/traces/choropleth/select.js b/src/traces/choropleth/select.js
new file mode 100644
index 00000000000..209b1ba35a7
--- /dev/null
+++ b/src/traces/choropleth/select.js
@@ -0,0 +1,54 @@
+/**
+* Copyright 2012-2017, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var DESELECTDIM = require('../../constants/interactions').DESELECTDIM;
+
+module.exports = function selectPoints(searchInfo, polygon) {
+ var cd = searchInfo.cd;
+ var xa = searchInfo.xaxis;
+ var ya = searchInfo.yaxis;
+ var selection = [];
+ var node3 = cd[0].node3;
+
+ var i, di, ct, x, y;
+
+ if(polygon === false) {
+ for(i = 0; i < cd.length; i++) {
+ cd[i].dim = 0;
+ }
+ } else {
+ for(i = 0; i < cd.length; i++) {
+ di = cd[i];
+ ct = di.ct;
+
+ if(!ct) continue;
+
+ x = xa.c2p(ct);
+ y = ya.c2p(ct);
+
+ if(polygon.contains([x, y])) {
+ selection.push({
+ pointNumber: i,
+ lon: ct[0],
+ lat: ct[1]
+ });
+ di.dim = 0;
+ } else {
+ di.dim = 1;
+ }
+ }
+ }
+
+ node3.selectAll('path').style('opacity', function(d) {
+ return d.dim ? DESELECTDIM : 1;
+ });
+
+ return selection;
+};
diff --git a/src/traces/contour/plot.js b/src/traces/contour/plot.js
index 72d9e892229..a4f13aa1a73 100644
--- a/src/traces/contour/plot.js
+++ b/src/traces/contour/plot.js
@@ -86,7 +86,7 @@ function plotOne(gd, plotinfo, cd) {
makeBackground(plotGroup, perimeter, contours);
makeFills(plotGroup, pathinfo, perimeter, contours);
makeLinesAndLabels(plotGroup, pathinfo, gd, cd[0], contours, perimeter);
- clipGaps(plotGroup, plotinfo, fullLayout._defs, cd[0], perimeter);
+ clipGaps(plotGroup, plotinfo, fullLayout._clips, cd[0], perimeter);
}
function emptyPathinfo(contours, plotinfo, cd0) {
@@ -281,7 +281,7 @@ function makeLinesAndLabels(plotgroup, pathinfo, gd, cd0, contours, perimeter) {
var linegroup = exports.createLines(lineContainer, showLines || showLabels, pathinfo);
var lineClip = exports.createLineClip(lineContainer, clipLinesForLabels,
- gd._fullLayout._defs, cd0.trace.uid);
+ gd._fullLayout._clips, cd0.trace.uid);
var labelGroup = plotgroup.selectAll('g.contourlabels')
.data(showLabels ? [0] : []);
@@ -403,10 +403,10 @@ exports.createLines = function(lineContainer, makeLines, pathinfo) {
return linegroup;
};
-exports.createLineClip = function(lineContainer, clipLinesForLabels, defs, uid) {
+exports.createLineClip = function(lineContainer, clipLinesForLabels, clips, uid) {
var clipId = clipLinesForLabels ? ('clipline' + uid) : null;
- var lineClip = defs.select('.clips').selectAll('#' + clipId)
+ var lineClip = clips.selectAll('#' + clipId)
.data(clipLinesForLabels ? [0] : []);
lineClip.exit().remove();
@@ -630,10 +630,10 @@ exports.drawLabels = function(labelGroup, labelData, gd, lineClip, labelClipPath
}
};
-function clipGaps(plotGroup, plotinfo, defs, cd0, perimeter) {
+function clipGaps(plotGroup, plotinfo, clips, cd0, perimeter) {
var clipId = 'clip' + cd0.trace.uid;
- var clipPath = defs.select('.clips').selectAll('#' + clipId)
+ var clipPath = clips.selectAll('#' + clipId)
.data(cd0.trace.connectgaps ? [] : [0]);
clipPath.enter().append('clipPath')
.classed('contourclip', true)
diff --git a/src/traces/scattergeo/hover.js b/src/traces/scattergeo/hover.js
index a82ae13cd0e..4d3135d5d2a 100644
--- a/src/traces/scattergeo/hover.js
+++ b/src/traces/scattergeo/hover.js
@@ -17,32 +17,27 @@ var getTraceColor = require('../scatter/get_trace_color');
var attributes = require('./attributes');
-module.exports = function hoverPoints(pointData) {
- var cd = pointData.cd,
- trace = cd[0].trace,
- xa = pointData.xa,
- ya = pointData.ya,
- geo = pointData.subplot;
-
- function c2p(lonlat) {
- return geo.projection(lonlat);
- }
+module.exports = function hoverPoints(pointData, xval, yval) {
+ var cd = pointData.cd;
+ var trace = cd[0].trace;
+ var xa = pointData.xa;
+ var ya = pointData.ya;
+ var geo = pointData.subplot;
+
+ var isLonLatOverEdges = geo.projection.isLonLatOverEdges;
+ var project = geo.project;
function distFn(d) {
var lonlat = d.lonlat;
if(lonlat[0] === BADNUM) return Infinity;
+ if(isLonLatOverEdges(lonlat)) return Infinity;
- if(geo.isLonLatOverEdges(lonlat)) return Infinity;
-
- var pos = c2p(lonlat);
-
- var xPx = xa.c2p(),
- yPx = ya.c2p();
-
- var dx = Math.abs(xPx - pos[0]),
- dy = Math.abs(yPx - pos[1]),
- rad = Math.max(3, d.mrc || 0);
+ var pt = project(lonlat);
+ var px = project([xval, yval]);
+ var dx = Math.abs(pt[0] - px[0]);
+ var dy = Math.abs(pt[1] - px[1]);
+ var rad = Math.max(3, d.mrc || 0);
// N.B. d.mrc is the calculated marker radius
// which is only set for trace with 'markers' mode.
@@ -55,10 +50,10 @@ module.exports = function hoverPoints(pointData) {
// skip the rest (for this trace) if we didn't find a close point
if(pointData.index === false) return;
- var di = cd[pointData.index],
- lonlat = di.lonlat,
- pos = c2p(lonlat),
- rad = di.mrc || 1;
+ var di = cd[pointData.index];
+ var lonlat = di.lonlat;
+ var pos = [xa.c2p(lonlat), ya.c2p(lonlat)];
+ var rad = di.mrc || 1;
pointData.x0 = pos[0] - rad;
pointData.x1 = pos[0] + rad;
diff --git a/src/traces/scattergeo/index.js b/src/traces/scattergeo/index.js
index d48f0351330..4e8c989ee33 100644
--- a/src/traces/scattergeo/index.js
+++ b/src/traces/scattergeo/index.js
@@ -18,11 +18,12 @@ ScatterGeo.calc = require('./calc');
ScatterGeo.plot = require('./plot');
ScatterGeo.hoverPoints = require('./hover');
ScatterGeo.eventData = require('./event_data');
+ScatterGeo.selectPoints = require('./select');
ScatterGeo.moduleType = 'trace';
ScatterGeo.name = 'scattergeo';
ScatterGeo.basePlotModule = require('../../plots/geo');
-ScatterGeo.categories = ['geo', 'symbols', 'markerColorscale', 'showLegend'];
+ScatterGeo.categories = ['geo', 'symbols', 'markerColorscale', 'showLegend', 'scatter-like'];
ScatterGeo.meta = {
hrName: 'scatter_geo',
description: [
diff --git a/src/traces/scattergeo/plot.js b/src/traces/scattergeo/plot.js
index 094e2b60e84..42965cee10c 100644
--- a/src/traces/scattergeo/plot.js
+++ b/src/traces/scattergeo/plot.js
@@ -21,8 +21,10 @@ var locationToFeature = require('../../lib/geo_location_utils').locationToFeatur
var geoJsonUtils = require('../../lib/geojson_utils');
var subTypes = require('../scatter/subtypes');
-
module.exports = function plot(geo, calcData) {
+ for(var i = 0; i < calcData.length; i++) {
+ calcGeoJSON(calcData[i], geo.topojson);
+ }
function keyFunc(d) { return d[0].trace.uid; }
@@ -32,37 +34,34 @@ module.exports = function plot(geo, calcData) {
}
}
- for(var i = 0; i < calcData.length; i++) {
- fillLocationLonLat(calcData[i], geo.topojson);
- }
-
- var gScatterGeoTraces = geo.framework.select('.scattergeolayer')
+ var gTraces = geo.layers.frontplot.select('.scatterlayer')
.selectAll('g.trace.scattergeo')
.data(calcData, keyFunc);
- gScatterGeoTraces.enter().append('g')
+ gTraces.enter().append('g')
.attr('class', 'trace scattergeo');
- gScatterGeoTraces.exit().remove();
+ gTraces.exit().remove();
// TODO find a way to order the inner nodes on update
- gScatterGeoTraces.selectAll('*').remove();
+ gTraces.selectAll('*').remove();
- gScatterGeoTraces.each(function(calcTrace) {
- var s = d3.select(this);
+ gTraces.each(function(calcTrace) {
+ var s = calcTrace[0].node3 = d3.select(this);
var trace = calcTrace[0].trace;
if(subTypes.hasLines(trace) || trace.fill !== 'none') {
var lineCoords = geoJsonUtils.calcTraceToLineCoords(calcTrace);
var lineData = (trace.fill !== 'none') ?
- geoJsonUtils.makePolygon(lineCoords, trace) :
- geoJsonUtils.makeLine(lineCoords, trace);
+ geoJsonUtils.makePolygon(lineCoords) :
+ geoJsonUtils.makeLine(lineCoords);
s.selectAll('path.js-line')
- .data([lineData])
+ .data([{geojson: lineData, trace: trace}])
.enter().append('path')
- .classed('js-line', true);
+ .classed('js-line', true)
+ .style('stroke-miterlimit', 2);
}
if(subTypes.hasMarkers(trace)) {
@@ -86,7 +85,7 @@ module.exports = function plot(geo, calcData) {
style(geo);
};
-function fillLocationLonLat(calcTrace, topojson) {
+function calcGeoJSON(calcTrace, topojson) {
var trace = calcTrace[0].trace;
if(!Array.isArray(trace.locations)) return;
@@ -103,15 +102,15 @@ function fillLocationLonLat(calcTrace, topojson) {
}
function style(geo) {
- var selection = geo.framework.selectAll('g.trace.scattergeo');
+ var gTraces = geo.layers.frontplot.selectAll('.trace.scattergeo');
- selection.style('opacity', function(calcTrace) {
+ gTraces.style('opacity', function(calcTrace) {
return calcTrace[0].trace.opacity;
});
- selection.each(function(calcTrace) {
- var trace = calcTrace[0].trace,
- group = d3.select(this);
+ gTraces.each(function(calcTrace) {
+ var trace = calcTrace[0].trace;
+ var group = d3.select(this);
group.selectAll('path.point')
.call(Drawing.pointStyle, trace, geo.graphDiv);
@@ -120,12 +119,12 @@ function style(geo) {
});
// this part is incompatible with Drawing.lineGroupStyle
- selection.selectAll('path.js-line')
+ gTraces.selectAll('path.js-line')
.style('fill', 'none')
.each(function(d) {
- var path = d3.select(this),
- trace = d.trace,
- line = trace.line || {};
+ var path = d3.select(this);
+ var trace = d.trace;
+ var line = trace.line || {};
path.call(Color.stroke, line.color)
.call(Drawing.dashLine, line.dash || '', line.width || 0);
diff --git a/src/traces/scattergeo/select.js b/src/traces/scattergeo/select.js
new file mode 100644
index 00000000000..aa06137d783
--- /dev/null
+++ b/src/traces/scattergeo/select.js
@@ -0,0 +1,63 @@
+/**
+* Copyright 2012-2017, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var subtypes = require('../scatter/subtypes');
+var DESELECTDIM = require('../../constants/interactions').DESELECTDIM;
+
+module.exports = function selectPoints(searchInfo, polygon) {
+ var cd = searchInfo.cd;
+ var xa = searchInfo.xaxis;
+ var ya = searchInfo.yaxis;
+ var selection = [];
+ var trace = cd[0].trace;
+ var node3 = cd[0].node3;
+
+ var di, lonlat, x, y, i;
+
+ var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace));
+ if(trace.visible !== true || hasOnlyLines) return;
+
+ var marker = trace.marker;
+ var opacity = Array.isArray(marker.opacity) ? 1 : marker.opacity;
+
+ if(polygon === false) {
+ for(i = 0; i < cd.length; i++) {
+ cd[i].dim = 0;
+ }
+ } else {
+ for(i = 0; i < cd.length; i++) {
+ di = cd[i];
+ lonlat = di.lonlat;
+ x = xa.c2p(lonlat);
+ y = ya.c2p(lonlat);
+
+ if(polygon.contains([x, y])) {
+ selection.push({
+ pointNumber: i,
+ lon: lonlat[0],
+ lat: lonlat[1]
+ });
+ di.dim = 0;
+ } else {
+ di.dim = 1;
+ }
+ }
+ }
+
+ node3.selectAll('path.point').style('opacity', function(d) {
+ return ((d.mo + 1 || opacity + 1) - 1) * (d.dim ? DESELECTDIM : 1);
+ });
+
+ node3.selectAll('text').style('opacity', function(d) {
+ return d.dim ? DESELECTDIM : 1;
+ });
+
+ return selection;
+};
diff --git a/test/image/baselines/geo_across-antimeridian.png b/test/image/baselines/geo_across-antimeridian.png
new file mode 100644
index 00000000000..c54950d9c2d
Binary files /dev/null and b/test/image/baselines/geo_across-antimeridian.png differ
diff --git a/test/image/baselines/geo_africa-insets.png b/test/image/baselines/geo_africa-insets.png
index 8af3269d1d5..7ccb92d4d3f 100644
Binary files a/test/image/baselines/geo_africa-insets.png and b/test/image/baselines/geo_africa-insets.png differ
diff --git a/test/image/baselines/geo_aitoff-sinusoidal.png b/test/image/baselines/geo_aitoff-sinusoidal.png
index a2ff63b68db..f1c6316363a 100644
Binary files a/test/image/baselines/geo_aitoff-sinusoidal.png and b/test/image/baselines/geo_aitoff-sinusoidal.png differ
diff --git a/test/image/baselines/geo_bg-color.png b/test/image/baselines/geo_bg-color.png
index 8421cd959f1..4ac712167d8 100644
Binary files a/test/image/baselines/geo_bg-color.png and b/test/image/baselines/geo_bg-color.png differ
diff --git a/test/image/baselines/geo_big-frame.png b/test/image/baselines/geo_big-frame.png
index cea96bd27b7..841380a025a 100644
Binary files a/test/image/baselines/geo_big-frame.png and b/test/image/baselines/geo_big-frame.png differ
diff --git a/test/image/baselines/geo_bubbles-colorscales.png b/test/image/baselines/geo_bubbles-colorscales.png
index 642da7d688c..ffe6a751441 100644
Binary files a/test/image/baselines/geo_bubbles-colorscales.png and b/test/image/baselines/geo_bubbles-colorscales.png differ
diff --git a/test/image/baselines/geo_bubbles-sizeref.png b/test/image/baselines/geo_bubbles-sizeref.png
index 7c2e297f822..cd82b00a59e 100644
Binary files a/test/image/baselines/geo_bubbles-sizeref.png and b/test/image/baselines/geo_bubbles-sizeref.png differ
diff --git a/test/image/baselines/geo_canadian-cites.png b/test/image/baselines/geo_canadian-cites.png
index a86ddf60375..560c7423061 100644
Binary files a/test/image/baselines/geo_canadian-cites.png and b/test/image/baselines/geo_canadian-cites.png differ
diff --git a/test/image/baselines/geo_centering.png b/test/image/baselines/geo_centering.png
new file mode 100644
index 00000000000..8e65a0e8163
Binary files /dev/null and b/test/image/baselines/geo_centering.png differ
diff --git a/test/image/baselines/geo_choropleth-text.png b/test/image/baselines/geo_choropleth-text.png
index f99718126de..d3c5c83d4a5 100644
Binary files a/test/image/baselines/geo_choropleth-text.png and b/test/image/baselines/geo_choropleth-text.png differ
diff --git a/test/image/baselines/geo_choropleth-usa.png b/test/image/baselines/geo_choropleth-usa.png
index 907be08490a..a8a084894aa 100644
Binary files a/test/image/baselines/geo_choropleth-usa.png and b/test/image/baselines/geo_choropleth-usa.png differ
diff --git a/test/image/baselines/geo_conic-conformal.png b/test/image/baselines/geo_conic-conformal.png
index 6b22d992168..af9247a1d9e 100644
Binary files a/test/image/baselines/geo_conic-conformal.png and b/test/image/baselines/geo_conic-conformal.png differ
diff --git a/test/image/baselines/geo_connectgaps.png b/test/image/baselines/geo_connectgaps.png
index 8526ed17636..f1d4ad2398d 100644
Binary files a/test/image/baselines/geo_connectgaps.png and b/test/image/baselines/geo_connectgaps.png differ
diff --git a/test/image/baselines/geo_country-names-text-chart.png b/test/image/baselines/geo_country-names-text-chart.png
index 76b36ed7517..b921e1e3dfc 100644
Binary files a/test/image/baselines/geo_country-names-text-chart.png and b/test/image/baselines/geo_country-names-text-chart.png differ
diff --git a/test/image/baselines/geo_country-names.png b/test/image/baselines/geo_country-names.png
index 156e4d175e6..b2524b79c5e 100644
Binary files a/test/image/baselines/geo_country-names.png and b/test/image/baselines/geo_country-names.png differ
diff --git a/test/image/baselines/geo_custom-colorscale.png b/test/image/baselines/geo_custom-colorscale.png
index 1d21bef9104..bb8652ce625 100644
Binary files a/test/image/baselines/geo_custom-colorscale.png and b/test/image/baselines/geo_custom-colorscale.png differ
diff --git a/test/image/baselines/geo_europe-bubbles.png b/test/image/baselines/geo_europe-bubbles.png
index cdcf55fe787..d474421526c 100644
Binary files a/test/image/baselines/geo_europe-bubbles.png and b/test/image/baselines/geo_europe-bubbles.png differ
diff --git a/test/image/baselines/geo_fill.png b/test/image/baselines/geo_fill.png
index c3941d6d1af..62acf49e8db 100644
Binary files a/test/image/baselines/geo_fill.png and b/test/image/baselines/geo_fill.png differ
diff --git a/test/image/baselines/geo_first.png b/test/image/baselines/geo_first.png
index 3929fca1fad..1fdff452c88 100644
Binary files a/test/image/baselines/geo_first.png and b/test/image/baselines/geo_first.png differ
diff --git a/test/image/baselines/geo_kavrayskiy7.png b/test/image/baselines/geo_kavrayskiy7.png
index 4647107c56e..9ced1a0afd4 100644
Binary files a/test/image/baselines/geo_kavrayskiy7.png and b/test/image/baselines/geo_kavrayskiy7.png differ
diff --git a/test/image/baselines/geo_legendonly.png b/test/image/baselines/geo_legendonly.png
index 4eff55d0d65..d9595eaa5ec 100644
Binary files a/test/image/baselines/geo_legendonly.png and b/test/image/baselines/geo_legendonly.png differ
diff --git a/test/image/baselines/geo_miterlimit-base-layers.png b/test/image/baselines/geo_miterlimit-base-layers.png
new file mode 100644
index 00000000000..d14040b2c7c
Binary files /dev/null and b/test/image/baselines/geo_miterlimit-base-layers.png differ
diff --git a/test/image/baselines/geo_multi-geos.png b/test/image/baselines/geo_multi-geos.png
index 6f0e5b43a5e..de0366ab098 100644
Binary files a/test/image/baselines/geo_multi-geos.png and b/test/image/baselines/geo_multi-geos.png differ
diff --git a/test/image/baselines/geo_multiple-usa-choropleths.png b/test/image/baselines/geo_multiple-usa-choropleths.png
index f98da60820b..bfd37cca985 100644
Binary files a/test/image/baselines/geo_multiple-usa-choropleths.png and b/test/image/baselines/geo_multiple-usa-choropleths.png differ
diff --git a/test/image/baselines/geo_orthographic.png b/test/image/baselines/geo_orthographic.png
index 9cb6ffe315e..00ceee11086 100644
Binary files a/test/image/baselines/geo_orthographic.png and b/test/image/baselines/geo_orthographic.png differ
diff --git a/test/image/baselines/geo_scattergeo-locations.png b/test/image/baselines/geo_scattergeo-locations.png
index 6724ddebbac..0de614431f0 100644
Binary files a/test/image/baselines/geo_scattergeo-locations.png and b/test/image/baselines/geo_scattergeo-locations.png differ
diff --git a/test/image/baselines/geo_scattergeo-out-of-usa.png b/test/image/baselines/geo_scattergeo-out-of-usa.png
new file mode 100644
index 00000000000..aa8b27212b0
Binary files /dev/null and b/test/image/baselines/geo_scattergeo-out-of-usa.png differ
diff --git a/test/image/baselines/geo_second.png b/test/image/baselines/geo_second.png
index 7e7da69d10b..93c2dcb394d 100644
Binary files a/test/image/baselines/geo_second.png and b/test/image/baselines/geo_second.png differ
diff --git a/test/image/baselines/geo_stereographic.png b/test/image/baselines/geo_stereographic.png
index 026f06e0e77..a77239e0fd9 100644
Binary files a/test/image/baselines/geo_stereographic.png and b/test/image/baselines/geo_stereographic.png differ
diff --git a/test/image/baselines/geo_text_chart_arrays.png b/test/image/baselines/geo_text_chart_arrays.png
index fdd95f847b4..fc74c91f82e 100644
Binary files a/test/image/baselines/geo_text_chart_arrays.png and b/test/image/baselines/geo_text_chart_arrays.png differ
diff --git a/test/image/baselines/geo_usa-states.png b/test/image/baselines/geo_usa-states.png
index 06c5c681ea4..e162702a55a 100644
Binary files a/test/image/baselines/geo_usa-states.png and b/test/image/baselines/geo_usa-states.png differ
diff --git a/test/image/baselines/geo_winkel-tripel.png b/test/image/baselines/geo_winkel-tripel.png
index e0e864fdab9..7ad7d616bd6 100644
Binary files a/test/image/baselines/geo_winkel-tripel.png and b/test/image/baselines/geo_winkel-tripel.png differ
diff --git a/test/image/baselines/plot_types.png b/test/image/baselines/plot_types.png
index b143904d672..765645e7924 100644
Binary files a/test/image/baselines/plot_types.png and b/test/image/baselines/plot_types.png differ
diff --git a/test/image/mocks/geo_across-antimeridian.json b/test/image/mocks/geo_across-antimeridian.json
new file mode 100644
index 00000000000..22508f86e40
--- /dev/null
+++ b/test/image/mocks/geo_across-antimeridian.json
@@ -0,0 +1,103 @@
+{
+ "data": [
+ {
+ "type": "scattergeo",
+ "mode": "markers+lines",
+ "lon": [170, 180, -180, -170],
+ "lat": [-50, -45, -10, -20.5],
+ "marker": { "size": 20 }
+ },
+ {
+ "type": "choropleth",
+ "locations": ["NZL", "AUS"],
+ "z": [0, 1],
+ "showscale": false
+ },
+ {
+ "type": "scattergeo",
+ "mode": "markers+lines",
+ "lon": [-170, 170],
+ "lat": [-50, -20.5],
+ "marker": { "size": 20 },
+ "line": { "width": 5 },
+ "geo": "geo2",
+ "name": "west to east"
+ },
+ {
+ "type": "scattergeo",
+ "mode": "markers+lines",
+ "lon": [170, -170],
+ "lat": [-50, -20.5],
+ "marker": { "size": 20 },
+ "line": { "width": 5 },
+ "geo": "geo2",
+ "name": "east to west"
+ },
+ {
+ "type": "choropleth",
+ "locations": ["NZL", "AUS"],
+ "z": [0, 1],
+ "showscale": false,
+ "geo": "geo2"
+ }
+ ],
+ "layout": {
+ "geo": {
+ "domain": {
+ "x": [0, 0.5],
+ "y": [0, 1]
+ },
+ "type": "miller",
+ "showocean": true,
+ "lonaxis": {
+ "showgrid": true,
+ "dtick": 2,
+ "range": [120, -120]
+ },
+ "projection": {
+ "scale": 2
+ },
+ "center": {
+ "lat": -45
+ }
+ },
+ "geo2": {
+ "domain": {
+ "x": [0.5, 1],
+ "y": [0, 1]
+ },
+ "type": "miller",
+ "showocean": true,
+ "lonaxis": {
+ "showgrid": true,
+ "dtick": 2,
+ "range": [150, -150]
+ },
+ "lataxis": {
+ "range": [-90, 0]
+ }
+ },
+ "annotations": [{
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "x": 0.25,
+ "y": 1,
+ "xanchor": "center",
+ "yanchor": "bottom",
+ "text": "set lonaxis.range, center.lon and projection.scale"
+ }, {
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "x": 0.75,
+ "y": 1,
+ "xanchor": "center",
+ "yanchor": "bottom",
+ "text": "set lonaxis.range and lataxis.range"
+ }],
+ "showlegend": false,
+ "width": 700,
+ "height": 440
+ }
+}
diff --git a/test/image/mocks/geo_canadian-cites.json b/test/image/mocks/geo_canadian-cites.json
index bedc324a9d3..16f6ce6a611 100644
--- a/test/image/mocks/geo_canadian-cites.json
+++ b/test/image/mocks/geo_canadian-cites.json
@@ -95,6 +95,9 @@
70
]
},
+ "center": {
+ "lat": 57
+ },
"showrivers": true,
"rivercolor": "#fff",
"showlakes": true,
diff --git a/test/image/mocks/geo_centering.json b/test/image/mocks/geo_centering.json
new file mode 100644
index 00000000000..6bcae0b9ea9
--- /dev/null
+++ b/test/image/mocks/geo_centering.json
@@ -0,0 +1,41 @@
+{
+ "data": [{
+ "type": "scattergeo",
+ "mode": "markers",
+ "marker": {
+ "size": 9,
+ "color": "#154360"
+ },
+ "lat": [44.065041, 42.012902],
+ "lon": [-82.649583, -82.545763]
+ }],
+ "layout": {
+ "margin": {"t": 70, "b": 40, "l": 10, "r": 10},
+ "geo": {
+ "projection": {
+ "type": "kavrayskiy7",
+ "rotation":{"lon": -81.6},
+ "scale": 6
+ },
+ "center": {
+ "lat": 39
+ },
+ "showcountries": true,
+ "showland": true,
+ "showsubunits": true,
+ "lataxis": {
+ "showgrid": true,
+ "tick0": 3,
+ "dtick": 3
+ },
+ "lonaxis": {
+ "showgrid": true,
+ "tick0": 3,
+ "dtick": 3
+ },
+ "landcolor": "rgb(243,243,243)",
+ "countrycolor": "rgb(204,204,204)",
+ "subunitcolor": "rgb(224,224,224)"
+ }
+ }
+}
diff --git a/test/image/mocks/geo_fill.json b/test/image/mocks/geo_fill.json
index 12d749caca7..3dc3a388574 100644
--- a/test/image/mocks/geo_fill.json
+++ b/test/image/mocks/geo_fill.json
@@ -91,7 +91,8 @@
"layout": {
"geo": {
"projection": {
- "type": "natural earth"
+ "type": "natural earth",
+ "rotation": {"lon": 0}
},
"lonaxis": {
"range": [-80, -65]
@@ -99,6 +100,9 @@
"lataxis": {
"range": [42, 51]
},
+ "center": {
+ "lon": -72.5
+ },
"showland": true
},
"showlegend": false,
diff --git a/test/image/mocks/geo_miterlimit-base-layers.json b/test/image/mocks/geo_miterlimit-base-layers.json
new file mode 100644
index 00000000000..a90c9344323
--- /dev/null
+++ b/test/image/mocks/geo_miterlimit-base-layers.json
@@ -0,0 +1,32 @@
+{
+ "data": [
+ {
+ "type": "scattergeo",
+ "lon": [-72],
+ "lat": [40],
+ "marker": {
+ "size": 20,
+ "color": "#d3d3d3"
+ }
+ }
+ ],
+ "layout": {
+ "geo": {
+ "projection": {
+ "type": "orthographic",
+ "rotation": {
+ "lon": -72,
+ "lat": 40
+ },
+ "scale": 15
+ },
+ "showcoastlines": true,
+ "showcountries": true,
+ "showland": true,
+ "showocean": true,
+ "countrywidth": 8
+ },
+ "width": 500,
+ "height": 500
+ }
+}
diff --git a/test/image/mocks/geo_scattergeo-out-of-usa.json b/test/image/mocks/geo_scattergeo-out-of-usa.json
new file mode 100644
index 00000000000..3118256d18d
--- /dev/null
+++ b/test/image/mocks/geo_scattergeo-out-of-usa.json
@@ -0,0 +1,20070 @@
+{
+ "data": [
+ {
+ "uid": "c7d814",
+ "lon": [
+ -113,
+ -66,
+ -113,
+ -114,
+ -111,
+ -111,
+ -109,
+ -115,
+ -113,
+ -114,
+ -67,
+ -112,
+ -65,
+ -112,
+ -113,
+ -105,
+ -112,
+ -112,
+ -109,
+ -107,
+ -112,
+ -113,
+ -66,
+ -114,
+ -114,
+ -108,
+ -108,
+ -116,
+ -107,
+ -65,
+ -113,
+ -66,
+ -107,
+ -113,
+ -113,
+ -112,
+ -112,
+ -114,
+ -110,
+ -111,
+ -110,
+ -112,
+ -108,
+ -113,
+ -109,
+ -82,
+ -115,
+ -66,
+ -111,
+ -112,
+ -109,
+ -113,
+ -111,
+ -111,
+ -110,
+ -112,
+ -114,
+ -113,
+ -109,
+ -111,
+ -105,
+ -109,
+ -110,
+ -103,
+ -112,
+ -113,
+ -110,
+ -109,
+ -110,
+ -107,
+ -103,
+ -111,
+ -109,
+ -110,
+ -101,
+ -111,
+ -115,
+ -114,
+ -112,
+ -103,
+ -98,
+ -111,
+ -112,
+ -113,
+ -112,
+ -115,
+ -111,
+ -115,
+ -113,
+ -114,
+ -111,
+ -113,
+ -109,
+ -110,
+ -112,
+ -106,
+ -114,
+ -65,
+ -112,
+ -66,
+ -111,
+ -115,
+ -115,
+ -107,
+ -105,
+ -108,
+ -111,
+ -114,
+ -107,
+ -115,
+ -100,
+ -113,
+ -114,
+ -104,
+ -111,
+ -116,
+ -106,
+ -116,
+ -115,
+ -110,
+ -103,
+ -113,
+ -114,
+ -106,
+ -109,
+ -115,
+ -114,
+ -115,
+ -91,
+ -111,
+ -104,
+ -104,
+ -114,
+ -114,
+ -114,
+ -114,
+ -112,
+ -82,
+ -107,
+ -114,
+ -114,
+ -66,
+ -110,
+ -114,
+ -104,
+ -112,
+ -104,
+ -109,
+ -111,
+ -114,
+ -110,
+ -100,
+ -105,
+ -106,
+ -101,
+ -114,
+ -113,
+ -102,
+ -112,
+ -109,
+ -107,
+ -89,
+ -108,
+ -82,
+ -106,
+ -107,
+ -108,
+ -111,
+ -109,
+ -114,
+ -106,
+ -106,
+ -93,
+ -115,
+ -66,
+ -102,
+ -66,
+ -103,
+ -108,
+ -102,
+ -105,
+ -105,
+ -106,
+ -112,
+ -113,
+ -115,
+ -107,
+ -115,
+ -105,
+ -116,
+ -115,
+ -104,
+ -82,
+ -112,
+ -113,
+ -100,
+ -118,
+ -103,
+ -108,
+ -104,
+ -102,
+ -81,
+ -115,
+ -86,
+ -104,
+ -105,
+ -102,
+ -88,
+ -107,
+ -103,
+ -107,
+ -111,
+ -103,
+ -95,
+ -101,
+ -103,
+ -113,
+ -112,
+ -106,
+ -101,
+ -105,
+ -112,
+ -105,
+ -114,
+ -113,
+ -92,
+ -112,
+ -114,
+ -109,
+ -99,
+ -116,
+ -115,
+ -114,
+ -93,
+ -103,
+ -92,
+ -104,
+ -116,
+ -103,
+ -99,
+ -108,
+ -105,
+ -104,
+ -97,
+ -113,
+ -103,
+ -92,
+ -106,
+ -103,
+ -109,
+ -93,
+ -95,
+ -110,
+ -104,
+ -109,
+ -112,
+ -105,
+ -107,
+ -111,
+ -102,
+ -114,
+ -80,
+ -115,
+ -107,
+ -105,
+ -98,
+ -106,
+ -106,
+ -89,
+ -115,
+ -114,
+ -104,
+ -93,
+ -86,
+ -106,
+ -106,
+ -112,
+ -104,
+ -108,
+ -116,
+ -90,
+ -101,
+ -100,
+ -114,
+ -115,
+ -65,
+ -114,
+ -112,
+ -94,
+ -83,
+ -115,
+ -111,
+ -116,
+ -104,
+ -107,
+ -115,
+ -105,
+ -95,
+ -104,
+ -101,
+ -114,
+ -114,
+ -105,
+ -112,
+ -117,
+ -102,
+ -114,
+ -84,
+ -113,
+ -93,
+ -106,
+ -114,
+ -82,
+ -105,
+ -110,
+ -105,
+ -106,
+ -67,
+ -98,
+ -118,
+ -66,
+ -95,
+ -107,
+ -85,
+ -106,
+ -103,
+ -105,
+ -103,
+ -101,
+ -90,
+ -93,
+ -100,
+ -104,
+ -112,
+ -86,
+ -104,
+ -105,
+ -106,
+ -107,
+ -104,
+ -111,
+ -116,
+ -91,
+ -98,
+ -85,
+ -104,
+ -101,
+ -88,
+ -96,
+ -85,
+ -66,
+ -108,
+ -104,
+ -102,
+ -96,
+ -100,
+ -82,
+ -109,
+ -113,
+ -111,
+ -97,
+ -104,
+ -99,
+ -105,
+ -96,
+ -95,
+ -103,
+ -107,
+ -105,
+ -117,
+ -115,
+ -81,
+ -106,
+ -102,
+ -82,
+ -110,
+ -66,
+ -105,
+ -106,
+ -107,
+ -105,
+ -83,
+ -107,
+ -92,
+ -100,
+ -85,
+ -108,
+ -104,
+ -87,
+ -81,
+ -104,
+ -103,
+ -86,
+ -100,
+ -107,
+ -102,
+ -115,
+ -91,
+ -98,
+ -104,
+ -81,
+ -104,
+ -115,
+ -116,
+ -90,
+ -100,
+ -114,
+ -107,
+ -106,
+ -101,
+ -100,
+ -114,
+ -103,
+ -98,
+ -104,
+ -96,
+ -103,
+ -102,
+ -119,
+ -86,
+ -105,
+ -86,
+ -83,
+ -91,
+ -108,
+ -103,
+ -115,
+ -104,
+ -105,
+ -104,
+ -84,
+ -95,
+ -106,
+ -101,
+ -102,
+ -101,
+ -102,
+ -87,
+ -91,
+ -114,
+ -104,
+ -92,
+ -106,
+ -106,
+ -97,
+ -105,
+ -115,
+ -98,
+ -101,
+ -108,
+ -101,
+ -117,
+ -86,
+ -67,
+ -103,
+ -98,
+ -81,
+ -103,
+ -84,
+ -98,
+ -98,
+ -91,
+ -89,
+ -100,
+ -88,
+ -104,
+ -109,
+ -83,
+ -104,
+ -82,
+ -109,
+ -94,
+ -101,
+ -95,
+ -88,
+ -101,
+ -101,
+ -100,
+ -101,
+ -99,
+ -109,
+ -95,
+ -105,
+ -109,
+ -86,
+ -66,
+ -102,
+ -104,
+ -101,
+ -84,
+ -94,
+ -102,
+ -100,
+ -85,
+ -100,
+ -98,
+ -95,
+ -105,
+ -105,
+ -101,
+ -113,
+ -105,
+ -116,
+ -85,
+ -103,
+ -114,
+ -95,
+ -114,
+ -103,
+ -102,
+ -82,
+ -101,
+ -89,
+ -100,
+ -97,
+ -84,
+ -87,
+ -97,
+ -102,
+ -95,
+ -101,
+ -97,
+ -107,
+ -103,
+ -105,
+ -94,
+ -104,
+ -108,
+ -84,
+ -97,
+ -112,
+ -106,
+ -106,
+ -93,
+ -105,
+ -84,
+ -100,
+ -100,
+ -103,
+ -97,
+ -89,
+ -94,
+ -106,
+ -93,
+ -101,
+ -101,
+ -100,
+ -113,
+ -104,
+ -102,
+ -101,
+ -102,
+ -97,
+ -100,
+ -102,
+ -97,
+ -106,
+ -101,
+ -82,
+ -118,
+ -100,
+ -87,
+ -84,
+ -115,
+ -107,
+ -106,
+ -100,
+ -109,
+ -97,
+ -85,
+ -104,
+ -103,
+ -100,
+ -100,
+ -100,
+ -102,
+ -90,
+ -102,
+ -93,
+ -83,
+ -89,
+ -115,
+ -113,
+ -102,
+ -100,
+ -98,
+ -105,
+ -117,
+ -106,
+ -99,
+ -99,
+ -105,
+ -85,
+ -107,
+ -85,
+ -105,
+ -99,
+ -96,
+ -115,
+ -104,
+ -108,
+ -83,
+ -105,
+ -98,
+ -100,
+ -114,
+ -85,
+ -83,
+ -87,
+ -102,
+ -103,
+ -114,
+ -103,
+ -102,
+ -100,
+ -103,
+ -116,
+ -100,
+ -101,
+ -117,
+ -105,
+ -100,
+ -103,
+ -100,
+ -104,
+ -107,
+ -93,
+ -103,
+ -116,
+ -93,
+ -97,
+ -85,
+ -103,
+ -101,
+ -85,
+ -88,
+ -114,
+ -83,
+ -81,
+ -104,
+ -84,
+ -98,
+ -104,
+ -84,
+ -103,
+ -105,
+ -80,
+ -102,
+ -81,
+ -82,
+ -98,
+ -94,
+ -102,
+ -93,
+ -100,
+ -100,
+ -100,
+ -90,
+ -100,
+ -86,
+ -101,
+ -92,
+ -86,
+ -109,
+ -106,
+ -107,
+ -99,
+ -103,
+ -86,
+ -102,
+ -107,
+ -108,
+ -101,
+ -101,
+ -84,
+ -89,
+ -96,
+ -113,
+ -95,
+ -101,
+ -102,
+ -105,
+ -103,
+ -82,
+ -100,
+ -84,
+ -83,
+ -82,
+ -100,
+ -100,
+ -102,
+ -88,
+ -95,
+ -102,
+ -101,
+ -84,
+ -107,
+ -105,
+ -104,
+ -101,
+ -104,
+ -98,
+ -93,
+ -86,
+ -95,
+ -103,
+ -83,
+ -97,
+ -102,
+ -99,
+ -108,
+ -116,
+ -103,
+ -89,
+ -117,
+ -117,
+ -83,
+ -96,
+ -100,
+ -100,
+ -90,
+ -100,
+ -87,
+ -100,
+ -83,
+ -104,
+ -106,
+ -104,
+ -99,
+ -84,
+ -103,
+ -88,
+ -112,
+ -101,
+ -100,
+ -110,
+ -98,
+ -82,
+ -86,
+ -102,
+ -105,
+ -104,
+ -94,
+ -83,
+ -95,
+ -100,
+ -84,
+ -102,
+ -103,
+ -104,
+ -114,
+ -104,
+ -100,
+ -103,
+ -81,
+ -103,
+ -107,
+ -91,
+ -103,
+ -97,
+ -104,
+ -104,
+ -103,
+ -84,
+ -102,
+ -106,
+ -98,
+ -94,
+ -99,
+ -96,
+ -116,
+ -102,
+ -84,
+ -101,
+ -94,
+ -82,
+ -84,
+ -106,
+ -99,
+ -97,
+ -109,
+ -106,
+ -99,
+ -94,
+ -113,
+ -87,
+ -89,
+ -97,
+ -106,
+ -87,
+ -105,
+ -104,
+ -101,
+ -83,
+ -91,
+ -90,
+ -101,
+ -106,
+ -92,
+ -104,
+ -102,
+ -85,
+ -90,
+ -104,
+ -98,
+ -95,
+ -101,
+ -101,
+ -99,
+ -94,
+ -82,
+ -99,
+ -84,
+ -90,
+ -94,
+ -98,
+ -96,
+ -100,
+ -85,
+ -114,
+ -83,
+ -102,
+ -83,
+ -84,
+ -91,
+ -118,
+ -83,
+ -100,
+ -97,
+ -101,
+ -85,
+ -93,
+ -97,
+ -109,
+ -100,
+ -105,
+ -91,
+ -88,
+ -98,
+ -91,
+ -86,
+ -98,
+ -84,
+ -98,
+ -85,
+ -104,
+ -87,
+ -102,
+ -85,
+ -91,
+ -85,
+ -87,
+ -114,
+ -101,
+ -105,
+ -104,
+ -100,
+ -114,
+ -112,
+ -106,
+ -99,
+ -83,
+ -100,
+ -97,
+ -86,
+ -104,
+ -95,
+ -82,
+ -98,
+ -116,
+ -102,
+ -109,
+ -101,
+ -101,
+ -86,
+ -101,
+ -99,
+ -102,
+ -84,
+ -115,
+ -90,
+ -96,
+ -82,
+ -103,
+ -90,
+ -100,
+ -94,
+ -98,
+ -95,
+ -84,
+ -114,
+ -99,
+ -101,
+ -115,
+ -102,
+ -84,
+ -102,
+ -81,
+ -82,
+ -93,
+ -104,
+ -106,
+ -105,
+ -100,
+ -86,
+ -99,
+ -88,
+ -86,
+ -103,
+ -101,
+ -89,
+ -98,
+ -91,
+ -88,
+ -100,
+ -103,
+ -107,
+ -100,
+ -96,
+ -100,
+ -101,
+ -105,
+ -102,
+ -85,
+ -83,
+ -96,
+ -100,
+ -103,
+ -106,
+ -89,
+ -111,
+ -98,
+ -89,
+ -82,
+ -104,
+ -103,
+ -96,
+ -100,
+ -103,
+ -102,
+ -99,
+ -100,
+ -104,
+ -93,
+ -95,
+ -103,
+ -104,
+ -83,
+ -86,
+ -99,
+ -99,
+ -81,
+ -101,
+ -116,
+ -101,
+ -113,
+ -106,
+ -106,
+ -86,
+ -100,
+ -97,
+ -83,
+ -114,
+ -83,
+ -83,
+ -84,
+ -101,
+ -102,
+ -99,
+ -83,
+ -103,
+ -85,
+ -92,
+ -88,
+ -100,
+ -101,
+ -99,
+ -97,
+ -88,
+ -99,
+ -94,
+ -96,
+ -107,
+ -94,
+ -102,
+ -104,
+ -86,
+ -104,
+ -86,
+ -102,
+ -84,
+ -93,
+ -96,
+ -90,
+ -87,
+ -87,
+ -104,
+ -105,
+ -96,
+ -104,
+ -103,
+ -83,
+ -100,
+ -88,
+ -102,
+ -105,
+ -89,
+ -84,
+ -105,
+ -103,
+ -85,
+ -83,
+ -98,
+ -97,
+ -100,
+ -100,
+ -97,
+ -100,
+ -100,
+ -97,
+ -102,
+ -85,
+ -104,
+ -66,
+ -100,
+ -104,
+ -96,
+ -85,
+ -101,
+ -103,
+ -82,
+ -101,
+ -87,
+ -95,
+ -92,
+ -97,
+ -100,
+ -103,
+ -86,
+ -99,
+ -100,
+ -94,
+ -91,
+ -98,
+ -99,
+ -93,
+ -85,
+ -102,
+ -100,
+ -112,
+ -102,
+ -105,
+ -99,
+ -114,
+ -95,
+ -94,
+ -83,
+ -99,
+ -104,
+ -84,
+ -97,
+ -84,
+ -93,
+ -101,
+ -100,
+ -91,
+ -82,
+ -103,
+ -83,
+ -98,
+ -100,
+ -104,
+ -100,
+ -101,
+ -96,
+ -87,
+ -81,
+ -95,
+ -98,
+ -104,
+ -91,
+ -89,
+ -88,
+ -101,
+ -87,
+ -98,
+ -96,
+ -102,
+ -86,
+ -90,
+ -98,
+ -90,
+ -114,
+ -91,
+ -102,
+ -84,
+ -99,
+ -111,
+ -84,
+ -98,
+ -102,
+ -100,
+ -86,
+ -85,
+ -101,
+ -106,
+ -101,
+ -84,
+ -85,
+ -99,
+ -100,
+ -90,
+ -85,
+ -83,
+ -80,
+ -105,
+ -96,
+ -83,
+ -93,
+ -104,
+ -103,
+ -83,
+ -100,
+ -102,
+ -90,
+ -84,
+ -105,
+ -101,
+ -96,
+ -95,
+ -101,
+ -93,
+ -92,
+ -103,
+ -99,
+ -83,
+ -99,
+ -89,
+ -96,
+ -95,
+ -66,
+ -85,
+ -90,
+ -83,
+ -95,
+ -103,
+ -85,
+ -101,
+ -101,
+ -91,
+ -96,
+ -100,
+ -96,
+ -105,
+ -96,
+ -105,
+ -97,
+ -84,
+ -86,
+ -91,
+ -88,
+ -89,
+ -112,
+ -104,
+ -87,
+ -94,
+ -84,
+ -94,
+ -92,
+ -86,
+ -101,
+ -103,
+ -94,
+ -99,
+ -85,
+ -114,
+ -97,
+ -97,
+ -96,
+ -100,
+ -94,
+ -96,
+ -93,
+ -83,
+ -87,
+ -88,
+ -95,
+ -102,
+ -113,
+ -101,
+ -99,
+ -107,
+ -100,
+ -95,
+ -103,
+ -101,
+ -100,
+ -105,
+ -100,
+ -96,
+ -97,
+ -117,
+ -89,
+ -91,
+ -104,
+ -98,
+ -99,
+ -103,
+ -99,
+ -104,
+ -104,
+ -98,
+ -84,
+ -101,
+ -91,
+ -95,
+ -93,
+ -86,
+ -85,
+ -105,
+ -84,
+ -83,
+ -103,
+ -101,
+ -100,
+ -84,
+ -97,
+ -86,
+ -88,
+ -84,
+ -96,
+ -114,
+ -100,
+ -99,
+ -95,
+ -100,
+ -84,
+ -88,
+ -102,
+ -102,
+ -83,
+ -97,
+ -87,
+ -104,
+ -93,
+ -104,
+ -106,
+ -80,
+ -84,
+ -104,
+ -99,
+ -85,
+ -115,
+ -115,
+ -101,
+ -85,
+ -97,
+ -99,
+ -99,
+ -115,
+ -101,
+ -85,
+ -94,
+ -84,
+ -98,
+ -107,
+ -96,
+ -101,
+ -100,
+ -104,
+ -101,
+ -102,
+ -102,
+ -99,
+ -85,
+ -98,
+ -104,
+ -105,
+ -105,
+ -85,
+ -97,
+ -115,
+ -99,
+ -99,
+ -101,
+ -97,
+ -100,
+ -105,
+ -84,
+ -86,
+ -87,
+ -89,
+ -93,
+ -83,
+ -85,
+ -101,
+ -99,
+ -101,
+ -100,
+ -85,
+ -90,
+ -84,
+ -96,
+ -103,
+ -89,
+ -98,
+ -86,
+ -102,
+ -87,
+ -100,
+ -90,
+ -91,
+ -101,
+ -114,
+ -104,
+ -104,
+ -97,
+ -83,
+ -96,
+ -98,
+ -83,
+ -91,
+ -98,
+ -86,
+ -103,
+ -100,
+ -88,
+ -85,
+ -89,
+ -112,
+ -99,
+ -113,
+ -84,
+ -100,
+ -102,
+ -88,
+ -84,
+ -106,
+ -96,
+ -94,
+ -108,
+ -113,
+ -85,
+ -103,
+ -84,
+ -81,
+ -103,
+ -92,
+ -101,
+ -84,
+ -91,
+ -100,
+ -90,
+ -79,
+ -81,
+ -88,
+ -97,
+ -104,
+ -84,
+ -98,
+ -86,
+ -97,
+ -86,
+ -101,
+ -92,
+ -105,
+ -97,
+ -87,
+ -97,
+ -97,
+ -84,
+ -84,
+ -113,
+ -115,
+ -102,
+ -104,
+ -100,
+ -103,
+ -101,
+ -88,
+ -87,
+ -101,
+ -103,
+ -106,
+ -113,
+ -95,
+ -101,
+ -99,
+ -98,
+ -99,
+ -100,
+ -88,
+ -97,
+ -103,
+ -95,
+ -103,
+ -85,
+ -106,
+ -90,
+ -97,
+ -87,
+ -108,
+ -100,
+ -83,
+ -81,
+ -101,
+ -97,
+ -104,
+ -85,
+ -105,
+ -97,
+ -92,
+ -84,
+ -104,
+ -89,
+ -113,
+ -90,
+ -103,
+ -105,
+ -100,
+ -85,
+ -93,
+ -103,
+ -88,
+ -81,
+ -97,
+ -102,
+ -98,
+ -88,
+ -108,
+ -92,
+ -116,
+ -98,
+ -116,
+ -97,
+ -91,
+ -101,
+ -97,
+ -99,
+ -88,
+ -90,
+ -91,
+ -100,
+ -92,
+ -95,
+ -86,
+ -96,
+ -88,
+ -99,
+ -100,
+ -85,
+ -87,
+ -98,
+ -88,
+ -99,
+ -99,
+ -87,
+ -103,
+ -84,
+ -90,
+ -87,
+ -100,
+ -99,
+ -96,
+ -100,
+ -99,
+ -115,
+ -107,
+ -114,
+ -91,
+ -85,
+ -105,
+ -100,
+ -99,
+ -82,
+ -87,
+ -84,
+ -100,
+ -92,
+ -96,
+ -83,
+ -104,
+ -113,
+ -102,
+ -95,
+ -102,
+ -115,
+ -84,
+ -93,
+ -96,
+ -116,
+ -94,
+ -111,
+ -103,
+ -104,
+ -97,
+ -112,
+ -102,
+ -101,
+ -89,
+ -82,
+ -84,
+ -88,
+ -103,
+ -84,
+ -87,
+ -84,
+ -89,
+ -97,
+ -96,
+ -90,
+ -97,
+ -89,
+ -100,
+ -115,
+ -102,
+ -88,
+ -91,
+ -90,
+ -96,
+ -104,
+ -100,
+ -84,
+ -83,
+ -94,
+ -98,
+ -97,
+ -83,
+ -87,
+ -97,
+ -96,
+ -106,
+ -98,
+ -97,
+ -87,
+ -82,
+ -97,
+ -103,
+ -101,
+ -101,
+ -104,
+ -113,
+ -84,
+ -101,
+ -98,
+ -98,
+ -100,
+ -90,
+ -87,
+ -100,
+ -91,
+ -93,
+ -90,
+ -97,
+ -95,
+ -102,
+ -113,
+ -89,
+ -84,
+ -106,
+ -95,
+ -115,
+ -103,
+ -92,
+ -89,
+ -98,
+ -84,
+ -105,
+ -101,
+ -105,
+ -85,
+ -103,
+ -100,
+ -85,
+ -91,
+ -85,
+ -98,
+ -97,
+ -106,
+ -100,
+ -100,
+ -89,
+ -83,
+ -94,
+ -103,
+ -94,
+ -87,
+ -86,
+ -93,
+ -86,
+ -97,
+ -85,
+ -112,
+ -100,
+ -114,
+ -101,
+ -99,
+ -98,
+ -92,
+ -100,
+ -104,
+ -87,
+ -106,
+ -100,
+ -86,
+ -82,
+ -86,
+ -101,
+ -101,
+ -90,
+ -85,
+ -101,
+ -99,
+ -98,
+ -99,
+ -92,
+ -102,
+ -101,
+ -115,
+ -100,
+ -102,
+ -89,
+ -100,
+ -114,
+ -88,
+ -84,
+ -101,
+ -86,
+ -85,
+ -85,
+ -101,
+ -90,
+ -99,
+ -92,
+ -87,
+ -96,
+ -91,
+ -102,
+ -104,
+ -82,
+ -87,
+ -85,
+ -97,
+ -98,
+ -92,
+ -93,
+ -108,
+ -85,
+ -103,
+ -85,
+ -102,
+ -99,
+ -86,
+ -98,
+ -103,
+ -82,
+ -102,
+ -100,
+ -104,
+ -102,
+ -99,
+ -85,
+ -104,
+ -99,
+ -80,
+ -84,
+ -100,
+ -99,
+ -87,
+ -100,
+ -84,
+ -87,
+ -84,
+ -105,
+ -101,
+ -102,
+ -84,
+ -96,
+ -92,
+ -84,
+ -86,
+ -90,
+ -91,
+ -85,
+ -106,
+ -88,
+ -85,
+ -101,
+ -85,
+ -99,
+ -98,
+ -102,
+ -92,
+ -99,
+ -97,
+ -84,
+ -83,
+ -102,
+ -101,
+ -100,
+ -99,
+ -99,
+ -95,
+ -103,
+ -92,
+ -85,
+ -99,
+ -104,
+ -92,
+ -83,
+ -80,
+ -98,
+ -85,
+ -86,
+ -97,
+ -94,
+ -98,
+ -85,
+ -105,
+ -98,
+ -87,
+ -97,
+ -85,
+ -114,
+ -94,
+ -98,
+ -86,
+ -108,
+ -86,
+ -96,
+ -98,
+ -104,
+ -94,
+ -104,
+ -98,
+ -83,
+ -93,
+ -98,
+ -96,
+ -99,
+ -99,
+ -97,
+ -91,
+ -87,
+ -102,
+ -103,
+ -101,
+ -99,
+ -89,
+ -83,
+ -101,
+ -82,
+ -103,
+ -87,
+ -95,
+ -100,
+ -85,
+ -101,
+ -103,
+ -103,
+ -91,
+ -83,
+ -99,
+ -83,
+ -96,
+ -82,
+ -101,
+ -83,
+ -109,
+ -82,
+ -98,
+ -88,
+ -81,
+ -114,
+ -103,
+ -98,
+ -105,
+ -106,
+ -81,
+ -90,
+ -103,
+ -87,
+ -82,
+ -96,
+ -82,
+ -93,
+ -95,
+ -82,
+ -100,
+ -100,
+ -96,
+ -89,
+ -95,
+ -94,
+ -97,
+ -84,
+ -99,
+ -102,
+ -87,
+ -100,
+ -111,
+ -88,
+ -99,
+ -100,
+ -100,
+ -94,
+ -83,
+ -96,
+ -93,
+ -101,
+ -91,
+ -101,
+ -100,
+ -98,
+ -101,
+ -90,
+ -100,
+ -116,
+ -85,
+ -98,
+ -84,
+ -96,
+ -87,
+ -101,
+ -101,
+ -114,
+ -94,
+ -95,
+ -99,
+ -102,
+ -82,
+ -85,
+ -89,
+ -84,
+ -112,
+ -97,
+ -96,
+ -97,
+ -95,
+ -103,
+ -101,
+ -86,
+ -100,
+ -83,
+ -83,
+ -98,
+ -85,
+ -99,
+ -103,
+ -91,
+ -98,
+ -87,
+ -102,
+ -105,
+ -84,
+ -87,
+ -93,
+ -83,
+ -97,
+ -82,
+ -84,
+ -90,
+ -91,
+ -98,
+ -100,
+ -97,
+ -91,
+ -87,
+ -102,
+ -93,
+ -100,
+ -99,
+ -98,
+ -103,
+ -92,
+ -101,
+ -82,
+ -86,
+ -104,
+ -98,
+ -106,
+ -85,
+ -102,
+ -98,
+ -89,
+ -104,
+ -81,
+ -100,
+ -89,
+ -91,
+ -112,
+ -101,
+ -100,
+ -89,
+ -94,
+ -93,
+ -105,
+ -91,
+ -83,
+ -88,
+ -99,
+ -98,
+ -104,
+ -91,
+ -89,
+ -90,
+ -85,
+ -98,
+ -115,
+ -101,
+ -99,
+ -87,
+ -83,
+ -95,
+ -87,
+ -88,
+ -93,
+ -85,
+ -116,
+ -102,
+ -98,
+ -86,
+ -97,
+ -99,
+ -87,
+ -115,
+ -91,
+ -115,
+ -103,
+ -93,
+ -97,
+ -97,
+ -96,
+ -114,
+ -105,
+ -91,
+ -85,
+ -82,
+ -101,
+ -80,
+ -84,
+ -86,
+ -88,
+ -99,
+ -93,
+ -115,
+ -95,
+ -94,
+ -90,
+ -98,
+ -94,
+ -102,
+ -96,
+ -86,
+ -82,
+ -85,
+ -102,
+ -89,
+ -102,
+ -88,
+ -115,
+ -100,
+ -87,
+ -97,
+ -103,
+ -89,
+ -85,
+ -96,
+ -102,
+ -100,
+ -96,
+ -84,
+ -94,
+ -94,
+ -103,
+ -98,
+ -100,
+ -85,
+ -99,
+ -100,
+ -94,
+ -91,
+ -97,
+ -82,
+ -105,
+ -83,
+ -85,
+ -86,
+ -97,
+ -85,
+ -103,
+ -80,
+ -96,
+ -87,
+ -82,
+ -100,
+ -95,
+ -96,
+ -102,
+ -94,
+ -100,
+ -106,
+ -104,
+ -86,
+ -90,
+ -86,
+ -102,
+ -89,
+ -88,
+ -87,
+ -88,
+ -83,
+ -84,
+ -87,
+ -99,
+ -94,
+ -90,
+ -84,
+ -94,
+ -103,
+ -83,
+ -89,
+ -90,
+ -89,
+ -104,
+ -89,
+ -88,
+ -101,
+ -85,
+ -85,
+ -91,
+ -84,
+ -98,
+ -112,
+ -116,
+ -115,
+ -98,
+ -103,
+ -93,
+ -94,
+ -100,
+ -83,
+ -99,
+ -116,
+ -95,
+ -91,
+ -88,
+ -114,
+ -83,
+ -89,
+ -84,
+ -101,
+ -102,
+ -83,
+ -98,
+ -95,
+ -81,
+ -93,
+ -85,
+ -95,
+ -101,
+ -98,
+ -100,
+ -102,
+ -100,
+ -104,
+ -80,
+ -97,
+ -113,
+ -98,
+ -87,
+ -102,
+ -99,
+ -98,
+ -104,
+ -98,
+ -99,
+ -101,
+ -99,
+ -102,
+ -99,
+ -99,
+ -85,
+ -92,
+ -80,
+ -105,
+ -86,
+ -95,
+ -85,
+ -104,
+ -102,
+ -81,
+ -103,
+ -116,
+ -93,
+ -99,
+ -82,
+ -98,
+ -86,
+ -97,
+ -105,
+ -84,
+ -95,
+ -97,
+ -83,
+ -104,
+ -97,
+ -94,
+ -115,
+ -101,
+ -98,
+ -88,
+ -84,
+ -95,
+ -95,
+ -83,
+ -101,
+ -103,
+ -88,
+ -89,
+ -85,
+ -97,
+ -103,
+ -103,
+ -101,
+ -100,
+ -103,
+ -84,
+ -99,
+ -103,
+ -103,
+ -90,
+ -99,
+ -84,
+ -90,
+ -83,
+ -86,
+ -96,
+ -96,
+ -93,
+ -83,
+ -94,
+ -89,
+ -95,
+ -93,
+ -104,
+ -89,
+ -102,
+ -103,
+ -101,
+ -116,
+ -82,
+ -98,
+ -83,
+ -99,
+ -101,
+ -89,
+ -85,
+ -97,
+ -114,
+ -101,
+ -101,
+ -104,
+ -96,
+ -99,
+ -101,
+ -91,
+ -99,
+ -99,
+ -99,
+ -82,
+ -113,
+ -102,
+ -116,
+ -82,
+ -99,
+ -97,
+ -83,
+ -100,
+ -86,
+ -90,
+ -81,
+ -84,
+ -100,
+ -98,
+ -105,
+ -81,
+ -84,
+ -101,
+ -112,
+ -89,
+ -100,
+ -96,
+ -87,
+ -97,
+ -86,
+ -100,
+ -103,
+ -99,
+ -101,
+ -85,
+ -100,
+ -88,
+ -103,
+ -91,
+ -84,
+ -92,
+ -93,
+ -104,
+ -81,
+ -81,
+ -87,
+ -100,
+ -100,
+ -84,
+ -98,
+ -86,
+ -102,
+ -97,
+ -110,
+ -90,
+ -106,
+ -105,
+ -101,
+ -83,
+ -83,
+ -108,
+ -93,
+ -98,
+ -91,
+ -83,
+ -86,
+ -101,
+ -83,
+ -99,
+ -104,
+ -97,
+ -98,
+ -104,
+ -98,
+ -106,
+ -95,
+ -84,
+ -101,
+ -104,
+ -82,
+ -104,
+ -97,
+ -113,
+ -97,
+ -83,
+ -95,
+ -95,
+ -89,
+ -96,
+ -86,
+ -90,
+ -98,
+ -83,
+ -99,
+ -102,
+ -98,
+ -106,
+ -89,
+ -97,
+ -101,
+ -99,
+ -95,
+ -99,
+ -95,
+ -96,
+ -95,
+ -95,
+ -113,
+ -84,
+ -83,
+ -115,
+ -110,
+ -98,
+ -104,
+ -98,
+ -96,
+ -82,
+ -108,
+ -100,
+ -93,
+ -100,
+ -92,
+ -96,
+ -82,
+ -103,
+ -100,
+ -99,
+ -100,
+ -84,
+ -101,
+ -84,
+ -85,
+ -85,
+ -86,
+ -101,
+ -85,
+ -99,
+ -115,
+ -86,
+ -100,
+ -86,
+ -101,
+ -100,
+ -85,
+ -90,
+ -91,
+ -102,
+ -92,
+ -82,
+ -100,
+ -84,
+ -102,
+ -99,
+ -95,
+ -100,
+ -100,
+ -93,
+ -101,
+ -99,
+ -103,
+ -99,
+ -90,
+ -87,
+ -82,
+ -115,
+ -95,
+ -85,
+ -99,
+ -101,
+ -96,
+ -85,
+ -85,
+ -90,
+ -98,
+ -90,
+ -82,
+ -87,
+ -99,
+ -101,
+ -81,
+ -83,
+ -99,
+ -104,
+ -102,
+ -92,
+ -99,
+ -100,
+ -100,
+ -93,
+ -103,
+ -101,
+ -96,
+ -98,
+ -104,
+ -89,
+ -83,
+ -84,
+ -85,
+ -85,
+ -82,
+ -84,
+ -93,
+ -88,
+ -97,
+ -101,
+ -98,
+ -94,
+ -95,
+ -99,
+ -99,
+ -99,
+ -94,
+ -87,
+ -82,
+ -86,
+ -98,
+ -98,
+ -84,
+ -84,
+ -105,
+ -86,
+ -100,
+ -102,
+ -92,
+ -104,
+ -96,
+ -90,
+ -86,
+ -89,
+ -97,
+ -85,
+ -85,
+ -103,
+ -95,
+ -89,
+ -87,
+ -101,
+ -86,
+ -93,
+ -98,
+ -85,
+ -94,
+ -103,
+ -87,
+ -83,
+ -95,
+ -91,
+ -100,
+ -85,
+ -87,
+ -81,
+ -96,
+ -96,
+ -85,
+ -112,
+ -85,
+ -113,
+ -93,
+ -91,
+ -102,
+ -97,
+ -92,
+ -91,
+ -86,
+ -89,
+ -99,
+ -95,
+ -104,
+ -92,
+ -99,
+ -101,
+ -84,
+ -102,
+ -85,
+ -99,
+ -98,
+ -87,
+ -79,
+ -95,
+ -101,
+ -84,
+ -82,
+ -92,
+ -94,
+ -98,
+ -95,
+ -95,
+ -100,
+ -100,
+ -102,
+ -99,
+ -101,
+ -90,
+ -101,
+ -84,
+ -87,
+ -99,
+ -96,
+ -99,
+ -97,
+ -93,
+ -101,
+ -84,
+ -105,
+ -95,
+ -99,
+ -86,
+ -91,
+ -101,
+ -94,
+ -81,
+ -91,
+ -95,
+ -86,
+ -98,
+ -104,
+ -102,
+ -96,
+ -86,
+ -96,
+ -80,
+ -95,
+ -97,
+ -101,
+ -87,
+ -90,
+ -104,
+ -102,
+ -95,
+ -98,
+ -111,
+ -88,
+ -100,
+ -93,
+ -100,
+ -101,
+ -89,
+ -86,
+ -101,
+ -86,
+ -104,
+ -99,
+ -104,
+ -86,
+ -84,
+ -85,
+ -100,
+ -84,
+ -103,
+ -98,
+ -85,
+ -86,
+ -89,
+ -85,
+ -101,
+ -106,
+ -102,
+ -96,
+ -85,
+ -101,
+ -114,
+ -96,
+ -102,
+ -104,
+ -104,
+ -86,
+ -90,
+ -92,
+ -89,
+ -102,
+ -104,
+ -95,
+ -90,
+ -100,
+ -99,
+ -103,
+ -103,
+ -83,
+ -85,
+ -97,
+ -96,
+ -81,
+ -102,
+ -86,
+ -99,
+ -93,
+ -104,
+ -88,
+ -88,
+ -99,
+ -103,
+ -91,
+ -90,
+ -96,
+ -95,
+ -113,
+ -82,
+ -89,
+ -91,
+ -99,
+ -99,
+ -86,
+ -86,
+ -97,
+ -87,
+ -91,
+ -101,
+ -102,
+ -105,
+ -97,
+ -104,
+ -84,
+ -104,
+ -91,
+ -104,
+ -98,
+ -104,
+ -114,
+ -83,
+ -83,
+ -102,
+ -102,
+ -100,
+ -96,
+ -88,
+ -87,
+ -82,
+ -91,
+ -97,
+ -101,
+ -113,
+ -102,
+ -103,
+ -85,
+ -101,
+ -103,
+ -92,
+ -90,
+ -101,
+ -99,
+ -92,
+ -85,
+ -86,
+ -102,
+ -99,
+ -95,
+ -84,
+ -87,
+ -102,
+ -104,
+ -91,
+ -101,
+ -103,
+ -88,
+ -100,
+ -84,
+ -99,
+ -100,
+ -85,
+ -88,
+ -97,
+ -102,
+ -96,
+ -99,
+ -96,
+ -90,
+ -83,
+ -103,
+ -98,
+ -103,
+ -85,
+ -99,
+ -84,
+ -93,
+ -85,
+ -87,
+ -83,
+ -95,
+ -87,
+ -85,
+ -94,
+ -97,
+ -100,
+ -81,
+ -105,
+ -99,
+ -102,
+ -104,
+ -99,
+ -100,
+ -91,
+ -88,
+ -83,
+ -85,
+ -86,
+ -93,
+ -95,
+ -81,
+ -89,
+ -90,
+ -84,
+ -92,
+ -88,
+ -84,
+ -98,
+ -83,
+ -87,
+ -84,
+ -81,
+ -85,
+ -96,
+ -100,
+ -94,
+ -98,
+ -95,
+ -90,
+ -87,
+ -94,
+ -93,
+ -98,
+ -86,
+ -96,
+ -99,
+ -98,
+ -102,
+ -85,
+ -86,
+ -83,
+ -94,
+ -85,
+ -91,
+ -101,
+ -92,
+ -86,
+ -97,
+ -95,
+ -103,
+ -99,
+ -95,
+ -113,
+ -89,
+ -86,
+ -100,
+ -89,
+ -88,
+ -93,
+ -91,
+ -106,
+ -103,
+ -89,
+ -94,
+ -95,
+ -89,
+ -89,
+ -99,
+ -103,
+ -103,
+ -84,
+ -102,
+ -87,
+ -82,
+ -84,
+ -95,
+ -100,
+ -91,
+ -100,
+ -100,
+ -98,
+ -114,
+ -101,
+ -99,
+ -99,
+ -100,
+ -92,
+ -84,
+ -92,
+ -99,
+ -101,
+ -86,
+ -83,
+ -89,
+ -85,
+ -97,
+ -116,
+ -84,
+ -93,
+ -113,
+ -98,
+ -97,
+ -86,
+ -101,
+ -87,
+ -93,
+ -101,
+ -95,
+ -97,
+ -100,
+ -98,
+ -101,
+ -101,
+ -102,
+ -84,
+ -102,
+ -84,
+ -83,
+ -95,
+ -99,
+ -84,
+ -86,
+ -89,
+ -115,
+ -85,
+ -112,
+ -114,
+ -99,
+ -93,
+ -81,
+ -84,
+ -107,
+ -103,
+ -103,
+ -101,
+ -88,
+ -100,
+ -102,
+ -96,
+ -92,
+ -86,
+ -94,
+ -91,
+ -105,
+ -103,
+ -85,
+ -96,
+ -113,
+ -111,
+ -81,
+ -84,
+ -104,
+ -96,
+ -85,
+ -99,
+ -103,
+ -83,
+ -89,
+ -90,
+ -98,
+ -84,
+ -85,
+ -103,
+ -86,
+ -94,
+ -92,
+ -83,
+ -87,
+ -99,
+ -80,
+ -87,
+ -94,
+ -84,
+ -92,
+ -85,
+ -88,
+ -97,
+ -87,
+ -88,
+ -89,
+ -81,
+ -90,
+ -99,
+ -84,
+ -104,
+ -100,
+ -99,
+ -96,
+ -93,
+ -86,
+ -93,
+ -98,
+ -93,
+ -98,
+ -90,
+ -100,
+ -101,
+ -100,
+ -102,
+ -92,
+ -100,
+ -102,
+ -88,
+ -101,
+ -113,
+ -99,
+ -104,
+ -87,
+ -99,
+ -97,
+ -95,
+ -99,
+ -101,
+ -100,
+ -84,
+ -94,
+ -88,
+ -105,
+ -101,
+ -102,
+ -99,
+ -103,
+ -83,
+ -103,
+ -83,
+ -89,
+ -85,
+ -82,
+ -101,
+ -99,
+ -98,
+ -92,
+ -116,
+ -98,
+ -86,
+ -108,
+ -100,
+ -85,
+ -96,
+ -91,
+ -82,
+ -83,
+ -88,
+ -88,
+ -96,
+ -95,
+ -84,
+ -89,
+ -100,
+ -100,
+ -96,
+ -86,
+ -113,
+ -102,
+ -89,
+ -87,
+ -84,
+ -81,
+ -97,
+ -98,
+ -106,
+ -100,
+ -100,
+ -98,
+ -100,
+ -86,
+ -87,
+ -97,
+ -86,
+ -89,
+ -82,
+ -87,
+ -91,
+ -85,
+ -96,
+ -88,
+ -86,
+ -85,
+ -87,
+ -83,
+ -88,
+ -101,
+ -92,
+ -101,
+ -100,
+ -85,
+ -101,
+ -87,
+ -97,
+ -89,
+ -97,
+ -97,
+ -92,
+ -101,
+ -86,
+ -102,
+ -83,
+ -100,
+ -85,
+ -93,
+ -100,
+ -85,
+ -87,
+ -99,
+ -103,
+ -115,
+ -95,
+ -85,
+ -98,
+ -101,
+ -86,
+ -97,
+ -98,
+ -89,
+ -102,
+ -101,
+ -98,
+ -101,
+ -82,
+ -88,
+ -88,
+ -103,
+ -83,
+ -82,
+ -83,
+ -95,
+ -93,
+ -92,
+ -84,
+ -96,
+ -112,
+ -111,
+ -84,
+ -99,
+ -98,
+ -86,
+ -101,
+ -85,
+ -99,
+ -98,
+ -99,
+ -98,
+ -85,
+ -84,
+ -83,
+ -93,
+ -93,
+ -100,
+ -95,
+ -84,
+ -99,
+ -95,
+ -98,
+ -88,
+ -85,
+ -97,
+ -100,
+ -86,
+ -87,
+ -98,
+ -94,
+ -84,
+ -83,
+ -90,
+ -101,
+ -99,
+ -102,
+ -89,
+ -99,
+ -89,
+ -102,
+ -89,
+ -97,
+ -112,
+ -84,
+ -105,
+ -91,
+ -92,
+ -101,
+ -113,
+ -81,
+ -106,
+ -86,
+ -87,
+ -93,
+ -102,
+ -89,
+ -99,
+ -104,
+ -84,
+ -93,
+ -82,
+ -97,
+ -104,
+ -85,
+ -83,
+ -83,
+ -99,
+ -88,
+ -85,
+ -103,
+ -98,
+ -114,
+ -92,
+ -115,
+ -97,
+ -91,
+ -95,
+ -83,
+ -85,
+ -97,
+ -99,
+ -81,
+ -85,
+ -101,
+ -95,
+ -98,
+ -93,
+ -84,
+ -85,
+ -101,
+ -86,
+ -86,
+ -99,
+ -85,
+ -95,
+ -100,
+ -98,
+ -96,
+ -102,
+ -94,
+ -85,
+ -104,
+ -89,
+ -96,
+ -95,
+ -88,
+ -100,
+ -93,
+ -85,
+ -102,
+ -87,
+ -100,
+ -88,
+ -97,
+ -96,
+ -94,
+ -95,
+ -97,
+ -82,
+ -99,
+ -99,
+ -85,
+ -104,
+ -84,
+ -100,
+ -92,
+ -98,
+ -96,
+ -113,
+ -94,
+ -95,
+ -94,
+ -97,
+ -99,
+ -84,
+ -96,
+ -81,
+ -95,
+ -84,
+ -82,
+ -87,
+ -94,
+ -102,
+ -103,
+ -84,
+ -95,
+ -88,
+ -99,
+ -93,
+ -103,
+ -100,
+ -87,
+ -91,
+ -87,
+ -98,
+ -82,
+ -86,
+ -97,
+ -100,
+ -100,
+ -86,
+ -100,
+ -84,
+ -85,
+ -99,
+ -104,
+ -92,
+ -87,
+ -96,
+ -92,
+ -89,
+ -91,
+ -99,
+ -90,
+ -93,
+ -102,
+ -85,
+ -97,
+ -105,
+ -102,
+ -83,
+ -83,
+ -84,
+ -85,
+ -101,
+ -99,
+ -103,
+ -95,
+ -84,
+ -115,
+ -103,
+ -104,
+ -99,
+ -96,
+ -82,
+ -100,
+ -92,
+ -84,
+ -101,
+ -91,
+ -98,
+ -97,
+ -85,
+ -96,
+ -91,
+ -84,
+ -102,
+ -102,
+ -84,
+ -84,
+ -99,
+ -86,
+ -98,
+ -85,
+ -99,
+ -83,
+ -98,
+ -88,
+ -96,
+ -95,
+ -85,
+ -90,
+ -83,
+ -101,
+ -104,
+ -93,
+ -105,
+ -90,
+ -88,
+ -100,
+ -85,
+ -91,
+ -99,
+ -95,
+ -82,
+ -88,
+ -97,
+ -101,
+ -112,
+ -88,
+ -85,
+ -100,
+ -83,
+ -100,
+ -85,
+ -89,
+ -85,
+ -87,
+ -83,
+ -115,
+ -98,
+ -103,
+ -103,
+ -96,
+ -101,
+ -99,
+ -85,
+ -90,
+ -103,
+ -89,
+ -84,
+ -80,
+ -90,
+ -99,
+ -102,
+ -98,
+ -85,
+ -99,
+ -88,
+ -95,
+ -99,
+ -104,
+ -100,
+ -113,
+ -98,
+ -102,
+ -97,
+ -84,
+ -91,
+ -93,
+ -87,
+ -114,
+ -102,
+ -97,
+ -100,
+ -103,
+ -98,
+ -89,
+ -100,
+ -83,
+ -98,
+ -99,
+ -84,
+ -104,
+ -104,
+ -98,
+ -83,
+ -97,
+ -100,
+ -84,
+ -91,
+ -87,
+ -97,
+ -104,
+ -85,
+ -89,
+ -84,
+ -94,
+ -97,
+ -100,
+ -97,
+ -87,
+ -89,
+ -87,
+ -86,
+ -87,
+ -114,
+ -83,
+ -85,
+ -97,
+ -85,
+ -90,
+ -86,
+ -100,
+ -103,
+ -100,
+ -87,
+ -87,
+ -99,
+ -103,
+ -90,
+ -99,
+ -97,
+ -104,
+ -85,
+ -83,
+ -80,
+ -114,
+ -100,
+ -103,
+ -96,
+ -83,
+ -98,
+ -103,
+ -102,
+ -83,
+ -88,
+ -99,
+ -85,
+ -87,
+ -100,
+ -85,
+ -90,
+ -85,
+ -83,
+ -116,
+ -114,
+ -102,
+ -88,
+ -90,
+ -94,
+ -85,
+ -83,
+ -85,
+ -86,
+ -91,
+ -100,
+ -85,
+ -98,
+ -83,
+ -91,
+ -84,
+ -91,
+ -115,
+ -91,
+ -91,
+ -94,
+ -95,
+ -97,
+ -113,
+ -103,
+ -88,
+ -101,
+ -85,
+ -99,
+ -87,
+ -91,
+ -86,
+ -83,
+ -101,
+ -101,
+ -88,
+ -83,
+ -83,
+ -99,
+ -96,
+ -81,
+ -85,
+ -104,
+ -85,
+ -93,
+ -99,
+ -99,
+ -87,
+ -99,
+ -99,
+ -86,
+ -84,
+ -90,
+ -87,
+ -99,
+ -104,
+ -95,
+ -99,
+ -100,
+ -85,
+ -83,
+ -100,
+ -85,
+ -84,
+ -94,
+ -102,
+ -84,
+ -82,
+ -85,
+ -87,
+ -92,
+ -85,
+ -95,
+ -87,
+ -93,
+ -100,
+ -83,
+ -87,
+ -102,
+ -101,
+ -91,
+ -101,
+ -87,
+ -87,
+ -100,
+ -86,
+ -84,
+ -97,
+ -87,
+ -88,
+ -105,
+ -93,
+ -86,
+ -87,
+ -113,
+ -83,
+ -94,
+ -82,
+ -104,
+ -103,
+ -85,
+ -84,
+ -83,
+ -85,
+ -83,
+ -83,
+ -101,
+ -91,
+ -97,
+ -83,
+ -84,
+ -98,
+ -92,
+ -102,
+ -90,
+ -91,
+ -82,
+ -83,
+ -95,
+ -97,
+ -90,
+ -84,
+ -98,
+ -84,
+ -92,
+ -98,
+ -96,
+ -103,
+ -98,
+ -101,
+ -82,
+ -100,
+ -90,
+ -101,
+ -99,
+ -97,
+ -93,
+ -87,
+ -95,
+ -114,
+ -99,
+ -102,
+ -97,
+ -95,
+ -103,
+ -84,
+ -95,
+ -87,
+ -89,
+ -85,
+ -97,
+ -98,
+ -87,
+ -100,
+ -99,
+ -103,
+ -96,
+ -99,
+ -98,
+ -90,
+ -100,
+ -91,
+ -87,
+ -91,
+ -92,
+ -90,
+ -83,
+ -98,
+ -94,
+ -87,
+ -84,
+ -86,
+ -87,
+ -100,
+ -87,
+ -101,
+ -104,
+ -97,
+ -83,
+ -100,
+ -88,
+ -98,
+ -86,
+ -85,
+ -97,
+ -98,
+ -100,
+ -95,
+ -97,
+ -82,
+ -95,
+ -85,
+ -104,
+ -114,
+ -108,
+ -98,
+ -113,
+ -87,
+ -98,
+ -99,
+ -85,
+ -85,
+ -85,
+ -87,
+ -93,
+ -91,
+ -89,
+ -100,
+ -85,
+ -82,
+ -91,
+ -85,
+ -91,
+ -101,
+ -82,
+ -96,
+ -86,
+ -86,
+ -82,
+ -100,
+ -102,
+ -96,
+ -93,
+ -102,
+ -96,
+ -93,
+ -84,
+ -89,
+ -90,
+ -88,
+ -96,
+ -87,
+ -100,
+ -83,
+ -81,
+ -89,
+ -98,
+ -95,
+ -98,
+ -87,
+ -104,
+ -95,
+ -85,
+ -88,
+ -101,
+ -90,
+ -102,
+ -100,
+ -100,
+ -90,
+ -101,
+ -82,
+ -102,
+ -101,
+ -87,
+ -86,
+ -85,
+ -84,
+ -87,
+ -88,
+ -93,
+ -89,
+ -99,
+ -84,
+ -101,
+ -95,
+ -102,
+ -82,
+ -82,
+ -88,
+ -104,
+ -85,
+ -85,
+ -98,
+ -90,
+ -87,
+ -96,
+ -102,
+ -94,
+ -82,
+ -87,
+ -101,
+ -102,
+ -87,
+ -102,
+ -85,
+ -84,
+ -90,
+ -96,
+ -88,
+ -95,
+ -101,
+ -83,
+ -85,
+ -104,
+ -112,
+ -97,
+ -96,
+ -114,
+ -96,
+ -100,
+ -103,
+ -85,
+ -82,
+ -86,
+ -96,
+ -96,
+ -84,
+ -84,
+ -87,
+ -100,
+ -85,
+ -98,
+ -89,
+ -100,
+ -88,
+ -98,
+ -97,
+ -91,
+ -102,
+ -89,
+ -93,
+ -95,
+ -101,
+ -93,
+ -88,
+ -90,
+ -96,
+ -87,
+ -85,
+ -85,
+ -100,
+ -85,
+ -97,
+ -88,
+ -99,
+ -92,
+ -84,
+ -101,
+ -99,
+ -113,
+ -85,
+ -93,
+ -112,
+ -97,
+ -91,
+ -101,
+ -96,
+ -117,
+ -97,
+ -87,
+ -100,
+ -101,
+ -98,
+ -84,
+ -92,
+ -94,
+ -85,
+ -83,
+ -96,
+ -82,
+ -91,
+ -99,
+ -89,
+ -81,
+ -112,
+ -98,
+ -96,
+ -81,
+ -97,
+ -87,
+ -84,
+ -93,
+ -100,
+ -92,
+ -89,
+ -101,
+ -99,
+ -112,
+ -82,
+ -84,
+ -88,
+ -81,
+ -97,
+ -101,
+ -98,
+ -89,
+ -90,
+ -87,
+ -92,
+ -91,
+ -98,
+ -102,
+ -100,
+ -96,
+ -97,
+ -81,
+ -84,
+ -101,
+ -89,
+ -100,
+ -95,
+ -102,
+ -113,
+ -83,
+ -82,
+ -89,
+ -97,
+ -99,
+ -87,
+ -86,
+ -91,
+ -82,
+ -83,
+ -102,
+ -102,
+ -89,
+ -95,
+ -87,
+ -89,
+ -90,
+ -98,
+ -85,
+ -82,
+ -103,
+ -94,
+ -86,
+ -96,
+ -83,
+ -97,
+ -93,
+ -102,
+ -91,
+ -89,
+ -84,
+ -98,
+ -103,
+ -106,
+ -83,
+ -85,
+ -88,
+ -91,
+ -112,
+ -102,
+ -84,
+ -101,
+ -112,
+ -104,
+ -102,
+ -87,
+ -93,
+ -99,
+ -101,
+ -84,
+ -98,
+ -85,
+ -101,
+ -99,
+ -87,
+ -102,
+ -83,
+ -99,
+ -103,
+ -101,
+ -83,
+ -90,
+ -89,
+ -87,
+ -86,
+ -101,
+ -100,
+ -84,
+ -85,
+ -82,
+ -87,
+ -86,
+ -85,
+ -98,
+ -84,
+ -83,
+ -90,
+ -93,
+ -104,
+ -96,
+ -103,
+ -88,
+ -91,
+ -117,
+ -104,
+ -82,
+ -100,
+ -102,
+ -93,
+ -99,
+ -87,
+ -84,
+ -91,
+ -82,
+ -102,
+ -101,
+ -86,
+ -99,
+ -99,
+ -102,
+ -97,
+ -100,
+ -87,
+ -83,
+ -99,
+ -84,
+ -96,
+ -96,
+ -99,
+ -96,
+ -94,
+ -89,
+ -100,
+ -99,
+ -86,
+ -99,
+ -100,
+ -100,
+ -100,
+ -101,
+ -88,
+ -103,
+ -101,
+ -100,
+ -91,
+ -94,
+ -92,
+ -115,
+ -83,
+ -85,
+ -83,
+ -86,
+ -86,
+ -83,
+ -101,
+ -85,
+ -98,
+ -99,
+ -89,
+ -86,
+ -85,
+ -101,
+ -97,
+ -102,
+ -86,
+ -97,
+ -93,
+ -82,
+ -88,
+ -104,
+ -95,
+ -102,
+ -86,
+ -85,
+ -86,
+ -96,
+ -98,
+ -105,
+ -97,
+ -99,
+ -103,
+ -99,
+ -82,
+ -97,
+ -98,
+ -87,
+ -93,
+ -102,
+ -84,
+ -85,
+ -97,
+ -87,
+ -92,
+ -97,
+ -83,
+ -99,
+ -100,
+ -113,
+ -85,
+ -100,
+ -89,
+ -84,
+ -104,
+ -89,
+ -103,
+ -88,
+ -89,
+ -97,
+ -103,
+ -97,
+ -93,
+ -97,
+ -104,
+ -101,
+ -95,
+ -83,
+ -83,
+ -100,
+ -88,
+ -83,
+ -87,
+ -113,
+ -90,
+ -84,
+ -85,
+ -86,
+ -81,
+ -84,
+ -85,
+ -82,
+ -89,
+ -88,
+ -97,
+ -93,
+ -98,
+ -114,
+ -112,
+ -99,
+ -103,
+ -90,
+ -94,
+ -86,
+ -91,
+ -101,
+ -89,
+ -86,
+ -100,
+ -87,
+ -100,
+ -82,
+ -96,
+ -91,
+ -102,
+ -85,
+ -87,
+ -83,
+ -100,
+ -84,
+ -89,
+ -101,
+ -83,
+ -104,
+ -85,
+ -94,
+ -96,
+ -91,
+ -100,
+ -83,
+ -97,
+ -81,
+ -86,
+ -91,
+ -102,
+ -87,
+ -92,
+ -87,
+ -113,
+ -86,
+ -99,
+ -83,
+ -85,
+ -93,
+ -88,
+ -87,
+ -101,
+ -90,
+ -96,
+ -101,
+ -97,
+ -86,
+ -102,
+ -86,
+ -101,
+ -102,
+ -97,
+ -99,
+ -99,
+ -112,
+ -93,
+ -92,
+ -84,
+ -93,
+ -93,
+ -83,
+ -84,
+ -87,
+ -97,
+ -84,
+ -97,
+ -98,
+ -91,
+ -98,
+ -85,
+ -85,
+ -115,
+ -114,
+ -83,
+ -97,
+ -99,
+ -96,
+ -84,
+ -83,
+ -89,
+ -99,
+ -94,
+ -83,
+ -87,
+ -99,
+ -88,
+ -83,
+ -96,
+ -107,
+ -93,
+ -100,
+ -94,
+ -87,
+ -115,
+ -83,
+ -86,
+ -95,
+ -96,
+ -114,
+ -90,
+ -85,
+ -104,
+ -99,
+ -100,
+ -98,
+ -96,
+ -90,
+ -95,
+ -93,
+ -88,
+ -97,
+ -82,
+ -85,
+ -96,
+ -83,
+ -102,
+ -83,
+ -81,
+ -99,
+ -84,
+ -100,
+ -95,
+ -102,
+ -83,
+ -89,
+ -99,
+ -82,
+ -87,
+ -100,
+ -89,
+ -95,
+ -97,
+ -90,
+ -87,
+ -97,
+ -104,
+ -99,
+ -93,
+ -83,
+ -92,
+ -90,
+ -103,
+ -92,
+ -104,
+ -84,
+ -100,
+ -83,
+ -83,
+ -101,
+ -85,
+ -98,
+ -86,
+ -90,
+ -87,
+ -115,
+ -93,
+ -84,
+ -91,
+ -96,
+ -92,
+ -96,
+ -87,
+ -99,
+ -86,
+ -102,
+ -95,
+ -97,
+ -92,
+ -106,
+ -83,
+ -91,
+ -83,
+ -93,
+ -81,
+ -98,
+ -101,
+ -99,
+ -89,
+ -86,
+ -87,
+ -84,
+ -105,
+ -101,
+ -101,
+ -98,
+ -88,
+ -95,
+ -102,
+ -81,
+ -103,
+ -89,
+ -82,
+ -84,
+ -83,
+ -92,
+ -97,
+ -102,
+ -93,
+ -114,
+ -99,
+ -95,
+ -95,
+ -85,
+ -114,
+ -102,
+ -99,
+ -101,
+ -102,
+ -103,
+ -98,
+ -98,
+ -96,
+ -101,
+ -96,
+ -98,
+ -83,
+ -84,
+ -100,
+ -95,
+ -113,
+ -103,
+ -97,
+ -99,
+ -98,
+ -84,
+ -87,
+ -99,
+ -101,
+ -99,
+ -80,
+ -82,
+ -83,
+ -111,
+ -83,
+ -94,
+ -86,
+ -99,
+ -99,
+ -99,
+ -82,
+ -99,
+ -88,
+ -100,
+ -84,
+ -86,
+ -83,
+ -95,
+ -103,
+ -95,
+ -83,
+ -95,
+ -98,
+ -85,
+ -101,
+ -89,
+ -82,
+ -100,
+ -103,
+ -87,
+ -101,
+ -85,
+ -88,
+ -91,
+ -99,
+ -97,
+ -92,
+ -116,
+ -90,
+ -105,
+ -102,
+ -96,
+ -98,
+ -113,
+ -87,
+ -99,
+ -84,
+ -84,
+ -86,
+ -97,
+ -97,
+ -100,
+ -98,
+ -83,
+ -88,
+ -88,
+ -116,
+ -100,
+ -93,
+ -97,
+ -93,
+ -85,
+ -95,
+ -102,
+ -84,
+ -85,
+ -101,
+ -99,
+ -98,
+ -99,
+ -102,
+ -86,
+ -89,
+ -83,
+ -84,
+ -83,
+ -98,
+ -100,
+ -82,
+ -92,
+ -83,
+ -101,
+ -83,
+ -85,
+ -91,
+ -85,
+ -94,
+ -114,
+ -82,
+ -114,
+ -85,
+ -91,
+ -101,
+ -113,
+ -82,
+ -86,
+ -84,
+ -87,
+ -98,
+ -99,
+ -93,
+ -99,
+ -89,
+ -100,
+ -86,
+ -81,
+ -85,
+ -113,
+ -95,
+ -97,
+ -85,
+ -90,
+ -95,
+ -100,
+ -97,
+ -91,
+ -102,
+ -93,
+ -85,
+ -99,
+ -89,
+ -102,
+ -96,
+ -89,
+ -98,
+ -101,
+ -93,
+ -99,
+ -94,
+ -83,
+ -97,
+ -103,
+ -102,
+ -101,
+ -87,
+ -89,
+ -100,
+ -99,
+ -86,
+ -104,
+ -95,
+ -88,
+ -102,
+ -103,
+ -119,
+ -99,
+ -87,
+ -119,
+ -89,
+ -90,
+ -83,
+ -88,
+ -95,
+ -90,
+ -95,
+ -93,
+ -93,
+ -90,
+ -95,
+ -87,
+ -84,
+ -92,
+ -92,
+ -98,
+ -87,
+ -102,
+ -84,
+ -100,
+ -104,
+ -101,
+ -94,
+ -89,
+ -113,
+ -89,
+ -98,
+ -85,
+ -99,
+ -94,
+ -84,
+ -85,
+ -84,
+ -101,
+ -84,
+ -105,
+ -90,
+ -91,
+ -101,
+ -82,
+ -94,
+ -87,
+ -90,
+ -86,
+ -98,
+ -88,
+ -97,
+ -98,
+ -98,
+ -96,
+ -84,
+ -96,
+ -99,
+ -99,
+ -101,
+ -102,
+ -83,
+ -102,
+ -100,
+ -101,
+ -100,
+ -97,
+ -102,
+ -99,
+ -99,
+ -85,
+ -97,
+ -84,
+ -96,
+ -84,
+ -89,
+ -99,
+ -100,
+ -104,
+ -105,
+ -99,
+ -99,
+ -100,
+ -101,
+ -87,
+ -92,
+ -97,
+ -112,
+ -84,
+ -97,
+ -83,
+ -96,
+ -82,
+ -102,
+ -100,
+ -99,
+ -99,
+ -86,
+ -82,
+ -102,
+ -82,
+ -89,
+ -85,
+ -100,
+ -98,
+ -99,
+ -92,
+ -99,
+ -89,
+ -86,
+ -100,
+ -82,
+ -100,
+ -88,
+ -103,
+ -84,
+ -86,
+ -86,
+ -88,
+ -90,
+ -96,
+ -85,
+ -101,
+ -85,
+ -99,
+ -86,
+ -89,
+ -82,
+ -94,
+ -88,
+ -88,
+ -113,
+ -85,
+ -84,
+ -91,
+ -90,
+ -84,
+ -113,
+ -98,
+ -86,
+ -102,
+ -106,
+ -101,
+ -84,
+ -96,
+ -84,
+ -85,
+ -86,
+ -119,
+ -100,
+ -86,
+ -99,
+ -83,
+ -103,
+ -96,
+ -100,
+ -96,
+ -104,
+ -81,
+ -99,
+ -87,
+ -89,
+ -102,
+ -84,
+ -100,
+ -101,
+ -98,
+ -95,
+ -117,
+ -114,
+ -84,
+ -83,
+ -93,
+ -95,
+ -116,
+ -95,
+ -91,
+ -95,
+ -85,
+ -88,
+ -92,
+ -85,
+ -100,
+ -84,
+ -99,
+ -87,
+ -88,
+ -98,
+ -106,
+ -102,
+ -98,
+ -98,
+ -102,
+ -98,
+ -82,
+ -93,
+ -101,
+ -86,
+ -86,
+ -94,
+ -84,
+ -118,
+ -102,
+ -98,
+ -97,
+ -104,
+ -95,
+ -100,
+ -99,
+ -91,
+ -92,
+ -99,
+ -84,
+ -84,
+ -111,
+ -100,
+ -97,
+ -83,
+ -85,
+ -103,
+ -100,
+ -95,
+ -101,
+ -99,
+ -101,
+ -99,
+ -85,
+ -88,
+ -102,
+ -98,
+ -100,
+ -101,
+ -99,
+ -103,
+ -92,
+ -84,
+ -82,
+ -92,
+ -97,
+ -97,
+ -90,
+ -103,
+ -99,
+ -82,
+ -100,
+ -99,
+ -94,
+ -96,
+ -103,
+ -87,
+ -95,
+ -113,
+ -83,
+ -97,
+ -90,
+ -97,
+ -99,
+ -103,
+ -101,
+ -97,
+ -101,
+ -94,
+ -94,
+ -90,
+ -85,
+ -88,
+ -86,
+ -84,
+ -85,
+ -83,
+ -100,
+ -84,
+ -86,
+ -115,
+ -83,
+ -87,
+ -84,
+ -83,
+ -102,
+ -82,
+ -102,
+ -91,
+ -88,
+ -92,
+ -85,
+ -102,
+ -99,
+ -99,
+ -100,
+ -103,
+ -82,
+ -98,
+ -86,
+ -99,
+ -99,
+ -92,
+ -100,
+ -95,
+ -92,
+ -86,
+ -88,
+ -85,
+ -91,
+ -84,
+ -103,
+ -91,
+ -100,
+ -97,
+ -100,
+ -86,
+ -84,
+ -102,
+ -88,
+ -100,
+ -87,
+ -86,
+ -86,
+ -89,
+ -93,
+ -85,
+ -96,
+ -114,
+ -102,
+ -113,
+ -101,
+ -88,
+ -91,
+ -101,
+ -99,
+ -86,
+ -84,
+ -89,
+ -89,
+ -98,
+ -101,
+ -103,
+ -90,
+ -85,
+ -100,
+ -82,
+ -85,
+ -91,
+ -85,
+ -114,
+ -97,
+ -100,
+ -84,
+ -96,
+ -86,
+ -91,
+ -94,
+ -84,
+ -85,
+ -85,
+ -98,
+ -82,
+ -94,
+ -84,
+ -84,
+ -103,
+ -92,
+ -84,
+ -99,
+ -97,
+ -89,
+ -84,
+ -89,
+ -85,
+ -98,
+ -92,
+ -90,
+ -87,
+ -99,
+ -97,
+ -84,
+ -83,
+ -97,
+ -104,
+ -84,
+ -90,
+ -89,
+ -87,
+ -84,
+ -95,
+ -97,
+ -85,
+ -83,
+ -86,
+ -103,
+ -99,
+ -96,
+ -91,
+ -83,
+ -99,
+ -84,
+ -87,
+ -94,
+ -96,
+ -92,
+ -82,
+ -84,
+ -84,
+ -101,
+ -96,
+ -98,
+ -99,
+ -88,
+ -101,
+ -86,
+ -96,
+ -100,
+ -84,
+ -91,
+ -102,
+ -100,
+ -88,
+ -99,
+ -82,
+ -95,
+ -86,
+ -99,
+ -89,
+ -96,
+ -114,
+ -97,
+ -97,
+ -90,
+ -97,
+ -87,
+ -85,
+ -85,
+ -85,
+ -95,
+ -93,
+ -81,
+ -89,
+ -85,
+ -100,
+ -101,
+ -100,
+ -100,
+ -91,
+ -99,
+ -83,
+ -96,
+ -99,
+ -90,
+ -97,
+ -97,
+ -85,
+ -85,
+ -93,
+ -85,
+ -100,
+ -84,
+ -101,
+ -88,
+ -97,
+ -99,
+ -99,
+ -99,
+ -99,
+ -101,
+ -98,
+ -100,
+ -89,
+ -103,
+ -101,
+ -102,
+ -87,
+ -97,
+ -101,
+ -92,
+ -86,
+ -84,
+ -84,
+ -82,
+ -92,
+ -98,
+ -96,
+ -82,
+ -91,
+ -98,
+ -81,
+ -98,
+ -102,
+ -85,
+ -100,
+ -93,
+ -94,
+ -97,
+ -99,
+ -106,
+ -89,
+ -88,
+ -89,
+ -88,
+ -89,
+ -100,
+ -99,
+ -85,
+ -88,
+ -100,
+ -95,
+ -101,
+ -88,
+ -87,
+ -94,
+ -101,
+ -97,
+ -94,
+ -98,
+ -90,
+ -111,
+ -83,
+ -97,
+ -83,
+ -86,
+ -90,
+ -84,
+ -86,
+ -83,
+ -88,
+ -85,
+ -114,
+ -88,
+ -84,
+ -94,
+ -93,
+ -89,
+ -102,
+ -97,
+ -98,
+ -97,
+ -95,
+ -88,
+ -89,
+ -101,
+ -99,
+ -96,
+ -97,
+ -83,
+ -83,
+ -83,
+ -91,
+ -96,
+ -102,
+ -87,
+ -92,
+ -87,
+ -88,
+ -95,
+ -101,
+ -95,
+ -100,
+ -97,
+ -98,
+ -86,
+ -105,
+ -87,
+ -95,
+ -98,
+ -101,
+ -100,
+ -82,
+ -95,
+ -101,
+ -92,
+ -86,
+ -102,
+ -90,
+ -82,
+ -103,
+ -84,
+ -92,
+ -98,
+ -89,
+ -100,
+ -103,
+ -100,
+ -102,
+ -97,
+ -86,
+ -94,
+ -103,
+ -84,
+ -88,
+ -84,
+ -97,
+ -82,
+ -82,
+ -97,
+ -94,
+ -95,
+ -87,
+ -103,
+ -102,
+ -83,
+ -115,
+ -90,
+ -103,
+ -94,
+ -98,
+ -85,
+ -96,
+ -89,
+ -89,
+ -100,
+ -89,
+ -98,
+ -88,
+ -103,
+ -98,
+ -86,
+ -83,
+ -84,
+ -98,
+ -86,
+ -102,
+ -86,
+ -105,
+ -100,
+ -102,
+ -104,
+ -99,
+ -82,
+ -93,
+ -101,
+ -97,
+ -82,
+ -90,
+ -85,
+ -88,
+ -98,
+ -100,
+ -90,
+ -87,
+ -86,
+ -90,
+ -82,
+ -84,
+ -95,
+ -93,
+ -94,
+ -93,
+ -85,
+ -99,
+ -83,
+ -87,
+ -92,
+ -87,
+ -98,
+ -83,
+ -82,
+ -94,
+ -86,
+ -96,
+ -87,
+ -97,
+ -91,
+ -99,
+ -91,
+ -105,
+ -84,
+ -89,
+ -97,
+ -93,
+ -88,
+ -104,
+ -83,
+ -105,
+ -97,
+ -103,
+ -92,
+ -82,
+ -85,
+ -98,
+ -90,
+ -86,
+ -88,
+ -99,
+ -105,
+ -115,
+ -83,
+ -87,
+ -85,
+ -101,
+ -93,
+ -85,
+ -89,
+ -82,
+ -100,
+ -84,
+ -86,
+ -85,
+ -89,
+ -83,
+ -99,
+ -103,
+ -96,
+ -101,
+ -92,
+ -92,
+ -97,
+ -99,
+ -102,
+ -89,
+ -92,
+ -100,
+ -96,
+ -104,
+ -97,
+ -101,
+ -97,
+ -84,
+ -101,
+ -93,
+ -100,
+ -90,
+ -84,
+ -83,
+ -85,
+ -102,
+ -84,
+ -101,
+ -82,
+ -86,
+ -104,
+ -104,
+ -91,
+ -89,
+ -84,
+ -93,
+ -98,
+ -87,
+ -100,
+ -116,
+ -84,
+ -85,
+ -89,
+ -91,
+ -98,
+ -96,
+ -89,
+ -90,
+ -87,
+ -103,
+ -87,
+ -95,
+ -100,
+ -95,
+ -101,
+ -100,
+ -92,
+ -95,
+ -103,
+ -87,
+ -100,
+ -84,
+ -100,
+ -80,
+ -104,
+ -89,
+ -96,
+ -87,
+ -100,
+ -98,
+ -99,
+ -96,
+ -84,
+ -101,
+ -113,
+ -100,
+ -100,
+ -100,
+ -96,
+ -94,
+ -101,
+ -85,
+ -85,
+ -96,
+ -89,
+ -91,
+ -86,
+ -83,
+ -92,
+ -98,
+ -94,
+ -89,
+ -86,
+ -117,
+ -85,
+ -117,
+ -99,
+ -86,
+ -99,
+ -100,
+ -101,
+ -87,
+ -95,
+ -98,
+ -105,
+ -102,
+ -106,
+ -84,
+ -96,
+ -99,
+ -87,
+ -87,
+ -87,
+ -85,
+ -90,
+ -100,
+ -100,
+ -99,
+ -89,
+ -97,
+ -103,
+ -97,
+ -87,
+ -85,
+ -99,
+ -99,
+ -89,
+ -91,
+ -99,
+ -94,
+ -84,
+ -85,
+ -92,
+ -91,
+ -97,
+ -90,
+ -87,
+ -98,
+ -100,
+ -83,
+ -101,
+ -97,
+ -86,
+ -84,
+ -94,
+ -82,
+ -85,
+ -104,
+ -96,
+ -93,
+ -98,
+ -104,
+ -97,
+ -98,
+ -83,
+ -86,
+ -101,
+ -85,
+ -98,
+ -89,
+ -91,
+ -84,
+ -94,
+ -89,
+ -84,
+ -84,
+ -99,
+ -83,
+ -82,
+ -84,
+ -90,
+ -99,
+ -97,
+ -96,
+ -98,
+ -104,
+ -86,
+ -100,
+ -98,
+ -82,
+ -95,
+ -101,
+ -91,
+ -103,
+ -82,
+ -92,
+ -97,
+ -83,
+ -94,
+ -96,
+ -100,
+ -88,
+ -86,
+ -92,
+ -97,
+ -84,
+ -105,
+ -89,
+ -93,
+ -114,
+ -91,
+ -98,
+ -92,
+ -96,
+ -115,
+ -98,
+ -83,
+ -81,
+ -87,
+ -85,
+ -118,
+ -90,
+ -97,
+ -87,
+ -84,
+ -100,
+ -85,
+ -90,
+ -86,
+ -98,
+ -82,
+ -96,
+ -100,
+ -101,
+ -86,
+ -83,
+ -85,
+ -89,
+ -99,
+ -99,
+ -94,
+ -98,
+ -95,
+ -92,
+ -91,
+ -101,
+ -99,
+ -98,
+ -99,
+ -91,
+ -86,
+ -117,
+ -98,
+ -99,
+ -87,
+ -81,
+ -84,
+ -100,
+ -101,
+ -97,
+ -98,
+ -97,
+ -88,
+ -119,
+ -102,
+ -102,
+ -104,
+ -85,
+ -97,
+ -102,
+ -100,
+ -104,
+ -87,
+ -82,
+ -86,
+ -93,
+ -100,
+ -99,
+ -84,
+ -98,
+ -84,
+ -86,
+ -100,
+ -88,
+ -103,
+ -85,
+ -88,
+ -99,
+ -96,
+ -99,
+ -87,
+ -84,
+ -83,
+ -96,
+ -97,
+ -86,
+ -97,
+ -97,
+ -86,
+ -82,
+ -100,
+ -85,
+ -89,
+ -95,
+ -96,
+ -101,
+ -88,
+ -87,
+ -96,
+ -84,
+ -87,
+ -90,
+ -100,
+ -96,
+ -90,
+ -88,
+ -87,
+ -96,
+ -102,
+ -92,
+ -101,
+ -92,
+ -84,
+ -85,
+ -86,
+ -116,
+ -103,
+ -84,
+ -98,
+ -93,
+ -98,
+ -97,
+ -99,
+ -86,
+ -84,
+ -85,
+ -87,
+ -98,
+ -99,
+ -101,
+ -97,
+ -96,
+ -85,
+ -80,
+ -115,
+ -81,
+ -83,
+ -95,
+ -100,
+ -83,
+ -96,
+ -89,
+ -99,
+ -90,
+ -84,
+ -101,
+ -96,
+ -84,
+ -100,
+ -84,
+ -97,
+ -97,
+ -82,
+ -99,
+ -98,
+ -85,
+ -98,
+ -93,
+ -89,
+ -105,
+ -100,
+ -94,
+ -83,
+ -92,
+ -98,
+ -87,
+ -116,
+ -87,
+ -92,
+ -91,
+ -85,
+ -86,
+ -85,
+ -103,
+ -93,
+ -112,
+ -89,
+ -88,
+ -90,
+ -88,
+ -114,
+ -84,
+ -92,
+ -86,
+ -98,
+ -104,
+ -84,
+ -90,
+ -90,
+ -93,
+ -84,
+ -89,
+ -103,
+ -98,
+ -87,
+ -98,
+ -92,
+ -88,
+ -98,
+ -96,
+ -91,
+ -86,
+ -96,
+ -102,
+ -91,
+ -86,
+ -103,
+ -99,
+ -97,
+ -83,
+ -97,
+ -113,
+ -82,
+ -84,
+ -103,
+ -101,
+ -83,
+ -101,
+ -100,
+ -97,
+ -94,
+ -89,
+ -100,
+ -87,
+ -102,
+ -89,
+ -86,
+ -86,
+ -93,
+ -96,
+ -98,
+ -101,
+ -98,
+ -98,
+ -97,
+ -97,
+ -100,
+ -91,
+ -100,
+ -99,
+ -114,
+ -84,
+ -104,
+ -95,
+ -84,
+ -91,
+ -103,
+ -82,
+ -90,
+ -90,
+ -89,
+ -98,
+ -101,
+ -91,
+ -84,
+ -99,
+ -99,
+ -84,
+ -86,
+ -83,
+ -95,
+ -96,
+ -91,
+ -84,
+ -99,
+ -104,
+ -88,
+ -98,
+ -83,
+ -103,
+ -91,
+ -92,
+ -95,
+ -98,
+ -94,
+ -104,
+ -82,
+ -87,
+ -99,
+ -85,
+ -88,
+ -95,
+ -91,
+ -97,
+ -103,
+ -92,
+ -85,
+ -86,
+ -100,
+ -86,
+ -99,
+ -89,
+ -97,
+ -86,
+ -99,
+ -80,
+ -88,
+ -102,
+ -97,
+ -101,
+ -95,
+ -97,
+ -87,
+ -102,
+ -87,
+ -90,
+ -83,
+ -104,
+ -101,
+ -105,
+ -85,
+ -99,
+ -82,
+ -84,
+ -99,
+ -85,
+ -96,
+ -98,
+ -100,
+ -99,
+ -99,
+ -89,
+ -87,
+ -86,
+ -100,
+ -85,
+ -87,
+ -99,
+ -93,
+ -113,
+ -96,
+ -84,
+ -93,
+ -94,
+ -90,
+ -94,
+ -91,
+ -116,
+ -98,
+ -85,
+ -84,
+ -85,
+ -84,
+ -100,
+ -93,
+ -97,
+ -114,
+ -94,
+ -102,
+ -89,
+ -87,
+ -96,
+ -87,
+ -100,
+ -98,
+ -86,
+ -92,
+ -93,
+ -96,
+ -99,
+ -89,
+ -82,
+ -98,
+ -99,
+ -99,
+ -93,
+ -100,
+ -88,
+ -85,
+ -92,
+ -85,
+ -87,
+ -89,
+ -92,
+ -101,
+ -113,
+ -100,
+ -96,
+ -87,
+ -97,
+ -95,
+ -100,
+ -84,
+ -85,
+ -98,
+ -97,
+ -100,
+ -104,
+ -89,
+ -100,
+ -99,
+ -100,
+ -93,
+ -84,
+ -118,
+ -99,
+ -97,
+ -104,
+ -91,
+ -98,
+ -92,
+ -113,
+ -101,
+ -89,
+ -98,
+ -100,
+ -98,
+ -86,
+ -95,
+ -86,
+ -100,
+ -86,
+ -96,
+ -100,
+ -84,
+ -101,
+ -100,
+ -83,
+ -95,
+ -84,
+ -101,
+ -102,
+ -99,
+ -103,
+ -96,
+ -81,
+ -98,
+ -84,
+ -115,
+ -101,
+ -84,
+ -99,
+ -93,
+ -98,
+ -101,
+ -85,
+ -84,
+ -83,
+ -104,
+ -82,
+ -104,
+ -100,
+ -84,
+ -79,
+ -89,
+ -82,
+ -84,
+ -99,
+ -83,
+ -96,
+ -86,
+ -84,
+ -93,
+ -84,
+ -88,
+ -101,
+ -93,
+ -86,
+ -95,
+ -102,
+ -100,
+ -119,
+ -87,
+ -98,
+ -93,
+ -93,
+ -100,
+ -81,
+ -83,
+ -90,
+ -101,
+ -101,
+ -86,
+ -99,
+ -94,
+ -88,
+ -102,
+ -102,
+ -88,
+ -83,
+ -84,
+ -87,
+ -101,
+ -116,
+ -91,
+ -89,
+ -88,
+ -102,
+ -99,
+ -101,
+ -101,
+ -86,
+ -100,
+ -87,
+ -101,
+ -99,
+ -85,
+ -101,
+ -98,
+ -96,
+ -85,
+ -96,
+ -90,
+ -100,
+ -89,
+ -93,
+ -96,
+ -83,
+ -100,
+ -99,
+ -102,
+ -86,
+ -86,
+ -95,
+ -94,
+ -105,
+ -102,
+ -93,
+ -85,
+ -94,
+ -83,
+ -94,
+ -87,
+ -101,
+ -100,
+ -83,
+ -100,
+ -94,
+ -86,
+ -87,
+ -91,
+ -95,
+ -100,
+ -93,
+ -98,
+ -96,
+ -98,
+ -94,
+ -85,
+ -91,
+ -102,
+ -90,
+ -91,
+ -97,
+ -113,
+ -103,
+ -85,
+ -87,
+ -104,
+ -102,
+ -93,
+ -87,
+ -92,
+ -102,
+ -84,
+ -99,
+ -102,
+ -88,
+ -85,
+ -101,
+ -91,
+ -100,
+ -87,
+ -98,
+ -81,
+ -100,
+ -86,
+ -98,
+ -102,
+ -85,
+ -102,
+ -81,
+ -98,
+ -94,
+ -104,
+ -87,
+ -112,
+ -99,
+ -89,
+ -84,
+ -99,
+ -106,
+ -99,
+ -87,
+ -100,
+ -85,
+ -98,
+ -83,
+ -90,
+ -100,
+ -95,
+ -86,
+ -104,
+ -89,
+ -102,
+ -95,
+ -87,
+ -87,
+ -98,
+ -83,
+ -87,
+ -83,
+ -102,
+ -91,
+ -90,
+ -112,
+ -100,
+ -84,
+ -99,
+ -98,
+ -81,
+ -90,
+ -98,
+ -101,
+ -101,
+ -99,
+ -86,
+ -102,
+ -119,
+ -85,
+ -102,
+ -87,
+ -91,
+ -83,
+ -95,
+ -98,
+ -91,
+ -101,
+ -100,
+ -92,
+ -89,
+ -102,
+ -92,
+ -99,
+ -85,
+ -95,
+ -102,
+ -85,
+ -97,
+ -114,
+ -82,
+ -99,
+ -98,
+ -93,
+ -87,
+ -90,
+ -91,
+ -86,
+ -99,
+ -83,
+ -103,
+ -90,
+ -87,
+ -115,
+ -100,
+ -96,
+ -113,
+ -91,
+ -104,
+ -87,
+ -83,
+ -106,
+ -100,
+ -102,
+ -93,
+ -96,
+ -84,
+ -90,
+ -97,
+ -87,
+ -99,
+ -88,
+ -99,
+ -88,
+ -90,
+ -84,
+ -83,
+ -93,
+ -95,
+ -95,
+ -83,
+ -104,
+ -102,
+ -86,
+ -84,
+ -100,
+ -99,
+ -84,
+ -85,
+ -99,
+ -81,
+ -95,
+ -101,
+ -91,
+ -96,
+ -97,
+ -99,
+ -112,
+ -99,
+ -82,
+ -87,
+ -100,
+ -85,
+ -100,
+ -94,
+ -91,
+ -98,
+ -81,
+ -90,
+ -86,
+ -85,
+ -119,
+ -99,
+ -101,
+ -84,
+ -86,
+ -83,
+ -85,
+ -97,
+ -99,
+ -101,
+ -88,
+ -95,
+ -89,
+ -96,
+ -98,
+ -97,
+ -116,
+ -102,
+ -87,
+ -98,
+ -91,
+ -90,
+ -96,
+ -98,
+ -83,
+ -90,
+ -103,
+ -85,
+ -98,
+ -115,
+ -91,
+ -99,
+ -101,
+ -117,
+ -87,
+ -96,
+ -100,
+ -85,
+ -94,
+ -91,
+ -86,
+ -100,
+ -101,
+ -95,
+ -84,
+ -82,
+ -83,
+ -91,
+ -93,
+ -99,
+ -96,
+ -97,
+ -83,
+ -84,
+ -89,
+ -85,
+ -103,
+ -95,
+ -103,
+ -98,
+ -100,
+ -85,
+ -96,
+ -102,
+ -91,
+ -96,
+ -94,
+ -84,
+ -89,
+ -95,
+ -102,
+ -102,
+ -84,
+ -83,
+ -102,
+ -95,
+ -100,
+ -92,
+ -95,
+ -90,
+ -92,
+ -86,
+ -84,
+ -98,
+ -83,
+ -91,
+ -98,
+ -83,
+ -92,
+ -101,
+ -99,
+ -97,
+ -99,
+ -99,
+ -88,
+ -101,
+ -84,
+ -97,
+ -99,
+ -90,
+ -98,
+ -100,
+ -93,
+ -84,
+ -96,
+ -105,
+ -99,
+ -90,
+ -114,
+ -94,
+ -101,
+ -93,
+ -98,
+ -96,
+ -101,
+ -84,
+ -98,
+ -101,
+ -98,
+ -119,
+ -116,
+ -86,
+ -84,
+ -119,
+ -98,
+ -99,
+ -97,
+ -115,
+ -97,
+ -86,
+ -89,
+ -100,
+ -114,
+ -102,
+ -98,
+ -89,
+ -87,
+ -99,
+ -89,
+ -89,
+ -96,
+ -93,
+ -88,
+ -114,
+ -86,
+ -89,
+ -103,
+ -102,
+ -100,
+ -90,
+ -93,
+ -84,
+ -90,
+ -83,
+ -96,
+ -99,
+ -96,
+ -84,
+ -96,
+ -89,
+ -93,
+ -87,
+ -99,
+ -98,
+ -86,
+ -83,
+ -103,
+ -99,
+ -91,
+ -88,
+ -85,
+ -84,
+ -114,
+ -101,
+ -83,
+ -97,
+ -100,
+ -93,
+ -87,
+ -98,
+ -90,
+ -95,
+ -101,
+ -86,
+ -87,
+ -90,
+ -97,
+ -83,
+ -89,
+ -87,
+ -101,
+ -91,
+ -91,
+ -93,
+ -99,
+ -83,
+ -94,
+ -85,
+ -82,
+ -96,
+ -96,
+ -85,
+ -90,
+ -99,
+ -98,
+ -84,
+ -89,
+ -98,
+ -85,
+ -90,
+ -99,
+ -112,
+ -90,
+ -102,
+ -86,
+ -93,
+ -94,
+ -93,
+ -86,
+ -104,
+ -82,
+ -102,
+ -94,
+ -99,
+ -103,
+ -83,
+ -86,
+ -85,
+ -86,
+ -85,
+ -98,
+ -97,
+ -98,
+ -95,
+ -93,
+ -91,
+ -84,
+ -84,
+ -93,
+ -90,
+ -98,
+ -99,
+ -97,
+ -91,
+ -99,
+ -103,
+ -86,
+ -82,
+ -100,
+ -100,
+ -83,
+ -84,
+ -118,
+ -84,
+ -97,
+ -100,
+ -117,
+ -91,
+ -86,
+ -100,
+ -102,
+ -88,
+ -89,
+ -89,
+ -102,
+ -102,
+ -91,
+ -98,
+ -99,
+ -95,
+ -92,
+ -100,
+ -92,
+ -91,
+ -84,
+ -96,
+ -98,
+ -100,
+ -102,
+ -101,
+ -86,
+ -93,
+ -85,
+ -100,
+ -84,
+ -98,
+ -98,
+ -100,
+ -85,
+ -89,
+ -99,
+ -91,
+ -116,
+ -106,
+ -98,
+ -84,
+ -102,
+ -98,
+ -94,
+ -95,
+ -100,
+ -99,
+ -99,
+ -83,
+ -84,
+ -88,
+ -91,
+ -80,
+ -85,
+ -87,
+ -119,
+ -99,
+ -104,
+ -93,
+ -100,
+ -94,
+ -99,
+ -96,
+ -81,
+ -102,
+ -93,
+ -99,
+ -119,
+ -100,
+ -102,
+ -98,
+ -101,
+ -102,
+ -99,
+ -112,
+ -118,
+ -91,
+ -100,
+ -87,
+ -88,
+ -100,
+ -97,
+ -96,
+ -101,
+ -84,
+ -87,
+ -101,
+ -91,
+ -96,
+ -87,
+ -101,
+ -93,
+ -96,
+ -88,
+ -99,
+ -97,
+ -114,
+ -98,
+ -83,
+ -85,
+ -91,
+ -90,
+ -87,
+ -86,
+ -86,
+ -82,
+ -85,
+ -86,
+ -84,
+ -97,
+ -87,
+ -95,
+ -103,
+ -86,
+ -100,
+ -99,
+ -100,
+ -81,
+ -94,
+ -100,
+ -101,
+ -82,
+ -90,
+ -83,
+ -118,
+ -102,
+ -98,
+ -84,
+ -96,
+ -85,
+ -111,
+ -99,
+ -87,
+ -101,
+ -101,
+ -99,
+ -98,
+ -93,
+ -101,
+ -101,
+ -88,
+ -95,
+ -84,
+ -97,
+ -102,
+ -97,
+ -89,
+ -98,
+ -97,
+ -101,
+ -95,
+ -100,
+ -85,
+ -84,
+ -96,
+ -95,
+ -84,
+ -92,
+ -103,
+ -119,
+ -87,
+ -90,
+ -95,
+ -84,
+ -84,
+ -83,
+ -99,
+ -102,
+ -91,
+ -88,
+ -100,
+ -95,
+ -99,
+ -102,
+ -88,
+ -86,
+ -92,
+ -91,
+ -84,
+ -88,
+ -86,
+ -98,
+ -83,
+ -87,
+ -97,
+ -96,
+ -104,
+ -94,
+ -98,
+ -85,
+ -113,
+ -86,
+ -98,
+ -100,
+ -91,
+ -88,
+ -100,
+ -95,
+ -88,
+ -85,
+ -91,
+ -88,
+ -88,
+ -88,
+ -100,
+ -84,
+ -101,
+ -86,
+ -103,
+ -93,
+ -89,
+ -98,
+ -84,
+ -104,
+ -115,
+ -81,
+ -88,
+ -97,
+ -88,
+ -89,
+ -80,
+ -96,
+ -98,
+ -99,
+ -102,
+ -102,
+ -83,
+ -93,
+ -98,
+ -84,
+ -102,
+ -113,
+ -84,
+ -86,
+ -84,
+ -102,
+ -84,
+ -112,
+ -104,
+ -81,
+ -98,
+ -96,
+ -100,
+ -90,
+ -85,
+ -98,
+ -98,
+ -82,
+ -101,
+ -97,
+ -98,
+ -89,
+ -93,
+ -97,
+ -94,
+ -90,
+ -85,
+ -97,
+ -103,
+ -84,
+ -89,
+ -97,
+ -94,
+ -100,
+ -89,
+ -99,
+ -95,
+ -86,
+ -92,
+ -86,
+ -89,
+ -89,
+ -114,
+ -93,
+ -101,
+ -102,
+ -102,
+ -96,
+ -88,
+ -88,
+ -84,
+ -99,
+ -99,
+ -119,
+ -95,
+ -101,
+ -84,
+ -90,
+ -87,
+ -92,
+ -104,
+ -88,
+ -83,
+ -90,
+ -96,
+ -85,
+ -113,
+ -98,
+ -93,
+ -97,
+ -101,
+ -94,
+ -95,
+ -103,
+ -101,
+ -100,
+ -100,
+ -103,
+ -86,
+ -85,
+ -84,
+ -95,
+ -85,
+ -97,
+ -86,
+ -84,
+ -87,
+ -100,
+ -90,
+ -114,
+ -84,
+ -84,
+ -96,
+ -100,
+ -95,
+ -90,
+ -88,
+ -91,
+ -101,
+ -100,
+ -99,
+ -95,
+ -101,
+ -119,
+ -117,
+ -85,
+ -118,
+ -100,
+ -116,
+ -91,
+ -84,
+ -92,
+ -99,
+ -90,
+ -99,
+ -103,
+ -85,
+ -97,
+ -98,
+ -99,
+ -89,
+ -102,
+ -99,
+ -89,
+ -89,
+ -99,
+ -85,
+ -87,
+ -98,
+ -103,
+ -98,
+ -93,
+ -98,
+ -93,
+ -89,
+ -90,
+ -95,
+ -95,
+ -86,
+ -91,
+ -102,
+ -86,
+ -101,
+ -84,
+ -83,
+ -87,
+ -99,
+ -88,
+ -86,
+ -99,
+ -95,
+ -101,
+ -94,
+ -96,
+ -93,
+ -99,
+ -117,
+ -90,
+ -103,
+ -103,
+ -89,
+ -97,
+ -98,
+ -86,
+ -97,
+ -84,
+ -84,
+ -82,
+ -85,
+ -89,
+ -82,
+ -100,
+ -101,
+ -102,
+ -83,
+ -94,
+ -93,
+ -83,
+ -90,
+ -85,
+ -90,
+ -93,
+ -97,
+ -86,
+ -88,
+ -101,
+ -97,
+ -101,
+ -98,
+ -99,
+ -98,
+ -99,
+ -95,
+ -102,
+ -116,
+ -119,
+ -97,
+ -90,
+ -93,
+ -90,
+ -102,
+ -99,
+ -99,
+ -97,
+ -82,
+ -83,
+ -100,
+ -102,
+ -101,
+ -91,
+ -99,
+ -100,
+ -102,
+ -101,
+ -100,
+ -96,
+ -86,
+ -87,
+ -99,
+ -82,
+ -100,
+ -90,
+ -119,
+ -100,
+ -83,
+ -85,
+ -85,
+ -99,
+ -93,
+ -96,
+ -103,
+ -101,
+ -100,
+ -103,
+ -97,
+ -89,
+ -90,
+ -88,
+ -100,
+ -99,
+ -98,
+ -101,
+ -93,
+ -98,
+ -95,
+ -84,
+ -99,
+ -93,
+ -98,
+ -88,
+ -95,
+ -102,
+ -97,
+ -86,
+ -89,
+ -101,
+ -100,
+ -98,
+ -87,
+ -98,
+ -99,
+ -100,
+ -97,
+ -99,
+ -83,
+ -90,
+ -90,
+ -88,
+ -92,
+ -96,
+ -103,
+ -101,
+ -88,
+ -85,
+ -114,
+ -114,
+ -84,
+ -103,
+ -103,
+ -98,
+ -97,
+ -84,
+ -86,
+ -97,
+ -83,
+ -100,
+ -99,
+ -100,
+ -83,
+ -90,
+ -103,
+ -95,
+ -101,
+ -102,
+ -96,
+ -93,
+ -87,
+ -86,
+ -87,
+ -88,
+ -86,
+ -100,
+ -92,
+ -100,
+ -98,
+ -95,
+ -86,
+ -97,
+ -96,
+ -95,
+ -90,
+ -118,
+ -99,
+ -103,
+ -93,
+ -86,
+ -84,
+ -101,
+ -87,
+ -87,
+ -97,
+ -100,
+ -88,
+ -95,
+ -100,
+ -96,
+ -94,
+ -96,
+ -86,
+ -85,
+ -91,
+ -91,
+ -102,
+ -92,
+ -93,
+ -112,
+ -99,
+ -91,
+ -100,
+ -93,
+ -100,
+ -117,
+ -86,
+ -101,
+ -84,
+ -88,
+ -98,
+ -90,
+ -100,
+ -99,
+ -88,
+ -102,
+ -86,
+ -102,
+ -97,
+ -100,
+ -90,
+ -102,
+ -99,
+ -94,
+ -87,
+ -97,
+ -101,
+ -92,
+ -86,
+ -84,
+ -91,
+ -101,
+ -114,
+ -95,
+ -86,
+ -98,
+ -99,
+ -104,
+ -96,
+ -86,
+ -90,
+ -87,
+ -101,
+ -96,
+ -100,
+ -98,
+ -85,
+ -83,
+ -94,
+ -98,
+ -101,
+ -99,
+ -89,
+ -90,
+ -95,
+ -94,
+ -118,
+ -81,
+ -84,
+ -102,
+ -88,
+ -87,
+ -100,
+ -102,
+ -93,
+ -85,
+ -102,
+ -99,
+ -88,
+ -100,
+ -99,
+ -93,
+ -103,
+ -95,
+ -91,
+ -94,
+ -98,
+ -101,
+ -97,
+ -84,
+ -100,
+ -93,
+ -103,
+ -91,
+ -94,
+ -90,
+ -92,
+ -100,
+ -115,
+ -88,
+ -96,
+ -101,
+ -98,
+ -82,
+ -91,
+ -93,
+ -102,
+ -83,
+ -97,
+ -97,
+ -87,
+ -87,
+ -96,
+ -87,
+ -90,
+ -90,
+ -96,
+ -87,
+ -84,
+ -87,
+ -98,
+ -103,
+ -85,
+ -84,
+ -92,
+ -84,
+ -97,
+ -99,
+ -96,
+ -93,
+ -100,
+ -115,
+ -85,
+ -85,
+ -94,
+ -118,
+ -83,
+ -100,
+ -98,
+ -87,
+ -97,
+ -94,
+ -114,
+ -90,
+ -100,
+ -91,
+ -84,
+ -113,
+ -89,
+ -103,
+ -88,
+ -101,
+ -91,
+ -104,
+ -97,
+ -99,
+ -90,
+ -84,
+ -114,
+ -112,
+ -94,
+ -98,
+ -98,
+ -84,
+ -94,
+ -93,
+ -99,
+ -101,
+ -98,
+ -90,
+ -85,
+ -103,
+ -86,
+ -104,
+ -93,
+ -91,
+ -101,
+ -90,
+ -93,
+ -94,
+ -97,
+ -102,
+ -96,
+ -102,
+ -97,
+ -100,
+ -118,
+ -94,
+ -100,
+ -87,
+ -99,
+ -84,
+ -96,
+ -82,
+ -91,
+ -98,
+ -101,
+ -99,
+ -86,
+ -84,
+ -93,
+ -85,
+ -96,
+ -100,
+ -83,
+ -93,
+ -102,
+ -88,
+ -91,
+ -116,
+ -90,
+ -92,
+ -89,
+ -100,
+ -101,
+ -118,
+ -96,
+ -88,
+ -87,
+ -98,
+ -95,
+ -93,
+ -116,
+ -101,
+ -99,
+ -114,
+ -97,
+ -93,
+ -102,
+ -96,
+ -83,
+ -91,
+ -118,
+ -90,
+ -91,
+ -84,
+ -99,
+ -100,
+ -83,
+ -101,
+ -87,
+ -93,
+ -99,
+ -101,
+ -97,
+ -115,
+ -96,
+ -91,
+ -87,
+ -86,
+ -94,
+ -83,
+ -95,
+ -100,
+ -98,
+ -87,
+ -91,
+ -84,
+ -83,
+ -85,
+ -84,
+ -93,
+ -91,
+ -99,
+ -90,
+ -96,
+ -100,
+ -103,
+ -97,
+ -97,
+ -83,
+ -99,
+ -99,
+ -85,
+ -95,
+ -91,
+ -94,
+ -85,
+ -95,
+ -97,
+ -100,
+ -94,
+ -88,
+ -87,
+ -102,
+ -100,
+ -99,
+ -91,
+ -98,
+ -82,
+ -99,
+ -94,
+ -84,
+ -89,
+ -87,
+ -114,
+ -95,
+ -97,
+ -97,
+ -98,
+ -88,
+ -101,
+ -85,
+ -97,
+ -94,
+ -99,
+ -102,
+ -118,
+ -99,
+ -92,
+ -98,
+ -91,
+ -91,
+ -91,
+ -95,
+ -97,
+ -95,
+ -100,
+ -95,
+ -93,
+ -98,
+ -90,
+ -87,
+ -102,
+ -102,
+ -89,
+ -89,
+ -98,
+ -93,
+ -113,
+ -92,
+ -92,
+ -100,
+ -89,
+ -98,
+ -99,
+ -100,
+ -90,
+ -95,
+ -98,
+ -101,
+ -99,
+ -92,
+ -92,
+ -99,
+ -84,
+ -102,
+ -91,
+ -113,
+ -86,
+ -98,
+ -103,
+ -102,
+ -119,
+ -85,
+ -101,
+ -93,
+ -99,
+ -103,
+ -82,
+ -103,
+ -97,
+ -94,
+ -99,
+ -102,
+ -101,
+ -87,
+ -96,
+ -101,
+ -99,
+ -99,
+ -102,
+ -116,
+ -98,
+ -100,
+ -85,
+ -93,
+ -99,
+ -98,
+ -87,
+ -101,
+ -84,
+ -113,
+ -83,
+ -98,
+ -92,
+ -96,
+ -114,
+ -102,
+ -89,
+ -91,
+ -102,
+ -101,
+ -98,
+ -100,
+ -88,
+ -100,
+ -97,
+ -87,
+ -103,
+ -98,
+ -101,
+ -85,
+ -93,
+ -99,
+ -97,
+ -91,
+ -98,
+ -86,
+ -101,
+ -117,
+ -84,
+ -95,
+ -101,
+ -83,
+ -100,
+ -100,
+ -117,
+ -99,
+ -83,
+ -91,
+ -102,
+ -83,
+ -101,
+ -95,
+ -98,
+ -101,
+ -94,
+ -85,
+ -94,
+ -114,
+ -117,
+ -86,
+ -98,
+ -100,
+ -91,
+ -91,
+ -84,
+ -103,
+ -102,
+ -88,
+ -114,
+ -103,
+ -98,
+ -87,
+ -117,
+ -101,
+ -84,
+ -98,
+ -94,
+ -98,
+ -95,
+ -98,
+ -99,
+ -96,
+ -97,
+ -89,
+ -90,
+ -100,
+ -100,
+ -99,
+ -86,
+ -100,
+ -114,
+ -100,
+ -103,
+ -87,
+ -90,
+ -86,
+ -93,
+ -94,
+ -90,
+ -101,
+ -99,
+ -85,
+ -87,
+ -83,
+ -89,
+ -86,
+ -84,
+ -86,
+ -97,
+ -88,
+ -84,
+ -96,
+ -86,
+ -83,
+ -104,
+ -87,
+ -99,
+ -101,
+ -96,
+ -102,
+ -86,
+ -101,
+ -83,
+ -105,
+ -92,
+ -95,
+ -82,
+ -98,
+ -84,
+ -88,
+ -97,
+ -98,
+ -99,
+ -85,
+ -93,
+ -98,
+ -96,
+ -100,
+ -102,
+ -96,
+ -92,
+ -118,
+ -100,
+ -97,
+ -98,
+ -119,
+ -86,
+ -93,
+ -99,
+ -90,
+ -103,
+ -100,
+ -99,
+ -86,
+ -84,
+ -101,
+ -99,
+ -92,
+ -118,
+ -92,
+ -101,
+ -95,
+ -101,
+ -104,
+ -91,
+ -91,
+ -100,
+ -86,
+ -116,
+ -114,
+ -98,
+ -102,
+ -94,
+ -103,
+ -100,
+ -98,
+ -97,
+ -97,
+ -98,
+ -93,
+ -119,
+ -99,
+ -119,
+ -86,
+ -101,
+ -90,
+ -104,
+ -96,
+ -81,
+ -88,
+ -88,
+ -94,
+ -118,
+ -97,
+ -102,
+ -100,
+ -92,
+ -100,
+ -100,
+ -99,
+ -99,
+ -90,
+ -96,
+ -99,
+ -95,
+ -102,
+ -101,
+ -101,
+ -91,
+ -85,
+ -97,
+ -101,
+ -96,
+ -85,
+ -100,
+ -85,
+ -99,
+ -99,
+ -96,
+ -105,
+ -96,
+ -91,
+ -87,
+ -97,
+ -98,
+ -97,
+ -97,
+ -100,
+ -87,
+ -89,
+ -96,
+ -95,
+ -85,
+ -96,
+ -101,
+ -100,
+ -97,
+ -96,
+ -86,
+ -97,
+ -116,
+ -99,
+ -103,
+ -100,
+ -98,
+ -101,
+ -99,
+ -99,
+ -101,
+ -101,
+ -99,
+ -113,
+ -102,
+ -99,
+ -87,
+ -100,
+ -100,
+ -96,
+ -88,
+ -99,
+ -89,
+ -103,
+ -93,
+ -87,
+ -91,
+ -101,
+ -90,
+ -94,
+ -89,
+ -102,
+ -100,
+ -96,
+ -92,
+ -97,
+ -96,
+ -112,
+ -88,
+ -94,
+ -97,
+ -103,
+ -100,
+ -96,
+ -95,
+ -101,
+ -98,
+ -101,
+ -88,
+ -88,
+ -98,
+ -87,
+ -86,
+ -99,
+ -84,
+ -90,
+ -97,
+ -88,
+ -102,
+ -99,
+ -85,
+ -98,
+ -118,
+ -102,
+ -84,
+ -104,
+ -89,
+ -84,
+ -86,
+ -102,
+ -89,
+ -84,
+ -97,
+ -98,
+ -89,
+ -91,
+ -88,
+ -85,
+ -88,
+ -87,
+ -87,
+ -92,
+ -87,
+ -100,
+ -96,
+ -100,
+ -95,
+ -88,
+ -99,
+ -113,
+ -101,
+ -92,
+ -83,
+ -103,
+ -101,
+ -98,
+ -99,
+ -101,
+ -88,
+ -93,
+ -90,
+ -85,
+ -95,
+ -97,
+ -90,
+ -90,
+ -101,
+ -101,
+ -89,
+ -86,
+ -100,
+ -98,
+ -94,
+ -91,
+ -100,
+ -86,
+ -88,
+ -96,
+ -97,
+ -100,
+ -95,
+ -84,
+ -96,
+ -90,
+ -118,
+ -94,
+ -102,
+ -100,
+ -101,
+ -97,
+ -103,
+ -89,
+ -102,
+ -91,
+ -88,
+ -88,
+ -86,
+ -85,
+ -88,
+ -99,
+ -99,
+ -83,
+ -85,
+ -85,
+ -91,
+ -98,
+ -84,
+ -104,
+ -84,
+ -116,
+ -99,
+ -100,
+ -97,
+ -101,
+ -98,
+ -100,
+ -90,
+ -98,
+ -117,
+ -86,
+ -99,
+ -97,
+ -100,
+ -84,
+ -102,
+ -87,
+ -103,
+ -85,
+ -87,
+ -93,
+ -93,
+ -90,
+ -100,
+ -85,
+ -93,
+ -102,
+ -117,
+ -84,
+ -85,
+ -101,
+ -92,
+ -99,
+ -104,
+ -98,
+ -95,
+ -85,
+ -101,
+ -101,
+ -100,
+ -98,
+ -91,
+ -102,
+ -99,
+ -98,
+ -98,
+ -90,
+ -96,
+ -96,
+ -85,
+ -97,
+ -101,
+ -88,
+ -85,
+ -100,
+ -98,
+ -93,
+ -99,
+ -116,
+ -98,
+ -97,
+ -98,
+ -100,
+ -94,
+ -86,
+ -93,
+ -97,
+ -119,
+ -99,
+ -84,
+ -95,
+ -95,
+ -118,
+ -101,
+ -99,
+ -98,
+ -96,
+ -94,
+ -93,
+ -95,
+ -89,
+ -101,
+ -85,
+ -98,
+ -102,
+ -87,
+ -95,
+ -94,
+ -94,
+ -100,
+ -89,
+ -93,
+ -88,
+ -97,
+ -102,
+ -104,
+ -88,
+ -99,
+ -118,
+ -86,
+ -100,
+ -94,
+ -97,
+ -82,
+ -99,
+ -91,
+ -97,
+ -116,
+ -98,
+ -84,
+ -99,
+ -99,
+ -91,
+ -83,
+ -94,
+ -96,
+ -99,
+ -82,
+ -95,
+ -83,
+ -94,
+ -102,
+ -100,
+ -101,
+ -100,
+ -97,
+ -84,
+ -101,
+ -93,
+ -96,
+ -92,
+ -92,
+ -99,
+ -99,
+ -118,
+ -98,
+ -94,
+ -98,
+ -101,
+ -84,
+ -88,
+ -118,
+ -103,
+ -101,
+ -88,
+ -98,
+ -98,
+ -117,
+ -100,
+ -104,
+ -87,
+ -82,
+ -99,
+ -101,
+ -93,
+ -89,
+ -101,
+ -84,
+ -100,
+ -99,
+ -87,
+ -100,
+ -101,
+ -95,
+ -96,
+ -97,
+ -118,
+ -84,
+ -94,
+ -93,
+ -117,
+ -117,
+ -98,
+ -100,
+ -97,
+ -85,
+ -91,
+ -99,
+ -97,
+ -93,
+ -90,
+ -100,
+ -97,
+ -85,
+ -87,
+ -96,
+ -98,
+ -117,
+ -97,
+ -117,
+ -95,
+ -98,
+ -93,
+ -117,
+ -88,
+ -83,
+ -86,
+ -81,
+ -87,
+ -102,
+ -99,
+ -89,
+ -117,
+ -103,
+ -88,
+ -93,
+ -100,
+ -96,
+ -99,
+ -98,
+ -85,
+ -94,
+ -92,
+ -98,
+ -97,
+ -99,
+ -92,
+ -98,
+ -94,
+ -91,
+ -118,
+ -118,
+ -99,
+ -87,
+ -89,
+ -99,
+ -118,
+ -88,
+ -84,
+ -98,
+ -99,
+ -94,
+ -85,
+ -118,
+ -102,
+ -87,
+ -93,
+ -84,
+ -100,
+ -96,
+ -90,
+ -89,
+ -97,
+ -87,
+ -92,
+ -94,
+ -99,
+ -87,
+ -94,
+ -117,
+ -102,
+ -118,
+ -94,
+ -102,
+ -97,
+ -85,
+ -118,
+ -91,
+ -97,
+ -89,
+ -115,
+ -100,
+ -102,
+ -91,
+ -101,
+ -86,
+ -85,
+ -98,
+ -95,
+ -89,
+ -113,
+ -98,
+ -97,
+ -99,
+ -102,
+ -93,
+ -101,
+ -97,
+ -94,
+ -96,
+ -86,
+ -117,
+ -100,
+ -102,
+ -90,
+ -117,
+ -82,
+ -91,
+ -82,
+ -96,
+ -90,
+ -102,
+ -93,
+ -97,
+ -89,
+ -84,
+ -91,
+ -85,
+ -117,
+ -99,
+ -91,
+ -98,
+ -105,
+ -103,
+ -101,
+ -101,
+ -90,
+ -95,
+ -85,
+ -101,
+ -114,
+ -98,
+ -98,
+ -116,
+ -112,
+ -100,
+ -99,
+ -86,
+ -93,
+ -99,
+ -102,
+ -84,
+ -102,
+ -92,
+ -96,
+ -99,
+ -89,
+ -91,
+ -101,
+ -99,
+ -94,
+ -92,
+ -96,
+ -96,
+ -97,
+ -86,
+ -94,
+ -102,
+ -96,
+ -95,
+ -101,
+ -89,
+ -86,
+ -99,
+ -100,
+ -93,
+ -117,
+ -102,
+ -95,
+ -91,
+ -98,
+ -117,
+ -116,
+ -102,
+ -98,
+ -99,
+ -87,
+ -100,
+ -88,
+ -99,
+ -84,
+ -93,
+ -95,
+ -100,
+ -94,
+ -96,
+ -102,
+ -97,
+ -92,
+ -87,
+ -99,
+ -100,
+ -98,
+ -92,
+ -100,
+ -94,
+ -100,
+ -87,
+ -97,
+ -99,
+ -105,
+ -101,
+ -93,
+ -88,
+ -98,
+ -99,
+ -118,
+ -98,
+ -102,
+ -92,
+ -96,
+ -95,
+ -103,
+ -89,
+ -98,
+ -91,
+ -100,
+ -96,
+ -117,
+ -86,
+ -101,
+ -98,
+ -92,
+ -92,
+ -89,
+ -94,
+ -117,
+ -100,
+ -117,
+ -92,
+ -90,
+ -84,
+ -100,
+ -98,
+ -86,
+ -96,
+ -96,
+ -84,
+ -100,
+ -102,
+ -89,
+ -94,
+ -95,
+ -87,
+ -101,
+ -102,
+ -88,
+ -95,
+ -100,
+ -100,
+ -101,
+ -84,
+ -98,
+ -95,
+ -98,
+ -99,
+ -98,
+ -86,
+ -93,
+ -98,
+ -101,
+ -101,
+ -97,
+ -100,
+ -97,
+ -101,
+ -84,
+ -116,
+ -100,
+ -95,
+ -87,
+ -97,
+ -98,
+ -99,
+ -99,
+ -95,
+ -92,
+ -83,
+ -92,
+ -99,
+ -91,
+ -99,
+ -85,
+ -99,
+ -98,
+ -94,
+ -113,
+ -100,
+ -100,
+ -101,
+ -99,
+ -98,
+ -84,
+ -92,
+ -117,
+ -99,
+ -94,
+ -90,
+ -83,
+ -99,
+ -102,
+ -86,
+ -85,
+ -96,
+ -88,
+ -96,
+ -100,
+ -101,
+ -100,
+ -98,
+ -103,
+ -100,
+ -99,
+ -97,
+ -86,
+ -101,
+ -95,
+ -101,
+ -97,
+ -99,
+ -118,
+ -97,
+ -100,
+ -95,
+ -87,
+ -97,
+ -118,
+ -99,
+ -92,
+ -100,
+ -97,
+ -89,
+ -88,
+ -100,
+ -112,
+ -82,
+ -94,
+ -97,
+ -117,
+ -88,
+ -104,
+ -87,
+ -99,
+ -99,
+ -99,
+ -98,
+ -98,
+ -99,
+ -96,
+ -90,
+ -102,
+ -92,
+ -102,
+ -97,
+ -102,
+ -86,
+ -85,
+ -102,
+ -96,
+ -98,
+ -117,
+ -85,
+ -98,
+ -117,
+ -97,
+ -113,
+ -93,
+ -101,
+ -100,
+ -117,
+ -93,
+ -95,
+ -99,
+ -118,
+ -88,
+ -98,
+ -96,
+ -119,
+ -90,
+ -91,
+ -92,
+ -97,
+ -98,
+ -95,
+ -99,
+ -99,
+ -85,
+ -97,
+ -104,
+ -91,
+ -91,
+ -99,
+ -85,
+ -97,
+ -92,
+ -101,
+ -100,
+ -85,
+ -117,
+ -100,
+ -98,
+ -99,
+ -96,
+ -117,
+ -94,
+ -98,
+ -91,
+ -98,
+ -102,
+ -114,
+ -98,
+ -97,
+ -91,
+ -98,
+ -83,
+ -95,
+ -105,
+ -96,
+ -98,
+ -118,
+ -101,
+ -97,
+ -95,
+ -97,
+ -116,
+ -99,
+ -99,
+ -102,
+ -85,
+ -95,
+ -117,
+ -97,
+ -99,
+ -93,
+ -97,
+ -101,
+ -90,
+ -95,
+ -97,
+ -98,
+ -88,
+ -94,
+ -101,
+ -84,
+ -86,
+ -117,
+ -100,
+ -100,
+ -98,
+ -117,
+ -98,
+ -90,
+ -101,
+ -99,
+ -116,
+ -100,
+ -87,
+ -102,
+ -103,
+ -98,
+ -90,
+ -92,
+ -95,
+ -95,
+ -100,
+ -118,
+ -97,
+ -118,
+ -96,
+ -97,
+ -103,
+ -100,
+ -84,
+ -85,
+ -99,
+ -98,
+ -100,
+ -99,
+ -116,
+ -118,
+ -98,
+ -102,
+ -92,
+ -98,
+ -100,
+ -83,
+ -86,
+ -94,
+ -99,
+ -98,
+ -104,
+ -97,
+ -118,
+ -117,
+ -97,
+ -88,
+ -100,
+ -118,
+ -102,
+ -83,
+ -92,
+ -93,
+ -93,
+ -100,
+ -101,
+ -97,
+ -116,
+ -101,
+ -117,
+ -96,
+ -94,
+ -94,
+ -100,
+ -102,
+ -97,
+ -97,
+ -95,
+ -97,
+ -98,
+ -100,
+ -103,
+ -100,
+ -91,
+ -117,
+ -96,
+ -85,
+ -90,
+ -89,
+ -87,
+ -89,
+ -102,
+ -117,
+ -118,
+ -99,
+ -99,
+ -103,
+ -117,
+ -101,
+ -86,
+ -97,
+ -95,
+ -100,
+ -95,
+ -98,
+ -98,
+ -101,
+ -83,
+ -95,
+ -95,
+ -97,
+ -102,
+ -93,
+ -95,
+ -86,
+ -98,
+ -96,
+ -90,
+ -97,
+ -89,
+ -102,
+ -94,
+ -93,
+ -84,
+ -100,
+ -102,
+ -86,
+ -101,
+ -92,
+ -102,
+ -91,
+ -102,
+ -103,
+ -103,
+ -97,
+ -114,
+ -90,
+ -100,
+ -87,
+ -117,
+ -98,
+ -98,
+ -88,
+ -103,
+ -91,
+ -98,
+ -100,
+ -88,
+ -99,
+ -117,
+ -96,
+ -87,
+ -99,
+ -88,
+ -102,
+ -101,
+ -96,
+ -96,
+ -117,
+ -100,
+ -97,
+ -90,
+ -98,
+ -99,
+ -101,
+ -94,
+ -84,
+ -99,
+ -96,
+ -113,
+ -102,
+ -89,
+ -95,
+ -96,
+ -103,
+ -86,
+ -117,
+ -99,
+ -97,
+ -101,
+ -93,
+ -84,
+ -86,
+ -99,
+ -86,
+ -101,
+ -93,
+ -92,
+ -117,
+ -99,
+ -95,
+ -100,
+ -98,
+ -99,
+ -103,
+ -101,
+ -88,
+ -98,
+ -93,
+ -99,
+ -117,
+ -82,
+ -100,
+ -96,
+ -101,
+ -100,
+ -88,
+ -99,
+ -117,
+ -91,
+ -101,
+ -117,
+ -117,
+ -90,
+ -97,
+ -117,
+ -90,
+ -117,
+ -102,
+ -97,
+ -87,
+ -84,
+ -93,
+ -99,
+ -84,
+ -90,
+ -96,
+ -87,
+ -117,
+ -103,
+ -86,
+ -101,
+ -117,
+ -97,
+ -95,
+ -118,
+ -96,
+ -117,
+ -97,
+ -98,
+ -95,
+ -98,
+ -91,
+ -90,
+ -117,
+ -98,
+ -100,
+ -98,
+ -84,
+ -100,
+ -96,
+ -98,
+ -86,
+ -96,
+ -96,
+ -103,
+ -95,
+ -85,
+ -96,
+ -95,
+ -98,
+ -100,
+ -99,
+ -100,
+ -87,
+ -101,
+ -100,
+ -101,
+ -117,
+ -99,
+ -84,
+ -99,
+ -99,
+ -99,
+ -100,
+ -98,
+ -90,
+ -101,
+ -98,
+ -101,
+ -117,
+ -85,
+ -93,
+ -86,
+ -92,
+ -101,
+ -94,
+ -87,
+ -118,
+ -95,
+ -101,
+ -89,
+ -93,
+ -102,
+ -92,
+ -91,
+ -96,
+ -94,
+ -98,
+ -97,
+ -100,
+ -102,
+ -101,
+ -84,
+ -93,
+ -92,
+ -88,
+ -89,
+ -89,
+ -101,
+ -101,
+ -117,
+ -86,
+ -118,
+ -117,
+ -100,
+ -117,
+ -101,
+ -98,
+ -98,
+ -100,
+ -97,
+ -99,
+ -98,
+ -89,
+ -100,
+ -99,
+ -102,
+ -93,
+ -102,
+ -98,
+ -101,
+ -92,
+ -101,
+ -99,
+ -101,
+ -117,
+ -100,
+ -90,
+ -102,
+ -99,
+ -98,
+ -104,
+ -99,
+ -94,
+ -96,
+ -117,
+ -97,
+ -99,
+ -102,
+ -117,
+ -98,
+ -99,
+ -92,
+ -100,
+ -84,
+ -90,
+ -97,
+ -87,
+ -91,
+ -99,
+ -117,
+ -97,
+ -116,
+ -98,
+ -87,
+ -94,
+ -97,
+ -97,
+ -97,
+ -117,
+ -89,
+ -118,
+ -96,
+ -100,
+ -118,
+ -101,
+ -85,
+ -94,
+ -92,
+ -93,
+ -89,
+ -117,
+ -84,
+ -95,
+ -100,
+ -103,
+ -87,
+ -93,
+ -117,
+ -87,
+ -96,
+ -101,
+ -86,
+ -96,
+ -104,
+ -92,
+ -93,
+ -99,
+ -117,
+ -98,
+ -90,
+ -98,
+ -96,
+ -99,
+ -100,
+ -117,
+ -90,
+ -101,
+ -117,
+ -102,
+ -94,
+ -98,
+ -103,
+ -87,
+ -99,
+ -102,
+ -85,
+ -88,
+ -118,
+ -100,
+ -99,
+ -90,
+ -95,
+ -99,
+ -89,
+ -85,
+ -117,
+ -97,
+ -85,
+ -99,
+ -94,
+ -116,
+ -94,
+ -100,
+ -117,
+ -96,
+ -117,
+ -84,
+ -97,
+ -118,
+ -97,
+ -100,
+ -94,
+ -101,
+ -118,
+ -85,
+ -97,
+ -118,
+ -100,
+ -84,
+ -93,
+ -98,
+ -98,
+ -85,
+ -94,
+ -99,
+ -97,
+ -118,
+ -99,
+ -94,
+ -104,
+ -89,
+ -99,
+ -99,
+ -97,
+ -95,
+ -100,
+ -96,
+ -117,
+ -84,
+ -94,
+ -88,
+ -89,
+ -97,
+ -87,
+ -99,
+ -103,
+ -86,
+ -101,
+ -91,
+ -98,
+ -102,
+ -97,
+ -118,
+ -116,
+ -117,
+ -98,
+ -100,
+ -95,
+ -99,
+ -100,
+ -117,
+ -95,
+ -99,
+ -87,
+ -93,
+ -82,
+ -97,
+ -87,
+ -117,
+ -94,
+ -96,
+ -97,
+ -99,
+ -97,
+ -98,
+ -92,
+ -99,
+ -100,
+ -101,
+ -95,
+ -96,
+ -117,
+ -97,
+ -90,
+ -118,
+ -117,
+ -98,
+ -99,
+ -97,
+ -92,
+ -118,
+ -117,
+ -96,
+ -101,
+ -98,
+ -85,
+ -86,
+ -87,
+ -85,
+ -117,
+ -91,
+ -101,
+ -102,
+ -100,
+ -101,
+ -99,
+ -98,
+ -100,
+ -97,
+ -96,
+ -99,
+ -100,
+ -99,
+ -100,
+ -118,
+ -100,
+ -89,
+ -100,
+ -98,
+ -100,
+ -98,
+ -101,
+ -94,
+ -93,
+ -102,
+ -100,
+ -99,
+ -87,
+ -117,
+ -99,
+ -86,
+ -97,
+ -117,
+ -101,
+ -98,
+ -98,
+ -87,
+ -97,
+ -95,
+ -99,
+ -118,
+ -117,
+ -95,
+ -87,
+ -101,
+ -87,
+ -83,
+ -118,
+ -101,
+ -102,
+ -98,
+ -97,
+ -95,
+ -117,
+ -97,
+ -88,
+ -98,
+ -117,
+ -92,
+ -118,
+ -98,
+ -99,
+ -102,
+ -117,
+ -98,
+ -117,
+ -96,
+ -99,
+ -117,
+ -100,
+ -97,
+ -98,
+ -117,
+ -99,
+ -118,
+ -90,
+ -99,
+ -98,
+ -91,
+ -117,
+ -102,
+ -105,
+ -95,
+ -117,
+ -118,
+ -100,
+ -99,
+ -95,
+ -99,
+ -84,
+ -115,
+ -103,
+ -100,
+ -94,
+ -82,
+ -99,
+ -100,
+ -98,
+ -100,
+ -103,
+ -96,
+ -98,
+ -96,
+ -101,
+ -83,
+ -84,
+ -117,
+ -98,
+ -99,
+ -91,
+ -98,
+ -100,
+ -90,
+ -103,
+ -118,
+ -89,
+ -96,
+ -98,
+ -89,
+ -86,
+ -98,
+ -97,
+ -83,
+ -89,
+ -85,
+ -99,
+ -87,
+ -90,
+ -100,
+ -95,
+ -102,
+ -91,
+ -91,
+ -96,
+ -97,
+ -100,
+ -95,
+ -117,
+ -101,
+ -100,
+ -99,
+ -84,
+ -90,
+ -96,
+ -86,
+ -88,
+ -89,
+ -86,
+ -98,
+ -102,
+ -99,
+ -100,
+ -86,
+ -99,
+ -117,
+ -98,
+ -103,
+ -101,
+ -89,
+ -118,
+ -98,
+ -98,
+ -99,
+ -100,
+ -101,
+ -98,
+ -89,
+ -101,
+ -93,
+ -85,
+ -98,
+ -97,
+ -96,
+ -97,
+ -100,
+ -102,
+ -102,
+ -118,
+ -91,
+ -118,
+ -101,
+ -118,
+ -99,
+ -97,
+ -84,
+ -101,
+ -99,
+ -94,
+ -117,
+ -93,
+ -96,
+ -96,
+ -100,
+ -101,
+ -100,
+ -89,
+ -100,
+ -118,
+ -93,
+ -95,
+ -96,
+ -89,
+ -91,
+ -92,
+ -100,
+ -99,
+ -83,
+ -93,
+ -117,
+ -99,
+ -100,
+ -101,
+ -99,
+ -96,
+ -99,
+ -88,
+ -97,
+ -103,
+ -117,
+ -117,
+ -99,
+ -117,
+ -88,
+ -95,
+ -98,
+ -100,
+ -98,
+ -94,
+ -87,
+ -102,
+ -102,
+ -98,
+ -92,
+ -98,
+ -89,
+ -98,
+ -97,
+ -95,
+ -102,
+ -85,
+ -88,
+ -91,
+ -99,
+ -100,
+ -101,
+ -99,
+ -101,
+ -117,
+ -103,
+ -93,
+ -94,
+ -117,
+ -97,
+ -117,
+ -100,
+ -102,
+ -99,
+ -99,
+ -93,
+ -99,
+ -88,
+ -102,
+ -102,
+ -118,
+ -118,
+ -93,
+ -118,
+ -100,
+ -99,
+ -100,
+ -118,
+ -88,
+ -101,
+ -99,
+ -96,
+ -91,
+ -89,
+ -99,
+ -118,
+ -97,
+ -90,
+ -102,
+ -100,
+ -97,
+ -117,
+ -102,
+ -95,
+ -100,
+ -91,
+ -93,
+ -100,
+ -88,
+ -101,
+ -100,
+ -93,
+ -98,
+ -93,
+ -118,
+ -101,
+ -98,
+ -118,
+ -83,
+ -97,
+ -118,
+ -99,
+ -117,
+ -96,
+ -99,
+ -118,
+ -117,
+ -95,
+ -118,
+ -101,
+ -118,
+ -83,
+ -92,
+ -100,
+ -86,
+ -102,
+ -97,
+ -102,
+ -94,
+ -89,
+ -104,
+ -117,
+ -102,
+ -101,
+ -98,
+ -100,
+ -118,
+ -94,
+ -117,
+ -96,
+ -99,
+ -100,
+ -97,
+ -99,
+ -101,
+ -99,
+ -98,
+ -99,
+ -100,
+ -99,
+ -100,
+ -96,
+ -98,
+ -100,
+ -88,
+ -94,
+ -102,
+ -96,
+ -97,
+ -101,
+ -118,
+ -86,
+ -98,
+ -117,
+ -95,
+ -100,
+ -117,
+ -98,
+ -94,
+ -99,
+ -118,
+ -118,
+ -93,
+ -118,
+ -98,
+ -118,
+ -102,
+ -117,
+ -88,
+ -97,
+ -83,
+ -101,
+ -100,
+ -89,
+ -99,
+ -96,
+ -96,
+ -102,
+ -101,
+ -89,
+ -99,
+ -99,
+ -100,
+ -98,
+ -118,
+ -97,
+ -117,
+ -102,
+ -98,
+ -99,
+ -93,
+ -100,
+ -97,
+ -99,
+ -103,
+ -87,
+ -91,
+ -97,
+ -118,
+ -99,
+ -99,
+ -85,
+ -99,
+ -99,
+ -100,
+ -98,
+ -101,
+ -97,
+ -98,
+ -91,
+ -98,
+ -99,
+ -97,
+ -90,
+ -103,
+ -95,
+ -101,
+ -90,
+ -88,
+ -88,
+ -85,
+ -96,
+ -118,
+ -117,
+ -118,
+ -98,
+ -98,
+ -95,
+ -90,
+ -117,
+ -100,
+ -99,
+ -100,
+ -117,
+ -101,
+ -97,
+ -117,
+ -102,
+ -101,
+ -98,
+ -97,
+ -93,
+ -94,
+ -118,
+ -117,
+ -100,
+ -84,
+ -97,
+ -118,
+ -101,
+ -118,
+ -102,
+ -102,
+ -96,
+ -102,
+ -92,
+ -99,
+ -97,
+ -98,
+ -103,
+ -99,
+ -118,
+ -88,
+ -99,
+ -87,
+ -92,
+ -99,
+ -96,
+ -98,
+ -87,
+ -95,
+ -96,
+ -99,
+ -98,
+ -88,
+ -101,
+ -117,
+ -94,
+ -99,
+ -94,
+ -94,
+ -92,
+ -99,
+ -100,
+ -100,
+ -99,
+ -95,
+ -100,
+ -96,
+ -118,
+ -101,
+ -100,
+ -101,
+ -99,
+ -99,
+ -102,
+ -91,
+ -118,
+ -99,
+ -117,
+ -98,
+ -100,
+ -117,
+ -89,
+ -100,
+ -101,
+ -95,
+ -118,
+ -96,
+ -95,
+ -91,
+ -101,
+ -93,
+ -84,
+ -91,
+ -97,
+ -117,
+ -99,
+ -102,
+ -98,
+ -98,
+ -96,
+ -98,
+ -118,
+ -99,
+ -93,
+ -98,
+ -100,
+ -83,
+ -118,
+ -102,
+ -100,
+ -92,
+ -118,
+ -99,
+ -98,
+ -96,
+ -94,
+ -98,
+ -99,
+ -84,
+ -99,
+ -99,
+ -87,
+ -97,
+ -99,
+ -90,
+ -91,
+ -101,
+ -100,
+ -91,
+ -118,
+ -102,
+ -118,
+ -89,
+ -88,
+ -90,
+ -99,
+ -101,
+ -118,
+ -92,
+ -89,
+ -118,
+ -88,
+ -99,
+ -101,
+ -95,
+ -95,
+ -100,
+ -97,
+ -102,
+ -91,
+ -99,
+ -91,
+ -99,
+ -118,
+ -98,
+ -100,
+ -118,
+ -117,
+ -103,
+ -101,
+ -117,
+ -99,
+ -117,
+ -118,
+ -93,
+ -102,
+ -99,
+ -88,
+ -117,
+ -97,
+ -99,
+ -97,
+ -117,
+ -118,
+ -100,
+ -94,
+ -96,
+ -93,
+ -101,
+ -102,
+ -98,
+ -100,
+ -118,
+ -93,
+ -100,
+ -96,
+ -99,
+ -98,
+ -99,
+ -118,
+ -99,
+ -99,
+ -102,
+ -96,
+ -99,
+ -100,
+ -102,
+ -102,
+ -99,
+ -101,
+ -96,
+ -117,
+ -96,
+ -100,
+ -104,
+ -99,
+ -118,
+ -99,
+ -101,
+ -104,
+ -100,
+ -99,
+ -98,
+ -98,
+ -99,
+ -100,
+ -99,
+ -93,
+ -95,
+ -87,
+ -92,
+ -90,
+ -103,
+ -98,
+ -88,
+ -86,
+ -118,
+ -102,
+ -98,
+ -85,
+ -102,
+ -100,
+ -117,
+ -101,
+ -102,
+ -91,
+ -99,
+ -88,
+ -103,
+ -91,
+ -82,
+ -97,
+ -101,
+ -95,
+ -118,
+ -81,
+ -94,
+ -100,
+ -90,
+ -101,
+ -117,
+ -97,
+ -99,
+ -99,
+ -103,
+ -118,
+ -101,
+ -93,
+ -99,
+ -94,
+ -102,
+ -99,
+ -99,
+ -88,
+ -99,
+ -89,
+ -101,
+ -118,
+ -94,
+ -96,
+ -87,
+ -92,
+ -117,
+ -101,
+ -100,
+ -100,
+ -117,
+ -101,
+ -97,
+ -100,
+ -99,
+ -101,
+ -96,
+ -102,
+ -96,
+ -118,
+ -97,
+ -84,
+ -98,
+ -97,
+ -85,
+ -117,
+ -101,
+ -100,
+ -99,
+ -88,
+ -98,
+ -95,
+ -85,
+ -102,
+ -102,
+ -103,
+ -95,
+ -117,
+ -96,
+ -96,
+ -88,
+ -102,
+ -117,
+ -90,
+ -85,
+ -100,
+ -94,
+ -101,
+ -100,
+ -103,
+ -100,
+ -85,
+ -93,
+ -93,
+ -94,
+ -95,
+ -98,
+ -99,
+ -102,
+ -103,
+ -99,
+ -117,
+ -89,
+ -100,
+ -100,
+ -100,
+ -97,
+ -91,
+ -89,
+ -99,
+ -95,
+ -101,
+ -117,
+ -117,
+ -92,
+ -93,
+ -92,
+ -100,
+ -91,
+ -100,
+ -98,
+ -103,
+ -97,
+ -98,
+ -100,
+ -95,
+ -98,
+ -103,
+ -100,
+ -94,
+ -100,
+ -118,
+ -103,
+ -117,
+ -96,
+ -90,
+ -101,
+ -99,
+ -91,
+ -100,
+ -101,
+ -97,
+ -101,
+ -100,
+ -99,
+ -83,
+ -118,
+ -99,
+ -92,
+ -96,
+ -88,
+ -99,
+ -82,
+ -99,
+ -98,
+ -89,
+ -97,
+ -92,
+ -99,
+ -98,
+ -92,
+ -90,
+ -95,
+ -98,
+ -100,
+ -100,
+ -101,
+ -96,
+ -100,
+ -117,
+ -97,
+ -100,
+ -96,
+ -101,
+ -98,
+ -97,
+ -99,
+ -97,
+ -89,
+ -118,
+ -97,
+ -99,
+ -91,
+ -87,
+ -97,
+ -94,
+ -100,
+ -100,
+ -100,
+ -98,
+ -102,
+ -99,
+ -92,
+ -98,
+ -91,
+ -88,
+ -98,
+ -96,
+ -90,
+ -100,
+ -103,
+ -101,
+ -88,
+ -92,
+ -86,
+ -101,
+ -86,
+ -98,
+ -84,
+ -98,
+ -89,
+ -99,
+ -99,
+ -117,
+ -97,
+ -100,
+ -99,
+ -100,
+ -94,
+ -89,
+ -99,
+ -98,
+ -100,
+ -117,
+ -89,
+ -91,
+ -117,
+ -99,
+ -118,
+ -101,
+ -92,
+ -97,
+ -118,
+ -100,
+ -91,
+ -101,
+ -117,
+ -86,
+ -98,
+ -96,
+ -100,
+ -100,
+ -100,
+ -100,
+ -89,
+ -91,
+ -103,
+ -101,
+ -87,
+ -117,
+ -87,
+ -86,
+ -99,
+ -93,
+ -102,
+ -97,
+ -100,
+ -102,
+ -90,
+ -100,
+ -91,
+ -97,
+ -93,
+ -91,
+ -89,
+ -103,
+ -117,
+ -117,
+ -96,
+ -99,
+ -117,
+ -98,
+ -100,
+ -102,
+ -85,
+ -91,
+ -97,
+ -99,
+ -117,
+ -100,
+ -88,
+ -98,
+ -100,
+ -100,
+ -101,
+ -98,
+ -99,
+ -101,
+ -94,
+ -102,
+ -99,
+ -117,
+ -87,
+ -101,
+ -93,
+ -91,
+ -118,
+ -101,
+ -95,
+ -100,
+ -93,
+ -99,
+ -102,
+ -99,
+ -98,
+ -101,
+ -117,
+ -117,
+ -99,
+ -89,
+ -101,
+ -85,
+ -98,
+ -99,
+ -99,
+ -92,
+ -102,
+ -102,
+ -97,
+ -94,
+ -99,
+ -97,
+ -100,
+ -95,
+ -92,
+ -102,
+ -90,
+ -90,
+ -118,
+ -99,
+ -102,
+ -99,
+ -100,
+ -104,
+ -95,
+ -96,
+ -100,
+ -96,
+ -99,
+ -99,
+ -101,
+ -88,
+ -102,
+ -101,
+ -94,
+ -94,
+ -118,
+ -99,
+ -97,
+ -85,
+ -88,
+ -96,
+ -101,
+ -92,
+ -101,
+ -98,
+ -99,
+ -102,
+ -99,
+ -92,
+ -100,
+ -98,
+ -99,
+ -89,
+ -99,
+ -89,
+ -99,
+ -89,
+ -96,
+ -87,
+ -100,
+ -101,
+ -94,
+ -99,
+ -101,
+ -88,
+ -99,
+ -102,
+ -102,
+ -101,
+ -92,
+ -98,
+ -85,
+ -100,
+ -100,
+ -98,
+ -97,
+ -99,
+ -87,
+ -100,
+ -98,
+ -92,
+ -97,
+ -89,
+ -100,
+ -93,
+ -100,
+ -101,
+ -98,
+ -89,
+ -118,
+ -89,
+ -100,
+ -99,
+ -87,
+ -91,
+ -101,
+ -99,
+ -95,
+ -99,
+ -95,
+ -99,
+ -91,
+ -92,
+ -86,
+ -89,
+ -97,
+ -100,
+ -98,
+ -117,
+ -98,
+ -100,
+ -99,
+ -101,
+ -98,
+ -99,
+ -99,
+ -101,
+ -97,
+ -88,
+ -98,
+ -85,
+ -97,
+ -97,
+ -101,
+ -93,
+ -98,
+ -100,
+ -99,
+ -99,
+ -97,
+ -99,
+ -95,
+ -102,
+ -87,
+ -101,
+ -89,
+ -102,
+ -90,
+ -101,
+ -90,
+ -101,
+ -101,
+ -90,
+ -100,
+ -102,
+ -98,
+ -98,
+ -99,
+ -89,
+ -100,
+ -98,
+ -85,
+ -99,
+ -99,
+ -96,
+ -117,
+ -99,
+ -86,
+ -101,
+ -92,
+ -97,
+ -98,
+ -100,
+ -84,
+ -94,
+ -102,
+ -101,
+ -87,
+ -95,
+ -86,
+ -102,
+ -98,
+ -93,
+ -91,
+ -117,
+ -100,
+ -93,
+ -98,
+ -101,
+ -98,
+ -84,
+ -100,
+ -101,
+ -99,
+ -86,
+ -96,
+ -92,
+ -83,
+ -100,
+ -99,
+ -85,
+ -101,
+ -100,
+ -99,
+ -94,
+ -104,
+ -87,
+ -117,
+ -98,
+ -87,
+ -98,
+ -98,
+ -90,
+ -91,
+ -98,
+ -93,
+ -98,
+ -101,
+ -92,
+ -92,
+ -99,
+ -101,
+ -86,
+ -87,
+ -100,
+ -101,
+ -99,
+ -101,
+ -97,
+ -104,
+ -90,
+ -101,
+ -85,
+ -103,
+ -100,
+ -99,
+ -101,
+ -101,
+ -92,
+ -102,
+ -100,
+ -100,
+ -104,
+ -98,
+ -99,
+ -97,
+ -102,
+ -89,
+ -99,
+ -91,
+ -102,
+ -101,
+ -97,
+ -98,
+ -100,
+ -88,
+ -93,
+ -87,
+ -101,
+ -100,
+ -103,
+ -96,
+ -91,
+ -101,
+ -91,
+ -98,
+ -100,
+ -103,
+ -99,
+ -98,
+ -98,
+ -102,
+ -102,
+ -99,
+ -100,
+ -89,
+ -101,
+ -98,
+ -99,
+ -99,
+ -100,
+ -97,
+ -95,
+ -87,
+ -101,
+ -100,
+ -87,
+ -85,
+ -102,
+ -86,
+ -96,
+ -91,
+ -94,
+ -99,
+ -98,
+ -93,
+ -92,
+ -100,
+ -100,
+ -101,
+ -103,
+ -100,
+ -91,
+ -103,
+ -99,
+ -101,
+ -98,
+ -91,
+ -103,
+ -100,
+ -85,
+ -99,
+ -100,
+ -100,
+ -100,
+ -100,
+ -91,
+ -99,
+ -114,
+ -100,
+ -92,
+ -105,
+ -88,
+ -90,
+ -101,
+ -96,
+ -99,
+ -99,
+ -94,
+ -100,
+ -96,
+ -97,
+ -105,
+ -96,
+ -100,
+ -102,
+ -94,
+ -99,
+ -99,
+ -89,
+ -85,
+ -87,
+ -105,
+ -105,
+ -87,
+ -97,
+ -98,
+ -97,
+ -99,
+ -97,
+ -98,
+ -87,
+ -99,
+ -89,
+ -99,
+ -91,
+ -97,
+ -98,
+ -95,
+ -90,
+ -98,
+ -97,
+ -96,
+ -98,
+ -98,
+ -101,
+ -100,
+ -100,
+ -99,
+ -97,
+ -90,
+ -97,
+ -98,
+ -98,
+ -96,
+ -97,
+ -97,
+ -96,
+ -97
+ ],
+ "name": "Lon",
+ "lat": [
+ 48,
+ 18,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 48,
+ 49,
+ 19,
+ 48,
+ 18,
+ 49,
+ 48,
+ 48,
+ 49,
+ 49,
+ 48,
+ 48,
+ 48,
+ 49,
+ 18,
+ 49,
+ 49,
+ 48,
+ 48,
+ 49,
+ 48,
+ 18,
+ 49,
+ 19,
+ 48,
+ 49,
+ 48,
+ 49,
+ 49,
+ 48,
+ 48,
+ 48,
+ 49,
+ 48,
+ 49,
+ 49,
+ 48,
+ 43,
+ 49,
+ 18,
+ 48,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 48,
+ 48,
+ 49,
+ 49,
+ 33,
+ 48,
+ 49,
+ 48,
+ 48,
+ 49,
+ 48,
+ 48,
+ 49,
+ 49,
+ 49,
+ 47,
+ 48,
+ 49,
+ 49,
+ 49,
+ 48,
+ 36,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 48,
+ 49,
+ 49,
+ 49,
+ 48,
+ 49,
+ 48,
+ 48,
+ 49,
+ 18,
+ 49,
+ 18,
+ 49,
+ 49,
+ 49,
+ 48,
+ 48,
+ 48,
+ 48,
+ 49,
+ 48,
+ 49,
+ 26,
+ 49,
+ 49,
+ 48,
+ 49,
+ 49,
+ 47,
+ 49,
+ 49,
+ 49,
+ 48,
+ 49,
+ 49,
+ 48,
+ 49,
+ 49,
+ 49,
+ 49,
+ 37,
+ 49,
+ 47,
+ 48,
+ 49,
+ 49,
+ 49,
+ 49,
+ 49,
+ 42,
+ 47,
+ 49,
+ 49,
+ 18,
+ 49,
+ 49,
+ 47,
+ 49,
+ 26,
+ 48,
+ 49,
+ 49,
+ 48,
+ 36,
+ 48,
+ 47,
+ 48,
+ 49,
+ 49,
+ 31,
+ 49,
+ 48,
+ 47,
+ 43,
+ 48,
+ 42,
+ 47,
+ 48,
+ 47,
+ 49,
+ 49,
+ 49,
+ 35,
+ 48,
+ 43,
+ 49,
+ 18,
+ 46,
+ 18,
+ 48,
+ 48,
+ 47,
+ 47,
+ 48,
+ 48,
+ 49,
+ 49,
+ 49,
+ 47,
+ 49,
+ 48,
+ 49,
+ 49,
+ 32,
+ 42,
+ 49,
+ 49,
+ 25,
+ 49,
+ 33,
+ 48,
+ 47,
+ 26,
+ 43,
+ 49,
+ 30,
+ 47,
+ 47,
+ 48,
+ 30,
+ 48,
+ 48,
+ 47,
+ 49,
+ 33,
+ 26,
+ 47,
+ 33,
+ 49,
+ 49,
+ 48,
+ 34,
+ 47,
+ 49,
+ 47,
+ 49,
+ 49,
+ 38,
+ 49,
+ 49,
+ 47,
+ 27,
+ 49,
+ 49,
+ 49,
+ 38,
+ 48,
+ 37,
+ 26,
+ 49,
+ 48,
+ 26,
+ 47,
+ 47,
+ 47,
+ 38,
+ 49,
+ 48,
+ 37,
+ 48,
+ 47,
+ 48,
+ 38,
+ 31,
+ 48,
+ 48,
+ 48,
+ 49,
+ 47,
+ 47,
+ 49,
+ 26,
+ 49,
+ 42,
+ 49,
+ 47,
+ 32,
+ 44,
+ 48,
+ 48,
+ 43,
+ 49,
+ 49,
+ 32,
+ 38,
+ 43,
+ 47,
+ 48,
+ 49,
+ 48,
+ 48,
+ 49,
+ 38,
+ 35,
+ 25,
+ 49,
+ 49,
+ 18,
+ 49,
+ 49,
+ 26,
+ 43,
+ 49,
+ 48,
+ 49,
+ 48,
+ 48,
+ 49,
+ 47,
+ 26,
+ 47,
+ 47,
+ 49,
+ 49,
+ 47,
+ 49,
+ 49,
+ 48,
+ 49,
+ 43,
+ 49,
+ 37,
+ 47,
+ 49,
+ 42,
+ 47,
+ 49,
+ 48,
+ 48,
+ 19,
+ 46,
+ 49,
+ 18,
+ 38,
+ 47,
+ 39,
+ 47,
+ 31,
+ 47,
+ 47,
+ 48,
+ 38,
+ 41,
+ 25,
+ 47,
+ 49,
+ 42,
+ 48,
+ 48,
+ 47,
+ 47,
+ 48,
+ 48,
+ 49,
+ 38,
+ 38,
+ 43,
+ 48,
+ 48,
+ 37,
+ 44,
+ 43,
+ 19,
+ 48,
+ 48,
+ 33,
+ 45,
+ 25,
+ 43,
+ 47,
+ 49,
+ 49,
+ 38,
+ 47,
+ 44,
+ 47,
+ 43,
+ 26,
+ 31,
+ 47,
+ 47,
+ 49,
+ 49,
+ 40,
+ 48,
+ 44,
+ 42,
+ 47,
+ 18,
+ 34,
+ 47,
+ 48,
+ 47,
+ 40,
+ 47,
+ 37,
+ 45,
+ 37,
+ 48,
+ 35,
+ 40,
+ 40,
+ 48,
+ 32,
+ 38,
+ 44,
+ 48,
+ 48,
+ 49,
+ 37,
+ 44,
+ 48,
+ 41,
+ 47,
+ 49,
+ 49,
+ 38,
+ 43,
+ 49,
+ 47,
+ 48,
+ 44,
+ 34,
+ 49,
+ 48,
+ 47,
+ 48,
+ 26,
+ 48,
+ 48,
+ 49,
+ 42,
+ 47,
+ 41,
+ 43,
+ 37,
+ 48,
+ 26,
+ 49,
+ 46,
+ 48,
+ 34,
+ 42,
+ 38,
+ 48,
+ 44,
+ 48,
+ 33,
+ 48,
+ 37,
+ 30,
+ 49,
+ 47,
+ 37,
+ 48,
+ 48,
+ 48,
+ 47,
+ 49,
+ 48,
+ 33,
+ 48,
+ 26,
+ 49,
+ 30,
+ 19,
+ 43,
+ 48,
+ 42,
+ 48,
+ 42,
+ 26,
+ 43,
+ 27,
+ 37,
+ 26,
+ 37,
+ 48,
+ 48,
+ 43,
+ 35,
+ 42,
+ 48,
+ 44,
+ 45,
+ 38,
+ 30,
+ 48,
+ 48,
+ 25,
+ 27,
+ 44,
+ 49,
+ 45,
+ 47,
+ 49,
+ 42,
+ 18,
+ 48,
+ 33,
+ 48,
+ 43,
+ 43,
+ 44,
+ 26,
+ 30,
+ 26,
+ 38,
+ 43,
+ 48,
+ 48,
+ 34,
+ 49,
+ 47,
+ 49,
+ 37,
+ 47,
+ 49,
+ 35,
+ 49,
+ 27,
+ 26,
+ 41,
+ 43,
+ 30,
+ 25,
+ 26,
+ 41,
+ 41,
+ 43,
+ 48,
+ 44,
+ 48,
+ 26,
+ 48,
+ 35,
+ 48,
+ 35,
+ 47,
+ 47,
+ 37,
+ 34,
+ 49,
+ 47,
+ 48,
+ 37,
+ 47,
+ 42,
+ 33,
+ 25,
+ 35,
+ 44,
+ 41,
+ 26,
+ 47,
+ 38,
+ 44,
+ 48,
+ 44,
+ 49,
+ 48,
+ 31,
+ 25,
+ 48,
+ 43,
+ 35,
+ 48,
+ 43,
+ 48,
+ 48,
+ 43,
+ 49,
+ 44,
+ 30,
+ 42,
+ 49,
+ 48,
+ 48,
+ 36,
+ 48,
+ 27,
+ 40,
+ 48,
+ 27,
+ 44,
+ 25,
+ 26,
+ 35,
+ 37,
+ 48,
+ 43,
+ 41,
+ 37,
+ 49,
+ 49,
+ 47,
+ 44,
+ 43,
+ 47,
+ 49,
+ 47,
+ 46,
+ 44,
+ 46,
+ 30,
+ 47,
+ 38,
+ 48,
+ 47,
+ 38,
+ 49,
+ 48,
+ 47,
+ 42,
+ 48,
+ 44,
+ 48,
+ 49,
+ 37,
+ 43,
+ 30,
+ 35,
+ 45,
+ 49,
+ 48,
+ 46,
+ 26,
+ 48,
+ 49,
+ 43,
+ 46,
+ 49,
+ 48,
+ 44,
+ 34,
+ 44,
+ 47,
+ 48,
+ 43,
+ 33,
+ 49,
+ 43,
+ 43,
+ 38,
+ 48,
+ 44,
+ 30,
+ 37,
+ 49,
+ 42,
+ 43,
+ 47,
+ 42,
+ 46,
+ 47,
+ 42,
+ 48,
+ 48,
+ 42,
+ 44,
+ 42,
+ 42,
+ 43,
+ 38,
+ 35,
+ 38,
+ 25,
+ 25,
+ 48,
+ 38,
+ 46,
+ 30,
+ 35,
+ 37,
+ 30,
+ 47,
+ 47,
+ 47,
+ 46,
+ 48,
+ 42,
+ 43,
+ 48,
+ 48,
+ 27,
+ 34,
+ 42,
+ 38,
+ 38,
+ 49,
+ 44,
+ 34,
+ 48,
+ 47,
+ 33,
+ 42,
+ 25,
+ 42,
+ 43,
+ 42,
+ 48,
+ 25,
+ 43,
+ 29,
+ 37,
+ 45,
+ 35,
+ 42,
+ 47,
+ 48,
+ 32,
+ 48,
+ 35,
+ 46,
+ 38,
+ 29,
+ 38,
+ 33,
+ 42,
+ 47,
+ 48,
+ 35,
+ 48,
+ 49,
+ 27,
+ 29,
+ 49,
+ 49,
+ 42,
+ 44,
+ 35,
+ 25,
+ 37,
+ 27,
+ 38,
+ 48,
+ 42,
+ 35,
+ 48,
+ 48,
+ 43,
+ 40,
+ 48,
+ 38,
+ 49,
+ 27,
+ 26,
+ 48,
+ 44,
+ 43,
+ 30,
+ 45,
+ 48,
+ 47,
+ 46,
+ 42,
+ 27,
+ 33,
+ 40,
+ 35,
+ 47,
+ 48,
+ 49,
+ 48,
+ 25,
+ 33,
+ 40,
+ 48,
+ 47,
+ 30,
+ 26,
+ 44,
+ 47,
+ 47,
+ 47,
+ 43,
+ 48,
+ 47,
+ 35,
+ 37,
+ 26,
+ 26,
+ 49,
+ 26,
+ 41,
+ 48,
+ 45,
+ 42,
+ 42,
+ 47,
+ 44,
+ 35,
+ 49,
+ 48,
+ 27,
+ 43,
+ 49,
+ 38,
+ 30,
+ 26,
+ 48,
+ 29,
+ 47,
+ 32,
+ 27,
+ 42,
+ 37,
+ 40,
+ 45,
+ 47,
+ 43,
+ 48,
+ 47,
+ 37,
+ 45,
+ 48,
+ 48,
+ 43,
+ 35,
+ 48,
+ 44,
+ 26,
+ 42,
+ 26,
+ 37,
+ 30,
+ 26,
+ 27,
+ 27,
+ 33,
+ 43,
+ 49,
+ 40,
+ 48,
+ 42,
+ 30,
+ 37,
+ 49,
+ 42,
+ 46,
+ 25,
+ 48,
+ 38,
+ 38,
+ 43,
+ 48,
+ 44,
+ 48,
+ 35,
+ 30,
+ 37,
+ 37,
+ 41,
+ 44,
+ 42,
+ 43,
+ 30,
+ 48,
+ 42,
+ 46,
+ 43,
+ 37,
+ 42,
+ 30,
+ 49,
+ 26,
+ 47,
+ 33,
+ 47,
+ 49,
+ 49,
+ 48,
+ 33,
+ 41,
+ 47,
+ 25,
+ 41,
+ 33,
+ 38,
+ 40,
+ 38,
+ 49,
+ 44,
+ 48,
+ 39,
+ 48,
+ 42,
+ 45,
+ 36,
+ 31,
+ 41,
+ 49,
+ 38,
+ 35,
+ 42,
+ 35,
+ 38,
+ 34,
+ 44,
+ 38,
+ 31,
+ 43,
+ 49,
+ 44,
+ 35,
+ 49,
+ 35,
+ 38,
+ 35,
+ 40,
+ 42,
+ 31,
+ 47,
+ 48,
+ 47,
+ 45,
+ 42,
+ 35,
+ 38,
+ 42,
+ 35,
+ 47,
+ 30,
+ 48,
+ 28,
+ 43,
+ 35,
+ 48,
+ 48,
+ 25,
+ 38,
+ 44,
+ 47,
+ 46,
+ 35,
+ 42,
+ 42,
+ 44,
+ 26,
+ 35,
+ 47,
+ 37,
+ 49,
+ 34,
+ 29,
+ 42,
+ 48,
+ 47,
+ 44,
+ 48,
+ 31,
+ 48,
+ 44,
+ 48,
+ 48,
+ 43,
+ 38,
+ 48,
+ 48,
+ 42,
+ 42,
+ 44,
+ 43,
+ 42,
+ 45,
+ 49,
+ 44,
+ 49,
+ 48,
+ 48,
+ 41,
+ 26,
+ 44,
+ 40,
+ 49,
+ 41,
+ 41,
+ 42,
+ 35,
+ 43,
+ 27,
+ 39,
+ 48,
+ 30,
+ 37,
+ 30,
+ 35,
+ 45,
+ 26,
+ 46,
+ 30,
+ 45,
+ 25,
+ 43,
+ 47,
+ 43,
+ 47,
+ 48,
+ 22,
+ 48,
+ 30,
+ 45,
+ 42,
+ 44,
+ 44,
+ 40,
+ 30,
+ 39,
+ 46,
+ 47,
+ 25,
+ 48,
+ 48,
+ 42,
+ 35,
+ 42,
+ 46,
+ 48,
+ 28,
+ 41,
+ 48,
+ 35,
+ 40,
+ 43,
+ 27,
+ 43,
+ 43,
+ 33,
+ 44,
+ 48,
+ 44,
+ 25,
+ 45,
+ 42,
+ 32,
+ 18,
+ 38,
+ 46,
+ 35,
+ 42,
+ 34,
+ 48,
+ 40,
+ 47,
+ 30,
+ 43,
+ 38,
+ 44,
+ 44,
+ 34,
+ 38,
+ 38,
+ 33,
+ 44,
+ 38,
+ 36,
+ 44,
+ 36,
+ 30,
+ 48,
+ 46,
+ 49,
+ 31,
+ 31,
+ 44,
+ 49,
+ 45,
+ 43,
+ 41,
+ 33,
+ 33,
+ 42,
+ 45,
+ 42,
+ 41,
+ 46,
+ 44,
+ 38,
+ 42,
+ 47,
+ 41,
+ 44,
+ 44,
+ 48,
+ 25,
+ 43,
+ 44,
+ 41,
+ 42,
+ 35,
+ 35,
+ 47,
+ 30,
+ 43,
+ 37,
+ 35,
+ 30,
+ 46,
+ 35,
+ 47,
+ 42,
+ 41,
+ 44,
+ 28,
+ 49,
+ 37,
+ 35,
+ 41,
+ 26,
+ 49,
+ 41,
+ 35,
+ 35,
+ 26,
+ 39,
+ 41,
+ 44,
+ 47,
+ 25,
+ 41,
+ 40,
+ 46,
+ 48,
+ 30,
+ 30,
+ 43,
+ 41,
+ 47,
+ 45,
+ 40,
+ 35,
+ 35,
+ 47,
+ 42,
+ 48,
+ 45,
+ 29,
+ 42,
+ 47,
+ 47,
+ 26,
+ 38,
+ 46,
+ 28,
+ 44,
+ 36,
+ 35,
+ 42,
+ 36,
+ 38,
+ 26,
+ 35,
+ 18,
+ 42,
+ 38,
+ 42,
+ 27,
+ 48,
+ 39,
+ 32,
+ 44,
+ 29,
+ 35,
+ 47,
+ 27,
+ 46,
+ 45,
+ 48,
+ 43,
+ 40,
+ 28,
+ 44,
+ 37,
+ 38,
+ 49,
+ 48,
+ 37,
+ 28,
+ 43,
+ 43,
+ 28,
+ 40,
+ 27,
+ 32,
+ 25,
+ 36,
+ 41,
+ 49,
+ 38,
+ 46,
+ 45,
+ 25,
+ 43,
+ 45,
+ 37,
+ 42,
+ 41,
+ 29,
+ 44,
+ 31,
+ 49,
+ 47,
+ 47,
+ 48,
+ 44,
+ 35,
+ 47,
+ 34,
+ 48,
+ 48,
+ 45,
+ 34,
+ 47,
+ 49,
+ 37,
+ 41,
+ 47,
+ 43,
+ 48,
+ 43,
+ 43,
+ 32,
+ 46,
+ 43,
+ 43,
+ 35,
+ 38,
+ 35,
+ 44,
+ 40,
+ 37,
+ 34,
+ 41,
+ 40,
+ 36,
+ 48,
+ 48,
+ 42,
+ 48,
+ 30,
+ 30,
+ 42,
+ 47,
+ 49,
+ 31,
+ 44,
+ 45,
+ 48,
+ 43,
+ 30,
+ 44,
+ 32,
+ 41,
+ 44,
+ 30,
+ 47,
+ 39,
+ 46,
+ 48,
+ 40,
+ 42,
+ 47,
+ 45,
+ 43,
+ 49,
+ 49,
+ 35,
+ 42,
+ 34,
+ 35,
+ 39,
+ 49,
+ 46,
+ 42,
+ 38,
+ 42,
+ 45,
+ 47,
+ 44,
+ 47,
+ 44,
+ 48,
+ 31,
+ 47,
+ 33,
+ 43,
+ 43,
+ 45,
+ 46,
+ 27,
+ 48,
+ 30,
+ 27,
+ 49,
+ 27,
+ 47,
+ 33,
+ 43,
+ 47,
+ 48,
+ 42,
+ 41,
+ 39,
+ 30,
+ 38,
+ 42,
+ 37,
+ 35,
+ 45,
+ 43,
+ 34,
+ 41,
+ 38,
+ 41,
+ 44,
+ 46,
+ 29,
+ 44,
+ 37,
+ 48,
+ 41,
+ 47,
+ 30,
+ 38,
+ 35,
+ 49,
+ 35,
+ 34,
+ 28,
+ 41,
+ 44,
+ 45,
+ 41,
+ 40,
+ 26,
+ 41,
+ 48,
+ 47,
+ 29,
+ 42,
+ 30,
+ 49,
+ 47,
+ 49,
+ 42,
+ 43,
+ 48,
+ 30,
+ 41,
+ 47,
+ 25,
+ 38,
+ 48,
+ 49,
+ 39,
+ 48,
+ 42,
+ 42,
+ 48,
+ 30,
+ 35,
+ 43,
+ 37,
+ 35,
+ 30,
+ 42,
+ 41,
+ 37,
+ 44,
+ 48,
+ 42,
+ 35,
+ 38,
+ 43,
+ 30,
+ 34,
+ 28,
+ 33,
+ 26,
+ 41,
+ 27,
+ 43,
+ 41,
+ 40,
+ 49,
+ 49,
+ 46,
+ 47,
+ 45,
+ 26,
+ 44,
+ 30,
+ 30,
+ 46,
+ 35,
+ 48,
+ 49,
+ 44,
+ 47,
+ 44,
+ 48,
+ 35,
+ 48,
+ 42,
+ 45,
+ 47,
+ 46,
+ 46,
+ 42,
+ 48,
+ 43,
+ 47,
+ 30,
+ 47,
+ 44,
+ 42,
+ 42,
+ 47,
+ 27,
+ 46,
+ 37,
+ 48,
+ 44,
+ 38,
+ 43,
+ 48,
+ 38,
+ 49,
+ 38,
+ 33,
+ 26,
+ 25,
+ 41,
+ 35,
+ 46,
+ 30,
+ 42,
+ 44,
+ 48,
+ 48,
+ 37,
+ 48,
+ 28,
+ 49,
+ 35,
+ 49,
+ 44,
+ 30,
+ 33,
+ 47,
+ 44,
+ 38,
+ 38,
+ 28,
+ 48,
+ 41,
+ 31,
+ 38,
+ 26,
+ 30,
+ 48,
+ 34,
+ 43,
+ 40,
+ 45,
+ 29,
+ 46,
+ 44,
+ 37,
+ 32,
+ 42,
+ 30,
+ 41,
+ 48,
+ 33,
+ 47,
+ 43,
+ 47,
+ 49,
+ 46,
+ 49,
+ 36,
+ 43,
+ 35,
+ 33,
+ 43,
+ 42,
+ 42,
+ 38,
+ 45,
+ 30,
+ 26,
+ 42,
+ 48,
+ 49,
+ 44,
+ 44,
+ 35,
+ 49,
+ 40,
+ 44,
+ 44,
+ 49,
+ 37,
+ 49,
+ 36,
+ 35,
+ 44,
+ 49,
+ 45,
+ 33,
+ 28,
+ 42,
+ 42,
+ 42,
+ 44,
+ 43,
+ 29,
+ 41,
+ 38,
+ 36,
+ 44,
+ 31,
+ 44,
+ 30,
+ 44,
+ 49,
+ 46,
+ 38,
+ 29,
+ 30,
+ 45,
+ 48,
+ 33,
+ 40,
+ 42,
+ 44,
+ 43,
+ 45,
+ 41,
+ 30,
+ 26,
+ 44,
+ 47,
+ 44,
+ 43,
+ 30,
+ 42,
+ 43,
+ 46,
+ 34,
+ 44,
+ 48,
+ 49,
+ 41,
+ 45,
+ 44,
+ 47,
+ 46,
+ 30,
+ 38,
+ 45,
+ 29,
+ 26,
+ 39,
+ 35,
+ 27,
+ 33,
+ 49,
+ 29,
+ 43,
+ 47,
+ 43,
+ 49,
+ 45,
+ 35,
+ 30,
+ 31,
+ 41,
+ 47,
+ 35,
+ 47,
+ 37,
+ 47,
+ 47,
+ 29,
+ 31,
+ 40,
+ 25,
+ 44,
+ 48,
+ 46,
+ 35,
+ 43,
+ 41,
+ 30,
+ 32,
+ 26,
+ 29,
+ 39,
+ 37,
+ 38,
+ 45,
+ 37,
+ 49,
+ 46,
+ 49,
+ 43,
+ 44,
+ 27,
+ 44,
+ 46,
+ 33,
+ 41,
+ 45,
+ 48,
+ 41,
+ 42,
+ 42,
+ 46,
+ 46,
+ 38,
+ 41,
+ 35,
+ 46,
+ 48,
+ 44,
+ 43,
+ 34,
+ 44,
+ 49,
+ 44,
+ 35,
+ 38,
+ 47,
+ 49,
+ 41,
+ 37,
+ 48,
+ 41,
+ 37,
+ 42,
+ 48,
+ 28,
+ 44,
+ 27,
+ 43,
+ 45,
+ 38,
+ 31,
+ 47,
+ 41,
+ 42,
+ 42,
+ 44,
+ 46,
+ 30,
+ 35,
+ 48,
+ 42,
+ 48,
+ 37,
+ 35,
+ 44,
+ 41,
+ 46,
+ 47,
+ 41,
+ 46,
+ 47,
+ 48,
+ 46,
+ 47,
+ 29,
+ 47,
+ 44,
+ 41,
+ 42,
+ 45,
+ 27,
+ 41,
+ 47,
+ 43,
+ 38,
+ 42,
+ 47,
+ 46,
+ 47,
+ 43,
+ 35,
+ 27,
+ 42,
+ 40,
+ 37,
+ 29,
+ 41,
+ 46,
+ 37,
+ 41,
+ 27,
+ 40,
+ 47,
+ 46,
+ 47,
+ 38,
+ 46,
+ 45,
+ 41,
+ 40,
+ 47,
+ 44,
+ 47,
+ 47,
+ 35,
+ 44,
+ 35,
+ 43,
+ 42,
+ 26,
+ 48,
+ 29,
+ 42,
+ 42,
+ 31,
+ 41,
+ 41,
+ 44,
+ 38,
+ 33,
+ 30,
+ 46,
+ 47,
+ 43,
+ 45,
+ 27,
+ 49,
+ 35,
+ 44,
+ 42,
+ 48,
+ 39,
+ 44,
+ 26,
+ 32,
+ 31,
+ 32,
+ 35,
+ 43,
+ 38,
+ 48,
+ 46,
+ 26,
+ 43,
+ 44,
+ 28,
+ 39,
+ 47,
+ 46,
+ 26,
+ 43,
+ 29,
+ 42,
+ 47,
+ 41,
+ 46,
+ 40,
+ 44,
+ 46,
+ 29,
+ 47,
+ 33,
+ 47,
+ 37,
+ 42,
+ 35,
+ 40,
+ 35,
+ 42,
+ 46,
+ 42,
+ 48,
+ 41,
+ 43,
+ 29,
+ 41,
+ 49,
+ 35,
+ 38,
+ 27,
+ 47,
+ 42,
+ 43,
+ 46,
+ 41,
+ 41,
+ 43,
+ 42,
+ 31,
+ 46,
+ 40,
+ 47,
+ 34,
+ 43,
+ 29,
+ 38,
+ 41,
+ 46,
+ 43,
+ 44,
+ 43,
+ 40,
+ 43,
+ 49,
+ 36,
+ 43,
+ 25,
+ 47,
+ 38,
+ 42,
+ 36,
+ 43,
+ 48,
+ 30,
+ 35,
+ 25,
+ 44,
+ 33,
+ 30,
+ 48,
+ 49,
+ 37,
+ 48,
+ 42,
+ 26,
+ 30,
+ 48,
+ 48,
+ 49,
+ 44,
+ 43,
+ 48,
+ 46,
+ 41,
+ 40,
+ 30,
+ 40,
+ 49,
+ 30,
+ 43,
+ 26,
+ 36,
+ 46,
+ 44,
+ 42,
+ 46,
+ 42,
+ 38,
+ 45,
+ 29,
+ 47,
+ 46,
+ 30,
+ 26,
+ 39,
+ 46,
+ 47,
+ 39,
+ 42,
+ 26,
+ 41,
+ 26,
+ 41,
+ 41,
+ 37,
+ 37,
+ 44,
+ 44,
+ 44,
+ 31,
+ 41,
+ 47,
+ 44,
+ 47,
+ 27,
+ 27,
+ 45,
+ 27,
+ 34,
+ 41,
+ 37,
+ 47,
+ 46,
+ 46,
+ 40,
+ 46,
+ 27,
+ 30,
+ 47,
+ 42,
+ 25,
+ 30,
+ 30,
+ 49,
+ 35,
+ 35,
+ 40,
+ 39,
+ 31,
+ 48,
+ 30,
+ 41,
+ 37,
+ 45,
+ 47,
+ 46,
+ 30,
+ 36,
+ 37,
+ 43,
+ 47,
+ 49,
+ 45,
+ 44,
+ 42,
+ 41,
+ 45,
+ 39,
+ 43,
+ 29,
+ 39,
+ 49,
+ 46,
+ 43,
+ 41,
+ 44,
+ 47,
+ 30,
+ 49,
+ 37,
+ 49,
+ 35,
+ 38,
+ 43,
+ 38,
+ 43,
+ 49,
+ 48,
+ 36,
+ 37,
+ 42,
+ 35,
+ 41,
+ 41,
+ 41,
+ 30,
+ 47,
+ 28,
+ 49,
+ 26,
+ 38,
+ 30,
+ 44,
+ 35,
+ 34,
+ 27,
+ 42,
+ 41,
+ 41,
+ 36,
+ 29,
+ 32,
+ 30,
+ 49,
+ 45,
+ 37,
+ 38,
+ 47,
+ 36,
+ 41,
+ 38,
+ 43,
+ 47,
+ 46,
+ 39,
+ 43,
+ 35,
+ 48,
+ 44,
+ 34,
+ 37,
+ 43,
+ 35,
+ 43,
+ 30,
+ 45,
+ 41,
+ 47,
+ 41,
+ 43,
+ 42,
+ 45,
+ 40,
+ 32,
+ 42,
+ 31,
+ 29,
+ 42,
+ 46,
+ 27,
+ 27,
+ 44,
+ 43,
+ 25,
+ 47,
+ 46,
+ 43,
+ 29,
+ 43,
+ 48,
+ 37,
+ 30,
+ 29,
+ 40,
+ 42,
+ 37,
+ 41,
+ 27,
+ 47,
+ 30,
+ 42,
+ 27,
+ 44,
+ 42,
+ 30,
+ 30,
+ 30,
+ 32,
+ 30,
+ 40,
+ 45,
+ 41,
+ 37,
+ 30,
+ 42,
+ 44,
+ 49,
+ 49,
+ 49,
+ 45,
+ 43,
+ 36,
+ 43,
+ 44,
+ 41,
+ 43,
+ 49,
+ 38,
+ 38,
+ 30,
+ 49,
+ 42,
+ 30,
+ 43,
+ 33,
+ 46,
+ 41,
+ 35,
+ 44,
+ 41,
+ 26,
+ 42,
+ 45,
+ 44,
+ 46,
+ 48,
+ 35,
+ 33,
+ 44,
+ 40,
+ 45,
+ 49,
+ 43,
+ 42,
+ 45,
+ 25,
+ 27,
+ 47,
+ 34,
+ 46,
+ 33,
+ 47,
+ 46,
+ 35,
+ 46,
+ 41,
+ 39,
+ 42,
+ 48,
+ 40,
+ 46,
+ 40,
+ 46,
+ 47,
+ 40,
+ 26,
+ 49,
+ 27,
+ 26,
+ 42,
+ 43,
+ 28,
+ 44,
+ 32,
+ 41,
+ 46,
+ 43,
+ 41,
+ 32,
+ 43,
+ 38,
+ 49,
+ 44,
+ 34,
+ 30,
+ 41,
+ 31,
+ 35,
+ 41,
+ 46,
+ 44,
+ 39,
+ 30,
+ 39,
+ 45,
+ 47,
+ 45,
+ 48,
+ 46,
+ 26,
+ 42,
+ 45,
+ 46,
+ 32,
+ 26,
+ 45,
+ 42,
+ 37,
+ 40,
+ 39,
+ 45,
+ 44,
+ 46,
+ 40,
+ 38,
+ 37,
+ 44,
+ 27,
+ 44,
+ 27,
+ 34,
+ 46,
+ 32,
+ 49,
+ 41,
+ 46,
+ 40,
+ 46,
+ 36,
+ 30,
+ 41,
+ 34,
+ 49,
+ 47,
+ 33,
+ 46,
+ 44,
+ 35,
+ 45,
+ 43,
+ 43,
+ 44,
+ 48,
+ 40,
+ 49,
+ 26,
+ 49,
+ 42,
+ 47,
+ 39,
+ 42,
+ 44,
+ 39,
+ 38,
+ 40,
+ 38,
+ 33,
+ 34,
+ 46,
+ 42,
+ 41,
+ 34,
+ 49,
+ 29,
+ 34,
+ 26,
+ 40,
+ 35,
+ 41,
+ 45,
+ 32,
+ 34,
+ 47,
+ 43,
+ 25,
+ 39,
+ 47,
+ 37,
+ 43,
+ 43,
+ 35,
+ 32,
+ 41,
+ 41,
+ 30,
+ 45,
+ 44,
+ 38,
+ 43,
+ 41,
+ 43,
+ 38,
+ 49,
+ 28,
+ 48,
+ 47,
+ 46,
+ 42,
+ 42,
+ 48,
+ 43,
+ 43,
+ 30,
+ 42,
+ 41,
+ 46,
+ 42,
+ 45,
+ 45,
+ 35,
+ 46,
+ 47,
+ 45,
+ 47,
+ 44,
+ 41,
+ 46,
+ 32,
+ 43,
+ 46,
+ 43,
+ 49,
+ 43,
+ 42,
+ 44,
+ 47,
+ 29,
+ 26,
+ 43,
+ 41,
+ 45,
+ 41,
+ 34,
+ 46,
+ 35,
+ 47,
+ 37,
+ 27,
+ 34,
+ 34,
+ 27,
+ 45,
+ 26,
+ 38,
+ 35,
+ 35,
+ 49,
+ 42,
+ 42,
+ 49,
+ 50,
+ 43,
+ 48,
+ 44,
+ 43,
+ 40,
+ 47,
+ 38,
+ 44,
+ 45,
+ 44,
+ 44,
+ 41,
+ 33,
+ 44,
+ 45,
+ 46,
+ 40,
+ 47,
+ 42,
+ 41,
+ 43,
+ 40,
+ 46,
+ 39,
+ 33,
+ 49,
+ 41,
+ 25,
+ 40,
+ 46,
+ 33,
+ 41,
+ 39,
+ 38,
+ 48,
+ 31,
+ 39,
+ 47,
+ 42,
+ 45,
+ 44,
+ 43,
+ 45,
+ 46,
+ 38,
+ 47,
+ 47,
+ 46,
+ 44,
+ 30,
+ 29,
+ 42,
+ 49,
+ 35,
+ 41,
+ 45,
+ 34,
+ 26,
+ 41,
+ 41,
+ 30,
+ 32,
+ 29,
+ 41,
+ 39,
+ 44,
+ 45,
+ 42,
+ 41,
+ 34,
+ 32,
+ 35,
+ 31,
+ 32,
+ 35,
+ 33,
+ 31,
+ 47,
+ 46,
+ 44,
+ 46,
+ 47,
+ 27,
+ 42,
+ 41,
+ 41,
+ 39,
+ 40,
+ 39,
+ 31,
+ 39,
+ 43,
+ 27,
+ 35,
+ 27,
+ 25,
+ 46,
+ 44,
+ 43,
+ 31,
+ 41,
+ 41,
+ 41,
+ 44,
+ 46,
+ 40,
+ 40,
+ 46,
+ 42,
+ 33,
+ 48,
+ 36,
+ 36,
+ 46,
+ 30,
+ 42,
+ 39,
+ 35,
+ 39,
+ 39,
+ 46,
+ 36,
+ 42,
+ 30,
+ 46,
+ 41,
+ 36,
+ 35,
+ 37,
+ 31,
+ 32,
+ 41,
+ 41,
+ 46,
+ 30,
+ 34,
+ 39,
+ 39,
+ 41,
+ 35,
+ 44,
+ 42,
+ 49,
+ 30,
+ 49,
+ 36,
+ 28,
+ 45,
+ 35,
+ 38,
+ 31,
+ 38,
+ 42,
+ 34,
+ 44,
+ 44,
+ 44,
+ 46,
+ 44,
+ 43,
+ 44,
+ 42,
+ 43,
+ 34,
+ 39,
+ 42,
+ 45,
+ 32,
+ 42,
+ 41,
+ 37,
+ 43,
+ 45,
+ 43,
+ 43,
+ 35,
+ 45,
+ 46,
+ 34,
+ 34,
+ 38,
+ 47,
+ 41,
+ 40,
+ 46,
+ 47,
+ 33,
+ 43,
+ 44,
+ 35,
+ 41,
+ 48,
+ 46,
+ 39,
+ 37,
+ 38,
+ 46,
+ 38,
+ 42,
+ 29,
+ 44,
+ 41,
+ 38,
+ 35,
+ 46,
+ 38,
+ 28,
+ 36,
+ 41,
+ 36,
+ 27,
+ 34,
+ 29,
+ 36,
+ 33,
+ 32,
+ 43,
+ 45,
+ 49,
+ 40,
+ 46,
+ 35,
+ 46,
+ 44,
+ 39,
+ 38,
+ 46,
+ 29,
+ 47,
+ 44,
+ 47,
+ 39,
+ 41,
+ 41,
+ 33,
+ 43,
+ 35,
+ 27,
+ 38,
+ 38,
+ 43,
+ 43,
+ 47,
+ 45,
+ 35,
+ 27,
+ 43,
+ 33,
+ 49,
+ 44,
+ 48,
+ 46,
+ 45,
+ 42,
+ 30,
+ 31,
+ 30,
+ 45,
+ 43,
+ 35,
+ 29,
+ 45,
+ 44,
+ 46,
+ 46,
+ 40,
+ 42,
+ 44,
+ 43,
+ 41,
+ 45,
+ 39,
+ 44,
+ 27,
+ 33,
+ 38,
+ 29,
+ 34,
+ 36,
+ 28,
+ 30,
+ 27,
+ 43,
+ 49,
+ 41,
+ 30,
+ 28,
+ 48,
+ 44,
+ 42,
+ 39,
+ 44,
+ 42,
+ 31,
+ 46,
+ 33,
+ 31,
+ 27,
+ 45,
+ 41,
+ 47,
+ 38,
+ 45,
+ 35,
+ 36,
+ 49,
+ 42,
+ 40,
+ 32,
+ 35,
+ 36,
+ 43,
+ 30,
+ 41,
+ 42,
+ 37,
+ 44,
+ 33,
+ 49,
+ 47,
+ 32,
+ 29,
+ 46,
+ 46,
+ 27,
+ 30,
+ 34,
+ 46,
+ 36,
+ 42,
+ 41,
+ 46,
+ 44,
+ 38,
+ 28,
+ 41,
+ 33,
+ 46,
+ 30,
+ 44,
+ 46,
+ 38,
+ 47,
+ 40,
+ 45,
+ 34,
+ 39,
+ 29,
+ 46,
+ 32,
+ 35,
+ 35,
+ 32,
+ 29,
+ 42,
+ 32,
+ 33,
+ 47,
+ 39,
+ 46,
+ 37,
+ 30,
+ 43,
+ 29,
+ 38,
+ 45,
+ 41,
+ 42,
+ 38,
+ 35,
+ 46,
+ 42,
+ 46,
+ 44,
+ 46,
+ 47,
+ 35,
+ 48,
+ 31,
+ 30,
+ 42,
+ 40,
+ 39,
+ 44,
+ 43,
+ 40,
+ 28,
+ 29,
+ 40,
+ 43,
+ 30,
+ 40,
+ 43,
+ 41,
+ 29,
+ 42,
+ 42,
+ 42,
+ 44,
+ 45,
+ 36,
+ 47,
+ 37,
+ 30,
+ 40,
+ 26,
+ 30,
+ 43,
+ 39,
+ 44,
+ 46,
+ 36,
+ 47,
+ 42,
+ 42,
+ 41,
+ 31,
+ 41,
+ 43,
+ 44,
+ 38,
+ 40,
+ 47,
+ 44,
+ 47,
+ 31,
+ 44,
+ 49,
+ 30,
+ 38,
+ 45,
+ 30,
+ 39,
+ 43,
+ 38,
+ 48,
+ 46,
+ 29,
+ 36,
+ 43,
+ 40,
+ 30,
+ 34,
+ 47,
+ 34,
+ 43,
+ 43,
+ 29,
+ 40,
+ 41,
+ 43,
+ 46,
+ 37,
+ 43,
+ 44,
+ 31,
+ 49,
+ 45,
+ 34,
+ 44,
+ 45,
+ 36,
+ 41,
+ 38,
+ 43,
+ 46,
+ 29,
+ 41,
+ 29,
+ 40,
+ 43,
+ 49,
+ 42,
+ 26,
+ 49,
+ 39,
+ 44,
+ 43,
+ 34,
+ 38,
+ 44,
+ 35,
+ 35,
+ 45,
+ 36,
+ 26,
+ 33,
+ 46,
+ 45,
+ 42,
+ 34,
+ 42,
+ 41,
+ 43,
+ 46,
+ 39,
+ 41,
+ 29,
+ 49,
+ 40,
+ 50,
+ 49,
+ 45,
+ 38,
+ 40,
+ 42,
+ 47,
+ 45,
+ 47,
+ 47,
+ 41,
+ 44,
+ 44,
+ 31,
+ 38,
+ 41,
+ 43,
+ 44,
+ 32,
+ 46,
+ 39,
+ 28,
+ 49,
+ 49,
+ 41,
+ 41,
+ 46,
+ 44,
+ 37,
+ 35,
+ 35,
+ 40,
+ 30,
+ 36,
+ 46,
+ 37,
+ 41,
+ 35,
+ 40,
+ 38,
+ 35,
+ 41,
+ 28,
+ 35,
+ 41,
+ 40,
+ 30,
+ 28,
+ 37,
+ 38,
+ 42,
+ 35,
+ 41,
+ 29,
+ 40,
+ 40,
+ 27,
+ 44,
+ 39,
+ 46,
+ 44,
+ 47,
+ 45,
+ 29,
+ 40,
+ 43,
+ 45,
+ 27,
+ 38,
+ 31,
+ 34,
+ 32,
+ 34,
+ 46,
+ 28,
+ 45,
+ 32,
+ 37,
+ 35,
+ 49,
+ 47,
+ 35,
+ 40,
+ 46,
+ 46,
+ 35,
+ 34,
+ 46,
+ 47,
+ 29,
+ 44,
+ 41,
+ 48,
+ 47,
+ 46,
+ 46,
+ 46,
+ 40,
+ 47,
+ 41,
+ 35,
+ 29,
+ 42,
+ 47,
+ 35,
+ 35,
+ 30,
+ 49,
+ 45,
+ 39,
+ 48,
+ 43,
+ 42,
+ 44,
+ 38,
+ 40,
+ 42,
+ 29,
+ 28,
+ 43,
+ 26,
+ 29,
+ 41,
+ 44,
+ 46,
+ 34,
+ 42,
+ 49,
+ 33,
+ 30,
+ 29,
+ 41,
+ 41,
+ 39,
+ 26,
+ 46,
+ 36,
+ 33,
+ 44,
+ 45,
+ 40,
+ 42,
+ 25,
+ 40,
+ 38,
+ 42,
+ 42,
+ 38,
+ 40,
+ 26,
+ 39,
+ 40,
+ 41,
+ 40,
+ 42,
+ 29,
+ 46,
+ 31,
+ 34,
+ 38,
+ 28,
+ 46,
+ 40,
+ 31,
+ 39,
+ 46,
+ 25,
+ 43,
+ 43,
+ 42,
+ 32,
+ 41,
+ 47,
+ 42,
+ 36,
+ 26,
+ 40,
+ 30,
+ 45,
+ 46,
+ 49,
+ 44,
+ 39,
+ 44,
+ 33,
+ 39,
+ 47,
+ 46,
+ 28,
+ 46,
+ 47,
+ 45,
+ 47,
+ 41,
+ 29,
+ 37,
+ 32,
+ 42,
+ 42,
+ 40,
+ 45,
+ 43,
+ 37,
+ 29,
+ 43,
+ 50,
+ 50,
+ 39,
+ 34,
+ 31,
+ 43,
+ 46,
+ 42,
+ 46,
+ 31,
+ 46,
+ 46,
+ 40,
+ 41,
+ 39,
+ 35,
+ 38,
+ 44,
+ 35,
+ 42,
+ 45,
+ 45,
+ 34,
+ 29,
+ 42,
+ 43,
+ 47,
+ 42,
+ 28,
+ 47,
+ 27,
+ 41,
+ 41,
+ 43,
+ 47,
+ 47,
+ 45,
+ 29,
+ 47,
+ 30,
+ 46,
+ 40,
+ 47,
+ 49,
+ 41,
+ 47,
+ 26,
+ 31,
+ 47,
+ 49,
+ 42,
+ 47,
+ 29,
+ 39,
+ 38,
+ 34,
+ 30,
+ 44,
+ 45,
+ 39,
+ 27,
+ 41,
+ 35,
+ 46,
+ 41,
+ 40,
+ 42,
+ 44,
+ 29,
+ 40,
+ 47,
+ 43,
+ 49,
+ 35,
+ 49,
+ 47,
+ 41,
+ 36,
+ 41,
+ 41,
+ 33,
+ 34,
+ 42,
+ 39,
+ 47,
+ 44,
+ 44,
+ 31,
+ 40,
+ 28,
+ 45,
+ 42,
+ 42,
+ 44,
+ 40,
+ 36,
+ 45,
+ 47,
+ 46,
+ 45,
+ 35,
+ 42,
+ 45,
+ 29,
+ 44,
+ 34,
+ 38,
+ 46,
+ 28,
+ 42,
+ 44,
+ 43,
+ 43,
+ 30,
+ 44,
+ 44,
+ 43,
+ 46,
+ 45,
+ 42,
+ 31,
+ 27,
+ 40,
+ 46,
+ 41,
+ 34,
+ 31,
+ 35,
+ 44,
+ 50,
+ 44,
+ 34,
+ 31,
+ 33,
+ 34,
+ 42,
+ 36,
+ 41,
+ 43,
+ 41,
+ 42,
+ 29,
+ 35,
+ 31,
+ 46,
+ 41,
+ 43,
+ 38,
+ 44,
+ 26,
+ 46,
+ 35,
+ 42,
+ 27,
+ 43,
+ 45,
+ 41,
+ 43,
+ 43,
+ 34,
+ 36,
+ 29,
+ 46,
+ 40,
+ 43,
+ 47,
+ 27,
+ 31,
+ 29,
+ 47,
+ 31,
+ 41,
+ 27,
+ 38,
+ 36,
+ 38,
+ 45,
+ 41,
+ 34,
+ 32,
+ 45,
+ 41,
+ 40,
+ 40,
+ 40,
+ 47,
+ 34,
+ 46,
+ 38,
+ 41,
+ 50,
+ 32,
+ 46,
+ 46,
+ 26,
+ 41,
+ 46,
+ 36,
+ 41,
+ 46,
+ 30,
+ 35,
+ 35,
+ 40,
+ 44,
+ 30,
+ 41,
+ 46,
+ 46,
+ 42,
+ 43,
+ 36,
+ 37,
+ 35,
+ 39,
+ 44,
+ 41,
+ 44,
+ 40,
+ 34,
+ 44,
+ 29,
+ 30,
+ 41,
+ 46,
+ 48,
+ 38,
+ 46,
+ 38,
+ 40,
+ 34,
+ 28,
+ 38,
+ 39,
+ 27,
+ 42,
+ 28,
+ 43,
+ 35,
+ 49,
+ 30,
+ 40,
+ 47,
+ 40,
+ 43,
+ 43,
+ 41,
+ 40,
+ 39,
+ 41,
+ 49,
+ 44,
+ 32,
+ 34,
+ 44,
+ 44,
+ 45,
+ 42,
+ 37,
+ 45,
+ 29,
+ 39,
+ 41,
+ 30,
+ 35,
+ 46,
+ 44,
+ 37,
+ 45,
+ 38,
+ 44,
+ 35,
+ 47,
+ 46,
+ 49,
+ 44,
+ 45,
+ 43,
+ 41,
+ 30,
+ 30,
+ 29,
+ 49,
+ 45,
+ 39,
+ 45,
+ 47,
+ 45,
+ 30,
+ 32,
+ 39,
+ 44,
+ 34,
+ 38,
+ 34,
+ 32,
+ 44,
+ 42,
+ 45,
+ 44,
+ 41,
+ 43,
+ 39,
+ 35,
+ 46,
+ 40,
+ 28,
+ 42,
+ 31,
+ 43,
+ 25,
+ 44,
+ 39,
+ 42,
+ 39,
+ 43,
+ 29,
+ 49,
+ 41,
+ 28,
+ 44,
+ 40,
+ 41,
+ 40,
+ 45,
+ 46,
+ 35,
+ 40,
+ 40,
+ 43,
+ 47,
+ 43,
+ 46,
+ 44,
+ 46,
+ 40,
+ 41,
+ 41,
+ 50,
+ 46,
+ 45,
+ 47,
+ 41,
+ 45,
+ 47,
+ 35,
+ 41,
+ 29,
+ 35,
+ 39,
+ 39,
+ 47,
+ 39,
+ 30,
+ 40,
+ 42,
+ 49,
+ 49,
+ 45,
+ 41,
+ 38,
+ 39,
+ 29,
+ 40,
+ 43,
+ 39,
+ 36,
+ 35,
+ 29,
+ 44,
+ 42,
+ 39,
+ 40,
+ 43,
+ 49,
+ 31,
+ 29,
+ 44,
+ 27,
+ 47,
+ 49,
+ 44,
+ 37,
+ 45,
+ 40,
+ 46,
+ 40,
+ 31,
+ 41,
+ 40,
+ 47,
+ 47,
+ 30,
+ 40,
+ 41,
+ 46,
+ 36,
+ 42,
+ 37,
+ 46,
+ 42,
+ 38,
+ 43,
+ 35,
+ 40,
+ 35,
+ 44,
+ 40,
+ 40,
+ 30,
+ 27,
+ 46,
+ 46,
+ 35,
+ 45,
+ 47,
+ 40,
+ 41,
+ 32,
+ 29,
+ 28,
+ 37,
+ 46,
+ 41,
+ 42,
+ 38,
+ 29,
+ 45,
+ 29,
+ 27,
+ 38,
+ 44,
+ 34,
+ 43,
+ 41,
+ 46,
+ 32,
+ 38,
+ 46,
+ 29,
+ 40,
+ 46,
+ 40,
+ 40,
+ 44,
+ 38,
+ 29,
+ 45,
+ 31,
+ 41,
+ 40,
+ 49,
+ 41,
+ 27,
+ 40,
+ 45,
+ 44,
+ 40,
+ 43,
+ 40,
+ 39,
+ 41,
+ 42,
+ 46,
+ 38,
+ 31,
+ 41,
+ 41,
+ 46,
+ 35,
+ 46,
+ 28,
+ 31,
+ 42,
+ 42,
+ 45,
+ 44,
+ 30,
+ 42,
+ 35,
+ 41,
+ 31,
+ 46,
+ 42,
+ 47,
+ 44,
+ 35,
+ 41,
+ 46,
+ 43,
+ 46,
+ 47,
+ 43,
+ 46,
+ 41,
+ 39,
+ 50,
+ 47,
+ 47,
+ 43,
+ 44,
+ 46,
+ 40,
+ 35,
+ 28,
+ 30,
+ 42,
+ 44,
+ 34,
+ 43,
+ 46,
+ 45,
+ 45,
+ 44,
+ 46,
+ 46,
+ 43,
+ 35,
+ 35,
+ 42,
+ 31,
+ 31,
+ 27,
+ 41,
+ 35,
+ 36,
+ 29,
+ 41,
+ 39,
+ 40,
+ 44,
+ 40,
+ 45,
+ 46,
+ 34,
+ 41,
+ 46,
+ 40,
+ 46,
+ 41,
+ 40,
+ 46,
+ 46,
+ 35,
+ 31,
+ 33,
+ 41,
+ 36,
+ 42,
+ 35,
+ 49,
+ 46,
+ 46,
+ 49,
+ 40,
+ 26,
+ 31,
+ 40,
+ 42,
+ 41,
+ 40,
+ 38,
+ 27,
+ 28,
+ 45,
+ 40,
+ 28,
+ 39,
+ 39,
+ 31,
+ 33,
+ 41,
+ 46,
+ 29,
+ 40,
+ 40,
+ 31,
+ 34,
+ 34,
+ 36,
+ 46,
+ 31,
+ 30,
+ 42,
+ 37,
+ 38,
+ 38,
+ 45,
+ 42,
+ 33,
+ 40,
+ 42,
+ 39,
+ 33,
+ 43,
+ 45,
+ 43,
+ 34,
+ 38,
+ 40,
+ 37,
+ 33,
+ 37,
+ 46,
+ 38,
+ 34,
+ 36,
+ 45,
+ 42,
+ 46,
+ 31,
+ 29,
+ 39,
+ 39,
+ 42,
+ 29,
+ 30,
+ 38,
+ 30,
+ 43,
+ 42,
+ 27,
+ 34,
+ 35,
+ 42,
+ 42,
+ 30,
+ 46,
+ 40,
+ 41,
+ 45,
+ 30,
+ 41,
+ 44,
+ 35,
+ 45,
+ 42,
+ 29,
+ 33,
+ 36,
+ 42,
+ 35,
+ 43,
+ 40,
+ 29,
+ 44,
+ 40,
+ 44,
+ 47,
+ 41,
+ 42,
+ 47,
+ 49,
+ 35,
+ 26,
+ 49,
+ 35,
+ 33,
+ 46,
+ 41,
+ 41,
+ 40,
+ 43,
+ 35,
+ 40,
+ 39,
+ 39,
+ 34,
+ 42,
+ 27,
+ 28,
+ 44,
+ 29,
+ 45,
+ 44,
+ 29,
+ 48,
+ 38,
+ 27,
+ 36,
+ 31,
+ 44,
+ 38,
+ 29,
+ 43,
+ 29,
+ 40,
+ 40,
+ 25,
+ 39,
+ 44,
+ 40,
+ 36,
+ 38,
+ 39,
+ 34,
+ 46,
+ 50,
+ 38,
+ 38,
+ 49,
+ 35,
+ 27,
+ 33,
+ 27,
+ 50,
+ 35,
+ 29,
+ 45,
+ 45,
+ 45,
+ 40,
+ 44,
+ 46,
+ 42,
+ 41,
+ 26,
+ 41,
+ 28,
+ 35,
+ 30,
+ 41,
+ 50,
+ 39,
+ 34,
+ 41,
+ 35,
+ 37,
+ 40,
+ 44,
+ 47,
+ 44,
+ 35,
+ 45,
+ 33,
+ 50,
+ 41,
+ 40,
+ 43,
+ 42,
+ 33,
+ 46,
+ 45,
+ 38,
+ 31,
+ 40,
+ 27,
+ 43,
+ 47,
+ 45,
+ 46,
+ 34,
+ 46,
+ 41,
+ 41,
+ 47,
+ 27,
+ 45,
+ 44,
+ 34,
+ 49,
+ 41,
+ 40,
+ 41,
+ 31,
+ 31,
+ 29,
+ 41,
+ 27,
+ 41,
+ 39,
+ 45,
+ 35,
+ 29,
+ 46,
+ 40,
+ 41,
+ 41,
+ 44,
+ 40,
+ 28,
+ 32,
+ 38,
+ 39,
+ 44,
+ 40,
+ 45,
+ 31,
+ 46,
+ 39,
+ 27,
+ 38,
+ 35,
+ 35,
+ 46,
+ 41,
+ 37,
+ 30,
+ 31,
+ 49,
+ 46,
+ 42,
+ 47,
+ 49,
+ 46,
+ 35,
+ 40,
+ 44,
+ 46,
+ 45,
+ 40,
+ 27,
+ 41,
+ 45,
+ 46,
+ 28,
+ 27,
+ 41,
+ 43,
+ 46,
+ 47,
+ 41,
+ 39,
+ 38,
+ 27,
+ 40,
+ 44,
+ 46,
+ 40,
+ 41,
+ 41,
+ 40,
+ 41,
+ 40,
+ 33,
+ 41,
+ 41,
+ 38,
+ 31,
+ 46,
+ 44,
+ 32,
+ 43,
+ 28,
+ 49,
+ 46,
+ 42,
+ 46,
+ 46,
+ 39,
+ 45,
+ 27,
+ 41,
+ 37,
+ 41,
+ 46,
+ 32,
+ 40,
+ 46,
+ 47,
+ 45,
+ 34,
+ 45,
+ 40,
+ 41,
+ 46,
+ 38,
+ 34,
+ 31,
+ 46,
+ 25,
+ 44,
+ 30,
+ 31,
+ 35,
+ 39,
+ 45,
+ 45,
+ 44,
+ 35,
+ 27,
+ 28,
+ 46,
+ 35,
+ 33,
+ 31,
+ 45,
+ 45,
+ 49,
+ 40,
+ 41,
+ 42,
+ 42,
+ 39,
+ 42,
+ 46,
+ 42,
+ 43,
+ 44,
+ 43,
+ 42,
+ 37,
+ 45,
+ 34,
+ 35,
+ 29,
+ 43,
+ 27,
+ 41,
+ 37,
+ 45,
+ 44,
+ 46,
+ 41,
+ 41,
+ 42,
+ 38,
+ 47,
+ 47,
+ 47,
+ 45,
+ 46,
+ 35,
+ 41,
+ 43,
+ 45,
+ 40,
+ 44,
+ 46,
+ 28,
+ 40,
+ 46,
+ 30,
+ 31,
+ 34,
+ 40,
+ 33,
+ 46,
+ 49,
+ 41,
+ 46,
+ 43,
+ 37,
+ 46,
+ 38,
+ 45,
+ 30,
+ 30,
+ 46,
+ 27,
+ 36,
+ 44,
+ 44,
+ 35,
+ 34,
+ 35,
+ 41,
+ 41,
+ 44,
+ 37,
+ 42,
+ 30,
+ 49,
+ 31,
+ 42,
+ 42,
+ 40,
+ 40,
+ 41,
+ 40,
+ 28,
+ 29,
+ 30,
+ 44,
+ 45,
+ 33,
+ 49,
+ 49,
+ 44,
+ 46,
+ 36,
+ 35,
+ 37,
+ 44,
+ 45,
+ 29,
+ 37,
+ 45,
+ 37,
+ 45,
+ 41,
+ 36,
+ 31,
+ 34,
+ 42,
+ 29,
+ 42,
+ 47,
+ 39,
+ 36,
+ 45,
+ 41,
+ 35,
+ 40,
+ 43,
+ 44,
+ 31,
+ 46,
+ 42,
+ 27,
+ 41,
+ 42,
+ 31,
+ 45,
+ 40,
+ 36,
+ 41,
+ 50,
+ 40,
+ 35,
+ 42,
+ 40,
+ 27,
+ 38,
+ 29,
+ 32,
+ 37,
+ 45,
+ 33,
+ 35,
+ 42,
+ 46,
+ 39,
+ 32,
+ 35,
+ 35,
+ 45,
+ 44,
+ 50,
+ 39,
+ 31,
+ 41,
+ 38,
+ 35,
+ 40,
+ 42,
+ 29,
+ 46,
+ 42,
+ 47,
+ 33,
+ 44,
+ 34,
+ 39,
+ 41,
+ 50,
+ 50,
+ 28,
+ 45,
+ 33,
+ 31,
+ 41,
+ 28,
+ 30,
+ 26,
+ 26,
+ 42,
+ 29,
+ 45,
+ 37,
+ 41,
+ 45,
+ 45,
+ 44,
+ 33,
+ 46,
+ 40,
+ 50,
+ 40,
+ 43,
+ 47,
+ 37,
+ 49,
+ 43,
+ 40,
+ 45,
+ 46,
+ 45,
+ 46,
+ 44,
+ 30,
+ 27,
+ 44,
+ 41,
+ 47,
+ 40,
+ 28,
+ 34,
+ 28,
+ 46,
+ 41,
+ 41,
+ 34,
+ 40,
+ 35,
+ 37,
+ 35,
+ 40,
+ 29,
+ 45,
+ 40,
+ 29,
+ 35,
+ 39,
+ 46,
+ 31,
+ 38,
+ 29,
+ 28,
+ 45,
+ 33,
+ 37,
+ 41,
+ 39,
+ 30,
+ 46,
+ 30,
+ 45,
+ 39,
+ 45,
+ 41,
+ 41,
+ 33,
+ 39,
+ 47,
+ 43,
+ 43,
+ 29,
+ 49,
+ 44,
+ 40,
+ 27,
+ 45,
+ 31,
+ 34,
+ 39,
+ 44,
+ 40,
+ 47,
+ 27,
+ 45,
+ 27,
+ 35,
+ 41,
+ 31,
+ 42,
+ 27,
+ 42,
+ 46,
+ 34,
+ 34,
+ 39,
+ 30,
+ 40,
+ 40,
+ 46,
+ 47,
+ 36,
+ 33,
+ 39,
+ 44,
+ 46,
+ 41,
+ 45,
+ 30,
+ 42,
+ 37,
+ 40,
+ 38,
+ 26,
+ 34,
+ 44,
+ 50,
+ 36,
+ 35,
+ 37,
+ 41,
+ 50,
+ 46,
+ 44,
+ 45,
+ 45,
+ 45,
+ 45,
+ 44,
+ 26,
+ 46,
+ 35,
+ 34,
+ 40,
+ 41,
+ 46,
+ 43,
+ 50,
+ 46,
+ 44,
+ 45,
+ 27,
+ 40,
+ 29,
+ 45,
+ 35,
+ 46,
+ 41,
+ 40,
+ 42,
+ 49,
+ 41,
+ 36,
+ 40,
+ 47,
+ 46,
+ 47,
+ 39,
+ 34,
+ 43,
+ 34,
+ 29,
+ 29,
+ 42,
+ 35,
+ 45,
+ 34,
+ 41,
+ 44,
+ 45,
+ 38,
+ 46,
+ 31,
+ 41,
+ 44,
+ 32,
+ 29,
+ 33,
+ 43,
+ 29,
+ 43,
+ 47,
+ 46,
+ 26,
+ 49,
+ 30,
+ 45,
+ 46,
+ 44,
+ 33,
+ 50,
+ 29,
+ 45,
+ 40,
+ 28,
+ 41,
+ 46,
+ 25,
+ 47,
+ 46,
+ 28,
+ 27,
+ 42,
+ 49,
+ 35,
+ 38,
+ 36,
+ 35,
+ 41,
+ 44,
+ 45,
+ 40,
+ 42,
+ 45,
+ 45,
+ 45,
+ 44,
+ 32,
+ 39,
+ 27,
+ 41,
+ 42,
+ 41,
+ 46,
+ 44,
+ 41,
+ 35,
+ 41,
+ 45,
+ 40,
+ 42,
+ 43,
+ 40,
+ 44,
+ 49,
+ 42,
+ 50,
+ 28,
+ 35,
+ 45,
+ 49,
+ 41,
+ 41,
+ 40,
+ 39,
+ 46,
+ 46,
+ 45,
+ 46,
+ 38,
+ 45,
+ 40,
+ 41,
+ 41,
+ 50,
+ 25,
+ 47,
+ 41,
+ 27,
+ 44,
+ 46,
+ 34,
+ 26,
+ 46,
+ 43,
+ 40,
+ 46,
+ 39,
+ 46,
+ 27,
+ 39,
+ 35,
+ 46,
+ 27,
+ 44,
+ 27,
+ 42,
+ 46,
+ 46,
+ 43,
+ 46,
+ 40,
+ 39,
+ 46,
+ 33,
+ 42,
+ 35,
+ 46,
+ 28,
+ 46,
+ 45,
+ 53,
+ 36,
+ 42,
+ 53,
+ 27,
+ 31,
+ 41,
+ 41,
+ 37,
+ 30,
+ 45,
+ 45,
+ 34,
+ 31,
+ 44,
+ 38,
+ 41,
+ 38,
+ 31,
+ 35,
+ 39,
+ 35,
+ 39,
+ 46,
+ 45,
+ 46,
+ 36,
+ 36,
+ 50,
+ 30,
+ 34,
+ 41,
+ 46,
+ 35,
+ 40,
+ 39,
+ 28,
+ 45,
+ 38,
+ 35,
+ 38,
+ 28,
+ 46,
+ 41,
+ 36,
+ 37,
+ 30,
+ 28,
+ 35,
+ 30,
+ 45,
+ 46,
+ 46,
+ 25,
+ 29,
+ 43,
+ 47,
+ 46,
+ 45,
+ 46,
+ 40,
+ 35,
+ 46,
+ 45,
+ 43,
+ 45,
+ 45,
+ 46,
+ 33,
+ 37,
+ 33,
+ 42,
+ 46,
+ 40,
+ 30,
+ 45,
+ 33,
+ 33,
+ 32,
+ 31,
+ 34,
+ 34,
+ 45,
+ 37,
+ 39,
+ 33,
+ 50,
+ 41,
+ 27,
+ 40,
+ 47,
+ 40,
+ 46,
+ 45,
+ 31,
+ 43,
+ 29,
+ 41,
+ 47,
+ 42,
+ 30,
+ 43,
+ 34,
+ 45,
+ 36,
+ 39,
+ 46,
+ 37,
+ 28,
+ 45,
+ 42,
+ 47,
+ 43,
+ 32,
+ 42,
+ 40,
+ 27,
+ 37,
+ 30,
+ 47,
+ 39,
+ 33,
+ 42,
+ 46,
+ 40,
+ 39,
+ 41,
+ 46,
+ 39,
+ 38,
+ 49,
+ 39,
+ 40,
+ 31,
+ 30,
+ 41,
+ 49,
+ 44,
+ 42,
+ 46,
+ 46,
+ 46,
+ 29,
+ 45,
+ 42,
+ 40,
+ 39,
+ 53,
+ 46,
+ 40,
+ 33,
+ 42,
+ 46,
+ 45,
+ 33,
+ 34,
+ 46,
+ 41,
+ 35,
+ 42,
+ 30,
+ 45,
+ 40,
+ 34,
+ 46,
+ 46,
+ 46,
+ 49,
+ 49,
+ 42,
+ 41,
+ 44,
+ 37,
+ 49,
+ 36,
+ 44,
+ 35,
+ 41,
+ 39,
+ 39,
+ 42,
+ 46,
+ 40,
+ 33,
+ 30,
+ 40,
+ 35,
+ 47,
+ 45,
+ 44,
+ 47,
+ 32,
+ 33,
+ 42,
+ 45,
+ 33,
+ 41,
+ 40,
+ 27,
+ 28,
+ 50,
+ 45,
+ 46,
+ 43,
+ 46,
+ 25,
+ 45,
+ 33,
+ 36,
+ 39,
+ 47,
+ 40,
+ 40,
+ 50,
+ 34,
+ 43,
+ 41,
+ 40,
+ 46,
+ 34,
+ 35,
+ 47,
+ 45,
+ 32,
+ 45,
+ 40,
+ 29,
+ 34,
+ 45,
+ 34,
+ 33,
+ 44,
+ 46,
+ 31,
+ 41,
+ 42,
+ 31,
+ 28,
+ 27,
+ 38,
+ 33,
+ 46,
+ 41,
+ 34,
+ 46,
+ 35,
+ 44,
+ 46,
+ 41,
+ 47,
+ 50,
+ 41,
+ 47,
+ 31,
+ 31,
+ 34,
+ 48,
+ 35,
+ 46,
+ 33,
+ 44,
+ 28,
+ 31,
+ 28,
+ 30,
+ 29,
+ 40,
+ 42,
+ 42,
+ 47,
+ 40,
+ 42,
+ 49,
+ 42,
+ 29,
+ 39,
+ 28,
+ 46,
+ 41,
+ 33,
+ 43,
+ 39,
+ 41,
+ 40,
+ 45,
+ 45,
+ 35,
+ 47,
+ 45,
+ 42,
+ 26,
+ 40,
+ 34,
+ 34,
+ 35,
+ 34,
+ 46,
+ 35,
+ 29,
+ 38,
+ 40,
+ 31,
+ 41,
+ 46,
+ 31,
+ 35,
+ 35,
+ 31,
+ 39,
+ 40,
+ 46,
+ 27,
+ 33,
+ 39,
+ 41,
+ 29,
+ 30,
+ 46,
+ 43,
+ 35,
+ 49,
+ 32,
+ 49,
+ 46,
+ 29,
+ 39,
+ 47,
+ 44,
+ 30,
+ 40,
+ 30,
+ 31,
+ 35,
+ 47,
+ 46,
+ 44,
+ 40,
+ 35,
+ 28,
+ 41,
+ 38,
+ 29,
+ 50,
+ 34,
+ 46,
+ 28,
+ 35,
+ 39,
+ 37,
+ 46,
+ 28,
+ 43,
+ 37,
+ 36,
+ 40,
+ 31,
+ 41,
+ 40,
+ 32,
+ 31,
+ 42,
+ 36,
+ 33,
+ 30,
+ 41,
+ 28,
+ 37,
+ 45,
+ 31,
+ 38,
+ 40,
+ 35,
+ 44,
+ 37,
+ 41,
+ 46,
+ 32,
+ 39,
+ 43,
+ 29,
+ 29,
+ 40,
+ 45,
+ 34,
+ 40,
+ 40,
+ 41,
+ 45,
+ 45,
+ 36,
+ 31,
+ 41,
+ 43,
+ 41,
+ 29,
+ 27,
+ 31,
+ 27,
+ 41,
+ 39,
+ 39,
+ 46,
+ 35,
+ 47,
+ 47,
+ 28,
+ 46,
+ 40,
+ 35,
+ 33,
+ 40,
+ 44,
+ 45,
+ 45,
+ 29,
+ 45,
+ 41,
+ 26,
+ 39,
+ 35,
+ 29,
+ 43,
+ 49,
+ 33,
+ 32,
+ 43,
+ 45,
+ 29,
+ 37,
+ 37,
+ 42,
+ 27,
+ 44,
+ 41,
+ 40,
+ 39,
+ 36,
+ 48,
+ 33,
+ 46,
+ 27,
+ 44,
+ 28,
+ 34,
+ 46,
+ 38,
+ 44,
+ 34,
+ 40,
+ 40,
+ 43,
+ 42,
+ 34,
+ 28,
+ 45,
+ 39,
+ 47,
+ 45,
+ 45,
+ 34,
+ 46,
+ 45,
+ 34,
+ 35,
+ 43,
+ 34,
+ 32,
+ 46,
+ 39,
+ 46,
+ 35,
+ 27,
+ 29,
+ 40,
+ 39,
+ 42,
+ 44,
+ 45,
+ 45,
+ 41,
+ 38,
+ 31,
+ 42,
+ 46,
+ 48,
+ 29,
+ 33,
+ 36,
+ 37,
+ 35,
+ 45,
+ 46,
+ 39,
+ 30,
+ 39,
+ 30,
+ 30,
+ 45,
+ 46,
+ 41,
+ 29,
+ 33,
+ 45,
+ 33,
+ 30,
+ 39,
+ 34,
+ 45,
+ 27,
+ 27,
+ 45,
+ 40,
+ 49,
+ 40,
+ 46,
+ 41,
+ 28,
+ 30,
+ 42,
+ 28,
+ 41,
+ 30,
+ 40,
+ 50,
+ 39,
+ 41,
+ 31,
+ 27,
+ 40,
+ 45,
+ 33,
+ 45,
+ 46,
+ 43,
+ 39,
+ 38,
+ 32,
+ 44,
+ 26,
+ 45,
+ 41,
+ 41,
+ 41,
+ 38,
+ 31,
+ 31,
+ 29,
+ 27,
+ 37,
+ 41,
+ 38,
+ 35,
+ 30,
+ 45,
+ 45,
+ 46,
+ 28,
+ 32,
+ 42,
+ 27,
+ 46,
+ 31,
+ 33,
+ 41,
+ 45,
+ 31,
+ 44,
+ 30,
+ 45,
+ 38,
+ 41,
+ 45,
+ 40,
+ 39,
+ 35,
+ 39,
+ 45,
+ 46,
+ 34,
+ 32,
+ 33,
+ 28,
+ 34,
+ 46,
+ 40,
+ 30,
+ 42,
+ 44,
+ 41,
+ 41,
+ 36,
+ 36,
+ 45,
+ 28,
+ 32,
+ 46,
+ 40,
+ 50,
+ 43,
+ 45,
+ 25,
+ 27,
+ 41,
+ 46,
+ 29,
+ 28,
+ 46,
+ 29,
+ 45,
+ 41,
+ 46,
+ 44,
+ 29,
+ 41,
+ 40,
+ 45,
+ 41,
+ 36,
+ 40,
+ 32,
+ 45,
+ 45,
+ 32,
+ 36,
+ 41,
+ 44,
+ 34,
+ 31,
+ 41,
+ 31,
+ 40,
+ 37,
+ 45,
+ 46,
+ 31,
+ 29,
+ 40,
+ 31,
+ 40,
+ 40,
+ 35,
+ 44,
+ 44,
+ 28,
+ 28,
+ 45,
+ 43,
+ 30,
+ 39,
+ 30,
+ 44,
+ 41,
+ 42,
+ 44,
+ 27,
+ 45,
+ 41,
+ 46,
+ 31,
+ 31,
+ 31,
+ 32,
+ 39,
+ 30,
+ 33,
+ 31,
+ 36,
+ 45,
+ 41,
+ 45,
+ 45,
+ 46,
+ 35,
+ 41,
+ 41,
+ 36,
+ 30,
+ 42,
+ 29,
+ 45,
+ 46,
+ 50,
+ 42,
+ 40,
+ 39,
+ 45,
+ 35,
+ 40,
+ 30,
+ 41,
+ 46,
+ 39,
+ 41,
+ 42,
+ 30,
+ 43,
+ 35,
+ 27,
+ 45,
+ 35,
+ 31,
+ 46,
+ 44,
+ 33,
+ 35,
+ 42,
+ 28,
+ 33,
+ 47,
+ 45,
+ 37,
+ 33,
+ 46,
+ 41,
+ 46,
+ 28,
+ 37,
+ 30,
+ 39,
+ 43,
+ 42,
+ 45,
+ 41,
+ 34,
+ 42,
+ 39,
+ 33,
+ 46,
+ 38,
+ 30,
+ 41,
+ 27,
+ 47,
+ 40,
+ 31,
+ 49,
+ 41,
+ 42,
+ 37,
+ 39,
+ 46,
+ 33,
+ 42,
+ 38,
+ 41,
+ 46,
+ 30,
+ 35,
+ 33,
+ 43,
+ 33,
+ 46,
+ 27,
+ 27,
+ 33,
+ 29,
+ 45,
+ 40,
+ 44,
+ 41,
+ 46,
+ 37,
+ 46,
+ 39,
+ 34,
+ 34,
+ 46,
+ 27,
+ 40,
+ 34,
+ 50,
+ 45,
+ 45,
+ 45,
+ 35,
+ 35,
+ 32,
+ 40,
+ 40,
+ 45,
+ 43,
+ 35,
+ 40,
+ 39,
+ 31,
+ 36,
+ 44,
+ 39,
+ 39,
+ 50,
+ 39,
+ 49,
+ 47,
+ 28,
+ 45,
+ 45,
+ 46,
+ 41,
+ 44,
+ 33,
+ 45,
+ 32,
+ 36,
+ 28,
+ 47,
+ 35,
+ 38,
+ 42,
+ 40,
+ 40,
+ 39,
+ 33,
+ 33,
+ 32,
+ 30,
+ 46,
+ 34,
+ 46,
+ 29,
+ 40,
+ 34,
+ 45,
+ 30,
+ 27,
+ 36,
+ 28,
+ 42,
+ 41,
+ 44,
+ 31,
+ 27,
+ 38,
+ 29,
+ 46,
+ 46,
+ 40,
+ 34,
+ 45,
+ 40,
+ 41,
+ 28,
+ 40,
+ 42,
+ 46,
+ 47,
+ 31,
+ 46,
+ 46,
+ 34,
+ 46,
+ 41,
+ 39,
+ 46,
+ 37,
+ 46,
+ 36,
+ 31,
+ 40,
+ 46,
+ 37,
+ 29,
+ 42,
+ 35,
+ 39,
+ 42,
+ 37,
+ 44,
+ 45,
+ 44,
+ 33,
+ 35,
+ 46,
+ 40,
+ 45,
+ 33,
+ 40,
+ 33,
+ 35,
+ 43,
+ 45,
+ 41,
+ 41,
+ 44,
+ 40,
+ 45,
+ 26,
+ 46,
+ 29,
+ 39,
+ 46,
+ 45,
+ 41,
+ 34,
+ 30,
+ 27,
+ 50,
+ 31,
+ 45,
+ 39,
+ 34,
+ 49,
+ 45,
+ 40,
+ 40,
+ 43,
+ 40,
+ 50,
+ 38,
+ 46,
+ 40,
+ 37,
+ 34,
+ 40,
+ 31,
+ 41,
+ 35,
+ 42,
+ 44,
+ 31,
+ 45,
+ 42,
+ 40,
+ 41,
+ 38,
+ 33,
+ 47,
+ 45,
+ 46,
+ 44,
+ 31,
+ 39,
+ 46,
+ 38,
+ 27,
+ 46,
+ 30,
+ 28,
+ 49,
+ 34,
+ 46,
+ 36,
+ 42,
+ 39,
+ 36,
+ 45,
+ 45,
+ 34,
+ 33,
+ 30,
+ 53,
+ 45,
+ 46,
+ 36,
+ 41,
+ 45,
+ 46,
+ 45,
+ 45,
+ 29,
+ 42,
+ 39,
+ 36,
+ 33,
+ 45,
+ 41,
+ 45,
+ 40,
+ 42,
+ 35,
+ 37,
+ 33,
+ 29,
+ 30,
+ 35,
+ 35,
+ 45,
+ 35,
+ 41,
+ 41,
+ 45,
+ 44,
+ 28,
+ 34,
+ 44,
+ 42,
+ 42,
+ 45,
+ 40,
+ 39,
+ 43,
+ 43,
+ 33,
+ 30,
+ 29,
+ 31,
+ 42,
+ 39,
+ 27,
+ 26,
+ 46,
+ 27,
+ 39,
+ 27,
+ 31,
+ 34,
+ 31,
+ 34,
+ 31,
+ 28,
+ 37,
+ 28,
+ 49,
+ 35,
+ 39,
+ 34,
+ 39,
+ 46,
+ 36,
+ 35,
+ 39,
+ 42,
+ 42,
+ 40,
+ 33,
+ 34,
+ 32,
+ 33,
+ 44,
+ 37,
+ 42,
+ 50,
+ 41,
+ 39,
+ 31,
+ 46,
+ 40,
+ 33,
+ 36,
+ 34,
+ 43,
+ 40,
+ 31,
+ 33,
+ 37,
+ 35,
+ 41,
+ 44,
+ 33,
+ 42,
+ 47,
+ 46,
+ 37,
+ 35,
+ 28,
+ 39,
+ 32,
+ 47,
+ 35,
+ 40,
+ 31,
+ 33,
+ 39,
+ 50,
+ 29,
+ 27,
+ 27,
+ 40,
+ 29,
+ 42,
+ 32,
+ 31,
+ 50,
+ 44,
+ 39,
+ 27,
+ 43,
+ 50,
+ 40,
+ 31,
+ 40,
+ 46,
+ 35,
+ 39,
+ 35,
+ 30,
+ 36,
+ 40,
+ 30,
+ 45,
+ 34,
+ 43,
+ 46,
+ 44,
+ 38,
+ 45,
+ 43,
+ 30,
+ 40,
+ 46,
+ 33,
+ 31,
+ 40,
+ 35,
+ 35,
+ 33,
+ 41,
+ 34,
+ 50,
+ 39,
+ 40,
+ 32,
+ 46,
+ 41,
+ 34,
+ 34,
+ 46,
+ 43,
+ 39,
+ 33,
+ 42,
+ 32,
+ 36,
+ 29,
+ 39,
+ 38,
+ 35,
+ 35,
+ 32,
+ 45,
+ 46,
+ 46,
+ 32,
+ 46,
+ 31,
+ 32,
+ 34,
+ 49,
+ 42,
+ 47,
+ 27,
+ 39,
+ 38,
+ 46,
+ 41,
+ 41,
+ 30,
+ 39,
+ 45,
+ 46,
+ 28,
+ 38,
+ 31,
+ 34,
+ 37,
+ 28,
+ 41,
+ 46,
+ 34,
+ 39,
+ 40,
+ 46,
+ 33,
+ 30,
+ 34,
+ 28,
+ 34,
+ 30,
+ 34,
+ 46,
+ 34,
+ 45,
+ 32,
+ 41,
+ 38,
+ 46,
+ 43,
+ 29,
+ 46,
+ 31,
+ 45,
+ 32,
+ 27,
+ 39,
+ 38,
+ 31,
+ 41,
+ 46,
+ 38,
+ 45,
+ 28,
+ 32,
+ 41,
+ 37,
+ 32,
+ 34,
+ 34,
+ 28,
+ 47,
+ 39,
+ 34,
+ 29,
+ 30,
+ 40,
+ 46,
+ 47,
+ 47,
+ 38,
+ 26,
+ 39,
+ 40,
+ 35,
+ 37,
+ 35,
+ 34,
+ 47,
+ 35,
+ 46,
+ 38,
+ 27,
+ 40,
+ 46,
+ 40,
+ 29,
+ 46,
+ 26,
+ 49,
+ 34,
+ 42,
+ 44,
+ 27,
+ 31,
+ 44,
+ 31,
+ 50,
+ 45,
+ 40,
+ 40,
+ 40,
+ 39,
+ 45,
+ 36,
+ 45,
+ 50,
+ 27,
+ 45,
+ 39,
+ 40,
+ 43,
+ 29,
+ 45,
+ 36,
+ 39,
+ 27,
+ 31,
+ 46,
+ 33,
+ 27,
+ 41,
+ 34,
+ 36,
+ 33,
+ 45,
+ 31,
+ 28,
+ 40,
+ 45,
+ 40,
+ 29,
+ 30,
+ 45,
+ 47,
+ 50,
+ 34,
+ 36,
+ 40,
+ 45,
+ 27,
+ 35,
+ 41,
+ 40,
+ 34,
+ 33,
+ 34,
+ 45,
+ 30,
+ 45,
+ 37,
+ 34,
+ 28,
+ 39,
+ 50,
+ 34,
+ 34,
+ 32,
+ 31,
+ 34,
+ 45,
+ 50,
+ 43,
+ 30,
+ 47,
+ 46,
+ 34,
+ 41,
+ 45,
+ 39,
+ 46,
+ 40,
+ 33,
+ 31,
+ 42,
+ 35,
+ 35,
+ 41,
+ 35,
+ 42,
+ 45,
+ 34,
+ 45,
+ 32,
+ 26,
+ 41,
+ 33,
+ 41,
+ 50,
+ 32,
+ 42,
+ 46,
+ 30,
+ 33,
+ 33,
+ 40,
+ 41,
+ 42,
+ 47,
+ 28,
+ 32,
+ 46,
+ 42,
+ 42,
+ 38,
+ 41,
+ 29,
+ 34,
+ 42,
+ 44,
+ 42,
+ 40,
+ 36,
+ 41,
+ 30,
+ 35,
+ 31,
+ 41,
+ 28,
+ 47,
+ 33,
+ 53,
+ 43,
+ 46,
+ 28,
+ 31,
+ 33,
+ 42,
+ 41,
+ 31,
+ 46,
+ 46,
+ 28,
+ 34,
+ 36,
+ 29,
+ 45,
+ 33,
+ 30,
+ 40,
+ 40,
+ 30,
+ 32,
+ 50,
+ 31,
+ 30,
+ 41,
+ 47,
+ 34,
+ 36,
+ 47,
+ 39,
+ 47,
+ 40,
+ 33,
+ 33,
+ 40,
+ 32,
+ 36,
+ 35,
+ 39,
+ 35,
+ 29,
+ 47,
+ 30,
+ 35,
+ 27,
+ 40,
+ 35,
+ 25,
+ 34,
+ 40,
+ 37,
+ 31,
+ 43,
+ 45,
+ 45,
+ 37,
+ 40,
+ 44,
+ 41,
+ 35,
+ 30,
+ 45,
+ 46,
+ 41,
+ 46,
+ 45,
+ 40,
+ 29,
+ 39,
+ 45,
+ 44,
+ 44,
+ 45,
+ 28,
+ 33,
+ 44,
+ 40,
+ 27,
+ 45,
+ 27,
+ 31,
+ 35,
+ 50,
+ 32,
+ 39,
+ 42,
+ 46,
+ 46,
+ 45,
+ 41,
+ 37,
+ 45,
+ 28,
+ 34,
+ 33,
+ 30,
+ 39,
+ 33,
+ 31,
+ 35,
+ 29,
+ 46,
+ 41,
+ 45,
+ 42,
+ 36,
+ 45,
+ 42,
+ 46,
+ 41,
+ 35,
+ 44,
+ 45,
+ 29,
+ 50,
+ 36,
+ 30,
+ 28,
+ 33,
+ 32,
+ 36,
+ 30,
+ 45,
+ 40,
+ 34,
+ 40,
+ 31,
+ 33,
+ 37,
+ 40,
+ 46,
+ 29,
+ 46,
+ 28,
+ 40,
+ 29,
+ 28,
+ 40,
+ 43,
+ 41,
+ 33,
+ 27,
+ 37,
+ 50,
+ 45,
+ 41,
+ 45,
+ 34,
+ 41,
+ 39,
+ 34,
+ 32,
+ 33,
+ 45,
+ 42,
+ 27,
+ 53,
+ 42,
+ 32,
+ 39,
+ 38,
+ 41,
+ 28,
+ 35,
+ 37,
+ 35,
+ 46,
+ 44,
+ 27,
+ 34,
+ 31,
+ 33,
+ 37,
+ 31,
+ 45,
+ 40,
+ 47,
+ 49,
+ 40,
+ 46,
+ 33,
+ 38,
+ 40,
+ 37,
+ 38,
+ 43,
+ 35,
+ 40,
+ 46,
+ 38,
+ 43,
+ 49,
+ 33,
+ 31,
+ 50,
+ 30,
+ 32,
+ 42,
+ 41,
+ 47,
+ 45,
+ 36,
+ 36,
+ 36,
+ 40,
+ 28,
+ 46,
+ 29,
+ 34,
+ 30,
+ 27,
+ 39,
+ 39,
+ 40,
+ 41,
+ 35,
+ 45,
+ 37,
+ 40,
+ 32,
+ 45,
+ 29,
+ 40,
+ 46,
+ 34,
+ 37,
+ 40,
+ 45,
+ 42,
+ 35,
+ 33,
+ 31,
+ 34,
+ 35,
+ 34,
+ 50,
+ 35,
+ 41,
+ 40,
+ 34,
+ 40,
+ 47,
+ 45,
+ 31,
+ 46,
+ 42,
+ 36,
+ 41,
+ 40,
+ 53,
+ 34,
+ 45,
+ 28,
+ 30,
+ 28,
+ 40,
+ 46,
+ 34,
+ 34,
+ 29,
+ 36,
+ 30,
+ 47,
+ 38,
+ 34,
+ 50,
+ 46,
+ 42,
+ 45,
+ 36,
+ 37,
+ 36,
+ 46,
+ 40,
+ 39,
+ 32,
+ 40,
+ 46,
+ 50,
+ 36,
+ 33,
+ 32,
+ 50,
+ 29,
+ 39,
+ 33,
+ 41,
+ 31,
+ 31,
+ 28,
+ 39,
+ 33,
+ 35,
+ 42,
+ 40,
+ 40,
+ 31,
+ 26,
+ 31,
+ 47,
+ 35,
+ 41,
+ 38,
+ 30,
+ 40,
+ 33,
+ 34,
+ 32,
+ 34,
+ 33,
+ 40,
+ 46,
+ 35,
+ 36,
+ 46,
+ 45,
+ 39,
+ 37,
+ 33,
+ 45,
+ 32,
+ 42,
+ 39,
+ 31,
+ 46,
+ 46,
+ 31,
+ 46,
+ 31,
+ 35,
+ 42,
+ 40,
+ 46,
+ 39,
+ 36,
+ 35,
+ 28,
+ 31,
+ 45,
+ 33,
+ 33,
+ 47,
+ 45,
+ 39,
+ 45,
+ 41,
+ 35,
+ 34,
+ 43,
+ 36,
+ 33,
+ 27,
+ 42,
+ 28,
+ 32,
+ 45,
+ 38,
+ 50,
+ 27,
+ 35,
+ 31,
+ 35,
+ 28,
+ 34,
+ 28,
+ 47,
+ 46,
+ 46,
+ 53,
+ 49,
+ 42,
+ 37,
+ 52,
+ 35,
+ 34,
+ 34,
+ 50,
+ 33,
+ 40,
+ 30,
+ 33,
+ 50,
+ 46,
+ 32,
+ 30,
+ 43,
+ 33,
+ 39,
+ 36,
+ 36,
+ 39,
+ 28,
+ 49,
+ 42,
+ 39,
+ 34,
+ 45,
+ 34,
+ 44,
+ 27,
+ 41,
+ 38,
+ 40,
+ 43,
+ 35,
+ 45,
+ 38,
+ 46,
+ 39,
+ 39,
+ 39,
+ 45,
+ 46,
+ 37,
+ 40,
+ 46,
+ 35,
+ 39,
+ 29,
+ 40,
+ 41,
+ 49,
+ 35,
+ 41,
+ 27,
+ 36,
+ 28,
+ 42,
+ 34,
+ 28,
+ 35,
+ 33,
+ 40,
+ 39,
+ 27,
+ 47,
+ 41,
+ 31,
+ 43,
+ 46,
+ 26,
+ 31,
+ 43,
+ 33,
+ 41,
+ 39,
+ 28,
+ 41,
+ 45,
+ 45,
+ 28,
+ 39,
+ 31,
+ 26,
+ 40,
+ 31,
+ 35,
+ 37,
+ 36,
+ 34,
+ 50,
+ 31,
+ 44,
+ 28,
+ 26,
+ 28,
+ 26,
+ 39,
+ 33,
+ 41,
+ 45,
+ 31,
+ 33,
+ 32,
+ 41,
+ 40,
+ 40,
+ 41,
+ 39,
+ 34,
+ 45,
+ 35,
+ 28,
+ 31,
+ 31,
+ 29,
+ 39,
+ 27,
+ 31,
+ 45,
+ 46,
+ 43,
+ 39,
+ 33,
+ 46,
+ 38,
+ 41,
+ 34,
+ 34,
+ 40,
+ 40,
+ 49,
+ 40,
+ 45,
+ 46,
+ 49,
+ 36,
+ 29,
+ 33,
+ 46,
+ 30,
+ 27,
+ 41,
+ 32,
+ 32,
+ 31,
+ 46,
+ 34,
+ 28,
+ 31,
+ 27,
+ 36,
+ 38,
+ 38,
+ 36,
+ 34,
+ 46,
+ 46,
+ 33,
+ 37,
+ 44,
+ 40,
+ 46,
+ 41,
+ 33,
+ 46,
+ 36,
+ 40,
+ 31,
+ 31,
+ 27,
+ 50,
+ 45,
+ 46,
+ 39,
+ 46,
+ 46,
+ 31,
+ 45,
+ 32,
+ 34,
+ 33,
+ 42,
+ 38,
+ 39,
+ 31,
+ 41,
+ 40,
+ 29,
+ 53,
+ 46,
+ 45,
+ 45,
+ 32,
+ 43,
+ 32,
+ 35,
+ 41,
+ 45,
+ 44,
+ 33,
+ 53,
+ 34,
+ 46,
+ 35,
+ 35,
+ 32,
+ 32,
+ 50,
+ 50,
+ 26,
+ 45,
+ 40,
+ 30,
+ 45,
+ 45,
+ 27,
+ 34,
+ 37,
+ 29,
+ 34,
+ 36,
+ 26,
+ 42,
+ 45,
+ 31,
+ 46,
+ 39,
+ 45,
+ 46,
+ 50,
+ 34,
+ 40,
+ 28,
+ 27,
+ 39,
+ 40,
+ 28,
+ 30,
+ 39,
+ 29,
+ 40,
+ 39,
+ 33,
+ 29,
+ 45,
+ 32,
+ 40,
+ 36,
+ 33,
+ 45,
+ 41,
+ 27,
+ 35,
+ 34,
+ 41,
+ 38,
+ 28,
+ 50,
+ 33,
+ 34,
+ 39,
+ 35,
+ 39,
+ 50,
+ 33,
+ 41,
+ 46,
+ 45,
+ 34,
+ 33,
+ 36,
+ 46,
+ 31,
+ 28,
+ 36,
+ 42,
+ 34,
+ 45,
+ 33,
+ 30,
+ 47,
+ 45,
+ 34,
+ 25,
+ 33,
+ 40,
+ 42,
+ 33,
+ 44,
+ 40,
+ 36,
+ 45,
+ 53,
+ 40,
+ 27,
+ 31,
+ 40,
+ 40,
+ 40,
+ 46,
+ 32,
+ 37,
+ 29,
+ 46,
+ 35,
+ 35,
+ 32,
+ 43,
+ 40,
+ 27,
+ 43,
+ 40,
+ 30,
+ 40,
+ 45,
+ 41,
+ 41,
+ 34,
+ 33,
+ 46,
+ 44,
+ 46,
+ 41,
+ 49,
+ 39,
+ 31,
+ 47,
+ 39,
+ 37,
+ 34,
+ 46,
+ 40,
+ 37,
+ 36,
+ 40,
+ 37,
+ 39,
+ 33,
+ 39,
+ 46,
+ 37,
+ 46,
+ 37,
+ 39,
+ 34,
+ 42,
+ 46,
+ 50,
+ 41,
+ 29,
+ 34,
+ 29,
+ 30,
+ 41,
+ 45,
+ 33,
+ 33,
+ 33,
+ 34,
+ 28,
+ 44,
+ 34,
+ 37,
+ 36,
+ 49,
+ 28,
+ 29,
+ 40,
+ 34,
+ 40,
+ 50,
+ 32,
+ 41,
+ 34,
+ 45,
+ 34,
+ 39,
+ 40,
+ 46,
+ 33,
+ 41,
+ 33,
+ 46,
+ 31,
+ 38,
+ 38,
+ 45,
+ 36,
+ 30,
+ 42,
+ 46,
+ 32,
+ 40,
+ 37,
+ 44,
+ 36,
+ 43,
+ 28,
+ 35,
+ 33,
+ 29,
+ 27,
+ 40,
+ 27,
+ 28,
+ 50,
+ 35,
+ 45,
+ 46,
+ 32,
+ 44,
+ 37,
+ 42,
+ 41,
+ 45,
+ 33,
+ 53,
+ 34,
+ 36,
+ 41,
+ 31,
+ 29,
+ 31,
+ 32,
+ 38,
+ 41,
+ 31,
+ 45,
+ 41,
+ 50,
+ 35,
+ 44,
+ 32,
+ 34,
+ 36,
+ 37,
+ 32,
+ 34,
+ 33,
+ 33,
+ 32,
+ 39,
+ 37,
+ 40,
+ 28,
+ 40,
+ 26,
+ 29,
+ 39,
+ 37,
+ 33,
+ 36,
+ 49,
+ 41,
+ 41,
+ 46,
+ 32,
+ 45,
+ 44,
+ 30,
+ 36,
+ 33,
+ 46,
+ 35,
+ 43,
+ 32,
+ 53,
+ 49,
+ 40,
+ 50,
+ 35,
+ 50,
+ 27,
+ 28,
+ 31,
+ 33,
+ 30,
+ 45,
+ 45,
+ 40,
+ 31,
+ 46,
+ 46,
+ 30,
+ 46,
+ 34,
+ 39,
+ 39,
+ 33,
+ 42,
+ 40,
+ 34,
+ 32,
+ 44,
+ 31,
+ 46,
+ 27,
+ 37,
+ 36,
+ 35,
+ 45,
+ 28,
+ 41,
+ 31,
+ 40,
+ 45,
+ 42,
+ 41,
+ 39,
+ 33,
+ 28,
+ 29,
+ 35,
+ 34,
+ 33,
+ 27,
+ 45,
+ 36,
+ 32,
+ 50,
+ 27,
+ 32,
+ 32,
+ 28,
+ 44,
+ 47,
+ 29,
+ 33,
+ 40,
+ 41,
+ 41,
+ 39,
+ 36,
+ 41,
+ 32,
+ 33,
+ 45,
+ 40,
+ 44,
+ 38,
+ 28,
+ 39,
+ 28,
+ 37,
+ 31,
+ 34,
+ 29,
+ 38,
+ 46,
+ 35,
+ 32,
+ 34,
+ 34,
+ 33,
+ 35,
+ 36,
+ 45,
+ 50,
+ 53,
+ 31,
+ 38,
+ 44,
+ 36,
+ 33,
+ 34,
+ 35,
+ 45,
+ 40,
+ 41,
+ 35,
+ 32,
+ 32,
+ 28,
+ 33,
+ 34,
+ 46,
+ 33,
+ 34,
+ 44,
+ 28,
+ 41,
+ 27,
+ 28,
+ 34,
+ 39,
+ 53,
+ 35,
+ 40,
+ 27,
+ 40,
+ 34,
+ 35,
+ 35,
+ 32,
+ 33,
+ 45,
+ 32,
+ 34,
+ 36,
+ 36,
+ 30,
+ 34,
+ 45,
+ 33,
+ 45,
+ 35,
+ 46,
+ 45,
+ 28,
+ 34,
+ 39,
+ 34,
+ 43,
+ 44,
+ 32,
+ 43,
+ 41,
+ 29,
+ 32,
+ 33,
+ 46,
+ 30,
+ 35,
+ 46,
+ 34,
+ 33,
+ 31,
+ 28,
+ 27,
+ 30,
+ 30,
+ 36,
+ 46,
+ 32,
+ 45,
+ 39,
+ 40,
+ 49,
+ 49,
+ 38,
+ 34,
+ 32,
+ 46,
+ 34,
+ 40,
+ 39,
+ 35,
+ 28,
+ 34,
+ 33,
+ 33,
+ 41,
+ 31,
+ 32,
+ 36,
+ 33,
+ 33,
+ 45,
+ 37,
+ 37,
+ 29,
+ 40,
+ 30,
+ 29,
+ 45,
+ 39,
+ 31,
+ 36,
+ 45,
+ 43,
+ 35,
+ 35,
+ 35,
+ 36,
+ 52,
+ 31,
+ 32,
+ 36,
+ 38,
+ 40,
+ 36,
+ 41,
+ 30,
+ 45,
+ 33,
+ 38,
+ 46,
+ 45,
+ 34,
+ 31,
+ 36,
+ 40,
+ 39,
+ 37,
+ 38,
+ 33,
+ 31,
+ 38,
+ 49,
+ 34,
+ 27,
+ 33,
+ 35,
+ 33,
+ 50,
+ 41,
+ 33,
+ 43,
+ 40,
+ 46,
+ 38,
+ 47,
+ 45,
+ 29,
+ 45,
+ 40,
+ 35,
+ 35,
+ 44,
+ 37,
+ 45,
+ 34,
+ 45,
+ 40,
+ 45,
+ 32,
+ 36,
+ 41,
+ 40,
+ 35,
+ 33,
+ 49,
+ 44,
+ 29,
+ 31,
+ 34,
+ 32,
+ 33,
+ 40,
+ 35,
+ 30,
+ 35,
+ 47,
+ 35,
+ 45,
+ 28,
+ 41,
+ 47,
+ 46,
+ 34,
+ 33,
+ 38,
+ 30,
+ 39,
+ 46,
+ 50,
+ 41,
+ 40,
+ 32,
+ 29,
+ 29,
+ 34,
+ 32,
+ 38,
+ 41,
+ 33,
+ 35,
+ 29,
+ 34,
+ 46,
+ 27,
+ 32,
+ 35,
+ 31,
+ 46,
+ 35,
+ 33,
+ 26,
+ 40,
+ 45,
+ 38,
+ 45,
+ 43,
+ 27,
+ 31,
+ 26,
+ 31,
+ 50,
+ 30,
+ 35,
+ 33,
+ 46,
+ 40,
+ 44,
+ 47,
+ 32,
+ 39,
+ 35,
+ 31,
+ 29,
+ 39,
+ 35,
+ 29,
+ 38,
+ 43,
+ 36,
+ 29,
+ 40,
+ 42,
+ 35,
+ 32,
+ 40,
+ 41,
+ 38,
+ 40,
+ 34,
+ 34,
+ 27,
+ 36,
+ 34,
+ 49,
+ 42,
+ 41,
+ 27,
+ 50,
+ 41,
+ 32,
+ 33,
+ 30,
+ 34,
+ 45,
+ 50,
+ 30,
+ 34,
+ 31,
+ 39,
+ 49,
+ 27,
+ 32,
+ 27,
+ 34,
+ 27,
+ 33,
+ 36,
+ 46,
+ 30,
+ 28,
+ 50,
+ 50,
+ 44,
+ 34,
+ 37,
+ 37,
+ 37,
+ 36,
+ 27,
+ 33,
+ 33,
+ 36,
+ 38,
+ 33,
+ 39,
+ 32,
+ 27,
+ 31,
+ 32,
+ 27,
+ 43,
+ 35,
+ 28,
+ 32,
+ 34,
+ 32,
+ 33,
+ 35,
+ 50,
+ 28,
+ 34,
+ 39,
+ 34,
+ 39,
+ 36,
+ 41,
+ 27,
+ 26,
+ 46,
+ 46,
+ 42,
+ 27,
+ 36,
+ 39,
+ 35,
+ 34,
+ 41,
+ 31,
+ 33,
+ 36,
+ 39,
+ 50,
+ 38,
+ 38,
+ 39,
+ 31,
+ 26,
+ 52,
+ 46,
+ 41,
+ 43,
+ 46,
+ 27,
+ 39,
+ 50,
+ 33,
+ 34,
+ 50,
+ 31,
+ 36,
+ 45,
+ 34,
+ 41,
+ 38,
+ 50,
+ 27,
+ 31,
+ 41,
+ 34,
+ 33,
+ 40,
+ 31,
+ 39,
+ 36,
+ 31,
+ 45,
+ 34,
+ 49,
+ 45,
+ 35,
+ 39,
+ 30,
+ 27,
+ 40,
+ 45,
+ 45,
+ 36,
+ 39,
+ 43,
+ 40,
+ 40,
+ 39,
+ 28,
+ 36,
+ 37,
+ 46,
+ 30,
+ 35,
+ 34,
+ 32,
+ 35,
+ 32,
+ 40,
+ 31,
+ 45,
+ 40,
+ 36,
+ 36,
+ 36,
+ 37,
+ 28,
+ 31,
+ 46,
+ 31,
+ 41,
+ 41,
+ 45,
+ 32,
+ 34,
+ 30,
+ 36,
+ 39,
+ 45,
+ 36,
+ 37,
+ 29,
+ 41,
+ 50,
+ 35,
+ 34,
+ 35,
+ 33,
+ 29,
+ 45,
+ 40,
+ 33,
+ 36,
+ 32,
+ 33,
+ 50,
+ 36,
+ 36,
+ 46,
+ 39,
+ 37,
+ 36,
+ 45,
+ 46,
+ 39,
+ 46,
+ 28,
+ 27,
+ 34,
+ 36,
+ 42,
+ 32,
+ 46,
+ 44,
+ 39,
+ 31,
+ 45,
+ 50,
+ 37,
+ 36,
+ 33,
+ 39,
+ 35,
+ 32,
+ 35,
+ 39,
+ 47,
+ 35,
+ 32,
+ 33,
+ 45,
+ 28,
+ 33,
+ 40,
+ 33,
+ 31,
+ 50,
+ 29,
+ 33,
+ 32,
+ 32,
+ 53,
+ 42,
+ 31,
+ 27,
+ 31,
+ 46,
+ 28,
+ 33,
+ 31,
+ 36,
+ 34,
+ 32,
+ 46,
+ 37,
+ 45,
+ 32,
+ 34,
+ 33,
+ 32,
+ 50,
+ 33,
+ 46,
+ 28,
+ 35,
+ 45,
+ 33,
+ 29,
+ 45,
+ 41,
+ 50,
+ 37,
+ 45,
+ 39,
+ 45,
+ 49,
+ 45,
+ 30,
+ 39,
+ 47,
+ 45,
+ 33,
+ 34,
+ 29,
+ 34,
+ 33,
+ 29,
+ 33,
+ 34,
+ 46,
+ 39,
+ 27,
+ 27,
+ 35,
+ 38,
+ 46,
+ 41,
+ 33,
+ 51,
+ 41,
+ 28,
+ 32,
+ 41,
+ 45,
+ 34,
+ 50,
+ 33,
+ 42,
+ 36,
+ 45,
+ 40,
+ 34,
+ 31,
+ 35,
+ 32,
+ 31,
+ 40,
+ 27,
+ 49,
+ 50,
+ 40,
+ 35,
+ 31,
+ 45,
+ 36,
+ 40,
+ 32,
+ 36,
+ 37,
+ 50,
+ 45,
+ 34,
+ 29,
+ 50,
+ 31,
+ 41,
+ 34,
+ 36,
+ 33,
+ 36,
+ 32,
+ 33,
+ 34,
+ 45,
+ 37,
+ 31,
+ 31,
+ 31,
+ 32,
+ 42,
+ 32,
+ 50,
+ 31,
+ 32,
+ 39,
+ 28,
+ 38,
+ 39,
+ 37,
+ 31,
+ 45,
+ 33,
+ 30,
+ 39,
+ 41,
+ 38,
+ 29,
+ 39,
+ 39,
+ 31,
+ 30,
+ 37,
+ 34,
+ 29,
+ 28,
+ 32,
+ 29,
+ 36,
+ 33,
+ 45,
+ 32,
+ 42,
+ 32,
+ 42,
+ 32,
+ 36,
+ 28,
+ 28,
+ 34,
+ 40,
+ 38,
+ 45,
+ 35,
+ 34,
+ 40,
+ 27,
+ 34,
+ 35,
+ 34,
+ 45,
+ 34,
+ 26,
+ 52,
+ 46,
+ 45,
+ 47,
+ 53,
+ 42,
+ 35,
+ 33,
+ 36,
+ 32,
+ 34,
+ 47,
+ 39,
+ 41,
+ 32,
+ 34,
+ 36,
+ 50,
+ 36,
+ 35,
+ 45,
+ 32,
+ 32,
+ 39,
+ 37,
+ 33,
+ 38,
+ 50,
+ 50,
+ 33,
+ 45,
+ 35,
+ 33,
+ 33,
+ 31,
+ 31,
+ 46,
+ 31,
+ 35,
+ 52,
+ 31,
+ 52,
+ 40,
+ 36,
+ 27,
+ 32,
+ 31,
+ 28,
+ 37,
+ 37,
+ 35,
+ 50,
+ 45,
+ 33,
+ 35,
+ 39,
+ 35,
+ 33,
+ 34,
+ 34,
+ 38,
+ 45,
+ 33,
+ 46,
+ 33,
+ 35,
+ 32,
+ 36,
+ 41,
+ 34,
+ 31,
+ 45,
+ 38,
+ 34,
+ 37,
+ 35,
+ 35,
+ 34,
+ 45,
+ 36,
+ 27,
+ 30,
+ 35,
+ 35,
+ 35,
+ 45,
+ 33,
+ 36,
+ 39,
+ 35,
+ 27,
+ 41,
+ 37,
+ 33,
+ 32,
+ 45,
+ 35,
+ 39,
+ 36,
+ 50,
+ 31,
+ 32,
+ 35,
+ 34,
+ 27,
+ 31,
+ 34,
+ 46,
+ 32,
+ 32,
+ 50,
+ 32,
+ 32,
+ 43,
+ 33,
+ 31,
+ 31,
+ 30,
+ 33,
+ 27,
+ 32,
+ 37,
+ 29,
+ 37,
+ 34,
+ 38,
+ 39,
+ 27,
+ 32,
+ 35,
+ 31,
+ 36,
+ 33,
+ 30,
+ 50,
+ 36,
+ 27,
+ 33,
+ 32,
+ 47,
+ 44,
+ 35,
+ 34,
+ 35,
+ 45,
+ 30,
+ 38,
+ 27,
+ 29,
+ 37,
+ 46,
+ 38,
+ 31,
+ 45,
+ 36,
+ 36,
+ 47,
+ 41,
+ 36,
+ 52,
+ 34,
+ 40,
+ 35,
+ 27,
+ 37,
+ 37,
+ 32,
+ 38,
+ 39,
+ 45,
+ 34,
+ 37,
+ 28,
+ 38,
+ 40,
+ 27,
+ 29,
+ 38,
+ 27,
+ 42,
+ 26,
+ 34,
+ 33,
+ 46,
+ 39,
+ 36,
+ 50,
+ 32,
+ 28,
+ 28,
+ 33,
+ 34,
+ 33,
+ 45,
+ 33,
+ 39,
+ 31,
+ 31,
+ 40,
+ 31,
+ 34,
+ 44,
+ 31,
+ 27,
+ 31,
+ 37,
+ 30,
+ 45,
+ 34,
+ 35,
+ 31,
+ 45,
+ 40,
+ 30,
+ 31,
+ 34,
+ 33,
+ 34,
+ 40,
+ 35,
+ 39,
+ 50,
+ 37,
+ 45,
+ 32,
+ 35,
+ 34,
+ 32,
+ 31,
+ 32,
+ 38,
+ 39,
+ 39,
+ 28,
+ 41,
+ 30,
+ 45,
+ 32,
+ 39,
+ 41,
+ 39,
+ 38,
+ 32,
+ 37,
+ 45,
+ 41,
+ 49,
+ 35,
+ 45,
+ 45,
+ 35,
+ 36,
+ 45,
+ 30,
+ 46,
+ 51,
+ 42,
+ 35,
+ 34,
+ 33,
+ 41,
+ 32,
+ 30,
+ 32,
+ 40,
+ 42,
+ 31,
+ 44,
+ 27,
+ 32,
+ 40,
+ 31,
+ 32,
+ 50,
+ 41,
+ 41,
+ 39,
+ 38,
+ 33,
+ 46,
+ 35,
+ 28,
+ 28,
+ 33,
+ 33,
+ 33,
+ 34,
+ 39,
+ 32,
+ 33,
+ 34,
+ 35,
+ 39,
+ 46,
+ 32,
+ 40,
+ 31,
+ 32,
+ 37,
+ 40,
+ 33,
+ 32,
+ 31,
+ 33,
+ 50,
+ 34,
+ 31,
+ 35,
+ 33,
+ 27,
+ 37,
+ 36,
+ 35,
+ 52,
+ 34,
+ 37,
+ 34,
+ 45,
+ 52,
+ 34,
+ 32,
+ 32,
+ 44,
+ 31,
+ 44,
+ 28,
+ 39,
+ 45,
+ 39,
+ 36,
+ 32,
+ 36,
+ 25,
+ 35,
+ 35,
+ 33,
+ 38,
+ 27,
+ 30,
+ 36,
+ 34,
+ 33,
+ 39,
+ 32,
+ 50,
+ 36,
+ 35,
+ 27,
+ 32,
+ 28,
+ 35,
+ 44,
+ 45,
+ 50,
+ 32,
+ 39,
+ 35,
+ 36,
+ 36,
+ 39,
+ 36,
+ 46,
+ 46,
+ 28,
+ 46,
+ 41,
+ 31,
+ 32,
+ 33,
+ 32,
+ 33,
+ 34,
+ 41,
+ 33,
+ 44,
+ 45,
+ 38,
+ 27,
+ 34,
+ 31,
+ 50,
+ 35,
+ 36,
+ 34,
+ 32,
+ 28,
+ 37,
+ 50,
+ 46,
+ 34,
+ 30,
+ 33,
+ 33,
+ 50,
+ 33,
+ 33,
+ 36,
+ 28,
+ 33,
+ 33,
+ 27,
+ 39,
+ 32,
+ 37,
+ 32,
+ 32,
+ 38,
+ 34,
+ 31,
+ 35,
+ 33,
+ 46,
+ 50,
+ 41,
+ 38,
+ 37,
+ 51,
+ 50,
+ 35,
+ 35,
+ 33,
+ 37,
+ 36,
+ 34,
+ 39,
+ 39,
+ 36,
+ 35,
+ 32,
+ 38,
+ 39,
+ 33,
+ 32,
+ 51,
+ 34,
+ 50,
+ 28,
+ 34,
+ 47,
+ 50,
+ 38,
+ 41,
+ 29,
+ 27,
+ 29,
+ 32,
+ 46,
+ 38,
+ 51,
+ 33,
+ 27,
+ 46,
+ 33,
+ 35,
+ 35,
+ 33,
+ 39,
+ 31,
+ 31,
+ 46,
+ 35,
+ 33,
+ 37,
+ 31,
+ 36,
+ 39,
+ 50,
+ 52,
+ 45,
+ 40,
+ 36,
+ 35,
+ 50,
+ 36,
+ 40,
+ 34,
+ 35,
+ 31,
+ 41,
+ 50,
+ 32,
+ 30,
+ 35,
+ 41,
+ 31,
+ 34,
+ 42,
+ 30,
+ 34,
+ 38,
+ 31,
+ 46,
+ 43,
+ 29,
+ 36,
+ 51,
+ 33,
+ 50,
+ 36,
+ 32,
+ 45,
+ 41,
+ 52,
+ 36,
+ 46,
+ 30,
+ 50,
+ 32,
+ 33,
+ 36,
+ 46,
+ 28,
+ 39,
+ 32,
+ 35,
+ 39,
+ 50,
+ 32,
+ 32,
+ 36,
+ 33,
+ 27,
+ 45,
+ 31,
+ 44,
+ 35,
+ 40,
+ 51,
+ 35,
+ 31,
+ 36,
+ 50,
+ 41,
+ 36,
+ 28,
+ 46,
+ 27,
+ 33,
+ 31,
+ 33,
+ 39,
+ 41,
+ 36,
+ 41,
+ 50,
+ 31,
+ 39,
+ 34,
+ 26,
+ 27,
+ 35,
+ 31,
+ 38,
+ 33,
+ 39,
+ 32,
+ 50,
+ 34,
+ 33,
+ 50,
+ 50,
+ 34,
+ 33,
+ 39,
+ 35,
+ 32,
+ 35,
+ 39,
+ 33,
+ 31,
+ 36,
+ 45,
+ 40,
+ 31,
+ 33,
+ 35,
+ 35,
+ 37,
+ 45,
+ 33,
+ 34,
+ 39,
+ 36,
+ 33,
+ 45,
+ 34,
+ 31,
+ 30,
+ 40,
+ 31,
+ 45,
+ 39,
+ 50,
+ 35,
+ 31,
+ 27,
+ 34,
+ 51,
+ 50,
+ 35,
+ 31,
+ 33,
+ 40,
+ 34,
+ 42,
+ 31,
+ 39,
+ 37,
+ 36,
+ 31,
+ 31,
+ 31,
+ 32,
+ 34,
+ 31,
+ 30,
+ 36,
+ 32,
+ 34,
+ 35,
+ 34,
+ 36,
+ 34,
+ 29,
+ 33,
+ 34,
+ 32,
+ 32,
+ 46,
+ 39,
+ 34,
+ 33,
+ 50,
+ 35,
+ 33,
+ 27,
+ 34,
+ 39,
+ 32,
+ 26,
+ 31,
+ 39,
+ 45,
+ 28,
+ 51,
+ 39,
+ 32,
+ 33,
+ 36,
+ 36,
+ 36,
+ 31,
+ 51,
+ 35,
+ 51,
+ 28,
+ 36,
+ 37,
+ 33,
+ 34,
+ 37,
+ 31,
+ 33,
+ 40,
+ 33,
+ 33,
+ 39,
+ 46,
+ 45,
+ 39,
+ 45,
+ 32,
+ 30,
+ 26,
+ 35,
+ 45,
+ 31,
+ 37,
+ 31,
+ 31,
+ 32,
+ 33,
+ 26,
+ 29,
+ 44,
+ 33,
+ 35,
+ 33,
+ 34,
+ 34,
+ 34,
+ 27,
+ 37,
+ 50,
+ 33,
+ 35,
+ 28,
+ 33,
+ 34,
+ 31,
+ 32,
+ 34,
+ 39,
+ 42,
+ 37,
+ 45,
+ 35,
+ 33,
+ 37,
+ 35,
+ 33,
+ 31,
+ 49,
+ 33,
+ 33,
+ 32,
+ 33,
+ 34,
+ 42,
+ 30,
+ 51,
+ 32,
+ 46,
+ 36,
+ 41,
+ 34,
+ 45,
+ 29,
+ 37,
+ 31,
+ 30,
+ 36,
+ 32,
+ 33,
+ 32,
+ 45,
+ 32,
+ 45,
+ 46,
+ 47,
+ 40,
+ 31,
+ 31,
+ 45,
+ 33,
+ 31,
+ 50,
+ 36,
+ 33,
+ 36,
+ 37,
+ 31,
+ 50,
+ 35,
+ 31,
+ 32,
+ 35,
+ 37,
+ 43,
+ 34,
+ 50,
+ 42,
+ 36,
+ 45,
+ 50,
+ 37,
+ 32,
+ 29,
+ 32,
+ 34,
+ 34,
+ 31,
+ 34,
+ 32,
+ 36,
+ 31,
+ 32,
+ 45,
+ 45,
+ 35,
+ 32,
+ 30,
+ 38,
+ 32,
+ 36,
+ 34,
+ 51,
+ 39,
+ 34,
+ 50,
+ 34,
+ 50,
+ 31,
+ 34,
+ 32,
+ 50,
+ 36,
+ 33,
+ 32,
+ 50,
+ 30,
+ 34,
+ 35,
+ 52,
+ 44,
+ 39,
+ 31,
+ 33,
+ 31,
+ 28,
+ 34,
+ 34,
+ 27,
+ 35,
+ 32,
+ 31,
+ 31,
+ 45,
+ 37,
+ 33,
+ 35,
+ 32,
+ 45,
+ 38,
+ 51,
+ 34,
+ 34,
+ 31,
+ 33,
+ 50,
+ 36,
+ 34,
+ 39,
+ 35,
+ 32,
+ 50,
+ 33,
+ 30,
+ 31,
+ 34,
+ 28,
+ 36,
+ 32,
+ 33,
+ 31,
+ 50,
+ 32,
+ 36,
+ 45,
+ 34,
+ 50,
+ 32,
+ 33,
+ 32,
+ 38,
+ 35,
+ 51,
+ 34,
+ 33,
+ 44,
+ 36,
+ 32,
+ 36,
+ 31,
+ 33,
+ 34,
+ 39,
+ 39,
+ 32,
+ 41,
+ 39,
+ 50,
+ 34,
+ 32,
+ 33,
+ 51,
+ 33,
+ 28,
+ 32,
+ 32,
+ 50,
+ 34,
+ 37,
+ 32,
+ 32,
+ 33,
+ 39,
+ 36,
+ 36,
+ 33,
+ 32,
+ 50,
+ 32,
+ 50,
+ 34,
+ 31,
+ 33,
+ 32,
+ 39,
+ 40,
+ 34,
+ 45,
+ 32,
+ 33,
+ 50,
+ 50,
+ 35,
+ 32,
+ 39,
+ 31,
+ 36,
+ 41,
+ 41,
+ 36,
+ 32,
+ 31,
+ 32,
+ 33,
+ 52,
+ 50,
+ 35,
+ 30,
+ 34,
+ 52,
+ 32,
+ 39,
+ 41,
+ 45,
+ 36,
+ 31,
+ 32,
+ 25,
+ 50,
+ 34,
+ 51,
+ 31,
+ 36,
+ 45,
+ 34,
+ 45,
+ 33,
+ 33,
+ 34,
+ 34,
+ 32,
+ 36,
+ 32,
+ 33,
+ 31,
+ 50,
+ 31,
+ 42,
+ 37,
+ 37,
+ 30,
+ 30,
+ 32,
+ 50,
+ 49,
+ 35,
+ 31,
+ 32,
+ 51,
+ 32,
+ 29,
+ 33,
+ 34,
+ 32,
+ 34,
+ 34,
+ 32,
+ 32,
+ 41,
+ 36,
+ 36,
+ 45,
+ 31,
+ 36,
+ 30,
+ 37,
+ 34,
+ 35,
+ 37,
+ 31,
+ 36,
+ 33,
+ 35,
+ 36,
+ 38,
+ 45,
+ 34,
+ 42,
+ 45,
+ 45,
+ 31,
+ 27,
+ 32,
+ 33,
+ 32,
+ 33,
+ 50,
+ 37,
+ 32,
+ 39,
+ 51,
+ 31,
+ 34,
+ 39,
+ 32,
+ 27,
+ 35,
+ 35,
+ 37,
+ 33,
+ 50,
+ 31,
+ 42,
+ 33,
+ 36,
+ 32,
+ 33,
+ 27,
+ 31,
+ 51,
+ 33,
+ 34,
+ 39,
+ 34,
+ 32,
+ 32,
+ 45,
+ 39,
+ 33,
+ 31,
+ 50,
+ 32,
+ 27,
+ 47,
+ 31,
+ 27,
+ 39,
+ 51,
+ 34,
+ 35,
+ 34,
+ 31,
+ 37,
+ 38,
+ 31,
+ 30,
+ 32,
+ 39,
+ 31,
+ 50,
+ 34,
+ 35,
+ 31,
+ 33,
+ 32,
+ 32,
+ 32,
+ 39,
+ 32,
+ 36,
+ 31,
+ 51,
+ 39,
+ 32,
+ 31,
+ 33,
+ 32,
+ 27,
+ 33,
+ 50,
+ 31,
+ 33,
+ 51,
+ 50,
+ 27,
+ 31,
+ 51,
+ 36,
+ 51,
+ 32,
+ 34,
+ 39,
+ 39,
+ 31,
+ 33,
+ 38,
+ 36,
+ 34,
+ 27,
+ 50,
+ 32,
+ 39,
+ 33,
+ 51,
+ 34,
+ 36,
+ 50,
+ 35,
+ 50,
+ 36,
+ 33,
+ 36,
+ 33,
+ 36,
+ 36,
+ 51,
+ 46,
+ 33,
+ 32,
+ 37,
+ 34,
+ 31,
+ 34,
+ 27,
+ 31,
+ 31,
+ 33,
+ 45,
+ 41,
+ 34,
+ 45,
+ 33,
+ 31,
+ 34,
+ 33,
+ 30,
+ 32,
+ 33,
+ 35,
+ 51,
+ 34,
+ 37,
+ 35,
+ 45,
+ 32,
+ 46,
+ 33,
+ 42,
+ 35,
+ 31,
+ 33,
+ 50,
+ 37,
+ 28,
+ 27,
+ 39,
+ 32,
+ 36,
+ 27,
+ 50,
+ 36,
+ 32,
+ 27,
+ 39,
+ 32,
+ 27,
+ 36,
+ 35,
+ 39,
+ 34,
+ 34,
+ 35,
+ 32,
+ 32,
+ 41,
+ 39,
+ 28,
+ 36,
+ 36,
+ 26,
+ 32,
+ 33,
+ 51,
+ 39,
+ 52,
+ 51,
+ 34,
+ 50,
+ 36,
+ 33,
+ 34,
+ 33,
+ 31,
+ 32,
+ 31,
+ 36,
+ 32,
+ 34,
+ 32,
+ 45,
+ 32,
+ 35,
+ 26,
+ 37,
+ 35,
+ 34,
+ 32,
+ 50,
+ 33,
+ 31,
+ 32,
+ 31,
+ 31,
+ 32,
+ 34,
+ 31,
+ 27,
+ 50,
+ 31,
+ 32,
+ 33,
+ 50,
+ 45,
+ 34,
+ 37,
+ 32,
+ 37,
+ 37,
+ 45,
+ 39,
+ 36,
+ 45,
+ 50,
+ 32,
+ 50,
+ 34,
+ 30,
+ 31,
+ 34,
+ 32,
+ 34,
+ 51,
+ 39,
+ 52,
+ 39,
+ 32,
+ 52,
+ 32,
+ 28,
+ 28,
+ 28,
+ 31,
+ 37,
+ 50,
+ 27,
+ 27,
+ 33,
+ 32,
+ 27,
+ 31,
+ 50,
+ 36,
+ 28,
+ 32,
+ 39,
+ 31,
+ 45,
+ 27,
+ 36,
+ 32,
+ 50,
+ 34,
+ 36,
+ 33,
+ 31,
+ 33,
+ 33,
+ 50,
+ 38,
+ 33,
+ 50,
+ 32,
+ 31,
+ 34,
+ 32,
+ 39,
+ 33,
+ 32,
+ 37,
+ 30,
+ 50,
+ 35,
+ 33,
+ 35,
+ 35,
+ 46,
+ 26,
+ 41,
+ 50,
+ 34,
+ 38,
+ 45,
+ 28,
+ 50,
+ 28,
+ 34,
+ 50,
+ 34,
+ 51,
+ 36,
+ 35,
+ 52,
+ 36,
+ 33,
+ 39,
+ 32,
+ 50,
+ 36,
+ 31,
+ 52,
+ 35,
+ 41,
+ 28,
+ 46,
+ 35,
+ 38,
+ 44,
+ 34,
+ 33,
+ 50,
+ 35,
+ 31,
+ 32,
+ 30,
+ 32,
+ 31,
+ 34,
+ 34,
+ 34,
+ 31,
+ 51,
+ 28,
+ 31,
+ 39,
+ 39,
+ 35,
+ 39,
+ 32,
+ 32,
+ 30,
+ 34,
+ 36,
+ 35,
+ 32,
+ 33,
+ 50,
+ 49,
+ 50,
+ 35,
+ 32,
+ 28,
+ 32,
+ 33,
+ 51,
+ 46,
+ 32,
+ 27,
+ 39,
+ 41,
+ 31,
+ 37,
+ 51,
+ 27,
+ 44,
+ 34,
+ 33,
+ 34,
+ 32,
+ 44,
+ 31,
+ 33,
+ 32,
+ 34,
+ 31,
+ 50,
+ 34,
+ 39,
+ 50,
+ 50,
+ 45,
+ 31,
+ 46,
+ 31,
+ 52,
+ 50,
+ 35,
+ 31,
+ 31,
+ 37,
+ 37,
+ 38,
+ 37,
+ 50,
+ 37,
+ 33,
+ 32,
+ 31,
+ 33,
+ 34,
+ 34,
+ 31,
+ 35,
+ 34,
+ 34,
+ 31,
+ 34,
+ 34,
+ 52,
+ 31,
+ 36,
+ 32,
+ 36,
+ 45,
+ 33,
+ 45,
+ 31,
+ 36,
+ 32,
+ 32,
+ 33,
+ 39,
+ 51,
+ 32,
+ 30,
+ 34,
+ 52,
+ 45,
+ 34,
+ 34,
+ 37,
+ 31,
+ 34,
+ 32,
+ 50,
+ 50,
+ 35,
+ 30,
+ 31,
+ 36,
+ 39,
+ 50,
+ 33,
+ 32,
+ 34,
+ 33,
+ 34,
+ 50,
+ 33,
+ 27,
+ 34,
+ 51,
+ 37,
+ 52,
+ 45,
+ 33,
+ 32,
+ 51,
+ 34,
+ 50,
+ 31,
+ 32,
+ 51,
+ 33,
+ 34,
+ 36,
+ 50,
+ 35,
+ 52,
+ 37,
+ 33,
+ 36,
+ 31,
+ 51,
+ 33,
+ 27,
+ 28,
+ 50,
+ 52,
+ 33,
+ 31,
+ 31,
+ 33,
+ 37,
+ 49,
+ 32,
+ 32,
+ 36,
+ 39,
+ 45,
+ 32,
+ 32,
+ 32,
+ 32,
+ 31,
+ 34,
+ 34,
+ 32,
+ 28,
+ 27,
+ 50,
+ 34,
+ 45,
+ 30,
+ 34,
+ 33,
+ 36,
+ 33,
+ 52,
+ 37,
+ 31,
+ 33,
+ 30,
+ 39,
+ 36,
+ 31,
+ 41,
+ 36,
+ 42,
+ 35,
+ 37,
+ 37,
+ 32,
+ 36,
+ 32,
+ 31,
+ 31,
+ 31,
+ 36,
+ 35,
+ 34,
+ 50,
+ 33,
+ 33,
+ 31,
+ 40,
+ 36,
+ 36,
+ 27,
+ 37,
+ 37,
+ 30,
+ 33,
+ 32,
+ 31,
+ 35,
+ 27,
+ 32,
+ 51,
+ 34,
+ 32,
+ 32,
+ 30,
+ 52,
+ 33,
+ 32,
+ 32,
+ 33,
+ 33,
+ 33,
+ 27,
+ 32,
+ 36,
+ 28,
+ 45,
+ 33,
+ 31,
+ 34,
+ 33,
+ 33,
+ 33,
+ 52,
+ 31,
+ 51,
+ 33,
+ 52,
+ 33,
+ 35,
+ 41,
+ 26,
+ 32,
+ 36,
+ 50,
+ 36,
+ 31,
+ 34,
+ 34,
+ 32,
+ 33,
+ 37,
+ 35,
+ 52,
+ 39,
+ 31,
+ 34,
+ 36,
+ 36,
+ 31,
+ 32,
+ 32,
+ 42,
+ 27,
+ 50,
+ 34,
+ 33,
+ 32,
+ 34,
+ 32,
+ 32,
+ 37,
+ 31,
+ 32,
+ 51,
+ 50,
+ 33,
+ 51,
+ 30,
+ 31,
+ 34,
+ 33,
+ 34,
+ 31,
+ 30,
+ 32,
+ 32,
+ 31,
+ 36,
+ 34,
+ 37,
+ 33,
+ 45,
+ 31,
+ 32,
+ 39,
+ 36,
+ 36,
+ 32,
+ 31,
+ 33,
+ 32,
+ 32,
+ 50,
+ 32,
+ 31,
+ 33,
+ 51,
+ 34,
+ 50,
+ 33,
+ 32,
+ 32,
+ 32,
+ 31,
+ 45,
+ 39,
+ 32,
+ 32,
+ 52,
+ 52,
+ 45,
+ 50,
+ 34,
+ 35,
+ 32,
+ 52,
+ 39,
+ 32,
+ 31,
+ 33,
+ 36,
+ 36,
+ 32,
+ 52,
+ 34,
+ 36,
+ 32,
+ 32,
+ 34,
+ 50,
+ 33,
+ 31,
+ 26,
+ 30,
+ 36,
+ 32,
+ 39,
+ 32,
+ 33,
+ 31,
+ 31,
+ 36,
+ 51,
+ 32,
+ 33,
+ 51,
+ 41,
+ 32,
+ 52,
+ 33,
+ 51,
+ 45,
+ 32,
+ 52,
+ 51,
+ 31,
+ 50,
+ 32,
+ 52,
+ 39,
+ 31,
+ 34,
+ 30,
+ 32,
+ 31,
+ 33,
+ 36,
+ 36,
+ 32,
+ 51,
+ 32,
+ 32,
+ 34,
+ 33,
+ 52,
+ 36,
+ 50,
+ 34,
+ 36,
+ 34,
+ 34,
+ 31,
+ 32,
+ 33,
+ 31,
+ 33,
+ 32,
+ 46,
+ 32,
+ 36,
+ 36,
+ 32,
+ 30,
+ 36,
+ 33,
+ 34,
+ 34,
+ 32,
+ 52,
+ 37,
+ 32,
+ 50,
+ 35,
+ 32,
+ 50,
+ 35,
+ 31,
+ 32,
+ 51,
+ 50,
+ 31,
+ 52,
+ 31,
+ 52,
+ 32,
+ 50,
+ 30,
+ 31,
+ 27,
+ 32,
+ 32,
+ 39,
+ 31,
+ 34,
+ 31,
+ 32,
+ 32,
+ 36,
+ 31,
+ 32,
+ 33,
+ 32,
+ 50,
+ 33,
+ 50,
+ 32,
+ 33,
+ 31,
+ 31,
+ 32,
+ 35,
+ 33,
+ 32,
+ 39,
+ 39,
+ 34,
+ 50,
+ 32,
+ 34,
+ 30,
+ 31,
+ 34,
+ 32,
+ 32,
+ 34,
+ 34,
+ 35,
+ 36,
+ 33,
+ 34,
+ 34,
+ 36,
+ 26,
+ 36,
+ 33,
+ 36,
+ 30,
+ 39,
+ 28,
+ 35,
+ 51,
+ 51,
+ 52,
+ 32,
+ 36,
+ 27,
+ 36,
+ 51,
+ 32,
+ 45,
+ 31,
+ 50,
+ 32,
+ 39,
+ 51,
+ 32,
+ 32,
+ 33,
+ 34,
+ 31,
+ 31,
+ 51,
+ 51,
+ 31,
+ 39,
+ 34,
+ 52,
+ 32,
+ 52,
+ 32,
+ 32,
+ 33,
+ 32,
+ 36,
+ 32,
+ 36,
+ 31,
+ 32,
+ 34,
+ 50,
+ 39,
+ 33,
+ 30,
+ 31,
+ 31,
+ 35,
+ 34,
+ 27,
+ 31,
+ 36,
+ 32,
+ 34,
+ 39,
+ 33,
+ 51,
+ 36,
+ 31,
+ 35,
+ 37,
+ 36,
+ 33,
+ 32,
+ 33,
+ 31,
+ 36,
+ 34,
+ 36,
+ 50,
+ 32,
+ 32,
+ 32,
+ 35,
+ 32,
+ 33,
+ 26,
+ 50,
+ 33,
+ 51,
+ 26,
+ 33,
+ 51,
+ 36,
+ 32,
+ 34,
+ 39,
+ 52,
+ 36,
+ 34,
+ 26,
+ 32,
+ 39,
+ 39,
+ 27,
+ 32,
+ 51,
+ 35,
+ 32,
+ 35,
+ 32,
+ 33,
+ 34,
+ 52,
+ 33,
+ 37,
+ 33,
+ 34,
+ 41,
+ 52,
+ 33,
+ 33,
+ 36,
+ 52,
+ 32,
+ 31,
+ 35,
+ 31,
+ 32,
+ 33,
+ 39,
+ 34,
+ 32,
+ 37,
+ 31,
+ 33,
+ 39,
+ 31,
+ 33,
+ 32,
+ 37,
+ 52,
+ 32,
+ 52,
+ 27,
+ 36,
+ 36,
+ 32,
+ 32,
+ 52,
+ 37,
+ 39,
+ 52,
+ 37,
+ 32,
+ 31,
+ 31,
+ 35,
+ 32,
+ 36,
+ 32,
+ 35,
+ 33,
+ 36,
+ 32,
+ 52,
+ 34,
+ 32,
+ 51,
+ 51,
+ 32,
+ 32,
+ 51,
+ 32,
+ 51,
+ 52,
+ 31,
+ 32,
+ 32,
+ 36,
+ 51,
+ 33,
+ 26,
+ 35,
+ 51,
+ 52,
+ 32,
+ 31,
+ 31,
+ 36,
+ 32,
+ 32,
+ 34,
+ 32,
+ 52,
+ 31,
+ 32,
+ 35,
+ 32,
+ 35,
+ 31,
+ 52,
+ 35,
+ 33,
+ 33,
+ 36,
+ 32,
+ 32,
+ 32,
+ 33,
+ 32,
+ 32,
+ 34,
+ 51,
+ 34,
+ 36,
+ 32,
+ 33,
+ 52,
+ 32,
+ 32,
+ 32,
+ 33,
+ 32,
+ 33,
+ 31,
+ 32,
+ 33,
+ 32,
+ 36,
+ 36,
+ 39,
+ 36,
+ 36,
+ 33,
+ 31,
+ 30,
+ 28,
+ 52,
+ 32,
+ 36,
+ 37,
+ 32,
+ 33,
+ 51,
+ 32,
+ 32,
+ 31,
+ 32,
+ 36,
+ 32,
+ 37,
+ 27,
+ 31,
+ 32,
+ 31,
+ 51,
+ 28,
+ 28,
+ 31,
+ 36,
+ 33,
+ 51,
+ 34,
+ 35,
+ 31,
+ 32,
+ 52,
+ 32,
+ 28,
+ 32,
+ 36,
+ 32,
+ 32,
+ 36,
+ 37,
+ 32,
+ 36,
+ 33,
+ 51,
+ 36,
+ 36,
+ 36,
+ 31,
+ 51,
+ 32,
+ 32,
+ 32,
+ 51,
+ 32,
+ 36,
+ 32,
+ 32,
+ 32,
+ 32,
+ 32,
+ 36,
+ 52,
+ 33,
+ 27,
+ 33,
+ 34,
+ 39,
+ 51,
+ 32,
+ 33,
+ 33,
+ 27,
+ 34,
+ 36,
+ 28,
+ 32,
+ 32,
+ 32,
+ 45,
+ 51,
+ 26,
+ 33,
+ 30,
+ 32,
+ 51,
+ 36,
+ 30,
+ 33,
+ 36,
+ 32,
+ 32,
+ 32,
+ 32,
+ 37,
+ 36,
+ 34,
+ 36,
+ 36,
+ 33,
+ 32,
+ 33,
+ 32,
+ 32,
+ 51,
+ 28,
+ 32,
+ 32,
+ 33,
+ 36,
+ 31,
+ 30,
+ 32,
+ 36,
+ 32,
+ 51,
+ 51,
+ 36,
+ 27,
+ 36,
+ 32,
+ 31,
+ 32,
+ 32,
+ 27,
+ 36,
+ 32,
+ 32,
+ 32,
+ 33,
+ 27,
+ 34,
+ 36,
+ 32,
+ 52,
+ 32,
+ 51,
+ 34,
+ 31,
+ 32,
+ 32,
+ 36,
+ 32,
+ 32,
+ 33,
+ 33,
+ 32,
+ 33,
+ 41,
+ 52,
+ 32,
+ 27,
+ 34,
+ 30,
+ 34,
+ 41,
+ 32,
+ 34,
+ 36,
+ 36,
+ 36,
+ 32,
+ 32,
+ 31,
+ 36,
+ 36,
+ 32,
+ 33,
+ 31,
+ 33,
+ 32,
+ 32,
+ 51,
+ 31,
+ 32,
+ 31,
+ 32,
+ 32,
+ 31,
+ 31,
+ 34,
+ 36,
+ 52,
+ 31,
+ 33,
+ 39,
+ 30,
+ 34,
+ 31,
+ 32,
+ 32,
+ 32,
+ 33,
+ 32,
+ 33,
+ 31,
+ 31,
+ 39,
+ 30,
+ 32,
+ 35,
+ 36,
+ 32,
+ 26,
+ 32,
+ 30,
+ 31,
+ 39,
+ 32,
+ 39,
+ 31,
+ 36,
+ 32,
+ 39,
+ 32,
+ 32,
+ 51,
+ 32,
+ 32,
+ 32,
+ 33,
+ 36,
+ 36,
+ 32,
+ 35,
+ 33,
+ 51,
+ 37,
+ 31,
+ 51,
+ 33,
+ 52,
+ 32,
+ 36,
+ 31,
+ 52,
+ 32,
+ 36,
+ 32,
+ 51,
+ 37,
+ 32,
+ 36,
+ 32,
+ 32,
+ 26,
+ 32,
+ 30,
+ 36,
+ 27,
+ 32,
+ 30,
+ 51,
+ 39,
+ 30,
+ 32,
+ 36,
+ 32,
+ 33,
+ 32,
+ 33,
+ 36,
+ 32,
+ 36,
+ 33,
+ 36,
+ 36,
+ 36,
+ 32,
+ 51,
+ 51,
+ 33,
+ 33,
+ 51,
+ 32,
+ 31,
+ 32,
+ 36,
+ 36,
+ 33,
+ 34,
+ 51,
+ 32,
+ 30,
+ 32,
+ 33,
+ 31,
+ 31,
+ 32,
+ 33,
+ 33,
+ 36,
+ 32,
+ 32,
+ 51,
+ 28,
+ 32,
+ 36,
+ 39,
+ 52,
+ 32,
+ 36,
+ 32,
+ 39,
+ 32,
+ 32,
+ 33,
+ 32,
+ 32,
+ 51,
+ 51,
+ 33,
+ 37,
+ 32,
+ 37,
+ 34,
+ 36,
+ 32,
+ 36,
+ 32,
+ 32,
+ 34,
+ 31,
+ 32,
+ 32,
+ 32,
+ 35,
+ 36,
+ 32,
+ 37,
+ 37,
+ 51,
+ 32,
+ 32,
+ 32,
+ 32,
+ 26,
+ 31,
+ 31,
+ 31,
+ 34,
+ 32,
+ 33,
+ 32,
+ 37,
+ 32,
+ 32,
+ 36,
+ 36,
+ 51,
+ 32,
+ 36,
+ 37,
+ 39,
+ 33,
+ 32,
+ 36,
+ 32,
+ 26,
+ 32,
+ 32,
+ 32,
+ 36,
+ 32,
+ 32,
+ 34,
+ 39,
+ 34,
+ 39,
+ 32,
+ 27,
+ 35,
+ 30,
+ 32,
+ 32,
+ 39,
+ 32,
+ 32,
+ 37,
+ 32,
+ 32,
+ 31,
+ 32,
+ 36,
+ 31,
+ 37,
+ 32,
+ 32,
+ 31,
+ 34,
+ 34,
+ 30,
+ 32,
+ 32,
+ 36,
+ 34,
+ 27,
+ 32,
+ 39,
+ 32,
+ 32,
+ 35,
+ 37,
+ 51,
+ 36,
+ 32,
+ 32,
+ 37,
+ 36,
+ 32,
+ 32,
+ 36,
+ 31,
+ 36,
+ 32,
+ 36,
+ 39,
+ 30,
+ 27,
+ 36,
+ 32,
+ 33,
+ 51,
+ 33,
+ 32,
+ 32,
+ 32,
+ 33,
+ 35,
+ 32,
+ 32,
+ 32,
+ 36,
+ 32,
+ 37,
+ 32,
+ 33,
+ 32,
+ 27,
+ 36,
+ 32,
+ 35,
+ 32,
+ 36,
+ 32,
+ 34,
+ 32,
+ 37,
+ 32,
+ 36,
+ 32,
+ 36,
+ 32,
+ 36,
+ 32,
+ 32,
+ 36,
+ 33,
+ 32,
+ 35,
+ 32,
+ 32,
+ 30,
+ 33,
+ 32,
+ 28,
+ 32,
+ 34,
+ 33,
+ 51,
+ 35,
+ 30,
+ 32,
+ 36,
+ 32,
+ 32,
+ 33,
+ 41,
+ 36,
+ 32,
+ 32,
+ 27,
+ 33,
+ 37,
+ 32,
+ 33,
+ 36,
+ 27,
+ 51,
+ 26,
+ 36,
+ 34,
+ 32,
+ 33,
+ 39,
+ 32,
+ 33,
+ 34,
+ 37,
+ 34,
+ 36,
+ 28,
+ 32,
+ 32,
+ 28,
+ 32,
+ 32,
+ 32,
+ 36,
+ 27,
+ 39,
+ 51,
+ 32,
+ 36,
+ 32,
+ 33,
+ 31,
+ 31,
+ 31,
+ 36,
+ 31,
+ 32,
+ 31,
+ 36,
+ 32,
+ 32,
+ 30,
+ 39,
+ 31,
+ 32,
+ 31,
+ 32,
+ 32,
+ 27,
+ 36,
+ 32,
+ 39,
+ 32,
+ 32,
+ 31,
+ 32,
+ 32,
+ 36,
+ 27,
+ 32,
+ 33,
+ 26,
+ 35,
+ 33,
+ 35,
+ 32,
+ 37,
+ 32,
+ 31,
+ 32,
+ 32,
+ 32,
+ 31,
+ 32,
+ 36,
+ 31,
+ 30,
+ 32,
+ 32,
+ 32,
+ 31,
+ 36,
+ 32,
+ 36,
+ 32,
+ 32,
+ 27,
+ 35,
+ 31,
+ 32,
+ 32,
+ 32,
+ 32,
+ 33,
+ 36,
+ 32,
+ 31,
+ 32,
+ 33,
+ 32,
+ 35,
+ 36,
+ 39,
+ 32,
+ 32,
+ 39,
+ 37,
+ 32,
+ 30,
+ 31,
+ 31,
+ 36,
+ 32,
+ 35,
+ 36,
+ 36,
+ 32,
+ 32,
+ 27,
+ 32,
+ 32,
+ 36,
+ 32,
+ 33,
+ 26,
+ 32,
+ 27,
+ 32,
+ 32,
+ 39,
+ 32,
+ 32,
+ 33,
+ 32,
+ 32,
+ 36,
+ 32,
+ 50,
+ 32,
+ 36,
+ 27,
+ 37,
+ 36,
+ 32,
+ 35,
+ 32,
+ 33,
+ 35,
+ 32,
+ 36,
+ 32,
+ 27,
+ 35,
+ 32,
+ 32,
+ 36,
+ 32,
+ 32,
+ 36,
+ 30,
+ 39,
+ 27,
+ 27,
+ 36,
+ 32,
+ 32,
+ 35,
+ 32,
+ 33,
+ 32,
+ 39,
+ 32,
+ 36,
+ 32,
+ 36,
+ 32,
+ 32,
+ 35,
+ 36,
+ 35,
+ 35,
+ 36,
+ 35,
+ 32,
+ 32,
+ 32,
+ 32,
+ 35,
+ 35,
+ 36,
+ 35,
+ 32,
+ 32,
+ 32,
+ 35,
+ 35,
+ 35,
+ 35
+ ],
+ "mode": "markers",
+ "marker": {
+ "color": "#19d3f3"
+ },
+ "latsrc": "jackp:17903:e3ab9a",
+ "type": "scattergeo",
+ "lonsrc": "jackp:17903:767d41"
+ }
+ ],
+ "layout": {
+ "yaxis": {
+ "zeroline": false,
+ "gridcolor": "#dfe8f3",
+ "title": " "
+ },
+ "margin": {
+ "l": 40,
+ "t": 10
+ },
+ "title": " ",
+ "barmode": "stack",
+ "xaxis": {
+ "zeroline": false,
+ "title": "Lat",
+ "showgrid": false
+ },
+ "font": {
+ "color": "#506784",
+ "size": "12px"
+ },
+ "geo": {
+ "showland": true,
+ "lonaxis": {
+ "range": [
+ -180,
+ -50
+ ]
+ },
+ "projection": {
+ "type": "albers usa"
+ },
+ "lataxis": {
+ "tick0": 15,
+ "range": [
+ 15,
+ 80
+ ]
+ },
+ "showlakes": true,
+ "showsubunits": true,
+ "subunitwidth": 1,
+ "landcolor": "rgb(212,212,212)",
+ "countrycolor": "rgb(255,255,255)",
+ "subunitcolor": "rgb(255,255,255)",
+ "showcountries": true,
+ "lakecolor": "rgb(255,255,255)",
+ "scope": "usa",
+ "resolution": 50
+ }
+ }
+}
diff --git a/test/image/mocks/geo_second.json b/test/image/mocks/geo_second.json
index 0a9c07f5c46..6f6c0937279 100644
--- a/test/image/mocks/geo_second.json
+++ b/test/image/mocks/geo_second.json
@@ -83,6 +83,9 @@
"projection": {
"type": "conic equal area"
},
+ "center": {
+ "lat": 35
+ },
"showframe": false,
"showrivers": true,
"showlakes": true,
diff --git a/test/image/mocks/geo_text_chart_arrays.json b/test/image/mocks/geo_text_chart_arrays.json
index 690f2e95c26..deceaa3d024 100644
--- a/test/image/mocks/geo_text_chart_arrays.json
+++ b/test/image/mocks/geo_text_chart_arrays.json
@@ -131,6 +131,9 @@
40,
70
]
+ },
+ "center": {
+ "lat": 57
}
},
"width": 800,
diff --git a/test/jasmine/tests/fx_test.js b/test/jasmine/tests/fx_test.js
index 3844045d65c..bb0a392c631 100644
--- a/test/jasmine/tests/fx_test.js
+++ b/test/jasmine/tests/fx_test.js
@@ -76,7 +76,7 @@ describe('Fx defaults', function() {
.layout;
expect(layoutOut.hovermode).toBe('closest', 'hovermode to closest');
- expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom');
+ expect(layoutOut.dragmode).toBe('pan', 'dragmode to zoom');
});
it('should default (multi plot type version)', function() {
diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js
index effb8ebc64e..ab082395d20 100644
--- a/test/jasmine/tests/geo_test.js
+++ b/test/jasmine/tests/geo_test.js
@@ -3,14 +3,14 @@ var Lib = require('@src/lib');
var Geo = require('@src/plots/geo');
var GeoAssets = require('@src/assets/geo_assets');
-var params = require('@src/plots/geo/constants');
-var supplyLayoutDefaults = require('@src/plots/geo/layout/axis_defaults');
+var constants = require('@src/plots/geo/constants');
var geoLocationUtils = require('@src/lib/geo_location_utils');
var topojsonUtils = require('@src/lib/topojson_utils');
var d3 = require('d3');
var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
+var fail = require('../assets/fail_test');
var getClientPosition = require('../assets/get_client_position');
var mouseEvent = require('../assets/mouse_event');
var click = require('../assets/click');
@@ -29,333 +29,436 @@ function move(fromX, fromY, toX, toY, delay) {
});
}
+describe('Test Geo layout defaults', function() {
+ var layoutAttributes = Geo.layoutAttributes;
+ var supplyLayoutDefaults = Geo.supplyLayoutDefaults;
-describe('Test geoaxes', function() {
- 'use strict';
-
- describe('supplyLayoutDefaults', function() {
- var geoLayoutIn,
- geoLayoutOut;
-
- beforeEach(function() {
- geoLayoutOut = {};
- });
-
- it('should default to lon(lat)range to params non-world scopes', function() {
- var scopeDefaults = params.scopeDefaults,
- scopes = Object.keys(scopeDefaults),
- customLonaxisRange = [-42.21313312, 40.321321],
- customLataxisRange = [-42.21313312, 40.321321];
-
- var dfltLonaxisRange, dfltLataxisRange;
-
- scopes.forEach(function(scope) {
- if(scope === 'world') return;
-
- dfltLonaxisRange = scopeDefaults[scope].lonaxisRange;
- dfltLataxisRange = scopeDefaults[scope].lataxisRange;
-
- geoLayoutIn = {};
- geoLayoutOut = {scope: scope};
+ var layoutIn, layoutOut, fullData;
- supplyLayoutDefaults(geoLayoutIn, geoLayoutOut);
- expect(geoLayoutOut.lonaxis.range).toEqual(dfltLonaxisRange);
- expect(geoLayoutOut.lataxis.range).toEqual(dfltLataxisRange);
- expect(geoLayoutOut.lonaxis.tick0).toEqual(dfltLonaxisRange[0]);
- expect(geoLayoutOut.lataxis.tick0).toEqual(dfltLataxisRange[0]);
+ beforeEach(function() {
+ layoutOut = {};
- geoLayoutIn = {
- lonaxis: {range: customLonaxisRange},
- lataxis: {range: customLataxisRange}
- };
- geoLayoutOut = {scope: scope};
+ // needs a geo-ref in a trace in order to be detected
+ fullData = [{ type: 'scattergeo', geo: 'geo' }];
+ });
- supplyLayoutDefaults(geoLayoutIn, geoLayoutOut);
- expect(geoLayoutOut.lonaxis.range).toEqual(customLonaxisRange);
- expect(geoLayoutOut.lataxis.range).toEqual(customLataxisRange);
- expect(geoLayoutOut.lonaxis.tick0).toEqual(customLonaxisRange[0]);
- expect(geoLayoutOut.lataxis.tick0).toEqual(customLataxisRange[0]);
- });
- });
+ var seaFields = [
+ 'showcoastlines', 'coastlinecolor', 'coastlinewidth',
+ 'showocean', 'oceancolor'
+ ];
- it('should adjust default lon(lat)range to projection.rotation in world scopes', function() {
- var expectedLonaxisRange, expectedLataxisRange;
+ var subunitFields = [
+ 'showsubunits', 'subunitcolor', 'subunitwidth'
+ ];
- function testOne() {
- supplyLayoutDefaults(geoLayoutIn, geoLayoutOut);
- expect(geoLayoutOut.lonaxis.range).toEqual(expectedLonaxisRange);
- expect(geoLayoutOut.lataxis.range).toEqual(expectedLataxisRange);
- }
+ var frameFields = [
+ 'showframe', 'framecolor', 'framewidth'
+ ];
- geoLayoutIn = {};
- geoLayoutOut = {
- scope: 'world',
+ it('should not coerce projection.rotation if type is albers usa', function() {
+ layoutIn = {
+ geo: {
projection: {
- type: 'equirectangular',
+ type: 'albers usa',
rotation: {
- lon: -75,
- lat: 45
+ lon: 10,
+ lat: 10
}
}
- };
- expectedLonaxisRange = [-255, 105]; // => -75 +/- 180
- expectedLataxisRange = [-45, 135]; // => 45 +/- 90
- testOne();
+ }
+ };
- geoLayoutIn = {};
- geoLayoutOut = {
- scope: 'world',
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ expect(layoutOut.geo.projection.rotation).toBeUndefined();
+ expect(layoutOut.geo.scope).toEqual('usa');
+ });
+
+ it('should not coerce projection.rotation if type is albers usa (converse)', function() {
+ layoutIn = {
+ geo: {
projection: {
- type: 'orthographic',
rotation: {
- lon: -75,
- lat: 45
+ lon: 10,
+ lat: 10
}
}
- };
- expectedLonaxisRange = [-165, 15]; // => -75 +/- 90
- expectedLataxisRange = [-45, 135]; // => 45 +/- 90
- testOne();
+ }
+ };
- geoLayoutIn = {
- lonaxis: {range: [-42.21313312, 40.321321]},
- lataxis: {range: [-42.21313312, 40.321321]}
- };
- expectedLonaxisRange = [-42.21313312, 40.321321];
- expectedLataxisRange = [-42.21313312, 40.321321];
- testOne();
- });
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ expect(layoutOut.geo.projection.rotation).toBeDefined();
+ expect(layoutOut.geo.scope).toEqual('world');
});
-});
-describe('Test Geo layout defaults', function() {
- 'use strict';
-
- var layoutAttributes = Geo.layoutAttributes;
- var supplyLayoutDefaults = Geo.supplyLayoutDefaults;
-
- describe('supplyLayoutDefaults', function() {
- var layoutIn, layoutOut, fullData;
-
- beforeEach(function() {
- layoutOut = {};
+ it('should not coerce coastlines and ocean if type is albers usa', function() {
+ layoutIn = {
+ geo: {
+ projection: {
+ type: 'albers usa'
+ },
+ showcoastlines: true,
+ showocean: true
+ }
+ };
- // needs a geo-ref in a trace in order to be detected
- fullData = [{ type: 'scattergeo', geo: 'geo' }];
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ seaFields.forEach(function(field) {
+ expect(layoutOut.geo[field]).toBeUndefined();
});
+ });
- var seaFields = [
- 'showcoastlines', 'coastlinecolor', 'coastlinewidth',
- 'showocean', 'oceancolor'
- ];
+ it('should not coerce coastlines and ocean if type is albers usa (converse)', function() {
+ layoutIn = {
+ geo: {
+ showcoastlines: true,
+ showocean: true
+ }
+ };
- var subunitFields = [
- 'showsubunits', 'subunitcolor', 'subunitwidth'
- ];
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ seaFields.forEach(function(field) {
+ expect(layoutOut.geo[field]).toBeDefined();
+ });
+ });
- var frameFields = [
- 'showframe', 'framecolor', 'framewidth'
- ];
+ it('should not coerce projection.parallels if type is conic', function() {
+ var projTypes = layoutAttributes.projection.type.values;
- it('should not coerce projection.rotation if type is albers usa', function() {
+ function testOne(projType) {
layoutIn = {
geo: {
projection: {
- type: 'albers usa',
- rotation: {
- lon: 10,
- lat: 10
- }
+ type: projType,
+ parallels: [10, 10]
}
}
};
supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- expect(layoutOut.geo.projection.rotation).toBeUndefined();
+ }
+
+ projTypes.forEach(function(projType) {
+ testOne(projType);
+ if(projType.indexOf('conic') !== -1) {
+ expect(layoutOut.geo.projection.parallels).toBeDefined();
+ } else {
+ expect(layoutOut.geo.projection.parallels).toBeUndefined();
+ }
});
+ });
- it('should not coerce projection.rotation if type is albers usa (converse)', function() {
- layoutIn = {
- geo: {
- projection: {
- rotation: {
- lon: 10,
- lat: 10
- }
- }
- }
- };
+ it('should coerce subunits only when available (usa case)', function() {
+ layoutIn = {
+ geo: { scope: 'usa' }
+ };
- supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- expect(layoutOut.geo.projection.rotation).toBeDefined();
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ subunitFields.forEach(function(field) {
+ expect(layoutOut.geo[field]).toBeDefined();
});
+ });
- it('should not coerce coastlines and ocean if type is albers usa', function() {
- layoutIn = {
- geo: {
- projection: {
- type: 'albers usa'
- },
- showcoastlines: true,
- showocean: true
- }
- };
+ it('should coerce subunits only when available (default case)', function() {
+ layoutIn = { geo: {} };
- supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- seaFields.forEach(function(field) {
- expect(layoutOut.geo[field]).toBeUndefined();
- });
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ subunitFields.forEach(function(field) {
+ expect(layoutOut.geo[field]).toBeUndefined();
});
+ });
- it('should not coerce coastlines and ocean if type is albers usa (converse)', function() {
- layoutIn = {
- geo: {
- showcoastlines: true,
- showocean: true
- }
- };
+ it('should coerce subunits only when available (NA case)', function() {
+ layoutIn = {
+ geo: {
+ scope: 'north america',
+ resolution: 50
+ }
+ };
- supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- seaFields.forEach(function(field) {
- expect(layoutOut.geo[field]).toBeDefined();
- });
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ subunitFields.forEach(function(field) {
+ expect(layoutOut.geo[field]).toBeDefined();
});
+ });
- it('should not coerce projection.parallels if type is conic', function() {
- var projTypes = layoutAttributes.projection.type.values;
+ it('should coerce subunits only when available (NA case 2)', function() {
+ layoutIn = {
+ geo: {
+ scope: 'north america',
+ resolution: '50'
+ }
+ };
- function testOne(projType) {
- layoutIn = {
- geo: {
- projection: {
- type: projType,
- parallels: [10, 10]
- }
- }
- };
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ subunitFields.forEach(function(field) {
+ expect(layoutOut.geo[field]).toBeDefined();
+ });
+ });
- supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ it('should coerce subunits only when available (NA case 2)', function() {
+ layoutIn = {
+ geo: {
+ scope: 'north america'
}
+ };
- projTypes.forEach(function(projType) {
- testOne(projType);
- if(projType.indexOf('conic') !== -1) {
- expect(layoutOut.geo.projection.parallels).toBeDefined();
- }
- else {
- expect(layoutOut.geo.projection.parallels).toBeUndefined();
- }
- });
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ subunitFields.forEach(function(field) {
+ expect(layoutOut.geo[field]).toBeUndefined();
});
+ });
- it('should coerce subunits only when available (usa case)', function() {
+ it('should not coerce frame unless for world scope', function() {
+ var scopes = layoutAttributes.scope.values;
+
+ function testOne(scope) {
layoutIn = {
- geo: { scope: 'usa' }
+ geo: { scope: scope }
};
supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- subunitFields.forEach(function(field) {
- expect(layoutOut.geo[field]).toBeDefined();
- });
+ }
+
+ scopes.forEach(function(scope) {
+ testOne(scope);
+ if(scope === 'world') {
+ frameFields.forEach(function(field) {
+ expect(layoutOut.geo[field]).toBeDefined();
+ });
+ } else {
+ frameFields.forEach(function(field) {
+ expect(layoutOut.geo[field]).toBeUndefined();
+ });
+ }
});
+ });
- it('should coerce subunits only when available (default case)', function() {
- layoutIn = { geo: {} };
+ it('should add geo data-only geos into layoutIn', function() {
+ layoutIn = {};
+ fullData = [{ type: 'scattergeo', geo: 'geo' }];
- supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- subunitFields.forEach(function(field) {
- expect(layoutOut.geo[field]).toBeUndefined();
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ expect(layoutIn.geo).toEqual({});
+ });
+
+ it('should add geo data-only geos into layoutIn (converse)', function() {
+ layoutIn = {};
+ fullData = [{ type: 'scatter' }];
+
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ expect(layoutIn.geo).toBe(undefined);
+ });
+
+ describe('should default to lon(lat)range to params non-world scopes', function() {
+ var scopeDefaults = constants.scopeDefaults;
+ var scopes = Object.keys(scopeDefaults);
+ var customLonaxisRange = [-42.21313312, 40.321321];
+ var customLataxisRange = [-42.21313312, 40.321321];
+
+ scopes.forEach(function(s) {
+ if(s === 'world') return;
+
+ it('base case for ' + s, function() {
+ layoutIn = {geo: {scope: s}};
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+
+ var dfltLonaxisRange = scopeDefaults[s].lonaxisRange;
+ var dfltLataxisRange = scopeDefaults[s].lataxisRange;
+
+ expect(layoutOut.geo.lonaxis.range).toEqual(dfltLonaxisRange);
+ expect(layoutOut.geo.lataxis.range).toEqual(dfltLataxisRange);
+ expect(layoutOut.geo.lonaxis.tick0).toEqual(dfltLonaxisRange[0]);
+ expect(layoutOut.geo.lataxis.tick0).toEqual(dfltLataxisRange[0]);
});
- });
- it('should coerce subunits only when available (NA case)', function() {
- layoutIn = {
- geo: {
- scope: 'north america',
- resolution: 50
- }
- };
+ it('custom case for ' + s, function() {
+ layoutIn = {
+ geo: {
+ scope: s,
+ lonaxis: {range: customLonaxisRange},
+ lataxis: {range: customLataxisRange}
+ }
+ };
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- subunitFields.forEach(function(field) {
- expect(layoutOut.geo[field]).toBeDefined();
+ expect(layoutOut.geo.lonaxis.range).toEqual(customLonaxisRange);
+ expect(layoutOut.geo.lataxis.range).toEqual(customLataxisRange);
+ expect(layoutOut.geo.lonaxis.tick0).toEqual(customLonaxisRange[0]);
+ expect(layoutOut.geo.lataxis.tick0).toEqual(customLataxisRange[0]);
});
});
+ });
- it('should coerce subunits only when available (NA case 2)', function() {
- layoutIn = {
- geo: {
- scope: 'north america',
- resolution: '50'
+ describe('should adjust default lon(lat)range to projection.rotation in world scopes', function() {
+ var specs = [{
+ geo: {
+ scope: 'world',
+ projection: {
+ type: 'equirectangular',
+ rotation: {lon: -75, lat: 45}
}
- };
+ },
+ // => -75 +/- 180
+ lonRange: [-255, 105],
+ // => 45 +/- 90
+ latRange: [-45, 135]
+ }, {
+ geo: {
+ scope: 'world',
+ projection: {
+ type: 'orthographic',
+ rotation: {lon: -75, lat: 45}
+ }
+ },
+ // => -75 +/- 90
+ lonRange: [-165, 15],
+ // => 45 +/- 90
+ latRange: [-45, 135]
+ }, {
+ geo: {
+ lonaxis: {range: [-42.21313312, 40.321321]},
+ lataxis: {range: [-42.21313312, 40.321321]}
+ },
+ lonRange: [-42.21313312, 40.321321],
+ latRange: [-42.21313312, 40.321321]
+ }];
- supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- subunitFields.forEach(function(field) {
- expect(layoutOut.geo[field]).toBeDefined();
+ specs.forEach(function(s, i) {
+ it('- case ' + i, function() {
+ layoutIn = {geo: s.geo};
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+
+ expect(layoutOut.geo.lonaxis.range).toEqual(s.lonRange);
+ expect(layoutOut.geo.lataxis.range).toEqual(s.latRange);
});
});
+ });
- it('should coerce subunits only when available (NA case 2)', function() {
- layoutIn = {
- geo: {
- scope: 'north america'
- }
- };
+ describe('should default projection.rotation.lon to lon-center of world-scope maps', function() {
+ var specs = [
+ { lonRange: [10, 80], projLon: 45 },
+ { lonRange: [-45, -10], projLon: -27.5 },
+ { lonRange: [-45, 45], projLon: 0 },
+ { lonRange: [-140, 140], projLon: 0 },
+ // N.B. 180 not -180 after removing ambiguity across antimeridian
+ { lonRange: [140, -140], projLon: 180 }
+ ];
- supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- subunitFields.forEach(function(field) {
- expect(layoutOut.geo[field]).toBeUndefined();
+ specs.forEach(function(s, i) {
+ it('- case ' + i, function() {
+ layoutIn = {
+ geo: { lonaxis: {range: s.lonRange} }
+ };
+
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ expect(layoutOut.geo.lonaxis.range)
+ .toEqual(s.lonRange, 'lonaxis.range');
+ expect(layoutOut.geo.projection.rotation.lon)
+ .toEqual(s.projLon, 'computed projection rotation lon');
});
});
- it('should not coerce frame unless for world scope', function() {
- var scopes = layoutAttributes.scope.values;
+ var scope = 'europe';
+ var dflt = constants.scopeDefaults[scope].projRotate[0];
- function testOne(scope) {
+ specs.forEach(function(s, i) {
+ it('- converse ' + i, function() {
layoutIn = {
- geo: { scope: scope }
+ geo: {
+ scope: 'europe',
+ lonaxis: {range: s.lonRange}
+ }
};
supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- }
+ expect(layoutOut.geo.lonaxis.range)
+ .toEqual(s.lonRange, 'lonaxis.range');
+ expect(layoutOut.geo.projection.rotation.lon)
+ .toEqual(dflt, 'scope dflt projection rotation lon');
+ });
+ });
+ });
- scopes.forEach(function(scope) {
- testOne(scope);
- if(scope === 'world') {
- frameFields.forEach(function(field) {
- expect(layoutOut.geo[field]).toBeDefined();
- });
- }
- else {
- frameFields.forEach(function(field) {
- expect(layoutOut.geo[field]).toBeUndefined();
- });
- }
+ describe('should default center.lon', function() {
+ var specs = [
+ { lonRange: [10, 80], projLon: 0, centerLon: 45 },
+ { lonRange: [-45, -10], projLon: -20, centerLon: -27.5 },
+ { lonRange: [-45, 45], projLon: 5, centerLon: 0 },
+ { lonRange: [-140, 140], projLon: 0, centerLon: 0 },
+ { lonRange: [140, -140], projLon: 160, centerLon: 180 }
+ ];
+
+ specs.forEach(function(s, i) {
+ it('to projection.rotation.lon on world maps - case ' + i, function() {
+ layoutIn = {
+ geo: {
+ lonaxis: {range: s.lonRange},
+ projection: {
+ rotation: {lon: s.projLon}
+ }
+ }
+ };
+
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ expect(layoutOut.geo.lonaxis.range)
+ .toEqual(s.lonRange, 'lonaxis.range');
+ expect(layoutOut.geo.projection.rotation.lon)
+ .toEqual(s.projLon, 'projection.rotation.lon');
+ expect(layoutOut.geo.center.lon)
+ .toEqual(s.projLon, 'center lon (inherited from projection.rotation.lon');
});
});
- it('should add geo data-only geos into layoutIn', function() {
- layoutIn = {};
- fullData = [{ type: 'scattergeo', geo: 'geo' }];
+ var scope = 'africa';
- supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- expect(layoutIn.geo).toEqual({});
+ specs.forEach(function(s, i) {
+ it('to lon-center on scoped maps - case ' + i, function() {
+ layoutIn = {
+ geo: {
+ scope: scope,
+ lonaxis: {range: s.lonRange},
+ projection: {
+ rotation: {lon: s.projLon}
+ }
+ }
+ };
+
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ expect(layoutOut.geo.lonaxis.range)
+ .toEqual(s.lonRange, 'lonaxis.range');
+ expect(layoutOut.geo.projection.rotation.lon)
+ .toEqual(s.projLon, 'projection.rotation.lon');
+ expect(layoutOut.geo.center.lon)
+ .toEqual(s.centerLon, 'computed center lon');
+ });
});
+ });
- it('should add geo data-only geos into layoutIn (converse)', function() {
- layoutIn = {};
- fullData = [{ type: 'scatter' }];
+ describe('should default center.lat', function() {
+ var specs = [
+ { latRange: [-90, 90], centerLat: 0 },
+ { latRange: [0, 30], centerLat: 15 },
+ { latRange: [-25, -5], centerLat: -15 }
+ ];
- supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- expect(layoutIn.geo).toBe(undefined);
+ specs.forEach(function(s, i) {
+ it('- case ' + i, function() {
+ layoutIn = {
+ geo: { lataxis: {range: s.latRange} }
+ };
+
+ supplyLayoutDefaults(layoutIn, layoutOut, fullData);
+ expect(layoutOut.geo.lataxis.range)
+ .toEqual(s.latRange, 'lataxis.range');
+ expect(layoutOut.geo.center.lat)
+ .toEqual(s.centerLat, 'computed center lat');
+
+ });
});
});
});
describe('geojson / topojson utils', function() {
- 'use strict';
-
function _locationToFeature(topojson, loc, locationmode) {
var trace = { locationmode: locationmode };
var features = topojsonUtils.getTopojsonFeatures(trace, topojson);
@@ -416,8 +519,6 @@ describe('geojson / topojson utils', function() {
});
describe('Test geo interactions', function() {
- 'use strict';
-
afterEach(destroyGraphDiv);
describe('mock geo_first.json', function() {
@@ -1043,11 +1144,204 @@ describe('Test geo interactions', function() {
done();
});
});
-
});
});
-});
+ it('should not throw during hover when out-of-range pts are present in *albers usa* map', function(done) {
+ var gd = createGraphDiv();
+ var fig = Lib.extendDeep({}, require('@mocks/geo_scattergeo-out-of-usa.json'));
+ fig.layout.width = 700;
+ fig.layout.height = 500;
+
+ Plotly.plot(gd, fig).then(function() {
+ mouseEvent('mousemove', 350, 250);
+ expect(d3.selectAll('g.hovertext').size()).toEqual(1);
+ })
+ .catch(fail)
+ .then(done);
+ });
+
+ it('@noCI should clear hover label when cursor slips off subplot', function(done) {
+ var gd = createGraphDiv();
+ var fig = Lib.extendDeep({}, require('@mocks/geo_orthographic.json'));
+
+ function _assert(msg, hoverLabelCnt) {
+ expect(d3.selectAll('g.hovertext').size())
+ .toBe(hoverLabelCnt, msg);
+ }
+
+ var px = 390;
+ var py = 290;
+ var cnt = 0;
+
+ Plotly.plot(gd, fig).then(function() {
+ gd.on('plotly_unhover', function() { cnt++; });
+
+ mouseEvent('mousemove', px, py);
+ _assert('base state', 1);
+
+ return new Promise(function(resolve) {
+ var interval = setInterval(function() {
+ px += 2;
+ mouseEvent('mousemove', px, py);
+
+ if(px < 402) {
+ _assert('- px ' + px, 1);
+ expect(cnt).toBe(0, 'no plotly_unhover event so far');
+ } else {
+ _assert('- px ' + px, 0);
+ expect(cnt).toBe(1, 'plotly_unhover event count');
+
+ clearInterval(interval);
+ resolve();
+ }
+ }, 100);
+ });
+ })
+ .catch(fail)
+ .then(done);
+ });
+
+ it('should not confuse positions on either side of the globe', function(done) {
+ var gd = createGraphDiv();
+ var fig = Lib.extendDeep({}, require('@mocks/geo_orthographic.json'));
+
+ fig.data[0].visible = false;
+ fig.layout.geo.projection.rotation = {lon: -75, lat: 90};
+
+ function check(p, hoverLabelCnt) {
+ mouseEvent('mousemove', p[0], p[1]);
+
+ var invert = gd._fullLayout.geo._subplot.projection.invert;
+ var lonlat = invert(p);
+
+ expect(d3.selectAll('g.hovertext').size())
+ .toBe(hoverLabelCnt, 'for ' + lonlat);
+
+ delete gd._lastHoverTime;
+ }
+
+ Plotly.plot(gd, fig).then(function() {
+ var px = 255;
+
+ check([px, 163], 0);
+ check([px, 360], 1);
+ })
+ .catch(fail)
+ .then(done);
+ });
+
+ it('should plot to scope defaults when user setting lead to NaN map bounds', function(done) {
+ var gd = createGraphDiv();
+
+ spyOn(Lib, 'warn');
+
+ Plotly.plot(gd, [{
+ type: 'scattergeo',
+ lon: [0],
+ lat: [0]
+ }], {
+ geo: {
+ projection: {
+ type: 'kavrayskiy7',
+ rotation: {
+ lat: 38.794799,
+ lon: -81.622334,
+ }
+ },
+ center: {
+ lat: -81
+ },
+ lataxis: {
+ range: [38.794799, 45.122292]
+ },
+ lonaxis: {
+ range: [-82.904731, -81.622334]
+ }
+ },
+ width: 700,
+ heigth: 500
+ })
+ .then(function() {
+ var geoLayout = gd._fullLayout.geo;
+ var geo = geoLayout._subplot;
+
+ expect(geoLayout.projection.rotation).toEqual({
+ lon: 0, lat: 0, roll: 0,
+ });
+ expect(geoLayout.center).toEqual({
+ lon: 0, lat: 0
+ });
+ expect(geoLayout.lonaxis.range).toEqual([-180, 180]);
+ expect(geoLayout.lataxis.range).toEqual([-90, 90]);
+
+ expect(geo.viewInitial).toEqual({
+ 'projection.rotation.lon': 0,
+ 'center.lon': 0,
+ 'center.lat': 0,
+ 'projection.scale': 1
+ });
+
+ expect(Lib.warn).toHaveBeenCalledTimes(1);
+ expect(Lib.warn).toHaveBeenCalledWith(
+ 'Invalid geo settings, relayout\'ing to default view.'
+ );
+ })
+ .catch(fail)
+ .then(done);
+ });
+
+ it('should get hover right for choropleths involving landmasses that cross antimeridian', function(done) {
+ var gd = createGraphDiv();
+
+ function check(lonlat, hoverLabelCnt, msg) {
+ var projection = gd._fullLayout.geo._subplot.projection;
+ var px = projection(lonlat);
+
+ mouseEvent('mousemove', px[0], px[1]);
+ expect(d3.selectAll('g.hovertext').size()).toBe(hoverLabelCnt, msg);
+
+ delete gd._lastHoverTime;
+ }
+
+ Plotly.newPlot(gd, [{
+ type: 'choropleth',
+ locations: ['RUS', 'FJI', 'ATA'],
+ z: [0, 1, 2]
+ }])
+ .then(function() {
+ check([81, 66], 1, 'spot in north-central Russia that polygon.contains gets wrong before +360 shift');
+ check([-80, 66], 0, 'spot north of Hudson bay that polygon.contains believe is in Russia before before +360 shift');
+
+ return Plotly.relayout(gd, 'geo.projection.rotation.lon', 180);
+ })
+ .then(function() {
+ check([-174, 65], 1, 'spot in Russia mainland beyond antimeridian');
+
+ return Plotly.relayout(gd, {
+ 'geo.center.lat': -16,
+ 'geo.projection.scale': 17
+ });
+ })
+ .then(function() {
+ check([179, -16.6], 1, 'spot on Fiji island that cross antimeridian west of antimeridian');
+ check([-179.9, -16.2], 1, 'spot on Fiji island that cross antimeridian east of antimeridian');
+
+ return Plotly.relayout(gd, {
+ 'geo.center.lat': null,
+ 'geo.projection': {
+ type: 'orthographic',
+ rotation: {lat: -90}
+ }
+ });
+ })
+ .then(function() {
+ check([-150, -89], 1, 'spot in Antarctica that requires *stitching*');
+ })
+ .catch(fail)
+ .then(done);
+ });
+});
describe('Test event property of interactions on a geo plot:', function() {
var mock = require('@mocks/geo_scattergeo-locations.json');
@@ -1244,3 +1538,485 @@ describe('Test event property of interactions on a geo plot:', function() {
});
});
});
+
+describe('Test geo base layers', function() {
+ afterEach(destroyGraphDiv);
+
+ it('should clear obsolete features and layers on *geo.scope* relayout calls', function(done) {
+ var gd = createGraphDiv();
+
+ function _assert(geojson, layers) {
+ var cd0 = gd.calcdata[0];
+ var subplot = gd._fullLayout.geo._subplot;
+
+ expect(cd0[0].geojson).negateIf(geojson[0]).toBe(null);
+ expect(cd0[1].geojson).negateIf(geojson[1]).toBe(null);
+
+ expect(Object.keys(subplot.layers).length).toEqual(layers.length, '# of layers');
+
+ d3.select(gd).selectAll('.geo > .layer').each(function(d, i) {
+ expect(d).toBe(layers[i], 'layer ' + d + ' at position ' + i);
+ });
+ }
+
+ Plotly.plot(gd, [{
+ type: 'choropleth',
+ locations: ['CAN', 'FRA'],
+ z: [10, 20]
+ }], {
+ geo: {showframe: true}
+ })
+ .then(function() {
+ _assert(
+ [true, true],
+ ['bg', 'coastlines', 'frame', 'backplot', 'frontplot']
+ );
+ return Plotly.relayout(gd, 'geo.scope', 'europe');
+ })
+ .then(function() {
+ _assert(
+ // 'CAN' is not drawn on 'europe' scope
+ [false, true],
+ // 'frame' is not drawn on scoped maps
+ // 'countries' are there by default on scoped maps
+ ['bg', 'countries', 'backplot', 'frontplot']
+ );
+ return Plotly.relayout(gd, 'geo.scope', 'africa');
+ })
+ .then(function() {
+ _assert(
+ [false, false],
+ ['bg', 'countries', 'backplot', 'frontplot']
+ );
+ return Plotly.relayout(gd, 'geo.scope', 'world');
+ })
+ .then(function() {
+ _assert(
+ [true, true],
+ ['bg', 'coastlines', 'frame', 'backplot', 'frontplot']
+ );
+ })
+ .catch(fail)
+ .then(done);
+ });
+});
+
+describe('Test geo zoom/pan/drag interactions:', function() {
+ var gd;
+ var eventData;
+ var dblClickCnt = 0;
+
+ afterEach(destroyGraphDiv);
+
+ function plot(fig) {
+ gd = createGraphDiv();
+
+ return Plotly.plot(gd, fig).then(function() {
+ gd.on('plotly_relayout', function(d) { eventData = d; });
+ gd.on('plotly_doubleclick', function() { dblClickCnt++; });
+ });
+ }
+
+ function assertEventData(msg, eventKeys) {
+ if(eventKeys === 'dblclick') {
+ expect(dblClickCnt).toBe(1, msg + 'double click got fired');
+ expect(eventData).toBeDefined(msg + 'relayout is fired on double clicks');
+ } else {
+ expect(dblClickCnt).toBe(0, 'double click not fired');
+
+ if(Array.isArray(eventKeys)) {
+ expect(Object.keys(eventData || {}).length)
+ .toBe(Object.keys(eventKeys).length, msg + '# of event data keys');
+ eventKeys.forEach(function(k) {
+ expect((eventData || {})[k]).toBeDefined(msg + 'event data key ' + k);
+ });
+ } else {
+ expect(eventData).toBeUndefined(msg + 'relayout not fired');
+ }
+ }
+
+ eventData = undefined;
+ dblClickCnt = 0;
+ }
+
+
+ function drag(path) {
+ var len = path.length;
+
+ mouseEvent('mousemove', path[0][0], path[0][1]);
+ mouseEvent('mousedown', path[0][0], path[0][1]);
+
+ path.slice(1, len).forEach(function(pt) {
+ mouseEvent('mousemove', pt[0], pt[1]);
+ });
+
+ mouseEvent('mouseup', path[len - 1][0], path[len - 1][1]);
+ }
+
+ function scroll(pos, delta) {
+ return new Promise(function(resolve) {
+ mouseEvent('mousemove', pos[0], pos[1]);
+ mouseEvent('scroll', pos[0], pos[1], {deltaX: delta[0], deltaY: delta[1]});
+ setTimeout(resolve, 100);
+ });
+ }
+
+ function dblClick(pos) {
+ return new Promise(function(resolve) {
+ mouseEvent('dblclick', pos[0], pos[1]);
+ setTimeout(resolve, 100);
+ });
+ }
+
+ it('should work for non-clipped projections', function(done) {
+ var fig = Lib.extendDeep({}, require('@mocks/geo_winkel-tripel'));
+ fig.layout.width = 700;
+ fig.layout.height = 500;
+ fig.layout.dragmode = 'pan';
+
+ function _assert(step, attr, proj, eventKeys) {
+ var msg = '[' + step + '] ';
+
+ var geoLayout = gd._fullLayout.geo;
+ var rotation = geoLayout.projection.rotation;
+ var center = geoLayout.center;
+ var scale = geoLayout.projection.scale;
+
+ expect(rotation.lon).toBeCloseTo(attr[0][0], 1, msg + 'rotation.lon');
+ expect(rotation.lat).toBeCloseTo(attr[0][1], 1, msg + 'rotation.lat');
+ expect(center.lon).toBeCloseTo(attr[1][0], 1, msg + 'center.lon');
+ expect(center.lat).toBeCloseTo(attr[1][1], 1, msg + 'center.lat');
+ expect(scale).toBeCloseTo(attr[2], 1, msg + 'zoom');
+
+ var geo = geoLayout._subplot;
+ var rotate = geo.projection.rotate();
+ var translate = geo.projection.translate();
+ var _center = geo.projection.center();
+ var _scale = geo.projection.scale();
+
+ expect(rotate[0]).toBeCloseTo(proj[0][0], 0, msg + 'rotate[0]');
+ expect(rotate[1]).toBeCloseTo(proj[0][1], 0, msg + 'rotate[1]');
+ expect(translate[0]).toBeCloseTo(proj[1][0], 0, msg + 'translate[0]');
+ expect(translate[1]).toBeCloseTo(proj[1][1], 0, msg + 'translate[1]');
+ expect(_center[0]).toBeCloseTo(proj[2][0], 0, msg + 'center[0]');
+ expect(_center[1]).toBeCloseTo(proj[2][1], 0, msg + 'center[1]');
+ expect(_scale).toBeCloseTo(proj[3], 0, msg + 'scale');
+
+ assertEventData(msg, eventKeys);
+ }
+
+ plot(fig).then(function() {
+ _assert('base', [
+ [-90, 0], [-90, 0], 1
+ ], [
+ [90, 0], [350, 260], [0, 0], 101.9
+ ], undefined);
+ return drag([[350, 250], [400, 250]]);
+ })
+ .then(function() {
+ _assert('after east-west drag', [
+ [-124.4, 0], [-124.4, 0], 1
+ ], [
+ [124.4, 0], [350, 260], [0, 0], 101.9
+ ], [
+ 'geo.projection.rotation.lon', 'geo.center.lon'
+ ]);
+ return drag([[400, 250], [400, 300]]);
+ })
+ .then(function() {
+ _assert('after north-south drag', [
+ [-124.4, 0], [-124.4, 28.1], 1
+ ], [
+ [124.4, 0], [350, 310], [0, 0], 101.9
+ ], [
+ 'geo.center.lat'
+ ]);
+ return scroll([200, 250], [-200, -200]);
+ })
+ .then(function() {
+ _assert('after off-center scroll', [
+ [-151.2, 0], [-151.2, 29.5], 1.3
+ ], [
+ [151.2, 0], [350, 329.2], [0, 0], 134.4
+ ], [
+ 'geo.projection.rotation.lon',
+ 'geo.center.lon', 'geo.center.lat',
+ 'geo.projection.scale'
+ ]);
+ return Plotly.relayout(gd, 'geo.showocean', false);
+ })
+ .then(function() {
+ _assert('after some relayout call that causes a replot', [
+ [-151.2, 0], [-151.2, 29.5], 1.3
+ ], [
+ // converts translate (px) to center (lonlat)
+ [151.2, 0], [350, 260], [0, 29.5], 134.4
+ ], [
+ 'geo.showocean'
+ ]);
+ return dblClick([350, 250]);
+ })
+ .then(function() {
+ // resets to initial view
+ _assert('after double click', [
+ [-90, 0], [-90, 0], 1
+ ], [
+ [90, 0], [350, 260], [0, 0], 101.9
+ ], 'dblclick');
+ })
+ .catch(fail)
+ .then(done);
+ });
+
+ it('should work for clipped projections', function(done) {
+ var fig = Lib.extendDeep({}, require('@mocks/geo_orthographic'));
+ fig.layout.dragmode = 'pan';
+
+ // of layout width = height = 500
+
+ function _assert(step, attr, proj, eventKeys) {
+ var msg = '[' + step + '] ';
+
+ var geoLayout = gd._fullLayout.geo;
+ var rotation = geoLayout.projection.rotation;
+ var scale = geoLayout.projection.scale;
+
+ expect(rotation.lon).toBeCloseTo(attr[0][0], 0, msg + 'rotation.lon');
+ expect(rotation.lat).toBeCloseTo(attr[0][1], 0, msg + 'rotation.lat');
+ expect(scale).toBeCloseTo(attr[1], 1, msg + 'zoom');
+
+ var geo = geoLayout._subplot;
+ var rotate = geo.projection.rotate();
+ var _scale = geo.projection.scale();
+
+ expect(rotate[0]).toBeCloseTo(proj[0][0], 0, msg + 'rotate[0]');
+ expect(rotate[1]).toBeCloseTo(proj[0][1], 0, msg + 'rotate[1]');
+ expect(_scale).toBeCloseTo(proj[1], 0, msg + 'scale');
+
+ assertEventData(msg, eventKeys);
+ }
+
+ plot(fig).then(function() {
+ _assert('base', [
+ [-75, 45], 1
+ ], [
+ [75, -45], 160
+ ], undefined);
+ return drag([[250, 250], [300, 250]]);
+ })
+ .then(function() {
+ _assert('after east-west drag', [
+ [-103.7, 49.3], 1
+ ], [
+ [103.7, -49.3], 160
+ ], [
+ 'geo.projection.rotation.lon', 'geo.projection.rotation.lat'
+ ]);
+ return drag([[250, 250], [300, 300]]);
+ })
+ .then(function() {
+ _assert('after NW-SE drag', [
+ [-135.5, 73.8], 1
+ ], [
+ [135.5, -73.8], 160
+ ], [
+ 'geo.projection.rotation.lon', 'geo.projection.rotation.lat'
+ ]);
+ return scroll([300, 300], [-200, -200]);
+ })
+ .then(function() {
+ _assert('after scroll', [
+ [-126.2, 67.1], 1.3
+ ], [
+ [126.2, -67.1], 211.1
+ ], [
+ 'geo.projection.rotation.lon', 'geo.projection.rotation.lat',
+ 'geo.projection.scale'
+ ]);
+ return Plotly.relayout(gd, 'geo.showocean', false);
+ })
+ .then(function() {
+ _assert('after some relayout call that causes a replot', [
+ [-126.2, 67.1], 1.3
+ ], [
+ [126.2, -67.1], 211.1
+ ], [
+ 'geo.showocean'
+ ]);
+ return dblClick([350, 250]);
+ })
+ .then(function() {
+ // resets to initial view
+ _assert('after double click', [
+ [-75, 45], 1
+ ], [
+ [75, -45], 160
+ ], 'dblclick');
+ })
+ .catch(fail)
+ .then(done);
+ });
+
+ it('should work for scoped projections', function(done) {
+ var fig = Lib.extendDeep({}, require('@mocks/geo_europe-bubbles'));
+ fig.layout.geo.resolution = 110;
+ fig.layout.dragmode = 'pan';
+
+ // of layout width = height = 500
+
+ function _assert(step, attr, proj, eventKeys) {
+ var msg = '[' + step + '] ';
+
+ var geoLayout = gd._fullLayout.geo;
+ var center = geoLayout.center;
+ var scale = geoLayout.projection.scale;
+
+ expect(center.lon).toBeCloseTo(attr[0][0], -0.5, msg + 'center.lon');
+ expect(center.lat).toBeCloseTo(attr[0][1], -0.5, msg + 'center.lat');
+ expect(scale).toBeCloseTo(attr[1], 1, msg + 'zoom');
+
+ var geo = geoLayout._subplot;
+ var translate = geo.projection.translate();
+ var _center = geo.projection.center();
+ var _scale = geo.projection.scale();
+
+ expect(translate[0]).toBeCloseTo(proj[0][0], -0.75, msg + 'translate[0]');
+ expect(translate[1]).toBeCloseTo(proj[0][1], -0.75, msg + 'translate[1]');
+ expect(_center[0]).toBeCloseTo(proj[1][0], -0.5, msg + 'center[0]');
+ expect(_center[1]).toBeCloseTo(proj[1][1], -0.5, msg + 'center[1]');
+ expect(_scale).toBeCloseTo(proj[2], -1, msg + 'scale');
+
+ assertEventData(msg, eventKeys);
+ }
+
+ plot(fig).then(function() {
+ _assert('base', [
+ [15, 57.5], 1,
+ ], [
+ [247, 260], [0, 57.5], 292.2
+ ], undefined);
+ return drag([[250, 250], [200, 200]]);
+ })
+ .then(function() {
+ _assert('after SW-NE drag', [
+ [30.9, 46.2], 1
+ ], [
+ // changes translate(), but not center()
+ [197, 210], [0, 57.5], 292.2
+ ], [
+ 'geo.center.lon', 'geo.center.lon'
+ ]);
+ return scroll([300, 300], [-200, -200]);
+ })
+ .then(function() {
+ _assert('after scroll', [
+ [34.3, 43.6], 1.3
+ ], [
+ [164.1, 181.2], [0, 57.5], 385.5
+ ], [
+ 'geo.center.lon', 'geo.center.lon', 'geo.projection.scale'
+ ]);
+ return Plotly.relayout(gd, 'geo.showlakes', true);
+ })
+ .then(function() {
+ _assert('after some relayout call that causes a replot', [
+ [34.3, 43.6], 1.3
+ ], [
+ // changes are now reflected in 'center'
+ [247, 260], [19.3, 43.6], 385.5
+ ], [
+ 'geo.showlakes'
+ ]);
+ return dblClick([250, 250]);
+ })
+ .then(function() {
+ _assert('after double click', [
+ [15, 57.5], 1,
+ ], [
+ [247, 260], [0, 57.5], 292.2
+ ], 'dblclick');
+ })
+ .catch(fail)
+ .then(done);
+ });
+
+ it('should work for *albers usa* projections', function(done) {
+ var fig = Lib.extendDeep({}, require('@mocks/geo_choropleth-usa'));
+ fig.layout.dragmode = 'pan';
+
+ // layout width = 870
+ // layout height = 598
+
+ function _assert(step, attr, proj, eventKeys) {
+ var msg = '[' + step + '] ';
+
+ var geoLayout = gd._fullLayout.geo;
+ var center = geoLayout.center;
+ var scale = geoLayout.projection.scale;
+
+ expect(center.lon).toBeCloseTo(attr[0][0], 1, msg + 'center.lon');
+ expect(center.lat).toBeCloseTo(attr[0][1], 1, msg + 'center.lat');
+ expect(scale).toBeCloseTo(attr[1], 1, msg + 'zoom');
+
+ // albersUsa projection does not have a center() method
+ var geo = geoLayout._subplot;
+ var translate = geo.projection.translate();
+ var _scale = geo.projection.scale();
+
+ expect(translate[0]).toBeCloseTo(proj[0][0], -1, msg + 'translate[0]');
+ expect(translate[1]).toBeCloseTo(proj[0][1], -1, msg + 'translate[1]');
+ expect(_scale).toBeCloseTo(proj[1], -1.5, msg + 'scale');
+
+ assertEventData(msg, eventKeys);
+ }
+
+ plot(fig).then(function() {
+ _assert('base', [
+ [-96.6, 38.7], 1,
+ ], [
+ [416, 309], 738.5
+ ], undefined);
+ return drag([[250, 250], [200, 200]]);
+ })
+ .then(function() {
+ _assert('after NW-SE drag', [
+ [-91.8, 34.8], 1,
+ ], [
+ [366, 259], 738.5
+ ], [
+ 'geo.center.lon', 'geo.center.lon'
+ ]);
+ return scroll([300, 300], [-200, -200]);
+ })
+ .then(function() {
+ _assert('after scroll', [
+ [-94.5, 35.0], 1.3
+ ], [
+ [387.1, 245.9], 974.4
+ ], [
+ 'geo.center.lon', 'geo.center.lon', 'geo.projection.scale'
+ ]);
+ return Plotly.relayout(gd, 'geo.showlakes', true);
+ })
+ .then(function() {
+ _assert('after some relayout call that causes a replot', [
+ [-94.5, 35.0], 1.3
+ ], [
+ // new center values are reflected in translate()
+ [387.1, 245.9], 974.4
+ ], [
+ 'geo.showlakes'
+ ]);
+ return dblClick([250, 250]);
+ })
+ .then(function() {
+ _assert('after double click', [
+ [-96.6, 38.7], 1,
+ ], [
+ [416, 309], 738.5
+ ], 'dblclick');
+ })
+ .catch(fail)
+ .then(done);
+ });
+});
diff --git a/test/jasmine/tests/modebar_test.js b/test/jasmine/tests/modebar_test.js
index c6c08ec98d9..3246769b229 100644
--- a/test/jasmine/tests/modebar_test.js
+++ b/test/jasmine/tests/modebar_test.js
@@ -173,10 +173,10 @@ describe('ModeBar', function() {
expectedButtonCount += group.length;
});
- expect(modeBar.hasButtons(buttons)).toBe(true);
- expect(countGroups(modeBar)).toEqual(expectedGroupCount);
- expect(countButtons(modeBar)).toEqual(expectedButtonCount);
- expect(countLogo(modeBar)).toEqual(1);
+ expect(modeBar.hasButtons(buttons)).toBe(true, 'modeBar.hasButtons');
+ expect(countGroups(modeBar)).toEqual(expectedGroupCount, 'correct group count');
+ expect(countButtons(modeBar)).toEqual(expectedButtonCount, 'correct button count');
+ expect(countLogo(modeBar)).toEqual(1, 'correct logo count');
}
it('creates mode bar (unselectable cartesian version)', function() {
@@ -256,6 +256,7 @@ describe('ModeBar', function() {
it('creates mode bar (geo version)', function() {
var buttons = getButtons([
['toImage', 'sendDataToCloud'],
+ ['pan2d'],
['zoomInGeo', 'zoomOutGeo', 'resetGeo'],
['hoverClosestGeo']
]);
@@ -269,6 +270,29 @@ describe('ModeBar', function() {
checkButtons(modeBar, buttons, 1);
});
+ it('creates mode bar (geo + selected version)', function() {
+ var buttons = getButtons([
+ ['toImage', 'sendDataToCloud'],
+ ['pan2d', 'select2d', 'lasso2d'],
+ ['zoomInGeo', 'zoomOutGeo', 'resetGeo'],
+ ['hoverClosestGeo']
+ ]);
+
+ var gd = getMockGraphInfo();
+ gd._fullLayout._basePlotModules = [{ name: 'geo' }];
+ gd._fullData = [{
+ type: 'scattergeo',
+ visible: true,
+ mode: 'markers',
+ _module: {selectPoints: true}
+ }];
+
+ manageModeBar(gd);
+ var modeBar = gd._fullLayout._modeBar;
+
+ checkButtons(modeBar, buttons, 1);
+ });
+
it('creates mode bar (mapbox version)', function() {
var buttons = getButtons([
['toImage', 'sendDataToCloud'],
diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js
index b2be82a162c..d6c2dd4c0c5 100644
--- a/test/jasmine/tests/select_test.js
+++ b/test/jasmine/tests/select_test.js
@@ -12,41 +12,46 @@ var touchEvent = require('../assets/touch_event');
var LONG_TIMEOUT_INTERVAL = 5 * jasmine.DEFAULT_TIMEOUT_INTERVAL;
+function drag(path, options) {
+ var len = path.length;
-describe('select box and lasso', function() {
- var mock = require('@mocks/14.json');
-
- var selectPath = [[93, 193], [143, 193]];
- var lassoPath = [[316, 171], [318, 239], [335, 243], [328, 169]];
+ if(!options) options = {type: 'mouse'};
- afterEach(destroyGraphDiv);
+ if(options.type === 'touch') {
+ touchEvent('touchstart', path[0][0], path[0][1]);
- function drag(path, options) {
- var len = path.length;
+ path.slice(1, len).forEach(function(pt) {
+ touchEvent('touchmove', pt[0], pt[1]);
+ });
- if(!options) options = {type: 'mouse'};
+ touchEvent('touchend', path[len - 1][0], path[len - 1][1]);
+ return;
+ }
- if(options.type === 'touch') {
- touchEvent('touchstart', path[0][0], path[0][1]);
+ mouseEvent('mousemove', path[0][0], path[0][1]);
+ mouseEvent('mousedown', path[0][0], path[0][1]);
- path.slice(1, len).forEach(function(pt) {
- touchEvent('touchmove', pt[0], pt[1]);
- });
+ path.slice(1, len).forEach(function(pt) {
+ mouseEvent('mousemove', pt[0], pt[1]);
+ });
- touchEvent('touchend', path[len - 1][0], path[len - 1][1]);
+ mouseEvent('mouseup', path[len - 1][0], path[len - 1][1]);
+}
- return;
- }
+function assertSelectionNodes(cornerCnt, outlineCnt) {
+ expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
+ .toBe(cornerCnt, 'selection corner count');
+ expect(d3.selectAll('.zoomlayer > .select-outline').size())
+ .toBe(outlineCnt, 'selection outline count');
+}
- mouseEvent('mousemove', path[0][0], path[0][1]);
- mouseEvent('mousedown', path[0][0], path[0][1]);
+describe('Test select box and lasso in general:', function() {
+ var mock = require('@mocks/14.json');
- path.slice(1, len).forEach(function(pt) {
- mouseEvent('mousemove', pt[0], pt[1]);
- });
+ var selectPath = [[93, 193], [143, 193]];
+ var lassoPath = [[316, 171], [318, 239], [335, 243], [328, 169]];
- mouseEvent('mouseup', path[len - 1][0], path[len - 1][1]);
- }
+ afterEach(destroyGraphDiv);
function assertRange(actual, expected) {
var PRECISION = 4;
@@ -72,13 +77,6 @@ describe('select box and lasso', function() {
});
}
- function assertSelectionNodes(cornerCnt, outlineCnt) {
- expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
- .toBe(cornerCnt, 'selection corner count');
- expect(d3.selectAll('.zoomlayer > .select-outline').size())
- .toBe(outlineCnt, 'selection outline count');
- }
-
describe('select elements', function() {
var mockCopy = Lib.extendDeep({}, mock);
mockCopy.layout.dragmode = 'select';
@@ -430,195 +428,264 @@ describe('select box and lasso', function() {
})
.then(done);
});
+});
- it('should work on scatterternary traces', function(done) {
- var fig = Lib.extendDeep({}, require('@mocks/ternary_simple'));
- var gd = createGraphDiv();
- var pts = [];
+describe('Test select box and lasso per trace:', function() {
+ var gd;
+ var eventData;
- fig.layout.width = 800;
- fig.layout.dragmode = 'select';
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function makeAssertPoints(keys) {
+ var callNumber = 0;
+
+ return function(expected) {
+ var msg = '(call #' + callNumber + ') ';
+ var pts = (eventData || {}).points || [];
- function assertPoints(expected) {
- expect(pts.length).toBe(expected.length, 'selected points length');
+ expect(pts.length).toBe(expected.length, msg + 'selected points length');
pts.forEach(function(p, i) {
- var e = expected[i];
- expect(p.a).toBe(e.a, 'selected pt a val');
- expect(p.b).toBe(e.b, 'selected pt b val');
- expect(p.c).toBe(e.c, 'selected pt c val');
+ var e = expected[i] || [];
+ keys.forEach(function(k, j) {
+ expect(p[k])
+ .toBe(e[j], msg + 'selected pt ' + i + ' - ' + k + ' val');
+ });
});
- pts = [];
- }
- Plotly.plot(gd, fig).then(function() {
- gd.on('plotly_selected', function(data) {
- pts = data.points;
- });
+ callNumber++;
+ };
+ }
- assertSelectionNodes(0, 0);
- drag([[400, 200], [445, 235]]);
+ function makeAssertRanges(subplot, tol) {
+ tol = tol || 1;
+ var callNumber = 0;
+
+ return function(expected) {
+ var msg = '(call #' + callNumber + ') ';
+ var ranges = (eventData.range || {})[subplot] || [];
+
+ expect(ranges)
+ .toBeCloseTo2DArray(expected, tol, msg + 'select box range for ' + subplot);
+
+ callNumber++;
+ };
+ }
+
+ function makeAssertLassoPoints(subplot, tol) {
+ tol = tol || 1;
+ var callNumber = 0;
+
+ return function(expected) {
+ var msg = '(call #' + callNumber + ') ';
+ var lassoPoints = (eventData.lassoPoints || {})[subplot] || [];
+
+ expect(lassoPoints)
+ .toBeCloseTo2DArray(expected, tol, msg + 'lasso points for ' + subplot);
+
+ callNumber++;
+ };
+ }
+
+ function _run(dragPath, afterDragFn, dblClickPos) {
+ afterDragFn = afterDragFn || function() {};
+ dblClickPos = dblClickPos || [250, 200];
+
+ var selectingCnt = 0;
+ var selectedCnt = 0;
+ var deselectCnt = 0;
+
+ gd.once('plotly_selecting', function() {
+ assertSelectionNodes(1, 2);
+ selectingCnt++;
+ });
+
+ gd.once('plotly_selected', function(d) {
assertSelectionNodes(0, 2);
- assertPoints([{ a: 0.5, b: 0.25, c: 0.25 }]);
+ selectedCnt++;
+ eventData = d;
+ });
+
+ gd.once('plotly_deselect', function() {
+ deselectCnt++;
+ assertSelectionNodes(0, 0);
+ });
+
+ assertSelectionNodes(0, 0);
+ drag(dragPath);
+ afterDragFn();
+
+ return doubleClick(dblClickPos[0], dblClickPos[1]).then(function() {
+ expect(selectingCnt).toBe(1, 'plotly_selecting call count');
+ expect(selectedCnt).toBe(1, 'plotly_selected call count');
+ expect(deselectCnt).toBe(1, 'plotly_deselect call count');
+ });
+ }
+
+ function _runNoSelect(dragPath, afterDragFn, dblClickPos) {
+ afterDragFn = afterDragFn || function() {};
+ dblClickPos = dblClickPos || [250, 200];
+
+ var selectingCnt = 0;
+ var selectedCnt = 0;
+ var deselectCnt = 0;
+
+ gd.once('plotly_selecting', function() {
+ selectingCnt++;
+ });
+
+ gd.once('plotly_selected', function() {
+ selectedCnt++;
+ });
+
+ gd.once('plotly_deselect', function() {
+ deselectCnt++;
+ });
+ assertSelectionNodes(0, 0);
+ drag(dragPath);
+ afterDragFn();
+
+ return doubleClick(dblClickPos[0], dblClickPos[1]).then(function() {
+ expect(selectingCnt).toBe(0, 'plotly_selecting call count');
+ expect(selectedCnt).toBe(0, 'plotly_selected call count');
+ expect(deselectCnt).toBe(0, 'plotly_deselect call count');
+ });
+ }
+
+ it('should work on scatterternary traces', function(done) {
+ var assertPoints = makeAssertPoints(['a', 'b', 'c']);
+
+ var fig = Lib.extendDeep({}, require('@mocks/ternary_simple'));
+ fig.layout.width = 800;
+ fig.layout.dragmode = 'select';
+
+ Plotly.plot(gd, fig).then(function() {
+ return _run([[400, 200], [445, 235]], function() {
+ assertPoints([[0.5, 0.25, 0.25]]);
+ }, [380, 180]);
+ })
+ .then(function() {
return Plotly.relayout(gd, 'dragmode', 'lasso');
})
.then(function() {
- assertSelectionNodes(0, 0);
- drag([[400, 200], [445, 200], [445, 235], [400, 235], [400, 200]]);
- assertSelectionNodes(0, 2);
- assertPoints([{ a: 0.5, b: 0.25, c: 0.25 }]);
-
+ return _run([[400, 200], [445, 200], [445, 235], [400, 235], [400, 200]], function() {
+ assertPoints([[0.5, 0.25, 0.25]]);
+ }, [380, 180]);
+ })
+ .then(function() {
// should work after a relayout too
return Plotly.relayout(gd, 'width', 400);
})
.then(function() {
- assertSelectionNodes(0, 0);
- drag([[200, 200], [230, 200], [230, 230], [200, 230], [200, 200]]);
- assertSelectionNodes(0, 2);
- assertPoints([{ a: 0.5, b: 0.25, c: 0.25 }]);
+ return _run([[200, 200], [230, 200], [230, 230], [200, 230], [200, 200]], function() {
+ assertPoints([[0.5, 0.25, 0.25]]);
+ }, [180, 180]);
})
.catch(fail)
.then(done);
});
it('should work on scattercarpet traces', function(done) {
- var fig = Lib.extendDeep({}, require('@mocks/scattercarpet'));
- var gd = createGraphDiv();
- var pts = [];
+ var assertPoints = makeAssertPoints(['a', 'b']);
+ var fig = Lib.extendDeep({}, require('@mocks/scattercarpet'));
fig.layout.dragmode = 'select';
- function assertPoints(expected) {
- expect(pts.length).toBe(expected.length, 'selected points length');
-
- pts.forEach(function(p, i) {
- var e = expected[i];
- expect(p.a).toBe(e.a, 'selected pt a val');
- expect(p.b).toBe(e.b, 'selected pt b val');
- });
- pts = [];
- }
-
Plotly.plot(gd, fig).then(function() {
- gd.on('plotly_selected', function(data) {
- pts = data.points;
+ return _run([[300, 200], [400, 250]], function() {
+ assertPoints([[0.2, 1.5]]);
});
-
- assertSelectionNodes(0, 0);
- drag([[300, 200], [400, 250]]);
- assertSelectionNodes(0, 2);
- assertPoints([{ a: 0.2, b: 1.5 }]);
-
+ })
+ .then(function() {
return Plotly.relayout(gd, 'dragmode', 'lasso');
})
.then(function() {
- assertSelectionNodes(0, 0);
- drag([[300, 200], [400, 200], [400, 250], [300, 250], [300, 200]]);
- assertSelectionNodes(0, 2);
- assertPoints([{ a: 0.2, b: 1.5 }]);
+ return _run([[300, 200], [400, 200], [400, 250], [300, 250], [300, 200]], function() {
+ assertPoints([[0.2, 1.5]]);
+ });
})
.catch(fail)
.then(done);
});
it('@noCI should work on scattermapbox traces', function(done) {
- var fig = Lib.extendDeep({}, require('@mocks/mapbox_bubbles-text'));
- var gd = createGraphDiv();
- var eventData;
+ var assertPoints = makeAssertPoints(['lon', 'lat']);
+ var assertRanges = makeAssertRanges('mapbox');
+ var assertLassoPoints = makeAssertLassoPoints('mapbox');
+ var fig = Lib.extendDeep({}, require('@mocks/mapbox_bubbles-text'));
fig.layout.dragmode = 'select';
fig.config = {
mapboxAccessToken: require('@build/credentials.json').MAPBOX_ACCESS_TOKEN
};
- function assertPoints(expected) {
- var pts = eventData.points || [];
-
- expect(pts.length).toBe(expected.length, 'selected points length');
-
- pts.forEach(function(p, i) {
- var e = expected[i];
- expect(p.lon).toBe(e.lon, 'selected pt lon val');
- expect(p.lat).toBe(e.lat, 'selected pt lat val');
- });
- }
-
- function assertRanges(expected) {
- var ranges = (eventData.range || {}).mapbox || [];
- expect(ranges).toBeCloseTo2DArray(expected, 1, 'select box range (in lon/lat)');
- }
-
- function assertLassoPoints(expected) {
- var lassoPoints = (eventData.lassoPoints || {}).mapbox || [];
- expect(lassoPoints).toBeCloseTo2DArray(expected, 1, 'lasso points (in lon/lat)');
- }
-
Plotly.plot(gd, fig).then(function() {
- var selectingCnt = 0;
- var selectedCnt = 0;
- var deselectCnt = 0;
-
- gd.once('plotly_selecting', function() {
- assertSelectionNodes(1, 2);
- selectingCnt++;
- });
-
- gd.once('plotly_selected', function(d) {
- assertSelectionNodes(0, 2);
- selectedCnt++;
- eventData = d;
- });
-
- gd.once('plotly_deselect', function() {
- deselectCnt++;
- assertSelectionNodes(0, 0);
- });
-
- drag([[370, 120], [500, 200]]);
- assertPoints([{lon: 30, lat: 30}]);
- assertRanges([[21.99, 34.55], [38.14, 25.98]]);
-
- return doubleClick(250, 200).then(function() {
- expect(selectingCnt).toBe(1, 'plotly_selecting call count');
- expect(selectedCnt).toBe(1, 'plotly_selected call count');
- expect(deselectCnt).toBe(1, 'plotly_deselect call count');
+ return _run([[370, 120], [500, 200]], function() {
+ assertPoints([[30, 30]]);
+ assertRanges([[21.99, 34.55], [38.14, 25.98]]);
});
})
.then(function() {
return Plotly.relayout(gd, 'dragmode', 'lasso');
})
.then(function() {
- var selectingCnt = 0;
- var selectedCnt = 0;
- var deselectCnt = 0;
-
- gd.once('plotly_selecting', function() {
- assertSelectionNodes(1, 2);
- selectingCnt++;
- });
-
- gd.once('plotly_selected', function(d) {
- assertSelectionNodes(0, 2);
- selectedCnt++;
- eventData = d;
+ return _run([[300, 200], [300, 300], [400, 300], [400, 200], [300, 200]], function() {
+ assertPoints([[20, 20]]);
+ assertLassoPoints([
+ [13.28, 25.97], [13.28, 14.33], [25.71, 14.33], [25.71, 25.97], [13.28, 25.97]
+ ]);
});
+ })
+ .then(function() {
+ // make selection handlers don't get called in 'pan' dragmode
+ return Plotly.relayout(gd, 'dragmode', 'pan');
+ })
+ .then(function() {
+ return _runNoSelect([[370, 120], [500, 200]]);
+ })
+ .catch(fail)
+ .then(done);
+ }, LONG_TIMEOUT_INTERVAL);
- gd.once('plotly_deselect', function() {
- deselectCnt++;
- assertSelectionNodes(0, 0);
+ it('should work on scattergeo traces', function(done) {
+ var assertPoints = makeAssertPoints(['lon', 'lat']);
+ var assertRanges = makeAssertRanges('geo');
+ var assertLassoPoints = makeAssertLassoPoints('geo');
+
+ Plotly.plot(gd, [{
+ type: 'scattergeo',
+ lon: [10, 20, 30],
+ lat: [10, 20, 30]
+ }, {
+ type: 'scattergeo',
+ lon: [-10, -20, -30],
+ lat: [10, 20, 30]
+ }], {
+ showlegend: false,
+ dragmode: 'select',
+ width: 800,
+ height: 600
+ })
+ .then(function() {
+ return _run([[350, 200], [450, 400]], function() {
+ assertPoints([[10, 10], [20, 20], [-10, 10], [-20, 20]]);
+ assertRanges([[-28.13, 61.88], [28.13, -50.64]]);
});
-
- drag([[300, 200], [300, 300], [400, 300], [400, 200], [300, 200]]);
- assertPoints([{lon: 20, lat: 20}]);
- assertLassoPoints([
- [13.28, 25.97], [13.28, 14.33], [25.71, 14.33], [25.71, 25.97], [13.28, 25.97]
- ]);
-
- return doubleClick(250, 200).then(function() {
- expect(selectingCnt).toBe(1, 'plotly_selecting call count');
- expect(selectedCnt).toBe(1, 'plotly_selected call count');
- expect(deselectCnt).toBe(1, 'plotly_deselect call count');
+ })
+ .then(function() {
+ return Plotly.relayout(gd, 'dragmode', 'lasso');
+ })
+ .then(function() {
+ return _run([[300, 200], [300, 300], [400, 300], [400, 200], [300, 200]], function() {
+ assertPoints([[-10, 10], [-20, 20], [-30, 30]]);
+ assertLassoPoints([
+ [-56.25, 61.88], [-56.24, 5.63], [0, 5.63], [0, 61.88], [-56.25, 61.88]
+ ]);
});
})
.then(function() {
@@ -626,27 +693,47 @@ describe('select box and lasso', function() {
return Plotly.relayout(gd, 'dragmode', 'pan');
})
.then(function() {
- var selectingCnt = 0;
- var selectedCnt = 0;
- var deselectCnt = 0;
-
- gd.once('plotly_selecting', function() {
- selectingCnt++;
- });
+ return _runNoSelect([[370, 120], [500, 200]]);
+ })
+ .catch(fail)
+ .then(done);
+ }, LONG_TIMEOUT_INTERVAL);
- gd.once('plotly_selected', function() {
- selectedCnt++;
- });
+ it('should work on choropleth traces', function(done) {
+ var assertPoints = makeAssertPoints(['location', 'z']);
+ var assertRanges = makeAssertRanges('geo', -0.5);
+ var assertLassoPoints = makeAssertLassoPoints('geo', -0.5);
- gd.once('plotly_deselect', function() {
- deselectCnt++;
- });
+ var fig = Lib.extendDeep({}, require('@mocks/geo_choropleth-text'));
+ fig.layout.width = 870;
+ fig.layout.height = 450;
+ fig.layout.dragmode = 'select';
+ fig.layout.geo.scope = 'europe';
- return doubleClick(250, 200).then(function() {
- expect(selectingCnt).toBe(0, 'plotly_selecting call count');
- expect(selectedCnt).toBe(0, 'plotly_selected call count');
- expect(deselectCnt).toBe(0, 'plotly_deselect call count');
- });
+ Plotly.plot(gd, fig)
+ .then(function() {
+ return _run([[350, 200], [400, 250]], function() {
+ assertPoints([['GBR', 26.507354205352502], ['IRL', 86.4125147625692]]);
+ assertRanges([[-19.11, 63.06], [7.31, 53.72]]);
+ }, [280, 190]);
+ })
+ .then(function() {
+ return Plotly.relayout(gd, 'dragmode', 'lasso');
+ })
+ .then(function() {
+ return _run([[350, 200], [400, 200], [400, 250], [350, 250], [350, 200]], function() {
+ assertPoints([['GBR', 26.507354205352502], ['IRL', 86.4125147625692]]);
+ assertLassoPoints([
+ [-19.11, 63.06], [5.50, 65.25], [7.31, 53.72], [-12.90, 51.70], [-19.11, 63.06]
+ ]);
+ }, [280, 190]);
+ })
+ .then(function() {
+ // make selection handlers don't get called in 'pan' dragmode
+ return Plotly.relayout(gd, 'dragmode', 'pan');
+ })
+ .then(function() {
+ return _runNoSelect([[370, 120], [500, 200]]);
})
.catch(fail)
.then(done);